diff --git a/TODO b/TODO index 331696b..9681b48 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,6 @@ - dynamic pages - refresh of dynamic pages (clock, loadavg) - scrolling -- daemonize: pid/log/config-file - disk utilization (MB/GB) - mntpoints given in config diff --git a/configfile.c b/configfile.c new file mode 100644 index 0000000..05a28b9 --- /dev/null +++ b/configfile.c @@ -0,0 +1,288 @@ +/*************************************************************************** + * Copyright (C) 03/2010 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 + +#include "configfile.h" +#include "list.h" +#include "logging.h" + +#define BUFSIZE 1024 + +struct conf_section { + struct list_head list; + struct list_head tupel_list; + const char *name; +}; + +struct conf_tupel { + struct list_head list; + const char *option; + const char *parameter; +}; + +static LIST_HEAD(config_list); + +static struct conf_section * config_add_section(const char *name) +{ + struct conf_section *section; + section = malloc(sizeof(struct conf_section) + strlen(name)); + if (section == NULL) + return NULL; + + INIT_LIST_HEAD(§ion->list); + INIT_LIST_HEAD(§ion->tupel_list); + + section->name = strdup(name); + if (section->name == NULL) { + free(section); + return NULL; + } + + list_add_tail(§ion->list, &config_list); + return section; +} + +static int config_add_tupel(struct conf_section *section, const char *option, const char *parameter) +{ + struct conf_tupel *tupel = malloc(sizeof(struct conf_tupel)); + if (tupel == NULL) + return -1; + + INIT_LIST_HEAD(&tupel->list); + tupel->option = strdup(option); + tupel->parameter = strdup(parameter); + + if (tupel->option == NULL || tupel->parameter == NULL) { + free(tupel); + return -1; + } + + list_add_tail(&tupel->list, §ion->tupel_list); + return 0; +} + +int config_parse(const char *config) +{ + FILE *fz = fopen(config, "r"); + if (fz == NULL) { + log_print(LOG_ERROR, "config_parse(): %s", config); + return -1; + } + + char *line = malloc(BUFSIZE); + if (line == NULL) { + log_print(LOG_ERROR, "config_parse(): out of memory"); + fclose(fz); + return -1; + } + + int linenum = 0; + struct conf_section *section = NULL; + while (fgets(line, BUFSIZE, fz) != NULL) { + linenum++; + + if (line[0] == '#' || line[0] <= ' ') { + continue; + + } else if (line[0] == '[') { + char *tok = strtok(line +1, " ]\n"); + + if (tok == NULL || (section = config_add_section(tok)) == NULL) { + log_print(LOG_WARN, "config_parse(): invalid section in row %d", linenum); + free(line); + fclose(fz); + return -1; + } + continue; + + } else if (section == NULL) { + log_print(LOG_WARN, "config_parse(): missing section in row %d", linenum); + free(line); + fclose(fz); + return -1; + } + + char *tmp, *tok = strtok_r(line, " \t\n", &tmp); + if (tok != NULL) { + char *tok2; + while ((tok2 = strtok_r(NULL, " \n", &tmp))) { + if (config_add_tupel(section, tok, tok2) != 0) + log_print(LOG_WARN, "config_parse(): invalid row %d", linenum); + } + } + } + + fclose(fz); + free(line); + return 0; +} + +void config_free(void) +{ + struct conf_section *section, *section_tmp; + struct conf_tupel *tupel, *tupel_tmp; + + list_for_each_entry_safe(section, section_tmp, &config_list, list) { + list_for_each_entry_safe(tupel, tupel_tmp, §ion->tupel_list, list) { + list_del(&tupel->list); + free((char *)tupel->option); + free((char *)tupel->parameter); + free(tupel); + } + list_del(§ion->list); + free(section); + } +} + +static struct conf_section * config_get_section(const char *name) +{ + struct conf_section *section; + + list_for_each_entry(section, &config_list, list) { + if (!strcmp(section->name, name)) + return section; + } + return NULL; +} + +const char * config_get_string(const char *section_str, const char *option, const char *def) +{ + struct conf_section *section = config_get_section(section_str); + if (section != NULL) { + struct conf_tupel *tupel; + list_for_each_entry(tupel, §ion->tupel_list, list) { + if (!strcmp(tupel->option, option)) + return tupel->parameter; + } + } + + if (def != NULL) + log_print(LOG_WARN, "config [%s:%s] not found, using default: '%s'", + section_str, option, def); + return def; +} + +int config_get_int(const char *section, const char *option, int *value, int def) +{ + const char *ret = config_get_string(section, option, NULL); + if (ret == NULL) { + log_print(LOG_WARN, "config [%s:%s] not found, using default: '%d'", + section, option, def); + + *value = def; + return -1; + } + + char *tmp; + *value = strtol(ret, &tmp, 0); + + if (*tmp != '\0' && !isspace(*tmp)) { + log_print(LOG_WARN, "config [%s:%s] not an integer: '%s', using default '%d'", + section, option, ret, def); + + *value = def; + return -1; + } + + return 0; +} + +int config_get_strings(const char *section_str, const char *option, + int (*callback)(const char *value, void *privdata), + void *privdata) +{ + struct conf_section *section = config_get_section(section_str); + if (section == NULL) + return -1; + + int cnt = 0; + struct conf_tupel *tupel; + list_for_each_entry(tupel, §ion->tupel_list, list) { + if (!strcmp(tupel->option, option)) + if (callback(tupel->parameter, privdata) == 0) + cnt++; + } + return cnt; +} + +struct strtoken * strtokenize(const char *input, const char *delim, int maxfields) +{ + struct strtoken *tokens = malloc(sizeof(struct strtoken) + + (maxfields +1) * sizeof(char *) + + strlen(input)); + if (tokens == NULL) + return NULL; + + char *ptr = (char *)&tokens->field[maxfields]; + strcpy(ptr, input); + + int i; + char *tmp; + + tokens->count = 0; + for (i = 0; i < maxfields; i++) { + tokens->field[i] = strtok_r(ptr, delim, &tmp); + ptr = NULL; + + if (tokens->field[i] != NULL) + tokens->count++; + } + + return tokens; +} + +struct strtoken * config_get_strtoken(const char *section, const char *option, const char *delim, int maxfields) +{ + const char *ret = config_get_string(section, option, NULL); + if (ret == NULL) { + log_print(LOG_WARN, "config [%s:%s] not found", section, option); + + return NULL; + } + + return strtokenize(ret, delim, maxfields); +} + +int config_get_strtokens(const char *section_str, const char *option, const char *delim, int maxfields, + int (*callback)(struct strtoken *data, void *privdata), + void *privdata) +{ + struct conf_section *section = config_get_section(section_str); + if (section == NULL) + return -1; + + int cnt = 0; + struct conf_tupel *tupel; + list_for_each_entry(tupel, §ion->tupel_list, list) { + if (!strcmp(tupel->option, option)) { + struct strtoken *tokens = strtokenize(tupel->parameter, delim, maxfields); + if (tokens != NULL) { + if (callback(tokens, privdata) == 0) + cnt++; + + free(tokens); + } + } + } + return cnt; +} diff --git a/configfile.h b/configfile.h new file mode 100644 index 0000000..63c2edb --- /dev/null +++ b/configfile.h @@ -0,0 +1,28 @@ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +int config_parse(const char *config); +void config_free(void); + +const char * config_get_string(const char *section_str, const char *option, const char *def); + +int config_get_int(const char *section, const char *option, int *value, int def); + +int config_get_strings(const char *section_str, const char *option, + int (*callback)(const char *value, void *privdata), + void *privdata); + +struct strtoken { + int count; + char *field[0]; +}; + +struct strtoken * strtokenize(const char *input, const char *delim, int maxfields); + +struct strtoken * config_get_strtoken(const char *section_str, const char *option, const char *delim, int maxfields); + +int config_get_strtokens(const char *section_str, const char *option, const char *delim, int maxfields, + int (*callback)(struct strtoken *tokens, void *privdata), + void *privdata); + +#endif /* _CONFIG_H_ */ diff --git a/event.c b/event.c index 4e52c31..b5a3898 100644 --- a/event.c +++ b/event.c @@ -199,16 +199,17 @@ void event_remove_timeout(struct event_timeout *entry) entry->flags |= EVENT_DELETE; } -int event_loop(int (*callback)(void *privdata), void *privdata) +int event_loop(int (*pre_select_cb)(int *maxfd, void *readfds, void *writefds, struct timeval *timeout, void *privdata), + int (*post_select_cb)(int retval, void *readfds, void *writefds, void *privdata), + void *privdata) { - fd_set *fdsets = malloc(sizeof(fd_set) * 2); - if (fdsets == NULL) { - log_print(LOG_ERROR, "event_loop(): out of memory"); - return -1; - } - while (1) { - struct timeval timeout, *timeout_p = NULL; + /* default value if no application timeout is present */ + struct timeval timeout = { + .tv_sec = 3600, + .tv_usec = 0, + }; + if (!list_empty(&event_timeout_list)) { struct timeval now; gettimeofday(&now, NULL); @@ -242,14 +243,16 @@ int event_loop(int (*callback)(void *privdata), void *privdata) /* calc select() timeout */ sub_timeval(&timeout, &entry->nextrun, &now); - timeout_p = &timeout; } } - fd_set *readfds = NULL, *writefds = NULL; struct event_fd *entry, *tmp; int maxfd = -1; + fd_set readfds, writefds; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + list_for_each_entry_safe(entry, tmp, &event_fd_list, list) { entry->flags &= ~EVENT_NEW; @@ -259,52 +262,49 @@ int event_loop(int (*callback)(void *privdata), void *privdata) continue; } - if (entry->flags & FD_READ) { - if (readfds == NULL) { - readfds = &fdsets[0]; - FD_ZERO(readfds); - } - FD_SET(entry->fd, readfds); - } + if (entry->flags & FD_READ) + FD_SET(entry->fd, &readfds); - if (entry->flags & FD_WRITE) { - if (writefds == NULL) { - writefds = &fdsets[1]; - FD_ZERO(writefds); - } - FD_SET(entry->fd, writefds); - } + if (entry->flags & FD_WRITE) + FD_SET(entry->fd, &writefds); maxfd = (entry->fd > maxfd) ? entry->fd : maxfd; } + maxfd++; + /* exit loop if callback returns true */ - if (callback != NULL && callback(privdata) != 0) + if (pre_select_cb != NULL && pre_select_cb(&maxfd, (void *)&readfds, (void *)&writefds, &timeout, privdata) != 0) break; - int i = select(maxfd +1, readfds, writefds, NULL, timeout_p); - if (i < 0 && errno == EINTR) { + int retval = select(maxfd, &readfds, &writefds, NULL, &timeout); + if (retval < 0 && errno == EINTR) { errno = 0; continue; - } else if (i < 0) { + } else if (retval < 0) { log_print(LOG_ERROR, "event_loop(): select():"); continue; - - } else if (i == 0) { - continue; } + /* exit loop if callback returns true */ + if (post_select_cb != NULL && post_select_cb(retval, (void *)&readfds, (void *)&writefds, privdata) != 0) + break; + + /* timeout */ + if (retval == 0) + continue; + list_for_each_entry(entry, &event_fd_list, list) { - if (((entry->flags & (FD_READ | EVENT_NEW)) == FD_READ) && FD_ISSET(entry->fd, readfds)) + if (((entry->flags & (FD_READ | EVENT_NEW)) == FD_READ) && FD_ISSET(entry->fd, &readfds)) if (entry->read_cb(entry->fd, entry->read_priv) != 0) entry->flags |= EVENT_DELETE; - if (((entry->flags & (FD_WRITE | EVENT_NEW)) == FD_WRITE) && FD_ISSET(entry->fd, writefds)) + if (((entry->flags & (FD_WRITE | EVENT_NEW)) == FD_WRITE) && FD_ISSET(entry->fd, &writefds)) if (entry->write_cb(entry->fd, entry->write_priv) != 0) entry->flags |= EVENT_DELETE; } } - free(fdsets); + return 0; } diff --git a/event.h b/event.h index cb8b0d4..da3391e 100644 --- a/event.h +++ b/event.h @@ -37,6 +37,8 @@ struct event_timeout * event_add_timeout( void event_remove_timeout(struct event_timeout *entry); -int event_loop(int (*callback)(void *privdata), void *privdata); +int event_loop(int (*pre_select_cb)(int *maxfd, void *readfds, void *writefds, struct timeval *timeout, void *privdata), + int (*post_select_cb)(int retval, void *readfds, void *writefds, void *privdata), + void *privdata); #endif /* _EVENT_H_ */ diff --git a/logging.h b/logging.h index 82d028d..87f1c2e 100644 --- a/logging.h +++ b/logging.h @@ -1,14 +1,18 @@ #ifndef _LOGGING_H_ #define _LOGGING_H_ -#define LOG_DEBUG 5 -#define LOG_INFO 4 -#define LOG_NOTICE 3 -#define LOG_WARN 2 -#define LOG_ERROR 1 -#define LOG_CRIT 0 +#define LOG_EMERG 0 /* system is unusable */ +#define LOG_ALERT 1 /* action must be taken immediately */ +#define LOG_CRIT 2 /* critical conditions */ +#define LOG_ERR 3 /* error conditions */ +#define LOG_WARNING 4 /* warning conditions */ +#define LOG_NOTICE 5 /* normal but significant condition */ +#define LOG_INFO 6 /* informational */ +#define LOG_DEBUG 7 /* debug-level messages */ -#define LOG_EVERYTIME 0 +#define LOG_EVERYTIME LOG_EMERG +#define LOG_ERROR LOG_ERR +#define LOG_WARN LOG_WARNING int log_init(const char *logfile); void log_close(void); diff --git a/pidfile.c b/pidfile.c new file mode 100644 index 0000000..f5dd0be --- /dev/null +++ b/pidfile.c @@ -0,0 +1,73 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "logging.h" + +int pidfile_create(const char *filename) +{ + int fd = open(filename, O_CREAT | O_EXCL | O_RDWR, 0644); + if (fd < 0) + return -1; + + char buf[8]; + int len = snprintf(buf, sizeof(buf), "%d", getpid()); + write(fd, buf, len); + + close(fd); + return 0; +} + +int pidfile_remove(const char *filename) +{ + return unlink(filename); +} + +pid_t pidfile_check(const char *filename, int remove_stale) +{ + int fd = open(filename, O_RDWR); + if (fd < 0) { + if (errno == ENOENT) { + errno = 0; + return 0; + } + return -1; + } + + char buf[9]; + int len = read(fd, buf, sizeof(buf) -1); + buf[len] = '\0'; + + close(fd); + + char *tmp; + pid_t pid = strtol(buf, &tmp, 10); + if (len == 0 || tmp == buf) + pid = -1; + + /* just return the pid */ + if (!remove_stale) + return pid; + + /* invalid pid, remove stale file */ + if (pid == -1) { + pidfile_remove(filename); + return 0; + } + + /* check if pid is still running */ + if (kill(pid, 0) == 0 || errno != ESRCH) { + errno = 0; + return pid; + } + + errno = 0; + pidfile_remove(filename); + return 0; +} diff --git a/pidfile.h b/pidfile.h new file mode 100644 index 0000000..2ba1512 --- /dev/null +++ b/pidfile.h @@ -0,0 +1,11 @@ +#ifndef _PIDFILE_H_ +#define _PIDFILE_H_ + +#include + +int pidfile_create(const char *filename); +int pidfile_remove(const char *filename); + +pid_t pidfile_check(const char *filename, int remove_stale); + +#endif // _PIDFILE_H_ diff --git a/qnaplcd.c b/qnaplcd.c index 209e6ec..e4c7258 100644 --- a/qnaplcd.c +++ b/qnaplcd.c @@ -1,15 +1,53 @@ +/*************************************************************************** + * Copyright (C) 04/2011 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; 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 * + * 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 +#include +#include +#include +#include +#include "configfile.h" #include "event.h" #include "lcd.h" #include "logging.h" +#include "pidfile.h" +#include "signals.h" #define LCD_DEVICE "/dev/ttyS0" #define LCD_TIMEOUT 10 +#define DEFAULT_CONFIG "qnaplcd.conf" +#define DEFAULT_LOGFILE "qnaplcd.log" +#define DEFAULT_PIDFILE "qnaplcd.pid" + +static struct option opts[] = { + {"config", 1, 0, 'c'}, + {"debug", 0, 0, 'd'}, + {"help", 0, 0, 'h'}, + {0, 0, 0, 0} +}; + #define PAGE_MAX 7 static int page; @@ -60,12 +98,116 @@ static int button_callback(struct lcddev *dev, int button) return 0; } -int main(int argc, const char *argv[]) +static int restart_var; + +static void trigger_restart(void *privdata) { - if (lcd_init(LCD_DEVICE, LCD_TIMEOUT, &button_callback) < 0) - return -1; - - event_loop(NULL, NULL); - - return 0; + int *restart = (int *)privdata; + *restart = 1; +} + +int check_restart(int *maxfd, void *readfds, void *writefds, struct timeval *timeout, void *privdata) +{ + int *restart = (int *)privdata; + if (*restart == 1) { + *restart = 0; + return 1; + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + char *config = DEFAULT_CONFIG; + int code, arg = 0, debug = 0; + + do { + code = getopt_long(argc, argv, "c:dh", opts, &arg); + + switch (code) { + case 'c': /* config */ + config = optarg; + break; + + case 'd': /* debug */ + debug = 1; + break; + + case 'h': /* help */ + printf("Usage: qnaplcd [options]\n" + "Options: \n" + " --config -c configfile use this configfile\n" + " --debug -d do not fork and log to stderr\n" + " --help -h this help\n" + "\n"); + exit(0); + break; + + case '?': /* error */ + exit(1); + break; + + default: /* unknown / all options parsed */ + break; + } + } while (code != -1); + + /* parse config file */ + if (config_parse(config) < 0) + exit(1); + + if (!debug) { + /* check pidfile */ + const char *pidfile = config_get_string("global", "pidfile", DEFAULT_PIDFILE); + if (pidfile_check(pidfile, 1) != 0) { + log_print(LOG_ERROR, "qnaplcd already running"); + exit(1); + } + + /* start logging */ + const char *logfile = config_get_string("global", "logfile", DEFAULT_LOGFILE); + if (log_init(logfile) < 0) + exit(1); + + /* zum daemon mutieren */ + if (daemon(-1, 0) < 0) { + log_print(LOG_ERROR, "failed to daemonize"); + exit(1); + } + + /* create pidfile */ + if (pidfile_create(pidfile) < 0) { + log_print(LOG_ERROR, "failed to create pidfile %s", pidfile); + exit(1); + } + } + + signal_init(); + signal_add_callback(SIGHUP, trigger_restart, (void *)&restart_var); + + log_print(LOG_EVERYTIME, "qnaplcd started (pid:%d)", getpid()); + + while (1) { + if (lcd_init(LCD_DEVICE, LCD_TIMEOUT, &button_callback) < 0) + break; + + /* exited on restart / SIGUSR1 */ + event_loop(check_restart, NULL, (void *)&restart_var); + + // TODO: need lcd_close() + + config_free(); + + if (config_parse(config) < 0) + break; + + const char *logfile = config_get_string("global", "logfile", DEFAULT_LOGFILE); + if (!debug && log_init(logfile) < 0) + break; + + log_print(LOG_EVERYTIME, "bcastfwd restarted (pid:%d) ", getpid()); + } + + return 0; } diff --git a/signals.c b/signals.c new file mode 100644 index 0000000..4ae7176 --- /dev/null +++ b/signals.c @@ -0,0 +1,151 @@ +/*************************************************************************** + * 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, "signal_remove_callback(): sigaction()"); + 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, "signal_add_callback(): out of memory"); + 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, "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(search->privdata); + } + + 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; +} diff --git a/signals.h b/signals.h new file mode 100644 index 0000000..3a9cc16 --- /dev/null +++ b/signals.h @@ -0,0 +1,12 @@ +#ifndef _SIGNALS_H +#define _SIGNALS_H + +#define SIG_DEFAULT 0x00 +#define SIG_IGNORE 0x01 + +int signal_remove_callback(int signum, int type); +int signal_add_callback(int signum, void (*callback)(void *privdata), void *privdata); + +int signal_init(void); + +#endif // _SIGNALS_H