LUO: only deserialize on boot, add version and structure to JSON payload (#42607)
This commit is contained in:
+47
-8
@@ -53,7 +53,7 @@ int manager_luo_restore_fd_stores(Manager *m) {
|
||||
_cleanup_close_ int device_fd = -EBADF;
|
||||
_cleanup_(luo_session_finishp) int session_fd = -EBADF;
|
||||
const char *unit_id;
|
||||
sd_json_variant *fds_json;
|
||||
sd_json_variant *unit_json;
|
||||
int r, n_total = 0;
|
||||
|
||||
assert(m);
|
||||
@@ -83,18 +83,39 @@ int manager_luo_restore_fd_stores(Manager *m) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
struct {
|
||||
uint64_t version;
|
||||
sd_json_variant *units;
|
||||
} q = {};
|
||||
|
||||
static const sd_json_dispatch_field mapping_dispatch_table[] = {
|
||||
{ "version", _SD_JSON_VARIANT_TYPE_INVALID, sd_json_dispatch_uint64, voffsetof(q, version), SD_JSON_MANDATORY },
|
||||
{ "units", SD_JSON_VARIANT_OBJECT, sd_json_dispatch_variant_noref, voffsetof(q, units), 0 },
|
||||
{}
|
||||
};
|
||||
|
||||
r = sd_json_dispatch(mapping, mapping_dispatch_table, SD_JSON_ALLOW_EXTENSIONS|SD_JSON_LOG, &q);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (q.version > LUO_PROTOCOL_VERSION) {
|
||||
log_warning("LUO mapping has unsupported version %" PRIu64 ", skipping state restoration.", q.version);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Retrieve all fds from the session and dispatch each to the named unit, eagerly loading the
|
||||
* unit if necessary. */
|
||||
JSON_VARIANT_OBJECT_FOREACH(unit_id, fds_json, mapping) {
|
||||
sd_json_variant *entry;
|
||||
JSON_VARIANT_OBJECT_FOREACH(unit_id, unit_json, q.units) {
|
||||
sd_json_variant *entry, *fds_json;
|
||||
|
||||
if (!unit_name_is_valid(unit_id, UNIT_NAME_ANY)) {
|
||||
log_warning("Invalid unit name '%s' in LUO mapping, skipping.", unit_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
fds_json = sd_json_variant_by_key(unit_json, "fdstore");
|
||||
if (!sd_json_variant_is_array(fds_json)) {
|
||||
log_warning("LUO mapping for unit '%s' is not a JSON array, skipping.", unit_id);
|
||||
log_warning("LUO mapping fdstore for unit '%s' is not a JSON array, skipping.", unit_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -172,7 +193,7 @@ int manager_luo_restore_fd_stores(Manager *m) {
|
||||
}
|
||||
|
||||
int manager_luo_serialize_fd_stores(Manager *m, FILE **ret_f, FDSet **ret_fds) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *root = NULL;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *root = NULL, *units = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
_cleanup_fdset_free_ FDSet *fds = NULL;
|
||||
Unit *u;
|
||||
@@ -192,8 +213,9 @@ int manager_luo_serialize_fd_stores(Manager *m, FILE **ret_f, FDSet **ret_fds) {
|
||||
if (!fds)
|
||||
return log_oom();
|
||||
|
||||
/* Build a JSON object: { "unit_id": [ { "type": "fd", "name": "...", "fd": N },
|
||||
* { "type": "luo_session", "name": "...", "fd": N, "sessionName": "..." } ], ... }
|
||||
/* Build a JSON object: { "version": 1,
|
||||
* "units": { "unit_id": { "fdstore": [ { "type": "fd", "name": "...", "fd": N },
|
||||
* { "type": "luo_session", "name": "...", "fd": N, "sessionName": "..." } ] }, ... } }
|
||||
* This is passed to systemd-shutdown which will create a LUO session and preserve the fds. */
|
||||
HASHMAP_FOREACH(u, m->units) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *entries = NULL;
|
||||
@@ -247,7 +269,17 @@ int manager_luo_serialize_fd_stores(Manager *m, FILE **ret_f, FDSet **ret_fds) {
|
||||
n_serialized++;
|
||||
}
|
||||
|
||||
r = sd_json_variant_set_field(&root, u->id, entries);
|
||||
if (!entries)
|
||||
continue;
|
||||
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *unit_json = NULL;
|
||||
r = sd_json_buildo(
|
||||
&unit_json,
|
||||
SD_JSON_BUILD_PAIR_VARIANT("fdstore", entries));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build LUO unit object: %m");
|
||||
|
||||
r = sd_json_variant_set_field(&units, u->id, unit_json);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add unit to LUO serialization JSON: %m");
|
||||
}
|
||||
@@ -259,6 +291,13 @@ int manager_luo_serialize_fd_stores(Manager *m, FILE **ret_f, FDSet **ret_fds) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = sd_json_buildo(
|
||||
&root,
|
||||
SD_JSON_BUILD_PAIR_UNSIGNED("version", LUO_PROTOCOL_VERSION),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(!!units, "units", SD_JSON_BUILD_VARIANT(units)));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build LUO serialization JSON: %m");
|
||||
|
||||
r = open_serialization_file("luo-fd-store", &f);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to create LUO serialization file: %m");
|
||||
|
||||
+4
-2
@@ -2194,8 +2194,10 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds, Hashmap *named_
|
||||
m->soft_reboots_count++;
|
||||
|
||||
/* If a LUO (Live Update Orchestrator) session from a previous kexec is available, restore
|
||||
* preserved file descriptors into the appropriate service fd stores now, before coldplug. */
|
||||
(void) manager_luo_restore_fd_stores(m);
|
||||
* preserved file descriptors into the appropriate service fd stores now, before coldplug.
|
||||
* Only do this when booting up (i.e., skip on reexec/soft reboot/etc.) */
|
||||
if (m->previous_objective < 0)
|
||||
(void) manager_luo_restore_fd_stores(m);
|
||||
|
||||
/* Pick up fds passed via the LISTEN_FDS=/LISTEN_FDNAMES= protocol that are tagged with a
|
||||
* unit id ("unit-id|fdname"), and route them into the matching unit's fd store. Untagged
|
||||
|
||||
+52
-18
@@ -5,6 +5,7 @@
|
||||
#include <linux/magic.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/vfs.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sd-json.h"
|
||||
|
||||
@@ -149,13 +150,23 @@ int luo_parse_serialization(sd_json_variant **ret, int **ret_fds, size_t *ret_n_
|
||||
|
||||
/* Collect all fd numbers referenced in the JSON (plus the serialization fd itself)
|
||||
* so the caller can protect them from close_all_fds(). */
|
||||
sd_json_variant *units = sd_json_variant_by_key(root, "units");
|
||||
if (units && !sd_json_variant_is_object(units))
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "LUO serialization 'units' field is not an object, ignoring.");
|
||||
|
||||
const char *unit_id _unused_;
|
||||
sd_json_variant *unit_entries;
|
||||
sd_json_variant *unit_json;
|
||||
|
||||
JSON_VARIANT_OBJECT_FOREACH(unit_id, unit_entries, root) {
|
||||
sd_json_variant *entry;
|
||||
JSON_VARIANT_OBJECT_FOREACH(unit_id, unit_json, units) {
|
||||
sd_json_variant *entry, *fdstore;
|
||||
|
||||
JSON_VARIANT_ARRAY_FOREACH(entry, unit_entries) {
|
||||
fdstore = sd_json_variant_by_key(unit_json, "fdstore");
|
||||
if (fdstore && !sd_json_variant_is_array(fdstore)) {
|
||||
log_warning("LUO serialization 'fdstore' field is not an array, skipping.");
|
||||
continue;
|
||||
}
|
||||
|
||||
JSON_VARIANT_ARRAY_FOREACH(entry, fdstore) {
|
||||
struct {
|
||||
int fd;
|
||||
} p = {
|
||||
@@ -196,9 +207,9 @@ int luo_parse_serialization(sd_json_variant **ret, int **ret_fds, size_t *ret_n_
|
||||
|
||||
int luo_preserve_fd_stores(sd_json_variant *serialization, int *ret_session_fd) {
|
||||
_cleanup_close_ int device_fd = -EBADF, session_fd = -EBADF;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *mapping = NULL;
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *mapping = NULL, *units = NULL;
|
||||
const char *unit_id;
|
||||
sd_json_variant *entries;
|
||||
sd_json_variant *unit_json, *unit_object;
|
||||
uint64_t token = LUO_MAPPING_INDEX + 1;
|
||||
int r;
|
||||
|
||||
@@ -222,17 +233,27 @@ int luo_preserve_fd_stores(sd_json_variant *serialization, int *ret_session_fd)
|
||||
return log_error_errno(session_fd, "Failed to create LUO session '%s': %m", LUO_SESSION_NAME);
|
||||
|
||||
/* Build the mapping JSON for the new kernel's PID 1 and preserve each fd.
|
||||
* JSON format: { "unit_id": [ {"type": "fd", "name": "...", "token": N},
|
||||
* {"type": "luo_session", "name": "...", "sessionName": "..."} ], ... }
|
||||
* JSON format: { "unit_id": { "fdstore": [ {"type": "fd", "name": "...", "token": N},
|
||||
* {"type": "luo_session", "name": "...", "sessionName": "..."} ] }, ... }
|
||||
*
|
||||
* For regular fds: type=fd, preserved in the systemd session with the given LUO token.
|
||||
* For LUO session fds: type=luo_session, the session survives kexec independently, as it cannot be
|
||||
* nested. */
|
||||
JSON_VARIANT_OBJECT_FOREACH(unit_id, entries, serialization) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *fd_list = NULL;
|
||||
sd_json_variant *entry;
|
||||
unit_object = sd_json_variant_by_key(serialization, "units");
|
||||
if (unit_object && !sd_json_variant_is_object(unit_object))
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "LUO serialization 'units' field is not an object");
|
||||
|
||||
JSON_VARIANT_ARRAY_FOREACH(entry, entries) {
|
||||
JSON_VARIANT_OBJECT_FOREACH(unit_id, unit_json, unit_object) {
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *fd_list = NULL;
|
||||
sd_json_variant *entry, *fds_json;
|
||||
|
||||
fds_json = sd_json_variant_by_key(unit_json, "fdstore");
|
||||
if (!sd_json_variant_is_array(fds_json)) {
|
||||
log_warning("LUO mapping fdstore for unit '%s' is not a JSON array, skipping.", unit_id);
|
||||
continue;
|
||||
}
|
||||
|
||||
JSON_VARIANT_ARRAY_FOREACH(entry, fds_json) {
|
||||
struct {
|
||||
const char *type;
|
||||
const char *name;
|
||||
@@ -298,17 +319,30 @@ int luo_preserve_fd_stores(sd_json_variant *serialization, int *ret_session_fd)
|
||||
}
|
||||
|
||||
if (fd_list) {
|
||||
r = sd_json_variant_set_field(&mapping, unit_id, fd_list);
|
||||
_cleanup_(sd_json_variant_unrefp) sd_json_variant *unit_entry = NULL;
|
||||
|
||||
r = sd_json_buildo(
|
||||
&unit_entry,
|
||||
SD_JSON_BUILD_PAIR_VARIANT("fdstore", fd_list));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build LUO unit object: %m");
|
||||
|
||||
r = sd_json_variant_set_field(&units, unit_id, unit_entry);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to add unit to LUO mapping: %m");
|
||||
}
|
||||
}
|
||||
|
||||
if (!mapping) {
|
||||
log_debug("No fds were preserved in LUO session.");
|
||||
*ret_session_fd = -EBADF;
|
||||
return 0;
|
||||
}
|
||||
sd_json_variant *version = sd_json_variant_by_key(serialization, "version");
|
||||
if (!sd_json_variant_is_unsigned(version))
|
||||
return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), "LUO serialization 'version' field is missing or not an unsigned integer");
|
||||
|
||||
r = sd_json_buildo(
|
||||
&mapping,
|
||||
SD_JSON_BUILD_PAIR("version", SD_JSON_BUILD_VARIANT(version)),
|
||||
SD_JSON_BUILD_PAIR_CONDITION(!!units, "units", SD_JSON_BUILD_VARIANT(units)));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to build LUO mapping: %m");
|
||||
|
||||
/* Store the mapping as a memfd at LUO token 0 */
|
||||
_cleanup_free_ char *mapping_text = NULL;
|
||||
|
||||
+13
-10
@@ -6,18 +6,20 @@
|
||||
|
||||
#define LUO_SESSION_NAME "systemd"
|
||||
|
||||
/* Index (token) 0 in the LUO session is always the mapping memfd, which contains a JSON document mapping
|
||||
* unit ids to arrays of fd store entries:
|
||||
/* Index (token) 0 in the LUO session is always the mapping memfd, which contains a versioned JSON document
|
||||
* with manager-level state and a "units" object mapping unit ids to per-unit objects with an "fdstore" array
|
||||
* of fd store entries:
|
||||
*
|
||||
* {
|
||||
* "unit-name.service": [
|
||||
* { "type": "fd", "name": "fdname1", "token": 1 },
|
||||
* { "type": "fd", "name": "fdname2", "token": 2 },
|
||||
* { "type": "luo_session", "name": "fdname3", "sessionName": "unit.service/myapp" }
|
||||
* ],
|
||||
* "other-unit.service": [
|
||||
* { "type": "fd", "name": "stored", "token": 3 }
|
||||
* ]
|
||||
* "version": 1,
|
||||
* "units": {
|
||||
* "unit-name.service": {
|
||||
* "fdstore": [
|
||||
* { "type": "fd", "name": "fdname1", "token": 1 },
|
||||
* { "type": "luo_session", "name": "fdname3", "sessionName": "unit.service/myapp" }
|
||||
* ]
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* type=fd: the fd was preserved in the "systemd" LUO session with the given token.
|
||||
@@ -25,6 +27,7 @@
|
||||
* retrieved by session_name on the next boot.
|
||||
*/
|
||||
#define LUO_MAPPING_INDEX UINT64_C(0)
|
||||
#define LUO_PROTOCOL_VERSION UINT64_C(1)
|
||||
|
||||
int luo_open_device(void);
|
||||
int luo_create_session(int device_fd, const char *name);
|
||||
|
||||
Reference in New Issue
Block a user