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.
master
Alejandro Sirgo Rica 2024-09-06 12:25:01 +02:00
parent 1fa3813b92
commit e3a73a504f
6 changed files with 355 additions and 0 deletions

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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,

View File

@ -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 },
};