source: ogServer-Git/sources/schedule.c @ 54c7ca3

Last change on this file since 54c7ca3 was 83b242c, checked in by OpenGnSys Support Team <soporte-og@…>, 4 years ago

#942 Add support for scheduled tasks and commands

This field needs to be at least 31 bits long to store all days in a month.
Other fields are also set to 32 bits because unsigned int length can change
depending on the system.

We also need to support the three ways that the ogAdmAgent and the WebConsole?
have to create an schedule. At first, we only supported the easiest
method:

  • Hour, day, month and year -> 10:00, 28, february, 2020

This commit adds these two ways to create an schedule:

  • Hour, week day, month and year -> 10:00, Monday, february, 2020
  • Hour, week, month and year -> 10:00, first week, february, 2020
  • Property mode set to 100644
File size: 8.6 KB
Line 
1#include "schedule.h"
2#include "list.h"
3#include <sys/types.h>
4#include <stdbool.h>
5#include <stdint.h>
6#include <stdlib.h>
7#include <syslog.h>
8#include <time.h>
9#include <ev.h>
10
11struct og_schedule *current_schedule = NULL;
12static LIST_HEAD(schedule_list);
13
14static void og_schedule_add(struct og_schedule *new)
15{
16        struct og_schedule *schedule, *next;
17        time_t now;
18
19        now = time(NULL);
20        if (new->seconds < now) {
21                free(new);
22                return;
23        }
24        list_for_each_entry_safe(schedule, next, &schedule_list, list) {
25                if (new->seconds < schedule->seconds) {
26                        list_add_tail(&new->list, &schedule->list);
27                        return;
28                }
29        }
30        list_add_tail(&new->list, &schedule_list);
31}
32
33/* Returns the days in a month from the weekday. */
34static void get_days_from_weekday(struct tm *tm, int wday, int *days, int *j)
35{
36        int i, mday = 0;
37
38        tm->tm_mday = 1;
39
40        //Shift week to start on Sunday instead of Monday
41        if (wday == 6)
42                wday = 0;
43        else
44                wday++;
45
46        /* A bit bruteforce, but simple. */
47        for (i = 0; i <= 30; i++) {
48                mktime(tm);
49                /* Not this weekday, skip. */
50                if (tm->tm_wday != wday) {
51                        tm->tm_mday++;
52                        continue;
53                }
54                /* Not interested in next month. */
55                if (tm->tm_mday < mday)
56                        break;
57
58                /* Found a matching. */
59                mday = tm->tm_mday;
60                days[(*j)++] = tm->tm_mday;
61                tm->tm_mday++;
62        }
63}
64
65/* Returns the days in the given week. */
66static void get_days_from_week(struct tm *tm, int week, int *days, int *k)
67{
68        int i, j, week_counter = 0;
69        bool week_over = false;
70
71        tm->tm_mday = 1;
72
73        /* Remaining days of this month. */
74        for (i = 0; i <= 30; i++) {
75                mktime(tm);
76
77                /* Last day of this week? */
78                if (tm->tm_wday == 6)
79                        week_over = true;
80
81                /* Not the week we are searching for. */
82                if (week != week_counter) {
83                        tm->tm_mday++;
84                        if (week_over) {
85                                week_counter++;
86                                week_over = false;
87                        }
88                        continue;
89                }
90
91                /* Found matching. */
92                for (j = tm->tm_wday; j <= 6; j++) {
93                        days[(*k)++] = tm->tm_mday++;
94                        mktime(tm);
95                }
96                break;
97        }
98}
99
100static int monthdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
101
102static int last_month_day(struct tm *tm)
103{
104        /* Leap year? Adjust it. */
105        if (tm->tm_mon == 1) {
106                tm->tm_mday = 29;
107                mktime(tm);
108                if (tm->tm_mday == 29)
109                        return 29;
110
111                tm->tm_mon = 1;
112        }
113
114        return monthdays[tm->tm_mon];
115}
116
117/* Returns the days in the given week. */
118static void get_last_week(struct tm *tm, int *days, int *j)
119{
120        int i, last_day;
121
122        last_day = last_month_day(tm);
123        tm->tm_mday = last_day;
124
125        for (i = last_day; i >= last_day - 6; i--) {
126                mktime(tm);
127
128                days[(*j)++] = tm->tm_mday;
129
130
131                syslog(LOG_ERR, "TM_WDAY: %d", tm->tm_wday);//XXX
132                syslog(LOG_ERR, "TM_MDAY: %d", tm->tm_mday);//XXX
133                /* Last day of this week? */
134                if (tm->tm_wday == 1) {
135                        syslog(LOG_ERR, "break week");//XXX
136                        break;
137                }
138
139                tm->tm_mday--;
140        }
141}
142
143static void og_parse_years(uint16_t years_mask, int years[])
144{
145        int i, j = 0;
146
147        for (i = 0; i < 16; i++) {
148                if ((1 << i) & years_mask)
149                        years[j++] = 2010 + i - 1900;
150        }
151}
152
153static void og_parse_months(uint16_t months_mask, int months[])
154{
155        int i, j = 0;
156
157        for (i = 0; i < 12; i++) {
158                if ((1 << i) & months_mask)
159                        months[j++] = i + 1;
160        }
161}
162
163static void og_parse_days(uint32_t days_mask, int *days)
164{
165        int i, j = 0;
166
167        for (i = 0; i < 31; i++) {
168                if ((1 << i) & days_mask)
169                        days[j++] = i + 1;
170        }
171}
172
173static void og_parse_hours(uint16_t hours_mask, uint8_t am_pm, int hours[])
174{
175        int pm = 12 * am_pm;
176        int i, j = 0;
177
178        for (i = 0; i < 12; i++) {
179                if ((1 << i) & hours_mask)
180                        hours[j++] = i + pm + 1;
181        }
182}
183
184static void og_schedule_remove_duplicates()
185{
186        struct og_schedule *schedule, *next, *prev = NULL;
187
188        list_for_each_entry_safe(schedule, next, &schedule_list, list) {
189                if (!prev) {
190                        prev = schedule;
191                        continue;
192                }
193                if (prev->seconds == schedule->seconds &&
194                    prev->task_id == schedule->task_id) {
195                        list_del(&prev->list);
196                        free(prev);
197                }
198                prev = schedule;
199        }
200}
201
202void og_schedule_create(unsigned int schedule_id, unsigned int task_id,
203                        struct og_schedule_time *time)
204{
205        struct og_schedule *schedule;
206        int months[12] = {};
207        int years[12] = {};
208        int hours[12] = {};
209        int days[31] = {};
210        struct tm tm = {};
211        int i, j, k, l = 0;
212        int minutes;
213
214        og_parse_years(time->years, years);
215        og_parse_months(time->months, months);
216        og_parse_days(time->days, days);
217        og_parse_hours(time->hours, time->am_pm, hours);
218        minutes = time->minutes;
219
220        for (i = 0; years[i] != 0 && i < 12; i++) {
221                for (j = 0; months[j] != 0 && j < 12; j++) {
222                        memset(&tm, 0, sizeof(tm));
223                        tm.tm_year = years[i];
224                        tm.tm_mon = months[j] - 1;
225                        if (time->week_days) {
226                                for (int wday = 0; wday < 7; wday++) {
227                                        if ((1 << wday) & time->week_days) {
228                                                int specific_month_days[5] = {};
229                                                int n_month_days = 0;
230                                                get_days_from_weekday(&tm,
231                                                                      wday,
232                                                                      specific_month_days,
233                                                                      &n_month_days);
234
235                                                for (k = 0; specific_month_days[k] != 0 && k < n_month_days; k++) {
236                                                        for (l = 0; hours[l] != 0 && l < 31; l++) {
237                                                                schedule = (struct og_schedule *)
238                                                                        calloc(1, sizeof(struct og_schedule));
239                                                                if (!schedule)
240                                                                        return;
241
242                                                                memset(&tm, 0, sizeof(tm));
243                                                                tm.tm_year = years[i];
244                                                                tm.tm_mon = months[j] - 1;
245                                                                tm.tm_mday = specific_month_days[k];
246                                                                tm.tm_hour = hours[l] - 1;
247                                                                tm.tm_min = minutes;
248
249                                                                schedule->seconds = mktime(&tm);
250                                                                schedule->task_id = task_id;
251                                                                schedule->schedule_id = schedule_id;
252                                                                og_schedule_add(schedule);
253                                                        }
254                                                }
255                                        }
256                                }
257                        }
258
259                        if (time->weeks) {
260                                for (int week = 0; week < 5; week++) {
261                                        if ((1 << week) & time->weeks) {
262                                                int specific_month_days[7] = {};
263                                                int n_month_days = 0;
264
265                                                if (week == 5)
266                                                        get_last_week(&tm,
267                                                                      specific_month_days,
268                                                                      &n_month_days);
269                                                else
270                                                        get_days_from_week(&tm,
271                                                                           week,
272                                                                           specific_month_days,
273                                                                           &n_month_days);
274
275                                                for (k = 0; specific_month_days[k] != 0 && k < n_month_days; k++) {
276                                                        for (l = 0; hours[l] != 0 && l < 31; l++) {
277                                                                schedule = (struct og_schedule *)
278                                                                        calloc(1, sizeof(struct og_schedule));
279                                                                if (!schedule)
280                                                                  return;
281
282                                                                memset(&tm, 0, sizeof(tm));
283                                                                tm.tm_year = years[i];
284                                                                tm.tm_mon = months[j] - 1;
285                                                                tm.tm_mday = specific_month_days[k];
286                                                                tm.tm_hour = hours[l] - 1;
287                                                                tm.tm_min = minutes;
288
289                                                                schedule->seconds = mktime(&tm);
290                                                                schedule->task_id = task_id;
291                                                                schedule->schedule_id = schedule_id;
292                                                                og_schedule_add(schedule);
293                                                        }
294                                                }
295                                        }
296                                }
297                        }
298
299                        if (time->days) {
300                                for (k = 0; days[k] != 0 && k < 31; k++) {
301                                        for (l = 0; hours[l] != 0 && l < 31; l++) {
302                                                schedule = (struct og_schedule *)
303                                                        calloc(1, sizeof(struct og_schedule));
304                                                if (!schedule)
305                                                        return;
306
307                                                memset(&tm, 0, sizeof(tm));
308                                                tm.tm_year = years[i];
309                                                tm.tm_mon = months[j] - 1;
310                                                tm.tm_mday = days[k];
311                                                tm.tm_hour = hours[l] - 1;
312                                                tm.tm_min = minutes;
313
314                                                schedule->seconds = mktime(&tm);
315                                                schedule->task_id = task_id;
316                                                schedule->schedule_id = schedule_id;
317                                                og_schedule_add(schedule);
318                                        }
319                                }
320                        }
321                }
322        }
323
324        og_schedule_remove_duplicates();
325}
326
327void og_schedule_delete(struct ev_loop *loop, uint32_t schedule_id)
328{
329        struct og_schedule *schedule, *next;
330
331        list_for_each_entry_safe(schedule, next, &schedule_list, list) {
332                if (schedule->schedule_id != schedule_id)
333                        continue;
334
335                list_del(&schedule->list);
336                if (current_schedule == schedule) {
337                        ev_timer_stop(loop, &schedule->timer);
338                        current_schedule = NULL;
339                        og_schedule_refresh(loop);
340                }
341                free(schedule);
342        }
343}
344
345void og_schedule_update(struct ev_loop *loop, unsigned int schedule_id,
346                        unsigned int task_id, struct og_schedule_time *time)
347{
348        og_schedule_delete(loop, schedule_id);
349        og_schedule_create(schedule_id, task_id, time);
350}
351
352static void og_agent_timer_cb(struct ev_loop *loop, ev_timer *timer, int events)
353{
354        struct og_schedule *current;
355
356        current = container_of(timer, struct og_schedule, timer);
357        og_dbi_schedule_task(current->task_id);
358
359        ev_timer_stop(loop, timer);
360        list_del(&current->list);
361        free(current);
362
363        og_schedule_next(loop);
364}
365
366void og_schedule_next(struct ev_loop *loop)
367{
368        struct og_schedule *schedule;
369        time_t now, seconds;
370
371        if (list_empty(&schedule_list)) {
372                current_schedule = NULL;
373                return;
374        }
375
376        schedule = list_first_entry(&schedule_list, struct og_schedule, list);
377        now = time(NULL);
378        if (schedule->seconds <= now)
379                seconds = 0;
380        else
381                seconds = schedule->seconds - now;
382
383        ev_timer_init(&schedule->timer, og_agent_timer_cb, seconds, 0.);
384        ev_timer_start(loop, &schedule->timer);
385        current_schedule = schedule;
386}
387
388void og_schedule_refresh(struct ev_loop *loop)
389{
390        if (current_schedule)
391                ev_timer_stop(loop, &current_schedule->timer);
392
393        og_schedule_next(loop);
394}
Note: See TracBrowser for help on using the repository browser.