diff --git a/src/daemon.c b/src/daemon.c index 1d5dc5b..d1b3eda 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -1,65 +1,173 @@ +/* +* Petit daemon qui va envoyer des notifications aux horaires des tâches +* il vérifie si la base de données change par l'intermédiaire d'un socket, +* (implémenté par un fichier stocké dans /tmp/takl.$USER), qui contient le pid +* de ce daemon afin de pouvoir déterminer si un autre daemon tourne. +* (Le cas où deux daemons sont lancés en même temps n'est pas traité.) +*/ +#include #include #include +#include #include -#include - -#include "main/include/colors.h" -#include "main/include/config.h" -#include "main/include/tasks.h" -#include "main/include/db.h" +#include +#include +#include +#include +#include #include "log/log.h" +#include "main/include/db.h" +#include "main/include/tasks.h" +#include "main/include/utils.h" +#include "main/include/colors.h" +#include "main/include/config.h" -task_t next_task(task_list_t* tasks) { - time_t now = time(0); - task_t next; - next.due_to = 0; +#define EVENT_SIZE ( sizeof (struct inotify_event) ) +#define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) ) - task_list_t* cur = tasks; - while (cur) { - task_t t = cur->task; - if (!t.done && (difftime(now, t.due_to) <= 0 || t.due_to == 0)) { // La tâche a une échéance, qui n'est pas passée - if (next.due_to == 0 || difftime(next.due_to, t.due_to) >= 0) { - next = t; - } +bool has_changed = false; +task_list_t* tasks; +pthread_mutex_t tasks_lock = PTHREAD_MUTEX_INITIALIZER; // locks tasks and has_changed at the same time + + + +void* inotify_check_changes(void* arg) { + (void)arg; + char buffer[EVENT_BUF_LEN]; + char* socket_path = get_socket_path(); + + while (1) { // Regarde si /tmp/takl.$USER a changé (donc si la BDD a changé et doit être rechargée) + int fd = inotify_init(); + if (fd < 0) { + log_fatal("Impossible d'initialiser inotify"); + free(socket_path); + exit(1); } - cur = cur->next; - } + int wd = inotify_add_watch(fd, socket_path, IN_ACCESS); - return next; + int length = read(fd, buffer, EVENT_BUF_LEN); + if (length < 0) { + log_fatal("inotify: lecture impossible"); + free(socket_path); + exit(1); + } + + log_debug("Base de données changée"); + pthread_mutex_lock(&tasks_lock); + free_task_list(tasks); + tasks = get_task_list(false); + has_changed = true; + pthread_mutex_unlock(&tasks_lock); + + inotify_rm_watch(fd, wd); + close(fd); + } + free(socket_path); + return NULL; } +int existing_takl_daemon() { + char* socket_path = get_socket_path(); + + if (!access(socket_path, F_OK)) { // tmp file exists + FILE* fp = fopen(socket_path, "r+"); + int daemon_pid; + fscanf(fp, "%d", &daemon_pid); + fclose(fp); + + if(!kill(daemon_pid, 0)) { + free(socket_path); + return true; // The process is running + } + } + FILE* fp = fopen(socket_path, "w"); + fprintf(fp, "%d", getpid()); + fclose(fp); + + free(socket_path); + return false; +} + int main() { - log_info("TaKl " VERSION " -- Daemon started"); + bool notified_no_change = false; + + if (!existing_takl_daemon()) { + log_info("TaKl " VERSION " -- Daemon started"); + } else { + log_info("TaKl Daemon déjà en cours d'exécution. Arrêt"); + return 1; + } - task_list_t* tasks = get_task_list(false); + tasks = get_task_list(false); + + pthread_t inotify_id = 0; // Lancement d'inotify dans un autre fil + pthread_create(&inotify_id, NULL, inotify_check_changes, NULL); while (1) { + pthread_mutex_lock(&tasks_lock); task_t next = next_task(tasks); + pthread_mutex_unlock(&tasks_lock); if (next.due_to == 0) { - log_fatal("Plus de tâches avec échéance dans la liste"); - free_task_list(tasks); - return 1; + if (!notified_no_change) + log_debug("Plus de tâches avec échéance dans la liste"); + notified_no_change = true; + + while (1) { // sleep until change + sleep(5); + + pthread_mutex_lock(&tasks_lock); + if (has_changed) { + log_debug("Changement détecté"); + has_changed = false; + pthread_mutex_unlock(&tasks_lock); + + break; + } + pthread_mutex_unlock(&tasks_lock); + } + } else { + time_t now = time(0); + + double wait = difftime(next.due_to, now) + 5; + log_debug("Prochaine tâche dans %0.1lfs", wait); + notified_no_change = false; + + while (difftime(next.due_to, now) >= 5) { + sleep(5); + pthread_mutex_lock(&tasks_lock); + if (has_changed) { + log_debug("Changement détecté"); + has_changed = false; + pthread_mutex_unlock(&tasks_lock); + + break; + } + pthread_mutex_unlock(&tasks_lock); + + now = time(0); + } + + if (difftime(next.due_to, now) < 5) { // Exit not du e to the break statement + log_debug("Envoi de la notification tâche [%d]", next.id); + desktop_notification(next); + } + //! si deux tâches sont à la même date à moins de 0.1s près, + //! l'une des deux seulement sera affichée + + sleep(3); // Attendre un peu pour ne pas reconsidérer la même tâche 1063 fois. } - - time_t now = time(0); - double wait = difftime(next.due_to, now) + 5; - log_debug("Attente de %0.1lfs", wait); - sleep(wait); - - log_debug("Envoi de la notification tâche [%d]", next.id); - desktop_notification(next); - //! si deux tâches sont à la même date à moins de 0.1s près, - //! l'une des deux seulement sera affichée } + pthread_join(inotify_id, NULL); + free_task_list(tasks); return 0; } \ No newline at end of file diff --git a/src/main/db.c b/src/main/db.c index 42dd54d..33514ab 100644 --- a/src/main/db.c +++ b/src/main/db.c @@ -11,6 +11,8 @@ Fonctions d'interaction avec la base de données #include "include/colors.h" #include "include/config.h" #include "include/struct.h" +#include "include/utils.h" +#include "include/db.h" char* get_db_path() { @@ -75,6 +77,17 @@ sqlite3* get_db() { return db; } +void notify_change() { + char* socket_path = get_socket_path(); + FILE* fp = fopen(socket_path, "r"); + + int pid; + fscanf(fp, "%d", &pid); // Trigger IN_ACCESS + (void)pid; + + fclose(fp); +} + int add_task(task_t t) { sqlite3* db = get_db(); @@ -105,6 +118,7 @@ int add_task(task_t t) { sqlite3_finalize(stmt); sqlite3_close(db); + notify_change(); if (ret != SQLITE_DONE) { if (ret == 19) { @@ -147,6 +161,7 @@ int update_task(task_t t) { sqlite3_finalize(stmt); sqlite3_close(db); + notify_change(); if (ret != SQLITE_DONE) { printf("Return value: %d\n", ret); @@ -175,6 +190,7 @@ void delete_task(int id) { sqlite3_finalize(stmt); sqlite3_close(db); + notify_change(); } diff --git a/src/main/include/config.h b/src/main/include/config.h index 4739434..4eec098 100644 --- a/src/main/include/config.h +++ b/src/main/include/config.h @@ -1,7 +1,7 @@ #ifndef DEF_CONFIG_H #define DEF_CONFIG_H -#define VERSION "1.0.3" +#define VERSION "1.1.0" // By default, $HOME/.config/takl.sqlite3 is used. You can change this behaviour here //#define DB_FILE "takl.sqlite3" diff --git a/src/main/include/db.h b/src/main/include/db.h index f04ece3..bc1ab5f 100644 --- a/src/main/include/db.h +++ b/src/main/include/db.h @@ -9,6 +9,11 @@ Arrête le programme en cas d'échec */ void create_db(); +/* +Open/closes the socket file to notify of db changes +*/ +void notify_change(); + /* Ajouter une tâche à la base de données. Le numéro de tâche ne doit pas déjà être utilisé