diff --git a/async.c b/async.c new file mode 100644 index 0000000..1f8036e --- /dev/null +++ b/async.c @@ -0,0 +1,90 @@ +/* + * async.c - functions to be called asynchronously (in signal handlers) + * also functions for non-standard operations over whole daemon + */ + +#include +#include +#include + +#include "async.h" +#include "task.h" +#include "trie.h" +#include "config.h" + +extern int sockfd; //in daemon.c + +void async_kill(int sig) { + syslog(cfg_log_facility | LOG_INFO, "Stopping"); + exit(0); + return; +} + +void async_shutdown (int sig) { + async_save(sig); + close(sockfd); + if(unlink(cfg_socket) == -1){ + syslog(cfg_log_facility | LOG_ERR, "async: unlink failed: %m"); + } + async_kill(sig); + return; +} + +void async_save(int sig) { + task_save(cfg_task_file); + trie_save(cfg_trie_file); + return; +} + +void async_flush(int sig) { + task_flush(); + trie_flush(); + return; +} + +void async_load (int sig) { + task_load(cfg_task_file); + trie_load(cfg_trie_file); + return; +} + +void async_restart (int sig) { + async_save(sig); + async_flush(sig); + async_load(sig); +} + +void async_run (int sig, siginfo_t *info, void *unused) { + if (info == NULL) { + syslog(cfg_log_facility | LOG_ERR, "async: someone is kidding us, we will kid them back and do nothing"); + return; + } + uint64_t *p_id = (info -> si_value).sival_ptr; + if (p_id == NULL) { + syslog(cfg_log_facility | LOG_ERR, "async: someone is kidding us again, we will kid them back and do nothing"); + return; + } + struct task t = task_details(*p_id); + // And now run it + // First, fork + pid_t pid = fork(); + if (pid == -1) { + syslog(cfg_log_facility | LOG_ERR, "async: cannot fork: %m"); + return; + } + else if (pid > 0) { + //We said in daemon.h that we don't care for children + return; + } + else if (pid == 0) { + // Reset signal handler for SIGCHLD (other signals are defined by us and will be reset during exec() + struct sigaction siga; + siga.sa_handler = SIG_DFL; + sigaction(SIGCHLD, &siga, NULL); + + // Exec + execvp(t.argv[0], t.argv); + syslog(cfg_log_facility | LOG_ERR, "async: cannot exec: %m"); + return; + } +} diff --git a/async.h b/async.h new file mode 100644 index 0000000..a07fad7 --- /dev/null +++ b/async.h @@ -0,0 +1,17 @@ +/* + * async.h - declaration of functions in async.c + */ + +#ifndef ASYNC_H +#define ASYNC_H + +#include +void async_kill(int sig); +void async_shutdown (int sig); +void async_save(int sig); +void async_flush(int sig); +void async_load (int sig); +void async_restart (int sig); +void async_run (int sig, siginfo_t *info, void *unused); + +#endif diff --git a/config.c b/config.c new file mode 100644 index 0000000..dbcb360 --- /dev/null +++ b/config.c @@ -0,0 +1,31 @@ +/* + * config.h: compilation config file + * Edit as needed. + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include +#include +#include +#include + +const int cfg_log_facility = LOG_CRON; +const char cfg_log_ident[] = "kairos"; +const int cfg_log_opt = LOG_PID | LOG_NDELAY; + +const char cfg_trie_file[] = "/home/ledoian/.kairos/trie"; +const char cfg_task_file[] = "/home/ledoian/.kairos/tasks"; +const char cfg_socket[] = "/tmp/kairos.sock"; + +const uint32_t cfg_sock_maxclients = 16; +const uint64_t cfg_var_name_max_len = 16; + +const clockid_t cfg_clockid = CLOCK_REALTIME; // select CLOCK_REALTIME_ALARM + // for waking system from suspend; + // requires CAP_WAKE_ALARM + +const char cfg_safe_string[] = "Sure"; + +#endif diff --git a/config.h b/config.h index 9ebad43..f297a62 100644 --- a/config.h +++ b/config.h @@ -7,16 +7,21 @@ #define CONFIG_H #include +#include -const int cfg_log_facility = LOG_CRON; -const char cfg_log_ident[] = "kairos"; -const int cfg_log_opt = LOG_PID | LOG_NDELAY; +extern const int cfg_log_facility; +extern const char cfg_log_ident[]; +extern const int cfg_log_opt; -const char cfg_trie_file[] = "/home/ledoian/.kairos/trie"; -const char cfg_task_file[] = "/home/ledoian/.kairos/tasks"; -const char cfg_socket[] = "/tmp/kairos.sock"; +extern const char cfg_trie_file[]; +extern const char cfg_task_file[]; +extern const char cfg_socket[]; -const uint32_t cfg_sock_maxclients = 16; -const uint64_t cfg_var_name_max_len = 16; +extern const uint32_t cfg_sock_maxclients; +extern const uint64_t cfg_var_name_max_len; + +extern const clockid_t cfg_clockid; + +extern const char cfg_safe_string[]; #endif diff --git a/daemon.c b/daemon.c index ac3c57b..61eae7e 100644 --- a/daemon.c +++ b/daemon.c @@ -15,11 +15,16 @@ #include "trie.h" #include "task.h" #include "daemonize.h" +#include "async.h" -void term_handle(int sigval); +enum resp { + SUCCESS, FAIL +}; + +void reg_sig_handlers(void); bool process(int fd); -static int sockfd; +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 @@ -27,33 +32,20 @@ int main (void) { daemonize(0); // Load saved state -- variable values and tasks - trie_load(cfg_trie_file); - task_load(cfg_task_file); + async_load(0); // Set basic signal handlers - sigset_t empty; - sigemptyset(&empty); // Shall not fail - struct sigaction *term_sigh = (struct sigaction *) malloc(sizeof(struct sigaction)); - term_sigh -> sa_handler = &term_handle; - term_sigh -> sa_mask = empty; - term_sigh -> sa_flags = 0; - sigaction(SIGTERM, term_sigh, NULL); - - struct sigaction *chld_sigh = (struct sigaction *) malloc(sizeof(struct sigaction)); - 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); + 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)); + memcpy(&addr.sun_path, cfg_socket, strlen(cfg_socket)+1); bind(sockfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)); listen(sockfd, cfg_sock_maxclients); - + // Everything prepared syslog(cfg_log_facility | LOG_INFO, "Started"); @@ -67,13 +59,284 @@ int main (void) { return 5; } -void term_handle (int signum) { - trie_save(cfg_trie_file); - task_save(cfg_task_file); - close(sockfd); - unlink(cfg_socket); - exit(0); +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[1]; + struct grow *tmp = grow_init(true); + do { + 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"); + return NULL; + } + for (uint64_t j = 0; j < tmp -> elems; j++) { + str[j] = *(char *)(tmp->arr[j]); + } + grow_drop(tmp); + 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) {} +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 = read_string(fd); + 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 = read_string(fd); + 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 = read_string(fd); + 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); +} diff --git a/task.c b/task.c index a79abe1..0144749 100644 --- a/task.c +++ b/task.c @@ -5,16 +5,40 @@ #include #include #include +#include +#include +#include +#include +#include +#include #include "task.h" #include "grow.h" -#include "async.h" +#include "config.h" +#include "rpn.h" static struct grow *tasks = NULL; +static struct itimerspec task_update_times(struct itimerspec times) { + times.it_interval.tv_nsec = times.it_value.tv_nsec = 0; // We are only precise to a second + struct timespec cur_time; + if(clock_gettime(cfg_clockid, &cur_time) == -1) { + syslog(cfg_log_facility | LOG_WARNING, "task: clock_gettime failed: %m"); + syslog(cfg_log_facility | LOG_NOTICE, "task: not updating time, weird things may happen"); + } else { + time_t modulo = times.it_value.tv_sec % times.it_interval.tv_sec; + if (cur_time.tv_nsec >= 500000000) cur_time.tv_sec++; //round + time_t current_modulo = cur_time.tv_sec % times.it_interval.tv_sec; + if (modulo < current_modulo) modulo += times.it_interval.tv_sec; + times.it_value.tv_sec = cur_time.tv_sec + (modulo - current_modulo); //Nearest future moment of running + } + return times; +} bool task_add(struct task t) { +// struct task t = task_convert(ts); bool retval = true; + t.times = task_update_times(t.times); struct task *p_t = malloc(sizeof(struct task)); if(p_t == NULL) { syslog(cfg_log_facility | LOG_ERR, "task: could not allocate space for task: %m"); @@ -22,26 +46,267 @@ bool task_add(struct task t) { } *p_t = t; if (tasks == NULL) { - tasks = grow_init(true); + tasks = grow_init(false); //We will have to clear for ourself, cannot do automatically without mem leak. if (tasks == NULL) { return false; } + // Task number 0 is reserved (for errors) + retval &= grow_push(NULL, tasks); + } + t.id = tasks -> elems; // This relies on implementation detail of our growing array. + if (t.when == VARCHG) { + t.run = rpn_eval_assoc(t.formula, t.id); } - uint64_t id = tasks -> elems; // This relies on implementation detail of our growing array. retval &= grow_push(p_t, tasks); // Create a timer for the task - // store timerid in ((struct task *)(tasks -> arr[id])) -> timerid + // store timerid in ((struct task *)(tasks -> arr[t.id])) -> timerid + struct sigevent evp; + evp.sigev_notify = SIGEV_SIGNAL; + evp.sigev_signo = SIGALRM; + evp.sigev_value.sival_ptr = tasks -> arr[t.id]; + if(timer_create(cfg_clockid, &evp, &(((struct task *)(tasks -> arr[t.id])) -> timerid)) == -1){ + syslog(cfg_log_facility | LOG_ERR, "task: could not create timer for task %ld: %m", t.id); + return false; + } + + // Arm the timer + if (t.enabled) { + if (timer_settime(t.timerid, TIMER_ABSTIME, &t.times, NULL) == -1) { + syslog(cfg_log_facility | LOG_ERR, "task: could not arm timer for task %ld: %m", t.id); + return false; + } + } + return retval; } -bool task_disable(uint64_t id); -bool task_enable(uint64_t id); -bool task_delete(uint64_t id); +bool task_disable(uint64_t id) { + if (id >= tasks->elems) { + syslog(cfg_log_facility | LOG_WARNING, "task: attempt to disable non-existent task: %ld", id); + return false; + } + struct task *t =(struct task *) tasks->arr[id]; + if (t == NULL) { + syslog(cfg_log_facility | LOG_WARNING, "task: attempt to disable non-existent task: %ld", id); + return false; + } + t -> enabled = false; + return true; +} +bool task_enable(uint64_t id) { + if (id >= tasks->elems) { + syslog(cfg_log_facility | LOG_WARNING, "task: attempt to enable non-existent task: %ld", id); + return false; + } + struct task *t =(struct task *) tasks->arr[id]; + if (t == NULL) { + syslog(cfg_log_facility | LOG_WARNING, "task: attempt to enable non-existent task: %ld", id); + return false; + } + t -> enabled = true; + return true; +} -struct grow *task_list(void); -struct task task_details(uint64_t id); -void task_recalc(uint64_t id); +bool task_delete(uint64_t id) { + if (id >= tasks->elems) { + syslog(cfg_log_facility | LOG_NOTICE, "task: attempt to delete non-existent task: %ld", id); + return true; + } + struct task *t =(struct task *) tasks->arr[id]; + if (t == NULL) { + syslog(cfg_log_facility | LOG_NOTICE, "task: attempt to delete non-existent task: %ld", id); + return true; + } + //disarm + timer_delete(t->timerid); //if it fails, it means the timer didn't exist + //.argv** + for (int i = 0; t->argv[i] != NULL; i++){ + free(t->argv[i]); + } + free(t->argv); + // unassociate + if (t->when == VARCHG) { + rpn_eval_unassoc(t->formula, t->id); + } + //.formula + free(t->formula); + //task + free(t); + // delete from list + tasks -> active_elems--; + tasks -> arr[id] = NULL; + return true; +} + +struct grow *task_list(void) { + return tasks; +} + +struct task task_details(uint64_t id) { + if (id >= tasks -> elems) { + struct task t; + t.id = 0; //error task + return t; + } + if (tasks->arr[id] != NULL) { + return *(struct task *)(tasks -> arr[id]); + } else { + struct task t; + t.id = 0; //error task + return t; + } +} -bool task_load(void); -bool task_save(void); -void task_flush(void); +bool task_recalc(uint64_t id) { + if (id >= tasks -> elems) { + syslog(cfg_log_facility | LOG_WARNING, "task: attempt to recalculate non-existent task: %ld", id); + return false; + } + if (tasks -> arr[id] == NULL) { + syslog(cfg_log_facility | LOG_WARNING, "task: attempt to recalculate non-existent task: %ld", id); + return false; + } + if (((struct task *)(tasks -> arr[id])) -> when != VARCHG) { + syslog(cfg_log_facility | LOG_WARNING, "task: attempt to recalculate task which is not precalculated: %ld", id); + return false; + } + ((struct task *)(tasks -> arr[id])) -> run = rpn_eval(((struct task *)(tasks -> arr[id])) -> formula); + return true; +} + +bool task_write(struct task t, int fd) { + bool retval = true; + if(write(fd, &t, sizeof(struct task)) == -1) { + syslog(cfg_log_facility | LOG_ERR, "task: could not write() task: %m"); + return false; + } + for (int i = 0; i < t.argc; i++) { + if (t.argv[i] == NULL) { + syslog(cfg_log_facility | LOG_WARNING, "task: attempt to write malformed task"); + char emptystring = '\0'; + write(fd, &emptystring, 1); //If there actually was some logic to it. + retval = false; + } else { + int len = strlen(t.argv[i]) + 1; + if(write(fd, t.argv[i], len) == -1) { + syslog(cfg_log_facility | LOG_ERR, "task: write() failed: %m"); + retval = false; + } + } + } + if (t.formula == NULL) { + syslog(cfg_log_facility | LOG_WARNING, "task: attempt to write malformed formula"); + char emptystring = '\0'; + write(fd, &emptystring, 1); //If there actually was some logic to it. + retval = false; + } else { + int len = strlen(t.formula) + 1; + if(write(fd, t.formula, len) == -1) { + syslog(cfg_log_facility | LOG_ERR, "task: write() failed: %m"); + retval = false; + } + } + return retval; +} + +struct task *task_read(int fd) { + struct task *t = malloc(sizeof(struct task)); + if (t == NULL) { + syslog(cfg_log_facility | LOG_ERR, "task: cannot malloc memory for a task: %m"); + return NULL; + } + errno = 0; + ssize_t readbytes; + readbytes = read(fd, t, sizeof(struct task)); + if (readbytes < (ssize_t)sizeof(struct task)) { + syslog(cfg_log_facility | LOG_ERR, "task: unexpected EOF when reading task (or error: %m)"); + return NULL; + } + t->argv = malloc((t->argc + 1) * sizeof(char *)); + if (t->argv == NULL) { + syslog(cfg_log_facility | LOG_ERR, "task: cannot malloc memory for task arguments: %m"); + return NULL; + } + t->argv[t->argc+1] = NULL; //for execvp() + for (int i = 0; i < t->argc; i++) { + // This should be done in some more clever way + char buf[1]; + struct grow *tmp = grow_init(true); + do { + read(fd, buf, 1); + grow_push(buf, tmp); + } while (buf[0] != '\0'); + t->argv[i] = malloc(tmp->elems * sizeof(char)); + if (t->argv[i] == NULL) { + syslog(cfg_log_facility | LOG_ERR, "task: cannot malloc memory for task argument: %m"); + return NULL; + } + for (uint64_t j = 0; j < tmp -> elems; j++) { + t->argv[i][j] = *(char *)(tmp->arr[j]); + } + grow_drop(tmp); + } + // and one more time for the formula + char buf[1]; + struct grow *tmp = grow_init(true); + do { + read(fd, buf, 1); + grow_push(buf, tmp); + } while (buf[0] != '\0'); + t->formula = malloc(tmp->elems * sizeof(char)); + if (t->formula == NULL) { + syslog(cfg_log_facility | LOG_ERR, "task: cannot malloc memory for task formula: %m"); + return NULL; + } + for (uint64_t j = 0; j < tmp -> elems; j++) { + t->formula[j] = *(char *)(tmp->arr[j]); + } + grow_drop(tmp); + return t; +} + + +bool task_load(const char fn[]) { + bool retval = true; + int fd = open(fn, O_RDONLY); + if (fd == -1) { + syslog(cfg_log_facility | LOG_WARNING, "task: could not load tasks from file: %m"); + return false; + } + + struct task *t; + while ((t = task_read(fd)) != NULL) { + task_add(*t); + } + close(fd); + return retval; +} + +bool task_save(const char fn[]) { + bool retval = true; + int fd = open(fn, O_CREAT | O_WRONLY | O_TRUNC); + if (fd == -1) { + syslog(cfg_log_facility | LOG_WARNING, "trie: could not save tasks to file: %m"); + return false; + } + for (uint64_t i = 0; i < tasks -> elems; i++) { + if (tasks -> arr[i] == NULL) continue; + struct task t = *(struct task *)(tasks->arr[i]); + retval &= task_write(t, fd); + } + close(fd); + return retval; +} +void task_flush(void) { + for (uint64_t i = 0; i < tasks->elems; i++) { + free(((struct task *)(tasks->arr[i]))->formula); + for (int j = 0; ((struct task *)(tasks->arr[i]))->argv[j] != NULL; j++) { + free(((struct task *)(tasks->arr[i]))->argv[j]); + } + free(((struct task *)(tasks->arr[i]))->argv); + free(tasks->arr[i]); + } + grow_drop(tasks); + tasks = NULL; + return; +} diff --git a/task.h b/task.h index 96a4f22..3f9a792 100644 --- a/task.h +++ b/task.h @@ -15,17 +15,20 @@ enum task_check { // whether to check formula in signal handler or when variable }; struct task { + uint64_t id; + // Time info struct itimerspec times; timer_t timerid; // Command to run + int argc; // count of elements in argv 'true' has 1, 'echo Lorem Ipsum' has 3, 'echo Lorem\ ipsum' has 2. char **argv; //NULL terminated list of NUL terminated strings // Formula to be checked when running char *formula; // in RPN, NUL terminated enum task_check when; - bool run; // dynamic programming + bool run; // whether to run task in case of when == VARCHG // Disabling/enabling bool enabled; @@ -38,10 +41,13 @@ bool task_delete(uint64_t id); struct grow *task_list(void); struct task task_details(uint64_t id); -void task_recalc(uint64_t id); +bool task_recalc(uint64_t id); + +bool task_write(struct task t, int fd); +struct task *task_read(int fd); -bool task_load(void); -bool task_save(void); +bool task_load(const char fn[]); +bool task_save(const char fn[]); void task_flush(void); #endif diff --git a/trie.c b/trie.c index b0758c9..2859a67 100644 --- a/trie.c +++ b/trie.c @@ -125,6 +125,7 @@ struct trie_retval trie_lookup(char string[]) { } bool trie_load(const char fn[]) { + bool retval; FILE *f = fopen(fn, "r"); if (f == NULL) { syslog(cfg_log_facility | LOG_WARNING, "trie: could not load trie from file: %m"); @@ -138,13 +139,15 @@ bool trie_load(const char fn[]) { while ((status = fscanf(f, "%s %ld\n", name, &val)) != EOF) { if (status != 2) { syslog(cfg_log_facility | LOG_ERR, "trie: fscanf matched bad number of items: %d", status); - return false; + retval = false; } if(trie_set(name, val) == false) { syslog(cfg_log_facility | LOG_WARNING, "trie: setting a variable failed: %s = %ld", name, val); + retval = false; } } - return true; + fclose(f); + return retval; } static bool trie_dfs(char prefix[], uint64_t prefixlen, struct grow *list, struct trie *vertex, enum dfs_list style) { @@ -204,6 +207,7 @@ bool trie_save(const char fn[]) { fprintf(f, "%s %ld\n", l.name, l.val); } grow_drop(list); + fclose(f); return true; } @@ -265,7 +269,7 @@ bool trie_unassoc(char var[], uint64_t task) { } } -void trie_drop (void) { +void trie_flush (void) { struct grow *list = grow_init(true); trie_dfs("", 0, list, trie_base, VERTICES); grow_drop(list); diff --git a/trie.h b/trie.h index 8276c89..472668b 100644 --- a/trie.h +++ b/trie.h @@ -32,7 +32,7 @@ struct trie_list { struct trie_retval trie_lookup(char string[]); bool trie_set (char string[], int64_t val); -bool trie_un (char string[]); +bool trie_unset (char string[]); struct grow *trie_list(void); bool trie_assoc(char var[], uint64_t task);