diff options
-rw-r--r-- | sources/list.h | 162 | ||||
-rw-r--r-- | sources/ogAdmServer.c | 413 | ||||
-rw-r--r-- | sources/ogAdmServer.h | 1 | ||||
-rwxr-xr-x | tests/run-tests.sh | 1 | ||||
-rw-r--r-- | tests/task.json | 1 |
5 files changed, 576 insertions, 2 deletions
diff --git a/sources/list.h b/sources/list.h new file mode 100644 index 0000000..8dfd476 --- /dev/null +++ b/sources/list.h @@ -0,0 +1,162 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +#include <stddef.h> + +#define container_of(ptr, type, member) ({ \ + typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +/* + * These are non-NULL pointers that will result in page faults + * under normal circumstances, used to verify that nobody uses + * non-initialized list entries. + */ +#define LIST_POISON1 ((void *) 0x00100100) +#define LIST_POISON2 ((void *) 0x00200200) + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_add(struct list_head *new, + struct list_head *prev, + struct list_head *next) +{ + next->prev = new; + new->next = next; + new->prev = prev; + prev->next = new; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static inline void list_add(struct list_head *new, struct list_head *head) +{ + __list_add(new, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @new: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static inline void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static inline void __list_del(struct list_head * prev, struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is + * in an undefined state. + */ +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + container_of(ptr, type, member) + +/** + * list_first_entry - get the first element from a list + * @ptr: the list head to take the element from. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_head within the struct. + * + * Note, that list is expected to be not empty. + */ +#define list_first_entry(ptr, type, member) \ + list_entry((ptr)->next, type, member) + +/** + * list_for_each_entry - iterate over list of given type + * @pos: the type * to use as a loop counter. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) + +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +#endif diff --git a/sources/ogAdmServer.c b/sources/ogAdmServer.c index c465ffe..37119b1 100644 --- a/sources/ogAdmServer.c +++ b/sources/ogAdmServer.c @@ -9,6 +9,7 @@ #include "ogAdmServer.h" #include "ogAdmLib.c" #include "dbi.h" +#include "list.h" #include <ev.h> #include <syslog.h> #include <sys/ioctl.h> @@ -853,6 +854,76 @@ bool recorreProcedimientos(struct og_dbi *dbi, char *parametros, FILE *fileexe, return true; } + +struct og_task { + uint32_t procedure_id; + uint32_t type_scope; + uint32_t scope; + const char *filtered_scope; + const char *params; +}; + +struct og_cmd { + struct list_head list; + uint32_t client_id; + const char *params; + const char *ip; + const char *mac; +}; + +static LIST_HEAD(cmd_list); + +static const struct og_cmd *og_cmd_find(char *client_ip) +{ + struct og_cmd *cmd, *next; + + list_for_each_entry_safe(cmd, next, &cmd_list, list) { + if (strcmp(cmd->ip, client_ip)) + continue; + + list_del(&cmd->list); + return cmd; + } + + return NULL; +} + +static void og_cmd_free(const struct og_cmd *cmd) +{ + free((void *)cmd->params); + free((void *)cmd->ip); + free((void *)cmd->mac); + free((void *)cmd); +} + +static TRAMA *og_msg_alloc(char *data, unsigned int len); +static void og_msg_free(TRAMA *ptrTrama); + +static int og_deliver_pending_command(const struct og_cmd *cmd, int *socket, + int idx) +{ + char buf[4096]; + TRAMA *msg; + int len; + + len = snprintf(buf, sizeof(buf), "%s\r", cmd->params); + + msg = og_msg_alloc(buf, len); + if (!msg) + return false; + + strcpy(tbsockets[idx].estado, CLIENTE_OCUPADO); + if (!mandaTrama(socket, msg)) { + syslog(LOG_ERR, "failed to send response to %s reason=%s\n", + cmd->ip, strerror(errno)); + return false; + } + og_msg_free(msg); + og_cmd_free(cmd); + + return true; +} + // ________________________________________________________________________________________________________ // Función: ComandosPendientes // @@ -869,6 +940,7 @@ static bool ComandosPendientes(TRAMA *ptrTrama, struct og_client *cli) { int socket_c = og_client_socket(cli); char *ido,*iph,pids[LONPRM]; + const struct og_cmd *cmd; int ids, idx; iph = copiaParametro("iph",ptrTrama); // Toma dirección IP @@ -880,7 +952,13 @@ static bool ComandosPendientes(TRAMA *ptrTrama, struct og_client *cli) syslog(LOG_ERR, "client does not exist\n"); return false; } - if (buscaComandos(ido, ptrTrama, &ids)) { // Existen comandos pendientes + + cmd = og_cmd_find(iph); + if (cmd) { + liberaMemoria(iph); + liberaMemoria(ido); + return og_deliver_pending_command(cmd, &socket_c, idx); + } else if (buscaComandos(ido, ptrTrama, &ids)) { // Existen comandos pendientes ptrTrama->tipo = MSG_COMANDO; sprintf(pids, "\rids=%d\r", ids); strcat(ptrTrama->parametros, pids); @@ -2936,6 +3014,7 @@ struct og_msg_params { bool echo; struct og_partition partition_setup[OG_PARTITION_MAX]; struct og_sync_params sync_setup; + const char *task_id; uint64_t flags; }; @@ -2970,6 +3049,7 @@ struct og_msg_params { #define OG_REST_PARAM_SYNC_PATH (1UL << 28) #define OG_REST_PARAM_SYNC_METHOD (1UL << 29) #define OG_REST_PARAM_ECHO (1UL << 30) +#define OG_REST_PARAM_TASK (1UL << 31) static bool og_msg_params_validate(const struct og_msg_params *params, const uint64_t flags) @@ -4306,6 +4386,328 @@ static int og_cmd_restore_incremental_image(json_t *element, struct og_msg_param return 0; } +static int og_queue_task_command(struct og_dbi *dbi, const struct og_task *task, + char *query) +{ + struct og_cmd *cmd; + const char *msglog; + dbi_result result; + + result = dbi_conn_queryf(dbi->conn, query); + 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)) { + cmd = (struct og_cmd *)calloc(1, sizeof(struct og_cmd)); + if (!cmd) { + dbi_result_free(result); + return -1; + } + + cmd->client_id = dbi_result_get_uint(result, "idordenador"); + cmd->params = task->params; + + cmd->ip = strdup(dbi_result_get_string(result, "ip")); + cmd->mac = strdup(dbi_result_get_string(result, "mac")); + + list_add_tail(&cmd->list, &cmd_list); + + } + + dbi_result_free(result); + + return 0; +} + +static int og_queue_task_group_clients(struct og_dbi *dbi, struct og_task *task, + char *query) +{ + + const char *msglog; + dbi_result result; + + result = dbi_conn_queryf(dbi->conn, query); + 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)) { + uint32_t group_id = dbi_result_get_uint(result, "idgrupo"); + + sprintf(query, "SELECT idgrupo FROM gruposordenadores " + "WHERE grupoid=%d", group_id); + if (og_queue_task_group_clients(dbi, task, query)) { + dbi_result_free(result); + return -1; + } + + sprintf(query,"SELECT ip, mac, idordenador FROM ordenadores " + "WHERE grupoid=%d", group_id); + if (og_queue_task_command(dbi, task, query)) { + dbi_result_free(result); + return -1; + } + + } + + dbi_result_free(result); + + return 0; +} + +static int og_queue_task_classrooms(struct og_dbi *dbi, struct og_task *task, + char *query) +{ + + const char *msglog; + dbi_result result; + + result = dbi_conn_queryf(dbi->conn, query); + 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)) { + uint32_t classroom_id = dbi_result_get_uint(result, "idaula"); + + sprintf(query, "SELECT idgrupo FROM gruposordenadores " + "WHERE idaula=%d AND grupoid=0", classroom_id); + if (og_queue_task_group_clients(dbi, task, query)) { + dbi_result_free(result); + return -1; + } + + sprintf(query,"SELECT ip, mac, idordenador FROM ordenadores " + "WHERE idaula=%d AND grupoid=0", classroom_id); + if (og_queue_task_command(dbi, task, query)) { + dbi_result_free(result); + return -1; + } + + } + + dbi_result_free(result); + + return 0; +} + +static int og_queue_task_group_classrooms(struct og_dbi *dbi, + struct og_task *task, char *query) +{ + + const char *msglog; + dbi_result result; + + result = dbi_conn_queryf(dbi->conn, query); + 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)) { + uint32_t group_id = dbi_result_get_uint(result, "idgrupo"); + + sprintf(query, "SELECT idgrupo FROM grupos " + "WHERE grupoid=%d AND tipo=%d", group_id, AMBITO_GRUPOSAULAS); + if (og_queue_task_group_classrooms(dbi, task, query)) { + dbi_result_free(result); + return -1; + } + + sprintf(query,"SELECT idaula FROM aulas WHERE grupoid=%d", group_id); + if (og_queue_task_classrooms(dbi, task, query)) { + dbi_result_free(result); + return -1; + } + + } + + dbi_result_free(result); + + return 0; +} + +static int og_queue_task_center(struct og_dbi *dbi, struct og_task *task, + char *query) +{ + + sprintf(query,"SELECT idgrupo FROM grupos WHERE idcentro=%i AND grupoid=0 AND tipo=%d", + task->scope, AMBITO_GRUPOSAULAS); + if (og_queue_task_group_classrooms(dbi, task, query)) + return -1; + + sprintf(query,"SELECT idaula FROM aulas WHERE idcentro=%i AND grupoid=0", + task->scope); + if (og_queue_task_classrooms(dbi, task, query)) + return -1; + + return 0; +} + +static int og_queue_task_clients(struct og_dbi *dbi, struct og_task *task) +{ + char query[4096]; + + switch (task->type_scope) { + case AMBITO_CENTROS: + return og_queue_task_center(dbi, task, query); + case AMBITO_GRUPOSAULAS: + sprintf(query, "SELECT idgrupo FROM grupos " + "WHERE idgrupo=%i AND tipo=%d", + task->scope, AMBITO_GRUPOSAULAS); + return og_queue_task_group_classrooms(dbi, task, query); + case AMBITO_AULAS: + sprintf(query, "SELECT idaula FROM aulas " + "WHERE idaula = %d", task->scope); + return og_queue_task_classrooms(dbi, task, query); + case AMBITO_GRUPOSORDENADORES: + sprintf(query, "SELECT idgrupo FROM gruposordenadores " + "WHERE idgrupo = %d", task->scope); + return og_queue_task_group_clients(dbi, task, query); + case AMBITO_ORDENADORES: + sprintf(query, "SELECT ip, mac, idordenador " + "FROM ordenadores " + "WHERE idordenador = %d", task->scope); + return og_queue_task_command(dbi, task, query); + } + return 0; +} + +static int og_queue_procedure(struct og_dbi *dbi, struct og_task *task) +{ + uint32_t procedure_id; + const char *msglog; + dbi_result result; + + result = dbi_conn_queryf(dbi->conn, + "SELECT parametros, procedimientoid " + "FROM procedimientos_acciones " + "WHERE idprocedimiento=%d ORDER BY orden", task->procedure_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; + } + + while (dbi_result_next_row(result)) { + procedure_id = dbi_result_get_uint(result, "procedimientoid"); + if (procedure_id > 0) { + task->procedure_id = procedure_id; + if (og_queue_procedure(dbi, task)) + return -1; + continue; + } + + task->params = strdup(dbi_result_get_string(result, "parametros")); + if (og_queue_task_clients(dbi, task)) + return -1; + } + + dbi_result_free(result); + + return 0; +} + +static int og_queue_task(struct og_dbi *dbi, uint32_t task_id) +{ + struct og_task task = {}; + uint32_t task_id_next; + const char *msglog; + dbi_result result; + + result = dbi_conn_queryf(dbi->conn, + "SELECT tareas_acciones.orden, " + "tareas_acciones.idprocedimiento, " + "tareas_acciones.tareaid, " + "tareas.ambito, " + "tareas.idambito, " + "tareas.restrambito " + " FROM tareas" + " INNER JOIN tareas_acciones ON tareas_acciones.idtarea=tareas.idtarea" + " WHERE tareas_acciones.idtarea=%u ORDER BY tareas_acciones.orden ASC", task_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; + } + + while (dbi_result_next_row(result)) { + task_id_next = dbi_result_get_uint(result, "procedimientoid"); + + if (task_id_next > 0) { + if (og_queue_task(dbi, task_id_next)) + return -1; + + continue; + } + task.procedure_id = dbi_result_get_uint(result, "idprocedimiento"); + task.type_scope = dbi_result_get_uint(result, "ambito"); + task.scope = dbi_result_get_uint(result, "idambito"); + task.filtered_scope = dbi_result_get_string(result, "restrambito"); + + og_queue_procedure(dbi, &task); + + } + + dbi_result_free(result); + + return 0; +} + +static int og_cmd_task_post(json_t *element, struct og_msg_params *params) +{ + struct og_cmd *cmd; + 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, ¶ms->task_id); + params->flags |= OG_REST_PARAM_TASK; + } + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_TASK)) + return -1; + + dbi = og_dbi_open(&dbi_config); + if (!dbi) { + syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", + __func__, __LINE__); + return -1; + } + + og_queue_task(dbi, atoi(params->task_id)); + og_dbi_close(dbi); + + list_for_each_entry(cmd, &cmd_list, list) + params->ips_array[params->ips_array_len++] = cmd->ip; + + return og_cmd_legacy_send(params, "Actualizar", CLIENTE_OCUPADO); +} + static int og_client_method_not_found(struct og_client *cli) { /* To meet RFC 7231, this function MUST generate an Allow header field @@ -4615,6 +5017,15 @@ static int og_client_state_process_payload_rest(struct og_client *cli) } err = og_cmd_run_schedule(root, ¶ms); + } else if (!strncmp(cmd, "task/run", strlen("task/run"))) { + 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_task_post(root, ¶ms); } else { syslog(LOG_ERR, "unknown command: %.32s ...\n", cmd); err = og_client_not_found(cli); diff --git a/sources/ogAdmServer.h b/sources/ogAdmServer.h index 0bfa61e..acc88c2 100644 --- a/sources/ogAdmServer.h +++ b/sources/ogAdmServer.h @@ -18,7 +18,6 @@ #include <netinet/in.h> #include <arpa/inet.h> #include <stdbool.h> -#include </usr/include/mysql/mysql.h> #include "ogAdmLib.h" // ________________________________________________________________________________________________________ // Variables globales diff --git a/tests/run-tests.sh b/tests/run-tests.sh index 893e026..b68dc7f 100755 --- a/tests/run-tests.sh +++ b/tests/run-tests.sh @@ -20,3 +20,4 @@ curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/image/create/inc curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/image/restore/basic -d @restore_basic_image.json curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/image/restore/incremental -d @restore_incremental_image.json curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/run/schedule -d @run_schedule.json +curl -X POST -H "Authorization: $API_KEY" http://127.0.0.1:8888/task/run -d @task.json diff --git a/tests/task.json b/tests/task.json new file mode 100644 index 0000000..fe9d19c --- /dev/null +++ b/tests/task.json @@ -0,0 +1 @@ +{ "task" : "13" } |