|
|
|
/*
|
|
|
|
* main.c: the daemon itself
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <syslog.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <signal.h>
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "trie.h"
|
|
|
|
#include "task.h"
|
|
|
|
#include "daemonize.h"
|
|
|
|
#include "async.h"
|
|
|
|
|
|
|
|
enum resp {
|
|
|
|
SUCCESS, FAIL
|
|
|
|
};
|
|
|
|
|
|
|
|
void reg_sig_handlers(void);
|
|
|
|
bool process(int fd);
|
|
|
|
|
|
|
|
int sockfd; //async.h has to be able to use it, and I would hate to write a getter for that
|
|
|
|
|
|
|
|
int main (void) {
|
|
|
|
openlog(cfg_log_ident, cfg_log_opt, cfg_log_facility); //I will probably close its fd a moment later, FIXME
|
|
|
|
syslog(cfg_log_facility | LOG_INFO, "Starting");
|
|
|
|
// No arguments, if you want something, go change source, according to documentation.
|
|
|
|
// daemonize(0);
|
|
|
|
|
|
|
|
// Load saved state -- variable values and tasks
|
|
|
|
async_load(0);
|
|
|
|
|
|
|
|
// Set basic signal handlers
|
|
|
|
reg_sig_handlers();
|
|
|
|
|
|
|
|
// Setup a socket to listen on
|
|
|
|
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
|
|
struct sockaddr_un addr;
|
|
|
|
socklen_t addrsize = sizeof(struct sockaddr_un);
|
|
|
|
addr.sun_family = AF_UNIX;
|
|
|
|
memcpy(&addr.sun_path, cfg_socket, strlen(cfg_socket)+1);
|
|
|
|
if(bind(sockfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1) {
|
|
|
|
syslog(cfg_log_facility | LOG_ERR, "daemon: bind failed: %m");
|
|
|
|
}
|
|
|
|
if(listen(sockfd, cfg_sock_maxclients) == -1) {
|
|
|
|
syslog(cfg_log_facility | LOG_ERR, "daemon: listen failed: %m");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Everything prepared
|
|
|
|
syslog(cfg_log_facility | LOG_INFO, "Started");
|
|
|
|
|
|
|
|
// Main loop: (everything else is handled by process() and signal handlers).
|
|
|
|
while (true) {
|
|
|
|
int fd = accept(sockfd, (struct sockaddr *) &addr, &addrsize);
|
|
|
|
if (fd == -1) {
|
|
|
|
syslog(cfg_log_facility | LOG_ERR, "daemon: accept failed: %m");
|
|
|
|
while(1);
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
syslog(cfg_log_facility | LOG_INFO, "daemon: accepted new connection");
|
|
|
|
process(fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
syslog(cfg_log_facility | LOG_CRIT, "Escaped infinite loop!");
|
|
|
|
return 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t read_uint64(int fd) {
|
|
|
|
char buf[8];
|
|
|
|
read(fd, buf, 8);
|
|
|
|
uint64_t num = *(uint64_t *)buf;
|
|
|
|
return num;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool write_uint64(uint64_t num, int fd) {
|
|
|
|
char *buf = #
|
|
|
|
write(fd, buf, 8);
|
|
|
|
return true; //TODO: error handling
|
|
|
|
}
|
|
|
|
|
|
|
|
char *read_string(int fd) {
|
|
|
|
char *buf;
|
|
|
|
struct grow *tmp = grow_init(true);
|
|
|
|
do {
|
|
|
|
buf = malloc(1);
|
|
|
|
if(buf == NULL) {
|
|
|
|
syslog(cfg_log_facility | LOG_ERR, "cannot malloc memory for a letter: %m");
|
|
|
|
grow_drop(tmp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
read(fd, buf, 1);
|
|
|
|
grow_push(buf, tmp);
|
|
|
|
} while (buf[0] != '\0');
|
|
|
|
char *str = malloc(tmp->elems * sizeof(char));
|
|
|
|
if (str == NULL) {
|
|
|
|
syslog(cfg_log_facility | LOG_ERR, "cannot malloc memory for string: %m");
|
|
|
|
grow_drop(tmp);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
for (uint64_t j = 0; j < tmp -> elems; j++) {
|
|
|
|
str[j] = *(char *)(tmp->arr[j]);
|
|
|
|
}
|
|
|
|
grow_drop(tmp);
|
|
|
|
syslog(cfg_log_facility | LOG_DEBUG, "daemon: read_string: returning '%s'", str);
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool write_string (char *str, int fd) {
|
|
|
|
int len = strlen(str)+1;
|
|
|
|
write(fd, str, len);
|
|
|
|
return true; // TODO: error handling
|
|
|
|
}
|
|
|
|
|
|
|
|
void response(enum resp r, int fd) {
|
|
|
|
if (r==SUCCESS) {
|
|
|
|
char str[] = "OK";
|
|
|
|
write(fd, str, 2);
|
|
|
|
} else {
|
|
|
|
char str[] = "KO";
|
|
|
|
write(fd, str, 2);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool process(int fd) {
|
|
|
|
char cmd[5];
|
|
|
|
read(fd, cmd, 4);
|
|
|
|
cmd[4] = '\0';
|
|
|
|
if (0){ //alignment
|
|
|
|
return true;
|
|
|
|
} else if (strcmp(cmd, "TADD") == 0) {
|
|
|
|
struct task *t = task_read(fd);
|
|
|
|
if (t == NULL) { // We do not handle errors here, they are already logged
|
|
|
|
response(FAIL,fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if(task_add(*t)) {
|
|
|
|
response(SUCCESS,fd);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
response(FAIL,fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (strcmp(cmd, "TDEL") == 0) {
|
|
|
|
uint64_t id = read_uint64(fd);
|
|
|
|
if(task_delete(id)) {
|
|
|
|
response(SUCCESS,fd);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
response(FAIL,fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (strcmp(cmd, "TENA") == 0) {
|
|
|
|
uint64_t id = read_uint64(fd);
|
|
|
|
if(task_enable(id)) {
|
|
|
|
response(SUCCESS,fd);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
response(FAIL,fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (strcmp(cmd, "TDIS") == 0) {
|
|
|
|
uint64_t id = read_uint64(fd);
|
|
|
|
if(task_disable(id)) {
|
|
|
|
response(SUCCESS,fd);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
response(FAIL,fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (strcmp(cmd, "TDET") == 0) {
|
|
|
|
uint64_t id = read_uint64(fd);
|
|
|
|
struct task t = task_details(id);
|
|
|
|
response(SUCCESS, fd);
|
|
|
|
return task_write(t, fd);
|
|
|
|
} else if (strcmp(cmd, "TLST") == 0) {
|
|
|
|
bool retval = true;
|
|
|
|
struct grow *lst = task_list();
|
|
|
|
if (lst == NULL) {
|
|
|
|
response(SUCCESS, fd);
|
|
|
|
retval &= write_uint64(0, fd);
|
|
|
|
} else {
|
|
|
|
response(SUCCESS,fd);
|
|
|
|
retval &= write_uint64(lst->active_elems, fd);
|
|
|
|
for (uint64_t i = 0; i < lst->elems; i++) {
|
|
|
|
if (lst->arr[i] == NULL) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
struct task t = *(struct task *)lst->arr[i];
|
|
|
|
retval &= task_write(t, fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
} else if (strcmp(cmd, "VADD") == 0) {
|
|
|
|
int64_t val = (int64_t) read_uint64(fd);
|
|
|
|
char *str = read_string(fd);
|
|
|
|
if(trie_set(str, val)) {
|
|
|
|
response(SUCCESS, fd);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
response(FAIL, fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (strcmp(cmd, "VSET") == 0) {
|
|
|
|
int64_t val = (int64_t) read_uint64(fd);
|
|
|
|
char *str = read_string(fd);
|
|
|
|
if(trie_set(str, val)) {
|
|
|
|
response(SUCCESS, fd);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
response(FAIL, fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (strcmp(cmd, "VDEL") == 0) {
|
|
|
|
char *str = read_string(fd);
|
|
|
|
if(trie_unset(str)) {
|
|
|
|
response(SUCCESS, fd);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
response(FAIL, fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (strcmp(cmd, "VLST") == 0) {
|
|
|
|
bool retval = true;
|
|
|
|
struct grow *lst = trie_list();
|
|
|
|
if (lst == NULL) {
|
|
|
|
response(SUCCESS, fd);
|
|
|
|
retval &= write_uint64(0, fd);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
response(SUCCESS, fd);
|
|
|
|
retval &= write_uint64(lst->elems, fd);
|
|
|
|
for (uint64_t i = 0; i < lst->elems; i++) {
|
|
|
|
struct trie_list item = *(struct trie_list *)lst->arr[i];
|
|
|
|
retval &= write_uint64(item.val, fd);
|
|
|
|
retval &= write_string(item.name, fd);
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
} else if (strcmp(cmd, "VDET") == 0) {
|
|
|
|
char *name = read_string(fd);
|
|
|
|
if (name == NULL) {
|
|
|
|
response(FAIL, fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
struct trie_retval var = trie_lookup(name);
|
|
|
|
response(SUCCESS, fd);
|
|
|
|
write(fd, &var, sizeof(struct trie_retval));
|
|
|
|
if (var.tasks == NULL) {
|
|
|
|
write_uint64(0, fd);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
write_uint64(var.tasks->active_elems, fd);
|
|
|
|
for(uint64_t i = 0; i< var.tasks->elems; i++) {
|
|
|
|
if(var.tasks->arr[i] == NULL) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
uint64_t task = *(uint64_t *)var.tasks->arr[i];
|
|
|
|
write_uint64(task, fd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true; // TODO: error handling!!
|
|
|
|
|
|
|
|
} else if (strcmp(cmd, "QUIT") == 0) {
|
|
|
|
response(SUCCESS, fd);
|
|
|
|
async_shutdown(0);
|
|
|
|
return true;
|
|
|
|
} else if (strcmp(cmd, "REST") == 0) {
|
|
|
|
response(SUCCESS, fd);
|
|
|
|
async_restart(0);
|
|
|
|
return true;
|
|
|
|
} else if (strcmp(cmd, "SAVE") == 0) {
|
|
|
|
response(SUCCESS, fd);
|
|
|
|
async_save(0);
|
|
|
|
return true;
|
|
|
|
} else if (strcmp(cmd, "FLSH") == 0) {
|
|
|
|
char *safe = calloc(1+strlen(cfg_safe_string), sizeof(char));
|
|
|
|
read(fd, safe, strlen(cfg_safe_string));
|
|
|
|
if (strcmp(safe, cfg_safe_string) == 0) {
|
|
|
|
response(SUCCESS, fd);
|
|
|
|
async_flush(0);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
response(FAIL, fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (strcmp(cmd, "LOAD") == 0) {
|
|
|
|
char *safe = calloc(1+strlen(cfg_safe_string), sizeof(char));
|
|
|
|
read(fd, safe, strlen(cfg_safe_string));
|
|
|
|
if (strcmp(safe, cfg_safe_string) == 0) {
|
|
|
|
response(SUCCESS, fd);
|
|
|
|
async_load(0);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
response(FAIL, fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (strcmp(cmd, "KILL") == 0) {
|
|
|
|
char *safe = calloc(1+strlen(cfg_safe_string), sizeof(char));
|
|
|
|
read(fd, safe, strlen(cfg_safe_string));
|
|
|
|
if (strcmp(safe, cfg_safe_string) == 0) {
|
|
|
|
response(SUCCESS, fd);
|
|
|
|
async_kill(0);
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
response(FAIL, fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
syslog(cfg_log_facility | LOG_NOTICE, "Unknown command: %s (0x%02X%02X%02X%02X)", cmd, cmd[0], cmd[1], cmd[2], cmd[3]);
|
|
|
|
char str[] = "KO";
|
|
|
|
write(fd, str, 2);
|
|
|
|
response(FAIL,fd);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void reg_sig_handlers(void) {
|
|
|
|
sigset_t empty;
|
|
|
|
sigemptyset(&empty); // Shall not fail
|
|
|
|
|
|
|
|
//SIGTERM
|
|
|
|
struct sigaction term_sigh;
|
|
|
|
term_sigh.sa_handler = &async_shutdown;
|
|
|
|
term_sigh.sa_mask = empty;
|
|
|
|
term_sigh.sa_flags = 0;
|
|
|
|
sigaction(SIGTERM, &term_sigh, NULL);
|
|
|
|
|
|
|
|
//SIGCHLD
|
|
|
|
struct sigaction chld_sigh;
|
|
|
|
chld_sigh.sa_handler = SIG_IGN; // We don't care for children
|
|
|
|
chld_sigh.sa_mask = empty;
|
|
|
|
chld_sigh.sa_flags = SA_NOCLDWAIT;
|
|
|
|
sigaction(SIGCHLD, &chld_sigh, NULL);
|
|
|
|
|
|
|
|
//SIGHUP
|
|
|
|
struct sigaction hup_sigh;
|
|
|
|
hup_sigh.sa_handler = &async_restart;
|
|
|
|
hup_sigh.sa_mask = empty;
|
|
|
|
hup_sigh.sa_flags = 0;
|
|
|
|
sigaction(SIGHUP, &hup_sigh, NULL);
|
|
|
|
|
|
|
|
//SIGINT
|
|
|
|
struct sigaction int_sigh;
|
|
|
|
int_sigh.sa_handler = &async_save;
|
|
|
|
int_sigh.sa_mask = empty;
|
|
|
|
int_sigh.sa_flags = 0;
|
|
|
|
sigaction(SIGINT, &int_sigh, NULL);
|
|
|
|
|
|
|
|
//SIGALRM
|
|
|
|
struct sigaction alrm_sigh;
|
|
|
|
alrm_sigh.sa_sigaction = &async_run;
|
|
|
|
alrm_sigh.sa_mask = empty;
|
|
|
|
alrm_sigh.sa_flags = SA_SIGINFO;
|
|
|
|
sigaction(SIGALRM, &alrm_sigh, NULL);
|
|
|
|
}
|