summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlejandro Sirgo Rica <asirgo@soleta.eu>2024-09-06 12:25:01 +0200
committerAlejandro Sirgo Rica <asirgo@soleta.eu>2024-09-11 14:09:51 +0200
commite3a73a504f6a63bf1a716131cf9dea75dc7b0b41 (patch)
tree7f5c3d265e654221d4f655d90f2e2a65d0250774
parent1fa3813b92139458854f33436c3e4af2f8ecae6b (diff)
rest: add GET /efi
Add GET /efi request to obtain information about the client's boot entries. Field inside the /refresh payload 'efi': { 'entries': [ { "order": 0, "name": "Boot0000", "active": false, "description": "grub" }, { "order": 1, "name": "Boot0001", "active": true, "description": "UEFI: PXE IP4 Realtek PCIe GBE Family Controller" } ] } If the client is not a EFI system it won't add the 'efi' field. If an entry is not in the boot order it won't have the 'order' field. GET /efi resquest payload structure: { 'clients': ['10.141.10.21', '10.141.10.22'] } GET /efi response's structure: { 'clients': [ { 'ip': '10.141.10.21', 'entries': [ { "order": 0, "name": "Boot0000", "active": false, "description": "grub" }, { "order": 1, "name": "Boot0001", "active": true, "description": "UEFI: PXE IP4 Realtek PCIe GBE Family Controller" } ] }, { 'ip': '10.141.10.22', 'entries': [] } ] } The client with ip 10.141.10.22 is a BIOS system. If an entry does not appear in the boot order it won't have the 'order' field.
-rw-r--r--src/client.c50
-rw-r--r--src/json.c97
-rw-r--r--src/json.h19
-rw-r--r--src/rest.c151
-rw-r--r--src/rest.h1
-rw-r--r--src/schema.c37
6 files changed, 355 insertions, 0 deletions
diff --git a/src/client.c b/src/client.c
index 675be74..17bc8a7 100644
--- a/src/client.c
+++ b/src/client.c
@@ -435,6 +435,45 @@ static int og_update_cache_info(struct og_dbi *dbi, struct list_head *cache_list
return 0;
}
+static int og_update_boot_entries(struct og_dbi *dbi, struct list_head *boot_entry_list, int client_id)
+{
+ struct og_boot_entry *boot_entry;
+ const char *msglog;
+ dbi_result result;
+
+ /* Remove old boot entries */
+ result = dbi_conn_queryf(dbi->conn,
+ "DELETE FROM boot_entries WHERE client_id=%d;", client_id);
+
+ if (!result) {
+ dbi_conn_error(dbi->conn, &msglog);
+ syslog(LOG_ERR, "failed to query database (%s:%d) %s\n",
+ __func__, __LINE__, msglog);
+ return -1;
+ }
+ dbi_result_free(result);
+ /* Add new boot entries */
+ list_for_each_entry(boot_entry, boot_entry_list, list) {
+ result = dbi_conn_queryf(dbi->conn,
+ "INSERT INTO boot_entries (client_id, name, active, description, entry_order)"
+ "VALUES (%d, '%s', %d, '%s', %d)",
+ client_id,
+ boot_entry->name,
+ boot_entry->active ? 1 : 0,
+ boot_entry->description,
+ boot_entry->order == UINT64_MAX ? -1 : boot_entry->order);
+
+ 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_resp_update_cache(json_t *data, struct og_client *cli)
{
struct og_computer computer = {};
@@ -496,6 +535,7 @@ static int og_resp_refresh(json_t *data, struct og_client *cli)
struct og_computer computer = {};
json_t *value, *cache = NULL;
const char *status = NULL;
+ LIST_HEAD(boot_entry_list);
LIST_HEAD(cache_list);
char cfg[4096] = {};
struct og_dbi *dbi;
@@ -519,6 +559,8 @@ static int og_resp_refresh(json_t *data, struct og_client *cli)
err = og_json_parse_string(value, &status);
} else if (!strcmp(key, "link")) {
err = og_json_parse_uint(value, &link);
+ } else if (!strcmp(key, "efi")) {
+ err = og_json_parse_efi(value, &boot_entry_list);
} else if (!strcmp(key, "cache")) {
err = og_json_parse_cache(value, &cache_list);
cache = value;
@@ -595,6 +637,12 @@ static int og_resp_refresh(json_t *data, struct og_client *cli)
goto err_out;
}
+ res = og_update_boot_entries(dbi, &boot_entry_list, computer.id);
+ if (err < 0) {
+ og_dbi_close(dbi);
+ goto err_out;
+ }
+
res = actualizaConfiguracion(dbi, cfg, computer.id);
og_dbi_close(dbi);
@@ -611,9 +659,11 @@ static int og_resp_refresh(json_t *data, struct og_client *cli)
}
og_cache_image_free(&cache_list);
+ og_boot_entry_free(&boot_entry_list);
return 0;
err_out:
og_cache_image_free(&cache_list);
+ og_boot_entry_free(&boot_entry_list);
return -1;
}
diff --git a/src/json.c b/src/json.c
index 11e66eb..638b1cb 100644
--- a/src/json.c
+++ b/src/json.c
@@ -303,3 +303,100 @@ int og_json_parse_cache(json_t *element, struct list_head *cache_list)
return err;
}
+
+void og_boot_entry_free(struct list_head *boot_entry_list)
+{
+ struct og_boot_entry *boot_entry, *tmp;
+
+ list_for_each_entry_safe(boot_entry, tmp, boot_entry_list, list) {
+ list_del(&boot_entry->list);
+ free(boot_entry);
+ }
+}
+
+int og_json_parse_boot_entries(json_t *json_entries, struct list_head *boot_entry_list)
+{
+ uint64_t required_flags = OG_PARAM_EFI_NAME | OG_PARAM_EFI_DESCRIPTION | OG_PARAM_EFI_ACTIVE;
+ struct og_boot_entry *boot_entry;
+ json_t *json_elem, *value;
+ uint64_t flags = 0UL;
+ unsigned long i;
+ const char *key;
+ int err = 0;
+
+ if (json_typeof(json_entries) != JSON_ARRAY)
+ return -1;
+
+ for (i = 0; i < json_array_size(json_entries); i++) {
+ boot_entry = calloc(1, sizeof(struct og_boot_entry));
+ boot_entry->order = UINT64_MAX;
+ flags = 0UL;
+
+ if (!boot_entry)
+ return -1;
+
+ json_elem = json_array_get(json_entries, i);
+
+ if (json_typeof(json_elem) != JSON_OBJECT) {
+ free(boot_entry);
+ return -1;
+ }
+
+ json_object_foreach(json_elem, key, value) {
+ if (!strcmp(key, "name")) {
+ err = og_json_parse_string(value, &boot_entry->name);
+ flags |= OG_PARAM_EFI_NAME;
+ } else if (!strcmp(key, "active")) {
+ err = og_json_parse_bool(value, &boot_entry->active);
+ flags |= OG_PARAM_EFI_ACTIVE;
+ } else if (!strcmp(key, "description")) {
+ err = og_json_parse_string(value, &boot_entry->description);
+ flags |= OG_PARAM_EFI_DESCRIPTION;
+ } else if (!strcmp(key, "order")) {
+ err = og_json_parse_uint64(value, &boot_entry->order);
+ } else
+ err = -1;
+
+ if (err < 0) {
+ free(boot_entry);
+ return err;
+ }
+ }
+
+ if ((flags & required_flags) != required_flags) {
+ free(boot_entry);
+ return -1;
+ }
+
+ list_add_tail(&boot_entry->list, boot_entry_list);
+ }
+ return err;
+}
+
+int og_json_parse_efi(json_t *element, struct list_head *boot_entry_list)
+{
+ uint64_t required_flags = OG_PARAM_EFI_ENTRIES;
+ uint64_t flags = 0UL;
+ const char *key;
+ json_t *value;
+ int err = 0;
+
+ if (json_typeof(element) != JSON_OBJECT)
+ return -1;
+
+ json_object_foreach(element, key, value) {
+ if (!strcmp(key, "entries")) {
+ err = og_json_parse_boot_entries(value, boot_entry_list);
+ flags |= OG_PARAM_EFI_ENTRIES;
+ } else
+ err = -1;
+
+ if (err < 0)
+ return err;
+ }
+
+ if ((flags & required_flags) != required_flags)
+ return -1;
+
+ return err;
+}
diff --git a/src/json.h b/src/json.h
index d3838cf..421f88d 100644
--- a/src/json.h
+++ b/src/json.h
@@ -2,6 +2,7 @@
#define _OG_JSON_H
#include <jansson.h>
+#include <stdint.h>
#include "schedule.h"
int og_json_parse_string(const json_t *element, const char **str);
@@ -146,4 +147,22 @@ struct og_cache_image {
void og_cache_image_free(struct list_head *cache_list);
int og_json_parse_cache(json_t *element, struct list_head *cache_list);
+
+#define OG_PARAM_EFI_ENTRIES (1UL << 0)
+#define OG_PARAM_EFI_ORDER (1UL << 1)
+#define OG_PARAM_EFI_NAME (1UL << 2)
+#define OG_PARAM_EFI_DESCRIPTION (1UL << 3)
+#define OG_PARAM_EFI_ACTIVE (1UL << 4)
+
+struct og_boot_entry {
+ const char *description;
+ const char *name;
+ bool active;
+ uint64_t order;
+ struct list_head list;
+};
+
+void og_boot_entry_free(struct list_head *boot_entry_list);
+int og_json_parse_efi(json_t *element, struct list_head *boot_entry_list);
+
#endif
diff --git a/src/rest.c b/src/rest.c
index 1894111..99fbe1f 100644
--- a/src/rest.c
+++ b/src/rest.c
@@ -4337,6 +4337,143 @@ static int og_cmd_cache_list(json_t *element, struct og_msg_params *params,
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(json_t *element, struct og_msg_params *params,
+ char *buffer_reply)
+{
+ json_t *value, *root, *clients;
+ struct og_dbi *dbi;
+ const char *key;
+ int err = 0, i;
+
+ struct og_buffer og_buffer = {
+ .data = buffer_reply
+ };
+
+ if (json_typeof(element) != JSON_OBJECT)
+ return -1;
+
+ json_object_foreach(element, key, value) {
+ if (!strcmp(key, "clients"))
+ err = og_json_parse_clients(value, params);
+ else
+ 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);
+ 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(json_t *element, struct og_msg_params *params,
char *buffer_reply)
{
@@ -8317,6 +8454,7 @@ struct {
[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_RUN_SCHEDULE] = { "run/schedule", },
[OG_URI_TASK_RUN] = { "task/run", },
@@ -8928,6 +9066,19 @@ int og_client_state_process_payload_rest(struct og_client *cli)
goto err_process_rest_payload;
}
err = og_cmd_cache_list(root, &params, buf_reply);
+ } else if (!strncmp(cmd, "efi", strlen("efi"))) {
+ if (method != OG_METHOD_GET) {
+ err = og_client_method_not_found(cli);
+ goto err_process_rest_payload;
+ }
+
+ if (!root) {
+ syslog(LOG_ERR,
+ "command efi with no payload\n");
+ err = og_client_bad_request(cli);
+ goto err_process_rest_payload;
+ }
+ err = og_cmd_efi_list(root, &params, buf_reply);
} else if (!strncmp(cmd, "cache/delete", strlen("cache/delete"))) {
if (method != OG_METHOD_POST) {
err = og_client_method_not_found(cli);
diff --git a/src/rest.h b/src/rest.h
index fea0849..28ccfda 100644
--- a/src/rest.h
+++ b/src/rest.h
@@ -128,6 +128,7 @@ enum og_rest_uri {
OG_URI_IMAGE_DELETE,
OG_URI_IMAGE_RESTRICT,
OG_URI_CACHE_LIST,
+ OG_URI_EFI,
OG_URI_CACHE_DELETE,
OG_URI_CACHE_FETCH,
OG_URI_PART_SETUP,
diff --git a/src/schema.c b/src/schema.c
index 182f992..fd1d573 100644
--- a/src/schema.c
+++ b/src/schema.c
@@ -398,6 +398,42 @@ static int og_dbi_schema_v9(struct og_dbi *dbi)
return 0;
}
+static int og_dbi_schema_v10(struct og_dbi *dbi)
+{
+ const char *msglog;
+ dbi_result result;
+
+ syslog(LOG_DEBUG, "Creating table boot_entries\n");
+ result = dbi_conn_query(dbi->conn, "CREATE TABLE `boot_entries` ("
+ "`id` BIGINT NOT NULL AUTO_INCREMENT,"
+ "`client_id` INT NOT NULL,"
+ "`name` VARCHAR(255),"
+ "`active` BOOLEAN,"
+ "`description` TEXT,"
+ "`entry_order` INT,"
+ "PRIMARY KEY (`id`),"
+ "FOREIGN KEY (`client_id`) REFERENCES `ordenadores` (`idordenador`) ON DELETE CASCADE"
+ ")");
+ if (!result) {
+ dbi_conn_error(dbi->conn, &msglog);
+ syslog(LOG_INFO, "Error when creating boot_entries (%s:%d) %s\n",
+ __func__, __LINE__, msglog);
+ return -1;
+ }
+ dbi_result_free(result);
+
+ result = dbi_conn_query(dbi->conn, "UPDATE version SET version = 10");
+ if (!result) {
+ dbi_conn_error(dbi->conn, &msglog);
+ syslog(LOG_INFO, "Could not update version row (%s:%d) %s\n",
+ __func__, __LINE__, msglog);
+ return -1;
+ }
+ dbi_result_free(result);
+
+ return 0;
+}
+
static struct og_schema_version {
int version;
int (*update)(struct og_dbi *dbi);
@@ -411,6 +447,7 @@ static struct og_schema_version {
{ .version = 7, .update = og_dbi_schema_v7, },
{ .version = 8, .update = og_dbi_schema_v8, },
{ .version = 9, .update = og_dbi_schema_v9, },
+ { .version = 10, .update = og_dbi_schema_v10,},
{ 0, NULL },
};