Browse Source

add daemon mode

master
Olaf Rempel 9 years ago
parent
commit
bc54aecceb
11 changed files with 757 additions and 47 deletions
  1. +0
    -1
      TODO
  2. +288
    -0
      configfile.c
  3. +28
    -0
      configfile.h
  4. +33
    -33
      event.c
  5. +3
    -1
      event.h
  6. +11
    -7
      logging.h
  7. +73
    -0
      pidfile.c
  8. +11
    -0
      pidfile.h
  9. +147
    -5
      qnaplcd.c
  10. +151
    -0
      signals.c
  11. +12
    -0
      signals.h

+ 0
- 1
TODO View File

@@ -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


+ 288
- 0
configfile.c View File

@@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>

#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(&section->list);
INIT_LIST_HEAD(&section->tupel_list);

section->name = strdup(name);
if (section->name == NULL) {
free(section);
return NULL;
}

list_add_tail(&section->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, &section->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, &section->tupel_list, list) {
list_del(&tupel->list);
free((char *)tupel->option);
free((char *)tupel->parameter);
free(tupel);
}
list_del(&section->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, &section->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, &section->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, &section->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;
}

+ 28
- 0
configfile.h View File

@@ -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_ */

+ 33
- 33
event.c View File

@@ -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;
}

/* exit loop if callback returns true */
if (post_select_cb != NULL && post_select_cb(retval, (void *)&readfds, (void *)&writefds, privdata) != 0)
break;

} else if (i == 0) {
/* 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;
}

+ 3
- 1
event.h View File

@@ -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_ */

+ 11
- 7
logging.h View File

@@ -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);


+ 73
- 0
pidfile.c View File

@@ -0,0 +1,73 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>

#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;
}

+ 11
- 0
pidfile.h View File

@@ -0,0 +1,11 @@
#ifndef _PIDFILE_H_
#define _PIDFILE_H_

#include <sys/types.h>

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_

+ 147
- 5
qnaplcd.c View File

@@ -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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <signal.h>

#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)
{
int *restart = (int *)privdata;
*restart = 1;
}

int check_restart(int *maxfd, void *readfds, void *writefds, struct timeval *timeout, void *privdata)
{
if (lcd_init(LCD_DEVICE, LCD_TIMEOUT, &button_callback) < 0)
return -1;
int *restart = (int *)privdata;
if (*restart == 1) {
*restart = 0;
return 1;
}

event_loop(NULL, NULL);
return 0;
}

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;
}

+ 151
- 0
signals.c View File

@@ -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 <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 *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;
}

+ 12
- 0
signals.h View File

@@ -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

Loading…
Cancel
Save