Commit e55efaac authored by Hermann Mayer's avatar Hermann Mayer

[Utils][Config][Daemon] Added initial utils, a daemon specific configuration…

[Utils][Config][Daemon] Added initial utils, a daemon specific configuration parser and a test case for it. Also added a initial daemon stub.
parent 8ead6625
include Makefile.shared
BINS = $(patsubst src/test/%.c,%,$(wildcard src/test/*.c))
TEST_BINS = $(BINS:%=test-%)
TEST_MEM_BINS = $(BINS:%=test-mem-%)
MAN_PAGES = $(patsubst docs/man/%,%,$(wildcard docs/man/*.[0-9]))
DOCS = $(MAN_PAGES:%=docs-%)
build:
$(MAKE) build -C src/
$(MAKE) build -C docs/man/
clean:
$(MAKE) clean -C src/
$(MAKE) clean -C docs/man/
test:
$(MAKE) test -C src/test/
$(TEST_BINS):
$(MAKE) $@ -C src/test/
$(TEST_MEM_BINS):
$(MAKE) $@ -C src/test/
$(DOCS):
$(MAKE) $@ -C docs/man/
install:
$(MAKE) $@ -C src/
$(MAKE) $@ -C docs/man/
$(MAKE) $@ -C dist/
uninstall:
$(MAKE) $@ -C src/
$(MAKE) $@ -C docs/man/
$(MAKE) $@ -C dist/
all: build
.PHONY: all clean test
export TEST := test
export PRINTF := printf
export CP := cp
export MV := mv
export RM := rm
export FIND := find
export GCC := gcc
export MAN := man
export INSTALL := install
XML_CONF := xml2-config
export VERSION := $(shell cat VERSION)
export MACHTYPE := $(shell $(CC) -dumpmachine)
export CFLAGS = $(shell $(XML_CONF) --cflags) -Wall -Werror -g -O0 \
-DVERSION="\"$(VERSION)\"" -DMACHTYPE="\"$(MACHTYPE)\""
export INCLUDES = $(shell $(XML_CONF) --libs) -lconfig -lbcm2835 -lmicrohttpd
DESTDIR ?=
export prefix ?= /usr/local
export datarootdir ?= $(prefix)/share
export exec_prefix ?= $(prefix)
export bindir ?= $(exec_prefix)/bin
export mandir ?= $(datarootdir)/man
export sysconfdir ?= $(prefix)/etc
export libdir ?= $(exec_prefix)/lib
SUBDIRS ?= utils test bin
BUILD_SUBDIRS := $(SUBDIRS:%=build-%)
INSTALL_SUBDIRS := $(SUBDIRS:%=install-%)
UNINSTALL_SUBDIRS := $(SUBDIRS:%=uninstall-%)
CLEAN_SUBDIRS := $(SUBDIRS:%=clean-%)
OBJS := $(patsubst %.c,%.o,$(wildcard *.c))
CLEAN_OBJS := $(OBJS:%=clean-%)
$(OBJS): INFILE = $(@:%o=%c)
$(OBJS): OUTFILE = $(@)
$(OBJS):
$(TEST) -f $(OUTFILE) || \
$(GCC) -c $(INFILE) $(CFLAGS) -std=gnu11 $(INCLUDES)
$(CLEAN_OBJS): FILE = $(@:clean-%=%)
$(CLEAN_OBJS):
$(TEST) -f $(FILE) && \
$(RM) $(FILE) 2>/dev/null || true
$(BUILD_SUBDIRS): DIR = $(@:build-%=%)
$(BUILD_SUBDIRS):
$(MAKE) all -C $(DIR)
$(CLEAN_SUBDIRS): DIR = $(@:clean-%=%)
$(CLEAN_SUBDIRS):
$(MAKE) clean -C $(DIR)
clean: $(CLEAN_OBJS) $(CLEAN_SUBDIRS)
build: $(OBJS) $(BUILD_SUBDIRS)
$(INSTALL_SUBDIRS): DIR = $(@:install-%=%)
$(INSTALL_SUBDIRS):
$(MAKE) install -C $(DIR)
$(UNINSTALL_SUBDIRS): DIR = $(@:uninstall-%=%)
$(UNINSTALL_SUBDIRS):
$(MAKE) uninstall -C $(DIR)
install: build $(INSTALL_SUBDIRS)
uninstall: $(UNINSTALL_SUBDIRS)
all: build
.PHONY: all clean $(BUILD_SUBDIRS) $(CLEAN_SUBDIRS) $(OBJS)
OBJS = $(shell $(FIND) .. -type f -name *.o)
BINS = $(patsubst %.c,%,$(wildcard *.c))
CLEAN_BINS = $(BINS:%=clean-%)
INSTALL_BINS = $(BINS:%=install-%)
UNINSTALL_BINS = $(BINS:%=uninstall-%)
$(BINS): INFILE = $(@:%=%.c)
$(BINS):
ifeq ($(OBJS),)
$(MAKE) build -C ..
endif
$(TEST) -f $@ || \
$(GCC) -o $@ $(INFILE) $(OBJS) $(CFLAGS) -std=gnu11 $(INCLUDES)
$(CLEAN_BINS): BIN = $(@:clean-%=%)
$(CLEAN_BINS):
$(TEST) -f $(BIN) && \
$(RM) $(BIN) 2>/dev/null || true
clean: $(CLEAN_BINS)
build: $(BINS)
$(INSTALL_BINS): BIN = $(@:install-%=%)
$(INSTALL_BINS):
-$(TEST) -d $(DESTDIR)$(bindir) || \
$(INSTALL) -dm755 $(DESTDIR)$(bindir)
$(INSTALL) -m755 $(BIN) $(DESTDIR)$(bindir)
$(UNINSTALL_BINS): BIN = $(@:uninstall-%=%)
$(UNINSTALL_BINS):
-$(TEST) -f $(DESTDIR)$(bindir)/$(BIN) && \
$(RM) $(DESTDIR)$(bindir)/$(BIN)
install: $(INSTALL_BINS)
uninstall: $(UNINSTALL_BINS)
all: build
.PHONY: clean build
/*
* Copyright (c) 2015 Hermann Mayer
*
* relayd - The daemon
*
* This file is part of relayd.
*
* relayd 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 3 of the License, or
* (at your option) any later version.
*
* relayd 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 relayd. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <getopt.h>
#include <sys/stat.h>
#include "../utils/logger.h"
#include "../utils/pidfile.h"
#include "../utils/config.h"
static const char *pidfile = "/run/relayd.pid";
static struct config conf;
void handle_requests(struct config *conf)
{
while (1) {
sleep(1000);
}
}
void init_relays(struct config *conf)
{
}
void handle_signal(int signo)
{
if (SIGINT == signo || SIGTERM == signo) {
utlog(LOG_NOTICE, "Received SIGINT/TERM signal, shuting down..");
pidfile_remove(pidfile);
exit(EXIT_SUCCESS);
}
}
void daemonize()
{
pid_t pid;
// Fork off the parent process
pid = fork();
// An error occurred
if (pid < 0) {
exit(EXIT_FAILURE);
}
// Success: Let the parent terminate
if (pid > 0) {
exit(EXIT_SUCCESS);
}
// On success: The child process becomes session leader
if (setsid() < 0) {
exit(EXIT_FAILURE);
}
// Fork off for the second time
pid = fork();
// An error occurred
if (pid < 0) {
exit(EXIT_FAILURE);
}
// Success: Let the parent terminate
if (pid > 0) {
exit(EXIT_SUCCESS);
}
// Set new file permissions
umask(0);
// Change the working directory to the root directory
chdir("/");
// Close all open file descriptors
for (int i = sysconf(_SC_OPEN_MAX); i > 0; i--) {
close(i);
}
}
void print_version(int exit_code)
{
utlog(LOG_NOTICE, "relayd version %s (%s)\n",
VERSION, MACHTYPE);
utlog(LOG_NOTICE, "Copyright (C) 2015 Hermann Mayer\n");
utlog(LOG_NOTICE, "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n");
utlog(LOG_NOTICE, "\n");
utlog(LOG_NOTICE, "This is free software; you are free to change and redistribute it.\n");
utlog(LOG_NOTICE, "There is NO WARRANTY, to the extent permitted by law.\n");
free_config(&conf);
exit(exit_code);
}
void print_help(int exit_code)
{
utlog(LOG_INFO, "relayd [OPTION]\n");
utlog(LOG_INFO, "\n");
utlog(LOG_INFO, "Control connected relays.\n");
utlog(LOG_INFO, "\n");
utlog(LOG_INFO, " -h --help Show the available arguments\n");
utlog(LOG_INFO, " -c --config Set the path to a config file\n");
utlog(LOG_INFO, " -f --foreground Keep the daemon running in foreground\n");
exit(exit_code);
}
int main(int argc, char **argv)
{
if (1 == argc) {
utlog(LOG_ERR, "No configuration file was specified\n\n");
print_help(EXIT_FAILURE);
}
int c;
int foreground = 0;
char *config_file = "";
while (1) {
static struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'v'},
{"config", required_argument, 0, 'c'},
{"foreground", no_argument, 0, 'f'},
{0, 0, 0, 0}
};
// getopt_long stores the option index here.
int option_index = 0;
c = getopt_long(argc, argv, "hvfc:",
long_options, &option_index);
// Detect the end of the options.
if (c == -1) {
break;
}
switch (c) {
case 'h':
print_help(EXIT_SUCCESS);
break;
case 'v':
print_version(EXIT_SUCCESS);
break;
case 'c':
config_file = optarg;
break;
case 'f':
foreground = 1;
break;
case '?':
// getopt_long already printed an error message.
utlog(LOG_INFO, "\n");
print_help(EXIT_FAILURE);
break;
default:
break;
}
}
if (0 == strcmp("", config_file)) {
utlog(LOG_ERR, "No configuration file was specified.\n");
exit(EXIT_FAILURE);
}
pid_t pid;
if (0 != (pid = pidfile_check(pidfile))) {
utlog(LOG_ERR, "A instance of relayd (%d) is already running\n",
pid);
exit(EXIT_FAILURE);
}
conf = get_config(config_file);
validate_config(&conf);
// Run as daemon if we should
if (0 == foreground) {
daemonize();
utlog_mode(LOG_BACKGROUND);
utlog_pri_mode(LOG_PRI_DISABLE);
} else {
utlog_mode(LOG_FOREGROUND);
utlog_pri_mode(LOG_PRI_ENABLE);
}
// Write a pidfile for the current daemon process
if (0 == pidfile_write(pidfile)) {
exit(EXIT_FAILURE);
}
// Bind the signal handler
signal(SIGINT, handle_signal);
signal(SIGTERM, handle_signal);
// Initialize the relays
init_relays(&conf);
// Call the business logic loop
handle_requests(&conf);
return EXIT_SUCCESS;
}
VALGRIND := valgrind
OBJS = $(shell $(FIND) .. -type f -name *.o)
BINS = $(patsubst %.c,%.out,$(wildcard *.c))
TEST_BINS = $(BINS:%.out=test-%)
TEST_MEM_BINS = $(BINS:%.out=test-mem-%)
CLEAN_BINS = $(BINS:%.out=clean-%)
$(BINS): INFILE = $(@:%.out=%.c)
$(BINS):
ifeq ($(OBJS),)
$(MAKE) build -C ..
endif
$(TEST) -f $@ || \
$(GCC) -o $@ $(INFILE) $(OBJS) $(CFLAGS) -std=gnu11 $(INCLUDES)
$(TEST_BINS): BIN = $(@:test-%=%.out)
$(TEST_BINS): test-%:%.out
#
# Test $(BIN)
#
@./$(BIN) || true
@$(PRINTF) "\n\n"
$(TEST_MEM_BINS): BIN = $(@:test-mem-%=%.out)
$(TEST_MEM_BINS): test-mem-%:%.out
#
# Test $(BIN)
#
@$(VALGRIND) -v \
--track-origins=yes \
--leak-check=full \
./$(BIN)
@$(PRINTF) "\n\n"
$(CLEAN_BINS): BIN = $(@:clean-%=%.out)
$(CLEAN_BINS):
$(TEST) -f $(BIN) && \
$(RM) $(BIN) 2>/dev/null || true
clean: $(CLEAN_BINS)
test: $(TEST_BINS)
test-mem: $(TEST_MEM_BINS)
build: $(BINS)
install: ;
uninstall: ;
all: build
.PHONY: clean build
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include "../utils/config.h"
int main(void)
{
setlocale(LC_CTYPE, "");
const char *config_file = "../../dist/relayd.conf";
struct config c;
c = get_config(config_file);
printf("server.interface: %s\n", c.server.interface);
printf(" server.port: %d\n", c.server.port);
printf("\n");
for (int i = 0; i < c.relays.length; i++) {
printf(" gpio[%d]: %d\n", i, c.relays.gpio[i]);
}
free_config(&c);
return 0;
}
SUBDIRS :=
include ../Makefile
/*
* Copyright (c) 2015 Hermann Mayer
*
* Project utilities
*
* This file is part of relayd.
*
* relayd 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 3 of the License, or
* (at your option) any later version.
*
* relayd 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 relayd. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "logger.h"
/* Initialize a default config struct */
struct config* init_config(struct config *conf)
{
conf->server.interface = "0.0.0.0";
conf->server.port = 641;
for (int i = 0; i < MAX_RELAYS; i++) {
conf->relays.gpio[i] = 0;
}
conf->relays.gpio[0] = 25;
conf->relays.length = 1;
return conf;
}
/* Read in a configuration from a file and return the config struct */
struct config get_config(const char *path)
{
struct config conf;
struct config_t *c = (struct config_t*) malloc(sizeof(config_t));
config_setting_t *s;
int gpio_len = 0;
init_config(&conf);
config_init(c);
if (!config_read_file(c, path)) {
utlog(LOG_ERR, "\n%s:%d - %s", config_error_file(c),
config_error_line(c), config_error_text(c));
config_destroy(c);
exit(EXIT_FAILURE);
}
/*
* Server settings
*/
config_lookup_string(c, "interface", &conf.server.interface);
config_lookup_int(c, "port", &conf.server.port);
/*
* Relays settings
*/
s = config_lookup(c, "gpios");
gpio_len = config_setting_length(s);
for (int i = 0; i < gpio_len; i++) {
conf.relays.gpio[i] = config_setting_get_int_elem(s, i);
}
conf.ptr = c;
return conf;
}
/* Validate a given config struct */
void validate_config(struct config *conf)
{
int err_cnt = 0;
if (0 == strcmp("", conf->server.interface)) {
utlog(LOG_ERR, "No network interface was configured.\n");
err_cnt++;
}
if (0 == conf->server.port) {
utlog(LOG_ERR, "The server port can't be 0.\n");
err_cnt++;
}
if (err_cnt > 0) {
if (err_cnt > 1) {
utlog(LOG_ERR, "Found %d configuration errors.\n", err_cnt);
} else {
utlog(LOG_ERR, "Found one configuration error.\n");
}
exit(EXIT_FAILURE);
}
}
/* Free the config struct */
void free_config(struct config *conf)
{
if (NULL != conf->ptr) {
config_destroy(conf->ptr);
free(conf->ptr);
}
}
/*
* Copyright (c) 2015 Hermann Mayer
*
* Project utilities
*
* This file is part of relayd.
*
* relayd 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 3 of the License, or
* (at your option) any later version.
*
* relayd 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 relayd. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef UTILS_CONFIG_H
#define UTILS_CONFIG_H
#include <libconfig.h>
#define MAX_RELAYS 32
struct config {
struct config_server {
const char *interface;
int port;
} server;
struct config_relays {
int length;
int gpio[MAX_RELAYS];
} relays;
struct config_t *ptr;
};
/* Initialize a default config struct */
struct config* init_config(struct config*);
/* Read in a configuration from a file and return the config struct */
struct config get_config(const char*);
/* Validate a given config struct */
void validate_config(struct config*);
/* Free the config struct */
void free_config(struct config*);
#endif
/*
* Copyright (c) 2015 Hermann Mayer
*
* Project utilities
*
* This file is part of relayd.
*
* relayd 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 3 of the License, or
* (at your option) any later version.
*
* relayd 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 relayd. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <stdarg.h>
#define SYSLOG_NAMES
#include <sys/syslog.h>
#include "logger.h"
static int log_out_mode = LOG_FOREGROUND;
static int log_pri_mode = LOG_PRI_DISABLE;
/* Switch the logger output mode */
void utlog_mode(int mode)
{
log_out_mode = mode;
}
/* Switch the logger priority mode */
void utlog_pri_mode(int mode)
{
log_pri_mode = mode;
}
/* Log a message with a given priority, format and a variable list of arguments */
void utlog(int pri, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (LOG_FOREGROUND == log_out_mode) {
FILE *outfile = stdout;
if (pri <= LOG_WARNING) {
outfile = stderr;
}
if (LOG_PRI_ENABLE == log_pri_mode) {
char cur_timew[26];
time_t cur_time = time(NULL);
strftime(cur_timew, 26, "%Y-%m-%dT%T%z", localtime(&cur_time));
size_t len = strlen(fmt) + 26 + 11;
char *nfmt = (char*) malloc(sizeof(char) * len);
char *priname = (char*) malloc(sizeof(char) * 8);
memset(nfmt, 0, sizeof(char) * len);
memset(priname, 0, sizeof(char) * 8);
for(CODE* scode = prioritynames; NULL != (*scode).c_name; scode++) {
if (pri == scode->c_val) {
for (int i = 0; i < strlen(scode->c_name); i++) {
priname[i] = toupper(scode->c_name[i]);
}
}
}
snprintf(nfmt, len, "[%s][%7s] %s", cur_timew, priname, fmt);
vfprintf(outfile, nfmt, ap);
free(nfmt);
free(priname);
} else {
vfprintf(outfile, fmt, ap);
}
fflush(outfile);
}
if (LOG_BACKGROUND == log_out_mode) {
vsyslog(pri, fmt, ap);
}
va_end(ap);
}
/*
* Copyright (c) 2015 Hermann Mayer
*
* Project utilities
*
* This file is part of relayd.
*
* relayd 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 3 of the License, or
* (at your option) any later version.
*
* relayd 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 relayd. If not, see <http://www.gnu.org/licenses/>.