seahorse-unix-signal.c 3.82 KB
Newer Older
1 2 3
/*
 * Seahorse
 *
4
 * Copyright (C) 2006 Stefan Walter
5 6 7 8 9 10 11 12 13 14 15
 *
 * This program 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 program 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
Daniel Mustieles's avatar
Daniel Mustieles committed
16
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 18 19 20 21 22 23 24 25 26 27 28
 */

#include <sys/types.h>
#include <sys/signal.h>

#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <err.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
29 30
#include <string.h>
#include <errno.h>
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140

#include "seahorse-unix-signal.h"

#define MAX_SIGNAL 32
static signal_handler signal_handlers[MAX_SIGNAL] = { NULL, };

static int signal_pipe[2] = { -1, -1 };
static GIOChannel *signal_channel = NULL;
static gint signal_watch_id = 0;


/* The unix signal handler. */
static void 
pipe_signals (int signal)
{
    gchar sig;

    if (signal > MAX_SIGNAL) {
        g_warning ("unix signal number too large: %d", signal);
        return;
    }

    sig = (char)signal;
    if (write (signal_pipe[1], &sig, sizeof (gchar)) != sizeof (gchar))
        g_critical ("unix signal %d lost", signal);
}
  
/* The event loop callback that handles the unix signals. Must be a GIOFunc. */
static gboolean 
deliver_signal (GIOChannel *source, GIOCondition cond, gpointer d)
{
    GError *err = NULL;
    GIOStatus st;
    gsize read;
    gchar sig;

    while ((st = g_io_channel_read_chars (source, &sig, sizeof (gchar), 
                                          &read, &err)) == G_IO_STATUS_NORMAL) {

        g_assert (err == NULL);
        if (read != 1)
            break;
        if (sig < MAX_SIGNAL) {
            if (signal_handlers[(int)sig])
                (signal_handlers[(int)sig]) (sig);
        }
    }
  
    if (err != NULL)
        g_warning ("reading signal pipe failed: %s", err->message);
    if (st == G_IO_STATUS_EOF) 
        g_critical ("signal pipe has been closed");

    return TRUE;
}

static void 
cleanup_signals ()
{
    if (signal_watch_id)
        g_source_remove (signal_watch_id);
    signal_watch_id = 0;

    if (signal_channel)
        g_io_channel_unref (signal_channel);
    signal_channel = NULL;

    if (signal_pipe[0] != -1) {
        close (signal_pipe[0]);
        close (signal_pipe[1]);
        signal_pipe[0] = signal_pipe[1] = -1;
    }
}

void 
seahorse_unix_signal_register (int sig, signal_handler handler)
{
    g_return_if_fail (sig < MAX_SIGNAL);
    g_return_if_fail (handler != NULL);

    /* Setup the signal channel */
    if (signal_channel == NULL) {

        memset (&signal_handlers, 0, sizeof (signal_handlers));
        
        if (pipe (signal_pipe)) {
            g_critical ("can't create signal pipe: %s", strerror (errno));
            return;
        }

        /* Non blocking to prevent deadlock */
        fcntl (signal_pipe[1], F_SETFL, fcntl (signal_pipe[1], F_GETFL) | O_NONBLOCK);

        /* convert the reading end of the pipe into a GIOChannel */
        signal_channel = g_io_channel_unix_new (signal_pipe[0]);
        g_io_channel_set_encoding (signal_channel, NULL, NULL);
        g_io_channel_set_flags (signal_channel, g_io_channel_get_flags (signal_channel) | G_IO_FLAG_NONBLOCK, NULL);

        /* register the reading end with the event loop */
        signal_watch_id = g_io_add_watch (signal_channel, G_IO_IN | G_IO_PRI | G_IO_HUP, deliver_signal, NULL);

        g_atexit (cleanup_signals);
    }

    /* Handle some signals */
    signal (sig, pipe_signals);

    signal_handlers[sig] = handler;
}