_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);
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;
}
}
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;
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;
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");
}
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");
#include <linux/magic.h>
#include <sys/ioctl.h>
#include <sys/vfs.h>
+#include <unistd.h>
#include "sd-json.h"
/* 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_json, units) {
+ sd_json_variant *entry, *fdstore;
- JSON_VARIANT_OBJECT_FOREACH(unit_id, unit_entries, root) {
- sd_json_variant *entry;
+ 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, unit_entries) {
+ JSON_VARIANT_ARRAY_FOREACH(entry, fdstore) {
struct {
int fd;
} p = {
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;
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) {
+ 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_OBJECT_FOREACH(unit_id, unit_json, unit_object) {
_cleanup_(sd_json_variant_unrefp) sd_json_variant *fd_list = NULL;
- sd_json_variant *entry;
+ sd_json_variant *entry, *fds_json;
- JSON_VARIANT_ARRAY_FOREACH(entry, entries) {
+ 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;
}
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;
#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.
* 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);