1
0
Fork 0
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.

313 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_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;
}