Files
sysprof/signal-handler.c
Søren Sandmann Pedersen 1b21157a12 Update TODO
2005-09-29 02:16:31 +00:00

232 lines
5.0 KiB
C

/* -*- 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 "watch.h"
#include "signal-handler.h"
typedef struct SignalWatch SignalWatch;
struct SignalWatch
{
int signo;
SignalFunc handler;
gpointer user_data;
int read_end;
int write_end;
struct sigaction old_action;
SignalWatch * next;
};
static SignalWatch *signal_watches;
static SignalWatch *
lookup_signal_watch (int signo)
{
SignalWatch *w;
for (w = signal_watches; w != NULL; w = w->next)
{
if (w->signo == signo)
return w;
}
return NULL;
}
/* These two functions might be interrupted by a signal handler that is
* going to run lookup_signal_watch(). Assuming that pointer writes are
* atomic, the code below should be ok.
*/
static void
add_signal_watch (SignalWatch *watch)
{
g_return_if_fail (lookup_signal_watch (watch->signo) == NULL);
watch->next = signal_watches;
signal_watches = watch;
}
static void
remove_signal_watch (SignalWatch *watch)
{
g_return_if_fail (watch != NULL);
/* It's either the first one in the list, or it is the ->next
* for some other watch
*/
if (watch == signal_watches)
{
signal_watches = watch->next;
watch->next = NULL;
}
else
{
SignalWatch *w;
for (w = signal_watches; w && w->next; w = w->next)
{
if (watch == w->next)
{
w->next = w->next->next;
watch->next = NULL;
break;
}
}
}
}
static void
signal_handler (int signo,
siginfo_t *info,
void *data)
{
SignalWatch *watch;
char x = 'x';
watch = lookup_signal_watch (signo);
write (watch->write_end, &x, 1);
}
static void
on_read (gpointer data)
{
SignalWatch *watch = data;
char x;
read (watch->read_end, &x, 1);
/* This is a callback to the application, so things like
* signal_unset_handler() can be called here.
*/
watch->handler (watch->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;
}
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 (SignalWatch *watch)
{
fd_remove_watch (watch->read_end);
close (watch->write_end);
close (watch->read_end);
remove_signal_watch (watch);
g_free (watch);
}
gboolean
signal_set_handler (int signo,
SignalFunc handler,
gpointer data,
GError **err)
{
SignalWatch *watch;
int read_end, write_end;
g_return_val_if_fail (lookup_signal_watch (signo) == NULL, FALSE);
if (!create_pipe (&read_end, &write_end, err))
return FALSE;
watch = g_new0 (SignalWatch, 1);
watch->signo = signo;
watch->handler = handler;
watch->user_data = data;
watch->read_end = read_end;
watch->write_end = write_end;
fd_add_watch (watch->read_end, watch);
fd_set_read_callback (watch->read_end, on_read);
add_signal_watch (watch);
if (!install_signal_handler (signo, &watch->old_action, err))
{
signal_watch_free (watch);
return FALSE;
}
return TRUE;
}
void
signal_unset_handler (int signo)
{
SignalWatch *watch;
watch = lookup_signal_watch (signo);
g_return_if_fail (watch != NULL);
sigaction (signo, &watch->old_action, NULL);
signal_watch_free (watch);
}