/* -*- 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 #include #include #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; } }