source: ogServer-Git/src/schedule.c @ 0212091

Last change on this file since 0212091 was e68fefe, checked in by OpenGnSys Support Team <soporte-og@…>, 3 years ago

#997 Set stale check flag when processing schedule/create

If you schedule a command in the past, the scheduler executes such
command immediately.

When expanding a schedule that result in commands that run weekly,
commands in the past are also executed, which is not expected.

Fix this by using the check_stale flag (formerly on_start) so
commands in the past that result from expansions are skipped.

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