You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
318 lines
8.8 KiB
C
318 lines
8.8 KiB
C
/*
|
|
* task.c: task management
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <time.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <syslog.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#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_RDWR);
|
|
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_RDWR | O_TRUNC, (mode_t) 0660);
|
|
if (fd == -1) {
|
|
syslog(cfg_log_facility | LOG_WARNING, "trie: could not save tasks to file: %m");
|
|
return false;
|
|
}
|
|
if (tasks != NULL) {
|
|
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) {
|
|
if (tasks == NULL) {
|
|
return;
|
|
}
|
|
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;
|
|
}
|