/*************************************************************************** * 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 #include #include #include #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 *privdata); void *privdata; }; 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, "%s(): sigaction(%d)", __FUNCTION__, signum); return -1; } /* trigger remove */ sig_handler(0); return 0; } int signal_add_callback(int signum, void (*callback)(void *privdata), void *privdata) { struct signal_entry *entry = malloc(sizeof(struct signal_entry)); if (entry == NULL) { log_print(LOG_WARN, "%s(): out of memory", __FUNCTION__); return -1; } entry->signum = signum; entry->deleted = 0; entry->callback = callback; entry->privdata = privdata; 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, "%s(): sigaction(%d)", __FUNCTION__, signum); 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, "%s(): read()", __FUNCTION__); 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(search->privdata); } return 0; } int signal_init(void) { if (pipe(sig_pipe) < 0) { log_print(LOG_ERROR, "%s(): pipe()", __FUNCTION__); return -1; } if (fcntl(sig_pipe[0], F_SETFD, FD_CLOEXEC) < 0) { log_print(LOG_WARN, "%s(): fcntl(FD_CLOEXEC)", __FUNCTION__); return -1; } if (fcntl(sig_pipe[1], F_SETFD, FD_CLOEXEC) < 0) { log_print(LOG_WARN, "%s(): fcntl(FD_CLOEXEC)", __FUNCTION__); return -1; } event_add_readfd(NULL, sig_pipe[0], sig_event, NULL); return 0; }