Add simple notification daemon mode
authorAndy Spencer <andy753421@gmail.com>
Sun, 11 Jun 2017 02:25:02 +0000 (02:25 +0000)
committerAndy Spencer <andy753421@gmail.com>
Sun, 11 Jun 2017 02:32:18 +0000 (02:32 +0000)
makefile
src/args.c
src/args.h
src/daemon.c [new file with mode: 0644]
src/daemon.h [new file with mode: 0644]
src/date.c
src/date.h
src/main.c
src/test.c

index b249a27..ce82c84 100644 (file)
--- a/makefile
+++ b/makefile
@@ -15,9 +15,9 @@ LDFLAGS   ?= -lncursesw -lical
 
 # Sources
 PROG      ?= lackey
-PROG_SRC  ?= main util args conf date cal view print
+PROG_SRC  ?= main util args conf date cal view print daemon
 TEST      ?= test
-TEST_SRC  ?= test util conf date cal
+TEST_SRC  ?= test util conf date cal daemon
 VIEWS     ?= day week month year events todo settings help edit
 CALS      ?= dummy ical
 
index 49fe2f1..0390e0f 100644 (file)
@@ -26,6 +26,7 @@
 
 /* Global data */
 int PRINT  = 0;
+int DAEMON = 0;
 
 /* Local data */
 char **calendars = NULL;
@@ -36,6 +37,7 @@ static char *short_options = "hp::d";
 struct option long_options[] = {
        {"help",   0, NULL, 'h'},
        {"print",  2, NULL, 'p'},
+       {"daemon", 0, NULL, 'd'},
 };
 
 /* Usage */
@@ -47,6 +49,7 @@ static void usage(char *name)
        printf("Options:\n");
        printf("  -h, --help        Print usage information\n");
        printf("  -p, --print=[dw]  Show upcomming events\n");
+       printf("  -d, --daemon      Run in daemon mode\n");
 }
 
 /* Initialize */
@@ -70,6 +73,9 @@ void args_setup(int argc, char **argv)
                                        match(optarg, "w")    ? 7 :
                                        match(optarg, "week") ? 7 : -1;
                                break;
+                       case 'd':
+                               DAEMON = 1;
+                               break;
                }
        }
 
@@ -79,6 +85,8 @@ void args_setup(int argc, char **argv)
        /* Validate arguments */
        if (PRINT < 0)
                error("Unknown print: %s\n", optarg);
+       if (PRINT && DAEMON)
+               error("Cannot print and run as daemon");
 
        /* Load calendars */
        for (int i = 0; calendars[i]; i++)
index c62c9c2..af7f09d 100644 (file)
@@ -17,6 +17,7 @@
 
 /* Global data */
 extern int PRINT;
+extern int DAEMON;
 
 /* Functions */
 void args_setup(int argc, char **argv);
