#define _POSIX_SOURCE #include #include static GPtrArray *signal_infos = NULL; typedef struct _GUnixSignalInfo { guint source_id; GMainContext *context; gboolean triggered; gint signum; } GUnixSignalInfo; typedef struct _GUnixSignalSource { GSource source; GUnixSignalInfo *info; } GUnixSignalSource; static inline GUnixSignalInfo* get_signal_info(guint index) { return (GUnixSignalInfo*)g_ptr_array_index(signal_infos, index); } static void handler(gint signum) { g_assert(signal_infos != NULL); for (guint i = 0; i < signal_infos->len; ++i) if (get_signal_info(i)->signum == signum) get_signal_info(i)->triggered = TRUE; sigaction(signum, &(struct sigaction){handler}, NULL); } static gboolean check(GSource *source) { GUnixSignalSource *signal_source = (GUnixSignalSource*) source; return signal_source->info->triggered; } static gboolean prepare(GSource *source, gint *timeout_) { GUnixSignalSource *signal_source = (GUnixSignalSource*) source; if (signal_source->info->context == NULL) { g_main_context_ref(signal_source->info->context = g_source_get_context(source)); signal_source->info->source_id = g_source_get_id(source); } *timeout_ = -1; return signal_source->info->triggered; } static gboolean dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { GUnixSignalSource *signal_source = (GUnixSignalSource*) source; signal_source->info->triggered = FALSE; return callback(user_data) ? TRUE : FALSE; } static void finalize(GSource *source) { GUnixSignalSource *signal_source = (GUnixSignalSource*) source; sigaction(signal_source->info->signum, &(struct sigaction){NULL}, NULL); g_main_context_unref(signal_source->info->context); g_ptr_array_remove_fast(signal_infos, signal_source->info); if (signal_infos->len == 0) signal_infos = (GPtrArray*) g_ptr_array_free(signal_infos, TRUE); g_free(signal_source->info); } static GSourceFuncs SourceFuncs = { .prepare = prepare, .check = check, .dispatch = dispatch, .finalize = finalize, .closure_callback = NULL, .closure_marshal = NULL }; static void g_unix_signal_source_init(GSource *source, gint signum) { GUnixSignalSource *signal_source = (GUnixSignalSource *) source; signal_source->info = g_new(GUnixSignalInfo, 1); signal_source->info->triggered = FALSE; signal_source->info->signum = signum; signal_source->info->context = NULL; if (signal_infos == NULL) signal_infos = g_ptr_array_new(); g_ptr_array_add(signal_infos, signal_source->info); } GSource *g_unix_signal_source_new(gint signum) { GSource *source = g_source_new(&SourceFuncs, sizeof(GUnixSignalSource)); g_unix_signal_source_init(source, signum); sigaction(signum, &(struct sigaction){handler}, NULL); return source; } guint g_unix_signal_add_full(gint priority, gint signum, GSourceFunc function, gpointer data, GDestroyNotify notify) { g_return_val_if_fail(function != NULL, 0); GSource *source = g_unix_signal_source_new(signum); if (priority != G_PRIORITY_DEFAULT) g_source_set_priority (source, priority); g_source_set_callback(source, function, data, notify); guint id = g_source_attach(source, NULL); g_source_unref(source); return id; } guint g_unix_signal_add(gint signum, GSourceFunc function, gpointer data) { return g_unix_signal_add_full(G_PRIORITY_DEFAULT, signum, function, data, NULL); }