/* * task.c: task management */ #include #include #include #include #include #include #include #include #include #include "task.h" #include "grow.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"); return false; } *p_t = t; if (tasks == NULL) { 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); } retval &= grow_push(p_t, tasks); // Create a timer for the task // 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) { 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; } 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_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; }