/* * Copyright (C) 2020-2021 Soleta Networks * * 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; either version 3 of the License, or * (at your option) any later version. */ #include "ogAdmServer.h" #include "dbi.h" #include "utils.h" #include "list.h" #include "rest.h" #include "core.h" #include "wol.h" #include "cfg.h" #include "repo.h" #include "boot.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct ev_loop *og_loop; #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_INLINE (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) #define OG_REST_PARAM_NETMASK (1UL << 40) #define OG_REST_PARAM_SCOPE (1UL << 41) #define OG_REST_PARAM_MODE (1UL << 42) #define OG_REST_PARAM_CENTER (1UL << 43) #define OG_REST_PARAM_BACKUP (1UL << 44) #define OG_REST_PARAM_ROOM (1UL << 45) #define OG_REST_PARAM_GATEWAY (1UL << 46) #define OG_REST_PARAM_FOLDER (1UL << 47) enum og_http_error { OG_HTTP_000_UNSPEC = 0, OG_HTTP_400_BAD_REQUEST, /* malformed request */ OG_HTTP_405_METHOD_NOT_ALLOWED, /* unsupported method */ OG_HTTP_404_NOT_FOUND, /* not found */ OG_HTTP_409_CONFLICT, /* already exists */ OG_HTTP_423_LOCKED, /* in use */ OG_HTTP_500_INTERNAL_SERVER_ERROR, /* request has failed */ OG_HTTP_501_SERVICE_UNAVAILABLE, /* cannot reach database */ OG_HTTP_507_INSUFFICIENT_STORAGE, /* no disk space in server */ }; static LIST_HEAD(client_list); static LIST_HEAD(client_wol_list); void og_client_add(struct og_client *cli) { list_add(&cli->list, &client_list); } struct og_client *__og_client_find(const struct in_addr *addr) { struct og_client *client; list_for_each_entry(client, &client_list, list) { if (!client->agent) continue; if (client->addr.sin_addr.s_addr == addr->s_addr) return client; } return NULL; } static struct og_client *og_client_find(const char *ip) { struct in_addr addr; int res; res = inet_aton(ip, &addr); if (!res) { syslog(LOG_ERR, "Invalid IP string: %s\n", ip); return NULL; } return __og_client_find(&addr); } static const char *og_client_status(const struct og_client *cli) { switch (cli->last_cmd.type) { case OG_CMD_UNSPEC: break; default: 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"; case OG_CLIENT_STATUS_LINUX: return "LNX"; case OG_CLIENT_STATUS_LINUX_SESSION: return "LNXS"; case OG_CLIENT_STATUS_WIN: return "WIN"; case OG_CLIENT_STATUS_WIN_SESSION: return "WINS"; 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 bool og_flags_validate(const uint64_t flags, const uint64_t required_flags) { return (flags & required_flags) == required_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++) { if (params->ips_array_len >= OG_CLIENTS_MAX) return -1; 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); } if (params->ips_array_len == 0) return -1; params->flags |= OG_REST_PARAM_ADDR; return 0; } 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 const char *og_cmd_to_uri[OG_CMD_MAX] = { [OG_CMD_WOL] = "wol", [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_UPDATE] = "image/update", [OG_CMD_IMAGE_RESTORE] = "image/restore", [OG_CMD_SETUP] = "setup", [OG_CMD_IMAGES] = "images", [OG_CMD_CACHE_DELETE] = "cache/delete", [OG_CMD_CACHE_FETCH] = "cache/fetch", }; 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.type != 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 struct og_cmd_ctx *ctx) { 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; bool has_seq; 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 | JSON_ENSURE_ASCII); uri = og_cmd_to_uri[type]; switch (type) { case OG_CMD_POWEROFF: case OG_CMD_REBOOT: case OG_CMD_STOP: has_seq = false; break; default: has_seq = true; break; } 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 (++cli->seq == 0) cli->seq++; snprintf(buf, OG_MSG_REQUEST_MAXLEN, "%s /%s HTTP/1.1\r\nContent-Length: %d\r\nX-Sequence: %u\r\n%s\r\n\r\n%s", method_str, uri, content_length, has_seq ? cli->seq : 0, content_type, content); if (send(client_sd, buf, strlen(buf), 0) < 0) continue; cli->last_cmd.type = type; if (ctx) cli->last_cmd.ctx = *ctx; } json_decref((json_t *)data); return 0; } struct og_buffer { char *data; int len; }; #define OG_MSG_RESPONSE_MAXLEN 262144 static int og_json_dump_clients(const char *buffer, size_t size, void *data) { struct og_buffer *og_buffer = (struct og_buffer *)data; if (size >= OG_MSG_RESPONSE_MAXLEN - og_buffer->len) { syslog(LOG_ERR, "Response JSON body is too large\n"); return -1; } memcpy(og_buffer->data + og_buffer->len, buffer, size); og_buffer->len += size; return 0; } static const char *og_cmd_result_str_array[] = { [OG_UNKNOWN] = "unknown", [OG_FAILURE] = "failure", [OG_SUCCESS] = "success", }; static const char *og_cmd_result_str(const enum og_cmd_result result) { if (result > OG_SUCCESS) return "unknown"; return og_cmd_result_str_array[result]; } static json_t *og_json_client_cmd_result(const enum og_cmd_result result) { const char *result_str; json_t *last_cmd; last_cmd = json_object(); result_str = og_cmd_result_str(result); json_object_set_new(last_cmd, "result", json_string(result_str)); return last_cmd; } static int og_json_client_append(json_t *array, struct og_client *client) { json_t *addr, *state, *last_cmd, *object; object = json_object(); if (!object) return -1; addr = json_string(inet_ntoa(client->addr.sin_addr)); if (!addr) { json_decref(object); return -1; } json_object_set_new(object, "addr", addr); state = json_string(og_client_status(client)); if (!state) { json_decref(object); return -1; } json_object_set_new(object, "state", state); json_object_set_new(object, "speed", json_integer(client->speed)); last_cmd = og_json_client_cmd_result(client->last_cmd.result); json_object_set_new(object, "last_cmd", last_cmd); json_array_append_new(array, object); return 0; } static int og_json_client_wol_append(json_t *array, struct og_client_wol *cli_wol) { json_t *addr, *state, *last_cmd, *object; object = json_object(); if (!object) return -1; addr = json_string(inet_ntoa(cli_wol->addr)); if (!addr) { json_decref(object); return -1; } json_object_set_new(object, "addr", addr); state = json_string(og_client_wol_status(cli_wol)); if (!state) { json_decref(object); return -1; } json_object_set_new(object, "state", state); last_cmd = og_json_client_cmd_result(OG_UNKNOWN); json_object_set_new(object, "last_cmd", last_cmd); json_array_append_new(array, object); return 0; } struct og_rest_ctx { json_t *json; struct og_msg_params params; char buf_reply[OG_MSG_RESPONSE_MAXLEN]; uint32_t http_error; }; static int og_cmd_get_clients(struct og_rest_ctx *ctx) { struct og_buffer og_buffer = { .data = ctx->buf_reply, }; struct og_client_wol *cli_wol; struct og_client *client; json_t *array, *root; array = json_array(); if (!array) return -1; list_for_each_entry(cli_wol, &client_wol_list, list) { if (og_json_client_wol_append(array, cli_wol) < 0) { json_decref(array); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } } list_for_each_entry(client, &client_list, list) { if (!client->agent) continue; if (og_json_client_append(array, client) < 0) { json_decref(array); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } } root = json_pack("{s:o}", "clients", array); if (!root) { json_decref(array); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(root); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } json_decref(root); 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; } struct og_client_wol *og_client_wol_find(const struct in_addr *addr) { struct og_client_wol *cli_wol; list_for_each_entry(cli_wol, &client_wol_list, list) { if (cli_wol->addr.s_addr == addr->s_addr) return cli_wol; } return NULL; } static int og_cmd_wol(struct og_rest_ctx *ctx) { char ips_str[(OG_DB_IP_MAXLEN + 1) * OG_CLIENTS_MAX + 1] = {}; struct og_msg_params *params = &ctx->params; struct og_msg_params new_params = {}; struct og_client_wol *cli_wol; struct in_addr addr, netmask; int ips_str_len = 0; const char *msglog; struct og_dbi *dbi; int err = 0, i = 0; uint64_t wol_type; dbi_result result; const char *key; json_t *value; int sd; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "clients")) { err = og_json_parse_clients(value, params); } else if (!strcmp(key, "type")) { err = og_json_parse_type(value, params); } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | OG_REST_PARAM_WOL_TYPE)) return -1; for (i = 0; i < params->ips_array_len; ++i) { ips_str_len += snprintf(ips_str + ips_str_len, sizeof(ips_str) - ips_str_len, "'%s',", params->ips_array[i]); } ips_str[ips_str_len - 1] = '\0'; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT ordenadores.ip, ordenadores.mac, " "aulas.netmask " "FROM ordenadores " "INNER JOIN aulas " "ON ordenadores.idaula = aulas.idaula " "WHERE ordenadores.ip IN (%s)", ips_str); 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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } for (i = 0; dbi_result_next_row(result); i++) { if (i >= OG_CLIENTS_MAX) { syslog(LOG_ERR, "too many IPs in WoL (%s:%d)\n", __func__, __LINE__); dbi_result_free(result); og_dbi_close(dbi); goto err_free_params; } new_params.ips_array[i] = dbi_result_get_string_copy(result, "ip"); new_params.mac_array[i] = dbi_result_get_string_copy(result, "mac"); new_params.netmask_array[i] = dbi_result_get_string_copy(result, "netmask"); } new_params.ips_array_len = i; dbi_result_free(result); og_dbi_close(dbi); if (i == 0) return 0; sd = wol_socket_open(); if (sd < 0) { syslog(LOG_ERR, "cannot open wol socket (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; goto err_free_params; } for (i = 0; i < new_params.ips_array_len; i++) { if (og_client_find(new_params.ips_array[i])) continue; if (!inet_aton(new_params.ips_array[i], &addr)) continue; cli_wol = og_client_wol_find(&addr); if (cli_wol) { og_client_wol_refresh(cli_wol); continue; } cli_wol = og_client_wol_create(&addr); if (!cli_wol) { ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; goto err_out; } list_add_tail(&cli_wol->list, &client_wol_list); if (!inet_aton(new_params.netmask_array[i], &netmask)) continue; if (safe_strtoull(params->wol_type, &wol_type, 10, UINT32_MAX) < 0) { syslog(LOG_ERR, "failed to parse wol type %s (%s:%d)\n", params->wol_type, __func__, __LINE__); continue; } if (wake_up(sd, &addr, &netmask, new_params.mac_array[i], wol_type) < 0) { syslog(LOG_ERR, "Failed to send wol packet to %s\n", new_params.ips_array[i]); continue; } } err_out: close(sd); err_free_params: for (i = 0; i < new_params.ips_array_len; ++i) { free((void *)new_params.ips_array[i]); free((void *)new_params.mac_array[i]); free((void *)new_params.netmask_array[i]); } 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(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; json_t *value, *clients; const char *key; bool inline_cmd; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, 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, "inline")) { err = og_json_parse_bool(value, &inline_cmd); params->flags |= OG_REST_PARAM_INLINE; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | OG_REST_PARAM_RUN_CMD | OG_REST_PARAM_INLINE)) return -1; clients = json_copy(ctx->json); json_object_del(clients, "clients"); return og_send_request(OG_METHOD_POST, OG_CMD_SHELL_RUN, params, clients, NULL); } static int og_cmd_run_get(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; struct og_buffer og_buffer = { .data = ctx->buf_reply, }; json_t *root, *value, *array; const char *key; unsigned int i; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, 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, *cmd; struct og_client *cli; cli = og_client_find(params->ips_array[i]); if (!cli || !cli->shell.tstamp) continue; 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); cmd = json_string(cli->shell.cmd); if (!cmd) { json_decref(object); json_decref(array); return -1; } json_object_set_new(object, "cmd", cmd); output = json_string(cli->shell.output); if (!output) { json_decref(object); json_decref(array); return -1; } json_object_set_new(object, "output", output); json_object_set_new(object, "retcode", json_integer(cli->shell.retcode)); json_object_set_new(object, "tstamp", json_integer(cli->shell.tstamp)); json_array_append_new(array, object); } root = json_pack("{s:o}", "clients", array); if (!root) return -1; if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(root); return -1; } json_decref(root); return 0; } #define OG_SCRIPT_PATH "/opt/opengnsys/client/shell" struct script_entry { char name[FILENAME_MAX]; struct list_head list; }; static void og_script_list_free(struct list_head *script_list) { struct script_entry *script, *tmp; list_for_each_entry_safe(script, tmp, script_list, list) { list_del(&script->list); free(script); } } static int og_get_client_scripts(struct list_head *script_list) { struct script_entry *script; struct dirent *dent; struct stat st; DIR *d; if (stat(OG_SCRIPT_PATH, &st) < 0 && errno == ENOENT) mkdir(OG_SCRIPT_PATH, 0755); d = opendir(OG_SCRIPT_PATH); if (!d) { syslog(LOG_ERR, "Cannot open directory %s (%s:%d)\n", OG_SCRIPT_PATH, __func__, __LINE__); return -1; } dent = readdir(d); while (dent) { if (dent->d_type != DT_REG) { dent = readdir(d); continue; } script = calloc(1, sizeof(struct script_entry)); if (!script) { closedir(d); og_script_list_free(script_list); return -1; } snprintf(script->name, FILENAME_MAX, "%s", dent->d_name); list_add_tail(&script->list, script_list); dent = readdir(d); } closedir(d); return 0; } static int og_cmd_shell_list(struct og_rest_ctx *ctx) { struct og_buffer og_buffer = { .data = ctx->buf_reply }; struct script_entry *entry; json_t *root, *script_arr; LIST_HEAD(script_list); if (og_get_client_scripts(&script_list) < 0) return -1; root = json_object(); if (!root) { og_script_list_free(&script_list); return -1; } script_arr = json_array(); if (!script_arr) { og_script_list_free(&script_list); json_decref(root); return -1; } list_for_each_entry(entry, &script_list, list) json_array_append_new(script_arr, json_string(entry->name)); json_object_set_new(root, "scripts", script_arr); if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { og_script_list_free(&script_list); json_decref(root); return -1; } og_script_list_free(&script_list); json_decref(root); return 0; } static int og_cmd_session(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; json_t *clients, *value; const char *key; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, 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(ctx->json); json_object_del(clients, "clients"); return og_send_request(OG_METHOD_POST, OG_CMD_SESSION, params, clients, NULL); } static int og_json_os_array_get(struct og_dbi *dbi, json_t *array, const char *ip) { unsigned int disk, partition; const char *os_name; const char *msglog; dbi_result result; json_t *item; result = dbi_conn_queryf(dbi->conn, "SELECT op.numdisk, op.numpar, nom.nombreso " "FROM ordenadores o " "INNER JOIN ordenadores_particiones op " " ON o.idordenador = op.idordenador " "INNER JOIN nombresos nom " " ON op.idnombreso = nom.idnombreso " "WHERE o.ip = '%s'", ip); 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)) { item = json_object(); if (!item) { dbi_result_free(result); return -1; } disk = dbi_result_get_uint(result, "numdisk"); partition = dbi_result_get_uint(result, "numpar"); os_name = dbi_result_get_string(result, "nombreso"); json_object_set_new(item, "disk", json_integer(disk)); json_object_set_new(item, "partition", json_integer(partition)); json_object_set_new(item, "name", json_string(os_name)); json_array_append_new(array, item); } dbi_result_free(result); return 0; } static int og_cmd_get_session(struct og_rest_ctx *ctx) { json_t *value, *root, *array, *client_data, *partitions; struct og_msg_params *params = &ctx->params; struct og_dbi *dbi; const char *key; int err = 0, i; struct og_buffer og_buffer = { .data = ctx->buf_reply }; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "client")) /* alias for backward compatibility */ err = og_json_parse_clients(value, params); else if (!strcmp(key, "clients")) err = og_json_parse_clients(value, params); else err = -1; if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } array = json_array(); if (!array) { og_dbi_close(dbi); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } for (i = 0; i < params->ips_array_len; i++) { client_data = json_object(); if (!client_data) { json_decref(array); og_dbi_close(dbi); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } json_object_set_new(client_data, "addr", json_string(params->ips_array[i])); partitions = json_array(); if (!partitions) { json_decref(client_data); json_decref(array); og_dbi_close(dbi); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (og_json_os_array_get(dbi, partitions, params->ips_array[i]) < 0) { json_decref(partitions); json_decref(client_data); json_decref(array); og_dbi_close(dbi); ctx->http_error = OG_HTTP_404_NOT_FOUND; return -1; } json_object_set_new(client_data, "partitions", partitions); json_array_append_new(array, client_data); } og_dbi_close(dbi); root = json_object(); if (!root){ json_decref(array); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } json_object_set_new(root, "sessions", array); if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(root); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } json_decref(root); return 0; } static int og_cmd_poweroff(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *key; json_t *value; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, 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; return og_send_request(OG_METHOD_POST, OG_CMD_POWEROFF, params, NULL, NULL); } static int og_cmd_refresh(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *key; json_t *value; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, 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; return og_send_request(OG_METHOD_GET, OG_CMD_REFRESH, params, NULL, NULL); } static int og_cmd_reboot(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *key; json_t *value; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, 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; return og_send_request(OG_METHOD_POST, OG_CMD_REBOOT, params, NULL, NULL); } #define OG_TFTP_TMPL_PATH_UEFI "/opt/opengnsys/tftpboot/grub/templates" #define OG_TFTP_TMPL_PATH "/opt/opengnsys/tftpboot/menu.lst/templates" struct og_boot_mode { struct list_head list; char name[FILENAME_MAX]; }; static void og_boot_mode_free(struct list_head *boot_mode_list) { struct og_boot_mode *mode, *tmp; list_for_each_entry_safe(mode, tmp, boot_mode_list, list) { list_del(&mode->list); free(mode); } } static int og_get_boot_modes(struct list_head *boot_mode_list) { struct og_boot_mode *mode; struct dirent *dent; DIR *d; d = opendir(OG_TFTP_TMPL_PATH_UEFI); if (!d) { syslog(LOG_ERR, "Cannot open directory %s (%s:%d)\n", OG_TFTP_TMPL_PATH_UEFI, __func__, __LINE__); return -1; } dent = readdir(d); while (dent) { if (dent->d_type != DT_REG) { dent = readdir(d); continue; } mode = calloc(1, sizeof(struct og_boot_mode)); if (!mode) { closedir(d); og_boot_mode_free(boot_mode_list); return -1; } snprintf(mode->name, FILENAME_MAX, "%s", dent->d_name); list_add_tail(&mode->list, boot_mode_list); dent = readdir(d); } closedir(d); /* ogrelive built-in grub2 template. */ mode = calloc(1, sizeof(struct og_boot_mode)); if (!mode) { og_boot_mode_free(boot_mode_list); return -1; } snprintf(mode->name, FILENAME_MAX, "ogrelive"); list_add_tail(&mode->list, boot_mode_list); return 0; } static int og_cmd_get_modes(struct og_rest_ctx *ctx) { struct og_buffer og_buffer = { .data = ctx->buf_reply }; struct og_boot_mode *mode; LIST_HEAD(boot_mode_list); json_t *root, *modes; int ret; root = json_object(); if (!root) { ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } modes = json_array(); if (!modes) { json_decref(root); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (og_get_boot_modes(&boot_mode_list) < 0) { json_decref(modes); json_decref(root); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } list_for_each_entry(mode, &boot_mode_list, list) json_array_append_new(modes, json_string(mode->name)); og_boot_mode_free(&boot_mode_list); json_object_set_new(root, "modes", modes); ret = json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII); json_decref(root); return ret; } static int og_change_db_mode(struct og_dbi *dbi, const char *mac, const char * mode) { const char *msglog; dbi_result result; result = dbi_conn_queryf(dbi->conn, "UPDATE ordenadores SET arranque='%s' " "WHERE mac='%s'", mode, mac); 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 bool og_boot_mode_is_valid(const char *name) { struct og_boot_mode *mode; LIST_HEAD(boot_mode_list); bool found = false; if (og_get_boot_modes(&boot_mode_list) < 0) { syslog(LOG_ERR, "failed to get boot mode list (%s:%d)\n", __FILE__, __LINE__); return false; } list_for_each_entry(mode, &boot_mode_list, list) { if (!strncmp(name, mode->name, FILENAME_MAX)) { found = true; break; } } og_boot_mode_free(&boot_mode_list); return found; } enum { OG_TFTP_BOOT_BIOS, OG_TFTP_BOOT_UEFI, PXE_BOOT_TYPE_MAX, }; const char *pxe_boot_type_to_dir[] = { [OG_TFTP_BOOT_BIOS] = "/opt/opengnsys/tftpboot/menu.lst", [OG_TFTP_BOOT_UEFI] = "/opt/opengnsys/tftpboot/grub" }; static const char *pxe_boot_type(const int boot_type) { if (boot_type < 0 || boot_type >= PXE_BOOT_TYPE_MAX) return NULL; return pxe_boot_type_to_dir[boot_type]; } static int get_grub_filename(unsigned int boot_type, const char *mac, char *pxe_filename, size_t pxe_filename_size) { const char *fmt; switch (boot_type) { case OG_TFTP_BOOT_BIOS: fmt = "01-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c"; break; case OG_TFTP_BOOT_UEFI: fmt = "01-%c%c:%c%c:%c%c:%c%c:%c%c:%c%c"; break; default: syslog(LOG_ERR, "unknown boot type %d", boot_type); return -1; } snprintf(pxe_filename, pxe_filename_size, fmt, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], mac[6], mac[7], mac[8], mac[9], mac[10], mac[11]); switch (boot_type) { case OG_TFTP_BOOT_BIOS: str_toupper(pxe_filename); break; case OG_TFTP_BOOT_UEFI: str_tolower(pxe_filename); break; default: syslog(LOG_ERR, "unknown boot type %d", boot_type); return -1; } return 0; } static int get_grub_file_path(unsigned int boot_type, const char *mac, char *pxe_path, size_t pxe_path_size) { char pxe_filename[PATH_MAX + 1]; const char *pxe_dir; int ret; pxe_dir = pxe_boot_type(boot_type); if (!pxe_dir) { syslog(LOG_ERR, "Invalid boot type parameter (%s:%d)\n", __func__, __LINE__); return -1; } ret = get_grub_filename(boot_type, mac, pxe_filename, sizeof(pxe_filename)); if (ret < 0) return -1; ret = snprintf(pxe_path, pxe_path_size, "%s/%s", pxe_dir, pxe_filename); if (ret >= pxe_path_size) { syslog(LOG_ERR, "failed to generate pxe file path (%s:%d)\n", __func__, __LINE__); return -1; } return 0; } static int og_remove_tftpboot_file(unsigned int boot_type, const char *mac) { char pxe_file_path[PATH_MAX + 1]; int ret; ret = get_grub_file_path(boot_type, mac, pxe_file_path, sizeof(pxe_file_path)); if (ret < 0) return -1; if (unlink(pxe_file_path) < 0) { syslog(LOG_ERR, "failed to delete file (%s:%d) %s\n", __func__, __LINE__, pxe_file_path); return -1; } return 0; } static int og_remove_tftpboot_files(struct og_dbi *dbi, const char *ip) { const char *msglog, *old_mac; dbi_result result; result = dbi_conn_queryf(dbi->conn, "SELECT mac FROM ordenadores WHERE ip='%s'", ip); 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)) { old_mac = dbi_result_get_string(result, "mac"); og_remove_tftpboot_file(OG_TFTP_BOOT_BIOS, old_mac); og_remove_tftpboot_file(OG_TFTP_BOOT_UEFI, old_mac); } dbi_result_free(result); return 0; } static int og_create_boot_file(unsigned int boot_type, const char *mac, const char *mode, char *params) { char tmp_filename[] = "/tmp/mode_params_XXXXXX"; char pxe_template_path[PATH_MAX + 1]; char pxe_file_path[PATH_MAX + 1]; char cmd_params[16384] = {}; const char *pxe_dir; int status; pid_t pid; int ret; int fd; pxe_dir = pxe_boot_type(boot_type); if (!pxe_dir) { syslog(LOG_ERR, "Invalid boot type parameter (%s:%d)\n", __func__, __LINE__); return -1; } snprintf(pxe_template_path, sizeof(pxe_template_path), "%s/templates/%s", pxe_dir, mode); ret = get_grub_file_path(boot_type, mac, pxe_file_path, sizeof(pxe_file_path)); if (ret < 0) return -1; if (unlink(pxe_file_path) < 0) syslog(LOG_INFO, "failed to delete file %s (%s:%d)\n", pxe_file_path, __func__, __LINE__); snprintf(cmd_params, sizeof(cmd_params), "DATA='%s'\nTEMPLATE_PATH='%s'\nPXEFILE_PATH='%s'", params, pxe_template_path, pxe_file_path); fd = mkstemp(tmp_filename); if (fd < 0) { syslog(LOG_ERR, "cannot generate temp file %s: %s (%s:%d)\n", tmp_filename, strerror(errno), __func__, __LINE__); return -1; } ret = write(fd, cmd_params, strlen(cmd_params) + 1); close(fd); if (ret < 0) { syslog(LOG_ERR, "cannot write file %s: %s (%s:%d)\n", tmp_filename, strerror(errno), __func__, __LINE__); unlink(tmp_filename); return -1; } pid = fork(); if (pid < 0) { syslog(LOG_ERR, "fork failed (%s:%d) %s\n", __func__, __LINE__, strerror(errno)); unlink(tmp_filename); return -1; } if (pid == 0) { execlp("/bin/bash", "/bin/bash", "/opt/opengnsys/bin/updategrubprofile", tmp_filename, NULL); syslog(LOG_ERR, "failed script execution (%s:%d) %s\n", __func__, __LINE__, strerror(errno)); _exit(-1); } else if (waitpid(pid, &status, 0) < 0) { syslog(LOG_ERR, "waitpid failed (%s:%d) %s\n", __func__, __LINE__, strerror(errno)); unlink(tmp_filename); return -1; } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { syslog(LOG_ERR, "failed script execution %s (%s:%d)\n", strerror(errno), __func__, __LINE__); unlink(tmp_filename); return -1; } unlink(tmp_filename); return 0; } struct og_boot_mode_params { const char *server_ip; const char *repo_ip; const char *ntp; const char *dns; const char *proxy; const char *ip; const char *nombreaula; const char *oglivedir; const char *hardprofile; const char *router; const char *netmask; const char *nombreordenador; const char *netiface; const char *directorio; const char *resolucion; uint32_t repoid; int is_prof; int has_unit; }; static void og_boot_params_free(struct og_boot_mode_params *boot_params) { free((void *)boot_params->server_ip); free((void *)boot_params->repo_ip); free((void *)boot_params->ip); free((void *)boot_params->router); free((void *)boot_params->netmask); free((void *)boot_params->nombreordenador); free((void *)boot_params->netiface); free((void *)boot_params->nombreaula); free((void *)boot_params->oglivedir); free((void *)boot_params->hardprofile); free((void *)boot_params->ntp); free((void *)boot_params->dns); free((void *)boot_params->proxy); free((void *)boot_params->directorio); free((void *)boot_params->resolucion); } static int og_boot_params_find(struct og_dbi *dbi, struct og_boot_mode_params *boot_params, const char *mac) { char repository_ip[OG_DB_IP_MAXLEN + 1] = {}; char server_ip[OG_DB_IP_MAXLEN + 1] = {}; const char *msglog; dbi_result result; result = dbi_conn_queryf(dbi->conn, "SELECT " "ordenadores.ip AS ip, " "aulas.router AS router, " "aulas.netmask AS netmask, " "ordenadores.nombreordenador AS nombreordenador, " "ordenadores.netiface AS netiface, " "REPLACE(TRIM(aulas.nombreaula), ' ', '_') AS nombreaula, " "repositorios.idrepositorio AS repoid, " "ordenadores.oglivedir AS oglivedir, " "IF(ordenadores.idordenador = aulas.idordprofesor, 1, 0) AS is_prof, " "REPLACE(TRIM(IFNULL(perfileshard.descripcion, '')), ' ', '_') AS hardprofile, " "IFNULL(aulas.ntp, '') AS ntp, " "IFNULL(aulas.dns, '') AS dns, " "IFNULL(aulas.proxy, '') AS proxy, " "IF(entidades.ogunit = 1 AND NOT centros.directorio = '', 1, 0) AS has_unit, " "centros.directorio AS directorio, " "IFNULL(menus.resolucion, '') AS resolucion " "FROM ordenadores " "JOIN aulas USING(idaula) " "JOIN centros USING(idcentro) " "JOIN entidades USING(identidad) " "LEFT JOIN repositorios USING(idrepositorio) " "LEFT JOIN perfileshard USING(idperfilhard) " "LEFT JOIN menus USING(idmenu) " "WHERE ordenadores.mac = '%s'", mac); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } if (!dbi_result_next_row(result)) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", // TODO __func__, __LINE__, msglog); dbi_result_free(result); return -1; } boot_params->ip = dbi_result_get_string_copy(result, "ip"); boot_params->router = dbi_result_get_string_copy(result, "router"); boot_params->netmask = dbi_result_get_string_copy(result, "netmask"); boot_params->nombreordenador = dbi_result_get_string_copy(result, "nombreordenador"); boot_params->netiface = dbi_result_get_string_copy(result, "netiface"); boot_params->nombreaula = dbi_result_get_string_copy(result, "nombreaula"); boot_params->oglivedir = dbi_result_get_string_copy(result, "oglivedir"); boot_params->hardprofile = dbi_result_get_string_copy(result, "hardprofile"); boot_params->ntp = dbi_result_get_string_copy(result, "ntp"); boot_params->dns = dbi_result_get_string_copy(result, "dns"); boot_params->proxy = dbi_result_get_string_copy(result, "proxy"); boot_params->directorio = dbi_result_get_string_copy(result, "directorio"); boot_params->resolucion = dbi_result_get_string_copy(result, "resolucion"); boot_params->repoid = dbi_result_get_uint(result, "repoid"); boot_params->has_unit = dbi_result_get_uint(result, "has_unit"); boot_params->is_prof = dbi_result_get_uint(result, "is_prof"); dbi_result_free(result); if (og_dbi_get_server_ip(dbi, boot_params->ip, server_ip) < 0) goto err_out; boot_params->server_ip = strdup(server_ip); if (og_dbi_get_repository_ip(dbi, boot_params->repoid, boot_params->ip, repository_ip) < 0) goto err_out; boot_params->repo_ip = strdup(repository_ip); return 0; err_out: og_boot_params_free(boot_params); return -1; } static int og_get_client_mode_params(struct og_dbi *dbi, const char *mac, char *params, size_t params_size) { struct og_boot_mode_params boot_params = {}; const char *res_prefix = " vga="; const char *res_value = "788"; char *lang = getenv("LANG"); int err = 0; if (og_boot_params_find(dbi, &boot_params, mac) < 0) return -1; if (boot_params.resolucion[0]) { res_value = boot_params.resolucion; if (strchr(boot_params.resolucion, ':')) res_prefix = " video="; } if (!strncmp(boot_params.oglivedir, "ogReLive", strlen("ogReLive"))) snprintf(params, params_size, " ip=%s:%s:%s:%s:%s:%s:none ogrepo=%s oglivedir=%s fetch=http://%s/%s/filesystem.squashfs", boot_params.ip, boot_params.server_ip, boot_params.router, boot_params.netmask, boot_params.nombreordenador, boot_params.netiface, boot_params.repo_ip, boot_params.oglivedir, boot_params.repo_ip, boot_params.oglivedir); else snprintf(params, params_size, " LANG=%s ip=%s:%s:%s:%s:%s:%s:none group=%s ogrepo=%s oglive=%s oglog=%s ogshare=%s oglivedir=%s ogprof=%s server=%s" "%s%s%s%s%s%s%s%s%s%s%s%s", lang, boot_params.ip, boot_params.server_ip, boot_params.router, boot_params.netmask, boot_params.nombreordenador, boot_params.netiface, boot_params.nombreaula, boot_params.repo_ip, boot_params.server_ip, boot_params.server_ip, boot_params.server_ip, boot_params.oglivedir, boot_params.is_prof ? "true" : "false", boot_params.server_ip, boot_params.hardprofile[0] ? " hardprofile=" : "", boot_params.hardprofile[0] ? boot_params.hardprofile: "", boot_params.ntp[0] ? " ogntp=" : "", boot_params.ntp[0] ? boot_params.ntp : "", boot_params.dns[0] ? " ogdns=" : "", boot_params.dns[0] ? boot_params.dns : "", boot_params.proxy[0] ? " ogproxy=" : "", boot_params.proxy[0] ? boot_params.proxy : "", boot_params.has_unit ? " ogunit=" : "", boot_params.has_unit ? boot_params.directorio : "", res_prefix, res_value); og_boot_params_free(&boot_params); return err; } static int og_set_client_mode(struct og_dbi *dbi, const char *client_ip, const char *mac, const char *mode) { char params[4096]; if (!og_boot_mode_is_valid(mode)) { syslog(LOG_ERR, "invalid boot mode in client (%s:%d)\n", __FILE__, __LINE__); return -1; } if (!strcmp(mode, "ogrelive")) { struct og_boot_mode_params boot_params = {}; struct og_boot_cfg boot_cfg = {}; if (og_boot_params_find(dbi, &boot_params, mac) < 0) { syslog(LOG_ERR, "failed to get boot parameters (%s:%d)\n", __FILE__, __LINE__); return -1; } boot_cfg.ogserver = boot_params.server_ip; boot_cfg.ogrepo = boot_params.repo_ip; boot_cfg.ogrelivedir = boot_params.oglivedir; boot_cfg.username = "opengnsys"; boot_cfg.passwd = ogconfig.db.pass; if (ogrelive_generate_grub2_file(&boot_cfg, mac) < 0) { syslog(LOG_ERR, "failed to create HTTP boot file (%s:%d)\n", __FILE__, __LINE__); og_boot_params_free(&boot_params); return -1; } og_boot_params_free(&boot_params); } else { if (og_get_client_mode_params(dbi, mac, params, sizeof(params)) < 0) { syslog(LOG_ERR, "failed to get boot parameters (%s:%d)\n", __FILE__, __LINE__); return -1; } if (og_create_boot_file(OG_TFTP_BOOT_BIOS, mac, mode, params) < 0) { syslog(LOG_ERR, "failed to create BIOS boot file (%s:%d)\n", __FILE__, __LINE__); return -1; } if (og_create_boot_file(OG_TFTP_BOOT_UEFI, mac, mode, params) < 0) { og_remove_tftpboot_file(OG_TFTP_BOOT_BIOS, mac); syslog(LOG_ERR, "failed to create UEFI boot file (%s:%d)\n", __FILE__, __LINE__); return -1; } } if (og_change_db_mode(dbi, mac, mode) < 0) { syslog(LOG_ERR, "failed to change db mode (%s:%d)\n", __func__, __LINE__); return -1; } return 0; } static int og_cmd_post_modes(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *mode_str, *mac; const char *msglog; struct og_dbi *dbi; uint64_t flags = 0; dbi_result result; const char *key; json_t *value; int err = 0; int i; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "clients")) { err = og_json_parse_clients(value, params); } else if (!strcmp(key, "mode")) { err = og_json_parse_string(value, &mode_str); flags |= OG_REST_PARAM_MODE; } else { err = -1; } if (err < 0) return err; } if (!og_flags_validate(flags, OG_REST_PARAM_MODE) || !og_msg_params_validate(params, OG_REST_PARAM_ADDR)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } for (i = 0; i < params->ips_array_len; i++) { result = dbi_conn_queryf(dbi->conn, "SELECT mac FROM ordenadores " "WHERE ip = '%s'", params->ips_array[i]); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (!dbi_result_next_row(result)) { syslog(LOG_ERR, "could not find client IP: %s (%s:%d)\n", params->ips_array[i], __func__, __LINE__); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_404_NOT_FOUND; return -1; } mac = dbi_result_get_string(result, "mac"); err = og_set_client_mode(dbi, params->ips_array[i], mac, mode_str); if (err < 0) { syslog(LOG_ERR, "cannot set boot mode for client IP: %s (%s:%d)\n", params->ips_array[i], __func__, __LINE__); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_507_INSUFFICIENT_STORAGE; return -1; } dbi_result_free(result); } og_dbi_close(dbi); return 0; } static int og_cmd_get_client_setup(struct og_rest_ctx *ctx) { json_t *value, *root, *partitions_array, *partition_json; struct og_msg_params *params = &ctx->params; const char *key, *msglog; unsigned int len_part; struct og_dbi *dbi; dbi_result result; int err = 0; struct og_buffer og_buffer = { .data = ctx->buf_reply }; struct { int disk; int number; int code; uint64_t size; int filesystem; int format; int os; uint64_t used_size; uint64_t free_size; int image; int software; } partition; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "client")) { err = og_json_parse_clients(value, params); } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) return -1; root = json_object(); if (!root) { ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } partitions_array = json_array(); if (!partitions_array) { json_decref(root); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } json_object_set_new(root, "partitions", partitions_array); dbi = og_dbi_open(&ogconfig.db); if (!dbi) { json_decref(root); syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT numdisk, numpar, codpar, tamano, " " idsistemafichero, idnombreso, " " idimagen, idperfilsoft, used_size, free_size " "FROM ordenadores_particiones " "INNER JOIN ordenadores " "ON ordenadores.idordenador = ordenadores_particiones.idordenador " "WHERE ordenadores.ip='%s'", params->ips_array[0]); if (!result) { json_decref(root); dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } len_part = 0; /* partition 0 represents the full disk, hence OG_PARTITION_MAX + 1. */ while (dbi_result_next_row(result) && len_part < OG_PARTITION_MAX + 1) { partition.disk = dbi_result_get_int(result, "numdisk"); partition.number = dbi_result_get_int(result, "numpar"); partition.code = dbi_result_get_int(result, "codpar"); partition.size = dbi_result_get_longlong(result, "tamano"); partition.filesystem = dbi_result_get_int(result, "idsistemafichero"); partition.os = dbi_result_get_int(result, "idnombreso"); partition.image = dbi_result_get_int(result, "idimagen"); partition.software = dbi_result_get_int(result, "idperfilsoft"); partition.used_size = dbi_result_get_longlong(result, "used_size"); partition.free_size = dbi_result_get_longlong(result, "free_size"); partition_json = json_object(); if (!partition_json) { json_decref(root); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } json_object_set_new(partition_json, "disk", json_integer(partition.disk)); json_object_set_new(partition_json, "partition", json_integer(partition.number)); json_object_set_new(partition_json, "code", json_integer(partition.code)); json_object_set_new(partition_json, "size", json_integer(partition.size)); json_object_set_new(partition_json, "used_size", json_integer(partition.used_size)); json_object_set_new(partition_json, "free_size", json_integer(partition.free_size)); json_object_set_new(partition_json, "filesystem", json_integer(partition.filesystem)); json_object_set_new(partition_json, "os", json_integer(partition.os)); json_object_set_new(partition_json, "image", json_integer(partition.image)); json_object_set_new(partition_json, "software", json_integer(partition.software)); json_array_append_new(partitions_array, partition_json); ++len_part; } dbi_result_free(result); og_dbi_close(dbi); if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(root); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } json_decref(root); return 0; } static int og_dbi_update_client_repo(struct og_dbi *dbi, const char *mac, uint32_t repo_id) { const char *msglog; dbi_result result; result = dbi_conn_queryf(dbi->conn, "UPDATE ordenadores SET idrepositorio=%u " "WHERE mac='%s'", repo_id, mac); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to update client's server (%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } dbi_result_free(result); return 0; } static int og_cmd_post_client_repo(struct og_rest_ctx *ctx) { char ips_str[(OG_DB_IP_MAXLEN + 1) * OG_CLIENTS_MAX + 1] = {}; struct og_msg_params *params = &ctx->params; const char *key, *msglog, *mac; int ips_str_len = 0; struct og_dbi *dbi; dbi_result result; uint32_t repo_id; int err = 0, i; json_t *value; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "clients")) { err = og_json_parse_clients(value, params); } else if (!strcmp(key, "id")) { err = og_json_parse_uint(value, &repo_id); params->flags |= OG_REST_PARAM_ID; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | OG_REST_PARAM_ID)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } for (i = 0; i < params->ips_array_len; ++i) { ips_str_len += snprintf(ips_str + ips_str_len, sizeof(ips_str) - ips_str_len, "'%s',", params->ips_array[i]); } ips_str[ips_str_len - 1] = '\0'; result = dbi_conn_queryf(dbi->conn, "SELECT mac FROM ordenadores " "WHERE ip IN (%s)", ips_str); 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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } while (dbi_result_next_row(result)) { mac = dbi_result_get_string(result, "mac"); err = og_dbi_update_client_repo(dbi, mac, repo_id); if (err != 0) { dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_404_NOT_FOUND; return -1; } } dbi_result_free(result); og_dbi_close(dbi); return 0; } static int og_cmd_get_center_info(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; struct og_center center = {}; json_t *value, *root; struct og_dbi *dbi; const char *key; int err = 0; struct og_buffer og_buffer = { .data = ctx->buf_reply }; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "id")) { err = og_json_parse_uint(value, ¢er.id); params->flags |= OG_REST_PARAM_CENTER; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_CENTER)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } if (og_dbi_get_center_info(dbi, ¢er)) { og_dbi_close(dbi); ctx->http_error = OG_HTTP_404_NOT_FOUND; return -1; } og_dbi_close(dbi); root = json_object(); if (!root) return -1; json_object_set_new(root, "comment", json_string(center.comment)); json_object_set_new(root, "id", json_integer(center.id)); json_object_set_new(root, "name", json_string(center.name)); if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(root); return -1; } json_decref(root); return 0; } static int og_cmd_get_client_info(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; struct og_computer computer = {}; json_t *value, *root; struct in_addr addr; struct og_dbi *dbi; const char *key; int err = 0; struct og_buffer og_buffer = { .data = ctx->buf_reply }; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "client")) { err = og_json_parse_clients(value, params); } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) return -1; if (!inet_aton(params->ips_array[0], &addr)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } if (og_dbi_get_computer_info(dbi, &computer, addr)) { og_dbi_close(dbi); ctx->http_error = OG_HTTP_404_NOT_FOUND; return -1; } og_dbi_close(dbi); root = json_object(); if (!root) { ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } json_object_set_new(root, "serial_number", json_string(computer.serial_number)); json_object_set_new(root, "hardware_id", json_integer(computer.hardware_id)); json_object_set_new(root, "netdriver", json_string(computer.netdriver)); json_object_set_new(root, "maintenance", json_boolean(computer.maintenance)); json_object_set_new(root, "netiface", json_string(computer.netiface)); json_object_set_new(root, "repo_id", json_integer(computer.repo_id)); json_object_set_new(root, "livedir", json_string(computer.livedir)); json_object_set_new(root, "netmask", json_string(computer.netmask)); json_object_set_new(root, "center", json_integer(computer.center)); json_object_set_new(root, "remote", json_boolean(computer.remote)); json_object_set_new(root, "room", json_integer(computer.room)); json_object_set_new(root, "name", json_string(computer.name)); json_object_set_new(root, "boot", json_string(computer.boot)); json_object_set_new(root, "mac", json_string(computer.mac)); json_object_set_new(root, "id", json_integer(computer.id)); json_object_set_new(root, "ip", json_string(computer.ip)); if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(root); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } json_decref(root); return 0; } static int og_cmd_post_center_update(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *key, *msglog; uint32_t center_id; struct og_dbi *dbi; dbi_result result; json_t *value; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "name")) { err = og_json_parse_string(value, ¶ms->name); params->flags |= OG_REST_PARAM_NAME; } else if (!strcmp(key, "comment")) { err = og_json_parse_string(value, ¶ms->comment); } else if (!strcmp(key, "id")) { err = og_json_parse_uint(value, ¢er_id); params->flags |= OG_REST_PARAM_CENTER; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_CENTER | OG_REST_PARAM_NAME)) return -1; if (!params->comment) params->comment = ""; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT idcentro FROM centros WHERE idcentro='%u'", center_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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (dbi_result_get_numrows(result) == 0) { syslog(LOG_ERR, "could not find a center with that id: %u\n", center_id); dbi_result_free(result); og_dbi_close(dbi); return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "SELECT idcentro FROM centros " "WHERE nombrecentro='%s' and idcentro<>'%u'", params->name, center_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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (dbi_result_next_row(result)) { center_id = dbi_result_get_uint(result, "idcentro"); syslog(LOG_ERR, "the center with id %u already has the name: %s\n", center_id, params->name); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_409_CONFLICT; return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "UPDATE centros" " SET nombrecentro='%s'," " comentarios='%s'" " WHERE idcentro='%u';", params->name, params->comment, center_id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to update center in database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); ctx->http_error = OG_HTTP_404_NOT_FOUND; return -1; } dbi_result_free(result); return 0; } static int og_cmd_post_client_update(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *key, *msglog, *client_ip; struct og_computer computer = {}; struct og_dbi *dbi; dbi_result result; json_t *value; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "ip")) { err = og_json_parse_string_copy(value, computer.ip, sizeof(computer.ip)); params->flags |= OG_REST_PARAM_ADDR; } else if (!strcmp(key, "id")) { err = og_json_parse_uint(value, &computer.id); } else if (!strcmp(key, "serial_number")) { err = og_json_parse_string_copy(value, computer.serial_number, sizeof(computer.serial_number)); } else if (!strcmp(key, "netdriver")) { err = og_json_parse_string_copy(value, computer.netdriver, sizeof(computer.netdriver)); } else if (!strcmp(key, "maintenance")) { err = og_json_parse_bool(value, &computer.maintenance); } else if (!strcmp(key, "netiface")) { err = og_json_parse_string_copy(value, computer.netiface, sizeof(computer.netiface)); } else if (!strcmp(key, "repo_id")) { err = og_json_parse_uint(value, &computer.repo_id); } else if (!strcmp(key, "netmask")) { err = og_json_parse_string_copy(value, computer.netmask, sizeof(computer.netmask)); } else if (!strcmp(key, "remote")) { err = og_json_parse_bool(value, &computer.remote); } else if (!strcmp(key, "room")) { err = og_json_parse_uint(value, &computer.room); } else if (!strcmp(key, "name")) { err = og_json_parse_string_copy(value, computer.name, sizeof(computer.name)); } else if (!strcmp(key, "boot")) { err = og_json_parse_string_copy(value, computer.boot, sizeof(computer.boot)); } else if (!strcmp(key, "mac")) { err = og_json_parse_string_copy(value, computer.mac, sizeof(computer.mac)); } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT ip FROM ordenadores WHERE ip='%s' AND idordenador<>'%u'", computer.ip, computer.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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (dbi_result_get_numrows(result) > 0) { syslog(LOG_ERR, "client with IP %s already exist\n", computer.ip); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_409_CONFLICT; return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "SELECT ip FROM ordenadores WHERE mac='%s' AND idordenador<>'%u'", computer.mac, computer.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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (dbi_result_next_row(result)) { client_ip = dbi_result_get_string(result, "ip"); syslog(LOG_ERR, "client with MAC %s already exist in %s\n", computer.mac, client_ip); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_409_CONFLICT; return -1; } dbi_result_free(result); og_remove_tftpboot_files(dbi, computer.ip); result = dbi_conn_queryf(dbi->conn, "UPDATE ordenadores" " SET numserie='%s'," " ip='%s'," " netdriver='%s'," " maintenance=%u," " netiface='%s'," " idrepositorio=%u," " mascara='%s'," " inremotepc=%u," " idaula=%u," " nombreordenador='%s'," " mac='%s'," " arranque='%s'" " WHERE idordenador='%u';", computer.serial_number, computer.ip, computer.netdriver, computer.maintenance, computer.netiface, computer.repo_id, computer.netmask, computer.remote, computer.room, computer.name, computer.mac, computer.boot, computer.id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to update client in database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); ctx->http_error = OG_HTTP_404_NOT_FOUND; return -1; } dbi_result_free(result); if (og_set_client_mode(dbi, computer.ip, computer.mac, computer.boot)) { syslog(LOG_ERR, "failed to set client boot mode (%s:%d)\n", __func__, __LINE__); og_dbi_close(dbi); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } og_dbi_close(dbi); return 0; } static int add_room_folder(struct og_dbi *dbi, struct og_folder *folder) { dbi_result result; const char *msglog; result = dbi_conn_queryf(dbi->conn, "SELECT nombregrupo FROM grupos WHERE " "nombregrupo='%s' AND idcentro=%u", folder->name, folder->center); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } if (dbi_result_get_numrows(result) > 0) { syslog(LOG_ERR, "room-folder with that name already exists: %s\n", folder->name); dbi_result_free(result); return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "INSERT INTO grupos(" " nombregrupo," " grupoid," " tipo," " idcentro," " iduniversidad" ") VALUES ('%s', 0, 2, %u, 0)", folder->name, folder->center); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to add room-folder to database (%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } dbi_result_free(result); return 0; } static int add_computer_folder(struct og_dbi *dbi, struct og_folder *folder) { dbi_result result; const char *msglog; result = dbi_conn_queryf(dbi->conn, "SELECT nombregrupoordenador FROM gruposordenadores WHERE " "nombregrupoordenador='%s' AND idaula=%u", folder->name, folder->room); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } if (dbi_result_get_numrows(result) > 0) { syslog(LOG_ERR, "computer-folder with that name already exists: %s\n", folder->name); dbi_result_free(result); return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "INSERT INTO gruposordenadores(" " nombregrupoordenador," " idaula," " grupoid" ") VALUES ('%s', %u, 0)", folder->name, folder->room); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to add computer-folder to database (%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } dbi_result_free(result); return 0; } static int og_computer_folder_update(struct og_dbi *dbi, struct og_folder *folder) { const char *msglog; dbi_result result; result = dbi_conn_queryf(dbi->conn, "SELECT idaula FROM gruposordenadores WHERE idgrupo='%u'", folder->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; } if (!dbi_result_next_row(result)) { syslog(LOG_ERR, "could not find a folder with that id: %u\n", folder->id); dbi_result_free(result); return -1; } folder->room = dbi_result_get_uint(result, "idaula"); dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "SELECT idgrupo FROM gruposordenadores " "WHERE nombregrupoordenador='%s' AND idaula=%u", folder->name, folder->room); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } if (dbi_result_get_numrows(result) > 0) { syslog(LOG_ERR, "Folder with name %s already exists in room " "with id %d\n", folder->name, folder->room); dbi_result_free(result); return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "UPDATE gruposordenadores" " SET nombregrupoordenador='%s'" " WHERE idgrupo='%u';", folder->name, folder->id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to update room in database (%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } dbi_result_free(result); return 0; } static int og_room_folder_update(struct og_dbi *dbi, struct og_folder *folder) { const char *msglog; dbi_result result; result = dbi_conn_queryf(dbi->conn, "SELECT idcentro FROM grupos WHERE idgrupo='%u'", folder->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; } if (!dbi_result_next_row(result)) { syslog(LOG_ERR, "could not find a folder with that id: %u\n", folder->id); dbi_result_free(result); return -1; } folder->center = dbi_result_get_uint(result, "idcentro"); dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "SELECT idgrupo FROM grupos " "WHERE nombregrupo='%s' AND idcentro=%u", folder->name, folder->center); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } if (dbi_result_get_numrows(result) > 0) { syslog(LOG_ERR, "Folder with name %s already exists in center " "with id %d\n", folder->name, folder->center); dbi_result_free(result); return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "UPDATE grupos" " SET nombregrupo='%s'" " WHERE idgrupo='%u';", folder->name, folder->id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to update room in database (%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } dbi_result_free(result); return 0; } #define OG_COMPUTER_FOLDER_MARKER 0x00010000 static int og_cmd_post_folder_update(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; struct og_folder folder = {}; struct og_dbi *dbi; const char *key; json_t *value; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "id")) { params->flags |= OG_REST_PARAM_FOLDER; err = og_json_parse_uint(value, &folder.id); } else if (!strcmp(key, "name")) { params->flags |= OG_REST_PARAM_NAME; err = og_json_parse_string_copy(value, folder.name, sizeof(folder.name)); } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_NAME)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } if (folder.id & OG_COMPUTER_FOLDER_MARKER) { folder.id = folder.id & 0x0000ffff; err = og_computer_folder_update(dbi, &folder); } else { err = og_room_folder_update(dbi, &folder); } og_dbi_close(dbi); return err; } static int og_cmd_post_folder_add(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; struct og_folder folder = {}; struct og_dbi *dbi; const char *key; json_t *value; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "center")) { params->flags |= OG_REST_PARAM_CENTER; err = og_json_parse_uint(value, &folder.center); } else if (!strcmp(key, "room")) { params->flags |= OG_REST_PARAM_ROOM; err = og_json_parse_uint(value, &folder.room); } else if (!strcmp(key, "name")) { params->flags |= OG_REST_PARAM_NAME; err = og_json_parse_string_copy(value, folder.name, sizeof(folder.name)); } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_NAME)) return -1; if (og_msg_params_validate(params, OG_REST_PARAM_ROOM | OG_REST_PARAM_CENTER)) return -1; if (!og_msg_params_validate(params, OG_REST_PARAM_ROOM) && !og_msg_params_validate(params, OG_REST_PARAM_CENTER)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } if (params->flags & OG_REST_PARAM_ROOM) err = add_computer_folder(dbi, &folder); else if (params->flags & OG_REST_PARAM_CENTER) err = add_room_folder(dbi, &folder); else err = -1; if (err < 0) ctx->http_error = OG_HTTP_409_CONFLICT; og_dbi_close(dbi); return err; } static int og_delete_computer_folder(struct og_dbi *dbi, uint32_t folder_id) { const char *msglog; dbi_result result; folder_id = folder_id & 0x0000ffff; result = dbi_conn_queryf(dbi->conn, "DELETE FROM gruposordenadores " "WHERE idgrupo=%u", folder_id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to delete computer-group from database " "(%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } if (dbi_result_get_numrows_affected(result) < 1) { syslog(LOG_ERR, "delete did not modify any row (%s:%d)\n", __func__, __LINE__); dbi_result_free(result); return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "DELETE FROM ordenadores " "WHERE grupoid=%u", folder_id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to delete computers from database " "(%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } dbi_result_free(result); return 0; } static int og_delete_room_folder(struct og_dbi *dbi, uint32_t folder_id) { const char *msglog; dbi_result result; result = dbi_conn_queryf(dbi->conn, "DELETE FROM grupos " "WHERE idgrupo=%u", folder_id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to delete room-group from database " "(%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } if (dbi_result_get_numrows_affected(result) < 1) { syslog(LOG_ERR, "delete did not modify any row (%s:%d)\n", __func__, __LINE__); dbi_result_free(result); return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "DELETE FROM aulas " "WHERE grupoid=%u", folder_id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to delete rooms from database " "(%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } dbi_result_free(result); return 0; } static int og_cmd_post_folder_delete(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; struct og_dbi *dbi; uint32_t folder_id; const char *key; json_t *value; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "id")) { err = og_json_parse_uint(value, &folder_id); params->flags |= OG_REST_PARAM_ID; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ID)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } if (folder_id & 0x00010000) { err = og_delete_computer_folder(dbi, folder_id); } else { err = og_delete_room_folder(dbi, folder_id); } if (err < 0) ctx->http_error = OG_HTTP_404_NOT_FOUND; og_dbi_close(dbi); return err; } static int og_cmd_post_client_add(struct og_rest_ctx *ctx) { const char *room_name, *computer_name, *computer_ip, *hwaddr; struct og_computer computer = {}; const char *key, *msglog; struct og_dbi *dbi; dbi_result result; json_t *value; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "serial_number")) { err = og_json_parse_string_copy(value, computer.serial_number, sizeof(computer.serial_number)); } else if (!strcmp(key, "hardware_id")) { err = og_json_parse_uint(value, &computer.hardware_id); } else if (!strcmp(key, "folder_id")) { err = og_json_parse_uint(value, &computer.folder_id); if (!err) computer.folder_id = computer.folder_id & 0x0000ffff; } else if (!strcmp(key, "netdriver")) { err = og_json_parse_string_copy(value, computer.netdriver, sizeof(computer.netdriver)); } else if (!strcmp(key, "maintenance")) { err = og_json_parse_bool(value, &computer.maintenance); } else if (!strcmp(key, "netiface")) { err = og_json_parse_string_copy(value, computer.netiface, sizeof(computer.netiface)); } else if (!strcmp(key, "repo_id")) { err = og_json_parse_uint(value, &computer.repo_id); } else if (!strcmp(key, "livedir")) { err = og_json_parse_string_copy(value, computer.livedir, sizeof(computer.livedir)); } else if (!strcmp(key, "netmask")) { err = og_json_parse_string_copy(value, computer.netmask, sizeof(computer.netmask)); } else if (!strcmp(key, "remote")) { err = og_json_parse_bool(value, &computer.remote); } else if (!strcmp(key, "room")) { err = og_json_parse_uint(value, &computer.room); } else if (!strcmp(key, "name")) { err = og_json_parse_string_copy(value, computer.name, sizeof(computer.name)); } else if (!strcmp(key, "boot")) { err = og_json_parse_string_copy(value, computer.boot, sizeof(computer.boot)); } else if (!strcmp(key, "mac")) { err = og_json_parse_string_copy(value, computer.mac, sizeof(computer.mac)); } else if (!strcmp(key, "ip")) { err = og_json_parse_string_copy(value, computer.ip, sizeof(computer.ip)); } if (err < 0) return err; } dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT ip FROM ordenadores WHERE ip='%s'", computer.ip); 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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (dbi_result_get_numrows(result) > 0) { syslog(LOG_ERR, "client with the same IP already exists: %s\n", computer.ip); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_409_CONFLICT; return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "SELECT nombreordenador, ip, nombreaula" " FROM ordenadores" " INNER JOIN aulas on ordenadores.idaula = aulas.idaula" " WHERE mac='%s'", computer.mac); 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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (dbi_result_next_row(result)) { room_name = dbi_result_get_string(result, "nombreaula"); computer_name = dbi_result_get_string(result, "nombreordenador"); computer_ip = dbi_result_get_string(result, "ip"); hwaddr = dbi_result_get_string(result, "mac"); syslog(LOG_ERR, "Failed to add client %s because %s (%s) in room %s already owns MAC address %s\n", computer.ip, computer_ip, computer_name, room_name, hwaddr); dbi_result_free(result); og_dbi_close(dbi); return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "INSERT INTO ordenadores(" " nombreordenador," " numserie," " ip," " mac," " idaula," " grupoid," " idperfilhard," " idrepositorio," " mascara," " arranque," " netiface," " netdriver," " oglivedir," " inremotepc," " maintenance" ") VALUES ('%s', '%s', '%s', '%s', %u, %u, %u," " %u, '%s', '%s', '%s', '%s'," " '%s', %u, %u)", computer.name, computer.serial_number, computer.ip, computer.mac, computer.room, computer.folder_id, computer.hardware_id, computer.repo_id, computer.netmask, computer.boot, computer.netiface, computer.netdriver, computer.livedir, computer.remote, computer.maintenance); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to add client to database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); return -1; } dbi_result_free(result); if (og_set_client_mode(dbi, computer.ip, computer.mac, computer.boot)) { syslog(LOG_ERR, "failed to set client boot mode (%s:%d)\n", __func__, __LINE__); og_dbi_close(dbi); return -1; } og_dbi_close(dbi); return 0; } static int og_cmd_post_client_delete(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *key, *msglog; struct og_dbi *dbi; dbi_result result; unsigned int i; json_t *value; int err = 0; json_object_foreach(ctx->json, 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; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } for (i = 0; i < params->ips_array_len; i++) { og_remove_tftpboot_files(dbi, params->ips_array[i]); result = dbi_conn_queryf(dbi->conn, "DELETE FROM ordenadores WHERE ip='%s'", params->ips_array[i]); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); return -1; } if (dbi_result_get_numrows_affected(result) < 1) { syslog(LOG_ERR, "delete did not modify any row (%s:%d)\n", __func__, __LINE__); dbi_result_free(result); og_dbi_close(dbi); return -1; } dbi_result_free(result); } og_dbi_close(dbi); return 0; } static int og_cmd_post_client_move(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; struct og_computer computer = {}; const char *key, *msglog; struct og_dbi *dbi; dbi_result result; unsigned int i; json_t *value; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "clients")) { err = og_json_parse_clients(value, params); } else if (!strcmp(key, "room")) { err = og_json_parse_uint(value, &computer.room); } else if (!strcmp(key, "folder_id")) { err = og_json_parse_uint(value, &computer.folder_id); if (!err) computer.folder_id = computer.folder_id & 0x0000ffff; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } for (i = 0; i < params->ips_array_len; i++) { result = dbi_conn_queryf(dbi->conn, "UPDATE ordenadores SET " "idaula=%u, grupoid=%u WHERE ip='%s'", computer.room, computer.folder_id, params->ips_array[i]); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); return -1; } if (dbi_result_get_numrows_affected(result) < 1) { syslog(LOG_ERR, "client move did not modify any row (%s:%d)\n", __func__, __LINE__); dbi_result_free(result); og_dbi_close(dbi); return -1; } dbi_result_free(result); } og_dbi_close(dbi); return 0; } static int og_cmd_get_room_info(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; struct og_room room = {}; json_t *value, *root; struct og_dbi *dbi; uint32_t room_id; const char *key; int err = 0; struct og_buffer og_buffer = { .data = ctx->buf_reply }; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "id")) { err = og_json_parse_uint(value, &room_id); params->flags |= OG_REST_PARAM_ID; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ID)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } if (og_dbi_get_room_info(dbi, &room, room_id)) { og_dbi_close(dbi); return -1; } og_dbi_close(dbi); root = json_object(); if (!root) return -1; json_object_set_new(root, "id", json_integer(room.id)); json_object_set_new(root, "name", json_string(room.name)); json_object_set_new(root, "gateway", json_string(room.gateway)); json_object_set_new(root, "netmask", json_string(room.netmask)); if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(root); return -1; } json_decref(root); return 0; } static int og_cmd_stop(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *key; json_t *value; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, 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; return og_send_request(OG_METHOD_POST, OG_CMD_STOP, params, NULL, NULL); } static int og_cmd_hardware(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *key; json_t *value; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, 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; return og_send_request(OG_METHOD_GET, OG_CMD_HARDWARE, params, NULL, NULL); } static int og_cmd_get_hardware(struct og_rest_ctx *ctx) { const char *key, *msglog, *hw_item, *hw_type; struct og_msg_params *params = &ctx->params; json_t *value, *root, *array, *item; struct og_dbi *dbi; dbi_result result; int err = 0; struct og_buffer og_buffer = { .data = ctx->buf_reply }; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "client")) err = og_json_parse_clients(value, params); if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT hardwares.descripcion AS item, " " tipohardwares.descripcion AS type " "FROM hardwares " "INNER JOIN perfileshard_hardwares " " ON hardwares.idhardware = perfileshard_hardwares.idhardware " "INNER JOIN ordenadores " " ON perfileshard_hardwares.idperfilhard = ordenadores.idperfilhard " "INNER JOIN tipohardwares " " ON hardwares.idtipohardware = tipohardwares.idtipohardware " "WHERE ordenadores.ip = '%s'", params->ips_array[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; } array = json_array(); if (!array) { dbi_result_free(result); og_dbi_close(dbi); return -1; } while (dbi_result_next_row(result)) { item = json_object(); if (!item) { dbi_result_free(result); og_dbi_close(dbi); json_decref(array); return -1; } hw_item = dbi_result_get_string(result, "item"); hw_type = dbi_result_get_string(result, "type"); json_object_set_new(item, "type", json_string(hw_type)); json_object_set_new(item, "description", json_string(hw_item)); json_array_append_new(array, item); } dbi_result_free(result); og_dbi_close(dbi); root = json_object(); if (!root){ json_decref(array); return -1; } json_object_set_new(root, "hardware", array); if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(root); return -1; } json_decref(root); return 0; } static int og_cmd_software(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; json_t *clients, *value; const char *key; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, 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(ctx->json); json_object_del(clients, "clients"); return og_send_request(OG_METHOD_GET, OG_CMD_SOFTWARE, params, clients, NULL); } static int og_cmd_get_software(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; json_t *value, *software, *root; const char *key, *msglog, *name; uint64_t disk, partition; uint64_t flags = 0; struct og_dbi *dbi; dbi_result result; int err = 0; struct og_buffer og_buffer = { .data = ctx->buf_reply }; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "client")) { err = og_json_parse_clients(value, params); } else if (!strcmp(key, "disk")) { err = og_json_parse_uint64(value, &disk); flags |= OG_REST_PARAM_DISK; } else if (!strcmp(key, "partition")) { err = og_json_parse_uint64(value, &partition); flags |= OG_REST_PARAM_PARTITION; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR) || !og_flags_validate(flags, OG_REST_PARAM_DISK | OG_REST_PARAM_PARTITION)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT s.descripcion " "FROM softwares s " "INNER JOIN perfilessoft_softwares pss " "ON s.idsoftware = pss.idsoftware " "INNER JOIN ordenadores_particiones op " "ON pss.idperfilsoft = op.idperfilsoft " "INNER JOIN ordenadores o " "ON o.idordenador = op.idordenador " "WHERE o.ip='%s' AND " " op.numdisk=%lu AND " " op.numpar=%lu", params->ips_array[0], disk, partition); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } software = json_array(); if (!software) { dbi_result_free(result); og_dbi_close(dbi); return -1; } while (dbi_result_next_row(result)) { name = dbi_result_get_string(result, "descripcion"); json_array_append_new(software, json_string(name)); } dbi_result_free(result); og_dbi_close(dbi); root = json_object(); if (!root) { json_decref(software); return -1; } json_object_set_new(root, "software", software); if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(root); return -1; } json_decref(root); return 0; } static const int og_cmd_get_repositories(struct og_rest_ctx *ctx) { json_t *root, *repository, *repositories, *ips; struct og_buffer og_buffer = { .data = ctx->buf_reply, }; LIST_HEAD(repo_list); struct og_repo *repo; uint32_t i; if (og_repo_list(&repo_list) < 0) return -1; root = json_object(); if (!root) return -1; repositories = json_array(); if (!repositories) { json_decref(root); return -1; } json_object_set_new(root, "repositories", repositories); list_for_each_entry(repo, &repo_list, list) { repository = json_object(); if (!repository) goto err_out; ips = json_array(); if (!ips) { json_decref(repository); goto err_out; } for (i = 0; i < repo->num_ips; i++) json_array_append_new(ips, json_string(inet_ntoa(repo->addr[i]))); json_object_set_new(repository, "id", json_integer(repo->id)); json_object_set_new(repository, "addr", ips); json_object_set_new(repository, "name", json_string(repo->name)); json_array_append_new(repositories, repository); } if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) goto err_out; og_repo_free_list(&repo_list); json_decref(root); return 0; err_out: og_repo_free_list(&repo_list); json_decref(root); return -1; } #define OG_IMAGE_TYPE_MAXLEN 4 static json_t *og_json_disk_alloc() { const char *dir = ogconfig.repo.dir; struct statvfs buffer; json_t *disk_json; int ret; ret = statvfs(dir, &buffer); if (ret) return NULL; disk_json = json_object(); if (!disk_json) return NULL; json_object_set_new(disk_json, "total", json_integer(buffer.f_blocks * buffer.f_frsize)); json_object_set_new(disk_json, "free", json_integer(buffer.f_bfree * buffer.f_frsize)); return disk_json; } #define OG_PERMS_IRWX (S_IRWXU | S_IRWXG | S_IRWXO) #define OG_PERMS_MAXLEN 4 static json_t *og_json_image_alloc(struct og_image *image) { char perms_string[OG_PERMS_MAXLEN]; json_t *image_json; char *modified; image_json = json_object(); if (!image_json) return NULL; snprintf(perms_string, sizeof(perms_string), "%o", image->perms); modified = ctime(&image->lastupdate); modified[strlen(modified) - 1] = '\0'; json_object_set_new(image_json, "name", json_string(image->name)); json_object_set_new(image_json, "datasize", json_integer(image->datasize)); json_object_set_new(image_json, "size", json_integer(image->size)); json_object_set_new(image_json, "modified", json_string(modified)); json_object_set_new(image_json, "permissions", json_string(perms_string)); json_object_set_new(image_json, "software_id", json_integer(image->software_id)); json_object_set_new(image_json, "type", json_integer(image->type)); json_object_set_new(image_json, "id", json_integer(image->id)); json_object_set_new(image_json, "repo_id", json_integer(image->repo_id)); json_object_set_new(image_json, "description", json_string(image->description)); json_object_set_new(image_json, "checksum", json_string(image->checksum)); return image_json; } static int og_cmd_images(struct og_rest_ctx *ctx) { json_t *root, *images, *image_json, *disk_json; struct og_buffer og_buffer = { .data = ctx->buf_reply }; struct og_image image; struct og_dbi *dbi; dbi_result result; root = json_object(); if (!root) return -1; images = json_array(); if (!images) { json_decref(root); return -1; } json_object_set_new(root, "images", images); dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); json_decref(root); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT i.nombreca, o.nombreordenador, " " i.clonator, i.compressor, " " i.filesystem, i.datasize, i.size, " " i.lastupdate, i.permissions, " " i.idperfilsoft, i.tipo, " " i.idimagen, i.idrepositorio, " " i.descripcion, i.checksum " "FROM imagenes i " "LEFT JOIN ordenadores o " "ON i.idordenador = o.idordenador "); while (dbi_result_next_row(result)) { image = (struct og_image){0}; image.datasize = dbi_result_get_ulonglong(result, "datasize"); image.size = dbi_result_get_ulonglong(result, "size"); image.lastupdate = dbi_result_get_ulonglong(result, "lastupdate"); image.perms = dbi_result_get_uint(result, "permissions"); image.software_id = dbi_result_get_ulonglong(result, "idperfilsoft"); image.type = dbi_result_get_ulonglong(result, "tipo"); image.id = dbi_result_get_ulonglong(result, "idimagen"); image.repo_id = dbi_result_get_ulonglong(result, "idrepositorio"); snprintf(image.checksum, sizeof(image.checksum), "%s", dbi_result_get_string(result, "checksum")); snprintf(image.name, sizeof(image.name), "%s", dbi_result_get_string(result, "nombreca")); snprintf(image.description, sizeof(image.description), "%s", dbi_result_get_string(result, "descripcion")); snprintf(image.checksum, sizeof(image.checksum), "%s", dbi_result_get_string(result, "checksum")); image_json = og_json_image_alloc(&image); if (!image_json) { dbi_result_free(result); og_dbi_close(dbi); json_decref(root); return -1; } json_array_append_new(images, image_json); } dbi_result_free(result); og_dbi_close(dbi); disk_json = og_json_disk_alloc(); if (!disk_json) { syslog(LOG_ERR, "cannot allocate disk json"); json_decref(root); return -1; } json_object_set_new(root, "disk", disk_json); if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(root); return -1; } json_decref(root); return 0; } static int og_json_parse_create_image(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *key; json_t *value; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, 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_copy(value, (char *)¶ms->image.name, sizeof(params->image.name)); params->flags |= OG_REST_PARAM_NAME; } else if (!strcmp(key, "repository_id")) { err = og_json_parse_uint64(value, ¶ms->image.repo_id); 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; } else if (!strcmp(key, "description")) { err = og_json_parse_string_copy(value, (char *)¶ms->image.description, sizeof(params->image.description)); } else if (!strcmp(key, "group_id")) { err = og_json_parse_uint64(value, ¶ms->image.group_id); } else if (!strcmp(key, "center_id")) { err = og_json_parse_uint64(value, ¶ms->image.center_id); } else if (!strcmp(key, "backup")) { err = og_json_parse_bool(value, ¶ms->backup); params->flags |= OG_REST_PARAM_BACKUP; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_BACKUP)) params->backup = true; return 0; } static int og_cmd_add_image(struct og_rest_ctx *ctx, bool update) { struct og_msg_params *params = &ctx->params; char repository_ip[OG_DB_IP_MAXLEN + 1]; char new_image_id[OG_DB_INT_MAXLEN + 1]; struct og_cmd_ctx cmd_ctx = {}; struct og_dbi *dbi; json_t *body; int err = 0; bool found; err = og_json_parse_create_image(ctx); if (err < 0) return err; 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)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } found = og_dbi_get_image(dbi, ¶ms->image); if (!found) { if (update) { syslog(LOG_ERR, "cannot update image file `%s', it does not exist\n", params->image.name); og_dbi_close(dbi); return -1; } if (!og_msg_params_validate(params, OG_REST_PARAM_REPO)) { syslog(LOG_ERR, "missing repo parameter in request (%s:%d)\n", __func__, __LINE__); og_dbi_close(dbi); return -1; } err = og_dbi_add_image(dbi, ¶ms->image); if (err < 0) { og_dbi_close(dbi); return err; } snprintf(new_image_id, sizeof(new_image_id), "%lu", params->image.id); params->id = new_image_id; } else { syslog(LOG_INFO, "updating existing image `%s'\n", params->image.name); snprintf(new_image_id, sizeof(new_image_id), "%lu", params->image.id); params->id = new_image_id; } json_object_set_new(ctx->json, "id", json_string(params->id)); err = og_dbi_get_repository_ip(dbi, params->image.repo_id, params->ips_array[0], repository_ip); og_dbi_close(dbi); if (err < 0) return err; body = json_copy(ctx->json); json_object_del(body, "clients"); json_object_set_new(body, "repository", json_string(repository_ip)); json_object_set_new(body, "backup", json_boolean(params->backup)); cmd_ctx.image.id = params->image.id; return og_send_request(OG_METHOD_POST, OG_CMD_IMAGE_CREATE, params, body, &cmd_ctx); } static int og_cmd_create_image(struct og_rest_ctx *ctx) { return og_cmd_add_image(ctx, false); } static int og_cmd_update_image(struct og_rest_ctx *ctx) { return og_cmd_add_image(ctx, true); } static int og_json_parse_restore_image(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *key; json_t *value; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, 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_id")) { err = og_json_parse_uint64(value, ¶ms->image.repo_id); 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) return err; } return 0; } static int og_cmd_restore_image(struct og_rest_ctx *ctx) { char repository_ip[OG_DB_IP_MAXLEN + 1] = {}; struct og_msg_params *params = &ctx->params; struct og_dbi *dbi; json_t *body; int err; err = og_json_parse_restore_image(ctx); if (err < 0) return err; 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; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } err = og_dbi_get_repository_ip(dbi, params->image.repo_id, params->ips_array[0], repository_ip); og_dbi_close(dbi); if (err < 0) return err; body = json_copy(ctx->json); json_object_del(body, "clients"); json_object_set_new(body, "repository", json_string(repository_ip)); return og_send_request(OG_METHOD_POST, OG_CMD_IMAGE_RESTORE, params, body, NULL); } static int og_cmd_delete_image(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; struct og_dbi *dbi; uint64_t image_id; const char *key; json_t *value; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "image")) { err = og_json_parse_string(value, ¶ms->id); params->flags |= OG_REST_PARAM_ID; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ID)) return -1; if (safe_strtoull(params->id, &image_id, 10, UINT32_MAX) < 0) { syslog(LOG_ERR, "failed to parse image id %s (%s:%d)\n", params->id, __func__, __LINE__); return -1; } dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } err = og_dbi_delete_image(dbi, image_id); if (err < 0) { og_dbi_close(dbi); return err; } og_dbi_close(dbi); return 0; } struct og_scope_image { struct list_head list; uint32_t id; }; static void og_scope_image_list_free(struct list_head *scope_list) { struct og_scope_image *scope, *next; list_for_each_entry_safe(scope, next, scope_list, list) { list_del(&scope->list); free(scope); } } static int og_json_parse_scope_image_list(json_t *element, struct list_head *scope_list) { struct og_scope_image *scope; 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_INTEGER) goto err_out; scope = calloc(1, sizeof(struct og_scope_image)); if (!scope) goto err_out; scope->id = json_integer_value(k); list_add_tail(&scope->list, scope_list); } return 0; err_out: og_scope_image_list_free(scope_list); return -1; } enum { OG_ATTR_IMAGE_ID = (1 << 0), OG_ATTR_IMAGE_SCOPE_ID = (1 << 0), }; static int og_cmd_image_scope_update(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; struct og_scope_image *scope; LIST_HEAD(scope_list); struct og_dbi *dbi; const char *msglog; uint32_t image_id; dbi_result result; const char *key; json_t *value; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "image")) { err = og_json_parse_uint(value, &image_id); params->flags |= OG_ATTR_IMAGE_ID; } else if (!strcmp(key, "scopes")) { err = og_json_parse_scope_image_list(value, &scope_list); if (err >= 0 && !list_empty(&scope_list)) params->flags |= OG_ATTR_IMAGE_SCOPE_ID; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_ATTR_IMAGE_ID | OG_ATTR_IMAGE_SCOPE_ID)) { og_scope_image_list_free(&scope_list); return -1; } dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); og_scope_image_list_free(&scope_list); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "DELETE FROM image_scope WHERE image_id=%d", image_id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); goto err_out; } dbi_result_free(result); list_for_each_entry(scope, &scope_list, list) { result = dbi_conn_queryf(dbi->conn, "INSERT INTO image_scope (image_id, scope_id) " "VALUES (%d, %d)", image_id, scope->id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); goto err_out; } dbi_result_free(result); } og_dbi_close(dbi); og_scope_image_list_free(&scope_list); return 0; err_out: og_scope_image_list_free(&scope_list); og_dbi_close(dbi); return -1; } static json_t *og_json_image_scope(struct og_dbi *dbi, uint32_t image_id) { json_t *scope_image, *scope_array; const char *msglog; uint32_t scope_id; dbi_result result; result = dbi_conn_queryf(dbi->conn, "SELECT scope_id FROM image_scope WHERE image_id = '%u'", image_id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return NULL; } scope_image = json_object(); if (!scope_image) { dbi_result_free(result); return NULL; } json_object_set_new(scope_image, "id", json_integer(image_id)); scope_array = json_array(); if (!scope_array) { json_decref(scope_image); dbi_result_free(result); return NULL; } while (dbi_result_next_row(result)) { scope_id = dbi_result_get_uint(result, "scope_id"); json_array_append_new(scope_array, json_integer(scope_id)); } dbi_result_free(result); json_object_set_new(scope_image, "scopes", scope_array); return scope_image; } static int og_cmd_image_scope_list(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; struct og_buffer og_buffer = { .data = ctx->buf_reply }; json_t *value, *scope_image; struct og_dbi *dbi; uint32_t image_id; const char *key; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "image")) { err = og_json_parse_uint(value, &image_id); params->flags |= OG_ATTR_IMAGE_ID; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_ATTR_IMAGE_ID)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } scope_image = og_json_image_scope(dbi, image_id); og_dbi_close(dbi); if (!scope_image) return -1; if (json_dump_callback(scope_image, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(scope_image); return -1; } json_decref(scope_image); return 0; } static int og_dbi_client_cache_get(struct og_dbi *dbi, json_t *clients, const char *ip) { uint64_t img_size, cache_size = 0, used_cache = 0, free_cache = 0; const char *img_name, *msglog, *img_checksum; json_t *client_data, *cache_arr, *img_info; dbi_result result; result = dbi_conn_queryf(dbi->conn, "SELECT ordenadores_particiones.tamano, " "ordenadores_particiones.used_size, ordenadores_particiones.free_size " "FROM ordenadores_particiones JOIN ordenadores " "ON ordenadores.idordenador = ordenadores_particiones.idordenador " "WHERE ordenadores.ip = '%s' AND codpar = 202", ip); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } if (dbi_result_next_row(result)) { cache_size = dbi_result_get_longlong(result, "tamano") * 1024; used_cache = dbi_result_get_longlong(result, "used_size"); free_cache = dbi_result_get_longlong(result, "free_size"); } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "SELECT cache.imagename, cache.size, cache.checksum " "FROM ordenadores JOIN cache " "ON ordenadores.idordenador = cache.clientid " "WHERE ordenadores.ip = '%s'", ip); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } client_data = json_object(); if (!client_data) { dbi_result_free(result); return -1; } cache_arr = json_array(); if (!cache_arr) { json_decref(client_data); dbi_result_free(result); return -1; } while (dbi_result_next_row(result)) { img_name = dbi_result_get_string(result, "imagename"); img_size = dbi_result_get_ulonglong(result, "size"); img_checksum = dbi_result_get_string(result, "checksum"); img_info = json_object(); if (!img_info) { json_decref(client_data); json_decref(cache_arr); dbi_result_free(result); return -1; } json_object_set_new(img_info, "name", json_string(img_name)); json_object_set_new(img_info, "size", json_integer(img_size)); json_object_set_new(img_info, "checksum", json_string(img_checksum)); json_array_append_new(cache_arr, img_info); } json_object_set_new(client_data, "ip", json_string(ip)); json_object_set_new(client_data, "cache_size", json_integer(cache_size)); json_object_set_new(client_data, "used_cache", json_integer(used_cache)); json_object_set_new(client_data, "free_cache", json_integer(free_cache)); json_object_set_new(client_data, "images", cache_arr); json_array_append_new(clients, client_data); dbi_result_free(result); return 0; } static int og_cmd_cache_list(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; json_t *value, *root, *clients; struct og_dbi *dbi; const char *key; int err = 0, i; struct og_buffer og_buffer = { .data = ctx->buf_reply }; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "clients")) err = og_json_parse_clients(value, params); else err = -1; if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) return -1; clients = json_array(); if (!clients) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); json_decref(clients); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } for (i = 0; i < params->ips_array_len; i++) { if (og_dbi_client_cache_get(dbi, clients, params->ips_array[i]) < 0) { json_decref(clients); og_dbi_close(dbi); return -1; } } og_dbi_close(dbi); root = json_object(); if (!root) { json_decref(clients); return -1; } json_object_set_new(root, "clients", clients); if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(root); return -1; } json_decref(root); return 0; } static int og_dbi_client_efi_get(struct og_dbi *dbi, json_t *clients, const char *ip) { json_t *client_data, *boot_entry, *boot_entry_arr; const char *msglog, *entry_name, *entry_description; bool entry_active; int entry_order; dbi_result result; client_data = json_object(); if (!client_data) return -1; json_object_set_new(client_data, "ip", json_string(ip)); result = dbi_conn_queryf(dbi->conn, "SELECT boot_entries.name, boot_entries.active, " "boot_entries.description, boot_entries.entry_order " "FROM boot_entries JOIN ordenadores " "ON ordenadores.idordenador = boot_entries.client_id " "WHERE ordenadores.ip = '%s' " "ORDER BY " " CASE " " WHEN boot_entries.entry_order >= 0 THEN 0 ELSE 1 " " END ASC, " " boot_entries.entry_order ASC", ip); if (!result) { json_decref(client_data); dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); return -1; } boot_entry_arr = json_array(); if (!boot_entry_arr) { json_decref(client_data); dbi_result_free(result); return -1; } while (dbi_result_next_row(result)) { entry_name = dbi_result_get_string(result, "name"); entry_active = dbi_result_get_int(result, "active") != 0; entry_description = dbi_result_get_string(result, "description"); entry_order = dbi_result_get_int(result, "entry_order"); boot_entry = json_object(); if (!boot_entry) { json_decref(client_data); json_decref(boot_entry_arr); dbi_result_free(result); return -1; } json_object_set_new(boot_entry, "name", json_string(entry_name)); json_object_set_new(boot_entry, "active", json_boolean(entry_active)); json_object_set_new(boot_entry, "description", json_string(entry_description)); if (entry_order >= 0) json_object_set_new(boot_entry, "order", json_integer(entry_order)); json_array_append_new(boot_entry_arr, boot_entry); } json_object_set_new(client_data, "entries", boot_entry_arr); json_array_append_new(clients, client_data); dbi_result_free(result); return 0; } static int og_cmd_efi_list(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; json_t *value, *root, *clients; struct og_dbi *dbi; const char *key; int err = 0, i; struct og_buffer og_buffer = { .data = ctx->buf_reply }; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "clients")) err = og_json_parse_clients(value, params); else err = -1; if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) return -1; clients = json_array(); if (!clients) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); json_decref(clients); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } for (i = 0; i < params->ips_array_len; i++) { if (og_dbi_client_efi_get(dbi, clients, params->ips_array[i]) < 0) { json_decref(clients); og_dbi_close(dbi); return -1; } } og_dbi_close(dbi); root = json_object(); if (!root) { json_decref(clients); return -1; } json_object_set_new(root, "clients", clients); if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(root); return -1; } json_decref(root); return 0; } static int og_cmd_cache_delete(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; json_t *value, *body, *image_arr = NULL; const char *key; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "clients")) { err = og_json_parse_clients(value, params); } else if (!strcmp(key, "images")) { if (json_typeof(value) != JSON_ARRAY) { err = -1; } image_arr = value; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR)) return -1; if (!image_arr) return -1; body = json_copy(ctx->json); json_object_del(body, "clients"); return og_send_request(OG_METHOD_POST, OG_CMD_CACHE_DELETE, params, body, NULL); } static int og_cmd_cache_fetch(struct og_rest_ctx *ctx) { char repository_ip[OG_DB_IP_MAXLEN + 1] = {}; struct og_msg_params *params = &ctx->params; json_t *value, *body; struct og_dbi *dbi; const char *key; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "clients")) { err = og_json_parse_clients(value, params); } else if (!strcmp(key, "image")) { err = og_json_parse_string(value, ¶ms->name); params->flags |= OG_REST_PARAM_NAME; } else if (!strcmp(key, "repository_id")) { err = og_json_parse_uint64(value, ¶ms->image.repo_id); params->flags |= OG_REST_PARAM_REPO; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_NAME | OG_REST_PARAM_ADDR | OG_REST_PARAM_REPO)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } err = og_dbi_get_repository_ip(dbi, params->image.repo_id, params->ips_array[0], repository_ip); og_dbi_close(dbi); if (err < 0) return err; body = json_copy(ctx->json); json_object_del(body, "clients"); json_object_set_new(body, "repository", json_string(repository_ip)); return og_send_request(OG_METHOD_POST, OG_CMD_CACHE_FETCH, params, body, NULL); } static int og_cmd_setup(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; json_t *value, *clients; const char *key; int err = 0; if (json_typeof(ctx->json) != JSON_OBJECT) return -1; json_object_foreach(ctx->json, key, value) { 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, "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) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | OG_REST_PARAM_TYPE | 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(ctx->json); json_object_del(clients, "clients"); return og_send_request(OG_METHOD_POST, OG_CMD_SETUP, params, clients, NULL); } static int og_cmd_scope_get(struct og_rest_ctx *ctx) { struct og_buffer og_buffer = { .data = ctx->buf_reply }; json_t *root, *array; struct og_dbi *dbi; root = json_object(); if (!root) return -1; array = json_array(); if (!array) { json_decref(root); return -1; } json_object_set_new(root, "scope", array); dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); json_decref(root); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } if (og_dbi_scope_get(dbi, array)) { og_dbi_close(dbi); json_decref(root); return -1; } og_dbi_close(dbi); if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(root); return -1; } json_decref(root); return 0; } #define OG_LIVE_JSON_FILE_PATH "/opt/opengnsys/etc/ogliveinfo.json" static int og_cmd_oglive_list(struct og_rest_ctx *ctx) { struct og_buffer og_buffer = { .data = ctx->buf_reply }; json_t *root, *live_entry, *oglive_array, *default_entry; const char *msglog, *live_name; uint64_t live_timestamp; json_error_t json_err; struct og_dbi *dbi; dbi_result result; int priority; root = json_load_file(OG_LIVE_JSON_FILE_PATH, 0, &json_err); if (!root) { syslog(LOG_ERR, "malformed json line %d: %s\n", json_err.line, json_err.text); return -1; } oglive_array = json_object_get(root, "oglive"); if (!oglive_array || !json_is_array(oglive_array)) { json_decref(root); return -1; } default_entry = json_object_get(root, "default"); if (!default_entry || json_typeof(default_entry) != JSON_INTEGER) { json_decref(root); return -1; } dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); json_decref(root); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT name, priority, " "UNIX_TIMESTAMP(creation_date) AS creation_timestamp " "FROM oglive"); 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); json_decref(root); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } while (dbi_result_next_row(result) > 0) { live_name = dbi_result_get_string(result, "name"); live_timestamp = dbi_result_get_longlong(result, "creation_timestamp"); priority = dbi_result_get_int(result, "priority"); if (priority > 0) { if (json_integer_set(default_entry, json_array_size(oglive_array)) < 0) { syslog(LOG_ERR, "Failed to modify 'default' entry value\n"); dbi_result_free(result); og_dbi_close(dbi); json_decref(root); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } } live_entry = json_object(); if (!live_entry) { syslog(LOG_ERR, "Cannot allocate JSON object (%s:%d)\n", __func__, __LINE__); dbi_result_free(result); og_dbi_close(dbi); json_decref(root); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } json_object_set_new(live_entry, "directory", json_string(live_name)); json_object_set_new(live_entry, "date", json_integer(live_timestamp)); json_array_append_new(oglive_array, live_entry); } dbi_result_free(result); og_dbi_close(dbi); if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(root); return -1; } json_decref(root); return 0; } static int og_cmd_post_center_add(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *key, *msglog; struct og_dbi *dbi; dbi_result result; json_t *value; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "name")) { err = og_json_parse_string(value, ¶ms->name); params->flags |= OG_REST_PARAM_NAME; } else if (!strcmp(key, "comment")) { err = og_json_parse_string(value, ¶ms->comment); } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_NAME)) return -1; if (!params->comment) params->comment = ""; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT nombrecentro FROM centros WHERE nombrecentro='%s'", params->name); 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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (dbi_result_get_numrows(result) > 0) { syslog(LOG_ERR, "Center with name %s already exists\n", params->name); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_409_CONFLICT; return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "INSERT INTO centros(" " nombrecentro," " comentarios," " identidad) VALUES (" "'%s', '%s', 1)", params->name, params->comment); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to add center to database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); return -1; } dbi_result_free(result); og_dbi_close(dbi); return 0; } static int og_cmd_post_center_delete(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *key, *msglog; struct og_dbi *dbi; dbi_result result; json_t *value; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "id")) { err = og_json_parse_string(value, ¶ms->id); params->flags |= OG_REST_PARAM_ID; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ID)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "DELETE FROM centros WHERE idcentro=%s", params->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; } if (dbi_result_get_numrows_affected(result) < 1) { syslog(LOG_ERR, "delete did not modify any row (%s:%d)\n", __func__, __LINE__); dbi_result_free(result); og_dbi_close(dbi); return -1; } dbi_result_free(result); return 0; } static int og_repo_list_find_ips(const char *ips_array[], uint32_t ips_array_len, uint32_t exclude_repo_id) { struct og_repo *repo; LIST_HEAD(repo_list); struct in_addr addr; uint32_t i; if (og_repo_list(&repo_list) < 0) return -1; for (i = 0; i < ips_array_len; i++) { if (!inet_aton(ips_array[i], &addr)) return -1; list_for_each_entry(repo, &repo_list, list) { if (exclude_repo_id && exclude_repo_id == repo->id) continue; if (addr.s_addr == repo->addr[i].s_addr) { syslog(LOG_ERR, "Cannot add new repository, IP %s is already in used by repository '%s'\n", ips_array[i], repo->name); return -1; } } } og_repo_free_list(&repo_list); return 0; } static int og_cmd_post_repository_update(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; char name[OG_DB_REPO_NAME_MAXLEN]; const char *key, *msglog; unsigned int repo_id; struct og_dbi *dbi; dbi_result result; int err = 0, i; json_t *value; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "id")) { err = og_json_parse_uint(value, &repo_id); params->flags |= OG_REST_PARAM_ID; } else if (!strcmp(key, "name")) { err = og_json_parse_string_copy(value, name, sizeof(name)); params->flags |= OG_REST_PARAM_NAME; } else if (!strcmp(key, "addr")) { err = og_json_parse_clients(value, params); } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ID | OG_REST_PARAM_NAME | OG_REST_PARAM_ADDR)) return -1; if (og_repo_list_find_ips(params->ips_array, params->ips_array_len, repo_id)) { syslog(LOG_ERR, "Repository IP already in use, cannot define overlapping\n"); return -1; } dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "DELETE FROM repositorios WHERE alias=%u;", repo_id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to update repository in database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "UPDATE repositorios " "SET ip='%s' WHERE idrepositorio=%u;", params->ips_array[0], repo_id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to add repository to database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); return -1; } dbi_result_free(result); for (i = 1; i < params->ips_array_len; i++) { result = dbi_conn_queryf(dbi->conn, "INSERT INTO repositorios(" "nombrerepositorio, ip, idcentro, grupoid, alias) VALUES " "('%s', '%s', 1, 0, %u);", name, params->ips_array[i], repo_id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to add repository to database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); return -1; } dbi_result_free(result); } og_dbi_close(dbi); return 0; } static int og_cmd_post_repository_add(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; char name[OG_DB_REPO_NAME_MAXLEN]; const char *key, *msglog; struct og_dbi *dbi; dbi_result result; int err = 0, i; json_t *value; uint32_t id; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "name")) { err = og_json_parse_string_copy(value, name, sizeof(name)); params->flags |= OG_REST_PARAM_NAME; } else if (!strcmp(key, "addr")) { err = og_json_parse_clients(value, params); } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ADDR | OG_REST_PARAM_NAME)) return -1; if (og_repo_list_find_ips(params->ips_array, params->ips_array_len, 0)) { syslog(LOG_ERR, "Repository IP already in use, cannot define overlapping\n"); return -1; } dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "INSERT INTO repositorios(" "nombrerepositorio, ip, idcentro, grupoid, alias) VALUES " "('%s', '%s', 1, 0, NULL);", name, params->ips_array[0]); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to add repository to database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); return -1; } dbi_result_free(result); id = dbi_conn_sequence_last(dbi->conn, NULL); for (i = 1; i < params->ips_array_len; i++) { result = dbi_conn_queryf(dbi->conn, "INSERT INTO repositorios(" "nombrerepositorio, ip, idcentro, grupoid, alias) VALUES " "('%s', '%s', 1, 0, %u);", name, params->ips_array[i], id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to add repository to database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); return -1; } dbi_result_free(result); } og_dbi_close(dbi); return 0; } static int og_cmd_post_repository_delete(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *key, *msglog; struct og_dbi *dbi; dbi_result result; uint32_t repo_id; json_t *value; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "id")) { err = og_json_parse_uint(value, &repo_id); params->flags |= OG_REST_PARAM_ID; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ID)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "DELETE FROM repositorios " "WHERE idrepositorio=%u OR alias=%u", repo_id, repo_id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to delete repository from database " "(%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); return -1; } if (dbi_result_get_numrows_affected(result) < 1) { syslog(LOG_ERR, "delete did not modify any row (%s:%d)\n", __func__, __LINE__); dbi_result_free(result); og_dbi_close(dbi); return -1; } dbi_result_free(result); og_dbi_close(dbi); return 0; } static int og_cmd_post_room_update(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; struct og_room room = {}; const char *key, *msglog; struct og_dbi *dbi; dbi_result result; json_t *value; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "name")) { err = og_json_parse_string_copy(value, room.name, sizeof(room.name)); params->flags |= OG_REST_PARAM_NAME; } else if (!strcmp(key, "gateway")) { err = og_json_parse_string_copy(value, room.gateway, sizeof(room.gateway)); params->flags |= OG_REST_PARAM_GATEWAY; } else if (!strcmp(key, "netmask")) { err = og_json_parse_string_copy(value, room.netmask, sizeof(room.netmask)); params->flags |= OG_REST_PARAM_NETMASK; } else if (!strcmp(key, "id")) { err = og_json_parse_uint(value, &room.id); params->flags |= OG_REST_PARAM_ROOM; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ROOM | OG_REST_PARAM_NAME | OG_REST_PARAM_NETMASK | OG_REST_PARAM_GATEWAY)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT idaula, idcentro FROM aulas WHERE idaula='%u'", room.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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (!dbi_result_next_row(result)) { syslog(LOG_ERR, "could not find a room with that id: %u\n", room.id); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_404_NOT_FOUND; return -1; } room.center = dbi_result_get_uint(result, "idcentro"); dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "SELECT nombreaula FROM aulas " "WHERE nombreaula='%s' AND idcentro=%d AND idaula<>'%u'", room.name, room.center, room.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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (dbi_result_get_numrows(result) > 0) { syslog(LOG_ERR, "Room with name %s already exists in the " "center with id %d\n", room.name, room.center); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_404_NOT_FOUND; return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "UPDATE aulas" " SET nombreaula='%s'," " netmask='%s'," " router='%s'" " WHERE idaula='%u';", room.name, room.netmask, room.gateway, room.id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to update room in database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } dbi_result_free(result); return 0; } static int og_cmd_post_room_add(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; struct og_room room = {}; const char *key, *msglog; struct og_dbi *dbi; dbi_result result; json_t *value; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "name")) { err = og_json_parse_string_copy(value, room.name, sizeof(room.name)); params->flags |= OG_REST_PARAM_NAME; } else if (!strcmp(key, "location")) { err = og_json_parse_string_copy(value, room.location, sizeof(room.location)); } else if (!strcmp(key, "gateway")) { err = og_json_parse_string_copy(value, room.gateway, sizeof(room.gateway)); } else if (!strcmp(key, "netmask")) { err = og_json_parse_string_copy(value, room.netmask, sizeof(room.netmask)); params->flags |= OG_REST_PARAM_NETMASK; } else if (!strcmp(key, "ntp")) { err = og_json_parse_string_copy(value, room.ntp, sizeof(room.ntp)); } else if (!strcmp(key, "dns")) { err = og_json_parse_string_copy(value, room.dns, sizeof(room.dns)); } else if (!strcmp(key, "center")) { err = og_json_parse_uint(value, &room.center); params->flags |= OG_REST_PARAM_CENTER; } else if (!strcmp(key, "folder_id")) { err = og_json_parse_uint(value, &room.folder_id); } else if (!strcmp(key, "remote")) { err = og_json_parse_bool(value, &room.remote); } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_NAME | OG_REST_PARAM_NETMASK | OG_REST_PARAM_CENTER)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT nombreaula FROM aulas " "WHERE nombreaula='%s' AND idcentro=%d", room.name, room.center); 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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (dbi_result_get_numrows(result) > 0) { syslog(LOG_ERR, "Room with name %s already exists in the " "center with id %d\n", room.name, room.center); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_409_CONFLICT; return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "INSERT INTO aulas(" " idcentro," " nombreaula," " netmask," " grupoid," " ubicacion," " router," " dns," " ntp," " inremotepc) VALUES (" "%d, '%s', '%s', %d, '%s', " "'%s', '%s', '%s', %d)", room.center, room.name, room.netmask, room.folder_id, room.location, room.gateway, room.dns, room.ntp, room.remote); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to add room to database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } dbi_result_free(result); og_dbi_close(dbi); return 0; } static int og_cmd_post_room_delete(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *key, *msglog; struct og_dbi *dbi; dbi_result result; json_t *value; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "id")) { err = og_json_parse_string(value, ¶ms->id); params->flags |= OG_REST_PARAM_ID; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ID)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "DELETE FROM aulas WHERE idaula=%s", params->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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (dbi_result_get_numrows_affected(result) < 1) { syslog(LOG_ERR, "delete did not modify any row (%s:%d)\n", __func__, __LINE__); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_404_NOT_FOUND; return -1; } dbi_result_free(result); og_dbi_close(dbi); return 0; } static int og_cmd_get_servers(struct og_rest_ctx *ctx) { json_t *root, *servers, *address; struct og_buffer og_buffer = { .data = ctx->buf_reply, }; char ipaddr[50] = {}; struct og_dbi *dbi; dbi_result result; int id, ret; root = json_object(); if (!root) return -1; servers = json_array(); if (!servers) { json_decref(root); return -1; } dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); json_decref(root); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT identorno, ipserveradm " "FROM entornos"); while (dbi_result_next_row(result)) { address = json_object(); id = dbi_result_get_int(result, "identorno"); snprintf(ipaddr, sizeof(ipaddr), "%s", dbi_result_get_string(result, "ipserveradm")); json_object_set_new(address, "id", json_integer(id)); json_object_set_new(address, "address", json_string(ipaddr)); json_array_append_new(servers, address); } json_object_set_new(root, "servers", servers); dbi_result_free(result); og_dbi_close(dbi); ret = json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII); json_decref(root); return ret; } static int og_cmd_post_server(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; struct og_buffer og_buffer = { .data = ctx->buf_reply, }; const char *key, *msglog; json_t *value, *root; int err = 0, id, ret; struct in_addr addr; struct og_dbi *dbi; dbi_result result; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "address")) { err = og_json_parse_string(value, ¶ms->name); params->flags |= OG_REST_PARAM_NAME; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_NAME)) return -1; if (inet_pton(AF_INET, params->name, &addr) <= 0) { syslog(LOG_ERR, "invalid server ip address (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT identorno FROM entornos WHERE ipserveradm='%s'", params->name); 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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (dbi_result_get_numrows(result) > 0) { syslog(LOG_ERR, "Address %s already exists\n", params->name); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_409_CONFLICT; return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "INSERT INTO entornos(" " ipserveradm," " protoclonacion) VALUES (" "'%s', 'UNICAST')", params->name); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to add new ogserver address to database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } root = json_object(); if (!root) { syslog(LOG_ERR, "Cannot allocate JSON object (%s:%d)\n", __func__, __LINE__); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } id = dbi_conn_sequence_last(dbi->conn, NULL); json_object_set_new(root, "id", json_integer(id)); ret = json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII); json_decref(root); dbi_result_free(result); og_dbi_close(dbi); return ret; } static int og_cmd_delete_server(struct og_rest_ctx *ctx) { struct og_msg_params *params = &ctx->params; const char *key, *msglog; struct og_dbi *dbi; dbi_result result; json_t *value; uint32_t id; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "id")) { err = og_json_parse_uint(value, &id); params->flags |= OG_REST_PARAM_ID; } if (err < 0) return err; } if (!og_msg_params_validate(params, OG_REST_PARAM_ID)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open conection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "DELETE FROM entornos WHERE identorno='%u'", id); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to delete server (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (dbi_result_get_numrows_affected(result) < 1) { syslog(LOG_ERR, "delete did not modify any row (%s:%d)\n", __func__, __LINE__); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_404_NOT_FOUND; return -1; } dbi_result_free(result); og_dbi_close(dbi); return 0; } static int og_dbi_update_oglive(struct og_dbi *dbi, const char *mac, const char * oglive) { const char *msglog; dbi_result result; result = dbi_conn_queryf(dbi->conn, "UPDATE ordenadores SET oglivedir='%s' " "WHERE mac='%s'", oglive, mac); 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_cmd_oglive_set(struct og_rest_ctx *ctx) { const char legacy_default_oglive_str[] = "ogLive"; struct og_msg_params *params = &ctx->params; const char *oglive_str, *mac, *mode_str; const char *msglog; struct og_dbi *dbi; uint64_t flags = 0; dbi_result result; const char *key; json_t *value; int err = 0; int i; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "clients")) { err = og_json_parse_clients(value, params); } else if (!strcmp(key, "name")) { err = og_json_parse_string(value, &oglive_str); flags |= OG_REST_PARAM_NAME; } else { err = -1; } if (err < 0) return err; } if (!og_flags_validate(flags, OG_REST_PARAM_NAME) || !og_msg_params_validate(params, OG_REST_PARAM_ADDR)) return -1; if (!strcmp(oglive_str, "default")) oglive_str = legacy_default_oglive_str; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } for (i = 0; i < params->ips_array_len; i++) { result = dbi_conn_queryf(dbi->conn, "SELECT mac, arranque FROM ordenadores " "WHERE ip = '%s'", params->ips_array[i]); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); og_dbi_close(dbi); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (!dbi_result_next_row(result)) { syslog(LOG_ERR, "could not find client IP: %s (%s:%d)\n", params->ips_array[i], __func__, __LINE__); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_404_NOT_FOUND; return -1; } mode_str = dbi_result_get_string(result, "arranque"); mac = dbi_result_get_string(result, "mac"); err = og_dbi_update_oglive(dbi, mac, oglive_str); if (err < 0) { syslog(LOG_ERR, "failed to change db oglive (%s:%d)\n", __func__, __LINE__); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } err = og_set_client_mode(dbi, params->ips_array[i], mac, mode_str); if (err < 0) { dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } dbi_result_free(result); } og_dbi_close(dbi); return 0; } static int og_cmd_oglive_add(struct og_rest_ctx *ctx) { const char *oglive_str; bool is_update = false; const char *msglog; struct og_dbi *dbi; uint64_t flags = 0; dbi_result result; const char *key; json_t *value; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "name")) { err = og_json_parse_string(value, &oglive_str); flags |= OG_REST_PARAM_NAME; } else { err = -1; } if (err < 0) return err; } if (!og_flags_validate(flags, OG_REST_PARAM_NAME)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT name FROM oglive WHERE name = '%s'", oglive_str); 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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (dbi_result_next_row(result)) { is_update = true; syslog(LOG_INFO, "Found oglive '%s' in database, updating entry", oglive_str); } dbi_result_free(result); if (is_update) result = dbi_conn_queryf(dbi->conn, "UPDATE oglive SET creation_date = NOW() " "WHERE name = '%s'", oglive_str); else result = dbi_conn_queryf(dbi->conn, "INSERT INTO oglive (name, creation_date) " "VALUES ('%s', NOW())", oglive_str); 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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } dbi_result_free(result); og_dbi_close(dbi); return 0; } static int og_cmd_oglive_delete(struct og_rest_ctx *ctx) { const char *oglive_str; const char *msglog; struct og_dbi *dbi; uint64_t flags = 0; dbi_result result; const char *key; json_t *value; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "name")) { err = og_json_parse_string(value, &oglive_str); flags |= OG_REST_PARAM_NAME; } else { err = -1; } if (err < 0) return err; } if (!og_flags_validate(flags, OG_REST_PARAM_NAME)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT oglivedir FROM ordenadores WHERE oglivedir = '%s'", oglive_str); 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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (dbi_result_next_row(result)) { syslog(LOG_ERR, "failed to delete oglive '%s', live in use by a client", oglive_str); dbi_result_free(result); og_dbi_close(dbi); ctx->http_error = OG_HTTP_423_LOCKED; return -1; } dbi_result_free(result); result = dbi_conn_queryf(dbi->conn, "DELETE FROM oglive WHERE name = '%s'", oglive_str); 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); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } if (dbi_result_get_numrows_affected(result) == 0) syslog(LOG_WARNING, "No live entries found with name %s in database ", oglive_str); dbi_result_free(result); og_dbi_close(dbi); return 0; } static int update_ogliveinfo_default(const char *oglive_str) { char buf_data[OG_MSG_RESPONSE_MAXLEN]; struct og_buffer og_buffer = { .data = buf_data }; json_t *root, *oglive_array, *default_entry, *live_entry, *dir_entry; char tmp_filename[] = "/tmp/ogliveinfo_XXXXXX"; int default_idx, i, fd, ret; const char *live_name; json_error_t json_err; root = json_load_file(OG_LIVE_JSON_FILE_PATH, 0, &json_err); if (!root) { syslog(LOG_ERR, "malformed json line %d: %s\n", json_err.line, json_err.text); return -1; } oglive_array = json_object_get(root, "oglive"); if (!oglive_array || !json_is_array(oglive_array)) { json_decref(root); return -1; } default_entry = json_object_get(root, "default"); if (!default_entry || json_typeof(default_entry) != JSON_INTEGER) { json_decref(root); return -1; } default_idx = -1; for (i = 0; i < json_array_size(oglive_array); i++) { live_entry = json_array_get(oglive_array, i); if (json_typeof(live_entry) != JSON_OBJECT) { json_decref(root); return -1; } dir_entry = json_object_get(live_entry, "directory"); if (!dir_entry || json_typeof(dir_entry) != JSON_STRING) { json_decref(root); return -1; } live_name = json_string_value(dir_entry); if (!strncmp(oglive_str, live_name, strlen(live_name))) { default_idx = i; break; } } if (default_idx < 0) { json_decref(root); return -1; } if (json_integer_set(default_entry, default_idx) < 0) { syslog(LOG_ERR, "Failed to modify 'default' entry value\n"); json_decref(root); return -1; } if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_INDENT(2) | JSON_PRESERVE_ORDER)) { json_decref(root); return -1; } json_decref(root); fd = mkstemp(tmp_filename); if (fd < 0) { syslog(LOG_ERR, "cannot generate temp file %s: %s (%s:%d)\n", tmp_filename, strerror(errno), __func__, __LINE__); return -1; } ret = write(fd, og_buffer.data, og_buffer.len); if (ret < 0) { syslog(LOG_ERR, "cannot write to temporary file %s: %s (%s:%d)\n", tmp_filename, strerror(errno), __func__, __LINE__); return -1; } ret = write(fd, "\n", 1); if (ret < 0) { syslog(LOG_ERR, "cannot write to temporary file %s: %s (%s:%d)\n", tmp_filename, strerror(errno), __func__, __LINE__); return -1; } close(fd); if (ret < 0) { syslog(LOG_ERR, "cannot write file %s: %s (%s:%d)\n", tmp_filename, strerror(errno), __func__, __LINE__); unlink(tmp_filename); return -1; } if (rename(tmp_filename, OG_LIVE_JSON_FILE_PATH) < 0) { syslog(LOG_ERR, "cannot rename file %s to %s: %s (%s:%d)\n", tmp_filename, OG_LIVE_JSON_FILE_PATH, strerror(errno), __func__, __LINE__); unlink(tmp_filename); return -1; } return 0; } static int og_cmd_oglive_set_default(struct og_rest_ctx *ctx) { const char *oglive_str; uint64_t flags = 0; const char *msglog; struct og_dbi *dbi; dbi_result result; bool db_has_live; const char *key; json_t *value; int priority; int err = 0; json_object_foreach(ctx->json, key, value) { if (!strcmp(key, "name")) { err = og_json_parse_string(value, &oglive_str); flags |= OG_REST_PARAM_NAME; } else { err = -1; } if (err < 0) return err; } if (!og_flags_validate(flags, OG_REST_PARAM_NAME)) return -1; dbi = og_dbi_open(&ogconfig.db); if (!dbi) { syslog(LOG_ERR, "cannot open connection database (%s:%d)\n", __func__, __LINE__); ctx->http_error = OG_HTTP_501_SERVICE_UNAVAILABLE; return -1; } result = dbi_conn_queryf(dbi->conn, "SELECT name FROM oglive WHERE name = '%s'", oglive_str); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "Failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; og_dbi_close(dbi); return -1; } db_has_live = dbi_result_next_row(result) > 0; dbi_result_free(result); priority = 1; if (db_has_live) { result = dbi_conn_queryf(dbi->conn, "UPDATE oglive SET priority = %d " "WHERE name = '%s'", priority, oglive_str); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; og_dbi_close(dbi); return -1; } dbi_result_free(result); } else if (update_ogliveinfo_default(oglive_str) < 0) { syslog(LOG_ERR, "No live entries found with name %s", oglive_str); ctx->http_error = OG_HTTP_404_NOT_FOUND; og_dbi_close(dbi); return -1; } result = dbi_conn_queryf(dbi->conn, "UPDATE oglive SET priority = 0 " "WHERE priority != 0 AND name != '%s'", oglive_str); if (!result) { dbi_conn_error(dbi->conn, &msglog); syslog(LOG_ERR, "failed to query database (%s:%d) %s\n", __func__, __LINE__, msglog); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; og_dbi_close(dbi); return -1; } dbi_result_free(result); og_dbi_close(dbi); return 0; } static int og_cmd_get_server_stats(struct og_rest_ctx *ctx) { json_t *root, *time_obj, *memory, *swap; struct og_buffer og_buffer = { .data = ctx->buf_reply }; struct sysinfo stats; time_t now; sysinfo(&stats); root = json_object(); if (!root) return -1; time_obj = json_object(); if (!time_obj) { json_decref(root); return -1; } memory = json_object(); if (!memory) { json_decref(root); json_decref(time_obj); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } swap = json_object(); if (!swap) { json_decref(root); json_decref(time_obj); json_decref(memory); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } now = time(NULL); json_object_set_new(time_obj, "now", json_integer(now)); json_object_set_new(time_obj, "boot", json_integer(stats.uptime)); json_object_set_new(time_obj, "start", json_integer(now - start_time)); json_object_set_new(root, "time", time_obj); json_object_set_new(memory, "size", json_integer(stats.totalram)); json_object_set_new(memory, "free", json_integer(stats.freeram)); json_object_set_new(root, "memory", memory); json_object_set_new(swap, "size", json_integer(stats.totalswap)); json_object_set_new(swap, "free", json_integer(stats.freeswap)); json_object_set_new(root, "swap", swap); if (json_dump_callback(root, og_json_dump_clients, &og_buffer, JSON_ENSURE_ASCII)) { json_decref(root); ctx->http_error = OG_HTTP_500_INTERNAL_SERVER_ERROR; return -1; } json_decref(root); return 0; } 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_conflict(struct og_client *cli) { char buf[] = "HTTP/1.1 409 Conflict\r\nContent-Length: 0\r\n\r\n"; send(og_client_socket(cli), buf, strlen(buf), 0); return -1; } static int og_client_locked(struct og_client *cli) { char buf[] = "HTTP/1.1 423 Locked\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; } static int og_server_service_unavailable(struct og_client *cli) { char buf[] = "HTTP/1.1 501 Service Unavailable\r\n" "Content-Length: 0\r\n\r\n"; send(og_client_socket(cli), buf, strlen(buf), 0); return -1; } static int og_server_insufficient_storage(struct og_client *cli) { char buf[] = "HTTP/1.1 507 Insufficient Storage\r\n" "Content-Length: 0\r\n\r\n"; send(og_client_socket(cli), buf, strlen(buf), 0); return -1; } static int og_client_ok(struct og_client *cli, char *buf_reply) { char buf[OG_MSG_RESPONSE_MAXLEN] = {}; int 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)) { syslog(LOG_ERR, "HTTP response to %s:%hu is too large\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port)); return og_server_internal_error(cli); } send(og_client_socket(cli), buf, strlen(buf), 0); return 0; } static const char *og_method_str[] = { [OG_METHOD_GET] = "GET", [OG_METHOD_POST] = "POST", [OG_METHOD_DELETE] = "DELETE", [OG_METHOD_NO_HTTP] = "NOHTTP", }; static const char *og_method(enum og_rest_method method) { if (method > OG_METHOD_NO_HTTP) return "UNKNOWN"; return og_method_str[method]; } struct { const char *uri; } og_uri_handler[] = { [OG_URI_UNKNOWN] = { "unknown", }, [OG_URI_CLIENTS] = { "clients", }, [OG_URI_CLIENT_REPO] = { "client/repo", }, [OG_URI_CLIENT_SETUP] = { "client/setup", }, [OG_URI_CLIENT_SERVER] = { "client/server", }, [OG_URI_CLIENT_INFO] = { "client/info", }, [OG_URI_CLIENT_ADD] = { "client/add", }, [OG_URI_CLIENT_UPDATE] = { "client/update", }, [OG_URI_CLIENT_DELETE] = { "client/delete", }, [OG_URI_CLIENT_MOVE] = { "client/move", }, [OG_URI_WOL] = { "wol", }, [OG_URI_SHELL_RUN] = { "shell/run", }, [OG_URI_SHELL_OUTPUT] = { "shell/output", }, [OG_URI_SHELL_LIST] = { "shell/list", }, [OG_URI_SESSION] = { "session", }, [OG_URI_SCOPES] = { "scopes", }, [OG_URI_POWEROFF] = { "poweroff", }, [OG_URI_REBOOT] = { "reboot", }, [OG_URI_BOOT_MODE] = { "mode", }, [OG_URI_STOP] = { "stop", }, [OG_URI_REFRESH] = { "refresh", }, [OG_URI_HARDWARE] = { "hardware", }, [OG_URI_SOFTWARE] = { "software", }, [OG_URI_REPO] = { "repositories", }, [OG_URI_REPO_ADD] = { "repository/add", }, [OG_URI_REPO_UPDATE] = { "repository/update", }, [OG_URI_REPO_DELETE] = { "repository/delete", }, [OG_URI_IMAGES] = { "images", }, [OG_URI_IMAGE_CREATE] = { "image/create" }, [OG_URI_IMAGE_UPDATE] = { "image/update" }, [OG_URI_IMAGE_RESTORE] = { "image/restore", }, [OG_URI_IMAGE_DELETE] = { "image/delete", }, [OG_URI_IMAGE_RESTRICT] = { "image/restrict", }, [OG_URI_CACHE_LIST] = { "cache/list", }, [OG_URI_CACHE_DELETE] = { "cache/delete", }, [OG_URI_CACHE_FETCH] = { "cache/fetch", }, [OG_URI_EFI] = { "efi", }, [OG_URI_PART_SETUP] = { "setup", }, [OG_URI_OGLIVE_LIST] = { "oglive/list", }, [OG_URI_OGLIVE_ADD] = { "oglive/add", }, [OG_URI_OGLIVE_DELETE] = { "oglive/delete", }, [OG_URI_OGLIVE_SET] = { "oglive/set", }, [OG_URI_OGLIVE_DEFAULT] = { "oglive/default", }, [OG_URI_CENTER_ADD] = { "center/add", }, [OG_URI_CENTER_UPDATE] = { "center/update", }, [OG_URI_CENTER_DELETE] = { "center/delete", }, [OG_URI_CENTER_INFO] = { "center/info", }, [OG_URI_ROOM_ADD] = { "room/add", }, [OG_URI_ROOM_UPDATE] = { "room/update", }, [OG_URI_ROOM_DELETE] = { "room/delete", }, [OG_URI_ROOM_INFO] = { "room/info", }, [OG_URI_SERVER] = { "server", }, [OG_URI_STATS] = { "stats", }, [OG_URI_FOLDER_ADD] = { "folder/add", }, [OG_URI_FOLDER_UPDATE] = { "folder/update", }, [OG_URI_FOLDER_DELETE] = { "folder/delete", }, }; static const char *og_uri(enum og_rest_uri uri) { if (uri >= OG_URI_MAX) return "unknown"; return og_uri_handler[uri].uri; } static enum og_rest_uri og_str_to_uri(const char *uri) { int i; for (i = 0; i < OG_URI_MAX; i++) { if (!strncmp(og_uri_handler[i].uri, uri, strlen(og_uri_handler[i].uri))) return i; } return OG_URI_UNKNOWN; } static void og_rest_log(const struct og_client *cli, enum og_rest_method method, enum og_rest_uri uri, const struct og_msg_params *params) { char log_buf[(16 * OG_CLIENTS_MAX) + 4096] = {}; int i, ret; switch (uri) { case OG_URI_SCOPES: case OG_URI_CLIENTS: /* very spammy, do not log these. */ return; default: break; } ret = snprintf(log_buf, sizeof(log_buf), "%s:%hu %s /%s ", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port), og_method(method), og_uri(uri)); if (params->ips_array_len > 0) { ret += snprintf(&log_buf[ret], sizeof(log_buf) - ret, "clients="); if (ret > sizeof(log_buf)) return; for (i = 0; i < params->ips_array_len - 1; i++) { ret += snprintf(&log_buf[ret], sizeof(log_buf) - ret, "%s,", params->ips_array[i]); if (ret > sizeof(log_buf)) return; } ret += snprintf(&log_buf[ret], sizeof(log_buf) - ret, "%s", params->ips_array[i]); if (ret > sizeof(log_buf)) return; } syslog(LOG_INFO, "%s", log_buf); } static int og_client_error(struct og_client *cli, enum og_http_error error) { switch (error) { case OG_HTTP_400_BAD_REQUEST: return og_client_bad_request(cli); case OG_HTTP_404_NOT_FOUND: return og_client_not_found(cli); case OG_HTTP_405_METHOD_NOT_ALLOWED: return og_client_method_not_found(cli); case OG_HTTP_409_CONFLICT: return og_client_conflict(cli); case OG_HTTP_423_LOCKED: return og_client_locked(cli); case OG_HTTP_500_INTERNAL_SERVER_ERROR: return og_server_internal_error(cli); case OG_HTTP_501_SERVICE_UNAVAILABLE: return og_server_service_unavailable(cli); case OG_HTTP_507_INSUFFICIENT_STORAGE: return og_server_insufficient_storage(cli); default: break; } return og_client_bad_request(cli); } int og_client_state_process_payload_rest(struct og_client *cli) { struct og_rest_ctx ctx = {}; enum og_rest_method method; const char *cmd, *body; json_error_t json_err; enum og_rest_uri uri; json_t *root = NULL; int err = 0; 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 if (!strncmp(cli->buf, "DELETE", strlen("DELETE"))) { method = OG_METHOD_DELETE; cmd = cli->buf + strlen("DELETE") + 2; } else return og_client_method_not_found(cli); body = strstr(cli->buf, "\r\n\r\n") + 4; if (strcmp(cli->auth_token, ogconfig.rest.api_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); } ctx.json = root; } uri = og_str_to_uri(cmd); if (!strncmp(cmd, "clients", strlen("clients"))) { if (method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } switch (method) { case OG_METHOD_GET: err = og_cmd_get_clients(&ctx); break; default: ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } } else if (!strncmp(cmd, "client/repo", strlen("client/repo"))) { switch (method) { case OG_METHOD_POST: if (!root) { syslog(LOG_ERR, "client post repo command with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_client_repo(&ctx); break; default: ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } } else if (!strncmp(cmd, "client/setup", strlen("client/setup"))) { if (method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command client partitions with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_get_client_setup(&ctx); } else if (!strncmp(cmd, "client/info", strlen("client/info"))) { if (method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command client info with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_get_client_info(&ctx); } else if (!strncmp(cmd, "client/add", strlen("client/add"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command client info with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_client_add(&ctx); } else if (!strncmp(cmd, "client/update", strlen("client/update"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command client info with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_client_update(&ctx); } else if (!strncmp(cmd, "client/delete", strlen("client/delete"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command client delete with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_client_delete(&ctx); } else if (!strncmp(cmd, "client/move", strlen("client/move"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command client move with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_client_move(&ctx); } else if (!strncmp(cmd, "folder/add", strlen("folder/add"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command folder add with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_folder_add(&ctx); } else if (!strncmp(cmd, "folder/update", strlen("folder/update"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command folder update with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_folder_update(&ctx); } else if (!strncmp(cmd, "folder/delete", strlen("folder/delete"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command folder delete with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_folder_delete(&ctx); } else if (!strncmp(cmd, "wol", strlen("wol"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command wol with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_wol(&ctx); } else if (!strncmp(cmd, "shell/run", strlen("shell/run"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command run with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_run_post(&ctx); } else if (!strncmp(cmd, "shell/output", strlen("shell/output"))) { if (method != OG_METHOD_POST && method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command output with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_run_get(&ctx); } else if (!strncmp(cmd, "shell/list", strlen("shell/list"))) { if (method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } err = og_cmd_shell_list(&ctx); } else if (!strncmp(cmd, "session", strlen("session"))) { if (method != OG_METHOD_POST && method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command session with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } if (method == OG_METHOD_POST) err = og_cmd_session(&ctx); else err = og_cmd_get_session(&ctx); } else if (!strncmp(cmd, "scopes", strlen("scopes"))) { if (method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (root) { syslog(LOG_ERR, "command scopes with payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_scope_get(&ctx); } else if (!strncmp(cmd, "poweroff", strlen("poweroff"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command poweroff with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_poweroff(&ctx); } else if (!strncmp(cmd, "reboot", strlen("reboot"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command reboot with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_reboot(&ctx); } else if (!strncmp(cmd, "mode", strlen("mode"))) { if (method != OG_METHOD_GET && method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (method == OG_METHOD_POST && !root) { syslog(LOG_ERR, "command mode with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } if (method == OG_METHOD_GET) err = og_cmd_get_modes(&ctx); else if (method == OG_METHOD_POST) err = og_cmd_post_modes(&ctx); } else if (!strncmp(cmd, "stop", strlen("stop"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command stop with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_stop(&ctx); } else if (!strncmp(cmd, "refresh", strlen("refresh"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command refresh with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_refresh(&ctx); } else if (!strncmp(cmd, "hardware", strlen("hardware"))) { if (method != OG_METHOD_GET && method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command hardware with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } if (method == OG_METHOD_GET) err = og_cmd_get_hardware(&ctx); else if (method == OG_METHOD_POST) err = og_cmd_hardware(&ctx); } else if (!strncmp(cmd, "software", strlen("software"))) { if (method != OG_METHOD_POST && method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command software with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } if (method == OG_METHOD_POST) err = og_cmd_software(&ctx); else err = og_cmd_get_software(&ctx); } else if (!strncmp(cmd, "repositories", strlen("repositories"))) { if (method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (root) { ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_get_repositories(&ctx); } else if (!strncmp(cmd, "repository/add", strlen("repository/add"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command repository add with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_repository_add(&ctx); } else if (!strncmp(cmd, "repository/update", strlen("repository/update"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command repository add with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_repository_update(&ctx); } else if (!strncmp(cmd, "repository/delete", strlen("repository/delete"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command repository delete with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_repository_delete(&ctx); } else if (!strncmp(cmd, "images", strlen("images"))) { if (method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (root) { ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_images(&ctx); } else if (!strncmp(cmd, "image/create", strlen("image/create"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command create with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_create_image(&ctx); } else if (!strncmp(cmd, "image/update", strlen("image/update"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command create with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_update_image(&ctx); } else if (!strncmp(cmd, "image/restore", strlen("image/restore"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command create with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_restore_image(&ctx); } else if (!strncmp(cmd, "image/delete", strlen("image/delete"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command image delete with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_delete_image(&ctx); } else if (!strncmp(cmd, "image/restrict", strlen("image/restrict"))) { if (!root) { ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } switch (method) { case OG_METHOD_GET: err = og_cmd_image_scope_list(&ctx); break; case OG_METHOD_POST: err = og_cmd_image_scope_update(&ctx); break; default: ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } } else if (!strncmp(cmd, "cache/list", strlen("cache/list"))) { if (method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command cache list with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_cache_list(&ctx); } else if (!strncmp(cmd, "efi", strlen("efi"))) { if (method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command efi with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_efi_list(&ctx); } else if (!strncmp(cmd, "cache/delete", strlen("cache/delete"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command cache list with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_cache_delete(&ctx); } else if (!strncmp(cmd, "cache/fetch", strlen("cache/fetch"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command cache fetch with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_cache_fetch(&ctx); } else if (!strncmp(cmd, "setup", strlen("setup"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command create with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_setup(&ctx); } else if (!strncmp(cmd, "oglive/list", strlen("oglive/list"))) { if (method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } err = og_cmd_oglive_list(&ctx); } else if (!strncmp(cmd, "oglive/add", strlen("oglive/add"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command oglive add with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_oglive_add(&ctx); } else if (!strncmp(cmd, "oglive/delete", strlen("oglive/delete"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command oglive delete with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_oglive_delete(&ctx); } else if (!strncmp(cmd, "oglive/set", strlen("oglive/set"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command oglive set with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_oglive_set(&ctx); } else if (!strncmp(cmd, "oglive/default", strlen("oglive/default"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command oglive default with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_oglive_set_default(&ctx); } else if (!strncmp(cmd, "center/add", strlen("center/add"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } err = og_cmd_post_center_add(&ctx); } else if (!strncmp(cmd, "center/delete", strlen("center/delete"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command center delete with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_center_delete(&ctx); } else if (!strncmp(cmd, "center/info", strlen("center/info"))) { if (method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command client info with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_get_center_info(&ctx); } else if (!strncmp(cmd, "center/update", strlen("center/update"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command center update with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_center_update(&ctx); } else if (!strncmp(cmd, "room/add", strlen("room/add"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command task with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_room_add(&ctx); } else if (!strncmp(cmd, "room/update", strlen("room/update"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command task with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_room_update(&ctx); } else if (!strncmp(cmd, "room/delete", strlen("room/delete"))) { if (method != OG_METHOD_POST) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command room delete with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_room_delete(&ctx); } else if (!strncmp(cmd, "room/info", strlen("room/info"))) { if (method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } if (!root) { syslog(LOG_ERR, "command room info with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_get_room_info(&ctx); } else if (!strncmp(cmd, "server", strlen("server"))) { switch (method) { case OG_METHOD_GET: err = og_cmd_get_servers(&ctx); break; case OG_METHOD_DELETE: err = og_cmd_delete_server(&ctx); break; case OG_METHOD_POST: if (!root) { syslog(LOG_ERR, "address add command with no payload\n"); ctx.http_error = OG_HTTP_400_BAD_REQUEST; goto err_process_rest_payload; } err = og_cmd_post_server(&ctx); break; default: ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } } else if (!strncmp(cmd, "stats", strlen("stats"))) { if (method != OG_METHOD_GET) { ctx.http_error = OG_HTTP_405_METHOD_NOT_ALLOWED; goto err_process_rest_payload; } err = og_cmd_get_server_stats(&ctx); } else { syslog(LOG_ERR, "unknown command: %.32s ...\n", cmd); ctx.http_error = OG_HTTP_404_NOT_FOUND; } og_rest_log(cli, method, uri, &ctx.params); json_decref(root); if (err < 0) return og_client_error(cli, ctx.http_error); return og_client_ok(cli, ctx.buf_reply); err_process_rest_payload: syslog(LOG_ERR, "%s:%hu %.32s ...\n", inet_ntoa(cli->addr.sin_addr), ntohs(cli->addr.sin_port), cli->buf); json_decref(root); return og_client_error(cli, ctx.http_error); }