sammler/signals.c
2009-05-03 14:07:22 +02:00

150 lines
3.8 KiB
C

/***************************************************************************
* Copyright (C) 05/2009 by Olaf Rempel *
* razzor@kopf-tisch.de *
* *
* 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; version 2 of the License *
* *
* 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 *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
***************************************************************************/
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include "event.h"
#include "list.h"
#include "logging.h"
#include "signals.h"
struct signal_entry {
struct list_head list;
int signum;
int deleted;
void (*callback)(void);
};
static LIST_HEAD(callback_list);
static int sig_pipe[2];
static void sig_handler(int signal)
{
unsigned char signum = (signal & 0xFF);
write(sig_pipe[1], &signum, 1);
}
int signal_remove_callback(int signum, int type)
{
/* mark all old handler for deletion */
struct signal_entry *search;
list_for_each_entry(search, &callback_list, list) {
if (search->signum == signum)
search->deleted = 1;
}
struct sigaction sig_action;
switch (type) {
case SIG_IGNORE:
sig_action.sa_handler = SIG_IGN;
break;
default:
case SIG_DEFAULT:
sig_action.sa_handler = SIG_DFL;
break;
}
if (sigaction(signum, &sig_action, NULL) < 0) {
log_print(LOG_WARN, "signal_remove_callback(): sigaction()");
return -1;
}
/* trigger remove */
sig_handler(0);
return 0;
}
int signal_set_callback(int signum, void (*callback)(void))
{
struct signal_entry *entry = malloc(sizeof(struct signal_entry));
if (entry == NULL) {
log_print(LOG_WARN, "signal_add_callback(): out of memory");
return -1;
}
entry->signum = signum;
entry->deleted = 0;
entry->callback = callback;
list_add_tail(&entry->list, &callback_list);
struct sigaction sig_action = {
.sa_handler = sig_handler,
};
if (sigaction(signum, &sig_action, NULL) < 0) {
log_print(LOG_WARN, "signal_add_callback(): sigaction()");
list_del(&entry->list);
free(entry);
return -1;
}
return 0;
}
static int sig_event(int fd, void *privdata)
{
unsigned char signum;
int len = read(fd, &signum, 1);
if (len <= 0) {
log_print(LOG_WARN, "sig_event(): read()");
return -1;
}
struct signal_entry *search, *tmp;
list_for_each_entry_safe(search, tmp, &callback_list, list) {
if (search->deleted) {
list_del(&search->list);
free(search);
continue;
}
if (search->signum == signum)
search->callback();
}
return 0;
}
int signal_init(void)
{
if (pipe(sig_pipe) < 0) {
log_print(LOG_ERROR, "signal_init(): pipe()");
return -1;
}
if (fcntl(sig_pipe[0], F_SETFD, FD_CLOEXEC) < 0) {
log_print(LOG_WARN, "signal_init(): fcntl(FD_CLOEXEC)");
return -1;
}
if (fcntl(sig_pipe[1], F_SETFD, FD_CLOEXEC) < 0) {
log_print(LOG_WARN, "signal_init(): fcntl(FD_CLOEXEC)");
return -1;
}
event_add_readfd(NULL, sig_pipe[0], sig_event, NULL);
return 0;
}