add daemon mode
This commit is contained in:
parent
6588d33321
commit
bc54aecceb
1
TODO
1
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
|
||||
|
288
configfile.c
Normal file
288
configfile.c
Normal 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(§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;
|
||||
}
|
28
configfile.h
Normal file
28
configfile.h
Normal 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_ */
|
68
event.c
68
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;
|
||||
}
|
||||
|
4
event.h
4
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_ */
|
||||
|
18
logging.h
18
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);
|
||||
|
73
pidfile.c
Normal file
73
pidfile.c
Normal 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
pidfile.h
Normal file
11
pidfile.h
Normal 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_
|
152
qnaplcd.c
152
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 <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[])
|
||||
{
|
||||
if (lcd_init(LCD_DEVICE, LCD_TIMEOUT, &button_callback) < 0)
|
||||
return -1;
|
||||
static int restart_var;
|
||||
|
||||
event_loop(NULL, NULL);
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
151
signals.c
Normal file
151
signals.c
Normal 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
signals.h
Normal file
12
signals.h
Normal 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…
Reference in New Issue
Block a user