]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/nspawn/nspawn-oci.c
core/scope: drop effectively unused unit_watch_pidref() calls (#38186)
[thirdparty/systemd.git] / src / nspawn / nspawn-oci.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
de40a303 2
de40a303 3#include <linux/oom.h>
6ee31a53 4#include <sys/stat.h>
de40a303 5
6ee31a53 6#include "sd-bus.h"
309a747f
LP
7#include "sd-json.h"
8
b78d73fa 9#include "alloc-util.h"
de40a303 10#include "bus-util.h"
2bdabb16 11#include "capability-list.h"
6ee31a53 12#include "cgroup-util.h"
de40a303 13#include "cpu-set-util.h"
ca822829 14#include "device-util.h"
7176f06c 15#include "devnum-util.h"
de40a303 16#include "env-util.h"
de40a303 17#include "hostname-util.h"
309a747f 18#include "json-util.h"
6ee31a53 19#include "nspawn-mount.h"
de40a303
LP
20#include "nspawn-oci.h"
21#include "path-util.h"
22#include "rlimit-util.h"
de40a303
LP
23#include "string-util.h"
24#include "strv.h"
6ee31a53 25#include "time-util.h"
de40a303
LP
26
27/* TODO:
28 * OCI runtime tool implementation
29 * hooks
30 *
31 * Spec issues:
32 *
33 * How is RLIM_INFINITY supposed to be encoded?
34 * configured effective caps is bullshit, as execv() corrupts it anyway
35 * pipes bind mounted is *very* different from pipes newly created, comments regarding bind mount or not are bogus
36 * annotation values structured? or string?
37 * configurable file system namespace path, but then also root path? wtf?
38 * apply sysctl inside of the container? or outside?
39 * how is unlimited pids tasks limit to be encoded?
40 * what are the defaults for caps if not specified?
41 * what are the default uid/gid mappings if one is missing but the other set, or when user ns is on but no namespace configured
42 * the source field of "mounts" is really weird, as it cannot realistically be relative to the bundle, since we never know if that's what the fs wants
43 * spec contradicts itself on the mount "type" field, as the example uses "bind" as type, but it's not listed in /proc/filesystem, and is something made up by /bin/mount
44 * if type of mount is left out, what shall be assumed? "bind"?
45 * readonly mounts is entirely redundant?
46 * should escaping be applied when joining mount options with ","?
47 * devices cgroup support is bogus, "allow" and "deny" on the kernel level is about adding/removing entries, not about access
48 * spec needs to say that "rwm" devices cgroup combination can't be the empty string
ba669952 49 * cgrouspv1 crap: kernel, kernelTCP, swappiness, disableOOMKiller, swap, devices, leafWeight
de40a303
LP
50 * general: it shouldn't leak lower level abstractions this obviously
51 * unmanagable cgroups stuff: realtimeRuntime/realtimePeriod
86b52a39 52 * needs to say what happense when some option is not specified, i.e. which defaults apply
de40a303
LP
53 * no architecture? no personality?
54 * seccomp example and logic is simply broken: there's no constant "SCMP_ACT_ERRNO".
55 * spec should say what to do with unknown props
56 * /bin/mount regarding NFS and FUSE required?
57 * what does terminal=false mean?
6b000af4 58 * sysctl inside or outside? allow-listing?
5238e957 59 * swapiness typo -> swappiness
de40a303
LP
60 *
61 * Unsupported:
62 *
63 * apparmorProfile
64 * selinuxLabel + mountLabel
65 * hugepageLimits
66 * network
67 * rdma
68 * intelRdt
69 * swappiness, disableOOMKiller, kernel, kernelTCP, leafWeight (because it's dead, cgroupsv2 can't do it and hence systemd neither)
70 *
71 * Non-slice cgroup paths
72 * Propagation that is not slave + shared
73 * more than one uid/gid mapping, mappings with a container base != 0, or non-matching uid/gid mappings
74 * device cgroups access = false items that are not catchall
75 * device cgroups matches where minor is specified, but major isn't. similar where major is specified but char/block is not. also, any match that only has a type set that has less than "rwm" set. also, any entry that has none of rwm set.
76 *
77 */
78
9e73d375
MY
79/* Special values for the cpu.shares attribute */
80#define CGROUP_CPU_SHARES_INVALID UINT64_MAX
81#define CGROUP_CPU_SHARES_MIN UINT64_C(2)
82#define CGROUP_CPU_SHARES_MAX UINT64_C(262144)
83#define CGROUP_CPU_SHARES_DEFAULT UINT64_C(1024)
84
85/* Special values for the blkio.weight attribute */
86#define CGROUP_BLKIO_WEIGHT_INVALID UINT64_MAX
87#define CGROUP_BLKIO_WEIGHT_MIN UINT64_C(10)
88#define CGROUP_BLKIO_WEIGHT_MAX UINT64_C(1000)
89#define CGROUP_BLKIO_WEIGHT_DEFAULT UINT64_C(500)
90
309a747f 91static int oci_unexpected(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
19130626 92 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
309a747f 93 "Unexpected OCI element '%s' of type '%s'.", name, sd_json_variant_type_to_string(sd_json_variant_type(v)));
de40a303
LP
94}
95
309a747f
LP
96static int oci_dispatch(sd_json_variant *v, const sd_json_dispatch_field table[], sd_json_dispatch_flags_t flags, void *userdata) {
97 return sd_json_dispatch_full(v, table, oci_unexpected, flags, userdata, /* reterr_bad_field= */ NULL);
f1b622a0
LP
98}
99
309a747f 100static int oci_unsupported(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
19130626 101 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
309a747f 102 "Unsupported OCI element '%s' of type '%s'.", name, sd_json_variant_type_to_string(sd_json_variant_type(v)));
de40a303
LP
103}
104
309a747f 105static int oci_terminal(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
79742424 106 Settings *s = ASSERT_PTR(userdata);
de40a303
LP
107
108 /* If not specified, or set to true, we'll default to either an interactive or a read-only
6757a013 109 * console. If specified as false, we'll forcibly move to "pipe" mode though. */
309a747f 110 s->console_mode = sd_json_variant_boolean(v) ? _CONSOLE_MODE_INVALID : CONSOLE_PIPE;
de40a303
LP
111 return 0;
112}
113
309a747f 114static int oci_console_dimension(const char *name, sd_json_variant *variant, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 115 unsigned *u = ASSERT_PTR(userdata);
718ca772 116 uint64_t k;
de40a303 117
309a747f 118 k = sd_json_variant_unsigned(variant);
19130626
ZJS
119 if (k == 0)
120 return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
121 "Console size field '%s' is too small.", strna(name));
122 if (k > USHRT_MAX) /* TIOCSWINSZ's struct winsize uses "unsigned short" for width and height */
123 return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE),
124 "Console size field '%s' is too large.", strna(name));
de40a303
LP
125
126 *u = (unsigned) k;
127 return 0;
128}
129
309a747f 130static int oci_console_size(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
79742424 131 Settings *s = ASSERT_PTR(userdata);
de40a303 132
309a747f
LP
133 static const sd_json_dispatch_field table[] = {
134 { "height", SD_JSON_VARIANT_UNSIGNED, oci_console_dimension, offsetof(Settings, console_height), SD_JSON_MANDATORY },
135 { "width", SD_JSON_VARIANT_UNSIGNED, oci_console_dimension, offsetof(Settings, console_width), SD_JSON_MANDATORY },
de40a303
LP
136 {}
137 };
138
f1b622a0 139 return oci_dispatch(v, table, flags, s);
de40a303
LP
140}
141
309a747f 142static int oci_env(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 143 char ***l = ASSERT_PTR(userdata);
309a747f 144 sd_json_variant *e;
de40a303
LP
145 int r;
146
de40a303
LP
147 JSON_VARIANT_ARRAY_FOREACH(e, v) {
148 const char *n;
149
309a747f 150 if (!sd_json_variant_is_string(e))
19130626
ZJS
151 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
152 "Environment array contains non-string.");
de40a303 153
309a747f 154 assert_se(n = sd_json_variant_string(e));
de40a303 155
19130626
ZJS
156 if (!env_assignment_is_valid(n))
157 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
158 "Environment assignment not valid: %s", n);
de40a303
LP
159
160 r = strv_extend(l, n);
161 if (r < 0)
162 return log_oom();
163 }
164
165 return 0;
166}
167
309a747f 168static int oci_args(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
de40a303 169 _cleanup_strv_free_ char **l = NULL;
99534007 170 char ***value = ASSERT_PTR(userdata);
de40a303
LP
171 int r;
172
309a747f 173 r = sd_json_variant_strv(v, &l);
a7f8c9ce
LP
174 if (r < 0)
175 return json_log(v, flags, r, "Cannot parse arguments as list of strings: %m");
de40a303 176
19130626
ZJS
177 if (strv_isempty(l))
178 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
179 "Argument list empty, refusing.");
de40a303 180
19130626
ZJS
181 if (isempty(l[0]))
182 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
183 "Executable name is empty, refusing.");
de40a303 184
6757a013 185 return strv_free_and_replace(*value, l);
de40a303
LP
186}
187
309a747f 188static int oci_rlimit_type(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
de40a303 189 const char *z;
79742424
FS
190 int *type = ASSERT_PTR(userdata);
191 int t;
de40a303 192
309a747f 193 z = startswith(sd_json_variant_string(v), "RLIMIT_");
19130626
ZJS
194 if (!z)
195 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
196 "rlimit entry's name does not begin with 'RLIMIT_', refusing: %s",
309a747f 197 sd_json_variant_string(v));
de40a303
LP
198
199 t = rlimit_from_string(z);
19130626 200 if (t < 0)
7211c853 201 return json_log(v, flags, t,
309a747f 202 "rlimit name unknown: %s", sd_json_variant_string(v));
de40a303
LP
203
204 *type = t;
205 return 0;
206}
207
309a747f 208static int oci_rlimit_value(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
79742424
FS
209 rlim_t *value = ASSERT_PTR(userdata);
210 rlim_t z;
de40a303 211
309a747f 212 if (sd_json_variant_is_negative(v))
de40a303
LP
213 z = RLIM_INFINITY;
214 else {
309a747f 215 if (!sd_json_variant_is_unsigned(v))
19130626
ZJS
216 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
217 "rlimits limit not unsigned, refusing.");
de40a303 218
309a747f 219 z = (rlim_t) sd_json_variant_unsigned(v);
de40a303 220
309a747f 221 if ((uint64_t) z != sd_json_variant_unsigned(v))
19130626
ZJS
222 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
223 "rlimits limit out of range, refusing.");
de40a303
LP
224 }
225
226 *value = z;
227 return 0;
228}
229
309a747f 230static int oci_rlimits(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 231 Settings *s = ASSERT_PTR(userdata);
309a747f 232 sd_json_variant *e;
de40a303
LP
233 int r;
234
de40a303
LP
235 JSON_VARIANT_ARRAY_FOREACH(e, v) {
236
237 struct rlimit_data {
238 int type;
239 rlim_t soft;
240 rlim_t hard;
241 } data = {
242 .type = -1,
243 .soft = RLIM_INFINITY,
244 .hard = RLIM_INFINITY,
245 };
246
309a747f
LP
247 static const sd_json_dispatch_field table[] = {
248 { "soft", SD_JSON_VARIANT_NUMBER, oci_rlimit_value, offsetof(struct rlimit_data, soft), SD_JSON_MANDATORY },
249 { "hard", SD_JSON_VARIANT_NUMBER, oci_rlimit_value, offsetof(struct rlimit_data, hard), SD_JSON_MANDATORY },
250 { "type", SD_JSON_VARIANT_STRING, oci_rlimit_type, offsetof(struct rlimit_data, type), SD_JSON_MANDATORY },
de40a303
LP
251 {}
252 };
253
f1b622a0 254 r = oci_dispatch(e, table, flags, &data);
de40a303
LP
255 if (r < 0)
256 return r;
257
258 assert(data.type >= 0);
259 assert(data.type < _RLIMIT_MAX);
260
19130626
ZJS
261 if (s->rlimit[data.type])
262 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
263 "rlimits array contains duplicate entry, refusing.");
de40a303
LP
264
265 s->rlimit[data.type] = new(struct rlimit, 1);
266 if (!s->rlimit[data.type])
267 return log_oom();
268
269 *s->rlimit[data.type] = (struct rlimit) {
270 .rlim_cur = data.soft,
271 .rlim_max = data.hard,
272 };
273
274 }
275 return 0;
276}
277
309a747f 278static int oci_capability_array(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
79742424
FS
279 uint64_t *mask = ASSERT_PTR(userdata);
280 uint64_t m = 0;
309a747f 281 sd_json_variant *e;
de40a303
LP
282
283 JSON_VARIANT_ARRAY_FOREACH(e, v) {
284 const char *n;
285 int cap;
286
309a747f 287 if (!sd_json_variant_is_string(e))
19130626
ZJS
288 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
289 "Entry in capabilities array is not a string.");
de40a303 290
309a747f 291 assert_se(n = sd_json_variant_string(e));
de40a303
LP
292
293 cap = capability_from_name(n);
19130626
ZJS
294 if (cap < 0)
295 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
296 "Unknown capability: %s", n);
de40a303
LP
297
298 m |= UINT64_C(1) << cap;
299 }
300
f5fbe71d 301 if (*mask == UINT64_MAX)
de40a303
LP
302 *mask = m;
303 else
304 *mask |= m;
305
306 return 0;
307}
308
309a747f 309static int oci_capabilities(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
de40a303 310
309a747f
LP
311 static const sd_json_dispatch_field table[] = {
312 { "effective", SD_JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, effective) },
313 { "bounding", SD_JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, bounding) },
314 { "inheritable", SD_JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, inheritable) },
315 { "permitted", SD_JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, permitted) },
316 { "ambient", SD_JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, ambient) },
de40a303
LP
317 {}
318 };
319
99534007 320 Settings *s = ASSERT_PTR(userdata);
de40a303
LP
321 int r;
322
f1b622a0 323 r = oci_dispatch(v, table, flags, &s->full_capabilities);
de40a303
LP
324 if (r < 0)
325 return r;
326
f5fbe71d 327 if (s->full_capabilities.bounding != UINT64_MAX) {
de40a303
LP
328 s->capability = s->full_capabilities.bounding;
329 s->drop_capability = ~s->full_capabilities.bounding;
330 }
331
332 return 0;
333}
334
309a747f 335static int oci_oom_score_adj(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 336 Settings *s = ASSERT_PTR(userdata);
718ca772 337 int64_t k;
de40a303 338
309a747f 339 k = sd_json_variant_integer(v);
19130626
ZJS
340 if (k < OOM_SCORE_ADJ_MIN || k > OOM_SCORE_ADJ_MAX)
341 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
c0f86d66 342 "oomScoreAdj value out of range: %" PRIi64, k);
de40a303
LP
343
344 s->oom_score_adjust = (int) k;
345 s->oom_score_adjust_set = true;
346
347 return 0;
348}
349
309a747f 350static int oci_supplementary_gids(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 351 Settings *s = ASSERT_PTR(userdata);
309a747f 352 sd_json_variant *e;
de40a303
LP
353 int r;
354
de40a303 355 JSON_VARIANT_ARRAY_FOREACH(e, v) {
223a67e5 356 gid_t gid;
de40a303 357
309a747f 358 if (!sd_json_variant_is_unsigned(e))
19130626
ZJS
359 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
360 "Supplementary GID entry is not a UID.");
de40a303 361
309a747f 362 r = sd_json_dispatch_uid_gid(name, e, flags, &gid);
de40a303
LP
363 if (r < 0)
364 return r;
365
223a67e5 366 if (!GREEDY_REALLOC(s->supplementary_gids, s->n_supplementary_gids + 1))
de40a303
LP
367 return log_oom();
368
de40a303
LP
369 s->supplementary_gids[s->n_supplementary_gids++] = gid;
370 }
371
372 return 0;
373}
374
309a747f 375static int oci_user(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
79742424 376
309a747f
LP
377 static const sd_json_dispatch_field table[] = {
378 { "uid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(Settings, uid), SD_JSON_MANDATORY },
379 { "gid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(Settings, gid), SD_JSON_MANDATORY },
380 { "additionalGids", SD_JSON_VARIANT_ARRAY, oci_supplementary_gids, 0, 0 },
de40a303
LP
381 {}
382 };
383
f1b622a0 384 return oci_dispatch(v, table, flags, userdata);
de40a303
LP
385}
386
309a747f
LP
387static int oci_process(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
388
389 static const sd_json_dispatch_field table[] = {
390 { "terminal", SD_JSON_VARIANT_BOOLEAN, oci_terminal, 0, 0 },
391 { "consoleSize", SD_JSON_VARIANT_OBJECT, oci_console_size, 0, 0 },
392 { "cwd", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(Settings, working_directory), 0 },
393 { "env", SD_JSON_VARIANT_ARRAY, oci_env, offsetof(Settings, environment), 0 },
394 { "args", SD_JSON_VARIANT_ARRAY, oci_args, offsetof(Settings, parameters), 0 },
395 { "rlimits", SD_JSON_VARIANT_ARRAY, oci_rlimits, 0, 0 },
396 { "apparmorProfile", SD_JSON_VARIANT_STRING, oci_unsupported, 0, SD_JSON_PERMISSIVE },
397 { "capabilities", SD_JSON_VARIANT_OBJECT, oci_capabilities, 0, 0 },
398 { "noNewPrivileges", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(Settings, no_new_privileges), 0 },
399 { "oomScoreAdj", SD_JSON_VARIANT_INTEGER, oci_oom_score_adj, 0, 0 },
400 { "selinuxLabel", SD_JSON_VARIANT_STRING, oci_unsupported, 0, SD_JSON_PERMISSIVE },
401 { "user", SD_JSON_VARIANT_OBJECT, oci_user, 0, 0 },
de40a303
LP
402 {}
403 };
404
f1b622a0 405 return oci_dispatch(v, table, flags, userdata);
de40a303
LP
406}
407
309a747f 408static int oci_root(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
79742424 409 Settings *s = ASSERT_PTR(userdata);
089cd8b3 410 int r;
de40a303 411
309a747f
LP
412 static const sd_json_dispatch_field table[] = {
413 { "path", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(Settings, root) },
414 { "readonly", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_tristate, offsetof(Settings, read_only) },
de40a303
LP
415 {}
416 };
417
f1b622a0 418 r = oci_dispatch(v, table, flags, s);
089cd8b3
AP
419 if (r < 0)
420 return r;
421
422 if (s->root && !path_is_absolute(s->root)) {
423 char *joined;
424
425 joined = path_join(s->bundle, s->root);
426 if (!joined)
427 return log_oom();
428
429 free_and_replace(s->root, joined);
430 }
431
432 return 0;
de40a303
LP
433}
434
309a747f 435static int oci_hostname(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 436 Settings *s = ASSERT_PTR(userdata);
de40a303
LP
437 const char *n;
438
309a747f 439 assert_se(n = sd_json_variant_string(v));
de40a303 440
52ef5dd7 441 if (!hostname_is_valid(n, 0))
19130626
ZJS
442 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
443 "Hostname string is not a valid hostname: %s", n);
de40a303 444
f1531db5 445 return free_and_strdup_warn(&s->hostname, n);
de40a303
LP
446}
447
448static bool oci_exclude_mount(const char *path) {
449
450 /* Returns "true" for all mounts we insist to mount on our own, and hence ignore the OCI data. */
451
452 if (PATH_IN_SET(path,
453 "/dev",
454 "/dev/mqueue",
455 "/dev/pts",
456 "/dev/shm",
457 "/proc",
458 "/proc/acpi",
459 "/proc/apm",
460 "/proc/asound",
461 "/proc/bus",
462 "/proc/fs",
463 "/proc/irq",
464 "/proc/kallsyms",
465 "/proc/kcore",
466 "/proc/keys",
467 "/proc/scsi",
468 "/proc/sys",
469 "/proc/sys/net",
470 "/proc/sysrq-trigger",
471 "/proc/timer_list",
472 "/run",
473 "/sys",
474 "/sys",
475 "/sys/fs/selinux",
476 "/tmp"))
477 return true;
478
479 /* Similar, skip the whole /sys/fs/cgroups subtree */
480 if (path_startswith(path, "/sys/fs/cgroup"))
481 return true;
482
483 return false;
484}
485
b2e07b1a
ZJS
486typedef struct oci_mount_data {
487 char *destination;
488 char *source;
489 char *type;
490 char **options;
491} oci_mount_data;
492
7244c6db 493static void oci_mount_data_done(oci_mount_data *data) {
f95c9f46
ZJS
494 assert(data);
495
b2e07b1a
ZJS
496 free(data->destination);
497 free(data->source);
b2e07b1a 498 free(data->type);
7244c6db 499 strv_free(data->options);
b2e07b1a
ZJS
500}
501
309a747f 502static int oci_mounts(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 503 Settings *s = ASSERT_PTR(userdata);
309a747f 504 sd_json_variant *e;
de40a303
LP
505 int r;
506
de40a303 507 JSON_VARIANT_ARRAY_FOREACH(e, v) {
309a747f
LP
508 static const sd_json_dispatch_field table[] = {
509 { "destination", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(oci_mount_data, destination), SD_JSON_MANDATORY },
510 { "source", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(oci_mount_data, source), 0 },
511 { "options", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(oci_mount_data, options), 0, },
512 { "type", SD_JSON_VARIANT_STRING, sd_json_dispatch_string, offsetof(oci_mount_data, type), 0 },
de40a303
LP
513 {}
514 };
515
516 _cleanup_free_ char *joined_options = NULL;
f95c9f46 517 _cleanup_(oci_mount_data_done) oci_mount_data data = {};
de40a303
LP
518 CustomMount *m;
519
f1b622a0 520 r = oci_dispatch(e, table, flags, &data);
de40a303 521 if (r < 0)
b2e07b1a 522 return r;
de40a303 523
b2e07b1a
ZJS
524 if (!path_is_absolute(data.destination))
525 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
526 "Mount destination not an absolute path: %s", data.destination);
de40a303
LP
527
528 if (oci_exclude_mount(data.destination))
b2e07b1a 529 continue;
de40a303
LP
530
531 if (data.options) {
532 joined_options = strv_join(data.options, ",");
b2e07b1a
ZJS
533 if (!joined_options)
534 return log_oom();
de40a303
LP
535 }
536
537 if (!data.type || streq(data.type, "bind")) {
b1f13b0e 538 if (data.source && !path_is_absolute(data.source)) {
de40a303
LP
539 char *joined;
540
541 joined = path_join(s->bundle, data.source);
b2e07b1a
ZJS
542 if (!joined)
543 return log_oom();
de40a303
LP
544
545 free_and_replace(data.source, joined);
546 }
547
548 data.type = mfree(data.type);
549
550 m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_BIND);
551 } else
552 m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_ARBITRARY);
b2e07b1a
ZJS
553 if (!m)
554 return log_oom();
de40a303
LP
555
556 m->destination = TAKE_PTR(data.destination);
557 m->source = TAKE_PTR(data.source);
558 m->options = TAKE_PTR(joined_options);
559 m->type_argument = TAKE_PTR(data.type);
de40a303
LP
560 }
561
562 return 0;
563}
564
309a747f 565static int oci_namespace_type(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 566 unsigned long *nsflags = ASSERT_PTR(userdata);
de40a303
LP
567 const char *n;
568
309a747f 569 assert_se(n = sd_json_variant_string(v));
de40a303
LP
570
571 /* We don't use namespace_flags_from_string() here, as the OCI spec uses slightly different names than the
572 * kernel here. */
573 if (streq(n, "pid"))
574 *nsflags = CLONE_NEWPID;
575 else if (streq(n, "network"))
576 *nsflags = CLONE_NEWNET;
577 else if (streq(n, "mount"))
578 *nsflags = CLONE_NEWNS;
579 else if (streq(n, "ipc"))
580 *nsflags = CLONE_NEWIPC;
581 else if (streq(n, "uts"))
582 *nsflags = CLONE_NEWUTS;
583 else if (streq(n, "user"))
584 *nsflags = CLONE_NEWUSER;
585 else if (streq(n, "cgroup"))
586 *nsflags = CLONE_NEWCGROUP;
19130626
ZJS
587 else
588 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
55d3c136 589 "Unknown namespace type, refusing: %s", n);
de40a303
LP
590
591 return 0;
592}
593
7244c6db
FS
594struct namespace_data {
595 unsigned long type;
596 char *path;
597};
598
f95c9f46
ZJS
599static void namespace_data_done(struct namespace_data *data) {
600 assert(data);
7244c6db 601
f95c9f46 602 free(data->path);
7244c6db
FS
603}
604
309a747f 605static int oci_namespaces(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
79742424 606 Settings *s = ASSERT_PTR(userdata);
de40a303 607 unsigned long n = 0;
309a747f 608 sd_json_variant *e;
de40a303
LP
609 int r;
610
de40a303 611 JSON_VARIANT_ARRAY_FOREACH(e, v) {
f95c9f46 612 _cleanup_(namespace_data_done) struct namespace_data data = {};
de40a303 613
309a747f
LP
614 static const sd_json_dispatch_field table[] = {
615 { "type", SD_JSON_VARIANT_STRING, oci_namespace_type, offsetof(struct namespace_data, type), SD_JSON_MANDATORY },
616 { "path", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(struct namespace_data, path), 0 },
de40a303
LP
617 {}
618 };
619
f1b622a0 620 r = oci_dispatch(e, table, flags, &data);
7244c6db 621 if (r < 0)
de40a303 622 return r;
de40a303
LP
623
624 if (data.path) {
7244c6db 625 if (data.type != CLONE_NEWNET)
19130626
ZJS
626 return json_log(e, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
627 "Specifying namespace path for non-network namespace is not supported.");
de40a303 628
7244c6db 629 if (s->network_namespace_path)
19130626
ZJS
630 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
631 "Network namespace path specified more than once, refusing.");
de40a303 632
7244c6db 633 free_and_replace(s->network_namespace_path, data.path);
de40a303
LP
634 }
635
d7a0f1f4 636 if (FLAGS_SET(n, data.type))
19130626
ZJS
637 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
638 "Duplicate namespace specification, refusing.");
de40a303
LP
639
640 n |= data.type;
641 }
642
19130626
ZJS
643 if (!FLAGS_SET(n, CLONE_NEWNS))
644 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
3426ec8e 645 "Containers without a mount namespace aren't supported.");
de40a303
LP
646
647 s->private_network = FLAGS_SET(n, CLONE_NEWNET);
648 s->userns_mode = FLAGS_SET(n, CLONE_NEWUSER) ? USER_NAMESPACE_FIXED : USER_NAMESPACE_NO;
649 s->use_cgns = FLAGS_SET(n, CLONE_NEWCGROUP);
650
651 s->clone_ns_flags = n & (CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS);
652
653 return 0;
654}
655
309a747f 656static int oci_uid_gid_range(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
79742424
FS
657 uid_t *uid = ASSERT_PTR(userdata);
658 uid_t u;
718ca772 659 uint64_t k;
de40a303 660
de40a303
LP
661 assert_cc(sizeof(uid_t) == sizeof(gid_t));
662
663 /* This is very much like oci_uid_gid(), except the checks are a bit different, as this is a UID range rather
f5fbe71d 664 * than a specific UID, and hence UID_INVALID has no special significance. OTOH a range of zero makes no
de40a303
LP
665 * sense. */
666
309a747f 667 k = sd_json_variant_unsigned(v);
de40a303 668 u = (uid_t) k;
718ca772 669 if ((uint64_t) u != k)
19130626 670 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
c0f86d66 671 "UID/GID out of range: %" PRIu64, k);
19130626
ZJS
672 if (u == 0)
673 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
674 "UID/GID range can't be zero.");
de40a303
LP
675
676 *uid = u;
677 return 0;
678}
679
309a747f 680static int oci_uid_gid_mappings(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
de40a303
LP
681 struct mapping_data {
682 uid_t host_id;
683 uid_t container_id;
684 uid_t range;
685 } data = {
686 .host_id = UID_INVALID,
687 .container_id = UID_INVALID,
688 .range = 0,
689 };
690
309a747f
LP
691 static const sd_json_dispatch_field table[] = {
692 { "containerID", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(struct mapping_data, container_id), SD_JSON_MANDATORY },
693 { "hostID", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(struct mapping_data, host_id), SD_JSON_MANDATORY },
694 { "size", SD_JSON_VARIANT_UNSIGNED, oci_uid_gid_range, offsetof(struct mapping_data, range), SD_JSON_MANDATORY },
de40a303
LP
695 {}
696 };
697
99534007 698 Settings *s = ASSERT_PTR(userdata);
309a747f 699 sd_json_variant *e;
de40a303
LP
700 int r;
701
309a747f 702 if (sd_json_variant_elements(v) == 0)
de40a303
LP
703 return 0;
704
309a747f 705 if (sd_json_variant_elements(v) > 1)
19130626
ZJS
706 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
707 "UID/GID mappings with more than one entry are not supported.");
de40a303 708
309a747f 709 assert_se(e = sd_json_variant_by_index(v, 0));
de40a303 710
f1b622a0 711 r = oci_dispatch(e, table, flags, &data);
de40a303
LP
712 if (r < 0)
713 return r;
714
91c4d1af
YW
715 if (data.range > UINT32_MAX - data.host_id ||
716 data.range > UINT32_MAX - data.container_id)
19130626
ZJS
717 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
718 "UID/GID range goes beyond UID/GID validity range, refusing.");
de40a303 719
19130626
ZJS
720 if (data.container_id != 0)
721 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
722 "UID/GID mappings with a non-zero container base are not supported.");
de40a303
LP
723
724 if (data.range < 0x10000)
309a747f 725 json_log(v, flags|SD_JSON_WARNING, 0,
19130626 726 "UID/GID mapping with less than 65536 UID/GIDS set up, you are looking for trouble.");
de40a303
LP
727
728 if (s->uid_range != UID_INVALID &&
19130626
ZJS
729 (s->uid_shift != data.host_id || s->uid_range != data.range))
730 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
731 "Non-matching UID and GID mappings are not supported.");
de40a303
LP
732
733 s->uid_shift = data.host_id;
734 s->uid_range = data.range;
735
736 return 0;
737}
738
309a747f 739static int oci_device_type(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 740 mode_t *mode = ASSERT_PTR(userdata);
de40a303
LP
741 const char *t;
742
309a747f 743 assert_se(t = sd_json_variant_string(v));
de40a303
LP
744
745 if (STR_IN_SET(t, "c", "u"))
746 *mode = (*mode & ~S_IFMT) | S_IFCHR;
747 else if (streq(t, "b"))
748 *mode = (*mode & ~S_IFMT) | S_IFBLK;
749 else if (streq(t, "p"))
750 *mode = (*mode & ~S_IFMT) | S_IFIFO;
19130626
ZJS
751 else
752 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
753 "Unknown device type: %s", t);
de40a303
LP
754
755 return 0;
756}
757
309a747f 758static int oci_device_major(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
79742424 759 unsigned *u = ASSERT_PTR(userdata);
718ca772 760 uint64_t k;
de40a303 761
309a747f 762 k = sd_json_variant_unsigned(v);
19130626
ZJS
763 if (!DEVICE_MAJOR_VALID(k))
764 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
c0f86d66 765 "Device major %" PRIu64 " out of range.", k);
de40a303
LP
766
767 *u = (unsigned) k;
768 return 0;
769}
770
309a747f 771static int oci_device_minor(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
79742424 772 unsigned *u = ASSERT_PTR(userdata);
718ca772 773 uint64_t k;
de40a303 774
309a747f 775 k = sd_json_variant_unsigned(v);
19130626
ZJS
776 if (!DEVICE_MINOR_VALID(k))
777 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
c0f86d66 778 "Device minor %" PRIu64 " out of range.", k);
de40a303
LP
779
780 *u = (unsigned) k;
781 return 0;
782}
783
309a747f 784static int oci_device_file_mode(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
79742424
FS
785 mode_t *mode = ASSERT_PTR(userdata);
786 mode_t m;
718ca772 787 uint64_t k;
de40a303 788
309a747f 789 k = sd_json_variant_unsigned(v);
de40a303
LP
790 m = (mode_t) k;
791
718ca772 792 if ((m & ~07777) != 0 || (uint64_t) m != k)
19130626
ZJS
793 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
794 "fileMode out of range, refusing.");
de40a303 795
cd70372b 796 *mode = (*mode & ~07777) | m;
de40a303
LP
797 return 0;
798}
799
309a747f 800static int oci_devices(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 801 Settings *s = ASSERT_PTR(userdata);
309a747f 802 sd_json_variant *e;
de40a303
LP
803 int r;
804
de40a303
LP
805 JSON_VARIANT_ARRAY_FOREACH(e, v) {
806
309a747f
LP
807 static const sd_json_dispatch_field table[] = {
808 { "type", SD_JSON_VARIANT_STRING, oci_device_type, offsetof(DeviceNode, mode), SD_JSON_MANDATORY },
809 { "path", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(DeviceNode, path), SD_JSON_MANDATORY },
810 { "major", SD_JSON_VARIANT_UNSIGNED, oci_device_major, offsetof(DeviceNode, major), 0 },
811 { "minor", SD_JSON_VARIANT_UNSIGNED, oci_device_minor, offsetof(DeviceNode, minor), 0 },
812 { "fileMode", SD_JSON_VARIANT_UNSIGNED, oci_device_file_mode, offsetof(DeviceNode, mode), 0 },
813 { "uid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(DeviceNode, uid), 0 },
814 { "gid", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uid_gid, offsetof(DeviceNode, gid), 0 },
de40a303
LP
815 {}
816 };
817
223a67e5 818 DeviceNode *node;
de40a303 819
223a67e5 820 if (!GREEDY_REALLOC(s->extra_nodes, s->n_extra_nodes + 1))
de40a303
LP
821 return log_oom();
822
223a67e5 823 node = s->extra_nodes + s->n_extra_nodes;
de40a303
LP
824 *node = (DeviceNode) {
825 .uid = UID_INVALID,
826 .gid = GID_INVALID,
f5fbe71d
YW
827 .major = UINT_MAX,
828 .minor = UINT_MAX,
de40a303
LP
829 .mode = 0644,
830 };
831
f1b622a0 832 r = oci_dispatch(e, table, flags, node);
de40a303
LP
833 if (r < 0)
834 goto fail_element;
835
836 if (S_ISCHR(node->mode) || S_ISBLK(node->mode)) {
837 _cleanup_free_ char *path = NULL;
838
f5fbe71d 839 if (node->major == UINT_MAX || node->minor == UINT_MAX) {
19130626 840 r = json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 841 "Major/minor required when device node is device node.");
de40a303
LP
842 goto fail_element;
843 }
844
845 /* Suppress a couple of implicit device nodes */
4fe46c34 846 r = devname_from_devnum(node->mode, makedev(node->major, node->minor), &path);
de40a303 847 if (r < 0)
309a747f 848 json_log(e, flags|SD_JSON_DEBUG, r, "Failed to resolve device node %u:%u, ignoring: %m", node->major, node->minor);
de40a303
LP
849 else {
850 if (PATH_IN_SET(path,
851 "/dev/null",
852 "/dev/zero",
853 "/dev/full",
854 "/dev/random",
855 "/dev/urandom",
856 "/dev/tty",
857 "/dev/net/tun",
858 "/dev/ptmx",
859 "/dev/pts/ptmx",
860 "/dev/console")) {
861
309a747f 862 json_log(e, flags|SD_JSON_DEBUG, 0, "Ignoring devices item for device '%s', as it is implicitly created anyway.", path);
de40a303
LP
863 free(node->path);
864 continue;
865 }
866 }
867 }
868
869 s->n_extra_nodes++;
870 continue;
871
872 fail_element:
873 free(node->path);
874 return r;
875 }
876
877 return 0;
878}
879
309a747f 880static int oci_cgroups_path(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
de40a303 881 _cleanup_free_ char *slice = NULL, *backwards = NULL;
99534007 882 Settings *s = ASSERT_PTR(userdata);
de40a303
LP
883 const char *p;
884 int r;
885
309a747f 886 assert_se(p = sd_json_variant_string(v));
de40a303
LP
887
888 r = cg_path_get_slice(p, &slice);
889 if (r < 0)
890 return json_log(v, flags, r, "Couldn't derive slice unit name from path '%s': %m", p);
891
892 r = cg_slice_to_path(slice, &backwards);
893 if (r < 0)
894 return json_log(v, flags, r, "Couldn't convert slice unit name '%s' back to path: %m", slice);
895
19130626
ZJS
896 if (!path_equal(backwards, p))
897 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
898 "Control group path '%s' does not refer to slice unit, refusing.", p);
de40a303
LP
899
900 free_and_replace(s->slice, slice);
901 return 0;
902}
903
309a747f 904static int oci_cgroup_device_type(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
79742424 905 mode_t *mode = ASSERT_PTR(userdata);
de40a303
LP
906 const char *n;
907
309a747f 908 assert_se(n = sd_json_variant_string(v));
de40a303
LP
909
910 if (streq(n, "c"))
911 *mode = S_IFCHR;
912 else if (streq(n, "b"))
913 *mode = S_IFBLK;
19130626
ZJS
914 else
915 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
916 "Control group device type unknown: %s", n);
de40a303
LP
917
918 return 0;
919}
920
921struct device_data {
922 bool allow;
923 bool r;
924 bool w;
925 bool m;
926 mode_t type;
927 unsigned major;
928 unsigned minor;
929};
930
309a747f 931static int oci_cgroup_device_access(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
79742424 932 struct device_data *d = ASSERT_PTR(userdata);
de40a303 933 bool r = false, w = false, m = false;
de40a303 934
1236f06c
YW
935 for (const char *s = ASSERT_PTR(sd_json_variant_string(v)); *s; s++)
936 if (*s == 'r')
de40a303 937 r = true;
1236f06c 938 else if (*s == 'w')
de40a303 939 w = true;
1236f06c 940 else if (*s == 'm')
de40a303 941 m = true;
19130626
ZJS
942 else
943 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
1236f06c 944 "Unknown device access character '%c'.", *s);
de40a303
LP
945
946 d->r = r;
947 d->w = w;
948 d->m = m;
949
950 return 0;
951}
952
309a747f 953static int oci_cgroup_devices(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
de40a303 954 _cleanup_free_ struct device_data *list = NULL;
99534007 955 Settings *s = ASSERT_PTR(userdata);
1236f06c 956 size_t n_list = 0;
de40a303 957 bool noop = false;
309a747f 958 sd_json_variant *e;
de40a303
LP
959 int r;
960
de40a303
LP
961 JSON_VARIANT_ARRAY_FOREACH(e, v) {
962
963 struct device_data data = {
f5fbe71d
YW
964 .major = UINT_MAX,
965 .minor = UINT_MAX,
223a67e5 966 };
de40a303 967
309a747f
LP
968 static const sd_json_dispatch_field table[] = {
969 { "allow", SD_JSON_VARIANT_BOOLEAN, sd_json_dispatch_stdbool, offsetof(struct device_data, allow), SD_JSON_MANDATORY },
970 { "type", SD_JSON_VARIANT_STRING, oci_cgroup_device_type, offsetof(struct device_data, type), 0 },
971 { "major", SD_JSON_VARIANT_UNSIGNED, oci_device_major, offsetof(struct device_data, major), 0 },
972 { "minor", SD_JSON_VARIANT_UNSIGNED, oci_device_minor, offsetof(struct device_data, minor), 0 },
973 { "access", SD_JSON_VARIANT_STRING, oci_cgroup_device_access, 0, 0 },
de40a303
LP
974 {}
975 };
976
f1b622a0 977 r = oci_dispatch(e, table, flags, &data);
de40a303
LP
978 if (r < 0)
979 return r;
980
981 if (!data.allow) {
6b000af4
LP
982 /* The fact that OCI allows 'deny' entries makes really no sense, as 'allow'
983 * vs. 'deny' for the devices cgroup controller is really not about allow-listing and
984 * deny-listing but about adding and removing entries from the allow list. Since we
985 * always start out with an empty allow list we hence ignore the whole thing, as
986 * removing entries which don't exist make no sense. We'll log about this, since this
987 * is really borked in the spec, with one exception: the entry that's supposed to
988 * drop the kernel's default we ignore silently */
de40a303 989
f5fbe71d 990 if (!data.r || !data.w || !data.m || data.type != 0 || data.major != UINT_MAX || data.minor != UINT_MAX)
309a747f 991 json_log(v, flags|SD_JSON_WARNING, 0, "Devices cgroup allow list with arbitrary 'allow' entries not supported, ignoring.");
de40a303
LP
992
993 /* We ignore the 'deny' entry as for us that's implied */
994 continue;
995 }
996
997 if (!data.r && !data.w && !data.m) {
6b000af4 998 json_log(v, flags|LOG_WARNING, 0, "Device cgroup allow list entry with no effect found, ignoring.");
de40a303
LP
999 continue;
1000 }
1001
f5fbe71d 1002 if (data.minor != UINT_MAX && data.major == UINT_MAX)
19130626 1003 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
6b000af4 1004 "Device cgroup allow list entries with minors but no majors not supported.");
de40a303 1005
f5fbe71d 1006 if (data.major != UINT_MAX && data.type == 0)
19130626 1007 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
6b000af4 1008 "Device cgroup allow list entries with majors but no device node type not supported.");
de40a303
LP
1009
1010 if (data.type == 0) {
6b000af4 1011 if (data.r && data.w && data.m) /* a catchall allow list entry means we are looking at a noop */
de40a303 1012 noop = true;
19130626
ZJS
1013 else
1014 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP),
6b000af4 1015 "Device cgroup allow list entries with no type not supported.");
de40a303
LP
1016 }
1017
223a67e5 1018 if (!GREEDY_REALLOC(list, n_list + 1))
de40a303
LP
1019 return log_oom();
1020
de40a303
LP
1021 list[n_list++] = data;
1022 }
1023
1024 if (noop)
1025 return 0;
1026
1027 r = settings_allocate_properties(s);
1028 if (r < 0)
1029 return r;
1030
1031 r = sd_bus_message_open_container(s->properties, 'r', "sv");
1032 if (r < 0)
1033 return bus_log_create_error(r);
1034
1035 r = sd_bus_message_append(s->properties, "s", "DeviceAllow");
1036 if (r < 0)
1037 return bus_log_create_error(r);
1038
1039 r = sd_bus_message_open_container(s->properties, 'v', "a(ss)");
1040 if (r < 0)
1041 return bus_log_create_error(r);
1042
1043 r = sd_bus_message_open_container(s->properties, 'a', "(ss)");
1044 if (r < 0)
1045 return bus_log_create_error(r);
1046
1236f06c 1047 FOREACH_ARRAY(d, list, n_list) {
de40a303
LP
1048 _cleanup_free_ char *pattern = NULL;
1049 char access[4];
1050 size_t n = 0;
1051
1236f06c 1052 if (d->minor == UINT_MAX) {
de40a303
LP
1053 const char *t;
1054
1236f06c 1055 if (d->type == S_IFBLK)
de40a303
LP
1056 t = "block";
1057 else {
1236f06c 1058 assert(d->type == S_IFCHR);
de40a303
LP
1059 t = "char";
1060 }
1061
1236f06c 1062 if (d->major == UINT_MAX) {
de40a303
LP
1063 pattern = strjoin(t, "-*");
1064 if (!pattern)
1065 return log_oom();
1066 } else {
1236f06c 1067 if (asprintf(&pattern, "%s-%u", t, d->major) < 0)
de40a303
LP
1068 return log_oom();
1069 }
1070
1071 } else {
1236f06c 1072 assert(d->major != UINT_MAX); /* If a minor is specified, then a major also needs to be specified */
de40a303 1073
1236f06c 1074 r = device_path_make_major_minor(d->type, makedev(d->major, d->minor), &pattern);
de40a303
LP
1075 if (r < 0)
1076 return log_oom();
1077 }
1078
1236f06c 1079 if (d->r)
de40a303 1080 access[n++] = 'r';
1236f06c 1081 if (d->w)
de40a303 1082 access[n++] = 'w';
1236f06c 1083 if (d->m)
de40a303
LP
1084 access[n++] = 'm';
1085 access[n] = 0;
1086
1087 assert(n > 0);
1088
1089 r = sd_bus_message_append(s->properties, "(ss)", pattern, access);
1090 if (r < 0)
1091 return bus_log_create_error(r);
1092 }
1093
1094 r = sd_bus_message_close_container(s->properties);
1095 if (r < 0)
1096 return bus_log_create_error(r);
1097
1098 r = sd_bus_message_close_container(s->properties);
1099 if (r < 0)
1100 return bus_log_create_error(r);
1101
1102 r = sd_bus_message_close_container(s->properties);
1103 if (r < 0)
1104 return bus_log_create_error(r);
1105
1106 return 0;
1107}
1108
309a747f 1109static int oci_cgroup_memory_limit(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 1110 uint64_t *m = ASSERT_PTR(userdata);
718ca772 1111 uint64_t k;
de40a303 1112
309a747f 1113 if (sd_json_variant_is_negative(v)) {
de40a303
LP
1114 *m = UINT64_MAX;
1115 return 0;
1116 }
1117
309a747f 1118 if (!sd_json_variant_is_unsigned(v))
19130626 1119 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 1120 "Memory limit is not an unsigned integer.");
de40a303 1121
309a747f 1122 k = sd_json_variant_unsigned(v);
19130626
ZJS
1123 if (k >= UINT64_MAX)
1124 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
c0f86d66 1125 "Memory limit too large: %" PRIu64, k);
de40a303
LP
1126
1127 *m = (uint64_t) k;
1128 return 0;
1129}
1130
309a747f 1131static int oci_cgroup_memory(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
de40a303
LP
1132
1133 struct memory_data {
1134 uint64_t limit;
1135 uint64_t reservation;
1136 uint64_t swap;
1137 } data = {
1138 .limit = UINT64_MAX,
1139 .reservation = UINT64_MAX,
1140 .swap = UINT64_MAX,
1141 };
1142
309a747f
LP
1143 static const sd_json_dispatch_field table[] = {
1144 { "limit", SD_JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, limit), 0 },
1145 { "reservation", SD_JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, reservation), 0 },
1146 { "swap", SD_JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, swap), 0 },
1147 { "kernel", SD_JSON_VARIANT_NUMBER, oci_unsupported, 0, SD_JSON_PERMISSIVE },
1148 { "kernelTCP", SD_JSON_VARIANT_NUMBER, oci_unsupported, 0, SD_JSON_PERMISSIVE },
1149 { "swapiness", SD_JSON_VARIANT_NUMBER, oci_unsupported, 0, SD_JSON_PERMISSIVE },
1150 { "disableOOMKiller", SD_JSON_VARIANT_BOOLEAN, oci_unsupported, 0, SD_JSON_PERMISSIVE },
de40a303
LP
1151 {}
1152 };
1153
79742424 1154 Settings *s = ASSERT_PTR(userdata);
de40a303
LP
1155 int r;
1156
f1b622a0 1157 r = oci_dispatch(v, table, flags, &data);
de40a303
LP
1158 if (r < 0)
1159 return r;
1160
1161 if (data.swap != UINT64_MAX) {
1162 if (data.limit == UINT64_MAX)
1163 json_log(v, flags|LOG_WARNING, 0, "swap limit without memory limit is not supported, ignoring.");
1164 else if (data.swap < data.limit)
1165 json_log(v, flags|LOG_WARNING, 0, "swap limit is below memory limit, ignoring.");
1166 else {
1167 r = settings_allocate_properties(s);
1168 if (r < 0)
1169 return r;
1170
1171 r = sd_bus_message_append(s->properties, "(sv)", "MemorySwapMax", "t", data.swap - data.limit);
1172 if (r < 0)
1173 return bus_log_create_error(r);
1174 }
1175 }
1176
1177 if (data.limit != UINT64_MAX) {
1178 r = settings_allocate_properties(s);
1179 if (r < 0)
1180 return r;
1181
1182 r = sd_bus_message_append(s->properties, "(sv)", "MemoryMax", "t", data.limit);
1183 if (r < 0)
1184 return bus_log_create_error(r);
1185 }
1186
1187 if (data.reservation != UINT64_MAX) {
1188 r = settings_allocate_properties(s);
1189 if (r < 0)
1190 return r;
1191
1192 r = sd_bus_message_append(s->properties, "(sv)", "MemoryLow", "t", data.reservation);
1193 if (r < 0)
1194 return bus_log_create_error(r);
1195 }
1196
1197 return 0;
1198}
1199
1200struct cpu_data {
173ffdaf 1201 uint64_t weight;
de40a303
LP
1202 uint64_t quota;
1203 uint64_t period;
0985c7c4 1204 CPUSet cpu_set;
de40a303
LP
1205};
1206
309a747f 1207static int oci_cgroup_cpu_shares(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
173ffdaf 1208 uint64_t k, *u = ASSERT_PTR(userdata);
de40a303 1209
309a747f 1210 k = sd_json_variant_unsigned(v);
19130626 1211 if (k < CGROUP_CPU_SHARES_MIN || k > CGROUP_CPU_SHARES_MAX)
173ffdaf 1212 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), "shares value out of range.");
de40a303 1213
173ffdaf
YW
1214 /* convert from cgroup v1 cpu.shares to v2 cpu.weight */
1215 assert_cc(CGROUP_CPU_SHARES_MAX <= UINT64_MAX / CGROUP_WEIGHT_DEFAULT);
1216 *u = CLAMP(k * CGROUP_WEIGHT_DEFAULT / CGROUP_CPU_SHARES_DEFAULT, CGROUP_WEIGHT_MIN, CGROUP_WEIGHT_MAX);
de40a303
LP
1217 return 0;
1218}
1219
309a747f 1220static int oci_cgroup_cpu_quota(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
b7fb6ec3 1221 uint64_t k, *u = ASSERT_PTR(userdata);
de40a303 1222
309a747f 1223 k = sd_json_variant_unsigned(v);
19130626 1224 if (k <= 0 || k >= UINT64_MAX)
b7fb6ec3 1225 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), "period/quota value out of range.");
de40a303 1226
b7fb6ec3 1227 *u = k;
de40a303
LP
1228 return 0;
1229}
1230
309a747f 1231static int oci_cgroup_cpu_cpus(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 1232 struct cpu_data *data = ASSERT_PTR(userdata);
0985c7c4 1233 CPUSet set;
de40a303 1234 const char *n;
0985c7c4 1235 int r;
de40a303 1236
309a747f 1237 assert_se(n = sd_json_variant_string(v));
de40a303 1238
0985c7c4
ZJS
1239 r = parse_cpu_set(n, &set);
1240 if (r < 0)
1241 return json_log(v, flags, r, "Failed to parse CPU set specification: %s", n);
de40a303 1242
296fe3d5 1243 return cpu_set_done_and_replace(data->cpu_set, set);
de40a303
LP
1244}
1245
309a747f 1246static int oci_cgroup_cpu(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
de40a303 1247
309a747f 1248 static const sd_json_dispatch_field table[] = {
173ffdaf 1249 { "shares", SD_JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_shares, offsetof(struct cpu_data, weight), 0 },
309a747f
LP
1250 { "quota", SD_JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_quota, offsetof(struct cpu_data, quota), 0 },
1251 { "period", SD_JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_quota, offsetof(struct cpu_data, period), 0 },
1252 { "realtimeRuntime", SD_JSON_VARIANT_UNSIGNED, oci_unsupported, 0, 0 },
1253 { "realtimePeriod", SD_JSON_VARIANT_UNSIGNED, oci_unsupported, 0, 0 },
1254 { "cpus", SD_JSON_VARIANT_STRING, oci_cgroup_cpu_cpus, 0, 0 },
1255 { "mems", SD_JSON_VARIANT_STRING, oci_unsupported, 0, 0 },
de40a303
LP
1256 {}
1257 };
1258
1259 struct cpu_data data = {
173ffdaf 1260 .weight = UINT64_MAX,
de40a303
LP
1261 .quota = UINT64_MAX,
1262 .period = UINT64_MAX,
1263 };
1264
79742424 1265 Settings *s = ASSERT_PTR(userdata);
de40a303
LP
1266 int r;
1267
f1b622a0 1268 r = oci_dispatch(v, table, flags, &data);
de40a303 1269 if (r < 0) {
296fe3d5 1270 cpu_set_done(&data.cpu_set);
de40a303
LP
1271 return r;
1272 }
1273
296fe3d5 1274 cpu_set_done_and_replace(s->cpu_set, data.cpu_set);
de40a303 1275
173ffdaf 1276 if (data.weight != UINT64_MAX) {
de40a303
LP
1277 r = settings_allocate_properties(s);
1278 if (r < 0)
1279 return r;
1280
173ffdaf 1281 r = sd_bus_message_append(s->properties, "(sv)", "CPUWeight", "t", data.weight);
de40a303
LP
1282 if (r < 0)
1283 return bus_log_create_error(r);
1284 }
1285
1286 if (data.quota != UINT64_MAX && data.period != UINT64_MAX) {
1287 r = settings_allocate_properties(s);
1288 if (r < 0)
1289 return r;
1290
b7fb6ec3
YW
1291 r = sd_bus_message_append(s->properties, "(sv)", "CPUQuotaPerSecUSec", "t", data.quota * USEC_PER_SEC / data.period);
1292 if (r < 0)
1293 return bus_log_create_error(r);
1294
1295 r = sd_bus_message_append(s->properties, "(sv)", "CPUQuotaPeriodUSec", "t", data.period);
de40a303
LP
1296 if (r < 0)
1297 return bus_log_create_error(r);
1298
19130626
ZJS
1299 } else if ((data.quota != UINT64_MAX) != (data.period != UINT64_MAX))
1300 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
1301 "CPU quota and period not used together.");
de40a303
LP
1302
1303 return 0;
1304}
1305
aed61aa3
YW
1306static uint64_t cgroup_weight_blkio_to_io(uint64_t blkio_weight) {
1307 /* convert from cgroup v1 blkio.weight to v2 io.weight */
1308 assert_cc(CGROUP_BLKIO_WEIGHT_MAX <= UINT64_MAX / CGROUP_WEIGHT_DEFAULT);
1309 return CLAMP(blkio_weight * CGROUP_WEIGHT_DEFAULT / CGROUP_BLKIO_WEIGHT_DEFAULT,
1310 CGROUP_WEIGHT_MIN, CGROUP_WEIGHT_MAX);
1311}
1312
309a747f 1313static int oci_cgroup_block_io_weight(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 1314 Settings *s = ASSERT_PTR(userdata);
718ca772 1315 uint64_t k;
de40a303
LP
1316 int r;
1317
309a747f 1318 k = sd_json_variant_unsigned(v);
19130626
ZJS
1319 if (k < CGROUP_BLKIO_WEIGHT_MIN || k > CGROUP_BLKIO_WEIGHT_MAX)
1320 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
1321 "Block I/O weight out of range.");
de40a303
LP
1322
1323 r = settings_allocate_properties(s);
1324 if (r < 0)
1325 return r;
1326
aed61aa3 1327 r = sd_bus_message_append(s->properties, "(sv)", "IOWeight", "t", cgroup_weight_blkio_to_io(k));
de40a303
LP
1328 if (r < 0)
1329 return bus_log_create_error(r);
1330
1331 return 0;
1332}
1333
309a747f 1334static int oci_cgroup_block_io_weight_device(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 1335 Settings *s = ASSERT_PTR(userdata);
309a747f 1336 sd_json_variant *e;
de40a303
LP
1337 int r;
1338
de40a303
LP
1339 JSON_VARIANT_ARRAY_FOREACH(e, v) {
1340 struct device_data {
1341 unsigned major;
1342 unsigned minor;
718ca772 1343 uint64_t weight;
de40a303 1344 } data = {
f5fbe71d
YW
1345 .major = UINT_MAX,
1346 .minor = UINT_MAX,
718ca772 1347 .weight = UINT64_MAX,
de40a303
LP
1348 };
1349
309a747f
LP
1350 static const sd_json_dispatch_field table[] = {
1351 { "major", SD_JSON_VARIANT_UNSIGNED, oci_device_major, offsetof(struct device_data, major), SD_JSON_MANDATORY },
1352 { "minor", SD_JSON_VARIANT_UNSIGNED, oci_device_minor, offsetof(struct device_data, minor), SD_JSON_MANDATORY },
1353 { "weight", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint64, offsetof(struct device_data, weight), 0 },
1354 { "leafWeight", SD_JSON_VARIANT_INTEGER, oci_unsupported, 0, SD_JSON_PERMISSIVE },
de40a303
LP
1355 {}
1356 };
1357
1358 _cleanup_free_ char *path = NULL;
1359
f1b622a0 1360 r = oci_dispatch(e, table, flags, &data);
de40a303
LP
1361 if (r < 0)
1362 return r;
1363
718ca772 1364 if (data.weight == UINT64_MAX)
de40a303
LP
1365 continue;
1366
19130626
ZJS
1367 if (data.weight < CGROUP_BLKIO_WEIGHT_MIN || data.weight > CGROUP_BLKIO_WEIGHT_MAX)
1368 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
1369 "Block I/O device weight out of range.");
de40a303
LP
1370
1371 r = device_path_make_major_minor(S_IFBLK, makedev(data.major, data.minor), &path);
1372 if (r < 0)
1373 return json_log(v, flags, r, "Failed to build device path: %m");
1374
1375 r = settings_allocate_properties(s);
1376 if (r < 0)
1377 return r;
1378
aed61aa3
YW
1379 r = sd_bus_message_append(s->properties, "(sv)", "IODeviceWeight", "a(st)", 1,
1380 path, cgroup_weight_blkio_to_io(data.weight));
de40a303
LP
1381 if (r < 0)
1382 return bus_log_create_error(r);
1383 }
1384
1385 return 0;
1386}
1387
309a747f 1388static int oci_cgroup_block_io_throttle(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 1389 Settings *s = ASSERT_PTR(userdata);
de40a303 1390 const char *pname;
309a747f 1391 sd_json_variant *e;
de40a303
LP
1392 int r;
1393
de40a303
LP
1394 pname = streq(name, "throttleReadBpsDevice") ? "IOReadBandwidthMax" :
1395 streq(name, "throttleWriteBpsDevice") ? "IOWriteBandwidthMax" :
1396 streq(name, "throttleReadIOPSDevice") ? "IOReadIOPSMax" :
1397 "IOWriteIOPSMax";
1398
1399 JSON_VARIANT_ARRAY_FOREACH(e, v) {
1400 struct device_data {
1401 unsigned major;
1402 unsigned minor;
718ca772 1403 uint64_t rate;
de40a303 1404 } data = {
f5fbe71d
YW
1405 .major = UINT_MAX,
1406 .minor = UINT_MAX,
de40a303
LP
1407 };
1408
309a747f
LP
1409 static const sd_json_dispatch_field table[] = {
1410 { "major", SD_JSON_VARIANT_UNSIGNED, oci_device_major, offsetof(struct device_data, major), SD_JSON_MANDATORY },
1411 { "minor", SD_JSON_VARIANT_UNSIGNED, oci_device_minor, offsetof(struct device_data, minor), SD_JSON_MANDATORY },
1412 { "rate", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint64, offsetof(struct device_data, rate), SD_JSON_MANDATORY },
de40a303
LP
1413 {}
1414 };
1415
1416 _cleanup_free_ char *path = NULL;
1417
f1b622a0 1418 r = oci_dispatch(e, table, flags, &data);
de40a303
LP
1419 if (r < 0)
1420 return r;
1421
19130626
ZJS
1422 if (data.rate >= UINT64_MAX)
1423 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
1424 "Block I/O device rate out of range.");
de40a303
LP
1425
1426 r = device_path_make_major_minor(S_IFBLK, makedev(data.major, data.minor), &path);
1427 if (r < 0)
1428 return json_log(v, flags, r, "Failed to build device path: %m");
1429
1430 r = settings_allocate_properties(s);
1431 if (r < 0)
1432 return r;
1433
1434 r = sd_bus_message_append(s->properties, "(sv)", pname, "a(st)", 1, path, (uint64_t) data.rate);
1435 if (r < 0)
1436 return bus_log_create_error(r);
1437 }
1438
1439 return 0;
1440}
1441
309a747f 1442static int oci_cgroup_block_io(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
de40a303 1443
309a747f
LP
1444 static const sd_json_dispatch_field table[] = {
1445 { "weight", SD_JSON_VARIANT_UNSIGNED, oci_cgroup_block_io_weight, 0, 0 },
1446 { "leafWeight", SD_JSON_VARIANT_UNSIGNED, oci_unsupported, 0, SD_JSON_PERMISSIVE },
1447 { "weightDevice", SD_JSON_VARIANT_ARRAY, oci_cgroup_block_io_weight_device, 0, 0 },
1448 { "throttleReadBpsDevice", SD_JSON_VARIANT_ARRAY, oci_cgroup_block_io_throttle, 0, 0 },
1449 { "throttleWriteBpsDevice", SD_JSON_VARIANT_ARRAY, oci_cgroup_block_io_throttle, 0, 0 },
1450 { "throttleReadIOPSDevice", SD_JSON_VARIANT_ARRAY, oci_cgroup_block_io_throttle, 0, 0 },
1451 { "throttleWriteIOPSDevice", SD_JSON_VARIANT_ARRAY, oci_cgroup_block_io_throttle, 0, 0 },
de40a303
LP
1452 {}
1453 };
1454
f1b622a0 1455 return oci_dispatch(v, table, flags, userdata);
de40a303
LP
1456}
1457
309a747f 1458static int oci_cgroup_pids(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
de40a303 1459
309a747f
LP
1460 static const sd_json_dispatch_field table[] = {
1461 { "limit", SD_JSON_VARIANT_NUMBER, sd_json_dispatch_variant, 0, SD_JSON_MANDATORY },
de40a303
LP
1462 {}
1463 };
1464
309a747f 1465 _cleanup_(sd_json_variant_unrefp) sd_json_variant *k = NULL;
99534007 1466 Settings *s = ASSERT_PTR(userdata);
de40a303
LP
1467 uint64_t m;
1468 int r;
1469
f1b622a0 1470 r = oci_dispatch(v, table, flags, &k);
de40a303
LP
1471 if (r < 0)
1472 return r;
1473
309a747f 1474 if (sd_json_variant_is_negative(k))
de40a303
LP
1475 m = UINT64_MAX;
1476 else {
309a747f 1477 if (!sd_json_variant_is_unsigned(k))
19130626
ZJS
1478 return json_log(k, flags, SYNTHETIC_ERRNO(EINVAL),
1479 "pids limit not unsigned integer, refusing.");
de40a303 1480
309a747f 1481 m = (uint64_t) sd_json_variant_unsigned(k);
de40a303 1482
309a747f 1483 if ((uint64_t) m != sd_json_variant_unsigned(k))
19130626
ZJS
1484 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
1485 "pids limit out of range, refusing.");
de40a303
LP
1486 }
1487
1488 r = settings_allocate_properties(s);
1489 if (r < 0)
1490 return r;
1491
1492 r = sd_bus_message_append(s->properties, "(sv)", "TasksMax", "t", m);
1493 if (r < 0)
1494 return bus_log_create_error(r);
1495
1496 return 0;
1497}
1498
309a747f 1499static int oci_resources(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
de40a303 1500
309a747f
LP
1501 static const sd_json_dispatch_field table[] = {
1502 { "devices", SD_JSON_VARIANT_ARRAY, oci_cgroup_devices, 0, 0 },
1503 { "memory", SD_JSON_VARIANT_OBJECT, oci_cgroup_memory, 0, 0 },
1504 { "cpu", SD_JSON_VARIANT_OBJECT, oci_cgroup_cpu, 0, 0 },
1505 { "blockIO", SD_JSON_VARIANT_OBJECT, oci_cgroup_block_io, 0, 0 },
1506 { "hugepageLimits", SD_JSON_VARIANT_ARRAY, oci_unsupported, 0, 0 },
1507 { "network", SD_JSON_VARIANT_OBJECT, oci_unsupported, 0, 0 },
1508 { "pids", SD_JSON_VARIANT_OBJECT, oci_cgroup_pids, 0, 0 },
1509 { "rdma", SD_JSON_VARIANT_OBJECT, oci_unsupported, 0, 0 },
de40a303
LP
1510 {}
1511 };
1512
f1b622a0 1513 return oci_dispatch(v, table, flags, userdata);
de40a303
LP
1514}
1515
1516static bool sysctl_key_valid(const char *s) {
1517 bool dot = true;
1518
1519 /* Note that we are a bit stricter here than in systemd-sysctl, as that inherited semantics from the old sysctl
1520 * tool, which were really weird (as it swaps / and . in both ways) */
1521
1522 if (isempty(s))
1523 return false;
1524
1525 for (; *s; s++) {
1526
1527 if (*s <= ' ' || *s >= 127)
1528 return false;
1529 if (*s == '/')
1530 return false;
1531 if (*s == '.') {
1532
1533 if (dot) /* Don't allow two dots next to each other (or at the beginning) */
1534 return false;
1535
1536 dot = true;
1537 } else
1538 dot = false;
1539 }
1540
1541 if (dot) /* don't allow a dot at the end */
1542 return false;
1543
1544 return true;
1545}
1546
309a747f 1547static int oci_sysctl(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 1548 Settings *s = ASSERT_PTR(userdata);
309a747f 1549 sd_json_variant *w;
33d60b8d 1550 const char *k;
de40a303
LP
1551 int r;
1552
de40a303 1553 JSON_VARIANT_OBJECT_FOREACH(k, w, v) {
33d60b8d 1554 const char *m;
de40a303 1555
309a747f 1556 if (!sd_json_variant_is_string(w))
19130626
ZJS
1557 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
1558 "sysctl parameter is not a string, refusing.");
de40a303 1559
309a747f 1560 assert_se(m = sd_json_variant_string(w));
de40a303 1561
fc832965 1562 if (!sysctl_key_valid(k))
19130626 1563 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
33d60b8d 1564 "sysctl key invalid, refusing: %s", k);
de40a303 1565
69f3c619 1566 r = strv_extend_many(&s->sysctl, k, m);
de40a303
LP
1567 if (r < 0)
1568 return log_oom();
1569 }
1570
1571 return 0;
1572}
1573
ae408d77 1574#if HAVE_SECCOMP
de40a303
LP
1575static int oci_seccomp_action_from_string(const char *name, uint32_t *ret) {
1576
1577 static const struct {
1578 const char *name;
1579 uint32_t action;
1580 } table[] = {
f9a3d8e2
LP
1581 { "SCMP_ACT_ALLOW", SCMP_ACT_ALLOW },
1582 { "SCMP_ACT_ERRNO", SCMP_ACT_ERRNO(EPERM) }, /* the OCI spec doesn't document the error, but it appears EPERM is supposed to be used */
1583 { "SCMP_ACT_KILL", SCMP_ACT_KILL },
1584#ifdef SCMP_ACT_KILL_PROCESS
1585 { "SCMP_ACT_KILL_PROCESS", SCMP_ACT_KILL_PROCESS },
1586#endif
1587#ifdef SCMP_ACT_KILL_THREAD
1588 { "SCMP_ACT_KILL_THREAD", SCMP_ACT_KILL_THREAD },
1589#endif
de40a303 1590#ifdef SCMP_ACT_LOG
f9a3d8e2 1591 { "SCMP_ACT_LOG", SCMP_ACT_LOG },
de40a303 1592#endif
f9a3d8e2 1593 { "SCMP_ACT_TRAP", SCMP_ACT_TRAP },
de40a303
LP
1594
1595 /* We don't support SCMP_ACT_TRACE because that requires a tracer, and that doesn't really make sense
1596 * here */
1597 };
1598
ddb8a639
I
1599 FOREACH_ELEMENT(i, table)
1600 if (streq_ptr(name, i->name)) {
1601 *ret = i->action;
de40a303
LP
1602 return 0;
1603 }
1604
1605 return -EINVAL;
1606}
1607
1608static int oci_seccomp_arch_from_string(const char *name, uint32_t *ret) {
1609
1610 static const struct {
1611 const char *name;
1612 uint32_t arch;
1613 } table[] = {
1614 { "SCMP_ARCH_AARCH64", SCMP_ARCH_AARCH64 },
1615 { "SCMP_ARCH_ARM", SCMP_ARCH_ARM },
f9d3fb6b
XW
1616#ifdef SCMP_ARCH_LOONGARCH64
1617 { "SCMP_ARCH_LOONGARCH64", SCMP_ARCH_LOONGARCH64 },
1618#endif
de40a303
LP
1619 { "SCMP_ARCH_MIPS", SCMP_ARCH_MIPS },
1620 { "SCMP_ARCH_MIPS64", SCMP_ARCH_MIPS64 },
1621 { "SCMP_ARCH_MIPS64N32", SCMP_ARCH_MIPS64N32 },
1622 { "SCMP_ARCH_MIPSEL", SCMP_ARCH_MIPSEL },
1623 { "SCMP_ARCH_MIPSEL64", SCMP_ARCH_MIPSEL64 },
1624 { "SCMP_ARCH_MIPSEL64N32", SCMP_ARCH_MIPSEL64N32 },
1625 { "SCMP_ARCH_NATIVE", SCMP_ARCH_NATIVE },
1626#ifdef SCMP_ARCH_PARISC
1627 { "SCMP_ARCH_PARISC", SCMP_ARCH_PARISC },
1628#endif
1629#ifdef SCMP_ARCH_PARISC64
1630 { "SCMP_ARCH_PARISC64", SCMP_ARCH_PARISC64 },
1631#endif
1632 { "SCMP_ARCH_PPC", SCMP_ARCH_PPC },
1633 { "SCMP_ARCH_PPC64", SCMP_ARCH_PPC64 },
1634 { "SCMP_ARCH_PPC64LE", SCMP_ARCH_PPC64LE },
f9252236
AJ
1635#ifdef SCMP_ARCH_RISCV64
1636 { "SCMP_ARCH_RISCV64", SCMP_ARCH_RISCV64 },
1637#endif
de40a303
LP
1638 { "SCMP_ARCH_S390", SCMP_ARCH_S390 },
1639 { "SCMP_ARCH_S390X", SCMP_ARCH_S390X },
1640 { "SCMP_ARCH_X32", SCMP_ARCH_X32 },
1641 { "SCMP_ARCH_X86", SCMP_ARCH_X86 },
1642 { "SCMP_ARCH_X86_64", SCMP_ARCH_X86_64 },
1643 };
1644
ddb8a639
I
1645 FOREACH_ELEMENT(i, table)
1646 if (streq_ptr(i->name, name)) {
1647 *ret = i->arch;
de40a303
LP
1648 return 0;
1649 }
1650
1651 return -EINVAL;
1652}
1653
1654static int oci_seccomp_compare_from_string(const char *name, enum scmp_compare *ret) {
1655
1656 static const struct {
1657 const char *name;
1658 enum scmp_compare op;
1659 } table[] = {
1660 { "SCMP_CMP_NE", SCMP_CMP_NE },
1661 { "SCMP_CMP_LT", SCMP_CMP_LT },
1662 { "SCMP_CMP_LE", SCMP_CMP_LE },
1663 { "SCMP_CMP_EQ", SCMP_CMP_EQ },
1664 { "SCMP_CMP_GE", SCMP_CMP_GE },
1665 { "SCMP_CMP_GT", SCMP_CMP_GT },
1666 { "SCMP_CMP_MASKED_EQ", SCMP_CMP_MASKED_EQ },
1667 };
1668
ddb8a639
I
1669 FOREACH_ELEMENT(i, table)
1670 if (streq_ptr(i->name, name)) {
1671 *ret = i->op;
de40a303
LP
1672 return 0;
1673 }
1674
1675 return -EINVAL;
1676}
1677
309a747f 1678static int oci_seccomp_archs(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 1679 scmp_filter_ctx *sc = ASSERT_PTR(userdata);
309a747f 1680 sd_json_variant *e;
de40a303
LP
1681 int r;
1682
de40a303
LP
1683 JSON_VARIANT_ARRAY_FOREACH(e, v) {
1684 uint32_t a;
1685
309a747f 1686 if (!sd_json_variant_is_string(e))
19130626 1687 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL),
4e494e6a 1688 "Architecture entry is not a string.");
de40a303 1689
309a747f 1690 r = oci_seccomp_arch_from_string(sd_json_variant_string(e), &a);
de40a303 1691 if (r < 0)
309a747f 1692 return json_log(e, flags, r, "Unknown architecture: %s", sd_json_variant_string(e));
de40a303
LP
1693
1694 r = seccomp_arch_add(sc, a);
1695 if (r == -EEXIST)
1696 continue;
1697 if (r < 0)
1698 return json_log(e, flags, r, "Failed to add architecture to seccomp filter: %m");
1699 }
1700
1701 return 0;
1702}
1703
1704struct syscall_rule {
1705 char **names;
1706 uint32_t action;
1707 struct scmp_arg_cmp *arguments;
1708 size_t n_arguments;
1709};
1710
7244c6db 1711static void syscall_rule_done(struct syscall_rule *rule) {
de40a303
LP
1712 assert(rule);
1713
1714 strv_free(rule->names);
1715 free(rule->arguments);
1716};
1717
309a747f 1718static int oci_seccomp_action(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 1719 uint32_t *action = ASSERT_PTR(userdata);
de40a303
LP
1720 int r;
1721
309a747f 1722 r = oci_seccomp_action_from_string(sd_json_variant_string(v), action);
de40a303 1723 if (r < 0)
309a747f 1724 return json_log(v, flags, r, "Unknown system call action '%s': %m", sd_json_variant_string(v));
de40a303
LP
1725
1726 return 0;
1727}
1728
309a747f 1729static int oci_seccomp_op(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 1730 enum scmp_compare *op = ASSERT_PTR(userdata);
de40a303
LP
1731 int r;
1732
309a747f 1733 r = oci_seccomp_compare_from_string(sd_json_variant_string(v), op);
de40a303 1734 if (r < 0)
309a747f 1735 return json_log(v, flags, r, "Unknown seccomp operator '%s': %m", sd_json_variant_string(v));
de40a303
LP
1736
1737 return 0;
1738}
1739
309a747f 1740static int oci_seccomp_args(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 1741 struct syscall_rule *rule = ASSERT_PTR(userdata);
309a747f 1742 sd_json_variant *e;
de40a303
LP
1743 int r;
1744
de40a303 1745 JSON_VARIANT_ARRAY_FOREACH(e, v) {
26d35019 1746 static const sd_json_dispatch_field table[] = {
309a747f
LP
1747 { "index", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint32, offsetof(struct scmp_arg_cmp, arg), SD_JSON_MANDATORY },
1748 { "value", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint64, offsetof(struct scmp_arg_cmp, datum_a), SD_JSON_MANDATORY },
1749 { "valueTwo", SD_JSON_VARIANT_UNSIGNED, sd_json_dispatch_uint64, offsetof(struct scmp_arg_cmp, datum_b), 0 },
1750 { "op", SD_JSON_VARIANT_STRING, oci_seccomp_op, offsetof(struct scmp_arg_cmp, op), SD_JSON_MANDATORY },
de40a303
LP
1751 {},
1752 };
1753
223a67e5 1754 struct scmp_arg_cmp *p;
de40a303
LP
1755 int expected;
1756
223a67e5 1757 if (!GREEDY_REALLOC(rule->arguments, rule->n_arguments + 1))
de40a303
LP
1758 return log_oom();
1759
de40a303
LP
1760 p = rule->arguments + rule->n_arguments;
1761
1762 *p = (struct scmp_arg_cmp) {
1763 .arg = 0,
1764 .datum_a = 0,
1765 .datum_b = 0,
1766 .op = 0,
1767 };
1768
f1b622a0 1769 r = oci_dispatch(e, table, flags, p);
de40a303
LP
1770 if (r < 0)
1771 return r;
1772
1773 expected = p->op == SCMP_CMP_MASKED_EQ ? 4 : 3;
1774 if (r != expected)
309a747f 1775 json_log(e, flags|SD_JSON_WARNING, 0, "Wrong number of system call arguments for JSON data, ignoring.");
de40a303
LP
1776
1777 /* Note that we are a bit sloppy here and do not insist that SCMP_CMP_MASKED_EQ gets two datum values,
1778 * and the other only one. That's because buildah for example by default calls things with
1779 * SCMP_CMP_MASKED_EQ but only one argument. We use 0 when the value is not specified. */
1780
1781 rule->n_arguments++;
1782 }
1783
1784 return 0;
1785}
1786
309a747f 1787static int oci_seccomp_syscalls(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 1788 scmp_filter_ctx *sc = ASSERT_PTR(userdata);
309a747f 1789 sd_json_variant *e;
de40a303
LP
1790 int r;
1791
de40a303 1792 JSON_VARIANT_ARRAY_FOREACH(e, v) {
309a747f
LP
1793 static const sd_json_dispatch_field table[] = {
1794 { "names", SD_JSON_VARIANT_ARRAY, sd_json_dispatch_strv, offsetof(struct syscall_rule, names), SD_JSON_MANDATORY },
1795 { "action", SD_JSON_VARIANT_STRING, oci_seccomp_action, offsetof(struct syscall_rule, action), SD_JSON_MANDATORY },
1796 { "args", SD_JSON_VARIANT_ARRAY, oci_seccomp_args, 0, 0 },
525c3e34 1797 {}
de40a303 1798 };
f95c9f46 1799 _cleanup_(syscall_rule_done) struct syscall_rule rule = {
f5fbe71d 1800 .action = UINT32_MAX,
de40a303 1801 };
de40a303 1802
f1b622a0 1803 r = oci_dispatch(e, table, flags, &rule);
de40a303 1804 if (r < 0)
7244c6db 1805 return r;
de40a303 1806
34e2897f
FS
1807 if (strv_isempty(rule.names))
1808 return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "System call name list is empty.");
de40a303
LP
1809
1810 STRV_FOREACH(i, rule.names) {
1811 int nr;
1812
1813 nr = seccomp_syscall_resolve_name(*i);
1814 if (nr == __NR_SCMP_ERROR) {
1815 log_debug("Unknown syscall %s, skipping.", *i);
1816 continue;
1817 }
1818
1819 r = seccomp_rule_add_array(sc, rule.action, nr, rule.n_arguments, rule.arguments);
1820 if (r < 0)
7244c6db 1821 return r;
de40a303 1822 }
de40a303
LP
1823 }
1824
1825 return 0;
1826}
ae408d77 1827#endif
de40a303 1828
309a747f 1829static int oci_seccomp(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
de40a303 1830
ae408d77 1831#if HAVE_SECCOMP
309a747f
LP
1832 static const sd_json_dispatch_field table[] = {
1833 { "defaultAction", SD_JSON_VARIANT_STRING, NULL, 0, SD_JSON_MANDATORY },
1834 { "architectures", SD_JSON_VARIANT_ARRAY, oci_seccomp_archs, 0, 0 },
1835 { "syscalls", SD_JSON_VARIANT_ARRAY, oci_seccomp_syscalls, 0, 0 },
de40a303
LP
1836 {}
1837 };
1838
1839 _cleanup_(seccomp_releasep) scmp_filter_ctx sc = NULL;
99534007 1840 Settings *s = ASSERT_PTR(userdata);
309a747f 1841 sd_json_variant *def;
de40a303
LP
1842 uint32_t d;
1843 int r;
1844
309a747f 1845 def = sd_json_variant_by_key(v, "defaultAction");
19130626
ZJS
1846 if (!def)
1847 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), "defaultAction element missing.");
de40a303 1848
309a747f 1849 if (!sd_json_variant_is_string(def))
19130626 1850 return json_log(def, flags, SYNTHETIC_ERRNO(EINVAL), "defaultAction is not a string.");
de40a303 1851
309a747f 1852 r = oci_seccomp_action_from_string(sd_json_variant_string(def), &d);
de40a303 1853 if (r < 0)
309a747f 1854 return json_log(def, flags, r, "Unknown default action: %s", sd_json_variant_string(def));
de40a303
LP
1855
1856 sc = seccomp_init(d);
19130626 1857 if (!sc)
ae408d77 1858 return json_log(v, flags, SYNTHETIC_ERRNO(ENOMEM), "Couldn't allocate seccomp object.");
de40a303 1859
f1b622a0 1860 r = oci_dispatch(v, table, flags, sc);
de40a303
LP
1861 if (r < 0)
1862 return r;
1863
1864 seccomp_release(s->seccomp);
1865 s->seccomp = TAKE_PTR(sc);
de40a303 1866 return 0;
ae408d77
LP
1867#else
1868 return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), "libseccomp support not enabled, can't parse seccomp object.");
1869#endif
de40a303
LP
1870}
1871
309a747f 1872static int oci_rootfs_propagation(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
de40a303
LP
1873 const char *s;
1874
309a747f 1875 s = sd_json_variant_string(v);
de40a303
LP
1876
1877 if (streq(s, "shared"))
1878 return 0;
1879
309a747f 1880 json_log(v, flags|SD_JSON_DEBUG, 0, "Ignoring rootfsPropagation setting '%s'.", s);
de40a303
LP
1881 return 0;
1882}
1883
309a747f 1884static int oci_masked_paths(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 1885 Settings *s = ASSERT_PTR(userdata);
309a747f 1886 sd_json_variant *e;
de40a303 1887
de40a303
LP
1888 JSON_VARIANT_ARRAY_FOREACH(e, v) {
1889 _cleanup_free_ char *destination = NULL;
1890 CustomMount *m;
1891 const char *p;
1892
309a747f 1893 if (!sd_json_variant_is_string(e))
19130626
ZJS
1894 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
1895 "Path is not a string, refusing.");
de40a303 1896
309a747f 1897 assert_se(p = sd_json_variant_string(e));
de40a303 1898
19130626
ZJS
1899 if (!path_is_absolute(p))
1900 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
ad337e55 1901 "Path is not absolute, refusing: %s", p);
de40a303
LP
1902
1903 if (oci_exclude_mount(p))
1904 continue;
1905
1906 destination = strdup(p);
1907 if (!destination)
1908 return log_oom();
1909
1910 m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_INACCESSIBLE);
1911 if (!m)
1912 return log_oom();
1913
1914 m->destination = TAKE_PTR(destination);
1915
1916 /* The spec doesn't say this, but apparently pre-existing implementations are lenient towards
1917 * non-existing paths to mask. Let's hence be too. */
1918 m->graceful = true;
1919 }
1920
1921 return 0;
1922}
1923
309a747f 1924static int oci_readonly_paths(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 1925 Settings *s = ASSERT_PTR(userdata);
309a747f 1926 sd_json_variant *e;
de40a303 1927
de40a303
LP
1928 JSON_VARIANT_ARRAY_FOREACH(e, v) {
1929 _cleanup_free_ char *source = NULL, *destination = NULL;
1930 CustomMount *m;
1931 const char *p;
1932
309a747f 1933 if (!sd_json_variant_is_string(e))
19130626
ZJS
1934 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
1935 "Path is not a string, refusing.");
de40a303 1936
309a747f 1937 assert_se(p = sd_json_variant_string(e));
de40a303 1938
19130626
ZJS
1939 if (!path_is_absolute(p))
1940 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
ad337e55 1941 "Path is not absolute, refusing: %s", p);
de40a303
LP
1942
1943 if (oci_exclude_mount(p))
1944 continue;
1945
1946 source = strjoin("+", p);
1947 if (!source)
1948 return log_oom();
1949
1950 destination = strdup(p);
1951 if (!destination)
1952 return log_oom();
1953
1954 m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_BIND);
1955 if (!m)
1956 return log_oom();
1957
1958 m->source = TAKE_PTR(source);
1959 m->destination = TAKE_PTR(destination);
1960 m->read_only = true;
1961 }
1962
1963 return 0;
1964}
1965
309a747f
LP
1966static int oci_linux(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
1967
1968 static const sd_json_dispatch_field table[] = {
1969 { "namespaces", SD_JSON_VARIANT_ARRAY, oci_namespaces, 0, 0 },
1970 { "uidMappings", SD_JSON_VARIANT_ARRAY, oci_uid_gid_mappings, 0, 0 },
1971 { "gidMappings", SD_JSON_VARIANT_ARRAY, oci_uid_gid_mappings, 0, 0 },
1972 { "devices", SD_JSON_VARIANT_ARRAY, oci_devices, 0, 0 },
1973 { "cgroupsPath", SD_JSON_VARIANT_STRING, oci_cgroups_path, 0, 0 },
1974 { "resources", SD_JSON_VARIANT_OBJECT, oci_resources, 0, 0 },
1975 { "intelRdt", SD_JSON_VARIANT_OBJECT, oci_unsupported, 0, SD_JSON_PERMISSIVE },
1976 { "sysctl", SD_JSON_VARIANT_OBJECT, oci_sysctl, 0, 0 },
1977 { "seccomp", SD_JSON_VARIANT_OBJECT, oci_seccomp, 0, 0 },
1978 { "rootfsPropagation", SD_JSON_VARIANT_STRING, oci_rootfs_propagation, 0, 0 },
1979 { "maskedPaths", SD_JSON_VARIANT_ARRAY, oci_masked_paths, 0, 0 },
1980 { "readonlyPaths", SD_JSON_VARIANT_ARRAY, oci_readonly_paths, 0, 0 },
1981 { "mountLabel", SD_JSON_VARIANT_STRING, oci_unsupported, 0, SD_JSON_PERMISSIVE },
de40a303
LP
1982 {}
1983 };
1984
f1b622a0 1985 return oci_dispatch(v, table, flags, userdata);
de40a303
LP
1986}
1987
309a747f 1988static int oci_hook_timeout(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
79742424 1989 usec_t *u = ASSERT_PTR(userdata);
718ca772 1990 uint64_t k;
de40a303 1991
309a747f 1992 k = sd_json_variant_unsigned(v);
fa8b675a
ZJS
1993 if (k == 0 || k > (UINT64_MAX-1)/USEC_PER_SEC)
1994 return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE),
1995 "Hook timeout value out of range.");
de40a303
LP
1996
1997 *u = k * USEC_PER_SEC;
1998 return 0;
1999}
2000
309a747f 2001static int oci_hooks_array(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
99534007 2002 Settings *s = ASSERT_PTR(userdata);
309a747f 2003 sd_json_variant *e;
de40a303
LP
2004 int r;
2005
de40a303
LP
2006 JSON_VARIANT_ARRAY_FOREACH(e, v) {
2007
309a747f
LP
2008 static const sd_json_dispatch_field table[] = {
2009 { "path", SD_JSON_VARIANT_STRING, json_dispatch_path, offsetof(OciHook, path), SD_JSON_MANDATORY },
2010 { "args", SD_JSON_VARIANT_ARRAY, oci_args, offsetof(OciHook, args), 0, },
2011 { "env", SD_JSON_VARIANT_ARRAY, oci_env, offsetof(OciHook, env), 0 },
2012 { "timeout", SD_JSON_VARIANT_UNSIGNED, oci_hook_timeout, offsetof(OciHook, timeout), 0 },
de40a303
LP
2013 {}
2014 };
2015
223a67e5 2016 OciHook **array, *new_item;
de40a303
LP
2017 size_t *n_array;
2018
2019 if (streq(name, "prestart")) {
2020 array = &s->oci_hooks_prestart;
2021 n_array = &s->n_oci_hooks_prestart;
2022 } else if (streq(name, "poststart")) {
2023 array = &s->oci_hooks_poststart;
2024 n_array = &s->n_oci_hooks_poststart;
2025 } else {
2026 assert(streq(name, "poststop"));
2027 array = &s->oci_hooks_poststop;
2028 n_array = &s->n_oci_hooks_poststop;
2029 }
2030
223a67e5 2031 if (!GREEDY_REALLOC(*array, *n_array + 1))
de40a303
LP
2032 return log_oom();
2033
223a67e5 2034 new_item = *array + *n_array;
de40a303
LP
2035
2036 *new_item = (OciHook) {
2037 .timeout = USEC_INFINITY,
2038 };
2039
f1b622a0 2040 r = oci_dispatch(e, table, flags, new_item);
de40a303
LP
2041 if (r < 0) {
2042 free(new_item->path);
2043 strv_free(new_item->args);
2044 strv_free(new_item->env);
2045 return r;
2046 }
2047
b3a9d980 2048 (*n_array)++;
de40a303
LP
2049 }
2050
2051 return 0;
2052}
2053
309a747f 2054static int oci_hooks(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
de40a303 2055
309a747f
LP
2056 static const sd_json_dispatch_field table[] = {
2057 { "prestart", SD_JSON_VARIANT_ARRAY, oci_hooks_array, 0, 0 },
2058 { "poststart", SD_JSON_VARIANT_ARRAY, oci_hooks_array, 0, 0 },
2059 { "poststop", SD_JSON_VARIANT_ARRAY, oci_hooks_array, 0, 0 },
de40a303
LP
2060 {}
2061 };
2062
f1b622a0 2063 return oci_dispatch(v, table, flags, userdata);
de40a303
LP
2064}
2065
309a747f
LP
2066static int oci_annotations(const char *name, sd_json_variant *v, sd_json_dispatch_flags_t flags, void *userdata) {
2067 sd_json_variant *w;
33d60b8d 2068 const char *k;
de40a303
LP
2069
2070 JSON_VARIANT_OBJECT_FOREACH(k, w, v) {
de40a303 2071
33d60b8d
LP
2072 if (isempty(k))
2073 return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL),
19130626 2074 "Annotation with empty key, refusing.");
de40a303 2075
309a747f 2076 if (!sd_json_variant_is_string(w))
19130626
ZJS
2077 return json_log(w, flags, SYNTHETIC_ERRNO(EINVAL),
2078 "Annotation has non-string value, refusing.");
de40a303 2079
309a747f 2080 json_log(w, flags|SD_JSON_DEBUG, 0, "Ignoring annotation '%s' with value '%s'.", k, sd_json_variant_string(w));
de40a303
LP
2081 }
2082
2083 return 0;
2084}
2085
2086int oci_load(FILE *f, const char *bundle, Settings **ret) {
2087
309a747f
LP
2088 static const sd_json_dispatch_field table[] = {
2089 { "ociVersion", SD_JSON_VARIANT_STRING, NULL, 0, SD_JSON_MANDATORY },
2090 { "process", SD_JSON_VARIANT_OBJECT, oci_process, 0, 0 },
2091 { "root", SD_JSON_VARIANT_OBJECT, oci_root, 0, 0 },
2092 { "hostname", SD_JSON_VARIANT_STRING, oci_hostname, 0, 0 },
2093 { "mounts", SD_JSON_VARIANT_ARRAY, oci_mounts, 0, 0 },
2094 { "linux", SD_JSON_VARIANT_OBJECT, oci_linux, 0, 0 },
2095 { "hooks", SD_JSON_VARIANT_OBJECT, oci_hooks, 0, 0 },
2096 { "annotations", SD_JSON_VARIANT_OBJECT, oci_annotations, 0, 0 },
de40a303
LP
2097 {}
2098 };
2099
309a747f 2100 _cleanup_(sd_json_variant_unrefp) sd_json_variant *oci = NULL;
de40a303
LP
2101 _cleanup_(settings_freep) Settings *s = NULL;
2102 unsigned line = 0, column = 0;
309a747f 2103 sd_json_variant *v;
de40a303
LP
2104 const char *path;
2105 int r;
2106
2107 assert_se(bundle);
2108
2109 path = strjoina(bundle, "/config.json");
2110
309a747f 2111 r = sd_json_parse_file(f, path, 0, &oci, &line, &column);
de40a303
LP
2112 if (r < 0) {
2113 if (line != 0 && column != 0)
2114 return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", path, line, column);
2115 else
2116 return log_error_errno(r, "Failed to parse '%s': %m", path);
2117 }
2118
309a747f 2119 v = sd_json_variant_by_key(oci, "ociVersion");
d7a0f1f4
FS
2120 if (!v)
2121 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2122 "JSON file '%s' is not an OCI bundle configuration file. Refusing.",
2123 path);
309a747f 2124 if (!streq_ptr(sd_json_variant_string(v), "1.0.0"))
d7a0f1f4
FS
2125 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2126 "OCI bundle version not supported: %s",
309a747f 2127 strna(sd_json_variant_string(v)));
de40a303
LP
2128
2129 // {
2130 // _cleanup_free_ char *formatted = NULL;
309a747f 2131 // assert_se(json_variant_format(oci, SD_JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR, &formatted) >= 0);
de40a303
LP
2132 // fputs(formatted, stdout);
2133 // }
2134
2135 s = settings_new();
2136 if (!s)
2137 return log_oom();
2138
2139 s->start_mode = START_PID1;
2140 s->resolv_conf = RESOLV_CONF_OFF;
2141 s->link_journal = LINK_NO;
2142 s->timezone = TIMEZONE_OFF;
2143
2144 s->bundle = strdup(bundle);
2145 if (!s->bundle)
2146 return log_oom();
2147
f1b622a0 2148 r = oci_dispatch(oci, table, 0, s);
de40a303
LP
2149 if (r < 0)
2150 return r;
2151
2152 if (s->properties) {
2153 r = sd_bus_message_seal(s->properties, 0, 0);
2154 if (r < 0)
2155 return log_error_errno(r, "Cannot seal properties bus message: %m");
2156 }
2157
2158 *ret = TAKE_PTR(s);
2159 return 0;
2160}