source: ogServer-Git/src/schedule.c

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

ogServer is AGPLv3+

Update license header in files.

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