#include "watch.h" #include #define LOG(kind, ...) GNUNET_log_from (kind, "dbus-watch", __VA_ARGS__) /* * Wraps a file descriptor that needs to be watched * for activity with select() */ struct Watch { /* * DBus watch data. Contains the actual file descritor wrapped by libdbus */ DBusWatch *watch; /* * Have we asked the scheduler to watch this? * Will be false if the associated task has not been * re-scheduled yet after execution or because dbus has asked * us to disable this watch. */ bool scheduled; /* * The task that is watching our file descriptor. * Only valid if scheduled is true. */ struct GNUNET_SCHEDULER_Task *task; struct GNUNET_NETWORK_Handle *net_handle; struct GNUNET_DISK_FileHandle *file_handle; unsigned ref_count; }; struct Watch * watch_create ( DBusWatch *watch) { struct Watch *w = GNUNET_new (struct Watch); w->watch = watch; w->scheduled = false; w->net_handle = NULL; w->file_handle = NULL; w->ref_count = 1; SOCKTYPE sock = dbus_watch_get_socket (watch); if (-1 != sock) { w->net_handle = GNUNET_NETWORK_socket_box_native (sock); if (NULL == w->net_handle) { LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to box network socket passed in from dbus.\n"); GNUNET_abort_ (); }; } else { int fd = dbus_watch_get_unix_fd (watch); if (-1 != fd) { w->file_handle = GNUNET_DISK_get_handle_from_int_fd (fd); if (NULL == w->file_handle) { LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to box file handle passed in from dbus.\n"); GNUNET_abort_ (); }; }; }; if (! w->net_handle && ! w->file_handle) { LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to create watch. dbus_watch_get_socket returned %d\n", (int)sock); GNUNET_abort_ (); }; return w; }; void watch_ref ( struct Watch *w) { w->ref_count++; }; void watch_unref ( struct Watch *w) { if (0 == w->ref_count) { LOG (GNUNET_ERROR_TYPE_ERROR, "Tried to unref watch with ref_count == 0\n"); GNUNET_abort_ (); }; if (0 == --w->ref_count) { if (w->net_handle) GNUNET_free (w->net_handle); if (w->file_handle) GNUNET_free (w->file_handle); GNUNET_free (w); } }; /* * Callback called by the scheduler to tell libdbus that there is activity on * one of its file descriptors. * * @param cls The watch * @param tc the context given to us by the scheduler for this execution */ void handle_watch ( void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { struct Watch *w = (struct Watch *)cls; w->scheduled = false; unsigned flags = 0; if (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY) flags |= DBUS_WATCH_READABLE; if (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY) flags |= DBUS_WATCH_WRITABLE; if (flags) { dbus_watch_handle(w->watch, flags); }; if(w->ref_count > 1 && ! (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) watch_schedule (w); watch_unref (w); }; /* * Ask the scheduler to watch this watch for activity. * * @param w The watch * @return GNUNET_OK or GNUNET_SYSERR */ void watch_schedule ( struct Watch *w) { unsigned flags = dbus_watch_get_flags (w->watch); if (! dbus_watch_get_enabled (w->watch)) { LOG (GNUNET_ERROR_TYPE_WARNING, "Tried to schedule watch that is disabled!\n"); return; }; if (w->scheduled) return; if (w->net_handle) { w->task = GNUNET_SCHEDULER_add_net_with_priority( GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_SCHEDULER_PRIORITY_DEFAULT, w->net_handle, flags & DBUS_WATCH_READABLE, flags & DBUS_WATCH_WRITABLE, handle_watch, w); w->scheduled = true; watch_ref (w); return; }; if (w->file_handle) { w->task = GNUNET_SCHEDULER_add_file_with_priority( GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_SCHEDULER_PRIORITY_DEFAULT, w->file_handle, flags & DBUS_WATCH_READABLE, flags & DBUS_WATCH_WRITABLE, handle_watch, w); w->scheduled = true; watch_ref (w); return; }; LOG (GNUNET_ERROR_TYPE_ERROR, "Failed to schedule watch.\n"); GNUNET_abort_ (); }; /* * Ask the scheduler to stop monitoring a watch either because we are shutting * down or dbus has asked us to disable this watch. * * @param w The watch * @return GNUNET_OK or GNUNET_SYSERR */ void do_watch_unschedule ( void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) { (void)tc; struct Watch *w = (struct Watch *)cls; if (! w->scheduled) return; void *ret = GNUNET_SCHEDULER_cancel (w->task); if ((struct Watch *)ret != w) LOG (GNUNET_ERROR_TYPE_WARNING, "Weird result unscheduling task. w == %p, GNUNET_SCHEDULER_cancel returned %p\n", w, ret); watch_unref (w); }; void watch_unschedule ( struct Watch *w) { GNUNET_SCHEDULER_add_now (do_watch_unschedule, w); }; struct WatchIter * watch_find (struct WatchIter *wi, DBusWatch *watch) { while (wi && wi->w->watch != watch) wi = wi->next; return wi; };