summaryrefslogtreecommitdiffstats
path: root/sources/schedule.c
diff options
context:
space:
mode:
authorOpenGnSys Support Team <soporte-og@soleta.eu>2020-01-22 13:32:12 +0100
committerOpenGnSys Support Team <soporte-og@soleta.eu>2020-06-02 12:32:36 +0200
commit83b242ce587fa910861bb2e6b4cdf2cffd38df65 (patch)
treec6fd25d74ac98afb8101913d810ca8798dc800d7 /sources/schedule.c
parentaf30cc7dbbd9ee4d6ae2c93fd9ecd46975bf16a6 (diff)
#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
Diffstat (limited to 'sources/schedule.c')
-rw-r--r--sources/schedule.c394
1 files changed, 394 insertions, 0 deletions
diff --git a/sources/schedule.c b/sources/schedule.c
new file mode 100644
index 0000000..21db921
--- /dev/null
+++ b/sources/schedule.c
@@ -0,0 +1,394 @@
+#include "schedule.h"
+#include "list.h"
+#include <sys/types.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <time.h>
+#include <ev.h>
+
+struct og_schedule *current_schedule = NULL;
+static LIST_HEAD(schedule_list);
+
+static void og_schedule_add(struct og_schedule *new)
+{
+ struct og_schedule *schedule, *next;
+ time_t now;
+
+ now = time(NULL);
+ if (new->seconds < now) {
+ free(new);
+ return;
+ }
+ list_for_each_entry_safe(schedule, next, &schedule_list, list) {
+ if (new->seconds < schedule->seconds) {
+ list_add_tail(&new->list, &schedule->list);
+ return;
+ }
+ }
+ list_add_tail(&new->list, &schedule_list);
+}
+
+/* Returns the days in a month from the weekday. */
+static void get_days_from_weekday(struct tm *tm, int wday, int *days, int *j)
+{
+ int i, mday = 0;
+
+ tm->tm_mday = 1;
+
+ //Shift week to start on Sunday instead of Monday
+ if (wday == 6)
+ wday = 0;
+ else
+ wday++;
+
+ /* A bit bruteforce, but simple. */
+ for (i = 0; i <= 30; i++) {
+ mktime(tm);
+ /* Not this weekday, skip. */
+ if (tm->tm_wday != wday) {
+ tm->tm_mday++;
+ continue;
+ }
+ /* Not interested in next month. */
+ if (tm->tm_mday < mday)
+ break;
+
+ /* Found a matching. */
+ mday = tm->tm_mday;
+ days[(*j)++] = tm->tm_mday;
+ tm->tm_mday++;
+ }
+}
+
+/* Returns the days in the given week. */
+static void get_days_from_week(struct tm *tm, int week, int *days, int *k)
+{
+ int i, j, week_counter = 0;
+ bool week_over = false;
+
+ tm->tm_mday = 1;
+
+ /* Remaining days of this month. */
+ for (i = 0; i <= 30; i++) {
+ mktime(tm);
+
+ /* Last day of this week? */
+ if (tm->tm_wday == 6)
+ week_over = true;
+
+ /* Not the week we are searching for. */
+ if (week != week_counter) {
+ tm->tm_mday++;
+ if (week_over) {
+ week_counter++;
+ week_over = false;
+ }
+ continue;
+ }
+
+ /* Found matching. */
+ for (j = tm->tm_wday; j <= 6; j++) {
+ days[(*k)++] = tm->tm_mday++;
+ mktime(tm);
+ }
+ break;
+ }
+}
+
+static int monthdays[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+static int last_month_day(struct tm *tm)
+{
+ /* Leap year? Adjust it. */
+ if (tm->tm_mon == 1) {
+ tm->tm_mday = 29;
+ mktime(tm);
+ if (tm->tm_mday == 29)
+ return 29;
+
+ tm->tm_mon = 1;
+ }
+
+ return monthdays[tm->tm_mon];
+}
+
+/* Returns the days in the given week. */
+static void get_last_week(struct tm *tm, int *days, int *j)
+{
+ int i, last_day;
+
+ last_day = last_month_day(tm);
+ tm->tm_mday = last_day;
+
+ for (i = last_day; i >= last_day - 6; i--) {
+ mktime(tm);
+
+ days[(*j)++] = tm->tm_mday;
+
+
+ syslog(LOG_ERR, "TM_WDAY: %d", tm->tm_wday);//XXX
+ syslog(LOG_ERR, "TM_MDAY: %d", tm->tm_mday);//XXX
+ /* Last day of this week? */
+ if (tm->tm_wday == 1) {
+ syslog(LOG_ERR, "break week");//XXX
+ break;
+ }
+
+ tm->tm_mday--;
+ }
+}
+
+static void og_parse_years(uint16_t years_mask, int years[])
+{
+ int i, j = 0;
+
+ for (i = 0; i < 16; i++) {
+ if ((1 << i) & years_mask)
+ years[j++] = 2010 + i - 1900;
+ }
+}
+
+static void og_parse_months(uint16_t months_mask, int months[])
+{
+ int i, j = 0;
+
+ for (i = 0; i < 12; i++) {
+ if ((1 << i) & months_mask)
+ months[j++] = i + 1;
+ }
+}
+
+static void og_parse_days(uint32_t days_mask, int *days)
+{
+ int i, j = 0;
+
+ for (i = 0; i < 31; i++) {
+ if ((1 << i) & days_mask)
+ days[j++] = i + 1;
+ }
+}
+
+static void og_parse_hours(uint16_t hours_mask, uint8_t am_pm, int hours[])
+{
+ int pm = 12 * am_pm;
+ int i, j = 0;
+
+ for (i = 0; i < 12; i++) {
+ if ((1 << i) & hours_mask)
+ hours[j++] = i + pm + 1;
+ }
+}
+
+static void og_schedule_remove_duplicates()
+{
+ struct og_schedule *schedule, *next, *prev = NULL;
+
+ list_for_each_entry_safe(schedule, next, &schedule_list, list) {
+ if (!prev) {
+ prev = schedule;
+ continue;
+ }
+ if (prev->seconds == schedule->seconds &&
+ prev->task_id == schedule->task_id) {
+ list_del(&prev->list);
+ free(prev);
+ }
+ prev = schedule;
+ }
+}
+
+void og_schedule_create(unsigned int schedule_id, unsigned int task_id,
+ struct og_schedule_time *time)
+{
+ struct og_schedule *schedule;
+ int months[12] = {};
+ int years[12] = {};
+ int hours[12] = {};
+ int days[31] = {};
+ struct tm tm = {};
+ int i, j, k, l = 0;
+ int minutes;
+
+ og_parse_years(time->years, years);
+ og_parse_months(time->months, months);
+ og_parse_days(time->days, days);
+ og_parse_hours(time->hours, time->am_pm, hours);
+ minutes = time->minutes;
+
+ for (i = 0; years[i] != 0 && i < 12; i++) {
+ for (j = 0; months[j] != 0 && j < 12; j++) {
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = years[i];
+ tm.tm_mon = months[j] - 1;
+ if (time->week_days) {
+ for (int wday = 0; wday < 7; wday++) {
+ if ((1 << wday) & time->week_days) {
+ int specific_month_days[5] = {};
+ int n_month_days = 0;
+ get_days_from_weekday(&tm,
+ wday,
+ specific_month_days,
+ &n_month_days);
+
+ for (k = 0; specific_month_days[k] != 0 && k < n_month_days; k++) {
+ for (l = 0; hours[l] != 0 && l < 31; l++) {
+ schedule = (struct og_schedule *)
+ calloc(1, sizeof(struct og_schedule));
+ if (!schedule)
+ return;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = years[i];
+ tm.tm_mon = months[j] - 1;
+ tm.tm_mday = specific_month_days[k];
+ tm.tm_hour = hours[l] - 1;
+ tm.tm_min = minutes;
+
+ schedule->seconds = mktime(&tm);
+ schedule->task_id = task_id;
+ schedule->schedule_id = schedule_id;
+ og_schedule_add(schedule);
+ }
+ }
+ }
+ }
+ }
+
+ if (time->weeks) {
+ for (int week = 0; week < 5; week++) {
+ if ((1 << week) & time->weeks) {
+ int specific_month_days[7] = {};
+ int n_month_days = 0;
+
+ if (week == 5)
+ get_last_week(&tm,
+ specific_month_days,
+ &n_month_days);
+ else
+ get_days_from_week(&tm,
+ week,
+ specific_month_days,
+ &n_month_days);
+
+ for (k = 0; specific_month_days[k] != 0 && k < n_month_days; k++) {
+ for (l = 0; hours[l] != 0 && l < 31; l++) {
+ schedule = (struct og_schedule *)
+ calloc(1, sizeof(struct og_schedule));
+ if (!schedule)
+ return;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = years[i];
+ tm.tm_mon = months[j] - 1;
+ tm.tm_mday = specific_month_days[k];
+ tm.tm_hour = hours[l] - 1;
+ tm.tm_min = minutes;
+
+ schedule->seconds = mktime(&tm);
+ schedule->task_id = task_id;
+ schedule->schedule_id = schedule_id;
+ og_schedule_add(schedule);
+ }
+ }
+ }
+ }
+ }
+
+ if (time->days) {
+ for (k = 0; days[k] != 0 && k < 31; k++) {
+ for (l = 0; hours[l] != 0 && l < 31; l++) {
+ schedule = (struct og_schedule *)
+ calloc(1, sizeof(struct og_schedule));
+ if (!schedule)
+ return;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = years[i];
+ tm.tm_mon = months[j] - 1;
+ tm.tm_mday = days[k];
+ tm.tm_hour = hours[l] - 1;
+ tm.tm_min = minutes;
+
+ schedule->seconds = mktime(&tm);
+ schedule->task_id = task_id;
+ schedule->schedule_id = schedule_id;
+ og_schedule_add(schedule);
+ }
+ }
+ }
+ }
+ }
+
+ og_schedule_remove_duplicates();
+}
+
+void og_schedule_delete(struct ev_loop *loop, uint32_t schedule_id)
+{
+ struct og_schedule *schedule, *next;
+
+ list_for_each_entry_safe(schedule, next, &schedule_list, list) {
+ if (schedule->schedule_id != schedule_id)
+ continue;
+
+ list_del(&schedule->list);
+ if (current_schedule == schedule) {
+ ev_timer_stop(loop, &schedule->timer);
+ current_schedule = NULL;
+ og_schedule_refresh(loop);
+ }
+ free(schedule);
+ }
+}
+
+void og_schedule_update(struct ev_loop *loop, unsigned int schedule_id,
+ unsigned int task_id, struct og_schedule_time *time)
+{
+ og_schedule_delete(loop, schedule_id);
+ og_schedule_create(schedule_id, task_id, time);
+}
+
+static void og_agent_timer_cb(struct ev_loop *loop, ev_timer *timer, int events)
+{
+ struct og_schedule *current;
+
+ current = container_of(timer, struct og_schedule, timer);
+ og_dbi_schedule_task(current->task_id);
+
+ ev_timer_stop(loop, timer);
+ list_del(&current->list);
+ free(current);
+
+ og_schedule_next(loop);
+}
+
+void og_schedule_next(struct ev_loop *loop)
+{
+ struct og_schedule *schedule;
+ time_t now, seconds;
+
+ if (list_empty(&schedule_list)) {
+ current_schedule = NULL;
+ return;
+ }
+
+ schedule = list_first_entry(&schedule_list, struct og_schedule, list);
+ now = time(NULL);
+ if (schedule->seconds <= now)
+ seconds = 0;
+ else
+ seconds = schedule->seconds - now;
+
+ ev_timer_init(&schedule->timer, og_agent_timer_cb, seconds, 0.);
+ ev_timer_start(loop, &schedule->timer);
+ current_schedule = schedule;
+}
+
+void og_schedule_refresh(struct ev_loop *loop)
+{
+ if (current_schedule)
+ ev_timer_stop(loop, &current_schedule->timer);
+
+ og_schedule_next(loop);
+}