summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSøren Sandmann Pedersen <ssp@dhcp-100-2-40.bos.redhat.com>2009-04-12 19:54:00 -0400
committerSøren Sandmann Pedersen <ssp@dhcp-100-2-40.bos.redhat.com>2009-04-12 19:54:00 -0400
commitfd7007163770f5f5bb7ef699a5c4ee153958b441 (patch)
treeade87f9fd77cec9b9b6187e785332edb4332eabb
parent85f4a3cd9d01eef1b280fc17f392612fb960fd45 (diff)
Add signal handling
-rw-r--r--Makefile.am1
-rw-r--r--TODO3
-rw-r--r--libnul.h11
-rw-r--r--signal-handler.c219
-rw-r--r--signal-handler.h27
5 files changed, 261 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index deed62c..4273d74 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,6 +8,7 @@ libnul_la_SOURCES = \
dbus.c \
array.c \
epoll.c \
+ signal-handler.c \
libnul.h
clean-local:
diff --git a/TODO b/TODO
index 87f53e5..c154ca1 100644
--- a/TODO
+++ b/TODO
@@ -1,3 +1,6 @@
+- handle the case where objects are just '/'. Currently they fail in invoke
+ with "failed to find object ''"
+
nul_dbus_service_start() should likely take a data parameter to
distinguish multiple instances of the same service. Method callbacks
will need to know this information. How does this work given the
diff --git a/libnul.h b/libnul.h
index c9fa41d..9fa5764 100644
--- a/libnul.h
+++ b/libnul.h
@@ -244,6 +244,17 @@ void nul_fd_set_poll_handler (int fd,
void nul_fd_remove_watch (int fd);
gboolean nul_fd_is_watched (int fd);
+/*
+ * Unix signal handlers
+ */
+typedef void (* signal_func_t) (int signo, gpointer data);
+
+/* FIXME: error out is required */
+gboolean nul_signal_set_handler (int signo,
+ signal_func_t handler,
+ gpointer data,
+ GError **err);
+
/* Implementing a service */
typedef struct nul_dbus_service_t nul_dbus_service_t;
diff --git a/signal-handler.c b/signal-handler.c
new file mode 100644
index 0000000..01cf8c8
--- /dev/null
+++ b/signal-handler.c
@@ -0,0 +1,219 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */
+
+/* Sysprof -- Sampling, systemwide CPU profiler
+ * Copyright (C) 2005 Søren Sandmann (sandmann@daimi.au.dk)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include "libnul.h"
+
+typedef struct signal_watch_t signal_watch_t;
+struct signal_watch_t
+{
+ int signo;
+ signal_func_t handler;
+ gpointer user_data;
+
+ struct sigaction old_action;
+
+ signal_watch_t * next;
+};
+
+static int read_end = -1;
+static int write_end = -1;
+static signal_watch_t *signal_watch_tes = NULL;
+
+static signal_watch_t *
+lookup_signal_watch_t (int signo)
+{
+ signal_watch_t *w;
+
+ for (w = signal_watch_tes; w != NULL; w = w->next)
+ {
+ if (w->signo == signo)
+ return w;
+ }
+
+ return NULL;
+}
+
+static void
+remove_signal_watch_t (signal_watch_t *watch)
+{
+ signal_watch_t *prev, *w;
+
+ g_return_if_fail (watch != NULL);
+
+ prev = NULL;
+ for (w = signal_watch_tes; w != NULL; w = w->next)
+ {
+ if (w == watch)
+ {
+ if (prev)
+ prev->next = w->next;
+ else
+ signal_watch_tes = w->next;
+
+ break;
+ }
+
+ prev = w;
+ }
+}
+
+static void
+signal_handler (int signo,
+ siginfo_t *info,
+ void *data)
+{
+ /* FIXME: I suppose we should handle short
+ * and non-successful writes ...
+ *
+ * And also, there is a deadlock if so many signals arrive that
+ * write() blocks. Then we will be stuck right here, and the
+ * main loop will never run. Kinda hard to fix without dropping
+ * signals ...
+ *
+ */
+ write (write_end, &signo, sizeof (int));
+}
+
+static void
+on_read (gpointer data)
+{
+ signal_watch_t *watch;
+ int signo;
+
+ /* FIXME: handle short read I suppose */
+ read (read_end, &signo, sizeof (int));
+
+ watch = lookup_signal_watch_t (signo);
+
+ if (watch)
+ watch->handler (signo, watch->user_data);
+}
+
+static gboolean
+create_pipe (int *read_end,
+ int *write_end,
+ GError **err)
+{
+ int p[2];
+
+ if (pipe (p) < 0)
+ {
+ /* FIXME - create an error */
+ return FALSE;
+ }
+
+ /* FIXME: We should probably make the fd's non-blocking */
+ if (read_end)
+ *read_end = p[0];
+
+ if (write_end)
+ *write_end = p[1];
+
+ return TRUE;
+}
+
+static gboolean
+install_signal_handler (int signo,
+ struct sigaction *old_action,
+ GError **err)
+{
+ struct sigaction action;
+
+ memset (&action, 0, sizeof (action));
+
+ action.sa_sigaction = signal_handler;
+ sigemptyset (&action.sa_mask);
+ action.sa_flags = SA_SIGINFO;
+
+ if (sigaction (signo, &action, old_action) < 0)
+ {
+ /* FIXME - create an error */
+ return TRUE;
+ }
+
+ return TRUE;
+}
+
+static void
+signal_watch_free (signal_watch_t *watch)
+{
+ remove_signal_watch_t (watch);
+
+ g_free (watch);
+}
+
+gboolean
+nul_signal_set_handler (int signo,
+ signal_func_t handler,
+ gpointer data,
+ GError **err)
+{
+ signal_watch_t *watch;
+
+ g_return_val_if_fail (handler == NULL ||
+ lookup_signal_watch_t (signo) == NULL, FALSE);
+
+ if (handler == NULL)
+ {
+ watch = lookup_signal_watch_t (signo);
+
+ if (watch)
+ {
+ /* FIXME: error */
+ sigaction (signo, &watch->old_action, NULL);
+
+ signal_watch_free (watch);
+
+ return TRUE;
+ }
+ }
+ else
+ {
+ if (read_end == -1)
+ {
+ if (!create_pipe (&read_end, &write_end, err))
+ return FALSE;
+
+ nul_fd_add_watch (read_end, NULL);
+ nul_fd_set_read_callback (read_end, on_read);
+ }
+
+ watch = g_new0 (signal_watch_t, 1);
+
+ watch->signo = signo;
+ watch->handler = handler;
+ watch->user_data = data;
+ watch->next = signal_watch_tes;
+ signal_watch_tes = watch;
+
+ if (!install_signal_handler (signo, &watch->old_action, err))
+ {
+ signal_watch_free (watch);
+
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+}
+
diff --git a/signal-handler.h b/signal-handler.h
new file mode 100644
index 0000000..44f3dde
--- /dev/null
+++ b/signal-handler.h
@@ -0,0 +1,27 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- */
+
+/* Sysprof -- Sampling, systemwide CPU profiler
+ * Copyright (C) 2005 Søren Sandmann (sandmann@daimi.au.dk)
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+typedef void (* SignalFunc) (int signo, gpointer data);
+
+gboolean signal_set_handler (int signo,
+ SignalFunc handler,
+ gpointer data,
+ GError **err);
+void signal_unset_handler (int signo);