diff options
author | OpenGnSys Support Team <soporte-og@soleta.eu> | 2020-06-26 20:13:42 +0200 |
---|---|---|
committer | OpenGnSys Support Team <soporte-og@soleta.eu> | 2020-06-26 20:13:42 +0200 |
commit | 37e91b2ebb14082e366ed4a9b09d76a2f6163818 (patch) | |
tree | 71b10fc0e6452d3c5e12e167c55ac3f9aace63a8 /src | |
parent | 8215e0c6c1465eca8ec93cbf3f1f835e8bb7495b (diff) |
#971 rename sources folder to src
Use the same folder as in ogClient.
Diffstat (limited to 'src')
-rw-r--r-- | src/cfg.c | 165 | ||||
-rw-r--r-- | src/cfg.h | 33 | ||||
-rw-r--r-- | src/client.c | 689 | ||||
-rw-r--r-- | src/client.h | 6 | ||||
-rw-r--r-- | src/core.c | 433 | ||||
-rw-r--r-- | src/core.h | 10 | ||||
-rw-r--r-- | src/dbi.c | 45 | ||||
-rw-r--r-- | src/dbi.h | 51 | ||||
-rw-r--r-- | src/json.c | 85 | ||||
-rw-r--r-- | src/json.h | 77 | ||||
-rw-r--r-- | src/list.h | 162 | ||||
-rw-r--r-- | src/main.c | 84 | ||||
-rw-r--r-- | src/ogAdmLib.c | 362 | ||||
-rw-r--r-- | src/ogAdmLib.h | 120 | ||||
-rw-r--r-- | src/ogAdmServer.c | 1301 | ||||
-rw-r--r-- | src/ogAdmServer.h | 51 | ||||
-rw-r--r-- | src/rest.c | 3296 | ||||
-rw-r--r-- | src/rest.h | 97 | ||||
-rw-r--r-- | src/schedule.c | 478 | ||||
-rw-r--r-- | src/schedule.h | 65 | ||||
-rw-r--r-- | src/utils.c | 22 | ||||
-rw-r--r-- | src/utils.h | 6 |
22 files changed, 7638 insertions, 0 deletions
diff --git a/src/cfg.c b/src/cfg.c new file mode 100644 index 0000000..54067b0 --- /dev/null +++ b/src/cfg.c @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2020 Soleta Networks <info@soleta.eu> + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the + * Free Software Foundation, version 3. + */ + +#include "json.h" +#include "cfg.h" +#include "ogAdmServer.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <syslog.h> + +static int parse_json_rest(struct og_server_cfg *cfg, json_t *element) +{ + const char *key; + json_t *value; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "ip")) { + if (og_json_parse_string(value, &cfg->rest.ip) < 0) + return -1; + } else if (!strcmp(key, "port")) { + if (og_json_parse_string(value, &cfg->rest.port) < 0) + return -1; + } else if (!strcmp(key, "api_token")) { + if (og_json_parse_string(value, &cfg->rest.api_token) < 0) + return -1; + } else { + syslog(LOG_ERR, "unknown key `%s' in rest\n", key); + return -1; + } + } + + return 0; +} + +static int parse_json_db(struct og_server_cfg *cfg, json_t *element) +{ + const char *key; + json_t *value; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "ip")) { + if (og_json_parse_string(value, &cfg->db.ip) < 0) + return -1; + } else if (!strcmp(key, "user")) { + if (og_json_parse_string(value, &cfg->db.user) < 0) + return -1; + } else if (!strcmp(key, "pass")) { + if (og_json_parse_string(value, &cfg->db.pass) < 0) + return -1; + } else if (!strcmp(key, "name")) { + if (og_json_parse_string(value, &cfg->db.name) < 0) + return -1; + } else { + syslog(LOG_ERR, "unknown key `%s' in db\n", key); + return -1; + } + } + + return 0; +} + +static int parse_json_wol(struct og_server_cfg *cfg, json_t *element) +{ + const char *key; + json_t *value; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "interface")) { + if (og_json_parse_string(value, &cfg->wol.interface) < 0) + return -1; + } else { + syslog(LOG_ERR, "unknown key `%s' in wol\n", key); + return -1; + } + } + + return 0; +} + +#define OG_SERVER_CFG_REST (1 << 0) +#define OG_SERVER_CFG_DB (1 << 1) +#define OG_SERVER_CFG_WOL (1 << 2) + +int parse_json_config(const char *filename, struct og_server_cfg *cfg) +{ + json_t *root, *value; + uint32_t flags = 0; + json_error_t err; + const char *key; + char buf[4096]; + int fd, ret; + + fd = open(filename, O_RDONLY); + if (fd < 0) { + syslog(LOG_ERR, "Cannot open %s", filename); + return -1; + } + + ret = read(fd, buf, sizeof(buf)); + if (ret < 0 || ret == sizeof(buf)) { + syslog(LOG_ERR, "Cannot read from %s", filename); + return -1; + } + + root = json_loads(buf, 0, &err); + if (!root) { + syslog(LOG_ERR, "Cannot parse malformed json file"); + return -1; + } + + json_object_foreach(root, key, value) { + if (!strcmp(key, "rest")) { + if (parse_json_rest(cfg, value) < 0) + return -1; + + flags |= OG_SERVER_CFG_REST; + } else if (!strcmp(key, "wol")) { + if (parse_json_wol(cfg, value) < 0) + return -1; + + flags |= OG_SERVER_CFG_WOL; + } else if (!strcmp(key, "database")) { + if (parse_json_db(cfg, value) < 0) + return -1; + + flags |= OG_SERVER_CFG_DB; + } else { + syslog(LOG_ERR, "unknown key `%s' in %s\n", + key, filename); + ret = -1; + } + } + + if ((flags & OG_SERVER_CFG_REST) && + (flags & OG_SERVER_CFG_DB) && + (flags & OG_SERVER_CFG_WOL)) { + ret = 0; + } else { + syslog(LOG_ERR, "Missing attributes in json file"); + ret = -1; + } + + json_decref(root); + + return ret; +} + +void from_json_to_legacy(struct og_server_cfg *cfg) +{ + snprintf(servidoradm, sizeof(servidoradm), cfg->rest.ip); + snprintf(puerto, sizeof(puerto), cfg->rest.port); + snprintf(usuario, sizeof(usuario), cfg->db.user); + snprintf(pasguor, sizeof(pasguor), cfg->db.pass); + snprintf(datasource, sizeof(datasource), cfg->db.ip); + snprintf(catalog, sizeof(catalog), cfg->db.name); + snprintf(interface, sizeof(interface), cfg->wol.interface); + snprintf(auth_token, sizeof(auth_token), cfg->rest.api_token); +} diff --git a/src/cfg.h b/src/cfg.h new file mode 100644 index 0000000..cfb37bd --- /dev/null +++ b/src/cfg.h @@ -0,0 +1,33 @@ +#ifndef _OG_SERVER_CFG_H +#define _OG_SERVER_CFG_H + +struct og_server_cfg { + struct { + const char *user; + const char *pass; + const char *ip; + const char *name; + } db; + struct { + const char *ip; + const char *port; + const char *api_token; + } rest; + struct { + const char *interface; + } wol; +}; + +int parse_json_config(const char *filename, struct og_server_cfg *cfg); + +extern char auth_token[4096]; +extern char usuario[4096]; +extern char pasguor[4096]; +extern char catalog[4096]; +extern char datasource[4096]; +extern char interface[4096]; +extern char api_token[4096]; + +void from_json_to_legacy(struct og_server_cfg *cfg); + +#endif diff --git a/src/client.c b/src/client.c new file mode 100644 index 0000000..60a84cb --- /dev/null +++ b/src/client.c @@ -0,0 +1,689 @@ +/* + * Copyright (C) 2020 Soleta Networks <info@soleta.eu> + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the + * Free Software Foundation, version 3. + */ + +#include "ogAdmServer.h" +#include "dbi.h" +#include "utils.h" +#include "list.h" +#include "rest.h" +#include "json.h" +#include "schedule.h" +#include <syslog.h> +#include <sys/ioctl.h> +#include <ifaddrs.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <jansson.h> +#include <time.h> + +struct og_computer { + unsigned int id; + unsigned int center; + unsigned int room; + char name[OG_DB_COMPUTER_NAME_MAXLEN + 1]; + unsigned int procedure_id; +}; + +static int og_dbi_get_computer_info(struct og_computer *computer, + struct in_addr addr) +{ + const char *msglog; + struct og_dbi *dbi; + 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 ordenadores.idordenador," + " ordenadores.nombreordenador," + " ordenadores.idaula," + " ordenadores.idproautoexec," + " centros.idcentro FROM ordenadores " + "INNER JOIN aulas ON aulas.idaula=ordenadores.idaula " + "INNER JOIN centros ON centros.idcentro=aulas.idcentro " + "WHERE ordenadores.ip='%s'", inet_ntoa(addr)); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + og_dbi_close(dbi); + return -1; + } + if (!dbi_result_next_row(result)) { + syslog(LOG_ERR, "client does not exist in database (%s:%d)\n", + __func__, __LINE__); + dbi_result_free(result); + og_dbi_close(dbi); + return -1; + } + + computer->id = dbi_result_get_uint(result, "idordenador"); + computer->center = dbi_result_get_uint(result, "idcentro"); + computer->room = dbi_result_get_uint(result, "idaula"); + computer->procedure_id = dbi_result_get_uint(result, "idproautoexec"); + strncpy(computer->name, + dbi_result_get_string(result, "nombreordenador"), + OG_DB_COMPUTER_NAME_MAXLEN); + + dbi_result_free(result); + og_dbi_close(dbi); + + return 0; +} + +static int og_resp_probe(struct og_client *cli, json_t *data) +{ + const char *status = NULL; + const char *key; + json_t *value; + int err = 0; + + if (json_typeof(data) != JSON_OBJECT) + return -1; + + json_object_foreach(data, key, value) { + if (!strcmp(key, "status")) { + err = og_json_parse_string(value, &status); + if (err < 0) + return err; + } else { + return -1; + } + } + + if (!strcmp(status, "BSY")) + cli->status = OG_CLIENT_STATUS_BUSY; + else if (!strcmp(status, "OPG")) + cli->status = OG_CLIENT_STATUS_OGLIVE; + else if (!strcmp(status, "VDI")) + cli->status = OG_CLIENT_STATUS_VIRTUAL; + + return status ? 0 : -1; +} + +static int og_resp_shell_run(struct og_client *cli, json_t *data) +{ + const char *output = NULL; + char filename[4096]; + const char *key; + json_t *value; + int err = -1; + FILE *file; + + if (json_typeof(data) != JSON_OBJECT) + return -1; + + json_object_foreach(data, key, value) { + if (!strcmp(key, "out")) { + err = og_json_parse_string(value, &output); + if (err < 0) + return err; + } else { + return -1; + } + } + + if (!output) { + syslog(LOG_ERR, "%s:%d: malformed json response\n", + __FILE__, __LINE__); + return -1; + } + + sprintf(filename, "/tmp/_Seconsola_%s", inet_ntoa(cli->addr.sin_addr)); + file = fopen(filename, "wt"); + if (!file) { + syslog(LOG_ERR, "cannot open file %s: %s\n", + filename, strerror(errno)); + return -1; + } + + fprintf(file, "%s", output); + fclose(file); + + return 0; +} + +struct og_computer_legacy { + char center[OG_DB_INT_MAXLEN + 1]; + char id[OG_DB_INT_MAXLEN + 1]; + char hardware[8192]; +}; + +static int og_resp_hardware(json_t *data, struct og_client *cli) +{ + struct og_computer_legacy legacy = {}; + const char *hardware = NULL; + struct og_computer computer; + struct og_dbi *dbi; + const char *key; + json_t *value; + int err = 0; + bool res; + + if (json_typeof(data) != JSON_OBJECT) + return -1; + + json_object_foreach(data, key, value) { + if (!strcmp(key, "hardware")) { + err = og_json_parse_string(value, &hardware); + if (err < 0) + return -1; + } else { + return -1; + } + } + + if (!hardware) { + syslog(LOG_ERR, "malformed response json\n"); + return -1; + } + + err = og_dbi_get_computer_info(&computer, cli->addr.sin_addr); + if (err < 0) + return -1; + + snprintf(legacy.center, sizeof(legacy.center), "%d", computer.center); + snprintf(legacy.id, sizeof(legacy.id), "%d", computer.id); + snprintf(legacy.hardware, sizeof(legacy.hardware), "%s", hardware); + + dbi = og_dbi_open(&dbi_config); + if (!dbi) { + syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", + __func__, __LINE__); + return -1; + } + + res = actualizaHardware(dbi, legacy.hardware, legacy.id, computer.name, + legacy.center); + og_dbi_close(dbi); + + if (!res) { + syslog(LOG_ERR, "Problem updating client configuration\n"); + return -1; + } + + return 0; +} + +struct og_software_legacy { + char software[8192]; + char center[OG_DB_INT_MAXLEN + 1]; + char part[OG_DB_SMALLINT_MAXLEN + 1]; + char id[OG_DB_INT_MAXLEN + 1]; +}; + +static int og_resp_software(json_t *data, struct og_client *cli) +{ + struct og_software_legacy legacy = {}; + const char *partition = NULL; + const char *software = NULL; + struct og_computer computer; + struct og_dbi *dbi; + const char *key; + json_t *value; + int err = 0; + bool res; + + if (json_typeof(data) != JSON_OBJECT) + return -1; + + json_object_foreach(data, key, value) { + if (!strcmp(key, "software")) + err = og_json_parse_string(value, &software); + else if (!strcmp(key, "partition")) + err = og_json_parse_string(value, &partition); + else + return -1; + + if (err < 0) + return -1; + } + + if (!software || !partition) { + syslog(LOG_ERR, "malformed response json\n"); + return -1; + } + + err = og_dbi_get_computer_info(&computer, cli->addr.sin_addr); + if (err < 0) + return -1; + + snprintf(legacy.software, sizeof(legacy.software), "%s", software); + snprintf(legacy.part, sizeof(legacy.part), "%s", partition); + snprintf(legacy.id, sizeof(legacy.id), "%d", computer.id); + snprintf(legacy.center, sizeof(legacy.center), "%d", computer.center); + + dbi = og_dbi_open(&dbi_config); + if (!dbi) { + syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", + __func__, __LINE__); + return -1; + } + + res = actualizaSoftware(dbi, legacy.software, legacy.part, legacy.id, + computer.name, legacy.center); + og_dbi_close(dbi); + + if (!res) { + syslog(LOG_ERR, "Problem updating client configuration\n"); + return -1; + } + + return 0; +} + +#define OG_PARAMS_RESP_REFRESH (OG_PARAM_PART_DISK | \ + OG_PARAM_PART_NUMBER | \ + OG_PARAM_PART_CODE | \ + OG_PARAM_PART_FILESYSTEM | \ + OG_PARAM_PART_OS | \ + OG_PARAM_PART_SIZE | \ + OG_PARAM_PART_USED_SIZE) + +static int og_json_parse_partition_array(json_t *value, + struct og_partition *partitions) +{ + json_t *element; + int i, err; + + if (json_typeof(value) != JSON_ARRAY) + return -1; + + for (i = 0; i < json_array_size(value) && i < OG_PARTITION_MAX; i++) { + element = json_array_get(value, i); + + err = og_json_parse_partition(element, &partitions[i], + OG_PARAMS_RESP_REFRESH); + if (err < 0) + return err; + } + + return 0; +} + +static int og_dbi_queue_autorun(uint32_t computer_id, uint32_t proc_id) +{ + struct og_task dummy_task = { + .scope = computer_id, + .type_scope = AMBITO_ORDENADORES, + .procedure_id = proc_id, + }; + struct og_dbi *dbi; + + dbi = og_dbi_open(&dbi_config); + if (!dbi) { + syslog(LOG_ERR, "cannot open connection database " + "(%s:%d)\n", __func__, __LINE__); + return -1; + } + if (og_dbi_queue_procedure(dbi, &dummy_task)) { + og_dbi_close(dbi); + return -1; + } + og_dbi_close(dbi); + + return 0; +} + +static int og_resp_refresh(json_t *data, struct og_client *cli) +{ + struct og_partition partitions[OG_PARTITION_MAX] = {}; + const char *serial_number = NULL; + struct og_computer computer = {}; + struct og_partition disk_setup; + char cfg[1024] = {}; + struct og_dbi *dbi; + const char *key; + unsigned int i; + json_t *value; + int err = 0; + bool res; + + if (json_typeof(data) != JSON_OBJECT) + return -1; + + json_object_foreach(data, key, value) { + if (!strcmp(key, "disk_setup")) { + err = og_json_parse_partition(value, + &disk_setup, + OG_PARAMS_RESP_REFRESH); + } else if (!strcmp(key, "partition_setup")) { + err = og_json_parse_partition_array(value, partitions); + } else if (!strcmp(key, "serial_number")) { + err = og_json_parse_string(value, &serial_number); + } else { + return -1; + } + + if (err < 0) + return err; + } + + err = og_dbi_get_computer_info(&computer, cli->addr.sin_addr); + if (err < 0) + return -1; + + if (strlen(serial_number) > 0) + snprintf(cfg, sizeof(cfg), "ser=%s\n", serial_number); + + if (!disk_setup.disk || !disk_setup.number || !disk_setup.code || + !disk_setup.filesystem || !disk_setup.os || !disk_setup.size || + !disk_setup.used_size) + return -1; + + snprintf(cfg + strlen(cfg), sizeof(cfg) - strlen(cfg), + "disk=%s\tpar=%s\tcpt=%s\tfsi=%s\tsoi=%s\ttam=%s\tuso=%s\n", + disk_setup.disk, disk_setup.number, disk_setup.code, + disk_setup.filesystem, disk_setup.os, disk_setup.size, + disk_setup.used_size); + + for (i = 0; i < OG_PARTITION_MAX; i++) { + if (!partitions[i].disk || !partitions[i].number || + !partitions[i].code || !partitions[i].filesystem || + !partitions[i].os || !partitions[i].size || + !partitions[i].used_size) + continue; + + snprintf(cfg + strlen(cfg), sizeof(cfg) - strlen(cfg), + "disk=%s\tpar=%s\tcpt=%s\tfsi=%s\tsoi=%s\ttam=%s\tuso=%s\n", + partitions[i].disk, partitions[i].number, + partitions[i].code, partitions[i].filesystem, + partitions[i].os, partitions[i].size, + partitions[i].used_size); + } + + dbi = og_dbi_open(&dbi_config); + if (!dbi) { + syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", + __func__, __LINE__); + return -1; + } + res = actualizaConfiguracion(dbi, cfg, computer.id); + og_dbi_close(dbi); + + if (!res) { + syslog(LOG_ERR, "Problem updating client configuration\n"); + return -1; + } + + if (!cli->autorun && computer.procedure_id) { + cli->autorun = true; + + if (og_dbi_queue_autorun(computer.id, computer.procedure_id)) + return -1; + } + + return 0; +} + +static int og_resp_image_create(json_t *data, struct og_client *cli) +{ + struct og_software_legacy soft_legacy; + struct og_image_legacy img_legacy; + const char *partition = NULL; + const char *software = NULL; + const char *image_id = NULL; + struct og_computer computer; + const char *disk = NULL; + const char *code = NULL; + const char *name = NULL; + const char *repo = NULL; + struct og_dbi *dbi; + const char *key; + json_t *value; + int err = 0; + bool res; + + if (json_typeof(data) != JSON_OBJECT) + return -1; + + json_object_foreach(data, key, value) { + if (!strcmp(key, "software")) + err = og_json_parse_string(value, &software); + else if (!strcmp(key, "partition")) + err = og_json_parse_string(value, &partition); + else if (!strcmp(key, "disk")) + err = og_json_parse_string(value, &disk); + else if (!strcmp(key, "code")) + err = og_json_parse_string(value, &code); + else if (!strcmp(key, "id")) + err = og_json_parse_string(value, &image_id); + else if (!strcmp(key, "name")) + err = og_json_parse_string(value, &name); + else if (!strcmp(key, "repository")) + err = og_json_parse_string(value, &repo); + else + return -1; + + if (err < 0) + return err; + } + + if (!software || !partition || !disk || !code || !image_id || !name || + !repo) { + syslog(LOG_ERR, "malformed response json\n"); + return -1; + } + + err = og_dbi_get_computer_info(&computer, cli->addr.sin_addr); + if (err < 0) + return -1; + + snprintf(soft_legacy.center, sizeof(soft_legacy.center), "%d", + computer.center); + snprintf(soft_legacy.software, sizeof(soft_legacy.software), "%s", + software); + snprintf(img_legacy.image_id, sizeof(img_legacy.image_id), "%s", + image_id); + snprintf(soft_legacy.id, sizeof(soft_legacy.id), "%d", computer.id); + snprintf(img_legacy.part, sizeof(img_legacy.part), "%s", partition); + snprintf(img_legacy.disk, sizeof(img_legacy.disk), "%s", disk); + snprintf(img_legacy.code, sizeof(img_legacy.code), "%s", code); + snprintf(img_legacy.name, sizeof(img_legacy.name), "%s", name); + snprintf(img_legacy.repo, sizeof(img_legacy.repo), "%s", repo); + + dbi = og_dbi_open(&dbi_config); + if (!dbi) { + syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", + __func__, __LINE__); + return -1; + } + + res = actualizaSoftware(dbi, + soft_legacy.software, + img_legacy.part, + soft_legacy.id, + computer.name, + soft_legacy.center); + if (!res) { + og_dbi_close(dbi); + syslog(LOG_ERR, "Problem updating client configuration\n"); + return -1; + } + + res = actualizaCreacionImagen(dbi, + img_legacy.image_id, + img_legacy.disk, + img_legacy.part, + img_legacy.code, + img_legacy.repo, + soft_legacy.id); + og_dbi_close(dbi); + + if (!res) { + syslog(LOG_ERR, "Problem updating client configuration\n"); + return -1; + } + + return 0; +} + +static int og_resp_image_restore(json_t *data, struct og_client *cli) +{ + struct og_software_legacy soft_legacy; + struct og_image_legacy img_legacy; + const char *partition = NULL; + const char *image_id = NULL; + struct og_computer computer; + const char *disk = NULL; + dbi_result query_result; + struct og_dbi *dbi; + const char *key; + json_t *value; + int err = 0; + bool res; + + if (json_typeof(data) != JSON_OBJECT) + return -1; + + json_object_foreach(data, key, value) { + if (!strcmp(key, "partition")) + err = og_json_parse_string(value, &partition); + else if (!strcmp(key, "disk")) + err = og_json_parse_string(value, &disk); + else if (!strcmp(key, "image_id")) + err = og_json_parse_string(value, &image_id); + else + return -1; + + if (err < 0) + return err; + } + + if (!partition || !disk || !image_id) { + syslog(LOG_ERR, "malformed response json\n"); + return -1; + } + + err = og_dbi_get_computer_info(&computer, cli->addr.sin_addr); + if (err < 0) + return -1; + + snprintf(img_legacy.image_id, sizeof(img_legacy.image_id), "%s", + image_id); + snprintf(img_legacy.part, sizeof(img_legacy.part), "%s", partition); + snprintf(img_legacy.disk, sizeof(img_legacy.disk), "%s", disk); + snprintf(soft_legacy.id, sizeof(soft_legacy.id), "%d", computer.id); + + dbi = og_dbi_open(&dbi_config); + if (!dbi) { + syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", + __func__, __LINE__); + return -1; + } + + query_result = dbi_conn_queryf(dbi->conn, + "SELECT idperfilsoft FROM imagenes " + " WHERE idimagen='%s'", + image_id); + if (!query_result) { + og_dbi_close(dbi); + syslog(LOG_ERR, "failed to query database\n"); + return -1; + } + if (!dbi_result_next_row(query_result)) { + dbi_result_free(query_result); + og_dbi_close(dbi); + syslog(LOG_ERR, "software profile does not exist in database\n"); + return -1; + } + snprintf(img_legacy.software_id, sizeof(img_legacy.software_id), + "%d", dbi_result_get_uint(query_result, "idperfilsoft")); + dbi_result_free(query_result); + + res = actualizaRestauracionImagen(dbi, + img_legacy.image_id, + img_legacy.disk, + img_legacy.part, + soft_legacy.id, + img_legacy.software_id); + og_dbi_close(dbi); + + if (!res) { + syslog(LOG_ERR, "Problem updating client configuration\n"); + return -1; + } + + return 0; +} + +int og_agent_state_process_response(struct og_client *cli) +{ + json_error_t json_err; + json_t *root; + int err = -1; + char *body; + + if (!strncmp(cli->buf, "HTTP/1.0 202 Accepted", + strlen("HTTP/1.0 202 Accepted"))) { + og_dbi_update_action(cli->last_cmd_id, true); + cli->last_cmd_id = 0; + return 1; + } + + if (strncmp(cli->buf, "HTTP/1.0 200 OK", strlen("HTTP/1.0 200 OK"))) { + og_dbi_update_action(cli->last_cmd_id, false); + cli->last_cmd_id = 0; + return -1; + } + og_dbi_update_action(cli->last_cmd_id, true); + cli->last_cmd_id = 0; + + if (!cli->content_length) { + cli->last_cmd = OG_CMD_UNSPEC; + return 0; + } + + body = strstr(cli->buf, "\r\n\r\n") + 4; + + root = json_loads(body, 0, &json_err); + if (!root) { + syslog(LOG_ERR, "%s:%d: malformed json line %d: %s\n", + __FILE__, __LINE__, json_err.line, json_err.text); + return -1; + } + + switch (cli->last_cmd) { + case OG_CMD_PROBE: + err = og_resp_probe(cli, root); + break; + case OG_CMD_SHELL_RUN: + err = og_resp_shell_run(cli, root); + break; + case OG_CMD_HARDWARE: + err = og_resp_hardware(root, cli); + break; + case OG_CMD_SOFTWARE: + err = og_resp_software(root, cli); + break; + case OG_CMD_REFRESH: + err = og_resp_refresh(root, cli); + break; + case OG_CMD_SETUP: + err = og_resp_refresh(root, cli); + break; + case OG_CMD_IMAGE_CREATE: + err = og_resp_image_create(root, cli); + break; + case OG_CMD_IMAGE_RESTORE: + err = og_resp_image_restore(root, cli); + break; + default: + err = -1; + break; + } + + cli->last_cmd = OG_CMD_UNSPEC; + + return err; +} diff --git a/src/client.h b/src/client.h new file mode 100644 index 0000000..c6e70c3 --- /dev/null +++ b/src/client.h @@ -0,0 +1,6 @@ +#ifndef OG_CLIENT_H_ +#define OG_CLIENT_H_ + +int og_agent_state_process_response(struct og_client *cli); + +#endif diff --git a/src/core.c b/src/core.c new file mode 100644 index 0000000..f7c25f5 --- /dev/null +++ b/src/core.c @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2020 Soleta Networks <info@soleta.eu> + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the + * Free Software Foundation, version 3. + */ + +#include "ogAdmServer.h" +#include "dbi.h" +#include "utils.h" +#include "list.h" +#include "rest.h" +#include "client.h" +#include "json.h" +#include "schedule.h" +#include <syslog.h> +#include <sys/ioctl.h> +#include <ifaddrs.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <jansson.h> +#include <time.h> + +static void og_client_release(struct ev_loop *loop, struct og_client *cli) +{ + if (cli->keepalive_idx >= 0) { + syslog(LOG_DEBUG, "closing keepalive connection for %s:%hu in slot %d\n", + inet_ntoa(cli->addr.sin_addr), + ntohs(cli->addr.sin_port), cli->keepalive_idx); + tbsockets[cli->keepalive_idx].cli = NULL; + } + + list_del(&cli->list); + ev_io_stop(loop, &cli->io); + close(cli->io.fd); + free(cli); +} + +static void og_client_keepalive(struct ev_loop *loop, struct og_client *cli) +{ + struct og_client *old_cli; + + old_cli = tbsockets[cli->keepalive_idx].cli; + if (old_cli && old_cli != cli) { + syslog(LOG_DEBUG, "closing old keepalive connection for %s:%hu\n", + inet_ntoa(old_cli->addr.sin_addr), + ntohs(old_cli->addr.sin_port)); + + og_client_release(loop, old_cli); + } + tbsockets[cli->keepalive_idx].cli = cli; +} + +static void og_client_reset_state(struct og_client *cli) +{ + cli->state = OG_CLIENT_RECEIVING_HEADER; + cli->buf_len = 0; +} + +static int og_client_payload_too_large(struct og_client *cli) +{ + char buf[] = "HTTP/1.1 413 Payload Too Large\r\n" + "Content-Length: 0\r\n\r\n"; + + send(og_client_socket(cli), buf, strlen(buf), 0); + + return -1; +} + +static int og_client_state_recv_hdr_rest(struct og_client *cli) +{ + char *ptr; + + ptr = strstr(cli->buf, "\r\n\r\n"); + if (!ptr) + return 0; + + cli->msg_len = ptr - cli->buf + 4; + + ptr = strstr(cli->buf, "Content-Length: "); + if (ptr) { + sscanf(ptr, "Content-Length: %i[^\r\n]", &cli->content_length); + if (cli->content_length < 0) + return -1; + cli->msg_len += cli->content_length; + } + + ptr = strstr(cli->buf, "Authorization: "); + if (ptr) + sscanf(ptr, "Authorization: %63[^\r\n]", cli->auth_token); + + return 1; +} + +static int og_client_recv(struct og_client *cli, int events) +{ + struct ev_io *io = &cli->io; + int ret; + + if (events & EV_ERROR) { + syslog(LOG_ERR, "unexpected error event from client %s:%hu\n", + inet_ntoa(cli->addr.sin_addr), + ntohs(cli->addr.sin_port)); + return 0; + } + + ret = recv(io->fd, cli->buf + cli->buf_len, + sizeof(cli->buf) - cli->buf_len, 0); + if (ret <= 0) { + if (ret < 0) { + syslog(LOG_ERR, "error reading from client %s:%hu (%s)\n", + inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port), + strerror(errno)); + } else { + syslog(LOG_DEBUG, "closed connection by %s:%hu\n", + inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); + } + return ret; + } + + return ret; +} + +static void og_client_read_cb(struct ev_loop *loop, struct ev_io *io, int events) +{ + struct og_client *cli; + int ret; + + cli = container_of(io, struct og_client, io); + + ret = og_client_recv(cli, events); + if (ret <= 0) + goto close; + + if (cli->keepalive_idx >= 0) + return; + + ev_timer_again(loop, &cli->timer); + + cli->buf_len += ret; + if (cli->buf_len >= sizeof(cli->buf)) { + syslog(LOG_ERR, "client request from %s:%hu is too long\n", + inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); + og_client_payload_too_large(cli); + goto close; + } + + switch (cli->state) { + case OG_CLIENT_RECEIVING_HEADER: + ret = og_client_state_recv_hdr_rest(cli); + if (ret < 0) + goto close; + if (!ret) + return; + + cli->state = OG_CLIENT_RECEIVING_PAYLOAD; + /* Fall through. */ + case OG_CLIENT_RECEIVING_PAYLOAD: + /* Still not enough data to process request. */ + if (cli->buf_len < cli->msg_len) + return; + + cli->state = OG_CLIENT_PROCESSING_REQUEST; + /* fall through. */ + case OG_CLIENT_PROCESSING_REQUEST: + ret = og_client_state_process_payload_rest(cli); + if (ret < 0) { + syslog(LOG_ERR, "Failed to process HTTP request from %s:%hu\n", + inet_ntoa(cli->addr.sin_addr), + ntohs(cli->addr.sin_port)); + } + if (ret < 0) + goto close; + + if (cli->keepalive_idx < 0) { + syslog(LOG_DEBUG, "server closing connection to %s:%hu\n", + inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); + goto close; + } else { + syslog(LOG_DEBUG, "leaving client %s:%hu in keepalive mode\n", + inet_ntoa(cli->addr.sin_addr), + ntohs(cli->addr.sin_port)); + og_client_keepalive(loop, cli); + og_client_reset_state(cli); + } + break; + default: + syslog(LOG_ERR, "unknown state, critical internal error\n"); + goto close; + } + return; +close: + ev_timer_stop(loop, &cli->timer); + og_client_release(loop, cli); +} + +enum og_agent_state { + OG_AGENT_RECEIVING_HEADER = 0, + OG_AGENT_RECEIVING_PAYLOAD, + OG_AGENT_PROCESSING_RESPONSE, +}; + +static int og_agent_state_recv_hdr_rest(struct og_client *cli) +{ + char *ptr; + + ptr = strstr(cli->buf, "\r\n\r\n"); + if (!ptr) + return 0; + + cli->msg_len = ptr - cli->buf + 4; + + ptr = strstr(cli->buf, "Content-Length: "); + if (ptr) { + sscanf(ptr, "Content-Length: %i[^\r\n]", &cli->content_length); + if (cli->content_length < 0) + return -1; + cli->msg_len += cli->content_length; + } + + return 1; +} + +static void og_agent_reset_state(struct og_client *cli) +{ + cli->state = OG_AGENT_RECEIVING_HEADER; + cli->buf_len = 0; + cli->content_length = 0; + memset(cli->buf, 0, sizeof(cli->buf)); +} + +static void og_agent_deliver_pending_cmd(struct og_client *cli) +{ + const struct og_cmd *cmd; + + cmd = og_cmd_find(inet_ntoa(cli->addr.sin_addr)); + if (!cmd) + return; + + og_send_request(cmd->method, cmd->type, &cmd->params, cmd->json); + cli->last_cmd_id = cmd->id; + + og_cmd_free(cmd); +} + +static void og_agent_read_cb(struct ev_loop *loop, struct ev_io *io, int events) +{ + struct og_client *cli; + int ret; + + cli = container_of(io, struct og_client, io); + + ret = og_client_recv(cli, events); + if (ret <= 0) + goto close; + + ev_timer_again(loop, &cli->timer); + + cli->buf_len += ret; + if (cli->buf_len >= sizeof(cli->buf)) { + syslog(LOG_ERR, "client request from %s:%hu is too long\n", + inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); + goto close; + } + + switch (cli->state) { + case OG_AGENT_RECEIVING_HEADER: + ret = og_agent_state_recv_hdr_rest(cli); + if (ret < 0) + goto close; + if (!ret) + return; + + cli->state = OG_AGENT_RECEIVING_PAYLOAD; + /* Fall through. */ + case OG_AGENT_RECEIVING_PAYLOAD: + /* Still not enough data to process request. */ + if (cli->buf_len < cli->msg_len) + return; + + cli->state = OG_AGENT_PROCESSING_RESPONSE; + /* fall through. */ + case OG_AGENT_PROCESSING_RESPONSE: + ret = og_agent_state_process_response(cli); + if (ret < 0) { + syslog(LOG_ERR, "Failed to process HTTP request from %s:%hu\n", + inet_ntoa(cli->addr.sin_addr), + ntohs(cli->addr.sin_port)); + goto close; + } else if (ret == 0) { + og_agent_deliver_pending_cmd(cli); + } + + syslog(LOG_DEBUG, "leaving client %s:%hu in keepalive mode\n", + inet_ntoa(cli->addr.sin_addr), + ntohs(cli->addr.sin_port)); + og_agent_reset_state(cli); + break; + default: + syslog(LOG_ERR, "unknown state, critical internal error\n"); + goto close; + } + return; +close: + ev_timer_stop(loop, &cli->timer); + og_client_release(loop, cli); +} + +static void og_client_timer_cb(struct ev_loop *loop, ev_timer *timer, int events) +{ + struct og_client *cli; + + cli = container_of(timer, struct og_client, timer); + if (cli->keepalive_idx >= 0) { + ev_timer_again(loop, &cli->timer); + return; + } + syslog(LOG_ERR, "timeout request for client %s:%hu\n", + inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); + + og_client_release(loop, cli); +} + +static void og_agent_send_refresh(struct og_client *cli) +{ + struct og_msg_params params; + int err; + + params.ips_array[0] = inet_ntoa(cli->addr.sin_addr); + params.ips_array_len = 1; + + err = og_send_request(OG_METHOD_GET, OG_CMD_REFRESH, ¶ms, NULL); + if (err < 0) { + syslog(LOG_ERR, "Can't send refresh to: %s\n", + params.ips_array[0]); + } else { + syslog(LOG_INFO, "Sent refresh to: %s\n", + params.ips_array[0]); + } +} + +/* Shut down connection if there is no complete message after 10 seconds. */ +#define OG_CLIENT_TIMEOUT 10 + +/* Agent client operation might take longer, shut down after 30 seconds. */ +#define OG_AGENT_CLIENT_TIMEOUT 30 + +int socket_rest, socket_agent_rest; + +void og_server_accept_cb(struct ev_loop *loop, struct ev_io *io, int events) +{ + struct sockaddr_in client_addr; + socklen_t addrlen = sizeof(client_addr); + struct og_client *cli; + int client_sd; + + if (events & EV_ERROR) + return; + + client_sd = accept(io->fd, (struct sockaddr *)&client_addr, &addrlen); + if (client_sd < 0) { + syslog(LOG_ERR, "cannot accept client connection\n"); + return; + } + + cli = (struct og_client *)calloc(1, sizeof(struct og_client)); + if (!cli) { + close(client_sd); + return; + } + memcpy(&cli->addr, &client_addr, sizeof(client_addr)); + if (io->fd == socket_agent_rest) + cli->keepalive_idx = 0; + else + cli->keepalive_idx = -1; + + if (io->fd == socket_rest) + cli->rest = true; + else if (io->fd == socket_agent_rest) + cli->agent = true; + + syslog(LOG_DEBUG, "connection from client %s:%hu\n", + inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); + + if (io->fd == socket_agent_rest) + ev_io_init(&cli->io, og_agent_read_cb, client_sd, EV_READ); + else + ev_io_init(&cli->io, og_client_read_cb, client_sd, EV_READ); + + ev_io_start(loop, &cli->io); + if (io->fd == socket_agent_rest) { + ev_timer_init(&cli->timer, og_client_timer_cb, + OG_AGENT_CLIENT_TIMEOUT, 0.); + } else { + ev_timer_init(&cli->timer, og_client_timer_cb, + OG_CLIENT_TIMEOUT, 0.); + } + ev_timer_start(loop, &cli->timer); + og_client_add(cli); + + if (io->fd == socket_agent_rest) { + og_agent_send_refresh(cli); + } +} + +int og_socket_server_init(const char *port) +{ + struct sockaddr_in local; + int sd, on = 1; + + sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sd < 0) { + syslog(LOG_ERR, "cannot create main socket\n"); + return -1; + } + setsockopt(sd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(int)); + + local.sin_addr.s_addr = htonl(INADDR_ANY); + local.sin_family = AF_INET; + local.sin_port = htons(atoi(port)); + + if (bind(sd, (struct sockaddr *) &local, sizeof(local)) < 0) { + close(sd); + syslog(LOG_ERR, "cannot bind socket\n"); + return -1; + } + + listen(sd, 250); + + return sd; +} diff --git a/src/core.h b/src/core.h new file mode 100644 index 0000000..dd5cd58 --- /dev/null +++ b/src/core.h @@ -0,0 +1,10 @@ +#ifndef _OG_CORE_H +#define _OG_CORE_H + +extern int socket_rest, socket_agent_rest; +extern struct ev_loop *og_loop; + +int og_socket_server_init(const char *port); +void og_server_accept_cb(struct ev_loop *loop, struct ev_io *io, int events); + +#endif diff --git a/src/dbi.c b/src/dbi.c new file mode 100644 index 0000000..6640f50 --- /dev/null +++ b/src/dbi.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2020 Soleta Networks <info@soleta.eu> + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the + * Free Software Foundation, version 3. + */ + +#include "dbi.h" + +struct og_dbi *og_dbi_open(struct og_dbi_config *config) +{ + struct og_dbi *dbi; + + dbi = (struct og_dbi *)malloc(sizeof(struct og_dbi)); + if (!dbi) + return NULL; + + dbi_initialize_r(NULL, &dbi->inst); + dbi->conn = dbi_conn_new_r("mysql", dbi->inst); + if (!dbi->conn) { + free(dbi); + return NULL; + } + + dbi_conn_set_option(dbi->conn, "host", config->host); + dbi_conn_set_option(dbi->conn, "username", config->user); + dbi_conn_set_option(dbi->conn, "password", config->passwd); + dbi_conn_set_option(dbi->conn, "dbname", config->database); + dbi_conn_set_option(dbi->conn, "encoding", "UTF-8"); + + if (dbi_conn_connect(dbi->conn) < 0) { + free(dbi); + return NULL; + } + + return dbi; +} + +void og_dbi_close(struct og_dbi *dbi) +{ + dbi_conn_close(dbi->conn); + dbi_shutdown_r(dbi->inst); + free(dbi); +} diff --git a/src/dbi.h b/src/dbi.h new file mode 100644 index 0000000..30327a7 --- /dev/null +++ b/src/dbi.h @@ -0,0 +1,51 @@ +#ifndef __OG_DBI +#define __OG_DBI + +#include <dbi/dbi.h> + +struct og_dbi_config { + const char *user; + const char *passwd; + const char *host; + const char *database; +}; + +struct og_dbi { + dbi_conn conn; + dbi_inst inst; +}; + +struct og_dbi *og_dbi_open(struct og_dbi_config *config); +void og_dbi_close(struct og_dbi *db); + +#define OG_DB_COMPUTER_NAME_MAXLEN 100 +#define OG_DB_CENTER_NAME_MAXLEN 100 +#define OG_DB_ROOM_NAME_MAXLEN 100 +#define OG_DB_IMAGE_NAME_MAXLEN 50 +#define OG_DB_FILESYSTEM_MAXLEN 16 +#define OG_DB_INT8_MAXLEN 8 +#define OG_DB_INT_MAXLEN 11 +#define OG_DB_IP_MAXLEN 15 +#define OG_DB_SMALLINT_MAXLEN 6 + +struct og_image_legacy { + char software_id[OG_DB_INT_MAXLEN + 1]; + char image_id[OG_DB_INT_MAXLEN + 1]; + char name[OG_DB_IMAGE_NAME_MAXLEN + 1]; + char repo[OG_DB_IP_MAXLEN + 1]; + char part[OG_DB_SMALLINT_MAXLEN + 1]; + char disk[OG_DB_SMALLINT_MAXLEN + 1]; + char code[OG_DB_INT8_MAXLEN + 1]; +}; + +struct og_legacy_partition { + char partition[OG_DB_SMALLINT_MAXLEN + 1]; + char code[OG_DB_INT8_MAXLEN + 1]; + char size[OG_DB_INT_MAXLEN + 1]; + char filesystem[OG_DB_FILESYSTEM_MAXLEN + 1]; + char format[2]; /* Format is a boolean 0 or 1 => length is 2 */ +}; + +extern struct og_dbi_config dbi_config; + +#endif diff --git a/src/json.c b/src/json.c new file mode 100644 index 0000000..b76a3b7 --- /dev/null +++ b/src/json.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2020 Soleta Networks <info@soleta.eu> + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the + * Free Software Foundation, version 3. + */ + +#include "json.h" +#include <stdint.h> + +int og_json_parse_string(json_t *element, const char **str) +{ + if (json_typeof(element) != JSON_STRING) + return -1; + + *str = json_string_value(element); + return 0; +} + +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; +} + +int og_json_parse_bool(json_t *element, bool *value) +{ + if (json_typeof(element) == JSON_TRUE) + *value = true; + else if (json_typeof(element) == JSON_FALSE) + *value = false; + else + return -1; + + return 0; +} + +int og_json_parse_partition(json_t *element, struct og_partition *part, + uint64_t required_flags) +{ + uint64_t flags = 0UL; + const char *key; + json_t *value; + int err = 0; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "partition")) { + err = og_json_parse_string(value, &part->number); + flags |= OG_PARAM_PART_NUMBER; + } else if (!strcmp(key, "code")) { + err = og_json_parse_string(value, &part->code); + flags |= OG_PARAM_PART_CODE; + } else if (!strcmp(key, "filesystem")) { + err = og_json_parse_string(value, &part->filesystem); + flags |= OG_PARAM_PART_FILESYSTEM; + } else if (!strcmp(key, "size")) { + err = og_json_parse_string(value, &part->size); + flags |= OG_PARAM_PART_SIZE; + } else if (!strcmp(key, "format")) { + err = og_json_parse_string(value, &part->format); + flags |= OG_PARAM_PART_FORMAT; + } else if (!strcmp(key, "disk")) { + err = og_json_parse_string(value, &part->disk); + flags |= OG_PARAM_PART_DISK; + } else if (!strcmp(key, "os")) { + err = og_json_parse_string(value, &part->os); + flags |= OG_PARAM_PART_OS; + } else if (!strcmp(key, "used_size")) { + err = og_json_parse_string(value, &part->used_size); + flags |= OG_PARAM_PART_USED_SIZE; + } + + if (err < 0) + return err; + } + + if (flags != required_flags) + return -1; + + return err; +} diff --git a/src/json.h b/src/json.h new file mode 100644 index 0000000..7c6c61c --- /dev/null +++ b/src/json.h @@ -0,0 +1,77 @@ +#ifndef _OG_JSON_H +#define _OG_JSON_H + +#include <jansson.h> +#include "schedule.h" + +int og_json_parse_string(json_t *element, const char **str); +int og_json_parse_uint(json_t *element, uint32_t *integer); +int og_json_parse_bool(json_t *element, bool *value); + +#define OG_PARAM_PART_NUMBER (1UL << 0) +#define OG_PARAM_PART_CODE (1UL << 1) +#define OG_PARAM_PART_FILESYSTEM (1UL << 2) +#define OG_PARAM_PART_SIZE (1UL << 3) +#define OG_PARAM_PART_FORMAT (1UL << 4) +#define OG_PARAM_PART_DISK (1UL << 5) +#define OG_PARAM_PART_OS (1UL << 6) +#define OG_PARAM_PART_USED_SIZE (1UL << 7) + +struct og_partition { + const char *disk; + const char *number; + const char *code; + const char *size; + const char *filesystem; + const char *format; + const char *os; + const char *used_size; +}; + +#define OG_PARTITION_MAX 4 + +int og_json_parse_partition(json_t *element, struct og_partition *part, + uint64_t required_flags); + +#define OG_CLIENTS_MAX 4096 + +struct og_sync_params { + const char *sync; + const char *diff; + const char *remove; + const char *compress; + const char *cleanup; + const char *cache; + const char *cleanup_cache; + const char *remove_dst; + const char *diff_id; + const char *diff_name; + const char *path; + const char *method; +}; + +struct og_msg_params { + const char *ips_array[OG_CLIENTS_MAX]; + const char *mac_array[OG_CLIENTS_MAX]; + unsigned int ips_array_len; + const char *wol_type; + char run_cmd[4096]; + const char *disk; + const char *partition; + const char *repository; + const char *name; + const char *id; + const char *code; + const char *type; + const char *profile; + const char *cache; + const char *cache_size; + 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; +}; + +#endif diff --git a/src/list.h b/src/list.h new file mode 100644 index 0000000..8dfd476 --- /dev/null +++ b/src/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/src/main.c b/src/main.c new file mode 100644 index 0000000..5f6d164 --- /dev/null +++ b/src/main.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2020 Soleta Networks <info@soleta.eu> + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the + * Free Software Foundation, version 3. + */ + +#include "ogAdmServer.h" +#include "dbi.h" +#include "utils.h" +#include "list.h" +#include "rest.h" +#include "client.h" +#include "json.h" +#include "schedule.h" +#include "core.h" +#include "cfg.h" +#include <syslog.h> + +#define OG_SERVER_CFG_JSON "/opt/opengnsys/cfg/ogserver.json" + +int main(int argc, char *argv[]) +{ + struct ev_io ev_io_server_rest, ev_io_agent_rest; + struct og_server_cfg cfg = {}; + int i; + + og_loop = ev_default_loop(0); + + if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) + exit(EXIT_FAILURE); + + openlog("ogserver", LOG_PID, LOG_DAEMON); + + if (!validacionParametros(argc, argv, 1)) // Valida parámetros de ejecución + exit(EXIT_FAILURE); + + if (parse_json_config(OG_SERVER_CFG_JSON, &cfg) < 0) { + syslog(LOG_INFO, "Falling back to legacy configuration file at %s\n", + szPathFileCfg); + if (!tomaConfiguracion(szPathFileCfg)) + exit(EXIT_FAILURE); + } else { + from_json_to_legacy(&cfg); + } + + for (i = 0; i < MAXIMOS_CLIENTES; i++) { + tbsockets[i].ip[0] = '\0'; + tbsockets[i].cli = NULL; + } + + socket_rest = og_socket_server_init("8888"); + if (socket_rest < 0) { + syslog(LOG_ERR, "Cannot open REST API server socket\n"); + exit(EXIT_FAILURE); + } + + ev_io_init(&ev_io_server_rest, og_server_accept_cb, socket_rest, EV_READ); + ev_io_start(og_loop, &ev_io_server_rest); + + socket_agent_rest = og_socket_server_init("8889"); + if (socket_agent_rest < 0) { + syslog(LOG_ERR, "Cannot open ogClient server socket\n"); + exit(EXIT_FAILURE); + } + + ev_io_init(&ev_io_agent_rest, og_server_accept_cb, socket_agent_rest, EV_READ); + ev_io_start(og_loop, &ev_io_agent_rest); + + if (og_dbi_schedule_get() < 0) { + syslog(LOG_ERR, "Cannot connect to database\n"); + exit(EXIT_FAILURE); + } + + og_schedule_next(og_loop); + + syslog(LOG_INFO, "Waiting for connections\n"); + + while (1) + ev_loop(og_loop, 0); + + exit(EXIT_SUCCESS); +} diff --git a/src/ogAdmLib.c b/src/ogAdmLib.c new file mode 100644 index 0000000..75dbd51 --- /dev/null +++ b/src/ogAdmLib.c @@ -0,0 +1,362 @@ +// ************************************************************************************************************************************************** +// Libreria: ogAdmLib +// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla +// Fecha Creación: Marzo-2010 +// Fecha Última modificación: Marzo-2010 +// Nombre del fichero: ogAdmLib.c +// Descripción: Este fichero implementa una libreria de funciones para uso común de los servicios +// ************************************************************************************************************************************************** + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/socket.h> +#include "ogAdmLib.h" + +//______________________________________________________________________________________________________ +// Función: ValidacionParametros +// +// Descripción: +// Valida que los parametros de ejecución del programa sean correctos +// Parámetros: +// - argc: Número de argumentos +// - argv: Puntero a cada argumento +// - eje: Tipo de ejecutable (1=Servicio,2=Repositorio o 3=Cliente) +// Devuelve: +// - TRUE si los argumentos pasados son correctos +// - FALSE en caso contrario +// Especificaciones: +// La sintaxis de los argumentos es la siguiente +// -f Archivo de configuración del servicio +// -l Archivo de logs +// -d Nivel de debuger (mensages que se escribirán en el archivo de logs) +// Devuelve: +// TRUE: Si el proceso es correcto +// FALSE: En caso de ocurrir algún error +//______________________________________________________________________________________________________ +BOOLEAN validacionParametros(int argc, char*argv[],int eje) { + int i; + + switch(eje){ + case 1: // Administrador + strcpy(szPathFileCfg, "ogserver.cfg"); // Valores por defecto de archivos + strcpy(szPathFileLog, "ogserver.log"); // de configuración y de logs + break; + case 2: // Repositorio + strcpy(szPathFileCfg, "ogAdmRepo.cfg"); // Valores por defecto de archivos + strcpy(szPathFileLog, "ogAdmRepo.log"); // de configuración y de logs + break; + case 3: // Cliente OpenGnsys + strcpy(szPathFileCfg, "ogAdmClient.cfg"); // Valores por defecto de archivos + strcpy(szPathFileLog, "ogAdmClient.log"); // de configuración y de logs + break; + case 4: // Servicios DHCP,BOOTP Y TFTP + strcpy(szPathFileCfg, "ogAdmBoot.cfg"); // Valores por defecto de archivos + strcpy(szPathFileLog, "ogAdmBoot.log"); // de configuración y de logs + break; + case 5: // Agente + strcpy(szPathFileCfg, "ogAdmAgent.cfg"); // Valores por defecto de archivos + strcpy(szPathFileLog, "ogAdmAgent.log"); // de configuración y de logs + break; + case 6: // Agente + strcpy(szPathFileCfg, "ogAdmWinClient.cfg"); // Valores por defecto de archivos + strcpy(szPathFileLog, "ogAdmWinClient.log"); // de configuración y de logs + break; + case 7: // Agente + strcpy(szPathFileCfg, "ogAdmnxClient.cfg"); // Valores por defecto de archivos + strcpy(szPathFileLog, "ogAdmLnxClient.log"); // de configuración y de logs + break; + } + + ndebug = 1; // Nivel de debuger por defecto + + for (i = 1; (i + 1) < argc; i += 2) { + if (argv[i][0] == '-') { + switch (tolower(argv[i][1])) { + case 'f': + if (argv[i + 1] != NULL) + strcpy(szPathFileCfg, argv[i + 1]); + else { + return (FALSE); + } + break; + case 'l': + if (argv[i + 1] != NULL) + strcpy(szPathFileLog, argv[i + 1]); + else { + return (FALSE); + } + break; + case 'd': + if (argv[i + 1] != NULL) { + ndebug = atoi(argv[i + 1]); + if (ndebug < 1) + ndebug = 1; // Por defecto el nivel de debug es 1 + } else + ndebug = 1; // Por defecto el nivel de debug es 1 + break; + default: + exit(EXIT_FAILURE); + break; + } + } + } + return (TRUE); +} +// ________________________________________________________________________________________________________ +// Función: splitCadena +// +// Descripción: +// Trocea una cadena según un carácter delimitador +// Parámetros: +// - trozos: Array de punteros a cadenas +// - cadena: Cadena a trocear +// - chd: Carácter delimitador +// Devuelve: +// Número de trozos en que se divide la cadena +// ________________________________________________________________________________________________________ +int splitCadena(char **trozos,char *cadena, char chd) +{ + int w=0; + if(cadena==NULL) return(w); + + trozos[w++]=cadena; + while(*cadena!='\0'){ + if(*cadena==chd){ + *cadena='\0'; + if(*(cadena+1)!='\0') + trozos[w++]=cadena+1; + } + cadena++; + } + return(w); // Devuelve el número de trozos +} +// ________________________________________________________________________________________________________ +// Función: escaparCadena +// +// Descripción: +// Sustituye las apariciones de un caracter comila simple ' por \' +// Parámetros: +// - cadena: Cadena a escapar +// Devuelve: +// La cadena con las comillas simples sustituidas por \' +// ________________________________________________________________________________________________________ +char* escaparCadena(char *cadena) +{ + int b,c; + char *buffer; + + buffer = (char*) reservaMemoria(strlen(cadena)*2); // Toma memoria para el buffer de conversión + if (buffer == NULL) { // No hay memoria suficiente para el buffer + return (FALSE); + } + + c=b=0; + while(cadena[c]!=0) { + if (cadena[c]=='\''){ + buffer[b++]='\\'; + buffer[b++]='\''; + } + else{ + buffer[b++]=cadena[c]; + } + c++; + } + return(buffer); +} + +// ________________________________________________________________________________________________________ +// Función: igualIP +// +// Descripción: +// Comprueba si una cadena con una dirección IP está incluida en otra que contienen varias direcciones ipes +// separadas por punto y coma +// Parámetros: +// - cadenaiph: Cadena de direcciones IPES +// - ipcliente: Cadena de la IP a buscar +// Devuelve: +// TRUE: Si el proceso es correcto +// FALSE: En caso de ocurrir algún error +// ________________________________________________________________________________________________________ +BOOLEAN contieneIP(char *cadenaiph,char *ipcliente) +{ + char *posa,*posb; + int lon, i; + + posa=strstr(cadenaiph,ipcliente); + if(posa==NULL) return(FALSE); // No existe la IP en la cadena + posb=posa; // Iguala direcciones + for (i = 0; i < LONIP; i++) { + if(*posb==';') break; + if(*posb=='\0') break; + if(*posb=='\r') break; + posb++; + } + lon=strlen(ipcliente); + if((posb-posa)==lon) return(TRUE); // IP encontrada + return(FALSE); +} +// ________________________________________________________________________________________________________ +// Función: rTrim +// +// Descripción: +// Elimina caracteres de espacios y de asci menor al espacio al final de la cadena +// Parámetros: +// - cadena: Cadena a procesar +// ________________________________________________________________________________________________________ +char* rTrim(char *cadena) +{ + int i,lon; + + lon=strlen(cadena); + for (i=lon-1;i>=0;i--){ + if(cadena[i]<32) + cadena[i]='\0'; + else + return(cadena); + } + return(cadena); +} +//______________________________________________________________________________________________________ +// Función: reservaMemoria +// +// Descripción: +// Reserva memoria para una variable +// Parámetros: +// - lon: Longitud en bytes de la reserva +// Devuelve: +// Un puntero a la zona de memoria reservada que ha sido previamente rellena con zeros o nulos +//______________________________________________________________________________________________________ +char* reservaMemoria(int lon) +{ + char *mem; + + mem=(char*)malloc(lon); + if(mem!=NULL) + memset(mem,0,lon); + return(mem); +} +//______________________________________________________________________________________________________ +// Función: ampliaMemoria +// +// Descripción: +// Amplia memoria para una variable +// Parámetros: +// - ptr: Puntero al buffer de memoria que se quiere ampliar +// - lon: Longitud en bytes de la amplicación +// Devuelve: +// Un puntero a la zona de memoria reservada que ha sido previamente rellena con zeros o nulos +//______________________________________________________________________________________________________ +char* ampliaMemoria(char* ptr,int lon) +{ + char *mem; + + mem=(char*)realloc(ptr,lon*sizeof(char*)); + if(mem!=NULL) + return(mem); + return(NULL); +} +//______________________________________________________________________________________________________ +// Función: liberaMemoria +// +// Descripción: +// Libera memoria para una variable +// Parámetros: +// - ptr: Puntero al buffer de memoria que se quiere liberar +// Devuelve: +// Nada +//______________________________________________________________________________________________________ +void liberaMemoria(void* ptr) +{ + if(ptr){ + free (ptr); + } +} +// ________________________________________________________________________________________________________ +// Función: sendData +// +// Descripción: +// EnvÃa datos por la red a través de un socket +// Parametros: +// - sock : El socket por donde se envÃa +// - datos: El contenido a enviar +// - lon: Cantidad de bites a enviar +// Devuelve: +// TRUE: Si el proceso es correcto +// FALSE: En caso de ocurrir algún error +// ________________________________________________________________________________________________________ +BOOLEAN sendData(SOCKET *sock, char* datos,int lon) +{ + int idx,ret; + idx = 0; + while (lon > 0) { + ret = send(*sock,&datos[idx],lon, 0); + if (ret == 0) { // Conexión cerrada por parte del cliente (Graceful close) + break; + } + else{ + if (ret == -1) + return (FALSE); + } + lon -= ret; + idx += ret; + } + return (TRUE); +} +// ________________________________________________________________________________________________________ +// Función: mandaTrama +// +// Descripción: +// EnvÃa una trama por la red +// Parametros: +// - sock : El socket del host al que se dirige la trama +// - trama: El contenido de la trama +// - lon: Longitud de la parte de parametros de la trama que se va a mandar +// Devuelve: +// TRUE: Si el proceso es correcto +// FALSE: En caso de ocurrir algún error +// ________________________________________________________________________________________________________ +BOOLEAN mandaTrama(SOCKET *sock, TRAMA* ptrTrama) +{ + int lonprm; + char *buffer,hlonprm[LONHEXPRM+1]; + BOOLEAN res; + + lonprm=strlen(ptrTrama->parametros); + sprintf(hlonprm,"%05X",LONGITUD_CABECERATRAMA+LONHEXPRM+lonprm); // Convierte en hexadecimal la longitud + + buffer=reservaMemoria(LONGITUD_CABECERATRAMA+LONHEXPRM+lonprm); // Longitud total de la trama + if(buffer==NULL) + return(FALSE); + memcpy(buffer,ptrTrama,LONGITUD_CABECERATRAMA); // Copia cabecera de trama + memcpy(&buffer[LONGITUD_CABECERATRAMA],hlonprm,LONHEXPRM); // Copia longitud de la trama + memcpy(&buffer[LONGITUD_CABECERATRAMA+LONHEXPRM],ptrTrama->parametros,lonprm); + res=sendData(sock,buffer,LONGITUD_CABECERATRAMA+LONHEXPRM+lonprm); + liberaMemoria(buffer); + return (res); +} + +//______________________________________________________________________________________________________ +// Función: initParammetros +// +// Descripción: +// Libera memoria del buffer de los parametros de la trama y vuelve a reservar espacio +// Parámetros: +// - parametros : Puntero a la zona donde están los parametros de una trama +// - lon : Tamaño de la nueva reserva de espacio para los parametros +// Devuelve: +// Un puntero a la nueva zona de memoria o NULL si ha habido algún error +// Especificaciones: +// En caso de que el parámetro lon valga cero el tamaño a reservar será el estandar +//______________________________________________________________________________________________________ +BOOLEAN initParametros(TRAMA* ptrTrama,int lon) +{ + if(lon==0) lon=LONGITUD_PARAMETROS; + ptrTrama->parametros=(char*)ampliaMemoria(ptrTrama->parametros,lon); + if(!ptrTrama->parametros) + return(FALSE); + else + return(TRUE); +} diff --git a/src/ogAdmLib.h b/src/ogAdmLib.h new file mode 100644 index 0000000..fde24eb --- /dev/null +++ b/src/ogAdmLib.h @@ -0,0 +1,120 @@ +// ************************************************************************************************************************************************** +// Libreria: ogAdmLib +// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla +// Fecha Creación: Marzo-2010 +// Fecha Última modificación: Marzo-2010 +// Nombre del fichero: ogAdmLib.h +// Descripción: Este fichero implementa el archivo de cabecera de la libreria ogAdmLib +// ************************************************************************************************************************************************** +// ________________________________________________________________________________________________________ +// Valores definidos +// ________________________________________________________________________________________________________ +#define LONSTD 1024 // Longitud de memoria estandar +#define LONINT 16 // Longitud de memoria estandar para un número entero +#define LONFIL 1024 // Longitud de memoria estandar para nombres de archivo completos (incluido path) +#define LONIP 16 // Longitud de memoria estandar para cadenas que contiene una dirección IP +#define LONMAC 16 // Longitud de memoria estandar para cadenas que contiene una dirección MAC +#define LONSQL 8192 // Longitud de memoria estandar para una sentencia SQL +#define LONPRM 4098 // Longitud estandar de los parámetros del fichero de configuración del servicio +#define LONSCP 4098 // Longitud estandar de los parámetros de las tramas +#define LONFUN 512 // Longitud estandar de los nombres de las funciones que procesan las tramas +#define LONSUC 4098 // Longitud de los mensajes de sucesos +#define LONBLK 8192 // Longitud de los paquetes de tramas leidos cada vez +#define MAXPRM 20 // Máximo número de parámeros del fichero de configuración del servicio +#define MAXPAR 128 // Maximo numero de particiones manejadas por el sistema, ahora con GPT es 128 +#define MAXLONURL 1024 // Longitud máxima de una dirección url con parámetros + +#define LONHEXPRM 5 // Longitud del campo que contiene el tamaño de la cadena de parámetros +#define LONGITUD_CABECERATRAMA 16 // Longitud de la cabecera de las tramas +#define LONGITUD_PARAMETROS 8192 // Longitud estandar de la información de la trama (parámetros) +#define MAXCMD_PARAMETROS 200 // Máximo número de parámetros de una trama + +#define MAXIMOS_CLIENTES 4000 // Máximo número de conexiones con ordenadores clientes +#define MAXIMAS_FUNCIONES LONSTD // Máximo número de funciones que procesan los mensajes entre servicio y clientes + +#define CLIENTE_OCUPADO "BSY" // Cliente ocupado +#define CLIENTE_APAGADO "OFF" // Cliente apagado +#define CLIENTE_INICIANDO "INI" // Cliente iniciando + +#define ACCION_SINRESULTADO 0 // Sin resultado +#define ACCION_EXITOSA 1 // Finalizada con éxito +#define ACCION_FALLIDA 2 // Finalizada con errores + +#define ACCION_INICIADA 1 // Acción activa +#define ACCION_DETENIDA 2 // Acción momentanemente parada +#define ACCION_FINALIZADA 3 // Accion finalizada + +#define EJECUCION_COMANDO 1 +#define EJECUCION_PROCEDIMIENTO 2 +#define EJECUCION_TAREA 3 +#define EJECUCION_RESERVA 4 + +#define AMBITO_CENTROS 0x01 +#define AMBITO_GRUPOSAULAS 0x02 +#define AMBITO_AULAS 0x04 +#define AMBITO_GRUPOSORDENADORES 0x08 +#define AMBITO_ORDENADORES 0x10 + +#define ANNOREF 2009 // Año de referencia base + +#define PUERTO_WAKEUP 9 // Puerto wake up + +#define MAXHARDWARE 128 // Máximos elementos hardware a detectar +#define MAXSOFTWARE 8096 // Máximos elementos software a detectar +// ________________________________________________________________________________________________________ +// Tipos definidos +// ________________________________________________________________________________________________________ +typedef unsigned long DWORD; +typedef unsigned short WORD; +typedef int BOOLEAN; +typedef char BYTE; +typedef int SOCKET; +typedef void* LPVOID; + +#define TRUE 1 +#define FALSE 0 + +// ________________________________________________________________________________________________________ +// Variables globales +// ________________________________________________________________________________________________________ +char szPathFileCfg[LONSTD],szPathFileLog[LONSTD]; +int ndebug; // Nivel de debuger + +typedef struct{ // Estructura de las tramas + char arroba; // Caracter arroba siempre + char identificador[14]; // Identificador de la trama, siempre JMMLCAMDJ_MCDJ + char tipo; // Tipo de mensaje + long lonprm; // Longitud en hexadecimal de los parámetros + char *parametros; // Parámetros de la trama +}TRAMA; +// ________________________________________________________________________________________________________ +// Prototipo de funciones +// ________________________________________________________________________________________________________ +BOOLEAN validacionParametros(int,char**,int); +char* reservaMemoria(int); +char* ampliaMemoria(char*,int); +void liberaMemoria(void*); +BOOLEAN initParametros(TRAMA*,int); +int splitCadena(char **,char *, char); +char* StrToUpper(char *); +void FINCADaINTRO(TRAMA*); +char *tomaParametro(const char*,TRAMA*); +char *copiaParametro(const char*,TRAMA *); +BOOLEAN contieneIP(char *,char *); +char* rTrim(char *); +BOOLEAN enviaMensaje(SOCKET *,TRAMA *,char); +BOOLEAN mandaTrama(SOCKET*,TRAMA*); +BOOLEAN sendData(SOCKET *, char* ,int ); +BOOLEAN enviaTrama(SOCKET *,TRAMA *); +TRAMA* recibeTrama(SOCKET*); +char* escaparCadena(char *cadena); + +#include <stddef.h> /* for offsetof. */ + +#define container_of(ptr, type, member) ({ \ + typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) + +#include <stdbool.h> + +bool tomaConfiguracion(const char *filecfg); diff --git a/src/ogAdmServer.c b/src/ogAdmServer.c new file mode 100644 index 0000000..436c3b1 --- /dev/null +++ b/src/ogAdmServer.c @@ -0,0 +1,1301 @@ +// ******************************************************************************************************* +// Servicio: ogAdmServer +// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla +// Fecha Creación: Marzo-2010 +// Fecha Última modificación: Marzo-2010 +// Nombre del fichero: ogAdmServer.cpp +// Descripción :Este fichero implementa el servicio de administración general del sistema +// ******************************************************************************************************* +#include "ogAdmServer.h" +#include "dbi.h" +#include "utils.h" +#include "list.h" +#include "rest.h" +#include "client.h" +#include "json.h" +#include "schedule.h" +#include <syslog.h> +#include <sys/ioctl.h> +#include <ifaddrs.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <jansson.h> +#include <time.h> + +char usuario[LONPRM]; // Usuario de acceso a la base de datos +char pasguor[LONPRM]; // Password del usuario +char datasource[LONPRM]; // Dirección IP del gestor de base de datos +char catalog[LONPRM]; // Nombre de la base de datos +char interface[LONPRM]; // Interface name +char auth_token[LONPRM]; // API token + +struct og_dbi_config dbi_config = { + .user = usuario, + .passwd = pasguor, + .host = datasource, + .database = catalog, +}; + +//________________________________________________________________________________________________________ +// Función: tomaConfiguracion +// +// Descripción: +// Lee el fichero de configuración del servicio +// Parámetros: +// filecfg : Ruta completa al fichero de configuración +// Devuelve: +// true: Si el proceso es correcto +// false: En caso de ocurrir algún error +//________________________________________________________________________________________________________ +bool tomaConfiguracion(const char *filecfg) +{ + char buf[1024], *line; + char *key, *value; + FILE *fcfg; + + if (filecfg == NULL || strlen(filecfg) == 0) { + syslog(LOG_ERR, "No configuration file has been specified\n"); + return false; + } + + fcfg = fopen(filecfg, "rt"); + if (fcfg == NULL) { + syslog(LOG_ERR, "Cannot open configuration file `%s'\n", + filecfg); + return false; + } + + servidoradm[0] = '\0'; //inicializar variables globales + + line = fgets(buf, sizeof(buf), fcfg); + while (line != NULL) { + const char *delim = "="; + + line[strlen(line) - 1] = '\0'; + + key = strtok(line, delim); + value = strtok(NULL, delim); + + if (!strcmp(str_toupper(key), "SERVIDORADM")) + snprintf(servidoradm, sizeof(servidoradm), "%s", value); + else if (!strcmp(str_toupper(key), "PUERTO")) + snprintf(puerto, sizeof(puerto), "%s", value); + else if (!strcmp(str_toupper(key), "USUARIO")) + snprintf(usuario, sizeof(usuario), "%s", value); + else if (!strcmp(str_toupper(key), "PASSWORD")) + snprintf(pasguor, sizeof(pasguor), "%s", value); + else if (!strcmp(str_toupper(key), "DATASOURCE")) + snprintf(datasource, sizeof(datasource), "%s", value); + else if (!strcmp(str_toupper(key), "CATALOG")) + snprintf(catalog, sizeof(catalog), "%s", value); + else if (!strcmp(str_toupper(key), "INTERFACE")) + snprintf(interface, sizeof(interface), "%s", value); + else if (!strcmp(str_toupper(key), "APITOKEN")) + snprintf(auth_token, sizeof(auth_token), "%s", value); + + line = fgets(buf, sizeof(buf), fcfg); + } + + fclose(fcfg); + + if (!servidoradm[0]) { + syslog(LOG_ERR, "Missing SERVIDORADM in configuration file\n"); + return false; + } + if (!puerto[0]) { + syslog(LOG_ERR, "Missing PUERTO in configuration file\n"); + return false; + } + if (!usuario[0]) { + syslog(LOG_ERR, "Missing USUARIO in configuration file\n"); + return false; + } + if (!pasguor[0]) { + syslog(LOG_ERR, "Missing PASSWORD in configuration file\n"); + return false; + } + if (!datasource[0]) { + syslog(LOG_ERR, "Missing DATASOURCE in configuration file\n"); + return false; + } + if (!catalog[0]) { + syslog(LOG_ERR, "Missing CATALOG in configuration file\n"); + return false; + } + if (!interface[0]) + syslog(LOG_ERR, "Missing INTERFACE in configuration file\n"); + + return true; +} + +#define OG_CMD_MAXLEN 64 + +// ________________________________________________________________________________________________________ +// Función: clienteDisponible +// +// Descripción: +// Comprueba la disponibilidad del cliente para recibir comandos interactivos +// Parametros: +// - ip : La ip del cliente a buscar +// - idx: (Salida) Indice que ocupa el cliente, de estar ya registrado +// Devuelve: +// true: Si el cliente está disponible +// false: En caso contrario +// ________________________________________________________________________________________________________ +bool clienteDisponible(char *ip, int* idx) +{ + int estado; + + if (clienteExistente(ip, idx)) { + estado = strcmp(tbsockets[*idx].estado, CLIENTE_OCUPADO); // Cliente ocupado + if (estado == 0) + return false; + + estado = strcmp(tbsockets[*idx].estado, CLIENTE_APAGADO); // Cliente apagado + if (estado == 0) + return false; + + estado = strcmp(tbsockets[*idx].estado, CLIENTE_INICIANDO); // Cliente en proceso de inclusión + if (estado == 0) + return false; + + return true; // En caso contrario el cliente está disponible + } + return false; // Cliente no está registrado en el sistema +} +// ________________________________________________________________________________________________________ +// Función: clienteExistente +// +// Descripción: +// Comprueba si el cliente está registrado en la tabla de socket del sistema +// Parametros: +// - ip : La ip del cliente a buscar +// - idx:(Salida) Indice que ocupa el cliente, de estar ya registrado +// Devuelve: +// true: Si el cliente está registrado +// false: En caso contrario +// ________________________________________________________________________________________________________ +bool clienteExistente(char *ip, int* idx) +{ + int i; + for (i = 0; i < MAXIMOS_CLIENTES; i++) { + if (contieneIP(ip, tbsockets[i].ip)) { // Si existe la IP en la cadena + *idx = i; + return true; + } + } + return false; +} +// ________________________________________________________________________________________________________ +// Función: actualizaConfiguracion +// +// Descripción: +// Esta función actualiza la base de datos con la configuracion de particiones de un cliente +// Parámetros: +// - db: Objeto base de datos (ya operativo) +// - tbl: Objeto tabla +// - cfg: cadena con una Configuración +// - ido: Identificador del ordenador cliente +// Devuelve: +// true: Si el proceso es correcto +// false: En caso de ocurrir algún error +// Especificaciones: +// Los parametros de la configuración son: +// par= Número de partición +// cpt= Codigo o tipo de partición +// sfi= Sistema de ficheros que está implementado en la partición +// soi= Nombre del sistema de ficheros instalado en la partición +// tam= Tamaño de la partición +// ________________________________________________________________________________________________________ +bool actualizaConfiguracion(struct og_dbi *dbi, char *cfg, int ido) +{ + int lon, p, c,i, dato, swu, idsoi, idsfi,k; + char *ptrPar[MAXPAR], *ptrCfg[7], *ptrDual[2], tbPar[LONSTD]; + char *ser, *disk, *par, *cpt, *sfi, *soi, *tam, *uso; // Parametros de configuración. + dbi_result result, result_update; + const char *msglog; + + lon = 0; + p = splitCadena(ptrPar, cfg, '\n'); + for (i = 0; i < p; i++) { + c = splitCadena(ptrCfg, ptrPar[i], '\t'); + + // Si la 1ª lÃnea solo incluye el número de serie del equipo; actualizar BD. + if (i == 0 && c == 1) { + splitCadena(ptrDual, ptrCfg[0], '='); + ser = ptrDual[1]; + if (strlen(ser) > 0) { + // Solo actualizar si número de serie no existÃa. + result = dbi_conn_queryf(dbi->conn, + "UPDATE ordenadores SET numserie='%s'" + " WHERE idordenador=%d AND numserie IS NULL", + ser, ido); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result); + } + continue; + } + + // Distribución de particionado. + disk = par = cpt = sfi = soi = tam = uso = NULL; + + splitCadena(ptrDual, ptrCfg[0], '='); + disk = ptrDual[1]; // Número de disco + + splitCadena(ptrDual, ptrCfg[1], '='); + par = ptrDual[1]; // Número de partición + + k=splitCadena(ptrDual, ptrCfg[2], '='); + if(k==2){ + cpt = ptrDual[1]; // Código de partición + }else{ + cpt = (char*)"0"; + } + + k=splitCadena(ptrDual, ptrCfg[3], '='); + if(k==2){ + sfi = ptrDual[1]; // Sistema de ficheros + /* Comprueba existencia del s0xistema de ficheros instalado */ + idsfi = checkDato(dbi, sfi, "sistemasficheros", "descripcion","idsistemafichero"); + } + else + idsfi=0; + + k=splitCadena(ptrDual, ptrCfg[4], '='); + if(k==2){ // Sistema operativo detecdtado + soi = ptrDual[1]; // Nombre del S.O. instalado + /* Comprueba existencia del sistema operativo instalado */ + idsoi = checkDato(dbi, soi, "nombresos", "nombreso", "idnombreso"); + } + else + idsoi=0; + + splitCadena(ptrDual, ptrCfg[5], '='); + tam = ptrDual[1]; // Tamaño de la partición + + splitCadena(ptrDual, ptrCfg[6], '='); + uso = ptrDual[1]; // Porcentaje de uso del S.F. + + lon += sprintf(tbPar + lon, "(%s, %s),", disk, par); + + result = dbi_conn_queryf(dbi->conn, + "SELECT numdisk, numpar, tamano, uso, idsistemafichero, idnombreso" + " FROM ordenadores_particiones" + " WHERE idordenador=%d AND numdisk=%s AND numpar=%s", + ido, disk, par); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + if (!dbi_result_next_row(result)) { + result_update = dbi_conn_queryf(dbi->conn, + "INSERT INTO ordenadores_particiones(idordenador,numdisk,numpar,codpar,tamano,uso,idsistemafichero,idnombreso,idimagen)" + " VALUES(%d,%s,%s,0x%s,%s,%s,%d,%d,0)", + ido, disk, par, cpt, tam, uso, idsfi, idsoi); + if (!result_update) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result_update); + + } else { // Existe el registro + swu = true; // Se supone que algún dato ha cambiado + + dato = dbi_result_get_uint(result, "tamano"); + if (atoi(tam) == dato) {// Parámetro tamaño igual al almacenado + dato = dbi_result_get_uint(result, "idsistemafichero"); + if (idsfi == dato) {// Parámetro sistema de fichero igual al almacenado + dato = dbi_result_get_uint(result, "idnombreso"); + if (idsoi == dato) {// Parámetro sistema de fichero distinto al almacenado + swu = false; // Todos los parámetros de la partición son iguales, no se actualiza + } + } + } + if (swu) { // Hay que actualizar los parámetros de la partición + result_update = dbi_conn_queryf(dbi->conn, + "UPDATE ordenadores_particiones SET " + " codpar=0x%s," + " tamano=%s," + " uso=%s," + " idsistemafichero=%d," + " idnombreso=%d," + " idimagen=0," + " idperfilsoft=0," + " fechadespliegue=NULL" + " WHERE idordenador=%d AND numdisk=%s AND numpar=%s", + cpt, tam, uso, idsfi, idsoi, ido, disk, par); + } else { // Actualizar porcentaje de uso. + result_update = dbi_conn_queryf(dbi->conn, + "UPDATE ordenadores_particiones SET " + " codpar=0x%s," + " uso=%s" + " WHERE idordenador=%d AND numdisk=%s AND numpar=%s", + cpt, uso, ido, disk, par); + } + if (!result_update) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + + dbi_result_free(result_update); + } + dbi_result_free(result); + } + lon += sprintf(tbPar + lon, "(0,0)"); + // Eliminar particiones almacenadas que ya no existen + result_update = dbi_conn_queryf(dbi->conn, + "DELETE FROM ordenadores_particiones WHERE idordenador=%d AND (numdisk, numpar) NOT IN (%s)", + ido, tbPar); + if (!result_update) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result_update); + + return true; +} +// ________________________________________________________________________________________________________ +// Función: checkDato +// +// Descripción: +// Esta función comprueba si existe un dato en una tabla y si no es asà lo incluye. devuelve en +// cualquier caso el identificador del registro existenet o del insertado +// Parámetros: +// - db: Objeto base de datos (ya operativo) +// - tbl: Objeto tabla +// - dato: Dato +// - tabla: Nombre de la tabla +// - nomdato: Nombre del dato en la tabla +// - nomidentificador: Nombre del identificador en la tabla +// Devuelve: +// El identificador del registro existente o el del insertado +// +// Especificaciones: +// En caso de producirse algún error se devuelve el valor 0 +// ________________________________________________________________________________________________________ + +int checkDato(struct og_dbi *dbi, char *dato, const char *tabla, + const char *nomdato, const char *nomidentificador) +{ + const char *msglog; + int identificador; + dbi_result result; + + if (strlen(dato) == 0) + return (0); // EL dato no tiene valor + result = dbi_conn_queryf(dbi->conn, + "SELECT %s FROM %s WHERE %s ='%s'", nomidentificador, + tabla, nomdato, dato); + + // Ejecuta consulta + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return (0); + } + if (!dbi_result_next_row(result)) { // Software NO existente + dbi_result_free(result); + + result = dbi_conn_queryf(dbi->conn, + "INSERT INTO %s (%s) VALUES('%s')", tabla, nomdato, dato); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return (0); + } + // Recupera el identificador del software + identificador = dbi_conn_sequence_last(dbi->conn, NULL); + } else { + identificador = dbi_result_get_uint(result, nomidentificador); + } + dbi_result_free(result); + + return (identificador); +} + +// ________________________________________________________________________________________________________ +// Función: Levanta +// +// Descripción: +// Enciende ordenadores a través de la red cuyas macs se pasan como parámetro +// Parámetros: +// - iph: Cadena de direcciones ip separadas por ";" +// - mac: Cadena de direcciones mac separadas por ";" +// - mar: Método de arranque (1=Broadcast, 2=Unicast) +// Devuelve: +// true: Si el proceso es correcto +// false: En caso de ocurrir algún error +// ________________________________________________________________________________________________________ + +bool Levanta(char *ptrIP[], char *ptrMacs[], int lon, char *mar) +{ + unsigned int on = 1; + struct sockaddr_in local; + int i, res; + int s; + + /* Creación de socket para envÃo de magig packet */ + s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (s < 0) { + syslog(LOG_ERR, "cannot create socket for magic packet\n"); + return false; + } + res = setsockopt(s, SOL_SOCKET, SO_BROADCAST, (unsigned int *) &on, + sizeof(on)); + if (res < 0) { + syslog(LOG_ERR, "cannot set broadcast socket\n"); + return false; + } + memset(&local, 0, sizeof(local)); + local.sin_family = AF_INET; + local.sin_port = htons(PUERTO_WAKEUP); + local.sin_addr.s_addr = htonl(INADDR_ANY); + + for (i = 0; i < lon; i++) { + if (!WakeUp(s, ptrIP[i], ptrMacs[i], mar)) { + syslog(LOG_ERR, "problem sending magic packet\n"); + close(s); + return false; + } + } + close(s); + return true; +} + +#define OG_WOL_SEQUENCE 6 +#define OG_WOL_MACADDR_LEN 6 +#define OG_WOL_REPEAT 16 + +struct wol_msg { + char secuencia_FF[OG_WOL_SEQUENCE]; + char macbin[OG_WOL_REPEAT][OG_WOL_MACADDR_LEN]; +}; + +static bool wake_up_broadcast(int sd, struct sockaddr_in *client, + const struct wol_msg *msg) +{ + struct sockaddr_in *broadcast_addr; + struct ifaddrs *ifaddr, *ifa; + int ret; + + if (getifaddrs(&ifaddr) < 0) { + syslog(LOG_ERR, "cannot get list of addresses\n"); + return false; + } + + client->sin_addr.s_addr = htonl(INADDR_BROADCAST); + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL || + ifa->ifa_addr->sa_family != AF_INET || + strcmp(ifa->ifa_name, interface) != 0) + continue; + + broadcast_addr = + (struct sockaddr_in *)ifa->ifa_ifu.ifu_broadaddr; + client->sin_addr.s_addr = broadcast_addr->sin_addr.s_addr; + break; + } + freeifaddrs(ifaddr); + + ret = sendto(sd, msg, sizeof(*msg), 0, + (struct sockaddr *)client, sizeof(*client)); + if (ret < 0) { + syslog(LOG_ERR, "failed to send broadcast wol\n"); + return false; + } + + return true; +} + +static bool wake_up_unicast(int sd, struct sockaddr_in *client, + const struct wol_msg *msg, + const struct in_addr *addr) +{ + int ret; + + client->sin_addr.s_addr = addr->s_addr; + + ret = sendto(sd, msg, sizeof(*msg), 0, + (struct sockaddr *)client, sizeof(*client)); + if (ret < 0) { + syslog(LOG_ERR, "failed to send unicast wol\n"); + return false; + } + + return true; +} + +enum wol_delivery_type { + OG_WOL_BROADCAST = 1, + OG_WOL_UNICAST = 2 +}; + +//_____________________________________________________________________________________________________________ +// Función: WakeUp +// +// Descripción: +// Enciende el ordenador cuya MAC se pasa como parámetro +// Parámetros: +// - s : Socket para enviar trama magic packet +// - iph : Cadena con la dirección ip +// - mac : Cadena con la dirección mac en formato XXXXXXXXXXXX +// - mar: Método de arranque (1=Broadcast, 2=Unicast) +// Devuelve: +// true: Si el proceso es correcto +// false: En caso de ocurrir algún error +//_____________________________________________________________________________________________________________ +// +bool WakeUp(int s, char* iph, char *mac, char *mar) +{ + unsigned int macaddr[OG_WOL_MACADDR_LEN]; + char HDaddress_bin[OG_WOL_MACADDR_LEN]; + struct sockaddr_in WakeUpCliente; + struct wol_msg Trama_WakeUp; + struct in_addr addr; + bool ret; + int i; + + for (i = 0; i < 6; i++) // Primera secuencia de la trama Wake Up (0xFFFFFFFFFFFF) + Trama_WakeUp.secuencia_FF[i] = 0xFF; + + sscanf(mac, "%02x%02x%02x%02x%02x%02x", + &macaddr[0], &macaddr[1], &macaddr[2], + &macaddr[3], &macaddr[4], &macaddr[5]); + + for (i = 0; i < 6; i++) + HDaddress_bin[i] = (uint8_t)macaddr[i]; + + for (i = 0; i < 16; i++) // Segunda secuencia de la trama Wake Up , repetir 16 veces su la MAC + memcpy(&Trama_WakeUp.macbin[i][0], &HDaddress_bin, 6); + + /* Creación de socket del cliente que recibe la trama magic packet */ + WakeUpCliente.sin_family = AF_INET; + WakeUpCliente.sin_port = htons((short) PUERTO_WAKEUP); + + switch (atoi(mar)) { + case OG_WOL_BROADCAST: + ret = wake_up_broadcast(s, &WakeUpCliente, &Trama_WakeUp); + break; + case OG_WOL_UNICAST: + if (inet_aton(iph, &addr) < 0) { + syslog(LOG_ERR, "bad IP address for unicast wol\n"); + ret = false; + break; + } + ret = wake_up_unicast(s, &WakeUpCliente, &Trama_WakeUp, &addr); + break; + default: + syslog(LOG_ERR, "unknown wol type\n"); + ret = false; + break; + } + return ret; +} + +// ________________________________________________________________________________________________________ +// Función: actualizaCreacionImagen +// +// Descripción: +// Esta función actualiza la base de datos con el resultado de la creación de una imagen +// Parámetros: +// - db: Objeto base de datos (ya operativo) +// - tbl: Objeto tabla +// - idi: Identificador de la imagen +// - dsk: Disco de donde se creó +// - par: Partición de donde se creó +// - cpt: Código de partición +// - ipr: Ip del repositorio +// - ido: Identificador del ordenador modelo +// Devuelve: +// true: Si el proceso es correcto +// false: En caso de ocurrir algún error +// ________________________________________________________________________________________________________ +bool actualizaCreacionImagen(struct og_dbi *dbi, char *idi, char *dsk, + char *par, char *cpt, char *ipr, char *ido) +{ + const char *msglog; + dbi_result result; + int idr,ifs; + + /* Toma identificador del repositorio correspondiente al ordenador modelo */ + result = dbi_conn_queryf(dbi->conn, + "SELECT repositorios.idrepositorio" + " FROM repositorios" + " LEFT JOIN ordenadores USING (idrepositorio)" + " WHERE repositorios.ip='%s' AND ordenadores.idordenador=%s", ipr, ido); + + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + if (!dbi_result_next_row(result)) { + syslog(LOG_ERR, + "repository does not exist in database (%s:%d)\n", + __func__, __LINE__); + dbi_result_free(result); + return false; + } + idr = dbi_result_get_uint(result, "idrepositorio"); + dbi_result_free(result); + + /* Toma identificador del perfilsoftware */ + result = dbi_conn_queryf(dbi->conn, + "SELECT idperfilsoft" + " FROM ordenadores_particiones" + " WHERE idordenador=%s AND numdisk=%s AND numpar=%s", ido, dsk, par); + + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + if (!dbi_result_next_row(result)) { + syslog(LOG_ERR, + "software profile does not exist in database (%s:%d)\n", + __func__, __LINE__); + dbi_result_free(result); + return false; + } + ifs = dbi_result_get_uint(result, "idperfilsoft"); + dbi_result_free(result); + + /* Actualizar los datos de la imagen */ + result = dbi_conn_queryf(dbi->conn, + "UPDATE imagenes" + " SET idordenador=%s, numdisk=%s, numpar=%s, codpar=%s," + " idperfilsoft=%d, idrepositorio=%d," + " fechacreacion=NOW(), revision=revision+1" + " WHERE idimagen=%s", ido, dsk, par, cpt, ifs, idr, idi); + + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result); + + /* Actualizar los datos en el cliente */ + result = dbi_conn_queryf(dbi->conn, + "UPDATE ordenadores_particiones" + " SET idimagen=%s, revision=(SELECT revision FROM imagenes WHERE idimagen=%s)," + " fechadespliegue=NOW()" + " WHERE idordenador=%s AND numdisk=%s AND numpar=%s", + idi, idi, ido, dsk, par); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result); + + return true; +} + +// ________________________________________________________________________________________________________ +// Función: actualizaRestauracionImagen +// +// Descripción: +// Esta función actualiza la base de datos con el resultado de la restauración de una imagen +// Parámetros: +// - db: Objeto base de datos (ya operativo) +// - tbl: Objeto tabla +// - idi: Identificador de la imagen +// - dsk: Disco de donde se restauró +// - par: Partición de donde se restauró +// - ido: Identificador del cliente donde se restauró +// - ifs: Identificador del perfil software contenido en la imagen +// Devuelve: +// true: Si el proceso es correcto +// false: En caso de ocurrir algún error +// ________________________________________________________________________________________________________ +bool actualizaRestauracionImagen(struct og_dbi *dbi, char *idi, + char *dsk, char *par, char *ido, char *ifs) +{ + const char *msglog; + dbi_result result; + + /* Actualizar los datos de la imagen */ + result = dbi_conn_queryf(dbi->conn, + "UPDATE ordenadores_particiones" + " SET idimagen=%s, idperfilsoft=%s, fechadespliegue=NOW()," + " revision=(SELECT revision FROM imagenes WHERE idimagen=%s)," + " idnombreso=IFNULL((SELECT idnombreso FROM perfilessoft WHERE idperfilsoft=%s),0)" + " WHERE idordenador=%s AND numdisk=%s AND numpar=%s", idi, ifs, idi, ifs, ido, dsk, par); + + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result); + + return true; +} +// ________________________________________________________________________________________________________ +// Función: actualizaHardware +// +// Descripción: +// Actualiza la base de datos con la configuracion hardware del cliente +// Parámetros: +// - db: Objeto base de datos (ya operativo) +// - tbl: Objeto tabla +// - hrd: cadena con el inventario hardware +// - ido: Identificador del ordenador +// - npc: Nombre del ordenador +// - idc: Identificador del centro o Unidad organizativa +// ________________________________________________________________________________________________________ +// +bool actualizaHardware(struct og_dbi *dbi, char *hrd, char *ido, char *npc, + char *idc) +{ + const char *msglog; + int idtipohardware, idperfilhard; + int lon, i, j, aux; + bool retval; + char *whard; + int tbidhardware[MAXHARDWARE]; + char *tbHardware[MAXHARDWARE],*dualHardware[2], strInt[LONINT], *idhardwares; + dbi_result result; + + /* Toma Centro (Unidad Organizativa) */ + result = dbi_conn_queryf(dbi->conn, + "SELECT idperfilhard FROM ordenadores WHERE idordenador=%s", + ido); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + if (!dbi_result_next_row(result)) { + syslog(LOG_ERR, "client does not exist in database (%s:%d)\n", + __func__, __LINE__); + dbi_result_free(result); + return false; + } + idperfilhard = dbi_result_get_uint(result, "idperfilhard"); + dbi_result_free(result); + + whard=escaparCadena(hrd); // Codificar comillas simples + if(!whard) + return false; + /* Recorre componentes hardware*/ + lon = splitCadena(tbHardware, whard, '\n'); + if (lon > MAXHARDWARE) + lon = MAXHARDWARE; // Limita el número de componentes hardware + /* + for (i=0;i<lon;i++){ + sprintf(msglog,"Linea de inventario: %s",tbHardware[i]); + RegistraLog(msglog,false); + } + */ + for (i = 0; i < lon; i++) { + splitCadena(dualHardware, rTrim(tbHardware[i]), '='); + //sprintf(msglog,"nemonico: %s",dualHardware[0]); + //RegistraLog(msglog,false); + //sprintf(msglog,"valor: %s",dualHardware[1]); + //RegistraLog(msglog,false); + result = dbi_conn_queryf(dbi->conn, + "SELECT idtipohardware,descripcion FROM tipohardwares WHERE nemonico='%s'", + dualHardware[0]); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + if (!dbi_result_next_row(result)) { // Tipo de Hardware NO existente + dbi_result_free(result); + return false; + } else { // Tipo de Hardware Existe + idtipohardware = dbi_result_get_uint(result, "idtipohardware"); + dbi_result_free(result); + + result = dbi_conn_queryf(dbi->conn, + "SELECT idhardware FROM hardwares WHERE idtipohardware=%d AND descripcion='%s'", + idtipohardware, dualHardware[1]); + + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + + if (!dbi_result_next_row(result)) { // Hardware NO existente + dbi_result_free(result); + result = dbi_conn_queryf(dbi->conn, + "INSERT hardwares (idtipohardware,descripcion,idcentro,grupoid) " + " VALUES(%d,'%s',%s,0)", idtipohardware, + dualHardware[1], idc); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + + // Recupera el identificador del hardware + tbidhardware[i] = dbi_conn_sequence_last(dbi->conn, NULL); + } else { + tbidhardware[i] = dbi_result_get_uint(result, "idhardware"); + } + dbi_result_free(result); + } + } + // Ordena tabla de identificadores para cosultar si existe un pefil con esas especificaciones + + for (i = 0; i < lon - 1; i++) { + for (j = i + 1; j < lon; j++) { + if (tbidhardware[i] > tbidhardware[j]) { + aux = tbidhardware[i]; + tbidhardware[i] = tbidhardware[j]; + tbidhardware[j] = aux; + } + } + } + /* Crea cadena de identificadores de componentes hardware separados por coma */ + sprintf(strInt, "%d", tbidhardware[lon - 1]); // Pasa a cadena el último identificador que es de mayor longitud + aux = strlen(strInt); // Calcula longitud de cadena para reservar espacio a todos los perfiles + idhardwares = calloc(1, sizeof(aux) * lon + lon); + if (idhardwares == NULL) { + syslog(LOG_ERR, "%s:%d OOM\n", __FILE__, __LINE__); + return false; + } + aux = sprintf(idhardwares, "%d", tbidhardware[0]); + for (i = 1; i < lon; i++) + aux += sprintf(idhardwares + aux, ",%d", tbidhardware[i]); + + if (!cuestionPerfilHardware(dbi, idc, ido, idperfilhard, idhardwares, + npc, tbidhardware, lon)) { + syslog(LOG_ERR, "Problem updating client hardware\n"); + retval=false; + } else { + retval=true; + } + free(whard); + free(idhardwares); + + return (retval); +} +// ________________________________________________________________________________________________________ +// Función: cuestionPerfilHardware +// +// Descripción: +// Comprueba existencia de perfil hardware y actualización de éste para el ordenador +// Parámetros: +// - db: Objeto base de datos (ya operativo) +// - tbl: Objeto tabla +// - idc: Identificador de la Unidad organizativa donde se encuentra el cliente +// - ido: Identificador del ordenador +// - tbidhardware: Identificador del tipo de hardware +// - con: Número de componentes detectados para configurar un el perfil hardware +// - npc: Nombre del cliente +// ________________________________________________________________________________________________________ +bool cuestionPerfilHardware(struct og_dbi *dbi, char *idc, char *ido, + int idperfilhardware, char *idhardwares, char *npc, int *tbidhardware, + int lon) +{ + const char *msglog; + dbi_result result; + int i; + int nwidperfilhard; + + // Busca perfil hard del ordenador que contenga todos los componentes hardware encontrados + result = dbi_conn_queryf(dbi->conn, + "SELECT idperfilhard FROM" + " (SELECT perfileshard_hardwares.idperfilhard as idperfilhard," + " group_concat(cast(perfileshard_hardwares.idhardware AS char( 11) )" + " ORDER BY perfileshard_hardwares.idhardware SEPARATOR ',' ) AS idhardwares" + " FROM perfileshard_hardwares" + " GROUP BY perfileshard_hardwares.idperfilhard) AS temp" + " WHERE idhardwares LIKE '%s'", idhardwares); + + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + if (!dbi_result_next_row(result)) { + // No existe un perfil hardware con esos componentes de componentes hardware, lo crea + dbi_result_free(result); + result = dbi_conn_queryf(dbi->conn, + "INSERT perfileshard (descripcion,idcentro,grupoid)" + " VALUES('Perfil hardware (%s) ',%s,0)", npc, idc); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result); + + // Recupera el identificador del nuevo perfil hardware + nwidperfilhard = dbi_conn_sequence_last(dbi->conn, NULL); + + // Crea la relación entre perfiles y componenetes hardware + for (i = 0; i < lon; i++) { + result = dbi_conn_queryf(dbi->conn, + "INSERT perfileshard_hardwares (idperfilhard,idhardware)" + " VALUES(%d,%d)", nwidperfilhard, tbidhardware[i]); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result); + } + } else { // Existe un perfil con todos esos componentes + nwidperfilhard = dbi_result_get_uint(result, "idperfilhard"); + dbi_result_free(result); + } + if (idperfilhardware != nwidperfilhard) { // No coinciden los perfiles + // Actualiza el identificador del perfil hardware del ordenador + result = dbi_conn_queryf(dbi->conn, + "UPDATE ordenadores SET idperfilhard=%d" + " WHERE idordenador=%s", nwidperfilhard, ido); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result); + } + /* Eliminar Relación de hardwares con Perfiles hardware que quedan húerfanos */ + result = dbi_conn_queryf(dbi->conn, + "DELETE FROM perfileshard_hardwares WHERE idperfilhard IN " + " (SELECT idperfilhard FROM perfileshard WHERE idperfilhard NOT IN" + " (SELECT DISTINCT idperfilhard from ordenadores))"); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result); + + /* Eliminar Perfiles hardware que quedan húerfanos */ + result = dbi_conn_queryf(dbi->conn, + "DELETE FROM perfileshard WHERE idperfilhard NOT IN" + " (SELECT DISTINCT idperfilhard FROM ordenadores)"); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result); + + /* Eliminar Relación de hardwares con Perfiles hardware que quedan húerfanos */ + result = dbi_conn_queryf(dbi->conn, + "DELETE FROM perfileshard_hardwares WHERE idperfilhard NOT IN" + " (SELECT idperfilhard FROM perfileshard)"); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result); + + return true; +} +// ________________________________________________________________________________________________________ +// Función: actualizaSoftware +// +// Descripción: +// Actualiza la base de datos con la configuración software del cliente +// Parámetros: +// - db: Objeto base de datos (ya operativo) +// - tbl: Objeto tabla +// - sft: cadena con el inventario software +// - par: Número de la partición +// - ido: Identificador del ordenador del cliente en la tabla +// - npc: Nombre del ordenador +// - idc: Identificador del centro o Unidad organizativa +// Devuelve: +// true: Si el proceso es correcto +// false: En caso de ocurrir algún error +// +// Versión 1.1.0: Se incluye el sistema operativo. Autora: Irina Gómez - ETSII Universidad Sevilla +// ________________________________________________________________________________________________________ +bool actualizaSoftware(struct og_dbi *dbi, char *sft, char *par,char *ido, + char *npc, char *idc) +{ + int i, j, lon, aux, idperfilsoft, idnombreso; + bool retval; + char *wsft; + int tbidsoftware[MAXSOFTWARE]; + char *tbSoftware[MAXSOFTWARE], strInt[LONINT], *idsoftwares; + const char *msglog; + dbi_result result; + + /* Toma Centro (Unidad Organizativa) y perfil software */ + result = dbi_conn_queryf(dbi->conn, + "SELECT idperfilsoft,numpar" + " FROM ordenadores_particiones" + " WHERE idordenador=%s", ido); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + idperfilsoft = 0; // Por defecto se supone que el ordenador no tiene aún detectado el perfil software + while (dbi_result_next_row(result)) { + aux = dbi_result_get_uint(result, "numpar"); + if (aux == atoi(par)) { // Se encuentra la partición + idperfilsoft = dbi_result_get_uint(result, "idperfilsoft"); + break; + } + } + dbi_result_free(result); + wsft=escaparCadena(sft); // Codificar comillas simples + if(!wsft) + return false; + + /* Recorre componentes software*/ + lon = splitCadena(tbSoftware, wsft, '\n'); + + if (lon == 0) + return true; // No hay lineas que procesar + if (lon > MAXSOFTWARE) + lon = MAXSOFTWARE; // Limita el número de componentes software + + idnombreso = 0; + for (i = 0; i < lon; i++) { + // Primera lÃnea es el sistema operativo: se obtiene identificador + if (i == 0) { + idnombreso = checkDato(dbi, rTrim(tbSoftware[i]), "nombresos", "nombreso", "idnombreso"); + continue; + } + + result = dbi_conn_queryf(dbi->conn, + "SELECT idsoftware FROM softwares WHERE descripcion ='%s'", + rTrim(tbSoftware[i])); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + + if (!dbi_result_next_row(result)) { + dbi_result_free(result); + result = dbi_conn_queryf(dbi->conn, + "INSERT INTO softwares (idtiposoftware,descripcion,idcentro,grupoid)" + " VALUES(2,'%s',%s,0)", tbSoftware[i], idc); + if (!result) { // Error al insertar + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + + // Recupera el identificador del software + tbidsoftware[i] = dbi_conn_sequence_last(dbi->conn, NULL); + } else { + tbidsoftware[i] = dbi_result_get_uint(result, "idsoftware"); + } + dbi_result_free(result); + } + + // Ordena tabla de identificadores para cosultar si existe un pefil con esas especificaciones + + for (i = 0; i < lon - 1; i++) { + for (j = i + 1; j < lon; j++) { + if (tbidsoftware[i] > tbidsoftware[j]) { + aux = tbidsoftware[i]; + tbidsoftware[i] = tbidsoftware[j]; + tbidsoftware[j] = aux; + } + } + } + /* Crea cadena de identificadores de componentes software separados por coma */ + sprintf(strInt, "%d", tbidsoftware[lon - 1]); // Pasa a cadena el último identificador que es de mayor longitud + aux = strlen(strInt); // Calcula longitud de cadena para reservar espacio a todos los perfiles + idsoftwares = calloc(1, (sizeof(aux)+1) * lon + lon); + if (idsoftwares == NULL) { + syslog(LOG_ERR, "%s:%d OOM\n", __FILE__, __LINE__); + return false; + } + aux = sprintf(idsoftwares, "%d", tbidsoftware[0]); + for (i = 1; i < lon; i++) + aux += sprintf(idsoftwares + aux, ",%d", tbidsoftware[i]); + + // Comprueba existencia de perfil software y actualización de éste para el ordenador + if (!cuestionPerfilSoftware(dbi, idc, ido, idperfilsoft, idnombreso, idsoftwares, + npc, par, tbidsoftware, lon)) { + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + retval=false; + } else { + retval=true; + } + free(wsft); + free(idsoftwares); + + return retval; +} +// ________________________________________________________________________________________________________ +// Función: CuestionPerfilSoftware +// +// Parámetros: +// - db: Objeto base de datos (ya operativo) +// - tbl: Objeto tabla +// - idcentro: Identificador del centro en la tabla +// - ido: Identificador del ordenador del cliente en la tabla +// - idnombreso: Identificador del sistema operativo +// - idsoftwares: Cadena con los identificadores de componentes software separados por comas +// - npc: Nombre del ordenador del cliente +// - particion: Número de la partición +// - tbidsoftware: Array con los identificadores de componentes software +// - lon: Número de componentes +// Devuelve: +// true: Si el proceso es correcto +// false: En caso de ocurrir algún error +// +// Versión 1.1.0: Se incluye el sistema operativo. Autora: Irina Gómez - ETSII Universidad Sevilla +//_________________________________________________________________________________________________________ +bool cuestionPerfilSoftware(struct og_dbi *dbi, char *idc, char *ido, + int idperfilsoftware, int idnombreso, + char *idsoftwares, char *npc, char *par, + int *tbidsoftware, int lon) +{ + int i, nwidperfilsoft; + const char *msglog; + dbi_result result; + + // Busca perfil soft del ordenador que contenga todos los componentes software encontrados + result = dbi_conn_queryf(dbi->conn, + "SELECT idperfilsoft FROM" + " (SELECT perfilessoft_softwares.idperfilsoft as idperfilsoft," + " group_concat(cast(perfilessoft_softwares.idsoftware AS char( 11) )" + " ORDER BY perfilessoft_softwares.idsoftware SEPARATOR ',' ) AS idsoftwares" + " FROM perfilessoft_softwares" + " GROUP BY perfilessoft_softwares.idperfilsoft) AS temp" + " WHERE idsoftwares LIKE '%s'", idsoftwares); + + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + if (!dbi_result_next_row(result)) { // No existe un perfil software con esos componentes de componentes software, lo crea + dbi_result_free(result); + result = dbi_conn_queryf(dbi->conn, + "INSERT perfilessoft (descripcion, idcentro, grupoid, idnombreso)" + " VALUES('Perfil Software (%s, Part:%s) ',%s,0,%i)", npc, par, idc,idnombreso); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + + dbi_result_free(result); + // Recupera el identificador del nuevo perfil software + nwidperfilsoft = dbi_conn_sequence_last(dbi->conn, NULL); + + // Crea la relación entre perfiles y componenetes software + for (i = 0; i < lon; i++) { + result = dbi_conn_queryf(dbi->conn, + "INSERT perfilessoft_softwares (idperfilsoft,idsoftware)" + " VALUES(%d,%d)", nwidperfilsoft, tbidsoftware[i]); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result); + } + } else { // Existe un perfil con todos esos componentes + nwidperfilsoft = dbi_result_get_uint(result, "idperfilsoft"); + dbi_result_free(result); + } + + if (idperfilsoftware != nwidperfilsoft) { // No coinciden los perfiles + // Actualiza el identificador del perfil software del ordenador + result = dbi_conn_queryf(dbi->conn, + "UPDATE ordenadores_particiones SET idperfilsoft=%d,idimagen=0" + " WHERE idordenador=%s AND numpar=%s", nwidperfilsoft, ido, par); + if (!result) { // Error al insertar + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result); + } + + /* DEPURACIÓN DE PERFILES SOFTWARE */ + + /* Eliminar Relación de softwares con Perfiles software que quedan húerfanos */ + result = dbi_conn_queryf(dbi->conn, + "DELETE FROM perfilessoft_softwares WHERE idperfilsoft IN "\ + " (SELECT idperfilsoft FROM perfilessoft WHERE idperfilsoft NOT IN"\ + " (SELECT DISTINCT idperfilsoft from ordenadores_particiones) AND idperfilsoft NOT IN"\ + " (SELECT DISTINCT idperfilsoft from imagenes))"); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result), + /* Eliminar Perfiles software que quedan húerfanos */ + result = dbi_conn_queryf(dbi->conn, + "DELETE FROM perfilessoft WHERE idperfilsoft NOT IN" + " (SELECT DISTINCT idperfilsoft from ordenadores_particiones)"\ + " AND idperfilsoft NOT IN"\ + " (SELECT DISTINCT idperfilsoft from imagenes)"); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result), + + /* Eliminar Relación de softwares con Perfiles software que quedan húerfanos */ + result = dbi_conn_queryf(dbi->conn, + "DELETE FROM perfilessoft_softwares WHERE idperfilsoft NOT IN" + " (SELECT idperfilsoft from perfilessoft)"); + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return false; + } + dbi_result_free(result); + + return true; +} diff --git a/src/ogAdmServer.h b/src/ogAdmServer.h new file mode 100644 index 0000000..d5061cd --- /dev/null +++ b/src/ogAdmServer.h @@ -0,0 +1,51 @@ +// ******************************************************************************************************** +// Servicio: ogAdmServer +// Autor: José Manuel Alonso (E.T.S.I.I.) Universidad de Sevilla +// Fecha Creación: Marzo-2010 +// Fecha Última modificación: Marzo-2010 +// Nombre del fichero: ogAdmServer.h +// Descripción: Este fichero implementa el servicio de administración general del sistema +// ******************************************************************************************************** +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <unistd.h> +#include <time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdbool.h> +#include "ogAdmLib.h" +// ________________________________________________________________________________________________________ +// Variables globales +// ________________________________________________________________________________________________________ +char servidoradm[LONPRM]; // Dirección IP del servidor de administración +char puerto[LONPRM]; // Puerto de comunicación + +struct og_client; + +typedef struct{ // Estructura usada para guardar información de los clientes + char ip[LONIP]; // IP del cliente + char estado[4]; // Tipo de Sistema Operativo en que se encuentra el cliente + struct og_client *cli; +}SOCKETCL; +SOCKETCL tbsockets[MAXIMOS_CLIENTES]; + +struct og_dbi; + +bool clienteExistente(char *,int *); +bool clienteDisponible(char *,int *); +bool actualizaConfiguracion(struct og_dbi *,char* ,int); +bool Levanta(char**, char**, int, char*); +bool WakeUp(int,char*,char*,char*); +bool actualizaCreacionImagen(struct og_dbi *,char*,char*,char*,char*,char*,char*); +bool actualizaRestauracionImagen(struct og_dbi *,char*,char*,char*,char*,char*); +bool actualizaHardware(struct og_dbi *dbi, char* ,char*,char*,char*); +bool cuestionPerfilHardware(struct og_dbi *dbi,char*,char*,int,char*,char*,int *,int); +bool actualizaSoftware(struct og_dbi *, char* , char* , char*,char*,char*); +bool cuestionPerfilSoftware(struct og_dbi *, char*, char*,int,int,char*,char*,char*,int *,int); + +int checkDato(struct og_dbi *,char*,const char*,const char*,const char*); diff --git a/src/rest.c b/src/rest.c new file mode 100644 index 0000000..097cbf8 --- /dev/null +++ b/src/rest.c @@ -0,0 +1,3296 @@ +/* + * Copyright (C) 2020 Soleta Networks <info@soleta.eu> + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the + * Free Software Foundation, version 3. + */ + +#include "ogAdmServer.h" +#include "dbi.h" +#include "utils.h" +#include "list.h" +#include "rest.h" +#include "cfg.h" +#include "schedule.h" +#include <ev.h> +#include <syslog.h> +#include <sys/ioctl.h> +#include <ifaddrs.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <jansson.h> +#include <time.h> + +struct ev_loop *og_loop; + +static TRAMA *og_msg_alloc(char *data, unsigned int len) +{ + TRAMA *ptrTrama; + + ptrTrama = calloc(1, sizeof(TRAMA)); + if (!ptrTrama) { + syslog(LOG_ERR, "OOM\n"); + return NULL; + } + + initParametros(ptrTrama, len); + memcpy(ptrTrama, "@JMMLCAMDJ_MCDJ", LONGITUD_CABECERATRAMA); + memcpy(ptrTrama->parametros, data, len); + ptrTrama->lonprm = len; + + return ptrTrama; +} + +static void og_msg_free(TRAMA *ptrTrama) +{ + free(ptrTrama->parametros); + free(ptrTrama); +} + +static bool og_send_cmd(char *ips_array[], int ips_array_len, + const char *state, TRAMA *ptrTrama) +{ + int i, idx; + + for (i = 0; i < ips_array_len; i++) { + if (clienteDisponible(ips_array[i], &idx)) { // Si el cliente puede recibir comandos + int sock = tbsockets[idx].cli ? tbsockets[idx].cli->io.fd : -1; + + strcpy(tbsockets[idx].estado, state); // Actualiza el estado del cliente + if (sock >= 0 && !mandaTrama(&sock, ptrTrama)) { + syslog(LOG_ERR, "failed to send response to %s:%s\n", + ips_array[i], strerror(errno)); + } + } + } + return true; +} + +#define OG_REST_PARAM_ADDR (1UL << 0) +#define OG_REST_PARAM_MAC (1UL << 1) +#define OG_REST_PARAM_WOL_TYPE (1UL << 2) +#define OG_REST_PARAM_RUN_CMD (1UL << 3) +#define OG_REST_PARAM_DISK (1UL << 4) +#define OG_REST_PARAM_PARTITION (1UL << 5) +#define OG_REST_PARAM_REPO (1UL << 6) +#define OG_REST_PARAM_NAME (1UL << 7) +#define OG_REST_PARAM_ID (1UL << 8) +#define OG_REST_PARAM_CODE (1UL << 9) +#define OG_REST_PARAM_TYPE (1UL << 10) +#define OG_REST_PARAM_PROFILE (1UL << 11) +#define OG_REST_PARAM_CACHE (1UL << 12) +#define OG_REST_PARAM_CACHE_SIZE (1UL << 13) +#define OG_REST_PARAM_PART_0 (1UL << 14) +#define OG_REST_PARAM_PART_1 (1UL << 15) +#define OG_REST_PARAM_PART_2 (1UL << 16) +#define OG_REST_PARAM_PART_3 (1UL << 17) +#define OG_REST_PARAM_SYNC_SYNC (1UL << 18) +#define OG_REST_PARAM_SYNC_DIFF (1UL << 19) +#define OG_REST_PARAM_SYNC_REMOVE (1UL << 20) +#define OG_REST_PARAM_SYNC_COMPRESS (1UL << 21) +#define OG_REST_PARAM_SYNC_CLEANUP (1UL << 22) +#define OG_REST_PARAM_SYNC_CACHE (1UL << 23) +#define OG_REST_PARAM_SYNC_CLEANUP_CACHE (1UL << 24) +#define OG_REST_PARAM_SYNC_REMOVE_DST (1UL << 25) +#define OG_REST_PARAM_SYNC_DIFF_ID (1UL << 26) +#define OG_REST_PARAM_SYNC_DIFF_NAME (1UL << 27) +#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) +#define OG_REST_PARAM_TIME_YEARS (1UL << 32) +#define OG_REST_PARAM_TIME_MONTHS (1UL << 33) +#define OG_REST_PARAM_TIME_WEEKS (1UL << 34) +#define OG_REST_PARAM_TIME_WEEK_DAYS (1UL << 35) +#define OG_REST_PARAM_TIME_DAYS (1UL << 36) +#define OG_REST_PARAM_TIME_HOURS (1UL << 37) +#define OG_REST_PARAM_TIME_AM_PM (1UL << 38) +#define OG_REST_PARAM_TIME_MINUTES (1UL << 39) + +static LIST_HEAD(client_list); + +void og_client_add(struct og_client *cli) +{ + list_add(&cli->list, &client_list); +} + +static struct og_client *og_client_find(const char *ip) +{ + struct og_client *client; + struct in_addr addr; + int res; + + res = inet_aton(ip, &addr); + if (!res) { + syslog(LOG_ERR, "Invalid IP string: %s\n", ip); + return NULL; + } + + list_for_each_entry(client, &client_list, list) { + if (client->addr.sin_addr.s_addr == addr.s_addr && client->agent) { + return client; + } + } + + return NULL; +} + +static const char *og_client_status(const struct og_client *cli) +{ + if (cli->last_cmd != OG_CMD_UNSPEC) + return "BSY"; + + switch (cli->status) { + case OG_CLIENT_STATUS_BUSY: + return "BSY"; + case OG_CLIENT_STATUS_OGLIVE: + return "OPG"; + case OG_CLIENT_STATUS_VIRTUAL: + return "VDI"; + default: + return "OFF"; + } +} + +static bool og_msg_params_validate(const struct og_msg_params *params, + const uint64_t flags) +{ + return (params->flags & flags) == flags; +} + +static int og_json_parse_clients(json_t *element, struct og_msg_params *params) +{ + unsigned int i; + json_t *k; + + if (json_typeof(element) != JSON_ARRAY) + return -1; + + for (i = 0; i < json_array_size(element); i++) { + k = json_array_get(element, i); + if (json_typeof(k) != JSON_STRING) + return -1; + + params->ips_array[params->ips_array_len++] = + json_string_value(k); + + params->flags |= OG_REST_PARAM_ADDR; + } + + return 0; +} + +static int og_json_parse_sync_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, "sync")) { + err = og_json_parse_string(value, ¶ms->sync_setup.sync); + params->flags |= OG_REST_PARAM_SYNC_SYNC; + } else if (!strcmp(key, "diff")) { + err = og_json_parse_string(value, ¶ms->sync_setup.diff); + params->flags |= OG_REST_PARAM_SYNC_DIFF; + } else if (!strcmp(key, "remove")) { + err = og_json_parse_string(value, ¶ms->sync_setup.remove); + params->flags |= OG_REST_PARAM_SYNC_REMOVE; + } else if (!strcmp(key, "compress")) { + err = og_json_parse_string(value, ¶ms->sync_setup.compress); + params->flags |= OG_REST_PARAM_SYNC_COMPRESS; + } else if (!strcmp(key, "cleanup")) { + err = og_json_parse_string(value, ¶ms->sync_setup.cleanup); + params->flags |= OG_REST_PARAM_SYNC_CLEANUP; + } else if (!strcmp(key, "cache")) { + err = og_json_parse_string(value, ¶ms->sync_setup.cache); + params->flags |= OG_REST_PARAM_SYNC_CACHE; + } else if (!strcmp(key, "cleanup_cache")) { + err = og_json_parse_string(value, ¶ms->sync_setup.cleanup_cache); + params->flags |= OG_REST_PARAM_SYNC_CLEANUP_CACHE; + } else if (!strcmp(key, "remove_dst")) { + err = og_json_parse_string(value, ¶ms->sync_setup.remove_dst); + params->flags |= OG_REST_PARAM_SYNC_REMOVE_DST; + } else if (!strcmp(key, "diff_id")) { + err = og_json_parse_string(value, ¶ms->sync_setup.diff_id); + params->flags |= OG_REST_PARAM_SYNC_DIFF_ID; + } else if (!strcmp(key, "diff_name")) { + err = og_json_parse_string(value, ¶ms->sync_setup.diff_name); + params->flags |= OG_REST_PARAM_SYNC_DIFF_NAME; + } else if (!strcmp(key, "path")) { + err = og_json_parse_string(value, ¶ms->sync_setup.path); + params->flags |= OG_REST_PARAM_SYNC_PATH; + } else if (!strcmp(key, "method")) { + err = og_json_parse_string(value, ¶ms->sync_setup.method); + params->flags |= OG_REST_PARAM_SYNC_METHOD; + } + + if (err != 0) + return err; + } + return err; +} + +static int og_json_parse_partition_setup(json_t *element, + struct og_msg_params *params) +{ + unsigned int i; + json_t *k; + + if (json_typeof(element) != JSON_ARRAY) + return -1; + + for (i = 0; i < json_array_size(element) && i < OG_PARTITION_MAX; ++i) { + k = json_array_get(element, i); + + if (json_typeof(k) != JSON_OBJECT) + return -1; + + if (og_json_parse_partition(k, ¶ms->partition_setup[i], + OG_PARAM_PART_NUMBER | + OG_PARAM_PART_CODE | + OG_PARAM_PART_FILESYSTEM | + OG_PARAM_PART_SIZE | + OG_PARAM_PART_FORMAT) < 0) + return -1; + + params->flags |= (OG_REST_PARAM_PART_0 << i); + } + 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, ¶ms->time.years); + params->flags |= OG_REST_PARAM_TIME_YEARS; + } else if (!strcmp(key, "months")) { + err = og_json_parse_uint(value, ¶ms->time.months); + params->flags |= OG_REST_PARAM_TIME_MONTHS; + } else if (!strcmp(key, "weeks")) { + err = og_json_parse_uint(value, ¶ms->time.weeks); + params->flags |= OG_REST_PARAM_TIME_WEEKS; + } else if (!strcmp(key, "week_days")) { + err = og_json_parse_uint(value, ¶ms->time.week_days); + params->flags |= OG_REST_PARAM_TIME_WEEK_DAYS; + } else if (!strcmp(key, "days")) { + err = og_json_parse_uint(value, ¶ms->time.days); + params->flags |= OG_REST_PARAM_TIME_DAYS; + } else if (!strcmp(key, "hours")) { + err = og_json_parse_uint(value, ¶ms->time.hours); + params->flags |= OG_REST_PARAM_TIME_HOURS; + } else if (!strcmp(key, "am_pm")) { + err = og_json_parse_uint(value, ¶ms->time.am_pm); + params->flags |= OG_REST_PARAM_TIME_AM_PM; + } else if (!strcmp(key, "minutes")) { + err = og_json_parse_uint(value, ¶ms->time.minutes); + params->flags |= OG_REST_PARAM_TIME_MINUTES; + } + if (err != 0) + return err; + } + + return err; +} + +static const char *og_cmd_to_uri[OG_CMD_MAX] = { + [OG_CMD_WOL] = "wol", + [OG_CMD_PROBE] = "probe", + [OG_CMD_SHELL_RUN] = "shell/run", + [OG_CMD_SESSION] = "session", + [OG_CMD_POWEROFF] = "poweroff", + [OG_CMD_REFRESH] = "refresh", + [OG_CMD_REBOOT] = "reboot", + [OG_CMD_STOP] = "stop", + [OG_CMD_HARDWARE] = "hardware", + [OG_CMD_SOFTWARE] = "software", + [OG_CMD_IMAGE_CREATE] = "image/create", + [OG_CMD_IMAGE_RESTORE] = "image/restore", + [OG_CMD_SETUP] = "setup", + [OG_CMD_RUN_SCHEDULE] = "run/schedule", +}; + +static bool og_client_is_busy(const struct og_client *cli, + enum og_cmd_type type) +{ + switch (type) { + case OG_CMD_REBOOT: + case OG_CMD_POWEROFF: + case OG_CMD_STOP: + break; + default: + if (cli->last_cmd != OG_CMD_UNSPEC) + return true; + break; + } + + return false; +} + +int og_send_request(enum og_rest_method method, enum og_cmd_type type, + const struct og_msg_params *params, + const json_t *data) +{ + const char *content_type = "Content-Type: application/json"; + char content [OG_MSG_REQUEST_MAXLEN - 700] = {}; + char buf[OG_MSG_REQUEST_MAXLEN] = {}; + unsigned int content_length; + char method_str[5] = {}; + struct og_client *cli; + const char *uri; + unsigned int i; + int client_sd; + + if (method == OG_METHOD_GET) + snprintf(method_str, 5, "GET"); + else if (method == OG_METHOD_POST) + snprintf(method_str, 5, "POST"); + else + return -1; + + if (!data) + content_length = 0; + else + content_length = json_dumpb(data, content, + OG_MSG_REQUEST_MAXLEN - 700, + JSON_COMPACT); + + uri = og_cmd_to_uri[type]; + snprintf(buf, OG_MSG_REQUEST_MAXLEN, + "%s /%s HTTP/1.1\r\nContent-Length: %d\r\n%s\r\n\r\n%s", + method_str, uri, content_length, content_type, content); + + for (i = 0; i < params->ips_array_len; i++) { + cli = og_client_find(params->ips_array[i]); + if (!cli) + continue; + + if (og_client_is_busy(cli, type)) + continue; + + client_sd = cli->io.fd; + if (client_sd < 0) { + syslog(LOG_INFO, "Client %s not conected\n", + params->ips_array[i]); + continue; + } + + if (send(client_sd, buf, strlen(buf), 0) < 0) + continue; + + cli->last_cmd = type; + } + + return 0; +} + +static int og_cmd_post_clients(json_t *element, struct og_msg_params *params) +{ + const char *key; + json_t *value; + int err = 0; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) + err = og_json_parse_clients(value, params); + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) + return -1; + + return og_send_request(OG_METHOD_POST, OG_CMD_PROBE, params, NULL); +} + +struct og_buffer { + char *data; + int len; +}; + +static int og_json_dump_clients(const char *buffer, size_t size, void *data) +{ + struct og_buffer *og_buffer = (struct og_buffer *)data; + + memcpy(og_buffer->data + og_buffer->len, buffer, size); + og_buffer->len += size; + + return 0; +} + +static int og_cmd_get_clients(json_t *element, struct og_msg_params *params, + char *buffer_reply) +{ + json_t *root, *array, *addr, *state, *object; + struct og_client *client; + struct og_buffer og_buffer = { + .data = buffer_reply, + }; + + array = json_array(); + if (!array) + return -1; + + list_for_each_entry(client, &client_list, list) { + if (!client->agent) + continue; + + object = json_object(); + if (!object) { + json_decref(array); + return -1; + } + addr = json_string(inet_ntoa(client->addr.sin_addr)); + if (!addr) { + json_decref(object); + json_decref(array); + return -1; + } + json_object_set_new(object, "addr", addr); + state = json_string(og_client_status(client)); + if (!state) { + json_decref(object); + json_decref(array); + return -1; + } + json_object_set_new(object, "state", state); + json_array_append_new(array, object); + } + root = json_pack("{s:o}", "clients", array); + if (!root) { + json_decref(array); + return -1; + } + + json_dump_callback(root, og_json_dump_clients, &og_buffer, 0); + json_decref(root); + + return 0; +} + +static int og_json_parse_target(json_t *element, struct og_msg_params *params) +{ + const char *key; + json_t *value; + + if (json_typeof(element) != JSON_OBJECT) { + return -1; + } + + json_object_foreach(element, key, value) { + if (!strcmp(key, "addr")) { + if (json_typeof(value) != JSON_STRING) + return -1; + + params->ips_array[params->ips_array_len] = + json_string_value(value); + + params->flags |= OG_REST_PARAM_ADDR; + } else if (!strcmp(key, "mac")) { + if (json_typeof(value) != JSON_STRING) + return -1; + + params->mac_array[params->ips_array_len] = + json_string_value(value); + + params->flags |= OG_REST_PARAM_MAC; + } + } + + return 0; +} + +static int og_json_parse_targets(json_t *element, struct og_msg_params *params) +{ + unsigned int i; + json_t *k; + int err; + + if (json_typeof(element) != JSON_ARRAY) + return -1; + + for (i = 0; i < json_array_size(element); i++) { + k = json_array_get(element, i); + + if (json_typeof(k) != JSON_OBJECT) + return -1; + + err = og_json_parse_target(k, params); + if (err < 0) + return err; + + params->ips_array_len++; + } + return 0; +} + +static int og_json_parse_type(json_t *element, struct og_msg_params *params) +{ + const char *type; + + if (json_typeof(element) != JSON_STRING) + return -1; + + params->wol_type = json_string_value(element); + + type = json_string_value(element); + if (!strcmp(type, "unicast")) + params->wol_type = "2"; + else if (!strcmp(type, "broadcast")) + params->wol_type = "1"; + + params->flags |= OG_REST_PARAM_WOL_TYPE; + + return 0; +} + +static int og_cmd_wol(json_t *element, struct og_msg_params *params) +{ + const char *key; + json_t *value; + int err = 0; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) { + err = og_json_parse_targets(value, params); + } else if (!strcmp(key, "type")) { + err = og_json_parse_type(value, params); + } + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | + OG_REST_PARAM_MAC | + OG_REST_PARAM_WOL_TYPE)) + return -1; + + if (!Levanta((char **)params->ips_array, (char **)params->mac_array, + params->ips_array_len, (char *)params->wol_type)) + return -1; + + return 0; +} + +static int og_json_parse_run(json_t *element, struct og_msg_params *params) +{ + if (json_typeof(element) != JSON_STRING) + return -1; + + snprintf(params->run_cmd, sizeof(params->run_cmd), "%s", + json_string_value(element)); + + params->flags |= OG_REST_PARAM_RUN_CMD; + + return 0; +} + +static int og_cmd_run_post(json_t *element, struct og_msg_params *params) +{ + json_t *value, *clients; + const char *key; + unsigned int i; + int err = 0; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) + err = og_json_parse_clients(value, params); + else if (!strcmp(key, "run")) + err = og_json_parse_run(value, params); + else if (!strcmp(key, "echo")) { + err = og_json_parse_bool(value, ¶ms->echo); + params->flags |= OG_REST_PARAM_ECHO; + } + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | + OG_REST_PARAM_RUN_CMD | + OG_REST_PARAM_ECHO)) + return -1; + + clients = json_copy(element); + json_object_del(clients, "clients"); + + err = og_send_request(OG_METHOD_POST, OG_CMD_SHELL_RUN, params, clients); + if (err < 0) + return err; + + for (i = 0; i < params->ips_array_len; i++) { + char filename[4096]; + FILE *f; + + sprintf(filename, "/tmp/_Seconsola_%s", params->ips_array[i]); + f = fopen(filename, "wt"); + fclose(f); + } + + return 0; +} + +static int og_cmd_run_get(json_t *element, struct og_msg_params *params, + char *buffer_reply) +{ + struct og_buffer og_buffer = { + .data = buffer_reply, + }; + json_t *root, *value, *array; + const char *key; + unsigned int i; + int err = 0; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) + err = og_json_parse_clients(value, params); + + if (err < 0) + return err; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) + return -1; + + array = json_array(); + if (!array) + return -1; + + for (i = 0; i < params->ips_array_len; i++) { + json_t *object, *output, *addr; + char data[4096] = {}; + char filename[4096]; + int fd, numbytes; + + sprintf(filename, "/tmp/_Seconsola_%s", params->ips_array[i]); + + fd = open(filename, O_RDONLY); + if (!fd) + return -1; + + numbytes = read(fd, data, sizeof(data)); + if (numbytes < 0) { + close(fd); + return -1; + } + data[sizeof(data) - 1] = '\0'; + close(fd); + + object = json_object(); + if (!object) { + json_decref(array); + return -1; + } + addr = json_string(params->ips_array[i]); + if (!addr) { + json_decref(object); + json_decref(array); + return -1; + } + json_object_set_new(object, "addr", addr); + + output = json_string(data); + if (!output) { + json_decref(object); + json_decref(array); + return -1; + } + json_object_set_new(object, "output", output); + + json_array_append_new(array, object); + } + + root = json_pack("{s:o}", "clients", array); + if (!root) + return -1; + + json_dump_callback(root, og_json_dump_clients, &og_buffer, 0); + json_decref(root); + + return 0; +} + +static int og_cmd_session(json_t *element, struct og_msg_params *params) +{ + json_t *clients, *value; + const char *key; + int err = 0; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) { + err = og_json_parse_clients(value, params); + } else if (!strcmp(key, "disk")) { + err = og_json_parse_string(value, ¶ms->disk); + params->flags |= OG_REST_PARAM_DISK; + } else if (!strcmp(key, "partition")) { + err = og_json_parse_string(value, ¶ms->partition); + params->flags |= OG_REST_PARAM_PARTITION; + } + + if (err < 0) + return err; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | + OG_REST_PARAM_DISK | + OG_REST_PARAM_PARTITION)) + return -1; + + clients = json_copy(element); + json_object_del(clients, "clients"); + + return og_send_request(OG_METHOD_POST, OG_CMD_SESSION, params, clients); +} + +static int og_cmd_poweroff(json_t *element, struct og_msg_params *params) +{ + const char *key; + json_t *value; + int err = 0; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) + err = og_json_parse_clients(value, params); + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) + return -1; + + return og_send_request(OG_METHOD_POST, OG_CMD_POWEROFF, params, NULL); +} + +static int og_cmd_refresh(json_t *element, struct og_msg_params *params) +{ + const char *key; + json_t *value; + int err = 0; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) + err = og_json_parse_clients(value, params); + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) + return -1; + + return og_send_request(OG_METHOD_GET, OG_CMD_REFRESH, params, NULL); +} + +static int og_cmd_reboot(json_t *element, struct og_msg_params *params) +{ + const char *key; + json_t *value; + int err = 0; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) + err = og_json_parse_clients(value, params); + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) + return -1; + + return og_send_request(OG_METHOD_POST, OG_CMD_REBOOT, params, NULL); +} + +static int og_cmd_stop(json_t *element, struct og_msg_params *params) +{ + const char *key; + json_t *value; + int err = 0; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) + err = og_json_parse_clients(value, params); + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) + return -1; + + return og_send_request(OG_METHOD_POST, OG_CMD_STOP, params, NULL); +} + +static int og_cmd_hardware(json_t *element, struct og_msg_params *params) +{ + const char *key; + json_t *value; + int err = 0; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) + err = og_json_parse_clients(value, params); + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) + return -1; + + return og_send_request(OG_METHOD_GET, OG_CMD_HARDWARE, params, NULL); +} + +static int og_cmd_software(json_t *element, struct og_msg_params *params) +{ + json_t *clients, *value; + const char *key; + int err = 0; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) + err = og_json_parse_clients(value, params); + else if (!strcmp(key, "disk")) { + err = og_json_parse_string(value, ¶ms->disk); + params->flags |= OG_REST_PARAM_DISK; + } + else if (!strcmp(key, "partition")) { + err = og_json_parse_string(value, ¶ms->partition); + params->flags |= OG_REST_PARAM_PARTITION; + } + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | + OG_REST_PARAM_DISK | + OG_REST_PARAM_PARTITION)) + return -1; + + clients = json_copy(element); + json_object_del(clients, "clients"); + + return og_send_request(OG_METHOD_POST, OG_CMD_SOFTWARE, params, clients); +} + +static int og_cmd_create_image(json_t *element, struct og_msg_params *params) +{ + json_t *value, *clients; + const char *key; + int err = 0; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "disk")) { + err = og_json_parse_string(value, ¶ms->disk); + params->flags |= OG_REST_PARAM_DISK; + } else if (!strcmp(key, "partition")) { + err = og_json_parse_string(value, ¶ms->partition); + params->flags |= OG_REST_PARAM_PARTITION; + } else if (!strcmp(key, "name")) { + err = og_json_parse_string(value, ¶ms->name); + params->flags |= OG_REST_PARAM_NAME; + } else if (!strcmp(key, "repository")) { + err = og_json_parse_string(value, ¶ms->repository); + params->flags |= OG_REST_PARAM_REPO; + } else if (!strcmp(key, "clients")) { + err = og_json_parse_clients(value, params); + } else if (!strcmp(key, "id")) { + err = og_json_parse_string(value, ¶ms->id); + params->flags |= OG_REST_PARAM_ID; + } else if (!strcmp(key, "code")) { + err = og_json_parse_string(value, ¶ms->code); + params->flags |= OG_REST_PARAM_CODE; + } + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | + OG_REST_PARAM_DISK | + OG_REST_PARAM_PARTITION | + OG_REST_PARAM_CODE | + OG_REST_PARAM_ID | + OG_REST_PARAM_NAME | + OG_REST_PARAM_REPO)) + return -1; + + clients = json_copy(element); + json_object_del(clients, "clients"); + + return og_send_request(OG_METHOD_POST, OG_CMD_IMAGE_CREATE, params, + clients); +} + +static int og_cmd_restore_image(json_t *element, struct og_msg_params *params) +{ + json_t *clients, *value; + const char *key; + int err = 0; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "disk")) { + err = og_json_parse_string(value, ¶ms->disk); + params->flags |= OG_REST_PARAM_DISK; + } else if (!strcmp(key, "partition")) { + err = og_json_parse_string(value, ¶ms->partition); + params->flags |= OG_REST_PARAM_PARTITION; + } else if (!strcmp(key, "name")) { + err = og_json_parse_string(value, ¶ms->name); + params->flags |= OG_REST_PARAM_NAME; + } else if (!strcmp(key, "repository")) { + err = og_json_parse_string(value, ¶ms->repository); + params->flags |= OG_REST_PARAM_REPO; + } else if (!strcmp(key, "clients")) { + err = og_json_parse_clients(value, params); + } else if (!strcmp(key, "type")) { + err = og_json_parse_string(value, ¶ms->type); + params->flags |= OG_REST_PARAM_TYPE; + } else if (!strcmp(key, "profile")) { + err = og_json_parse_string(value, ¶ms->profile); + params->flags |= OG_REST_PARAM_PROFILE; + } else if (!strcmp(key, "id")) { + err = og_json_parse_string(value, ¶ms->id); + params->flags |= OG_REST_PARAM_ID; + } + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | + OG_REST_PARAM_DISK | + OG_REST_PARAM_PARTITION | + OG_REST_PARAM_NAME | + OG_REST_PARAM_REPO | + OG_REST_PARAM_TYPE | + OG_REST_PARAM_PROFILE | + OG_REST_PARAM_ID)) + return -1; + + clients = json_copy(element); + json_object_del(clients, "clients"); + + return og_send_request(OG_METHOD_POST, OG_CMD_IMAGE_RESTORE, params, + clients); +} + +static int og_cmd_setup(json_t *element, struct og_msg_params *params) +{ + json_t *value, *clients; + const char *key; + int err = 0; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) { + err = og_json_parse_clients(value, params); + } else if (!strcmp(key, "disk")) { + err = og_json_parse_string(value, ¶ms->disk); + params->flags |= OG_REST_PARAM_DISK; + } else if (!strcmp(key, "cache")) { + err = og_json_parse_string(value, ¶ms->cache); + params->flags |= OG_REST_PARAM_CACHE; + } else if (!strcmp(key, "cache_size")) { + err = og_json_parse_string(value, ¶ms->cache_size); + params->flags |= OG_REST_PARAM_CACHE_SIZE; + } else if (!strcmp(key, "partition_setup")) { + err = og_json_parse_partition_setup(value, params); + } + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | + OG_REST_PARAM_DISK | + OG_REST_PARAM_CACHE | + OG_REST_PARAM_CACHE_SIZE | + OG_REST_PARAM_PART_0 | + OG_REST_PARAM_PART_1 | + OG_REST_PARAM_PART_2 | + OG_REST_PARAM_PART_3)) + return -1; + + clients = json_copy(element); + json_object_del(clients, "clients"); + + return og_send_request(OG_METHOD_POST, OG_CMD_SETUP, params, clients); +} + +static int og_cmd_run_schedule(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, "clients")) + err = og_json_parse_clients(value, params); + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) + return -1; + + return og_send_request(OG_METHOD_GET, OG_CMD_RUN_SCHEDULE, params, + NULL); +} + +static int og_cmd_create_basic_image(json_t *element, struct og_msg_params *params) +{ + char buf[4096] = {}; + int err = 0, len; + const char *key; + json_t *value; + TRAMA *msg; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) { + err = og_json_parse_clients(value, params); + } else if (!strcmp(key, "disk")) { + err = og_json_parse_string(value, ¶ms->disk); + params->flags |= OG_REST_PARAM_DISK; + } else if (!strcmp(key, "partition")) { + err = og_json_parse_string(value, ¶ms->partition); + params->flags |= OG_REST_PARAM_PARTITION; + } else if (!strcmp(key, "code")) { + err = og_json_parse_string(value, ¶ms->code); + params->flags |= OG_REST_PARAM_CODE; + } else if (!strcmp(key, "id")) { + err = og_json_parse_string(value, ¶ms->id); + params->flags |= OG_REST_PARAM_ID; + } else if (!strcmp(key, "name")) { + err = og_json_parse_string(value, ¶ms->name); + params->flags |= OG_REST_PARAM_NAME; + } else if (!strcmp(key, "repository")) { + err = og_json_parse_string(value, ¶ms->repository); + params->flags |= OG_REST_PARAM_REPO; + } else if (!strcmp(key, "sync_params")) { + err = og_json_parse_sync_params(value, params); + } + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | + OG_REST_PARAM_DISK | + OG_REST_PARAM_PARTITION | + OG_REST_PARAM_CODE | + OG_REST_PARAM_ID | + OG_REST_PARAM_NAME | + OG_REST_PARAM_REPO | + OG_REST_PARAM_SYNC_SYNC | + OG_REST_PARAM_SYNC_DIFF | + OG_REST_PARAM_SYNC_REMOVE | + OG_REST_PARAM_SYNC_COMPRESS | + OG_REST_PARAM_SYNC_CLEANUP | + OG_REST_PARAM_SYNC_CACHE | + OG_REST_PARAM_SYNC_CLEANUP_CACHE | + OG_REST_PARAM_SYNC_REMOVE_DST)) + return -1; + + len = snprintf(buf, sizeof(buf), + "nfn=CrearImagenBasica\rdsk=%s\rpar=%s\rcpt=%s\ridi=%s\r" + "nci=%s\ripr=%s\rrti=\rmsy=%s\rwhl=%s\reli=%s\rcmp=%s\rbpi=%s\r" + "cpc=%s\rbpc=%s\rnba=%s\r", + params->disk, params->partition, params->code, params->id, + params->name, params->repository, params->sync_setup.sync, + params->sync_setup.diff, params->sync_setup.remove, + params->sync_setup.compress, params->sync_setup.cleanup, + params->sync_setup.cache, params->sync_setup.cleanup_cache, + params->sync_setup.remove_dst); + + msg = og_msg_alloc(buf, len); + if (!msg) + return -1; + + og_send_cmd((char **)params->ips_array, params->ips_array_len, + CLIENTE_OCUPADO, msg); + + og_msg_free(msg); + + return 0; +} + +static int og_cmd_create_incremental_image(json_t *element, struct og_msg_params *params) +{ + char buf[4096] = {}; + int err = 0, len; + const char *key; + json_t *value; + TRAMA *msg; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) + err = og_json_parse_clients(value, params); + else if (!strcmp(key, "disk")) { + err = og_json_parse_string(value, ¶ms->disk); + params->flags |= OG_REST_PARAM_DISK; + } else if (!strcmp(key, "partition")) { + err = og_json_parse_string(value, ¶ms->partition); + params->flags |= OG_REST_PARAM_PARTITION; + } else if (!strcmp(key, "id")) { + err = og_json_parse_string(value, ¶ms->id); + params->flags |= OG_REST_PARAM_ID; + } else if (!strcmp(key, "name")) { + err = og_json_parse_string(value, ¶ms->name); + params->flags |= OG_REST_PARAM_NAME; + } else if (!strcmp(key, "repository")) { + err = og_json_parse_string(value, ¶ms->repository); + params->flags |= OG_REST_PARAM_REPO; + } else if (!strcmp(key, "sync_params")) { + err = og_json_parse_sync_params(value, params); + } + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | + OG_REST_PARAM_DISK | + OG_REST_PARAM_PARTITION | + OG_REST_PARAM_ID | + OG_REST_PARAM_NAME | + OG_REST_PARAM_REPO | + OG_REST_PARAM_SYNC_SYNC | + OG_REST_PARAM_SYNC_PATH | + OG_REST_PARAM_SYNC_DIFF | + OG_REST_PARAM_SYNC_DIFF_ID | + OG_REST_PARAM_SYNC_DIFF_NAME | + OG_REST_PARAM_SYNC_REMOVE | + OG_REST_PARAM_SYNC_COMPRESS | + OG_REST_PARAM_SYNC_CLEANUP | + OG_REST_PARAM_SYNC_CACHE | + OG_REST_PARAM_SYNC_CLEANUP_CACHE | + OG_REST_PARAM_SYNC_REMOVE_DST)) + return -1; + + len = snprintf(buf, sizeof(buf), + "nfn=CrearSoftIncremental\rdsk=%s\rpar=%s\ridi=%s\rnci=%s\r" + "rti=%s\ripr=%s\ridf=%s\rncf=%s\rmsy=%s\rwhl=%s\reli=%s\rcmp=%s\r" + "bpi=%s\rcpc=%s\rbpc=%s\rnba=%s\r", + params->disk, params->partition, params->id, params->name, + params->sync_setup.path, params->repository, params->sync_setup.diff_id, + params->sync_setup.diff_name, params->sync_setup.sync, + params->sync_setup.diff, params->sync_setup.remove_dst, + params->sync_setup.compress, params->sync_setup.cleanup, + params->sync_setup.cache, params->sync_setup.cleanup_cache, + params->sync_setup.remove_dst); + + msg = og_msg_alloc(buf, len); + if (!msg) + return -1; + + og_send_cmd((char **)params->ips_array, params->ips_array_len, + CLIENTE_OCUPADO, msg); + + og_msg_free(msg); + + return 0; +} + +static int og_cmd_restore_basic_image(json_t *element, struct og_msg_params *params) +{ + char buf[4096] = {}; + int err = 0, len; + const char *key; + json_t *value; + TRAMA *msg; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) { + err = og_json_parse_clients(value, params); + } else if (!strcmp(key, "disk")) { + err = og_json_parse_string(value, ¶ms->disk); + params->flags |= OG_REST_PARAM_DISK; + } else if (!strcmp(key, "partition")) { + err = og_json_parse_string(value, ¶ms->partition); + params->flags |= OG_REST_PARAM_PARTITION; + } else if (!strcmp(key, "id")) { + err = og_json_parse_string(value, ¶ms->id); + params->flags |= OG_REST_PARAM_ID; + } else if (!strcmp(key, "name")) { + err = og_json_parse_string(value, ¶ms->name); + params->flags |= OG_REST_PARAM_NAME; + } else if (!strcmp(key, "repository")) { + err = og_json_parse_string(value, ¶ms->repository); + params->flags |= OG_REST_PARAM_REPO; + } else if (!strcmp(key, "profile")) { + err = og_json_parse_string(value, ¶ms->profile); + params->flags |= OG_REST_PARAM_PROFILE; + } else if (!strcmp(key, "type")) { + err = og_json_parse_string(value, ¶ms->type); + params->flags |= OG_REST_PARAM_TYPE; + } else if (!strcmp(key, "sync_params")) { + err = og_json_parse_sync_params(value, params); + } + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | + OG_REST_PARAM_DISK | + OG_REST_PARAM_PARTITION | + OG_REST_PARAM_ID | + OG_REST_PARAM_NAME | + OG_REST_PARAM_REPO | + OG_REST_PARAM_PROFILE | + OG_REST_PARAM_TYPE | + OG_REST_PARAM_SYNC_PATH | + OG_REST_PARAM_SYNC_METHOD | + OG_REST_PARAM_SYNC_SYNC | + OG_REST_PARAM_SYNC_DIFF | + OG_REST_PARAM_SYNC_REMOVE | + OG_REST_PARAM_SYNC_COMPRESS | + OG_REST_PARAM_SYNC_CLEANUP | + OG_REST_PARAM_SYNC_CACHE | + OG_REST_PARAM_SYNC_CLEANUP_CACHE | + OG_REST_PARAM_SYNC_REMOVE_DST)) + return -1; + + len = snprintf(buf, sizeof(buf), + "nfn=RestaurarImagenBasica\rdsk=%s\rpar=%s\ridi=%s\rnci=%s\r" + "ipr=%s\rifs=%s\rrti=%s\rmet=%s\rmsy=%s\rtpt=%s\rwhl=%s\r" + "eli=%s\rcmp=%s\rbpi=%s\rcpc=%s\rbpc=%s\rnba=%s\r", + params->disk, params->partition, params->id, params->name, + params->repository, params->profile, params->sync_setup.path, + params->sync_setup.method, params->sync_setup.sync, params->type, + params->sync_setup.diff, params->sync_setup.remove, + params->sync_setup.compress, params->sync_setup.cleanup, + params->sync_setup.cache, params->sync_setup.cleanup_cache, + params->sync_setup.remove_dst); + + msg = og_msg_alloc(buf, len); + if (!msg) + return -1; + + og_send_cmd((char **)params->ips_array, params->ips_array_len, + CLIENTE_OCUPADO, msg); + + og_msg_free(msg); + + return 0; +} + +static int og_cmd_restore_incremental_image(json_t *element, struct og_msg_params *params) +{ + char buf[4096] = {}; + int err = 0, len; + const char *key; + json_t *value; + TRAMA *msg; + + if (json_typeof(element) != JSON_OBJECT) + return -1; + + json_object_foreach(element, key, value) { + if (!strcmp(key, "clients")) { + err = og_json_parse_clients(value, params); + } else if (!strcmp(key, "disk")) { + err = og_json_parse_string(value, ¶ms->disk); + params->flags |= OG_REST_PARAM_DISK; + } else if (!strcmp(key, "partition")) { + err = og_json_parse_string(value, ¶ms->partition); + params->flags |= OG_REST_PARAM_PARTITION; + } else if (!strcmp(key, "id")) { + err = og_json_parse_string(value, ¶ms->id); + params->flags |= OG_REST_PARAM_ID; + } else if (!strcmp(key, "name")) { + err = og_json_parse_string(value, ¶ms->name); + params->flags |= OG_REST_PARAM_NAME; + } else if (!strcmp(key, "repository")) { + err = og_json_parse_string(value, ¶ms->repository); + params->flags |= OG_REST_PARAM_REPO; + } else if (!strcmp(key, "profile")) { + err = og_json_parse_string(value, ¶ms->profile); + params->flags |= OG_REST_PARAM_PROFILE; + } else if (!strcmp(key, "type")) { + err = og_json_parse_string(value, ¶ms->type); + params->flags |= OG_REST_PARAM_TYPE; + } else if (!strcmp(key, "sync_params")) { + err = og_json_parse_sync_params(value, params); + } + + if (err < 0) + break; + } + + if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | + OG_REST_PARAM_DISK | + OG_REST_PARAM_PARTITION | + OG_REST_PARAM_ID | + OG_REST_PARAM_NAME | + OG_REST_PARAM_REPO | + OG_REST_PARAM_PROFILE | + OG_REST_PARAM_TYPE | + OG_REST_PARAM_SYNC_DIFF_ID | + OG_REST_PARAM_SYNC_DIFF_NAME | + OG_REST_PARAM_SYNC_PATH | + OG_REST_PARAM_SYNC_METHOD | + OG_REST_PARAM_SYNC_SYNC | + OG_REST_PARAM_SYNC_DIFF | + OG_REST_PARAM_SYNC_REMOVE | + OG_REST_PARAM_SYNC_COMPRESS | + OG_REST_PARAM_SYNC_CLEANUP | + OG_REST_PARAM_SYNC_CACHE | + OG_REST_PARAM_SYNC_CLEANUP_CACHE | + OG_REST_PARAM_SYNC_REMOVE_DST)) + return -1; + + len = snprintf(buf, sizeof(buf), + "nfn=RestaurarSoftIncremental\rdsk=%s\rpar=%s\ridi=%s\rnci=%s\r" + "ipr=%s\rifs=%s\ridf=%s\rncf=%s\rrti=%s\rmet=%s\rmsy=%s\r" + "tpt=%s\rwhl=%s\reli=%s\rcmp=%s\rbpi=%s\rcpc=%s\rbpc=%s\r" + "nba=%s\r", + params->disk, params->partition, params->id, params->name, + params->repository, params->profile, params->sync_setup.diff_id, + params->sync_setup.diff_name, params->sync_setup.path, + params->sync_setup.method, params->sync_setup.sync, params->type, + params->sync_setup.diff, params->sync_setup.remove, + params->sync_setup.compress, params->sync_setup.cleanup, + params->sync_setup.cache, params->sync_setup.cleanup_cache, + params->sync_setup.remove_dst); + + msg = og_msg_alloc(buf, len); + if (!msg) + return -1; + + og_send_cmd((char **)params->ips_array, params->ips_array_len, + CLIENTE_OCUPADO, msg); + + og_msg_free(msg); + + return 0; +} + +static LIST_HEAD(cmd_list); + +const struct og_cmd *og_cmd_find(const 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; +} + +void og_cmd_free(const struct og_cmd *cmd) +{ + struct og_msg_params *params = (struct og_msg_params *)&cmd->params; + int i; + + for (i = 0; i < params->ips_array_len; i++) { + free((void *)params->ips_array[i]); + free((void *)params->mac_array[i]); + } + free((void *)params->wol_type); + + if (cmd->json) + json_decref(cmd->json); + + free((void *)cmd->ip); + free((void *)cmd->mac); + free((void *)cmd); +} + +static void og_cmd_init(struct og_cmd *cmd, enum og_rest_method method, + enum og_cmd_type type, json_t *root) +{ + cmd->type = type; + cmd->method = method; + cmd->params.ips_array[0] = strdup(cmd->ip); + cmd->params.ips_array_len = 1; + cmd->json = root; +} + +static int og_cmd_legacy_wol(const char *input, struct og_cmd *cmd) +{ + char wol_type[2] = {}; + + if (sscanf(input, "mar=%s", wol_type) != 1) { + syslog(LOG_ERR, "malformed database legacy input\n"); + return -1; + } + + og_cmd_init(cmd, OG_METHOD_NO_HTTP, OG_CMD_WOL, NULL); + cmd->params.mac_array[0] = strdup(cmd->mac); + cmd->params.wol_type = strdup(wol_type); + + return 0; +} + +static int og_cmd_legacy_shell_run(const char *input, struct og_cmd *cmd) +{ + json_t *root, *script, *echo; + + script = json_string(input + 4); + echo = json_boolean(false); + + root = json_object(); + if (!root) + return -1; + json_object_set_new(root, "run", script); + json_object_set_new(root, "echo", echo); + + og_cmd_init(cmd, OG_METHOD_POST, OG_CMD_SHELL_RUN, root); + + return 0; +} + +static int og_cmd_legacy_session(const char *input, struct og_cmd *cmd) +{ + char part_str[OG_DB_SMALLINT_MAXLEN + 1]; + char disk_str[OG_DB_SMALLINT_MAXLEN + 1]; + json_t *root, *disk, *partition; + + if (sscanf(input, "dsk=%s\rpar=%s\r", disk_str, part_str) != 2) + return -1; + partition = json_string(part_str); + disk = json_string(disk_str); + + root = json_object(); + if (!root) + return -1; + json_object_set_new(root, "partition", partition); + json_object_set_new(root, "disk", disk); + + og_cmd_init(cmd, OG_METHOD_POST, OG_CMD_SESSION, root); + + return 0; +} + +static int og_cmd_legacy_poweroff(const char *input, struct og_cmd *cmd) +{ + og_cmd_init(cmd, OG_METHOD_POST, OG_CMD_POWEROFF, NULL); + + return 0; +} + +static int og_cmd_legacy_refresh(const char *input, struct og_cmd *cmd) +{ + og_cmd_init(cmd, OG_METHOD_GET, OG_CMD_REFRESH, NULL); + + return 0; +} + +static int og_cmd_legacy_reboot(const char *input, struct og_cmd *cmd) +{ + og_cmd_init(cmd, OG_METHOD_POST, OG_CMD_REBOOT, NULL); + + return 0; +} + +static int og_cmd_legacy_stop(const char *input, struct og_cmd *cmd) +{ + og_cmd_init(cmd, OG_METHOD_POST, OG_CMD_STOP, NULL); + + return 0; +} + +static int og_cmd_legacy_hardware(const char *input, struct og_cmd *cmd) +{ + og_cmd_init(cmd, OG_METHOD_GET, OG_CMD_HARDWARE, NULL); + + return 0; +} + +static int og_cmd_legacy_software(const char *input, struct og_cmd *cmd) +{ + og_cmd_init(cmd, OG_METHOD_GET, OG_CMD_SOFTWARE, NULL); + + return 0; +} + +static int og_cmd_legacy_image_create(const char *input, struct og_cmd *cmd) +{ + json_t *root, *disk, *partition, *code, *image_id, *name, *repo; + struct og_image_legacy img = {}; + + if (sscanf(input, "dsk=%s\rpar=%s\rcpt=%s\ridi=%s\rnci=%s\ripr=%s\r", + img.disk, img.part, img.code, img.image_id, img.name, + img.repo) != 6) + return -1; + image_id = json_string(img.image_id); + partition = json_string(img.part); + code = json_string(img.code); + name = json_string(img.name); + repo = json_string(img.repo); + disk = json_string(img.disk); + + root = json_object(); + if (!root) + return -1; + json_object_set_new(root, "partition", partition); + json_object_set_new(root, "repository", repo); + json_object_set_new(root, "id", image_id); + json_object_set_new(root, "code", code); + json_object_set_new(root, "name", name); + json_object_set_new(root, "disk", disk); + + og_cmd_init(cmd, OG_METHOD_POST, OG_CMD_IMAGE_CREATE, root); + + return 0; +} + +#define OG_DB_RESTORE_TYPE_MAXLEN 64 + +static int og_cmd_legacy_image_restore(const char *input, struct og_cmd *cmd) +{ + json_t *root, *disk, *partition, *image_id, *name, *repo; + char restore_type_str[OG_DB_RESTORE_TYPE_MAXLEN + 1] = {}; + char software_id_str[OG_DB_INT_MAXLEN + 1] = {}; + json_t *software_id, *restore_type; + struct og_image_legacy img = {}; + + if (sscanf(input, + "dsk=%s\rpar=%s\ridi=%s\rnci=%s\ripr=%s\rifs=%s\rptc=%s\r", + img.disk, img.part, img.image_id, img.name, img.repo, + software_id_str, restore_type_str) != 7) + return -1; + + restore_type = json_string(restore_type_str); + software_id = json_string(software_id_str); + image_id = json_string(img.image_id); + partition = json_string(img.part); + name = json_string(img.name); + repo = json_string(img.repo); + disk = json_string(img.disk); + + root = json_object(); + if (!root) + return -1; + json_object_set_new(root, "profile", software_id); + json_object_set_new(root, "partition", partition); + json_object_set_new(root, "type", restore_type); + json_object_set_new(root, "repository", repo); + json_object_set_new(root, "id", image_id); + json_object_set_new(root, "name", name); + json_object_set_new(root, "disk", disk); + + og_cmd_init(cmd, OG_METHOD_POST, OG_CMD_IMAGE_RESTORE, root); + + return 0; +} + +static int og_cmd_legacy_setup(const char *input, struct og_cmd *cmd) +{ + json_t *root, *disk, *cache, *cache_size, *partition_setup, *object; + struct og_legacy_partition part_cfg[OG_PARTITION_MAX] = {}; + char cache_size_str [OG_DB_INT_MAXLEN + 1]; + char disk_str [OG_DB_SMALLINT_MAXLEN + 1]; + json_t *part, *code, *fs, *size, *format; + unsigned int partition_len = 0; + const char *in_ptr; + char cache_str[2]; + + if (sscanf(input, "dsk=%s\rcfg=dis=%*[^*]*che=%[^*]*tch=%[^!]!", + disk_str, cache_str, cache_size_str) != 3) + return -1; + + in_ptr = strstr(input, "!") + 1; + while (strlen(in_ptr) > 0) { + if(sscanf(in_ptr, + "par=%[^*]*cpt=%[^*]*sfi=%[^*]*tam=%[^*]*ope=%[^%%]%%", + part_cfg[partition_len].partition, + part_cfg[partition_len].code, + part_cfg[partition_len].filesystem, + part_cfg[partition_len].size, + part_cfg[partition_len].format) != 5) + return -1; + in_ptr = strstr(in_ptr, "%") + 1; + partition_len++; + } + + root = json_object(); + if (!root) + return -1; + + cache_size = json_string(cache_size_str); + cache = json_string(cache_str); + partition_setup = json_array(); + disk = json_string(disk_str); + + for (unsigned int i = 0; i < partition_len; ++i) { + object = json_object(); + if (!object) { + json_decref(root); + return -1; + } + + part = json_string(part_cfg[i].partition); + fs = json_string(part_cfg[i].filesystem); + format = json_string(part_cfg[i].format); + code = json_string(part_cfg[i].code); + size = json_string(part_cfg[i].size); + + json_object_set_new(object, "partition", part); + json_object_set_new(object, "filesystem", fs); + json_object_set_new(object, "format", format); + json_object_set_new(object, "code", code); + json_object_set_new(object, "size", size); + + json_array_append_new(partition_setup, object); + } + + json_object_set_new(root, "partition_setup", partition_setup); + json_object_set_new(root, "cache_size", cache_size); + json_object_set_new(root, "cache", cache); + json_object_set_new(root, "disk", disk); + + og_cmd_init(cmd, OG_METHOD_POST, OG_CMD_SETUP, root); + + return 0; +} + +static int og_cmd_legacy_run_schedule(const char *input, struct og_cmd *cmd) +{ + og_cmd_init(cmd, OG_METHOD_GET, OG_CMD_RUN_SCHEDULE, NULL); + + return 0; +} + +static int og_cmd_legacy(const char *input, struct og_cmd *cmd) +{ + char legacy_cmd[32] = {}; + int err = -1; + + if (sscanf(input, "nfn=%31s\r", legacy_cmd) != 1) { + syslog(LOG_ERR, "malformed database legacy input\n"); + return -1; + } + input = strchr(input, '\r') + 1; + + if (!strcmp(legacy_cmd, "Arrancar")) { + err = og_cmd_legacy_wol(input, cmd); + } else if (!strcmp(legacy_cmd, "EjecutarScript")) { + err = og_cmd_legacy_shell_run(input, cmd); + } else if (!strcmp(legacy_cmd, "IniciarSesion")) { + err = og_cmd_legacy_session(input, cmd); + } else if (!strcmp(legacy_cmd, "Apagar")) { + err = og_cmd_legacy_poweroff(input, cmd); + } else if (!strcmp(legacy_cmd, "Actualizar")) { + err = og_cmd_legacy_refresh(input, cmd); + } else if (!strcmp(legacy_cmd, "Reiniciar")) { + err = og_cmd_legacy_reboot(input, cmd); + } else if (!strcmp(legacy_cmd, "Purgar")) { + err = og_cmd_legacy_stop(input, cmd); + } else if (!strcmp(legacy_cmd, "InventarioHardware")) { + err = og_cmd_legacy_hardware(input, cmd); + } else if (!strcmp(legacy_cmd, "InventarioSoftware")) { + err = og_cmd_legacy_software(input, cmd); + } else if (!strcmp(legacy_cmd, "CrearImagen")) { + err = og_cmd_legacy_image_create(input, cmd); + } else if (!strcmp(legacy_cmd, "RestaurarImagen")) { + err = og_cmd_legacy_image_restore(input, cmd); + } else if (!strcmp(legacy_cmd, "Configurar")) { + err = og_cmd_legacy_setup(input, cmd); + } else if (!strcmp(legacy_cmd, "EjecutaComandosPendientes") || + !strcmp(legacy_cmd, "Actualizar")) { + err = og_cmd_legacy_run_schedule(input, cmd); + } + + return err; +} + +static int og_dbi_add_action(const struct og_dbi *dbi, const struct og_task *task, + struct og_cmd *cmd) +{ + char start_date_string[24]; + struct tm *start_date; + const char *msglog; + dbi_result result; + time_t now; + + time(&now); + start_date = localtime(&now); + + sprintf(start_date_string, "%hu/%hhu/%hhu %hhu:%hhu:%hhu", + start_date->tm_year + 1900, start_date->tm_mon + 1, + start_date->tm_mday, start_date->tm_hour, start_date->tm_min, + start_date->tm_sec); + result = dbi_conn_queryf(dbi->conn, + "INSERT INTO acciones (idordenador, " + "tipoaccion, idtipoaccion, descriaccion, ip, " + "sesion, idcomando, parametros, fechahorareg, " + "estado, resultado, ambito, idambito, " + "restrambito, idprocedimiento, idcentro, " + "idprogramacion) " + "VALUES (%d, %d, %d, '%s', '%s', %d, %d, '%s', " + "'%s', %d, %d, %d, %d, '%s', %d, %d, %d)", + cmd->client_id, EJECUCION_TAREA, task->task_id, + "", cmd->ip, 0, task->command_id, + task->params, start_date_string, + ACCION_INICIADA, ACCION_SINRESULTADO, + task->type_scope, task->scope, "", + task->procedure_id, task->center_id, + task->schedule_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; + } + cmd->id = dbi_conn_sequence_last(dbi->conn, NULL); + dbi_result_free(result); + + 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->ip = strdup(dbi_result_get_string(result, "ip")); + cmd->mac = strdup(dbi_result_get_string(result, "mac")); + + og_cmd_legacy(task->params, cmd); + + if (task->procedure_id) { + if (og_dbi_add_action(dbi, task, cmd)) { + dbi_result_free(result); + return -1; + } + } else { + cmd->id = task->task_id; + } + + 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_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 ip,mac,idordenador " + "FROM ordenadores INNER JOIN aulas " + "WHERE ordenadores.idaula=aulas.idaula " + "AND aulas.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_clients(struct og_dbi *dbi, struct og_task *task) +{ + char query[4096]; + + switch (task->type_scope) { + case AMBITO_CENTROS: + sprintf(query, + "SELECT ip,mac,idordenador " + "FROM ordenadores INNER JOIN aulas " + "WHERE ordenadores.idaula=aulas.idaula " + "AND idcentro=%d", + task->scope); + return og_queue_task_command(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 ip,mac,idordenador FROM ordenadores " + "WHERE idaula=%d", + task->scope); + return og_queue_task_command(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; +} + +int og_dbi_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, idcomando " + "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_dbi_queue_procedure(dbi, task)) + return -1; + continue; + } + + task->params = strdup(dbi_result_get_string(result, "parametros")); + task->command_id = dbi_result_get_uint(result, "idcomando"); + if (og_queue_task_clients(dbi, task)) + return -1; + } + + dbi_result_free(result); + + return 0; +} + +static int og_dbi_queue_task(struct og_dbi *dbi, uint32_t task_id, + uint32_t schedule_id) +{ + struct og_task task = {}; + uint32_t task_id_next; + const char *msglog; + dbi_result result; + + task.schedule_id = schedule_id; + + result = dbi_conn_queryf(dbi->conn, + "SELECT tareas_acciones.orden, " + "tareas_acciones.idprocedimiento, " + "tareas_acciones.tareaid, " + "tareas.idtarea, " + "tareas.idcentro, " + "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, "tareaid"); + + if (task_id_next > 0) { + if (og_dbi_queue_task(dbi, task_id_next, schedule_id)) + return -1; + + continue; + } + task.task_id = dbi_result_get_uint(result, "idtarea"); + task.center_id = dbi_result_get_uint(result, "idcentro"); + 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_dbi_queue_procedure(dbi, &task); + } + + dbi_result_free(result); + + return 0; +} + +static int og_dbi_queue_command(struct og_dbi *dbi, uint32_t task_id, + uint32_t schedule_id) +{ + struct og_task task = {}; + const char *msglog; + dbi_result result; + char query[4096]; + + result = dbi_conn_queryf(dbi->conn, + "SELECT idaccion, idcentro, idordenador, parametros " + "FROM acciones " + "WHERE sesion = %u", 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.task_id = dbi_result_get_uint(result, "idaccion"); + task.center_id = dbi_result_get_uint(result, "idcentro"); + task.scope = dbi_result_get_uint(result, "idordenador"); + task.params = strdup(dbi_result_get_string(result, "parametros")); + + sprintf(query, + "SELECT ip, mac, idordenador FROM ordenadores " + "WHERE idordenador = %d", + task.scope); + if (og_queue_task_command(dbi, &task, query)) { + dbi_result_free(result); + return -1; + } + } + + dbi_result_free(result); + + return 0; +} + +int og_dbi_update_action(uint32_t id, bool success) +{ + char end_date_string[24]; + struct tm *end_date; + const char *msglog; + struct og_dbi *dbi; + uint8_t status = 2; + dbi_result result; + time_t now; + + if (!id) + return 0; + + dbi = og_dbi_open(&dbi_config); + if (!dbi) { + syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", + __func__, __LINE__); + return -1; + } + + time(&now); + end_date = localtime(&now); + + sprintf(end_date_string, "%hu/%hhu/%hhu %hhu:%hhu:%hhu", + end_date->tm_year + 1900, end_date->tm_mon + 1, + end_date->tm_mday, end_date->tm_hour, end_date->tm_min, + end_date->tm_sec); + result = dbi_conn_queryf(dbi->conn, + "UPDATE acciones SET fechahorafin='%s', " + "estado=%d, resultado=%d WHERE idaccion=%d", + end_date_string, ACCION_FINALIZADA, + status - success, id); + + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + og_dbi_close(dbi); + return -1; + } + dbi_result_free(result); + og_dbi_close(dbi); + + return 0; +} + +void og_schedule_run(unsigned int task_id, unsigned int schedule_id, + enum og_schedule_type type) +{ + struct og_msg_params params = {}; + bool duplicated = false; + struct og_cmd *cmd, *next; + 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; + } + + switch (type) { + case OG_SCHEDULE_TASK: + og_dbi_queue_task(dbi, task_id, schedule_id); + break; + case OG_SCHEDULE_PROCEDURE: + case OG_SCHEDULE_COMMAND: + og_dbi_queue_command(dbi, task_id, schedule_id); + break; + } + 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; + } + + list_for_each_entry_safe(cmd, next, &cmd_list, list) { + if (cmd->type != OG_CMD_WOL) + continue; + + if (Levanta((char **)cmd->params.ips_array, + (char **)cmd->params.mac_array, + cmd->params.ips_array_len, + (char *)cmd->params.wol_type)) + og_dbi_update_action(cmd->id, true); + + list_del(&cmd->list); + og_cmd_free(cmd); + } + + og_send_request(OG_METHOD_GET, OG_CMD_RUN_SCHEDULE, ¶ms, NULL); +} + +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_schedule_run(atoi(params->task_id), 0, OG_SCHEDULE_TASK); + og_dbi_close(dbi); + + list_for_each_entry(cmd, &cmd_list, list) + params->ips_array[params->ips_array_len++] = cmd->ip; + + return og_send_request(OG_METHOD_GET, OG_CMD_RUN_SCHEDULE, params, + NULL); +} + +static int og_dbi_scope_get_center(struct og_dbi *dbi, json_t *array) +{ + char center_name[OG_DB_CENTER_NAME_MAXLEN + 1] = {}; + const char *msglog; + uint32_t center_id; + dbi_result result; + json_t *center; + + result = dbi_conn_queryf(dbi->conn, + "SELECT nombrecentro, idcentro FROM centros"); + 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)) { + center_id = dbi_result_get_uint(result, "idcentro"); + strncpy(center_name, + dbi_result_get_string(result, "nombrecentro"), + OG_DB_CENTER_NAME_MAXLEN); + + center = json_object(); + if (!center) { + dbi_result_free(result); + return -1; + } + + json_object_set_new(center, "name", json_string(center_name)); + json_object_set_new(center, "type", json_string("center")); + json_object_set_new(center, "id", json_integer(center_id)); + json_object_set_new(center, "scope", json_array()); + json_array_append(array, center); + json_decref(center); + } + dbi_result_free(result); + + return 0; +} + +static int og_dbi_scope_get_room(struct og_dbi *dbi, json_t *array, + uint32_t center_id) +{ + char room_name[OG_DB_ROOM_NAME_MAXLEN + 1] = {}; + const char *msglog; + dbi_result result; + uint32_t room_id; + json_t *room; + + result = dbi_conn_queryf(dbi->conn, + "SELECT idaula, nombreaula FROM aulas WHERE " + "idcentro=%d", + center_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)) { + room_id = dbi_result_get_uint(result, "idaula"); + strncpy(room_name, + dbi_result_get_string(result, "nombreaula"), + OG_DB_CENTER_NAME_MAXLEN); + + room = json_object(); + if (!room) { + dbi_result_free(result); + return -1; + } + + json_object_set_new(room, "name", json_string(room_name)); + json_object_set_new(room, "type", json_string("room")); + json_object_set_new(room, "id", json_integer(room_id)); + json_object_set_new(room, "scope", json_array()); + json_array_append(array, room); + json_decref(room); + } + dbi_result_free(result); + + return 0; +} + +static int og_dbi_scope_get_computer(struct og_dbi *dbi, json_t *array, + uint32_t room_id) +{ + char computer_name[OG_DB_COMPUTER_NAME_MAXLEN + 1] = {}; + uint32_t computer_id; + const char *msglog; + dbi_result result; + json_t *computer; + + result = dbi_conn_queryf(dbi->conn, + "SELECT idordenador, nombreordenador, ip " + "FROM ordenadores WHERE idaula=%d", + room_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)) { + computer_id = dbi_result_get_uint(result, "idordenador"); + strncpy(computer_name, + dbi_result_get_string(result, "nombreordenador"), + OG_DB_CENTER_NAME_MAXLEN); + + computer = json_object(); + if (!computer) { + dbi_result_free(result); + return -1; + } + + json_object_set_new(computer, "name", json_string(computer_name)); + json_object_set_new(computer, "type", json_string("computer")); + json_object_set_new(computer, "id", json_integer(computer_id)); + json_object_set_new(computer, "scope", json_array()); + json_array_append(array, computer); + json_decref(computer); + } + dbi_result_free(result); + + return 0; +} + +static int og_cmd_scope_get(json_t *element, struct og_msg_params *params, + char *buffer_reply) +{ + json_t *root, *children_root, *children_center, *children_room, + *center_value, *room_value; + uint32_t center_id, room_id, index1, index2; + struct og_dbi *dbi; + + struct og_buffer og_buffer = { + .data = buffer_reply + }; + + root = json_object(); + children_root = json_array(); + if (!root || !children_root) + return -1; + + json_object_set(root, "scope", children_root); + + dbi = og_dbi_open(&dbi_config); + if (!dbi) { + syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", + __func__, __LINE__); + return -1; + } + + if (og_dbi_scope_get_center(dbi, children_root)) { + og_dbi_close(dbi); + return -1; + } + + json_array_foreach(children_root, index1, center_value) { + center_id = json_integer_value(json_object_get(center_value,"id")); + children_center = json_object_get(center_value, "scope"); + if (og_dbi_scope_get_room(dbi, children_center, center_id)) { + og_dbi_close(dbi); + return -1; + } + + json_array_foreach(children_center, index2, room_value) { + room_id = json_integer_value(json_object_get(room_value, "id")); + children_room = json_object_get(room_value, "scope"); + if (og_dbi_scope_get_computer(dbi, children_room, room_id)) { + og_dbi_close(dbi); + return -1; + } + } + } + + og_dbi_close(dbi); + + json_dump_callback(root, og_json_dump_clients, &og_buffer, 0); + json_decref(root); + + return 0; +} + +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); + og_dbi_close(dbi); + 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"); + time.on_start = true; + + og_schedule_create(schedule_id, task_id, OG_SCHEDULE_TASK, + &time); + } + + dbi_result_free(result); + og_dbi_close(dbi); + + return 0; +} + +static int og_dbi_schedule_create(struct og_dbi *dbi, + struct og_msg_params *params, + uint32_t *schedule_id, + enum og_schedule_type schedule_type) +{ + uint8_t suspended = 0; + uint32_t session = 0; + const char *msglog; + dbi_result result; + uint8_t type; + + switch (schedule_type) { + case OG_SCHEDULE_TASK: + type = 3; + break; + case OG_SCHEDULE_PROCEDURE: + type = 2; + break; + case OG_SCHEDULE_COMMAND: + session = atoi(params->task_id); + type = 1; + break; + } + + result = dbi_conn_queryf(dbi->conn, + "INSERT INTO programaciones (tipoaccion," + " identificador, nombrebloque, annos, meses," + " semanas, dias, diario, horas, ampm, minutos," + " suspendida, sesion) VALUES (%d, %s, '%s'," + " %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)", + type, params->task_id, params->name, + params->time.years, params->time.months, + params->time.weeks, params->time.week_days, + params->time.days, params->time.hours, + params->time.am_pm, params->time.minutes, + suspended, session); + 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; +} + +struct og_db_schedule { + uint32_t id; + uint32_t task_id; + const char *name; + struct og_schedule_time time; + uint32_t week_days; + uint32_t weeks; + uint32_t suspended; + uint32_t session; +}; + +static int og_dbi_schedule_get_json(struct og_dbi *dbi, json_t *root, + const char *task_id, const char *schedule_id) +{ + struct og_db_schedule schedule; + json_t *obj, *array; + const char *msglog; + dbi_result result; + int err = 0; + + if (task_id) { + result = dbi_conn_queryf(dbi->conn, + "SELECT idprogramacion," + " identificador, nombrebloque," + " annos, meses, diario, dias," + " semanas, horas, ampm," + " minutos,suspendida, sesion " + "FROM programaciones " + "WHERE identificador=%d", + atoi(task_id)); + } else if (schedule_id) { + result = dbi_conn_queryf(dbi->conn, + "SELECT idprogramacion," + " identificador, nombrebloque," + " annos, meses, diario, dias," + " semanas, horas, ampm," + " minutos,suspendida, sesion " + "FROM programaciones " + "WHERE idprogramacion=%d", + atoi(schedule_id)); + } else { + result = dbi_conn_queryf(dbi->conn, + "SELECT idprogramacion," + " identificador, nombrebloque," + " annos, meses, diario, dias," + " semanas, horas, ampm," + " minutos,suspendida, sesion " + "FROM programaciones"); + } + + if (!result) { + dbi_conn_error(dbi->conn, &msglog); + syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", + __func__, __LINE__, msglog); + return -1; + } + + array = json_array(); + if (!array) + return -1; + + while (dbi_result_next_row(result)) { + schedule.id = dbi_result_get_uint(result, "idprogramacion"); + schedule.task_id = dbi_result_get_uint(result, "identificador"); + schedule.name = dbi_result_get_string(result, "nombrebloque"); + schedule.time.years = dbi_result_get_uint(result, "annos"); + schedule.time.months = dbi_result_get_uint(result, "meses"); + schedule.time.days = dbi_result_get_uint(result, "diario"); + schedule.time.hours = dbi_result_get_uint(result, "horas"); + schedule.time.am_pm = dbi_result_get_uint(result, "ampm"); + schedule.time.minutes = dbi_result_get_uint(result, "minutos"); + schedule.week_days = dbi_result_get_uint(result, "dias"); + schedule.weeks = dbi_result_get_uint(result, "semanas"); + schedule.suspended = dbi_result_get_uint(result, "suspendida"); + schedule.session = dbi_result_get_uint(result, "sesion"); + + obj = json_object(); + if (!obj) { + err = -1; + break; + } + json_object_set_new(obj, "id", json_integer(schedule.id)); + json_object_set_new(obj, "task", json_integer(schedule.task_id)); + json_object_set_new(obj, "name", json_string(schedule.name)); + json_object_set_new(obj, "years", json_integer(schedule.time.years)); + json_object_set_new(obj, "months", json_integer(schedule.time.months)); + json_object_set_new(obj, "days", json_integer(schedule.time.days)); + json_object_set_new(obj, "hours", json_integer(schedule.time.hours)); + json_object_set_new(obj, "am_pm", json_integer(schedule.time.am_pm)); + json_object_set_new(obj, "minutes", json_integer(schedule.time.minutes)); + json_object_set_new(obj, "week_days", json_integer(schedule.week_days)); + json_object_set_new(obj, "weeks", json_integer(schedule.weeks)); + json_object_set_new(obj, "suspended", json_integer(schedule.suspended)); + json_object_set_new(obj, "session", json_integer(schedule.session)); + + json_array_append_new(array, obj); + } + + json_object_set_new(root, "schedule", array); + + dbi_result_free(result); + + return err; +} + +static int og_task_schedule_create(struct og_msg_params *params) +{ + enum og_schedule_type type; + uint32_t schedule_id; + struct og_dbi *dbi; + int err; + + if (!strcmp(params->type, "task")) + type = OG_SCHEDULE_TASK; + else if (!strcmp(params->type, "procedure")) + type = OG_SCHEDULE_PROCEDURE; + else if (!strcmp(params->type, "command")) + type = OG_SCHEDULE_COMMAND; + else + 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, type); + if (err < 0) { + og_dbi_close(dbi); + return -1; + } + og_schedule_create(schedule_id, atoi(params->task_id), type, + ¶ms->time); + og_schedule_refresh(og_loop); + og_dbi_close(dbi); + + return 0; +} + +static int og_cmd_schedule_create(json_t *element, struct og_msg_params *params) +{ + 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; + } else if (!strcmp(key, "name")) { + err = og_json_parse_string(value, ¶ms->name); + params->flags |= OG_REST_PARAM_NAME; + } else if (!strcmp(key, "when")) { + err = og_json_parse_time_params(value, params); + } else if (!strcmp(key, "type")) { + err = og_json_parse_string(value, ¶ms->type); + params->flags |= OG_REST_PARAM_TYPE; + } + + 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_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 | + OG_REST_PARAM_TYPE)) + return -1; + + return og_task_schedule_create(params); +} + +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, ¶ms->id); + params->flags |= OG_REST_PARAM_ID; + } else if (!strcmp(key, "task")) { + err = og_json_parse_string(value, ¶ms->task_id); + params->flags |= OG_REST_PARAM_TASK; + } else if (!strcmp(key, "name")) { + err = og_json_parse_string(value, ¶ms->name); + params->flags |= OG_REST_PARAM_NAME; + } else if (!strcmp(key, "when")) + 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), + ¶ms->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, ¶ms->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_cmd_schedule_get(json_t *element, struct og_msg_params *params, + char *buffer_reply) +{ + struct og_buffer og_buffer = { + .data = buffer_reply, + }; + json_t *schedule_root; + struct og_dbi *dbi; + const char *key; + json_t *value; + int err; + + if (element) { + 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); + } else if (!strcmp(key, "id")) { + err = og_json_parse_string(value, ¶ms->id); + } else { + return -1; + } + + if (err < 0) + break; + } + } + + dbi = og_dbi_open(&dbi_config); + if (!dbi) { + syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", + __func__, __LINE__); + return -1; + } + + schedule_root = json_object(); + if (!schedule_root) { + og_dbi_close(dbi); + return -1; + } + + err = og_dbi_schedule_get_json(dbi, schedule_root, + params->task_id, params->id); + og_dbi_close(dbi); + + if (err >= 0) + json_dump_callback(schedule_root, og_json_dump_clients, &og_buffer, 0); + + json_decref(schedule_root); + + 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 + * containing the correct methods. For example: "Allow: POST\r\n" + */ + char buf[] = "HTTP/1.1 405 Method Not Allowed\r\n" + "Content-Length: 0\r\n\r\n"; + + send(og_client_socket(cli), buf, strlen(buf), 0); + + return -1; +} + +static int og_client_bad_request(struct og_client *cli) +{ + char buf[] = "HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n"; + + send(og_client_socket(cli), buf, strlen(buf), 0); + + return -1; +} + +static int og_client_not_found(struct og_client *cli) +{ + char buf[] = "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n"; + + send(og_client_socket(cli), buf, strlen(buf), 0); + + return -1; +} + +static int og_client_not_authorized(struct og_client *cli) +{ + char buf[] = "HTTP/1.1 401 Unauthorized\r\n" + "WWW-Authenticate: Basic\r\n" + "Content-Length: 0\r\n\r\n"; + + send(og_client_socket(cli), buf, strlen(buf), 0); + + return -1; +} + +static int og_server_internal_error(struct og_client *cli) +{ + char buf[] = "HTTP/1.1 500 Internal Server Error\r\n" + "Content-Length: 0\r\n\r\n"; + + send(og_client_socket(cli), buf, strlen(buf), 0); + + return -1; +} + +#define OG_MSG_RESPONSE_MAXLEN 65536 + +static int og_client_ok(struct og_client *cli, char *buf_reply) +{ + char buf[OG_MSG_RESPONSE_MAXLEN] = {}; + int err = 0, len; + + len = snprintf(buf, sizeof(buf), + "HTTP/1.1 200 OK\r\nContent-Length: %ld\r\n\r\n%s", + strlen(buf_reply), buf_reply); + if (len >= (int)sizeof(buf)) + err = og_server_internal_error(cli); + + send(og_client_socket(cli), buf, strlen(buf), 0); + + return err; +} + +int og_client_state_process_payload_rest(struct og_client *cli) +{ + char buf_reply[OG_MSG_RESPONSE_MAXLEN] = {}; + struct og_msg_params params = {}; + enum og_rest_method method; + const char *cmd, *body; + json_error_t json_err; + json_t *root = NULL; + int err = 0; + + syslog(LOG_DEBUG, "%s:%hu %.32s ...\n", + inet_ntoa(cli->addr.sin_addr), + ntohs(cli->addr.sin_port), cli->buf); + + if (!strncmp(cli->buf, "GET", strlen("GET"))) { + method = OG_METHOD_GET; + cmd = cli->buf + strlen("GET") + 2; + } else if (!strncmp(cli->buf, "POST", strlen("POST"))) { + method = OG_METHOD_POST; + cmd = cli->buf + strlen("POST") + 2; + } else + return og_client_method_not_found(cli); + + body = strstr(cli->buf, "\r\n\r\n") + 4; + + if (strcmp(cli->auth_token, auth_token)) { + syslog(LOG_ERR, "wrong Authentication key\n"); + return og_client_not_authorized(cli); + } + + if (cli->content_length) { + root = json_loads(body, 0, &json_err); + if (!root) { + syslog(LOG_ERR, "malformed json line %d: %s\n", + json_err.line, json_err.text); + return og_client_not_found(cli); + } + } + + if (!strncmp(cmd, "clients", strlen("clients"))) { + if (method != OG_METHOD_POST && + method != OG_METHOD_GET) + return og_client_method_not_found(cli); + + if (method == OG_METHOD_POST && !root) { + syslog(LOG_ERR, "command clients with no payload\n"); + return og_client_bad_request(cli); + } + switch (method) { + case OG_METHOD_POST: + err = og_cmd_post_clients(root, ¶ms); + break; + case OG_METHOD_GET: + err = og_cmd_get_clients(root, ¶ms, buf_reply); + break; + default: + return og_client_bad_request(cli); + } + } else if (!strncmp(cmd, "wol", strlen("wol"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command wol with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_wol(root, ¶ms); + } else if (!strncmp(cmd, "shell/run", strlen("shell/run"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command run with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_run_post(root, ¶ms); + } else if (!strncmp(cmd, "shell/output", strlen("shell/output"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command output with no payload\n"); + return og_client_bad_request(cli); + } + + err = og_cmd_run_get(root, ¶ms, buf_reply); + } else if (!strncmp(cmd, "session", strlen("session"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command session with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_session(root, ¶ms); + } else if (!strncmp(cmd, "scopes", strlen("scopes"))) { + if (method != OG_METHOD_GET) + return og_client_method_not_found(cli); + + err = og_cmd_scope_get(root, ¶ms, buf_reply); + } else if (!strncmp(cmd, "poweroff", strlen("poweroff"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command poweroff with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_poweroff(root, ¶ms); + } else if (!strncmp(cmd, "reboot", strlen("reboot"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command reboot with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_reboot(root, ¶ms); + } else if (!strncmp(cmd, "stop", strlen("stop"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command stop with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_stop(root, ¶ms); + } else if (!strncmp(cmd, "refresh", strlen("refresh"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command refresh with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_refresh(root, ¶ms); + } else if (!strncmp(cmd, "hardware", strlen("hardware"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command hardware with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_hardware(root, ¶ms); + } else if (!strncmp(cmd, "software", strlen("software"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command software with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_software(root, ¶ms); + } else if (!strncmp(cmd, "image/create/basic", + strlen("image/create/basic"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command create with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_create_basic_image(root, ¶ms); + } else if (!strncmp(cmd, "image/create/incremental", + strlen("image/create/incremental"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command create with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_create_incremental_image(root, ¶ms); + } else if (!strncmp(cmd, "image/create", strlen("image/create"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command create with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_create_image(root, ¶ms); + } else if (!strncmp(cmd, "image/restore/basic", + strlen("image/restore/basic"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command create with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_restore_basic_image(root, ¶ms); + } else if (!strncmp(cmd, "image/restore/incremental", + strlen("image/restore/incremental"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command create with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_restore_incremental_image(root, ¶ms); + } else if (!strncmp(cmd, "image/restore", strlen("image/restore"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command create with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_restore_image(root, ¶ms); + } else if (!strncmp(cmd, "setup", strlen("setup"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command create with no payload\n"); + return og_client_bad_request(cli); + } + err = og_cmd_setup(root, ¶ms); + } else if (!strncmp(cmd, "run/schedule", strlen("run/schedule"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + if (!root) { + syslog(LOG_ERR, "command create with no payload\n"); + return og_client_bad_request(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 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, ¶ms); + } 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, ¶ms); + } 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, ¶ms); + } else if (!strncmp(cmd, "schedule/get", + strlen("schedule/get"))) { + if (method != OG_METHOD_POST) + return og_client_method_not_found(cli); + + err = og_cmd_schedule_get(root, ¶ms, buf_reply); + } else { + syslog(LOG_ERR, "unknown command: %.32s ...\n", cmd); + err = og_client_not_found(cli); + } + + if (root) + json_decref(root); + + if (err < 0) + return og_client_bad_request(cli); + + err = og_client_ok(cli, buf_reply); + if (err < 0) { + syslog(LOG_ERR, "HTTP response to %s:%hu is too large\n", + inet_ntoa(cli->addr.sin_addr), + ntohs(cli->addr.sin_port)); + } + + return err; +} diff --git a/src/rest.h b/src/rest.h new file mode 100644 index 0000000..4f2347f --- /dev/null +++ b/src/rest.h @@ -0,0 +1,97 @@ +#ifndef OG_REST_H +#define OG_REST_H + +#include <ev.h> + +extern struct ev_loop *og_loop; + +enum og_client_state { + OG_CLIENT_RECEIVING_HEADER = 0, + OG_CLIENT_RECEIVING_PAYLOAD, + OG_CLIENT_PROCESSING_REQUEST, +}; + +enum og_client_status { + OG_CLIENT_STATUS_OGLIVE, + OG_CLIENT_STATUS_BUSY, + OG_CLIENT_STATUS_VIRTUAL, +}; + +enum og_cmd_type { + OG_CMD_UNSPEC, + OG_CMD_WOL, + OG_CMD_PROBE, + OG_CMD_SHELL_RUN, + OG_CMD_SESSION, + OG_CMD_POWEROFF, + OG_CMD_REFRESH, + OG_CMD_REBOOT, + OG_CMD_STOP, + OG_CMD_HARDWARE, + OG_CMD_SOFTWARE, + OG_CMD_IMAGE_CREATE, + OG_CMD_IMAGE_RESTORE, + OG_CMD_SETUP, + OG_CMD_RUN_SCHEDULE, + OG_CMD_MAX +}; + +#define OG_MSG_REQUEST_MAXLEN 65536 + +struct og_client { + struct list_head list; + struct ev_io io; + struct ev_timer timer; + struct sockaddr_in addr; + enum og_client_state state; + char buf[OG_MSG_REQUEST_MAXLEN]; + unsigned int buf_len; + unsigned int msg_len; + int keepalive_idx; + bool rest; + bool agent; + int content_length; + char auth_token[64]; + enum og_client_status status; + enum og_cmd_type last_cmd; + unsigned int last_cmd_id; + bool autorun; +}; + +void og_client_add(struct og_client *cli); + +static inline int og_client_socket(const struct og_client *cli) +{ + return cli->io.fd; +} + +#include "json.h" + +int og_client_state_process_payload_rest(struct og_client *cli); + +enum og_rest_method { + OG_METHOD_GET = 0, + OG_METHOD_POST, + OG_METHOD_NO_HTTP +}; + +int og_send_request(enum og_rest_method method, enum og_cmd_type type, + const struct og_msg_params *params, + const json_t *data); + +struct og_cmd { + uint32_t id; + struct list_head list; + uint32_t client_id; + const char *ip; + const char *mac; + enum og_cmd_type type; + enum og_rest_method method; + struct og_msg_params params; + json_t *json; +}; + +const struct og_cmd *og_cmd_find(const char *client_ip); +void og_cmd_free(const struct og_cmd *cmd); + +#endif diff --git a/src/schedule.c b/src/schedule.c new file mode 100644 index 0000000..6dc54e0 --- /dev/null +++ b/src/schedule.c @@ -0,0 +1,478 @@ +/* + * Copyright (C) 2020 Soleta Networks <info@soleta.eu> + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the + * Free Software Foundation, version 3. + */ + +#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; + + 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; + + + /* Last day of this week? */ + if (tm->tm_wday == 1) + 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; + } +} + +static bool og_schedule_stale(time_t seconds) +{ + time_t now; + + now = time(NULL); + if (seconds < now) + return true; + + return false; +} + +static void og_schedule_create_weekdays(int month, int year, + int *hours, int minutes, int week_days, + uint32_t task_id, uint32_t schedule_id, + enum og_schedule_type type, + bool on_start) +{ + struct og_schedule *schedule; + int month_days[5]; + int n_month_days; + time_t seconds; + uint32_t wday; + struct tm tm; + int k, l; + + for (wday = 0; wday < 7; wday++) { + if (!((1 << wday) & week_days)) + continue; + + memset(&tm, 0, sizeof(tm)); + tm.tm_mon = month; + tm.tm_year = year; + + n_month_days = 0; + memset(month_days, 0, sizeof(month_days)); + get_days_from_weekday(&tm, wday, month_days, &n_month_days); + + for (k = 0; month_days[k] != 0 && k < n_month_days; k++) { + for (l = 0; hours[l] != 0 && l < 31; l++) { + memset(&tm, 0, sizeof(tm)); + tm.tm_year = year; + tm.tm_mon = month; + tm.tm_mday = month_days[k]; + tm.tm_hour = hours[l] - 1; + tm.tm_min = minutes; + seconds = mktime(&tm); + + if (on_start && og_schedule_stale(seconds)) + continue; + + schedule = (struct og_schedule *) + calloc(1, sizeof(struct og_schedule)); + if (!schedule) + return; + + schedule->seconds = seconds; + schedule->task_id = task_id; + schedule->schedule_id = schedule_id; + schedule->type = type; + og_schedule_add(schedule); + } + } + } +} + +static void og_schedule_create_weeks(int month, int year, + int *hours, int minutes, int weeks, + uint32_t task_id, uint32_t schedule_id, + enum og_schedule_type type, bool on_start) +{ + struct og_schedule *schedule; + int month_days[7]; + int n_month_days; + time_t seconds; + struct tm tm; + int week; + int k, l; + + for (week = 0; week < 5; week++) { + if (!((1 << week) & weeks)) + continue; + + memset(&tm, 0, sizeof(tm)); + tm.tm_mon = month; + tm.tm_year = year; + + n_month_days = 0; + memset(month_days, 0, sizeof(month_days)); + if (week == 5) + get_last_week(&tm, month_days, &n_month_days); + else + get_days_from_week(&tm, week, month_days, &n_month_days); + + for (k = 0; month_days[k] != 0 && k < n_month_days; k++) { + for (l = 0; hours[l] != 0 && l < 31; l++) { + memset(&tm, 0, sizeof(tm)); + tm.tm_year = year; + tm.tm_mon = month; + tm.tm_mday = month_days[k]; + tm.tm_hour = hours[l] - 1; + tm.tm_min = minutes; + seconds = mktime(&tm); + + if (on_start && og_schedule_stale(seconds)) + continue; + + schedule = (struct og_schedule *) + calloc(1, sizeof(struct og_schedule)); + if (!schedule) + return; + + schedule->seconds = seconds; + schedule->task_id = task_id; + schedule->schedule_id = schedule_id; + schedule->type = type; + og_schedule_add(schedule); + } + } + } +} + +static void og_schedule_create_days(int month, int year, + int *hours, int minutes, int *days, + uint32_t task_id, uint32_t schedule_id, + enum og_schedule_type type, bool on_start) +{ + struct og_schedule *schedule; + time_t seconds; + struct tm tm; + int k, l; + + for (k = 0; days[k] != 0 && k < 31; k++) { + for (l = 0; hours[l] != 0 && l < 31; l++) { + + memset(&tm, 0, sizeof(tm)); + tm.tm_year = year; + tm.tm_mon = month; + tm.tm_mday = days[k]; + tm.tm_hour = hours[l] - 1; + tm.tm_min = minutes; + seconds = mktime(&tm); + + if (on_start && og_schedule_stale(seconds)) + continue; + + schedule = (struct og_schedule *) + calloc(1, sizeof(struct og_schedule)); + if (!schedule) + return; + + schedule->seconds = seconds; + schedule->task_id = task_id; + schedule->schedule_id = schedule_id; + schedule->type = type; + og_schedule_add(schedule); + } + } +} + +void og_schedule_create(unsigned int schedule_id, unsigned int task_id, + enum og_schedule_type type, + struct og_schedule_time *time) +{ + int year, month, minutes; + int months[12] = {}; + int years[12] = {}; + int hours[12] = {}; + int days[31] = {}; + int i, j; + + 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++) { + month = months[j] - 1; + year = years[i]; + + if (time->week_days) + og_schedule_create_weekdays(month, year, + hours, minutes, + time->week_days, + task_id, + schedule_id, + type, + time->on_start); + + if (time->weeks) + og_schedule_create_weeks(month, year, + hours, minutes, + time->weeks, + task_id, + schedule_id, + type, time->on_start); + + if (time->days) + og_schedule_create_days(month, year, + hours, minutes, + days, + task_id, + schedule_id, + type, time->on_start); + } + } + + 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, OG_SCHEDULE_TASK, 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_schedule_run(current->task_id, current->schedule_id, current->type); + + ev_timer_stop(loop, timer); + list_del(¤t->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, ¤t_schedule->timer); + + og_schedule_next(loop); +} diff --git a/src/schedule.h b/src/schedule.h new file mode 100644 index 0000000..14b8998 --- /dev/null +++ b/src/schedule.h @@ -0,0 +1,65 @@ +#ifndef _OG_SCHEDULE_H_ +#define _OG_SCHEDULE_H_ + +#include <stdint.h> +#include <stdbool.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; + bool on_start; +}; + +enum og_schedule_type { + OG_SCHEDULE_TASK, + OG_SCHEDULE_PROCEDURE, + OG_SCHEDULE_COMMAND, +}; + +struct og_schedule { + struct list_head list; + struct ev_timer timer; + time_t seconds; + unsigned int task_id; + unsigned int schedule_id; + enum og_schedule_type type; +}; + +void og_schedule_create(unsigned int schedule_id, unsigned int task_id, + enum og_schedule_type type, + 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_schedule_run(unsigned int task_id, unsigned int schedule_id, + enum og_schedule_type type); + +int og_dbi_schedule_get(void); +int og_dbi_update_action(uint32_t id, bool success); + +struct og_task { + uint32_t task_id; + uint32_t procedure_id; + uint32_t command_id; + uint32_t center_id; + uint32_t schedule_id; + uint32_t type_scope; + uint32_t scope; + const char *filtered_scope; + const char *params; +}; + +int og_dbi_queue_procedure(struct og_dbi *dbi, struct og_task *task); + +#endif diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..433a0dc --- /dev/null +++ b/src/utils.c @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2020 Soleta Networks <info@soleta.eu> + * + * This program is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the + * Free Software Foundation, version 3. + */ + +#include <ctype.h> +#include "utils.h" + +const char *str_toupper(char *str) +{ + char *c = str; + + while (*c) { + *c = toupper(*c); + c++; + } + + return str; +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..e32d006 --- /dev/null +++ b/src/utils.h @@ -0,0 +1,6 @@ +#ifndef _OG_UTILS_H +#define _OG_UTILS_H + +const char *str_toupper(char *str); + +#endif |