From 141b0797e17f616d633704a4b011fde515496ab3 Mon Sep 17 00:00:00 2001 From: "Jose M. Guisado" Date: Wed, 9 Jun 2021 11:25:29 +0000 Subject: #915 Add schedule/command Enables ogserver to schedule commands (also referred as actions in legacy web console jargon). This feature enables ogserver to write in the "acciones" table in order to have full capabilities for command scheduling purposes, thus not depending in the legacy web console to insert into "acciones" table. --- Makefile.am | 3 +- src/json.h | 6 ++ src/legacy.c | 269 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/legacy.h | 6 ++ src/rest.c | 219 ++++++++++++++++++++++++++++++++++++++++++++++-- src/rest.h | 4 + 6 files changed, 497 insertions(+), 10 deletions(-) create mode 100644 src/legacy.c create mode 100644 src/legacy.h diff --git a/Makefile.am b/Makefile.am index bf93816..2dc6fbb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -14,4 +14,5 @@ ogserver_SOURCES= src/ogAdmServer.c \ src/client.c \ src/json.c \ src/ogAdmLib.c \ - src/wol.c + src/wol.c \ + src/legacy.c diff --git a/src/json.h b/src/json.h index 193544a..08a78d0 100644 --- a/src/json.h +++ b/src/json.h @@ -92,4 +92,10 @@ struct og_msg_params { uint64_t flags; }; +struct og_cmd_json { + const char *type; + json_t *json; + uint32_t flags; +}; + #endif diff --git a/src/legacy.c b/src/legacy.c new file mode 100644 index 0000000..b1f1084 --- /dev/null +++ b/src/legacy.c @@ -0,0 +1,269 @@ +#include +#include +#include +#include + +#include "json.h" +#include "rest.h" +#include "legacy.h" + +#define LEGACY_CMD_MAX 4096 + +static const char *og_cmd_wol_to_legacy(struct og_cmd_json *cmd) +{ + char legacy_cmd[LEGACY_CMD_MAX + 1] = {}; + const json_t *root = cmd->json; + const char *wol_type; + uint32_t type; + int len; + + wol_type = json_string_value(json_object_get(root, "type")); + if (!wol_type) + return NULL; + + if (!strcmp(wol_type, "broadcast")) + type = 1; + else + type = 2; + + len = snprintf(legacy_cmd, sizeof(legacy_cmd), "nfn=Arrancar\rmar=%u", type); + if (len >= (int)sizeof(legacy_cmd)) + return NULL; + + return strdup(legacy_cmd); +} + +static const char *og_cmd_poweroff_to_legacy(struct og_cmd_json *cmd) +{ + char legacy_cmd[LEGACY_CMD_MAX + 1] = {}; + int len; + + len = snprintf(legacy_cmd, sizeof(legacy_cmd), "nfn=Apagar"); + if (len >= (int)sizeof(legacy_cmd)) + return NULL; + + return strdup(legacy_cmd); +} + +static const char *og_cmd_reboot_to_legacy(struct og_cmd_json *cmd) +{ + char legacy_cmd[LEGACY_CMD_MAX + 1] = {}; + int len; + + len = snprintf(legacy_cmd, sizeof(legacy_cmd), "nfn=Reiniciar"); + if (len >= (int)sizeof(legacy_cmd)) + return NULL; + + return strdup(legacy_cmd); +} + +static const char *og_cmd_session_to_legacy(struct og_cmd_json *cmd) +{ + char legacy_cmd[LEGACY_CMD_MAX + 1] = {}; + const json_t *root = cmd->json; + const char *dsk, *par; + int len; + + dsk = json_string_value(json_object_get(root, "disk")); + if (!dsk) + return NULL; + par = json_string_value(json_object_get(root, "part")); + if (!par) + return NULL; + + len = snprintf(legacy_cmd, sizeof(legacy_cmd), + "nfn=IniciarSesion\rdsk=%s\rpar=%s", + dsk, par); + if (len >= (int)sizeof(legacy_cmd)) + return NULL; + + return strdup(legacy_cmd); +} + +static const char *og_cmd_software_to_legacy(struct og_cmd_json *cmd) +{ + char legacy_cmd[LEGACY_CMD_MAX + 1] = {}; + const json_t *root = cmd->json; + const char *dsk, *par; + int len; + + dsk = json_string_value(json_object_get(root, "disk")); + if (!dsk) + return NULL; + par = json_string_value(json_object_get(root, "partition")); + if (!par) + return NULL; + + len = snprintf(legacy_cmd, sizeof(legacy_cmd), + "nfn=InventarioSoftware\rdsk=%s\rpar=%s", + dsk, par); + if (len >= (int)sizeof(legacy_cmd)) + return NULL; + + return strdup(legacy_cmd); +} + +static const char *og_cmd_hardware_to_legacy(struct og_cmd_json *cmd) +{ + char legacy_cmd[LEGACY_CMD_MAX + 1] = {}; + int len; + + len = snprintf(legacy_cmd, sizeof(legacy_cmd), + "nfn=InventarioHardware"); + if (len >= (int)sizeof(legacy_cmd)) + return NULL; + + return strdup(legacy_cmd); +} + +static const char *og_cmd_shell_run_to_legacy(struct og_cmd_json *cmd) +{ + const json_t *root = cmd->json; + char legacy_cmd[LEGACY_CMD_MAX + 1] = {}; + const char *scp; + int len; + + scp = json_string_value(json_object_get(root, "run")); + if (!scp) + return NULL; + + len = snprintf(legacy_cmd, sizeof(legacy_cmd), + "nfn=EjecutarScript\rscp=%s", scp); + if (len >= (int)sizeof(legacy_cmd)) { + syslog(LOG_ERR, "script payload too large (%s:%d)\n", + __func__, __LINE__); + return NULL; + } + + return strdup(legacy_cmd); +} + +static char *og_cmd_image_create_to_legacy(struct og_cmd_json *cmd) +{ + char legacy_cmd[LEGACY_CMD_MAX + 1] = {}; + struct og_msg_params params = {}; + json_t *root = cmd->json; + int len; + + if (og_json_parse_create_image(root, ¶ms) < 0) + return NULL; + + len = snprintf(legacy_cmd, sizeof(legacy_cmd), + "nfn=CrearImagen\rdsk=%s\rpar=%s\rcpt=%s\ridi=%s\rnci=%s\ripr=%s", + params.disk, params.partition, params.code, params.id, + params.name, params.repository); + if (len >= (int)sizeof(legacy_cmd)) + return NULL; + + return strdup(legacy_cmd); +} + +static const char *og_cmd_image_restore_to_legacy(struct og_cmd_json *cmd) +{ + char legacy_cmd[LEGACY_CMD_MAX + 1] = {}; + struct og_msg_params params = {}; + json_t *root = cmd->json; + int len; + + if (og_json_parse_restore_image(root, ¶ms) < 0) + return NULL; + + len = snprintf(legacy_cmd, sizeof(legacy_cmd), + "nfn=RestaurarImagen\rdsk=%s\rpar=%s\ridi=%s\rnci=%s\ripr=%s\rifs=%s\rptc=%s", + params.disk, params.partition, params.id, params.name, + params.repository, params.profile, params.type); + if (len >= (int)sizeof(legacy_cmd)) { + return NULL; + } + + return strdup(legacy_cmd); +} + +static const char *og_cmd_setup_to_legacy(struct og_cmd_json *cmd) +{ + char legacy_cmd[LEGACY_CMD_MAX + 1] = {}; + uint32_t bufsiz = sizeof(legacy_cmd); + const char *dsk, *ttp, *che, *tch; + struct og_msg_params params = {}; + json_t *partition_setup, *value; + const json_t *root = cmd->json; + uint32_t consumed = 0; + size_t index; + int len; + + dsk = json_string_value(json_object_get(root, "disk")); + if (!dsk) + return NULL; + ttp = json_string_value(json_object_get(root, "type")); + if (!ttp) + return NULL; + che = json_string_value(json_object_get(root, "cache")); + if (!che) + return NULL; + tch = json_string_value(json_object_get(root, "cache_size")); + if (!tch) + return NULL; + + len = snprintf(legacy_cmd + consumed, bufsiz, "nfn=Configurar\rttp=%s\rdsk=%s\rcfg=dis=%s*che=%s*tch=%s!", + ttp, dsk, dsk, che, tch); + if (len >= bufsiz) + return NULL; + consumed += len; + if (consumed < bufsiz) + bufsiz -= len; + + partition_setup = json_object_get(root, "partition_setup"); + if (!partition_setup) + return NULL; + if (og_json_parse_partition_setup(partition_setup, ¶ms) < 0) + return NULL; + + json_array_foreach(partition_setup, index, value) { + len = snprintf(legacy_cmd + consumed, bufsiz, "par=%s*cpt=%s*sfi=%s*tam=%s*ope=%s%%", + params.partition_setup[index].number, + params.partition_setup[index].code, + params.partition_setup[index].filesystem, + params.partition_setup[index].size, + params.partition_setup[index].format); + if (len >= bufsiz) + return NULL; + consumed += len; + if (consumed < bufsiz) + bufsiz -= len; + } + + return strdup(legacy_cmd); +} + +const char *og_msg_params_to_legacy(struct og_cmd_json *cmd) +{ + const char *legacy_cmd = NULL; + + if (!strncmp(cmd->type, "wol", strlen("wol"))) + legacy_cmd = og_cmd_wol_to_legacy(cmd); + else if (!strncmp(cmd->type, "poweroff", strlen("poweroff"))) + legacy_cmd = og_cmd_poweroff_to_legacy(cmd); + else if (!strncmp(cmd->type, "reboot", strlen("reboot"))) + legacy_cmd = og_cmd_reboot_to_legacy(cmd); + else if (!strncmp(cmd->type, "session", strlen("session"))) + legacy_cmd = og_cmd_session_to_legacy(cmd); + else if (!strncmp(cmd->type, "software", strlen("software"))) + legacy_cmd = og_cmd_software_to_legacy(cmd); + else if (!strncmp(cmd->type, "hardware", strlen("hardware"))) + legacy_cmd = og_cmd_hardware_to_legacy(cmd); + else if (!strncmp(cmd->type, "run", strlen("run"))) + legacy_cmd = og_cmd_shell_run_to_legacy(cmd); + else if (!strncmp(cmd->type, "image_create", strlen("image_create"))) + legacy_cmd = og_cmd_image_create_to_legacy(cmd); + else if (!strncmp(cmd->type, "image_restore", strlen("image_restore"))) + legacy_cmd = og_cmd_image_restore_to_legacy(cmd); + else if (!strncmp(cmd->type, "setup", strlen("setup"))) + legacy_cmd = og_cmd_setup_to_legacy(cmd); + + if (!legacy_cmd) { + syslog(LOG_ERR, "failed to translate command %s (%s:%d)\n", + cmd->type, __func__, __LINE__); + } + + return legacy_cmd; +} diff --git a/src/legacy.h b/src/legacy.h new file mode 100644 index 0000000..d72963c --- /dev/null +++ b/src/legacy.h @@ -0,0 +1,6 @@ +#ifndef _OG_LEGACY_H +#define _OG_LEGACY_H + +const char *og_msg_params_to_legacy(struct og_cmd_json *cmd); + +#endif diff --git a/src/rest.c b/src/rest.c index e9eb7ca..feeb306 100644 --- a/src/rest.c +++ b/src/rest.c @@ -15,6 +15,7 @@ #include "wol.h" #include "cfg.h" #include "schedule.h" +#include "legacy.h" #include #include #include @@ -162,8 +163,7 @@ static int og_json_parse_clients(json_t *element, struct og_msg_params *params) return 0; } -static int og_json_parse_partition_setup(json_t *element, - struct og_msg_params *params) +int og_json_parse_partition_setup(json_t *element, struct og_msg_params *params) { unsigned int i; json_t *k; @@ -2041,12 +2041,11 @@ static int og_cmd_images(char *buffer_reply) return 0; } -static int og_cmd_create_image(json_t *element, struct og_msg_params *params) +int og_json_parse_create_image(json_t *element, + struct og_msg_params *params) { - char new_image_id[OG_DB_INT_MAXLEN + 1]; struct og_image image = {}; - json_t *value, *clients; - struct og_dbi *dbi; + json_t *value; const char *key; int err = 0; @@ -2088,6 +2087,21 @@ static int og_cmd_create_image(json_t *element, struct og_msg_params *params) return err; } + return 0; +} + +static int og_cmd_create_image(json_t *element, struct og_msg_params *params) +{ + char new_image_id[OG_DB_INT_MAXLEN + 1]; + struct og_image image = {}; + struct og_dbi *dbi; + json_t *clients; + int err = 0; + + err = og_json_parse_create_image(element, params); + if (err < 0) + return err; + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | OG_REST_PARAM_DISK | OG_REST_PARAM_PARTITION | @@ -2126,10 +2140,10 @@ static int og_cmd_create_image(json_t *element, struct og_msg_params *params) clients); } -static int og_cmd_restore_image(json_t *element, struct og_msg_params *params) +int og_json_parse_restore_image(json_t *element, struct og_msg_params *params) { - json_t *clients, *value; const char *key; + json_t *value; int err = 0; if (json_typeof(element) != JSON_OBJECT) @@ -2165,6 +2179,18 @@ static int og_cmd_restore_image(json_t *element, struct og_msg_params *params) return err; } + return 0; +} + +static int og_cmd_restore_image(json_t *element, struct og_msg_params *params) +{ + json_t *clients; + int err = 0; + + err = og_json_parse_restore_image(element, params); + if (err < 0) + return err; + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | OG_REST_PARAM_DISK | OG_REST_PARAM_PARTITION | @@ -2953,7 +2979,7 @@ static int og_dbi_queue_command(struct og_dbi *dbi, uint32_t task_id, result = dbi_conn_queryf(dbi->conn, "SELECT idaccion, idcentro, idordenador, parametros " "FROM acciones " - "WHERE sesion = %u", task_id); + "WHERE idaccion = %u", task_id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", @@ -4282,6 +4308,168 @@ static int og_cmd_post_room_delete(json_t *element, return 0; } +enum { + OG_SCHEDULE_CMD_TYPE = 0, + OG_SCHEDULE_CMD_PARAMS, +}; + +static bool og_cmd_validate(const struct og_cmd_json *cmd, + const uint64_t flags) +{ + return (cmd->flags & flags) == flags; +} + + +static int og_cmd_post_schedule_command(json_t *element, + struct og_msg_params *params) +{ + char *centerid_query = "SELECT o.idordenador, c.idcentro " + "FROM `ordenadores` AS o " + "INNER JOIN aulas AS a ON o.idaula = a.idaula " + "INNER JOIN centros AS c ON a.idcentro = c.idcentro " + "WHERE o.ip = '%s';"; + int center_id, client_id, len; + struct og_cmd_json cmd = {}; + const char *legacy_params; + const char *key, *msglog; + struct og_dbi *dbi; + char task_id[128]; + uint32_t sequence; + bool when = false; + dbi_result result; + json_t *value; + int err = 0, i; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) { + err = og_json_parse_clients(value, params); + } else if (!strcmp(key, "command")) { + err = og_json_parse_string(value, &cmd.type); + cmd.flags |= OG_SCHEDULE_CMD_TYPE; + } else if (!strcmp(key, "params")) { + cmd.json = value; + cmd.flags |= OG_SCHEDULE_CMD_PARAMS; + } else if (!strcmp(key, "when")) { + err = og_json_parse_time_params(value, params); + when = true; + } + + if (err < 0) + return err; + } + + if (!og_cmd_validate(&cmd, OG_SCHEDULE_CMD_TYPE | + OG_SCHEDULE_CMD_PARAMS)) + return -1; + + if (!when) { + params->time.check_stale = false; + og_schedule_time_now(¶ms->time); + params->flags |= OG_REST_PARAM_TIME_YEARS | + OG_REST_PARAM_TIME_MONTHS | + OG_REST_PARAM_TIME_WEEKS | + OG_REST_PARAM_TIME_WEEK_DAYS | + OG_REST_PARAM_TIME_DAYS | + OG_REST_PARAM_TIME_HOURS | + OG_REST_PARAM_TIME_AM_PM | + OG_REST_PARAM_TIME_MINUTES; + } else { + params->time.check_stale = true; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | + OG_REST_PARAM_TIME_YEARS | + OG_REST_PARAM_TIME_MONTHS | + OG_REST_PARAM_TIME_WEEKS | + OG_REST_PARAM_TIME_WEEK_DAYS | + OG_REST_PARAM_TIME_DAYS | + OG_REST_PARAM_TIME_HOURS | + OG_REST_PARAM_TIME_MINUTES | + OG_REST_PARAM_TIME_AM_PM)) + return -1; + + params->type = "command"; + dbi = og_dbi_open(&ogconfig.db); + if (!dbi) { + syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", + __func__, __LINE__); + goto err_dbi_open; + } + + legacy_params = og_msg_params_to_legacy(&cmd); + if (!legacy_params) + goto err_legacy_params; + + /* ips_array -> ids */ + for (i = 0; i < params->ips_array_len; i++) { + + result = dbi_conn_queryf(dbi->conn, centerid_query, params->ips_array[i]); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + goto err_dbi_result; + } + if (dbi_result_get_numrows(result) != 1) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "client not found (%s:%d) %s\n", + __func__, __LINE__, msglog); + goto err_dbi; + } + + if (!dbi_result_next_row(result)) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to get idcentro (%s:%d) %s\n", + __func__, __LINE__, msglog); + goto err_dbi; + } + center_id = dbi_result_get_uint(result, "idcentro"); + if (!center_id) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to get idcentro (%s:%d) %s\n", + __func__, __LINE__, msglog); + goto err_dbi; + } + client_id = dbi_result_get_uint(result, "idordenador"); + dbi_result_free(result); + + result = dbi_conn_queryf(dbi->conn, "INSERT INTO acciones (idordenador, " + "idcentro, parametros)" + "VALUES (%d, %d, '%s')", + client_id, center_id, legacy_params); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + goto err_dbi_result; + } + dbi_result_free(result); + + sequence = dbi_conn_sequence_last(dbi->conn, NULL); + len = snprintf(task_id, sizeof(sequence), "%d", sequence); + if (len >= (int)sizeof(task_id)) { + syslog(LOG_ERR, "truncated snprintf (%s:%d)\n", + __func__, __LINE__); + goto err_dbi; + } + params->task_id = task_id; + og_task_schedule_create(params); + } + + free((char *)legacy_params); + og_dbi_close(dbi); + return 0; + +err_dbi: + dbi_result_free(result); +err_dbi_result: + free((char *)legacy_params); +err_legacy_params: + og_dbi_close(dbi); +err_dbi_open: + return -1; +} + static int og_client_method_not_found(struct og_client *cli) { /* To meet RFC 7231, this function MUST generate an Allow header field @@ -4823,6 +5011,19 @@ int og_client_state_process_payload_rest(struct og_client *cli) goto err_process_rest_payload; } err = og_cmd_post_procedure_add(root, ¶ms); + } else if (!strncmp(cmd, "schedule/command", strlen("schedule/command"))) { + if (method != OG_METHOD_POST) { + err = og_client_method_not_found(cli); + goto err_process_rest_payload; + } + + if (!root) { + syslog(LOG_ERR, + "command schedule action with no payload\n"); + err = og_client_bad_request(cli); + goto err_process_rest_payload; + } + err = og_cmd_post_schedule_command(root, ¶ms); } else { syslog(LOG_ERR, "unknown command: %.32s ...\n", cmd); err = og_client_not_found(cli); diff --git a/src/rest.h b/src/rest.h index 43ddd66..508a234 100644 --- a/src/rest.h +++ b/src/rest.h @@ -94,4 +94,8 @@ struct og_cmd { const struct og_cmd *og_cmd_find(const char *client_ip); void og_cmd_free(const struct og_cmd *cmd); +int og_json_parse_partition_setup(json_t *element, struct og_msg_params *params); +int og_json_parse_create_image(json_t *element, struct og_msg_params *params); +int og_json_parse_restore_image(json_t *element, struct og_msg_params *params); + #endif -- cgit v1.2.3-18-g5258