diff --git a/src/daemon.c b/src/daemon.c
new file mode 100644 (file)
index 0000000..ea61afb
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 Andy Spencer <andy753421@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define _POSIX_C_SOURCE 200112L
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "conf.h"
+#include "date.h"
+#include "cal.h"
+#include "daemon.h"
+
+/* Config data */
+static int   reminder = 15*60;
+static char *command  = NULL;
+
+/* Local data */
+static date_t old;
+static date_t new;
+
+/* Local functions */
+static void date_str(char *str, size_t size, date_t *date)
+{
+       snprintf(str, size, "%04d-%02d-%02d %02d:%02d:%02d",
+                       date->year+1, date->month+1, date->day,
+                       date->hour,   date->min,     date->sec);
+}
+
+static void notify(event_t *event)
+{
+       static char start_str[32];
+       static char end_str[32];
+
+       date_t start = event->start;
+       date_t end   = event->end;
+       wday_t wday  = day_of_week(start.year, start.month, start.day);
+
+       if (command) {
+               date_str(start_str, sizeof(start_str), &event->start);
+               date_str(end_str, sizeof(end_str), &event->end);
+
+               setenv("EVENT_NAME",  event->name ?: "", 1);
+               setenv("EVENT_DESC",  event->desc ?: "", 1);
+               setenv("EVENT_LOC",   event->loc  ?: "", 1);
+               setenv("EVENT_START", start_str, 1);
+               setenv("EVENT_END",   end_str,   1);
+
+               system(command);
+
+               unsetenv("EVENT_NAME");
+               unsetenv("EVENT_DESC");
+               unsetenv("EVENT_LOC");
+               unsetenv("EVENT_START");
+               unsetenv("EVENT_END");
+       } else {
+               printf("%s, %s %d, %d, %02d:%02d-%02d:%02d\n",
+                       day_to_string(wday),
+                       month_to_string(start.month),
+                       start.day+1,
+                       start.year,
+                       start.hour, start.min,
+                       end.hour,   end.min);
+
+               if (event->name)
+                       printf("    Name:     %s\n", event->name);
+               if (event->loc)
+                       printf("    Location: %s\n", event->loc);
+               if (event->desc)
+                       printf("    Details:  %s\n", event->desc);
+       }
+}
+
+static int step(void)
+{
+       int delay = 24*60*60;
+
+       debug("Polling: %02dh %02dm %02ds",
+               NOW.hour, NOW.min, NOW.sec);
+
+       /* Bump timestamps */
+       old = new;
+       new = NOW;
+
+       /* Print notifications */
+       for (event_t *cur = EVENTS; cur; cur = cur->next) {
+               if (compare(&cur->start, &old) < 0)
+                       continue;
+
+               int old_until = get_secs(&old, &cur->start);
+               int new_until = get_secs(&new, &cur->start);
+
+               if (old_until > reminder && new_until <= reminder) {
+                       debug("Notify: %s", cur->name);
+                       notify(cur);
+               }
+               if (new_until > reminder) {
+                       delay = new_until - reminder;
+                       break;
+               }
+       }
+
+       /* Return delay */
+       return delay;
+}
+
+/* Initialize */
+void daemon_init(void)
+{
+       old = NOW;
+       new = NOW;
+}
+
+void daemon_main(void)
+{
+       int delay = 0;
+       while (1) {
+               date_sync();
+               delay = step();
+               fflush(stdout);
+               sleep(delay);
+       }
+}
+
+void daemon_exit(void)
+{
+}
+
+/* Config parser */
+void daemon_config(const char *group, const char *name, const char *key, const char *value)
+{
+       if (match(group, "daemon")) {
+               if (match(key, "reminder")) {
+                       reminder = get_number(value);
+               } else if (match(key, "notify")) {
+                       command = get_string(value);
+               }
+       }
+}
+
+/* Test */
+void daemon_test(void)
+{
+       static event_t event0 = {
+               .name   = "test event",
+               .desc   = "this event is random and does not exist",
+       };
+       static event_t event1 = {
+               .name   = "test event",
+               .desc   = "this event is random and does not exist",
+       };
+       static todo_t todo = {
+               .name   = "test todo",
+               .desc   = "this todo is random and does not exist",
+               .status = 50,
+       };
+
+       EVENTS = &event0;
+       EVENTS->next = &event1;
+       TODOS = &todo;
+
+       date_sync();
+
+       NOW.hour    = 0;
+       NOW.min     = 0;
+       NOW.sec     = 0;
+
+       event0.start = ((date_t){NOW.year, NOW.month, NOW.day, 12, 0});
+       event0.end   = ((date_t){NOW.year, NOW.month, NOW.day, 13, 0});
+       event1.start = ((date_t){NOW.year, NOW.month, NOW.day, 14, 0});
+       event1.end   = ((date_t){NOW.year, NOW.month, NOW.day, 15, 0});
+       todo.due     = ((date_t){NOW.year, NOW.month, NOW.day, 16, 0});
+
+       util_init();
+
+       daemon_config("daemon", NULL, "reminder", "15");
+       daemon_config("daemon", NULL, "notify", "lackey-notify");
+
+       daemon_init();
+       while (NOW.day == EVENTS->start.day) {
+               int delay = step();
+               delay = MIN(delay, rand() % (60*60));
+               NOW = get_date(get_stamp(NOW)+delay);
+       }
+       daemon_exit();
+}
diff --git a/src/daemon.h b/src/daemon.h
new file mode 100644 (file)
index 0000000..b1ab1f7
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 Andy Spencer <andy753421@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Functions */
+void daemon_init(void);
+void daemon_main(void);
+void daemon_exit(void);
+void daemon_config(const char *group, const char *name, const char *key, const char *value);
index 4371dff..182d5aa 100644 (file)
@@ -159,6 +159,11 @@ int get_mins(date_t *start, date_t *end)
        return (get_stamp(*end)-get_stamp(*start))/60;
 }
 
+int get_secs(date_t *start, date_t *end)
+{
+       return (get_stamp(*end)-get_stamp(*start));
+}
+
 int compare(date_t *a, date_t *b)
 {
        int rval = a->year  < b->year  ? -1 : a->year  > b->year  ? 1 :
index ccd2252..85fb805 100644 (file)
@@ -83,6 +83,7 @@ void add_months(year_t *year, month_t *month, int months);
 date_t get_date(stamp_t stamp);
 stamp_t get_stamp(date_t date);
 int get_mins(date_t *start, date_t *end);
+int get_secs(date_t *start, date_t *end);
 int compare(date_t *a, date_t *b);
 int same_day(date_t *a, date_t *b);
 int before(date_t *start, int year, int month, int day, int hour, int min);
index bf518ff..4c701df 100644 (file)
@@ -26,6 +26,7 @@
 #include "cal.h"
 #include "view.h"
 #include "print.h"
+#include "daemon.h"
 
 /* Config parser */
 static void on_config(const char *group, const char *name, const char *key, const char *value)
@@ -33,6 +34,7 @@ static void on_config(const char *group, const char *name, const char *key, cons
        date_config(group, name, key, value);
        cal_config(group, name, key, value);
        view_config(group, name, key, value);
+       daemon_config(group, name, key, value);
 }
 
 /* Control-C handler, so we don't hose the therminal */
@@ -40,6 +42,8 @@ static void on_sigint(int signum)
 {
        if (PRINT)
                print_exit();
+       else if (DAEMON)
+               daemon_exit();
        else
                view_exit();
        exit(0);
@@ -70,6 +74,11 @@ int main(int argc, char **argv)
                print_main();
                print_exit();
        }
+       else if (DAEMON) {
+               daemon_init();
+               daemon_main();
+               daemon_exit();
+       }
        else {
                view_init();
                view_main();
index 96f7f8c..9152b70 100644 (file)
@@ -23,6 +23,7 @@
 void date_test(void);
 void conf_test(void);
 void ical_test(void *path);
+void daemon_test();
 
 int main(int argc, char **argv)
 {
@@ -30,6 +31,7 @@ int main(int argc, char **argv)
                if (match(argv[i], "date")) date_test();
                if (match(argv[i], "conf")) conf_test();
                if (match(argv[i], "ical")) ical_test(argv[++i]);
+               if (match(argv[i], "daemon")) daemon_test();
        }
        return 0;
 }