]> Pileus Git - lackey/blob - src/daemon.c
Add simple notification daemon mode
[lackey] / src / daemon.c
1 /*
2  * Copyright (C) 2017 Andy Spencer <andy753421@gmail.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #define _POSIX_C_SOURCE 200112L
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23
24 #include "util.h"
25 #include "conf.h"
26 #include "date.h"
27 #include "cal.h"
28 #include "daemon.h"
29
30 /* Config data */
31 static int   reminder = 15*60;
32 static char *command  = NULL;
33
34 /* Local data */
35 static date_t old;
36 static date_t new;
37
38 /* Local functions */
39 static void date_str(char *str, size_t size, date_t *date)
40 {
41         snprintf(str, size, "%04d-%02d-%02d %02d:%02d:%02d",
42                         date->year+1, date->month+1, date->day,
43                         date->hour,   date->min,     date->sec);
44 }
45
46 static void notify(event_t *event)
47 {
48         static char start_str[32];
49         static char end_str[32];
50
51         date_t start = event->start;
52         date_t end   = event->end;
53         wday_t wday  = day_of_week(start.year, start.month, start.day);
54
55         if (command) {
56                 date_str(start_str, sizeof(start_str), &event->start);
57                 date_str(end_str, sizeof(end_str), &event->end);
58
59                 setenv("EVENT_NAME",  event->name ?: "", 1);
60                 setenv("EVENT_DESC",  event->desc ?: "", 1);
61                 setenv("EVENT_LOC",   event->loc  ?: "", 1);
62                 setenv("EVENT_START", start_str, 1);
63                 setenv("EVENT_END",   end_str,   1);
64
65                 system(command);
66
67                 unsetenv("EVENT_NAME");
68                 unsetenv("EVENT_DESC");
69                 unsetenv("EVENT_LOC");
70                 unsetenv("EVENT_START");
71                 unsetenv("EVENT_END");
72         } else {
73                 printf("%s, %s %d, %d, %02d:%02d-%02d:%02d\n",
74                         day_to_string(wday),
75                         month_to_string(start.month),
76                         start.day+1,
77                         start.year,
78                         start.hour, start.min,
79                         end.hour,   end.min);
80
81                 if (event->name)
82                         printf("    Name:     %s\n", event->name);
83                 if (event->loc)
84                         printf("    Location: %s\n", event->loc);
85                 if (event->desc)
86                         printf("    Details:  %s\n", event->desc);
87         }
88 }
89
90 static int step(void)
91 {
92         int delay = 24*60*60;
93
94         debug("Polling: %02dh %02dm %02ds",
95                 NOW.hour, NOW.min, NOW.sec);
96
97         /* Bump timestamps */
98         old = new;
99         new = NOW;
100
101         /* Print notifications */
102         for (event_t *cur = EVENTS; cur; cur = cur->next) {
103                 if (compare(&cur->start, &old) < 0)
104                         continue;
105
106                 int old_until = get_secs(&old, &cur->start);
107                 int new_until = get_secs(&new, &cur->start);
108
109                 if (old_until > reminder && new_until <= reminder) {
110                         debug("Notify: %s", cur->name);
111                         notify(cur);
112                 }
113                 if (new_until > reminder) {
114                         delay = new_until - reminder;
115                         break;
116                 }
117         }
118
119         /* Return delay */
120         return delay;
121 }
122
123 /* Initialize */
124 void daemon_init(void)
125 {
126         old = NOW;
127         new = NOW;
128 }
129
130 void daemon_main(void)
131 {
132         int delay = 0;
133         while (1) {
134                 date_sync();
135                 delay = step();
136                 fflush(stdout);
137                 sleep(delay);
138         }
139 }
140
141 void daemon_exit(void)
142 {
143 }
144
145 /* Config parser */
146 void daemon_config(const char *group, const char *name, const char *key, const char *value)
147 {
148         if (match(group, "daemon")) {
149                 if (match(key, "reminder")) {
150                         reminder = get_number(value);
151                 } else if (match(key, "notify")) {
152                         command = get_string(value);
153                 }
154         }
155 }
156
157 /* Test */
158 void daemon_test(void)
159 {
160         static event_t event0 = {
161                 .name   = "test event",
162                 .desc   = "this event is random and does not exist",
163         };
164         static event_t event1 = {
165                 .name   = "test event",
166                 .desc   = "this event is random and does not exist",
167         };
168         static todo_t todo = {
169                 .name   = "test todo",
170                 .desc   = "this todo is random and does not exist",
171                 .status = 50,
172         };
173
174         EVENTS = &event0;
175         EVENTS->next = &event1;
176         TODOS = &todo;
177
178         date_sync();
179
180         NOW.hour    = 0;
181         NOW.min     = 0;
182         NOW.sec     = 0;
183
184         event0.start = ((date_t){NOW.year, NOW.month, NOW.day, 12, 0});
185         event0.end   = ((date_t){NOW.year, NOW.month, NOW.day, 13, 0});
186         event1.start = ((date_t){NOW.year, NOW.month, NOW.day, 14, 0});
187         event1.end   = ((date_t){NOW.year, NOW.month, NOW.day, 15, 0});
188         todo.due     = ((date_t){NOW.year, NOW.month, NOW.day, 16, 0});
189
190         util_init();
191
192         daemon_config("daemon", NULL, "reminder", "15");
193         daemon_config("daemon", NULL, "notify", "lackey-notify");
194
195         daemon_init();
196         while (NOW.day == EVENTS->start.day) {
197                 int delay = step();
198                 delay = MIN(delay, rand() % (60*60));
199                 NOW = get_date(get_stamp(NOW)+delay);
200         }
201         daemon_exit();
202 }