add daemon mode
This commit is contained in:
parent
6588d33321
commit
bc54aecceb
1
TODO
1
TODO
@ -1,7 +1,6 @@
|
|||||||
- dynamic pages
|
- dynamic pages
|
||||||
- refresh of dynamic pages (clock, loadavg)
|
- refresh of dynamic pages (clock, loadavg)
|
||||||
- scrolling
|
- scrolling
|
||||||
- daemonize: pid/log/config-file
|
|
||||||
|
|
||||||
- disk utilization (MB/GB)
|
- disk utilization (MB/GB)
|
||||||
- mntpoints given in config
|
- 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;
|
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) {
|
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)) {
|
if (!list_empty(&event_timeout_list)) {
|
||||||
struct timeval now;
|
struct timeval now;
|
||||||
gettimeofday(&now, NULL);
|
gettimeofday(&now, NULL);
|
||||||
@ -242,14 +243,16 @@ int event_loop(int (*callback)(void *privdata), void *privdata)
|
|||||||
|
|
||||||
/* calc select() timeout */
|
/* calc select() timeout */
|
||||||
sub_timeval(&timeout, &entry->nextrun, &now);
|
sub_timeval(&timeout, &entry->nextrun, &now);
|
||||||
timeout_p = &timeout;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fd_set *readfds = NULL, *writefds = NULL;
|
|
||||||
struct event_fd *entry, *tmp;
|
struct event_fd *entry, *tmp;
|
||||||
int maxfd = -1;
|
int maxfd = -1;
|
||||||
|
|
||||||
|
fd_set readfds, writefds;
|
||||||
|
FD_ZERO(&readfds);
|
||||||
|
FD_ZERO(&writefds);
|
||||||
|
|
||||||
list_for_each_entry_safe(entry, tmp, &event_fd_list, list) {
|
list_for_each_entry_safe(entry, tmp, &event_fd_list, list) {
|
||||||
entry->flags &= ~EVENT_NEW;
|
entry->flags &= ~EVENT_NEW;
|
||||||
|
|
||||||
@ -259,52 +262,49 @@ int event_loop(int (*callback)(void *privdata), void *privdata)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry->flags & FD_READ) {
|
if (entry->flags & FD_READ)
|
||||||
if (readfds == NULL) {
|
FD_SET(entry->fd, &readfds);
|
||||||
readfds = &fdsets[0];
|
|
||||||
FD_ZERO(readfds);
|
|
||||||
}
|
|
||||||
FD_SET(entry->fd, readfds);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry->flags & FD_WRITE) {
|
if (entry->flags & FD_WRITE)
|
||||||
if (writefds == NULL) {
|
FD_SET(entry->fd, &writefds);
|
||||||
writefds = &fdsets[1];
|
|
||||||
FD_ZERO(writefds);
|
|
||||||
}
|
|
||||||
FD_SET(entry->fd, writefds);
|
|
||||||
}
|
|
||||||
|
|
||||||
maxfd = (entry->fd > maxfd) ? entry->fd : maxfd;
|
maxfd = (entry->fd > maxfd) ? entry->fd : maxfd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maxfd++;
|
||||||
|
|
||||||
/* exit loop if callback returns true */
|
/* 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;
|
break;
|
||||||
|
|
||||||
int i = select(maxfd +1, readfds, writefds, NULL, timeout_p);
|
int retval = select(maxfd, &readfds, &writefds, NULL, &timeout);
|
||||||
if (i < 0 && errno == EINTR) {
|
if (retval < 0 && errno == EINTR) {
|
||||||
errno = 0;
|
errno = 0;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
} else if (i < 0) {
|
} else if (retval < 0) {
|
||||||
log_print(LOG_ERROR, "event_loop(): select():");
|
log_print(LOG_ERROR, "event_loop(): select():");
|
||||||
continue;
|
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) {
|
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)
|
if (entry->read_cb(entry->fd, entry->read_priv) != 0)
|
||||||
entry->flags |= EVENT_DELETE;
|
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)
|
if (entry->write_cb(entry->fd, entry->write_priv) != 0)
|
||||||
entry->flags |= EVENT_DELETE;
|
entry->flags |= EVENT_DELETE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(fdsets);
|
|
||||||
return 0;
|
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);
|
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_ */
|
#endif /* _EVENT_H_ */
|
||||||
|
18
logging.h
18
logging.h
@ -1,14 +1,18 @@
|
|||||||
#ifndef _LOGGING_H_
|
#ifndef _LOGGING_H_
|
||||||
#define _LOGGING_H_
|
#define _LOGGING_H_
|
||||||
|
|
||||||
#define LOG_DEBUG 5
|
#define LOG_EMERG 0 /* system is unusable */
|
||||||
#define LOG_INFO 4
|
#define LOG_ALERT 1 /* action must be taken immediately */
|
||||||
#define LOG_NOTICE 3
|
#define LOG_CRIT 2 /* critical conditions */
|
||||||
#define LOG_WARN 2
|
#define LOG_ERR 3 /* error conditions */
|
||||||
#define LOG_ERROR 1
|
#define LOG_WARNING 4 /* warning conditions */
|
||||||
#define LOG_CRIT 0
|
#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);
|
int log_init(const char *logfile);
|
||||||
void log_close(void);
|
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_
|
156
qnaplcd.c
156
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 <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <string.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 "event.h"
|
||||||
#include "lcd.h"
|
#include "lcd.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
|
#include "pidfile.h"
|
||||||
|
#include "signals.h"
|
||||||
|
|
||||||
#define LCD_DEVICE "/dev/ttyS0"
|
#define LCD_DEVICE "/dev/ttyS0"
|
||||||
#define LCD_TIMEOUT 10
|
#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
|
#define PAGE_MAX 7
|
||||||
|
|
||||||
static int page;
|
static int page;
|
||||||
@ -60,12 +98,116 @@ static int button_callback(struct lcddev *dev, int button)
|
|||||||
return 0;
|
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)
|
int *restart = (int *)privdata;
|
||||||
return -1;
|
*restart = 1;
|
||||||
|
}
|
||||||
event_loop(NULL, NULL);
|
|
||||||
|
int check_restart(int *maxfd, void *readfds, void *writefds, struct timeval *timeout, void *privdata)
|
||||||
return 0;
|
{
|
||||||
|
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