summaryrefslogtreecommitdiff
path: root/src/g_unix_signal.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/g_unix_signal.c')
-rw-r--r--src/g_unix_signal.c112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/g_unix_signal.c b/src/g_unix_signal.c
new file mode 100644
index 0000000..4d6a1a1
--- /dev/null
+++ b/src/g_unix_signal.c
@@ -0,0 +1,112 @@
+#define _POSIX_SOURCE
+#include <signal.h>
+#include <glib.h>
+
+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);
+}