summaryrefslogtreecommitdiffstats
path: root/sources
diff options
context:
space:
mode:
Diffstat (limited to 'sources')
-rw-r--r--sources/ogAdmServer.c428
-rw-r--r--sources/schedule.c394
-rw-r--r--sources/schedule.h37
3 files changed, 847 insertions, 12 deletions
diff --git a/sources/ogAdmServer.c b/sources/ogAdmServer.c
index b6fb375..116852c 100644
--- a/sources/ogAdmServer.c
+++ b/sources/ogAdmServer.c
@@ -10,6 +10,7 @@
#include "ogAdmLib.c"
#include "dbi.h"
#include "list.h"
+#include "schedule.h"
#include <ev.h>
#include <syslog.h>
#include <sys/ioctl.h>
@@ -18,6 +19,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <jansson.h>
+#include <time.h>
static char usuario[LONPRM]; // Usuario de acceso a la base de datos
static char pasguor[LONPRM]; // Password del usuario
@@ -3028,6 +3030,7 @@ struct og_msg_params {
bool echo;
struct og_partition partition_setup[OG_PARTITION_MAX];
struct og_sync_params sync_setup;
+ struct og_schedule_time time;
const char *task_id;
uint64_t flags;
};
@@ -3073,6 +3076,12 @@ struct og_computer {
#define OG_REST_PARAM_SYNC_METHOD (1UL << 29)
#define OG_REST_PARAM_ECHO (1UL << 30)
#define OG_REST_PARAM_TASK (1UL << 31)
+#define OG_REST_PARAM_TIME_YEARS (1UL << 32)
+#define OG_REST_PARAM_TIME_MONTHS (1UL << 33)
+#define OG_REST_PARAM_TIME_DAYS (1UL << 34)
+#define OG_REST_PARAM_TIME_HOURS (1UL << 35)
+#define OG_REST_PARAM_TIME_AM_PM (1UL << 36)
+#define OG_REST_PARAM_TIME_MINUTES (1UL << 37)
enum og_rest_method {
OG_METHOD_GET = 0,
@@ -3190,6 +3199,15 @@ static int og_json_parse_string(json_t *element, const char **str)
return 0;
}
+static int og_json_parse_uint(json_t *element, uint32_t *integer)
+{
+ if (json_typeof(element) != JSON_INTEGER)
+ return -1;
+
+ *integer = json_integer_value(element);
+ return 0;
+}
+
static int og_json_parse_bool(json_t *element, bool *value)
{
if (json_typeof(element) == JSON_TRUE)
@@ -3337,6 +3355,40 @@ static int og_json_parse_partition_setup(json_t *element,
return 0;
}
+static int og_json_parse_time_params(json_t *element,
+ struct og_msg_params *params)
+{
+ const char *key;
+ json_t *value;
+ int err = 0;
+
+ json_object_foreach(element, key, value) {
+ if (!strcmp(key, "years")) {
+ err = og_json_parse_uint(value, &params->time.years);
+ params->flags |= OG_REST_PARAM_TIME_YEARS;
+ } else if (!strcmp(key, "months")) {
+ err = og_json_parse_uint(value, &params->time.months);
+ params->flags |= OG_REST_PARAM_TIME_MONTHS;
+ } else if (!strcmp(key, "days")) {
+ err = og_json_parse_uint(value, &params->time.days);
+ params->flags |= OG_REST_PARAM_TIME_DAYS;
+ } else if (!strcmp(key, "hours")) {
+ err = og_json_parse_uint(value, &params->time.hours);
+ params->flags |= OG_REST_PARAM_TIME_HOURS;
+ } else if (!strcmp(key, "am_pm")) {
+ err = og_json_parse_uint(value, &params->time.am_pm);
+ params->flags |= OG_REST_PARAM_TIME_AM_PM;
+ } else if (!strcmp(key, "minutes")) {
+ err = og_json_parse_uint(value, &params->time.minutes);
+ params->flags |= OG_REST_PARAM_TIME_MINUTES;
+ }
+ if (err != 0)
+ return err;
+ }
+
+ return err;
+}
+
static int og_cmd_post_clients(json_t *element, struct og_msg_params *params)
{
const char *key;
@@ -4571,7 +4623,7 @@ static int og_queue_task_clients(struct og_dbi *dbi, struct og_task *task)
return 0;
}
-static int og_queue_procedure(struct og_dbi *dbi, struct og_task *task)
+static int og_dbi_queue_procedure(struct og_dbi *dbi, struct og_task *task)
{
uint32_t procedure_id;
const char *msglog;
@@ -4592,7 +4644,7 @@ static int og_queue_procedure(struct og_dbi *dbi, struct og_task *task)
procedure_id = dbi_result_get_uint(result, "procedimientoid");
if (procedure_id > 0) {
task->procedure_id = procedure_id;
- if (og_queue_procedure(dbi, task))
+ if (og_dbi_queue_procedure(dbi, task))
return -1;
continue;
}
@@ -4607,7 +4659,7 @@ static int og_queue_procedure(struct og_dbi *dbi, struct og_task *task)
return 0;
}
-static int og_queue_task(struct og_dbi *dbi, uint32_t task_id)
+static int og_dbi_queue_task(struct og_dbi *dbi, uint32_t task_id)
{
struct og_task task = {};
uint32_t task_id_next;
@@ -4635,7 +4687,7 @@ static int og_queue_task(struct og_dbi *dbi, uint32_t task_id)
task_id_next = dbi_result_get_uint(result, "procedimientoid");
if (task_id_next > 0) {
- if (og_queue_task(dbi, task_id_next))
+ if (og_dbi_queue_task(dbi, task_id_next))
return -1;
continue;
@@ -4645,8 +4697,7 @@ static int og_queue_task(struct og_dbi *dbi, uint32_t task_id)
task.scope = dbi_result_get_uint(result, "idambito");
task.filtered_scope = dbi_result_get_string(result, "restrambito");
- og_queue_procedure(dbi, &task);
-
+ og_dbi_queue_procedure(dbi, &task);
}
dbi_result_free(result);
@@ -4654,6 +4705,41 @@ static int og_queue_task(struct og_dbi *dbi, uint32_t task_id)
return 0;
}
+void og_dbi_schedule_task(unsigned int task_id)
+{
+ struct og_msg_params params = {};
+ bool duplicated = false;
+ struct og_cmd *cmd;
+ struct og_dbi *dbi;
+ unsigned int i;
+
+ dbi = og_dbi_open(&dbi_config);
+ if (!dbi) {
+ syslog(LOG_ERR, "cannot open connection database (%s:%d)\n",
+ __func__, __LINE__);
+ return;
+ }
+ og_dbi_queue_task(dbi, task_id);
+ og_dbi_close(dbi);
+
+ list_for_each_entry(cmd, &cmd_list, list) {
+ for (i = 0; i < params.ips_array_len; i++) {
+ if (!strncmp(cmd->ip, params.ips_array[i],
+ OG_DB_IP_MAXLEN)) {
+ duplicated = true;
+ break;
+ }
+ }
+
+ if (!duplicated)
+ params.ips_array[params.ips_array_len++] = cmd->ip;
+ else
+ duplicated = false;
+ }
+
+ og_send_request("run/schedule", OG_METHOD_GET, &params, NULL);
+}
+
static int og_cmd_task_post(json_t *element, struct og_msg_params *params)
{
struct og_cmd *cmd;
@@ -4685,7 +4771,7 @@ static int og_cmd_task_post(json_t *element, struct og_msg_params *params)
return -1;
}
- og_queue_task(dbi, atoi(params->task_id));
+ og_dbi_queue_task(dbi, atoi(params->task_id));
og_dbi_close(dbi);
list_for_each_entry(cmd, &cmd_list, list)
@@ -4694,6 +4780,288 @@ static int og_cmd_task_post(json_t *element, struct og_msg_params *params)
return og_send_request("run/schedule", OG_METHOD_GET, params, NULL);
}
+static int og_dbi_schedule_get(void)
+{
+ uint32_t schedule_id, task_id;
+ struct og_schedule_time time;
+ struct og_dbi *dbi;
+ const char *msglog;
+ dbi_result result;
+
+ dbi = og_dbi_open(&dbi_config);
+ if (!dbi) {
+ syslog(LOG_ERR, "cannot open connection database (%s:%d)\n",
+ __func__, __LINE__);
+ return -1;
+ }
+
+ result = dbi_conn_queryf(dbi->conn,
+ "SELECT idprogramacion, tipoaccion, identificador, "
+ "sesion, annos, meses, diario, dias, semanas, horas, "
+ "ampm, minutos FROM programaciones "
+ "WHERE suspendida = 0");
+ if (!result) {
+ dbi_conn_error(dbi->conn, &msglog);
+ syslog(LOG_ERR, "failed to query database (%s:%d) %s\n",
+ __func__, __LINE__, msglog);
+ return -1;
+ }
+
+ while (dbi_result_next_row(result)) {
+ memset(&time, 0, sizeof(time));
+ schedule_id = dbi_result_get_uint(result, "idprogramacion");
+ task_id = dbi_result_get_uint(result, "identificador");
+ time.years = dbi_result_get_uint(result, "annos");
+ time.months = dbi_result_get_uint(result, "meses");
+ time.weeks = dbi_result_get_uint(result, "semanas");
+ time.week_days = dbi_result_get_uint(result, "dias");
+ time.days = dbi_result_get_uint(result, "diario");
+ time.hours = dbi_result_get_uint(result, "horas");
+ time.am_pm = dbi_result_get_uint(result, "ampm");
+ time.minutes = dbi_result_get_uint(result, "minutos");
+
+ og_schedule_create(schedule_id, task_id, &time);
+ }
+
+ dbi_result_free(result);
+
+ return 0;
+}
+
+static int og_dbi_schedule_create(struct og_dbi *dbi,
+ struct og_msg_params *params,
+ uint32_t *schedule_id)
+{
+ const char *msglog;
+ dbi_result result;
+ uint8_t suspended = 0;
+ uint8_t type = 3;
+
+ result = dbi_conn_queryf(dbi->conn,
+ "INSERT INTO programaciones (tipoaccion,"
+ " identificador, nombrebloque, annos, meses,"
+ " diario, horas, ampm, minutos, suspendida) VALUES (%d,"
+ " %s, '%s', %d, %d, %d, %d, %d, %d, %d)", type,
+ params->task_id, params->name, params->time.years,
+ params->time.months, params->time.days,
+ params->time.hours, params->time.am_pm,
+ params->time.minutes, suspended);
+ if (!result) {
+ dbi_conn_error(dbi->conn, &msglog);
+ syslog(LOG_ERR, "failed to query database (%s:%d) %s\n",
+ __func__, __LINE__, msglog);
+ return -1;
+ }
+ dbi_result_free(result);
+
+ *schedule_id = dbi_conn_sequence_last(dbi->conn, NULL);
+
+ return 0;
+}
+
+static int og_dbi_schedule_update(struct og_dbi *dbi,
+ struct og_msg_params *params)
+{
+ const char *msglog;
+ dbi_result result;
+ uint8_t type = 3;
+
+ result = dbi_conn_queryf(dbi->conn,
+ "UPDATE programaciones SET tipoaccion=%d, "
+ "identificador='%s', nombrebloque='%s', "
+ "annos=%d, meses=%d, "
+ "diario=%d, horas=%d, ampm=%d, minutos=%d "
+ "WHERE idprogramacion='%s'",
+ type, params->task_id, params->name,
+ params->time.years, params->time.months,
+ params->time.days, params->time.hours,
+ params->time.am_pm, params->time.minutes,
+ params->id);
+
+ if (!result) {
+ dbi_conn_error(dbi->conn, &msglog);
+ syslog(LOG_ERR, "failed to query database (%s:%d) %s\n",
+ __func__, __LINE__, msglog);
+ return -1;
+ }
+ dbi_result_free(result);
+
+ return 0;
+}
+
+static int og_dbi_schedule_delete(struct og_dbi *dbi, uint32_t id)
+{
+ const char *msglog;
+ dbi_result result;
+
+ result = dbi_conn_queryf(dbi->conn,
+ "DELETE FROM programaciones WHERE idprogramacion=%d",
+ id);
+ if (!result) {
+ dbi_conn_error(dbi->conn, &msglog);
+ syslog(LOG_ERR, "failed to query database (%s:%d) %s\n",
+ __func__, __LINE__, msglog);
+ return -1;
+ }
+ dbi_result_free(result);
+
+ return 0;
+}
+
+static struct ev_loop *og_loop;
+
+static int og_cmd_schedule_create(json_t *element, struct og_msg_params *params)
+{
+ uint32_t schedule_id;
+ struct og_dbi *dbi;
+ const char *key;
+ json_t *value;
+ int err;
+
+ if (json_typeof(element) != JSON_OBJECT)
+ return -1;
+
+ json_object_foreach(element, key, value) {
+ if (!strcmp(key, "task")) {
+ err = og_json_parse_string(value, &params->task_id);
+ params->flags |= OG_REST_PARAM_TASK;
+ } else if (!strcmp(key, "name")) {
+ err = og_json_parse_string(value, &params->name);
+ params->flags |= OG_REST_PARAM_NAME;
+ } else if (!strcmp(key, "time_params"))
+ err = og_json_parse_time_params(value, params);
+
+ if (err < 0)
+ break;
+ }
+
+ if (!og_msg_params_validate(params, OG_REST_PARAM_TASK |
+ OG_REST_PARAM_NAME |
+ OG_REST_PARAM_TIME_YEARS |
+ OG_REST_PARAM_TIME_MONTHS |
+ OG_REST_PARAM_TIME_DAYS |
+ OG_REST_PARAM_TIME_HOURS |
+ OG_REST_PARAM_TIME_MINUTES |
+ OG_REST_PARAM_TIME_AM_PM))
+ return -1;
+
+ dbi = og_dbi_open(&dbi_config);
+ if (!dbi) {
+ syslog(LOG_ERR, "cannot open connection database (%s:%d)\n",
+ __func__, __LINE__);
+ return -1;
+ }
+
+ err = og_dbi_schedule_create(dbi, params, &schedule_id);
+ og_dbi_close(dbi);
+
+ if (err < 0)
+ return -1;
+
+ og_schedule_create(schedule_id, atoi(params->task_id), &params->time);
+ og_schedule_refresh(og_loop);
+
+ return err;
+}
+
+static int og_cmd_schedule_update(json_t *element, struct og_msg_params *params)
+{
+ struct og_dbi *dbi;
+ const char *key;
+ json_t *value;
+ int err;
+
+ if (json_typeof(element) != JSON_OBJECT)
+ return -1;
+
+ json_object_foreach(element, key, value) {
+ if (!strcmp(key, "id")) {
+ err = og_json_parse_string(value, &params->id);
+ params->flags |= OG_REST_PARAM_ID;
+ } else if (!strcmp(key, "task")) {
+ err = og_json_parse_string(value, &params->task_id);
+ params->flags |= OG_REST_PARAM_TASK;
+ } else if (!strcmp(key, "name")) {
+ err = og_json_parse_string(value, &params->name);
+ params->flags |= OG_REST_PARAM_NAME;
+ } else if (!strcmp(key, "time_params"))
+ err = og_json_parse_time_params(value, params);
+
+ if (err < 0)
+ break;
+ }
+
+ if (!og_msg_params_validate(params, OG_REST_PARAM_ID |
+ OG_REST_PARAM_TASK |
+ OG_REST_PARAM_NAME |
+ OG_REST_PARAM_TIME_YEARS |
+ OG_REST_PARAM_TIME_MONTHS |
+ OG_REST_PARAM_TIME_DAYS |
+ OG_REST_PARAM_TIME_HOURS |
+ OG_REST_PARAM_TIME_MINUTES |
+ OG_REST_PARAM_TIME_AM_PM))
+ return -1;
+
+ dbi = og_dbi_open(&dbi_config);
+ if (!dbi) {
+ syslog(LOG_ERR, "cannot open connection database (%s:%d)\n",
+ __func__, __LINE__);
+ return -1;
+ }
+
+ err = og_dbi_schedule_update(dbi, params);
+ og_dbi_close(dbi);
+
+ if (err < 0)
+ return err;
+
+ og_schedule_update(og_loop, atoi(params->id), atoi(params->task_id),
+ &params->time);
+ og_schedule_refresh(og_loop);
+
+ return err;
+}
+
+static int og_cmd_schedule_delete(json_t *element, struct og_msg_params *params)
+{
+ struct og_dbi *dbi;
+ const char *key;
+ json_t *value;
+ int err;
+
+ if (json_typeof(element) != JSON_OBJECT)
+ return -1;
+
+ json_object_foreach(element, key, value) {
+ if (!strcmp(key, "id")) {
+ err = og_json_parse_string(value, &params->id);
+ params->flags |= OG_REST_PARAM_ID;
+ } else {
+ return -1;
+ }
+
+ if (err < 0)
+ break;
+ }
+
+ if (!og_msg_params_validate(params, OG_REST_PARAM_ID))
+ return -1;
+
+ dbi = og_dbi_open(&dbi_config);
+ if (!dbi) {
+ syslog(LOG_ERR, "cannot open connection database (%s:%d)\n",
+ __func__, __LINE__);
+ return -1;
+ }
+
+ err = og_dbi_schedule_delete(dbi, atoi(params->id));
+ og_dbi_close(dbi);
+
+ og_schedule_delete(og_loop, atoi(params->id));
+
+ return err;
+}
+
static int og_client_method_not_found(struct og_client *cli)
{
/* To meet RFC 7231, this function MUST generate an Allow header field
@@ -5007,6 +5375,36 @@ static int og_client_state_process_payload_rest(struct og_client *cli)
return og_client_bad_request(cli);
}
err = og_cmd_task_post(root, &params);
+ } else if (!strncmp(cmd, "schedule/create",
+ strlen("schedule/create"))) {
+ if (method != OG_METHOD_POST)
+ return og_client_method_not_found(cli);
+
+ if (!root) {
+ syslog(LOG_ERR, "command task with no payload\n");
+ return og_client_bad_request(cli);
+ }
+ err = og_cmd_schedule_create(root, &params);
+ } else if (!strncmp(cmd, "schedule/delete",
+ strlen("schedule/delete"))) {
+ if (method != OG_METHOD_POST)
+ return og_client_method_not_found(cli);
+
+ if (!root) {
+ syslog(LOG_ERR, "command task with no payload\n");
+ return og_client_bad_request(cli);
+ }
+ err = og_cmd_schedule_delete(root, &params);
+ } else if (!strncmp(cmd, "schedule/update",
+ strlen("schedule/update"))) {
+ if (method != OG_METHOD_POST)
+ return og_client_method_not_found(cli);
+
+ if (!root) {
+ syslog(LOG_ERR, "command task with no payload\n");
+ return og_client_bad_request(cli);
+ }
+ err = og_cmd_schedule_update(root, &params);
} else {
syslog(LOG_ERR, "unknown command: %.32s ...\n", cmd);
err = og_client_not_found(cli);
@@ -6015,9 +6413,10 @@ static int og_socket_server_init(const char *port)
int main(int argc, char *argv[])
{
struct ev_io ev_io_server, ev_io_server_rest, ev_io_agent_rest;
- struct ev_loop *loop = ev_default_loop(0);
int i;
+ og_loop = ev_default_loop(0);
+
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
exit(EXIT_FAILURE);
@@ -6048,21 +6447,26 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE);
ev_io_init(&ev_io_server, og_server_accept_cb, socket_s, EV_READ);
- ev_io_start(loop, &ev_io_server);
+ ev_io_start(og_loop, &ev_io_server);
socket_rest = og_socket_server_init("8888");
if (socket_rest < 0)
exit(EXIT_FAILURE);
ev_io_init(&ev_io_server_rest, og_server_accept_cb, socket_rest, EV_READ);
- ev_io_start(loop, &ev_io_server_rest);
+ ev_io_start(og_loop, &ev_io_server_rest);
socket_agent_rest = og_socket_server_init("8889");
if (socket_agent_rest < 0)
exit(EXIT_FAILURE);
ev_io_init(&ev_io_agent_rest, og_server_accept_cb, socket_agent_rest, EV_READ);
- ev_io_start(loop, &ev_io_agent_rest);
+ ev_io_start(og_loop, &ev_io_agent_rest);
+
+ if (og_dbi_schedule_get() < 0)
+ exit(EXIT_FAILURE);
+
+ og_schedule_next(og_loop);
infoLog(1); // Inicio de sesiĆ³n
@@ -6072,7 +6476,7 @@ int main(int argc, char *argv[])
syslog(LOG_INFO, "Waiting for connections\n");
while (1)
- ev_loop(loop, 0);
+ ev_loop(og_loop, 0);
exit(EXIT_SUCCESS);
}
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);
+}
diff --git a/sources/schedule.h b/sources/schedule.h
new file mode 100644
index 0000000..7195a05
--- /dev/null
+++ b/sources/schedule.h
@@ -0,0 +1,37 @@
+#ifndef _OG_SCHEDULE_H_
+#define _OG_SCHEDULE_H_
+
+#include <stdint.h>
+#include "dbi.h"
+#include "list.h"
+#include <ev.h>
+
+struct og_schedule_time {
+ uint32_t years;
+ uint32_t months;
+ uint32_t weeks;
+ uint32_t week_days;
+ uint32_t days;
+ uint32_t hours;
+ uint32_t am_pm;
+ uint32_t minutes;
+};
+
+struct og_schedule {
+ struct list_head list;
+ struct ev_timer timer;
+ time_t seconds;
+ unsigned int task_id;
+ unsigned int schedule_id;
+};
+
+void og_schedule_create(unsigned int schedule_id, unsigned int task_id,
+ struct og_schedule_time *time);
+void og_schedule_update(struct ev_loop *loop, unsigned int schedule_id,
+ unsigned int task_id, struct og_schedule_time *time);
+void og_schedule_delete(struct ev_loop *loop, uint32_t schedule_id);
+void og_schedule_next(struct ev_loop *loop);
+void og_schedule_refresh(struct ev_loop *loop);
+void og_dbi_schedule_task(unsigned int task_id);
+
+#endif