source: ogServer-Git/sources/schedule.c @ 6c7d827

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

#942 ignore stale schedules on start

  • Property mode set to 100644
File size: 10.0 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                /* Last day of this week? */
132                if (tm->tm_wday == 1)
133                        break;
134
135                tm->tm_mday--;
136        }
137}
138
139static void og_parse_years(uint16_t years_mask, int years[])
140{
141        int i, j = 0;
142
143        for (i = 0; i < 16; i++) {
144                if ((1 << i) & years_mask)
145                        years[j++] = 2010 + i - 1900;
146        }
147}
148
149static void og_parse_months(uint16_t months_mask, int months[])
150{
151        int i, j = 0;
152
153        for (i = 0; i < 12; i++) {
154                if ((1 << i) & months_mask)
155                        months[j++] = i + 1;
156        }
157}
158
159static void og_parse_days(uint32_t days_mask, int *days)
160{
161        int i, j = 0;
162
163        for (i = 0; i < 31; i++) {
164                if ((1 << i) & days_mask)
165                        days[j++] = i + 1;
166        }
167}
168
169static void og_parse_hours(uint16_t hours_mask, uint8_t am_pm, int hours[])
170{
171        int pm = 12 * am_pm;
172        int i, j = 0;
173
174        for (i = 0; i < 12; i++) {
175                if ((1 << i) & hours_mask)
176                        hours[j++] = i + pm + 1;
177        }
178}
179
180static void og_schedule_remove_duplicates()
181{
182        struct og_schedule *schedule, *next, *prev = NULL;
183
184        list_for_each_entry_safe(schedule, next, &schedule_list, list) {
185                if (!prev) {
186                        prev = schedule;
187                        continue;
188                }
189                if (prev->seconds == schedule->seconds &&
190                    prev->task_id == schedule->task_id) {
191                        list_del(&prev->list);
192                        free(prev);
193                }
194                prev = schedule;
195        }
196}
197
198static bool og_schedule_stale(time_t seconds)
199{
200        time_t now;
201
202        now = time(NULL);
203        if (seconds < now)
204                return true;
205
206        return false;
207}
208
209static void og_schedule_create_weekdays(int month, int year,
210                                        int *hours, int minutes, int week_days,
211                                        uint32_t task_id, uint32_t schedule_id,
212                                        enum og_schedule_type type,
213                                        bool on_start)
214{
215        struct og_schedule *schedule;
216        int month_days[5];
217        int n_month_days;
218        time_t seconds;
219        uint32_t wday;
220        struct tm tm;
221        int k, l;
222
223        for (wday = 0; wday < 7; wday++) {
224                if (!((1 << wday) & week_days))
225                        continue;
226
227                memset(&tm, 0, sizeof(tm));
228                tm.tm_mon = month;
229                tm.tm_year = year;
230
231                n_month_days = 0;
232                memset(month_days, 0, sizeof(month_days));
233                get_days_from_weekday(&tm, wday, month_days, &n_month_days);
234
235                for (k = 0; month_days[k] != 0 && k < n_month_days; k++) {
236                        for (l = 0; hours[l] != 0 && l < 31; l++) {
237                                memset(&tm, 0, sizeof(tm));
238                                tm.tm_year = year;
239                                tm.tm_mon = month;
240                                tm.tm_mday = month_days[k];
241                                tm.tm_hour = hours[l] - 1;
242                                tm.tm_min = minutes;
243                                seconds = mktime(&tm);
244
245                                if (on_start && og_schedule_stale(seconds))
246                                        continue;
247
248                                schedule = (struct og_schedule *)
249                                        calloc(1, sizeof(struct og_schedule));
250                                if (!schedule)
251                                        return;
252
253                                schedule->seconds = seconds;
254                                schedule->task_id = task_id;
255                                schedule->schedule_id = schedule_id;
256                                schedule->type = type;
257                                og_schedule_add(schedule);
258                        }
259                }
260        }
261}
262
263static void og_schedule_create_weeks(int month, int year,
264                                     int *hours, int minutes, int weeks,
265                                     uint32_t task_id, uint32_t schedule_id,
266                                     enum og_schedule_type type, bool on_start)
267{
268        struct og_schedule *schedule;
269        int month_days[7];
270        int n_month_days;
271        time_t seconds;
272        struct tm tm;
273        int week;
274        int k, l;
275
276        for (week = 0; week < 5; week++) {
277                if (!((1 << week) & weeks))
278                        continue;
279
280                memset(&tm, 0, sizeof(tm));
281                tm.tm_mon = month;
282                tm.tm_year = year;
283
284                n_month_days = 0;
285                memset(month_days, 0, sizeof(month_days));
286                if (week == 5)
287                        get_last_week(&tm, month_days, &n_month_days);
288                else
289                        get_days_from_week(&tm,  week, month_days, &n_month_days);
290
291                for (k = 0; month_days[k] != 0 && k < n_month_days; k++) {
292                        for (l = 0; hours[l] != 0 && l < 31; l++) {
293                                memset(&tm, 0, sizeof(tm));
294                                tm.tm_year = year;
295                                tm.tm_mon = month;
296                                tm.tm_mday = month_days[k];
297                                tm.tm_hour = hours[l] - 1;
298                                tm.tm_min = minutes;
299                                seconds = mktime(&tm);
300
301                                if (on_start && og_schedule_stale(seconds))
302                                        continue;
303
304                                schedule = (struct og_schedule *)
305                                        calloc(1, sizeof(struct og_schedule));
306                                if (!schedule)
307                                        return;
308
309                                schedule->seconds = seconds;
310                                schedule->task_id = task_id;
311                                schedule->schedule_id = schedule_id;
312                                schedule->type = type;
313                                og_schedule_add(schedule);
314                        }
315                }
316        }
317}
318
319static void og_schedule_create_days(int month, int year,
320                                    int *hours, int minutes, int *days,
321                                    uint32_t task_id, uint32_t schedule_id,
322                                    enum og_schedule_type type, bool on_start)
323{
324        struct og_schedule *schedule;
325        time_t seconds;
326        struct tm tm;
327        int k, l;
328
329        for (k = 0; days[k] != 0 && k < 31; k++) {
330                for (l = 0; hours[l] != 0 && l < 31; l++) {
331
332                        memset(&tm, 0, sizeof(tm));
333                        tm.tm_year = year;
334                        tm.tm_mon = month;
335                        tm.tm_mday = days[k];
336                        tm.tm_hour = hours[l] - 1;
337                        tm.tm_min = minutes;
338                        seconds = mktime(&tm);
339
340                        if (on_start && og_schedule_stale(seconds))
341                                continue;
342
343                        schedule = (struct og_schedule *)
344                                calloc(1, sizeof(struct og_schedule));
345                        if (!schedule)
346                                return;
347
348                        schedule->seconds = seconds;
349                        schedule->task_id = task_id;
350                        schedule->schedule_id = schedule_id;
351                        schedule->type = type;
352                        og_schedule_add(schedule);
353                }
354        }
355}
356
357void og_schedule_create(unsigned int schedule_id, unsigned int task_id,
358                        enum og_schedule_type type,
359                        struct og_schedule_time *time)
360{
361        int year, month, minutes;
362        int months[12] = {};
363        int years[12] = {};
364        int hours[12] = {};
365        int days[31] = {};
366        int i, j;
367
368        og_parse_years(time->years, years);
369        og_parse_months(time->months, months);
370        og_parse_days(time->days, days);
371        og_parse_hours(time->hours, time->am_pm, hours);
372        minutes = time->minutes;
373
374        for (i = 0; years[i] != 0 && i < 12; i++) {
375                for (j = 0; months[j] != 0 && j < 12; j++) {
376                        month = months[j] - 1;
377                        year = years[i];
378
379                        if (time->week_days)
380                                og_schedule_create_weekdays(month, year,
381                                                            hours, minutes,
382                                                            time->week_days,
383                                                            task_id,
384                                                            schedule_id,
385                                                            type,
386                                                            time->on_start);
387
388                        if (time->weeks)
389                                og_schedule_create_weeks(month, year,
390                                                         hours, minutes,
391                                                         time->weeks,
392                                                         task_id,
393                                                         schedule_id,
394                                                         type, time->on_start);
395
396                        if (time->days)
397                                og_schedule_create_days(month, year,
398                                                        hours, minutes,
399                                                        days,
400                                                        task_id,
401                                                        schedule_id,
402                                                        type, time->on_start);
403                }
404        }
405
406        og_schedule_remove_duplicates();
407}
408
409void og_schedule_delete(struct ev_loop *loop, uint32_t schedule_id)
410{
411        struct og_schedule *schedule, *next;
412
413        list_for_each_entry_safe(schedule, next, &schedule_list, list) {
414                if (schedule->schedule_id != schedule_id)
415                        continue;
416
417                list_del(&schedule->list);
418                if (current_schedule == schedule) {
419                        ev_timer_stop(loop, &schedule->timer);
420                        current_schedule = NULL;
421                        og_schedule_refresh(loop);
422                }
423                free(schedule);
424        }
425}
426
427void og_schedule_update(struct ev_loop *loop, unsigned int schedule_id,
428                        unsigned int task_id, struct og_schedule_time *time)
429{
430        og_schedule_delete(loop, schedule_id);
431        og_schedule_create(schedule_id, task_id, OG_SCHEDULE_TASK, time);
432}
433
434static void og_agent_timer_cb(struct ev_loop *loop, ev_timer *timer, int events)
435{
436        struct og_schedule *current;
437
438        current = container_of(timer, struct og_schedule, timer);
439        og_schedule_run(current->task_id, current->schedule_id, current->type);
440
441        ev_timer_stop(loop, timer);
442        list_del(&current->list);
443        free(current);
444
445        og_schedule_next(loop);
446}
447
448void og_schedule_next(struct ev_loop *loop)
449{
450        struct og_schedule *schedule;
451        time_t now, seconds;
452
453        if (list_empty(&schedule_list)) {
454                current_schedule = NULL;
455                return;
456        }
457
458        schedule = list_first_entry(&schedule_list, struct og_schedule, list);
459        now = time(NULL);
460        if (schedule->seconds <= now)
461                seconds = 0;
462        else
463                seconds = schedule->seconds - now;
464
465        ev_timer_init(&schedule->timer, og_agent_timer_cb, seconds, 0.);
466        ev_timer_start(loop, &schedule->timer);
467        current_schedule = schedule;
468}
469
470void og_schedule_refresh(struct ev_loop *loop)
471{
472        if (current_schedule)
473                ev_timer_stop(loop, &current_schedule->timer);
474
475        og_schedule_next(loop);
476}
Note: See TracBrowser for help on using the repository browser.