]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/core/unit-serialize.c
Merge pull request #20303 from andir/sysconfig-example
[thirdparty/systemd.git] / src / core / unit-serialize.c
CommitLineData
2d3b784d
ZJS
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
cd09a5f3 3#include "bpf-socket-bind.h"
2d3b784d
ZJS
4#include "bus-util.h"
5#include "dbus.h"
6#include "fileio-label.h"
7#include "fileio.h"
8#include "format-util.h"
9#include "parse-util.h"
6f50d4f7 10#include "restrict-ifaces.h"
2d3b784d
ZJS
11#include "serialize.h"
12#include "string-table.h"
13#include "unit-serialize.h"
14#include "user-util.h"
15
16static int serialize_cgroup_mask(FILE *f, const char *key, CGroupMask mask) {
17 _cleanup_free_ char *s = NULL;
18 int r;
19
20 assert(f);
21 assert(key);
22
23 if (mask == 0)
24 return 0;
25
26 r = cg_mask_to_string(mask, &s);
27 if (r < 0)
28 return log_error_errno(r, "Failed to format cgroup mask: %m");
29
30 return serialize_item(f, key, s);
31}
32
ff68472a
ZJS
33/* Make sure out values fit in the bitfield. */
34assert_cc(_UNIT_MARKER_MAX <= sizeof(((Unit){}).markers) * 8);
35
36static int serialize_markers(FILE *f, unsigned markers) {
37 assert(f);
38
39 if (markers == 0)
40 return 0;
41
42 fputs("markers=", f);
43 for (UnitMarker m = 0; m < _UNIT_MARKER_MAX; m++)
44 if (FLAGS_SET(markers, 1u << m))
45 fputs(unit_marker_to_string(m), f);
46 fputc('\n', f);
47 return 0;
48}
49
50static int deserialize_markers(Unit *u, const char *value) {
51 assert(u);
52 assert(value);
53 int r;
54
55 for (const char *p = value;;) {
56 _cleanup_free_ char *word = NULL;
57
58 r = extract_first_word(&p, &word, NULL, 0);
59 if (r <= 0)
60 return r;
61
62 UnitMarker m = unit_marker_from_string(word);
63 if (m < 0) {
64 log_unit_debug_errno(u, m, "Unknown unit marker \"%s\", ignoring.", word);
65 continue;
66 }
67
68 u->markers |= 1u << m;
69 }
70}
71
2d3b784d
ZJS
72static const char *const ip_accounting_metric_field[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
73 [CGROUP_IP_INGRESS_BYTES] = "ip-accounting-ingress-bytes",
74 [CGROUP_IP_INGRESS_PACKETS] = "ip-accounting-ingress-packets",
75 [CGROUP_IP_EGRESS_BYTES] = "ip-accounting-egress-bytes",
76 [CGROUP_IP_EGRESS_PACKETS] = "ip-accounting-egress-packets",
77};
78
79static const char *const io_accounting_metric_field_base[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
80 [CGROUP_IO_READ_BYTES] = "io-accounting-read-bytes-base",
81 [CGROUP_IO_WRITE_BYTES] = "io-accounting-write-bytes-base",
82 [CGROUP_IO_READ_OPERATIONS] = "io-accounting-read-operations-base",
83 [CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-base",
84};
85
86static const char *const io_accounting_metric_field_last[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
87 [CGROUP_IO_READ_BYTES] = "io-accounting-read-bytes-last",
88 [CGROUP_IO_WRITE_BYTES] = "io-accounting-write-bytes-last",
89 [CGROUP_IO_READ_OPERATIONS] = "io-accounting-read-operations-last",
90 [CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-last",
91};
92
755021d4 93int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool switching_root) {
2d3b784d
ZJS
94 int r;
95
96 assert(u);
97 assert(f);
98 assert(fds);
99
755021d4
ZJS
100 if (switching_root && UNIT_VTABLE(u)->exclude_from_switch_root_serialization) {
101 /* In the new root, paths for mounts and automounts will be different, so it doesn't make
102 * much sense to serialize things. API file systems will be moved to the new root, but we
103 * don't have mount units for those. */
104 log_unit_debug(u, "not serializing before switch-root");
105 return 0;
106 }
107
108 /* Start marker */
109 fputs(u->id, f);
110 fputc('\n', f);
111
1085c0eb
ZJS
112 assert(!!UNIT_VTABLE(u)->serialize == !!UNIT_VTABLE(u)->deserialize_item);
113
114 if (UNIT_VTABLE(u)->serialize) {
2d3b784d
ZJS
115 r = UNIT_VTABLE(u)->serialize(u, f, fds);
116 if (r < 0)
117 return r;
118 }
119
120 (void) serialize_dual_timestamp(f, "state-change-timestamp", &u->state_change_timestamp);
121
122 (void) serialize_dual_timestamp(f, "inactive-exit-timestamp", &u->inactive_exit_timestamp);
123 (void) serialize_dual_timestamp(f, "active-enter-timestamp", &u->active_enter_timestamp);
124 (void) serialize_dual_timestamp(f, "active-exit-timestamp", &u->active_exit_timestamp);
125 (void) serialize_dual_timestamp(f, "inactive-enter-timestamp", &u->inactive_enter_timestamp);
126
127 (void) serialize_dual_timestamp(f, "condition-timestamp", &u->condition_timestamp);
128 (void) serialize_dual_timestamp(f, "assert-timestamp", &u->assert_timestamp);
129
130 if (dual_timestamp_is_set(&u->condition_timestamp))
131 (void) serialize_bool(f, "condition-result", u->condition_result);
132
133 if (dual_timestamp_is_set(&u->assert_timestamp))
134 (void) serialize_bool(f, "assert-result", u->assert_result);
135
136 (void) serialize_bool(f, "transient", u->transient);
137 (void) serialize_bool(f, "in-audit", u->in_audit);
138
139 (void) serialize_bool(f, "exported-invocation-id", u->exported_invocation_id);
140 (void) serialize_bool(f, "exported-log-level-max", u->exported_log_level_max);
141 (void) serialize_bool(f, "exported-log-extra-fields", u->exported_log_extra_fields);
142 (void) serialize_bool(f, "exported-log-rate-limit-interval", u->exported_log_ratelimit_interval);
143 (void) serialize_bool(f, "exported-log-rate-limit-burst", u->exported_log_ratelimit_burst);
144
145 (void) serialize_item_format(f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base);
146 if (u->cpu_usage_last != NSEC_INFINITY)
147 (void) serialize_item_format(f, "cpu-usage-last", "%" PRIu64, u->cpu_usage_last);
148
149 if (u->managed_oom_kill_last > 0)
150 (void) serialize_item_format(f, "managed-oom-kill-last", "%" PRIu64, u->managed_oom_kill_last);
151
152 if (u->oom_kill_last > 0)
153 (void) serialize_item_format(f, "oom-kill-last", "%" PRIu64, u->oom_kill_last);
154
155 for (CGroupIOAccountingMetric im = 0; im < _CGROUP_IO_ACCOUNTING_METRIC_MAX; im++) {
156 (void) serialize_item_format(f, io_accounting_metric_field_base[im], "%" PRIu64, u->io_accounting_base[im]);
157
158 if (u->io_accounting_last[im] != UINT64_MAX)
159 (void) serialize_item_format(f, io_accounting_metric_field_last[im], "%" PRIu64, u->io_accounting_last[im]);
160 }
161
162 if (u->cgroup_path)
163 (void) serialize_item(f, "cgroup", u->cgroup_path);
164
165 (void) serialize_bool(f, "cgroup-realized", u->cgroup_realized);
166 (void) serialize_cgroup_mask(f, "cgroup-realized-mask", u->cgroup_realized_mask);
167 (void) serialize_cgroup_mask(f, "cgroup-enabled-mask", u->cgroup_enabled_mask);
168 (void) serialize_cgroup_mask(f, "cgroup-invalidated-mask", u->cgroup_invalidated_mask);
169
cd09a5f3 170 (void) bpf_serialize_socket_bind(u, f, fds);
3d027d4d 171
b57d7523
LP
172 (void) bpf_program_serialize_attachment(f, fds, "ip-bpf-ingress-installed", u->ip_bpf_ingress_installed);
173 (void) bpf_program_serialize_attachment(f, fds, "ip-bpf-egress-installed", u->ip_bpf_egress_installed);
174 (void) bpf_program_serialize_attachment_set(f, fds, "ip-bpf-custom-ingress-installed", u->ip_bpf_custom_ingress_installed);
175 (void) bpf_program_serialize_attachment_set(f, fds, "ip-bpf-custom-egress-installed", u->ip_bpf_custom_egress_installed);
176
6f50d4f7
MV
177 (void) serialize_restrict_network_interfaces(u, f, fds);
178
2d3b784d
ZJS
179 if (uid_is_valid(u->ref_uid))
180 (void) serialize_item_format(f, "ref-uid", UID_FMT, u->ref_uid);
181 if (gid_is_valid(u->ref_gid))
182 (void) serialize_item_format(f, "ref-gid", GID_FMT, u->ref_gid);
183
184 if (!sd_id128_is_null(u->invocation_id))
185 (void) serialize_item_format(f, "invocation-id", SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id));
186
187 (void) serialize_item_format(f, "freezer-state", "%s", freezer_state_to_string(unit_freezer_state(u)));
ff68472a 188 (void) serialize_markers(f, u->markers);
2d3b784d
ZJS
189
190 bus_track_serialize(u->bus_track, f, "ref");
191
192 for (CGroupIPAccountingMetric m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) {
193 uint64_t v;
194
195 r = unit_get_ip_accounting(u, m, &v);
196 if (r >= 0)
197 (void) serialize_item_format(f, ip_accounting_metric_field[m], "%" PRIu64, v);
198 }
199
755021d4 200 if (!switching_root) {
2d3b784d
ZJS
201 if (u->job) {
202 fputs("job\n", f);
203 job_serialize(u->job, f);
204 }
205
206 if (u->nop_job) {
207 fputs("job\n", f);
208 job_serialize(u->nop_job, f);
209 }
210 }
211
212 /* End marker */
213 fputc('\n', f);
214 return 0;
215}
216
217static int unit_deserialize_job(Unit *u, FILE *f) {
218 _cleanup_(job_freep) Job *j = NULL;
219 int r;
220
221 assert(u);
222 assert(f);
223
224 j = job_new_raw(u);
225 if (!j)
226 return log_oom();
227
228 r = job_deserialize(j, f);
229 if (r < 0)
230 return r;
231
232 r = job_install_deserialized(j);
233 if (r < 0)
234 return r;
235
236 TAKE_PTR(j);
237 return 0;
238}
239
9466ec13
ZJS
240#define MATCH_DESERIALIZE(key, l, v, parse_func, target) \
241 ({ \
242 bool _deserialize_matched = streq(l, key); \
243 if (_deserialize_matched) { \
244 int _deserialize_r = parse_func(v); \
245 if (_deserialize_r < 0) \
246 log_unit_debug_errno(u, _deserialize_r, \
247 "Failed to parse \"%s=%s\", ignoring.", l, v); \
248 else \
249 target = _deserialize_r; \
250 }; \
251 _deserialize_matched; \
252 })
253
254#define MATCH_DESERIALIZE_IMMEDIATE(key, l, v, parse_func, target) \
255 ({ \
256 bool _deserialize_matched = streq(l, key); \
257 if (_deserialize_matched) { \
258 int _deserialize_r = parse_func(v, &target); \
259 if (_deserialize_r < 0) \
260 log_unit_debug_errno(u, _deserialize_r, \
261 "Failed to parse \"%s=%s\", ignoring", l, v); \
262 }; \
263 _deserialize_matched; \
264 })
265
2d3b784d
ZJS
266int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
267 int r;
268
269 assert(u);
270 assert(f);
271 assert(fds);
272
273 for (;;) {
274 _cleanup_free_ char *line = NULL;
275 char *l, *v;
276 ssize_t m;
277 size_t k;
278
279 r = read_line(f, LONG_LINE_MAX, &line);
280 if (r < 0)
281 return log_error_errno(r, "Failed to read serialization line: %m");
282 if (r == 0) /* eof */
283 break;
284
285 l = strstrip(line);
286 if (isempty(l)) /* End marker */
287 break;
288
289 k = strcspn(l, "=");
290
291 if (l[k] == '=') {
292 l[k] = 0;
293 v = l+k+1;
294 } else
295 v = l+k;
296
297 if (streq(l, "job")) {
298 if (v[0] == '\0') {
299 /* New-style serialized job */
300 r = unit_deserialize_job(u, f);
301 if (r < 0)
302 return r;
303 } else /* Legacy for pre-44 */
304 log_unit_warning(u, "Update from too old systemd versions are unsupported, cannot deserialize job: %s", v);
305 continue;
306 } else if (streq(l, "state-change-timestamp")) {
307 (void) deserialize_dual_timestamp(v, &u->state_change_timestamp);
308 continue;
309 } else if (streq(l, "inactive-exit-timestamp")) {
310 (void) deserialize_dual_timestamp(v, &u->inactive_exit_timestamp);
311 continue;
312 } else if (streq(l, "active-enter-timestamp")) {
313 (void) deserialize_dual_timestamp(v, &u->active_enter_timestamp);
314 continue;
315 } else if (streq(l, "active-exit-timestamp")) {
316 (void) deserialize_dual_timestamp(v, &u->active_exit_timestamp);
317 continue;
318 } else if (streq(l, "inactive-enter-timestamp")) {
319 (void) deserialize_dual_timestamp(v, &u->inactive_enter_timestamp);
320 continue;
321 } else if (streq(l, "condition-timestamp")) {
322 (void) deserialize_dual_timestamp(v, &u->condition_timestamp);
323 continue;
324 } else if (streq(l, "assert-timestamp")) {
325 (void) deserialize_dual_timestamp(v, &u->assert_timestamp);
326 continue;
2d3b784d 327
9466ec13 328 } else if (MATCH_DESERIALIZE("condition-result", l, v, parse_boolean, u->condition_result))
2d3b784d
ZJS
329 continue;
330
9466ec13 331 else if (MATCH_DESERIALIZE("assert-result", l, v, parse_boolean, u->assert_result))
2d3b784d
ZJS
332 continue;
333
9466ec13 334 else if (MATCH_DESERIALIZE("transient", l, v, parse_boolean, u->transient))
2d3b784d
ZJS
335 continue;
336
9466ec13 337 else if (MATCH_DESERIALIZE("in-audit", l, v, parse_boolean, u->in_audit))
2d3b784d
ZJS
338 continue;
339
9466ec13 340 else if (MATCH_DESERIALIZE("exported-invocation-id", l, v, parse_boolean, u->exported_invocation_id))
2d3b784d
ZJS
341 continue;
342
9466ec13 343 else if (MATCH_DESERIALIZE("exported-log-level-max", l, v, parse_boolean, u->exported_log_level_max))
2d3b784d
ZJS
344 continue;
345
9466ec13 346 else if (MATCH_DESERIALIZE("exported-log-extra-fields", l, v, parse_boolean, u->exported_log_extra_fields))
2d3b784d
ZJS
347 continue;
348
9466ec13 349 else if (MATCH_DESERIALIZE("exported-log-rate-limit-interval", l, v, parse_boolean, u->exported_log_ratelimit_interval))
2d3b784d
ZJS
350 continue;
351
9466ec13 352 else if (MATCH_DESERIALIZE("exported-log-rate-limit-burst", l, v, parse_boolean, u->exported_log_ratelimit_burst))
2d3b784d
ZJS
353 continue;
354
9466ec13
ZJS
355 else if (MATCH_DESERIALIZE_IMMEDIATE("cpu-usage-base", l, v, safe_atou64, u->cpu_usage_base) ||
356 MATCH_DESERIALIZE_IMMEDIATE("cpuacct-usage-base", l, v, safe_atou64, u->cpu_usage_base))
2d3b784d
ZJS
357 continue;
358
9466ec13 359 else if (MATCH_DESERIALIZE_IMMEDIATE("cpu-usage-last", l, v, safe_atou64, u->cpu_usage_last))
2d3b784d
ZJS
360 continue;
361
9466ec13 362 else if (MATCH_DESERIALIZE_IMMEDIATE("managed-oom-kill-last", l, v, safe_atou64, u->managed_oom_kill_last))
2d3b784d
ZJS
363 continue;
364
9466ec13 365 else if (MATCH_DESERIALIZE_IMMEDIATE("oom-kill-last", l, v, safe_atou64, u->oom_kill_last))
2d3b784d
ZJS
366 continue;
367
9466ec13 368 else if (streq(l, "cgroup")) {
2d3b784d
ZJS
369 r = unit_set_cgroup_path(u, v);
370 if (r < 0)
371 log_unit_debug_errno(u, r, "Failed to set cgroup path %s, ignoring: %m", v);
372
373 (void) unit_watch_cgroup(u);
374 (void) unit_watch_cgroup_memory(u);
375
376 continue;
2d3b784d 377
9466ec13 378 } else if (MATCH_DESERIALIZE("cgroup-realized", l, v, parse_boolean, u->cgroup_realized))
2d3b784d
ZJS
379 continue;
380
9466ec13 381 else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-realized-mask", l, v, cg_mask_from_string, u->cgroup_realized_mask))
2d3b784d
ZJS
382 continue;
383
9466ec13 384 else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-enabled-mask", l, v, cg_mask_from_string, u->cgroup_enabled_mask))
2d3b784d
ZJS
385 continue;
386
9466ec13 387 else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-invalidated-mask", l, v, cg_mask_from_string, u->cgroup_invalidated_mask))
2d3b784d
ZJS
388 continue;
389
3d027d4d
JK
390 else if (STR_IN_SET(l, "ipv4-socket-bind-bpf-link-fd", "ipv6-socket-bind-bpf-link-fd")) {
391 int fd;
392
393 if (safe_atoi(v, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
394 log_unit_debug(u, "Failed to parse %s value: %s, ignoring.", l, v);
395 else {
396 if (fdset_remove(fds, fd) < 0) {
397 log_unit_debug(u, "Failed to remove %s value=%d from fdset", l, fd);
3d027d4d
JK
398 continue;
399 }
400
cd09a5f3 401 (void) bpf_socket_bind_add_initial_link_fd(u, fd);
3d027d4d
JK
402 }
403 continue;
3d027d4d 404
b57d7523
LP
405 } else if (streq(l, "ip-bpf-ingress-installed")) {
406 (void) bpf_program_deserialize_attachment(v, fds, &u->ip_bpf_ingress_installed);
407 continue;
408 } else if (streq(l, "ip-bpf-egress-installed")) {
409 (void) bpf_program_deserialize_attachment(v, fds, &u->ip_bpf_egress_installed);
410 continue;
411
412 } else if (streq(l, "ip-bpf-custom-ingress-installed")) {
413 (void) bpf_program_deserialize_attachment_set(v, fds, &u->ip_bpf_custom_ingress_installed);
414 continue;
415 } else if (streq(l, "ip-bpf-custom-egress-installed")) {
416 (void) bpf_program_deserialize_attachment_set(v, fds, &u->ip_bpf_custom_egress_installed);
417 continue;
418
6f50d4f7
MV
419 } else if (streq(l, "restrict-ifaces-bpf-fd")) {
420 int fd;
421
422 if (safe_atoi(v, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd)) {
423 log_unit_debug(u, "Failed to parse restrict-ifaces-bpf-fd value: %s", v);
424 continue;
425 }
426 if (fdset_remove(fds, fd) < 0) {
427 log_unit_debug(u, "Failed to remove restrict-ifaces-bpf-fd %d from fdset", fd);
428 continue;
429 }
430
431 (void) restrict_network_interfaces_add_initial_link_fd(u, fd);
432 continue;
433
b57d7523 434 } else if (streq(l, "ref-uid")) {
2d3b784d
ZJS
435 uid_t uid;
436
437 r = parse_uid(v, &uid);
438 if (r < 0)
9466ec13 439 log_unit_debug(u, "Failed to parse \"%s=%s\", ignoring.", l, v);
2d3b784d
ZJS
440 else
441 unit_ref_uid_gid(u, uid, GID_INVALID);
2d3b784d
ZJS
442 continue;
443
444 } else if (streq(l, "ref-gid")) {
445 gid_t gid;
446
447 r = parse_gid(v, &gid);
448 if (r < 0)
9466ec13 449 log_unit_debug(u, "Failed to parse \"%s=%s\", ignoring.", l, v);
2d3b784d
ZJS
450 else
451 unit_ref_uid_gid(u, UID_INVALID, gid);
2d3b784d
ZJS
452 continue;
453
454 } else if (streq(l, "ref")) {
2d3b784d
ZJS
455 r = strv_extend(&u->deserialized_refs, v);
456 if (r < 0)
457 return log_oom();
2d3b784d 458 continue;
9466ec13 459
2d3b784d
ZJS
460 } else if (streq(l, "invocation-id")) {
461 sd_id128_t id;
462
463 r = sd_id128_from_string(v, &id);
464 if (r < 0)
9466ec13 465 log_unit_debug(u, "Failed to parse \"%s=%s\", ignoring.", l, v);
2d3b784d
ZJS
466 else {
467 r = unit_set_invocation_id(u, id);
468 if (r < 0)
469 log_unit_warning_errno(u, r, "Failed to set invocation ID for unit: %m");
470 }
471
472 continue;
2d3b784d 473
9466ec13 474 } else if (MATCH_DESERIALIZE("freezer-state", l, v, freezer_state_from_string, u->freezer_state))
2d3b784d 475 continue;
2d3b784d 476
ff68472a
ZJS
477 else if (streq(l, "markers")) {
478 r = deserialize_markers(u, v);
479 if (r < 0)
480 log_unit_debug_errno(u, r, "Failed to deserialize \"%s=%s\", ignoring: %m", l, v);
481 continue;
482 }
483
2d3b784d
ZJS
484 /* Check if this is an IP accounting metric serialization field */
485 m = string_table_lookup(ip_accounting_metric_field, ELEMENTSOF(ip_accounting_metric_field), l);
486 if (m >= 0) {
487 uint64_t c;
488
489 r = safe_atou64(v, &c);
490 if (r < 0)
491 log_unit_debug(u, "Failed to parse IP accounting value %s, ignoring.", v);
492 else
493 u->ip_accounting_extra[m] = c;
494 continue;
495 }
496
497 m = string_table_lookup(io_accounting_metric_field_base, ELEMENTSOF(io_accounting_metric_field_base), l);
498 if (m >= 0) {
499 uint64_t c;
500
501 r = safe_atou64(v, &c);
502 if (r < 0)
503 log_unit_debug(u, "Failed to parse IO accounting base value %s, ignoring.", v);
504 else
505 u->io_accounting_base[m] = c;
506 continue;
507 }
508
509 m = string_table_lookup(io_accounting_metric_field_last, ELEMENTSOF(io_accounting_metric_field_last), l);
510 if (m >= 0) {
511 uint64_t c;
512
513 r = safe_atou64(v, &c);
514 if (r < 0)
515 log_unit_debug(u, "Failed to parse IO accounting last value %s, ignoring.", v);
516 else
517 u->io_accounting_last[m] = c;
518 continue;
519 }
520
fe50aae5
ZJS
521 r = exec_runtime_deserialize_compat(u, l, v, fds);
522 if (r < 0) {
523 log_unit_warning(u, "Failed to deserialize runtime parameter '%s', ignoring.", l);
524 continue;
525 } else if (r > 0)
2d3b784d 526 /* Returns positive if key was handled by the call */
fe50aae5 527 continue;
2d3b784d 528
1085c0eb 529 if (UNIT_VTABLE(u)->deserialize_item) {
2d3b784d
ZJS
530 r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds);
531 if (r < 0)
532 log_unit_warning(u, "Failed to deserialize unit parameter '%s', ignoring.", l);
533 }
534 }
535
536 /* Versions before 228 did not carry a state change timestamp. In this case, take the current
537 * time. This is useful, so that timeouts based on this timestamp don't trigger too early, and is
538 * in-line with the logic from before 228 where the base for timeouts was not persistent across
539 * reboots. */
540
541 if (!dual_timestamp_is_set(&u->state_change_timestamp))
542 dual_timestamp_get(&u->state_change_timestamp);
543
544 /* Let's make sure that everything that is deserialized also gets any potential new cgroup settings
545 * applied after we are done. For that we invalidate anything already realized, so that we can
546 * realize it again. */
cc815b7f
MK
547 if (u->cgroup_realized) {
548 unit_invalidate_cgroup(u, _CGROUP_MASK_ALL);
549 unit_invalidate_cgroup_bpf(u);
550 }
2d3b784d
ZJS
551
552 return 0;
553}
554
555int unit_deserialize_skip(FILE *f) {
556 int r;
557 assert(f);
558
559 /* Skip serialized data for this unit. We don't know what it is. */
560
561 for (;;) {
562 _cleanup_free_ char *line = NULL;
563 char *l;
564
565 r = read_line(f, LONG_LINE_MAX, &line);
566 if (r < 0)
567 return log_error_errno(r, "Failed to read serialization line: %m");
568 if (r == 0)
569 return 0;
570
571 l = strstrip(line);
572
573 /* End marker */
574 if (isempty(l))
575 return 1;
576 }
577}
578
579static void print_unit_dependency_mask(FILE *f, const char *kind, UnitDependencyMask mask, bool *space) {
580 const struct {
581 UnitDependencyMask mask;
582 const char *name;
583 } table[] = {
584 { UNIT_DEPENDENCY_FILE, "file" },
585 { UNIT_DEPENDENCY_IMPLICIT, "implicit" },
586 { UNIT_DEPENDENCY_DEFAULT, "default" },
587 { UNIT_DEPENDENCY_UDEV, "udev" },
588 { UNIT_DEPENDENCY_PATH, "path" },
589 { UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT, "mountinfo-implicit" },
590 { UNIT_DEPENDENCY_MOUNTINFO_DEFAULT, "mountinfo-default" },
591 { UNIT_DEPENDENCY_PROC_SWAP, "proc-swap" },
592 };
593
594 assert(f);
595 assert(kind);
596 assert(space);
597
598 for (size_t i = 0; i < ELEMENTSOF(table); i++) {
599
600 if (mask == 0)
601 break;
602
603 if (FLAGS_SET(mask, table[i].mask)) {
604 if (*space)
605 fputc(' ', f);
606 else
607 *space = true;
608
609 fputs(kind, f);
610 fputs("-", f);
611 fputs(table[i].name, f);
612
613 mask &= ~table[i].mask;
614 }
615 }
616
617 assert(mask == 0);
618}
619
620void unit_dump(Unit *u, FILE *f, const char *prefix) {
621 char *t, **j;
622 const char *prefix2;
2d3b784d
ZJS
623 Unit *following;
624 _cleanup_set_free_ Set *following_set = NULL;
625 CGroupMask m;
626 int r;
627
628 assert(u);
629 assert(u->type >= 0);
630
631 prefix = strempty(prefix);
632 prefix2 = strjoina(prefix, "\t");
633
634 fprintf(f,
635 "%s-> Unit %s:\n",
636 prefix, u->id);
637
638 SET_FOREACH(t, u->aliases)
639 fprintf(f, "%s\tAlias: %s\n", prefix, t);
640
641 fprintf(f,
642 "%s\tDescription: %s\n"
643 "%s\tInstance: %s\n"
644 "%s\tUnit Load State: %s\n"
645 "%s\tUnit Active State: %s\n"
646 "%s\tState Change Timestamp: %s\n"
647 "%s\tInactive Exit Timestamp: %s\n"
648 "%s\tActive Enter Timestamp: %s\n"
649 "%s\tActive Exit Timestamp: %s\n"
650 "%s\tInactive Enter Timestamp: %s\n"
651 "%s\tMay GC: %s\n"
652 "%s\tNeed Daemon Reload: %s\n"
653 "%s\tTransient: %s\n"
654 "%s\tPerpetual: %s\n"
39628fed 655 "%s\tGarbage Collection Mode: %s\n",
2d3b784d
ZJS
656 prefix, unit_description(u),
657 prefix, strna(u->instance),
658 prefix, unit_load_state_to_string(u->load_state),
659 prefix, unit_active_state_to_string(unit_active_state(u)),
04f5c018
ZJS
660 prefix, strna(FORMAT_TIMESTAMP(u->state_change_timestamp.realtime)),
661 prefix, strna(FORMAT_TIMESTAMP(u->inactive_exit_timestamp.realtime)),
662 prefix, strna(FORMAT_TIMESTAMP(u->active_enter_timestamp.realtime)),
663 prefix, strna(FORMAT_TIMESTAMP(u->active_exit_timestamp.realtime)),
664 prefix, strna(FORMAT_TIMESTAMP(u->inactive_enter_timestamp.realtime)),
2d3b784d
ZJS
665 prefix, yes_no(unit_may_gc(u)),
666 prefix, yes_no(unit_need_daemon_reload(u)),
667 prefix, yes_no(u->transient),
668 prefix, yes_no(u->perpetual),
39628fed 669 prefix, collect_mode_to_string(u->collect_mode));
2d3b784d 670
ff68472a
ZJS
671 if (u->markers != 0) {
672 fprintf(f, "%s\tMarkers:", prefix);
673
674 for (UnitMarker marker = 0; marker < _UNIT_MARKER_MAX; marker++)
675 if (FLAGS_SET(u->markers, 1u << marker))
676 fprintf(f, " %s", unit_marker_to_string(marker));
677 fputs("\n", f);
678 }
679
39628fed
LP
680 if (UNIT_HAS_CGROUP_CONTEXT(u)) {
681 fprintf(f,
682 "%s\tSlice: %s\n"
683 "%s\tCGroup: %s\n"
684 "%s\tCGroup realized: %s\n",
685 prefix, strna(unit_slice_name(u)),
686 prefix, strna(u->cgroup_path),
687 prefix, yes_no(u->cgroup_realized));
688
689 if (u->cgroup_realized_mask != 0) {
690 _cleanup_free_ char *s = NULL;
691 (void) cg_mask_to_string(u->cgroup_realized_mask, &s);
692 fprintf(f, "%s\tCGroup realized mask: %s\n", prefix, strnull(s));
693 }
2d3b784d 694
39628fed
LP
695 if (u->cgroup_enabled_mask != 0) {
696 _cleanup_free_ char *s = NULL;
697 (void) cg_mask_to_string(u->cgroup_enabled_mask, &s);
698 fprintf(f, "%s\tCGroup enabled mask: %s\n", prefix, strnull(s));
699 }
2d3b784d 700
39628fed
LP
701 m = unit_get_own_mask(u);
702 if (m != 0) {
703 _cleanup_free_ char *s = NULL;
704 (void) cg_mask_to_string(m, &s);
705 fprintf(f, "%s\tCGroup own mask: %s\n", prefix, strnull(s));
706 }
2d3b784d 707
39628fed
LP
708 m = unit_get_members_mask(u);
709 if (m != 0) {
710 _cleanup_free_ char *s = NULL;
711 (void) cg_mask_to_string(m, &s);
712 fprintf(f, "%s\tCGroup members mask: %s\n", prefix, strnull(s));
713 }
2d3b784d 714
39628fed
LP
715 m = unit_get_delegate_mask(u);
716 if (m != 0) {
717 _cleanup_free_ char *s = NULL;
718 (void) cg_mask_to_string(m, &s);
719 fprintf(f, "%s\tCGroup delegate mask: %s\n", prefix, strnull(s));
720 }
2d3b784d
ZJS
721 }
722
723 if (!sd_id128_is_null(u->invocation_id))
724 fprintf(f, "%s\tInvocation ID: " SD_ID128_FORMAT_STR "\n",
725 prefix, SD_ID128_FORMAT_VAL(u->invocation_id));
726
727 STRV_FOREACH(j, u->documentation)
728 fprintf(f, "%s\tDocumentation: %s\n", prefix, *j);
729
730 following = unit_following(u);
731 if (following)
732 fprintf(f, "%s\tFollowing: %s\n", prefix, following->id);
733
734 r = unit_following_set(u, &following_set);
735 if (r >= 0) {
736 Unit *other;
737
738 SET_FOREACH(other, following_set)
739 fprintf(f, "%s\tFollowing Set Member: %s\n", prefix, other->id);
740 }
741
742 if (u->fragment_path)
743 fprintf(f, "%s\tFragment Path: %s\n", prefix, u->fragment_path);
744
745 if (u->source_path)
746 fprintf(f, "%s\tSource Path: %s\n", prefix, u->source_path);
747
748 STRV_FOREACH(j, u->dropin_paths)
749 fprintf(f, "%s\tDropIn Path: %s\n", prefix, *j);
750
751 if (u->failure_action != EMERGENCY_ACTION_NONE)
752 fprintf(f, "%s\tFailure Action: %s\n", prefix, emergency_action_to_string(u->failure_action));
753 if (u->failure_action_exit_status >= 0)
754 fprintf(f, "%s\tFailure Action Exit Status: %i\n", prefix, u->failure_action_exit_status);
755 if (u->success_action != EMERGENCY_ACTION_NONE)
756 fprintf(f, "%s\tSuccess Action: %s\n", prefix, emergency_action_to_string(u->success_action));
757 if (u->success_action_exit_status >= 0)
758 fprintf(f, "%s\tSuccess Action Exit Status: %i\n", prefix, u->success_action_exit_status);
759
760 if (u->job_timeout != USEC_INFINITY)
5291f26d 761 fprintf(f, "%s\tJob Timeout: %s\n", prefix, FORMAT_TIMESPAN(u->job_timeout, 0));
2d3b784d
ZJS
762
763 if (u->job_timeout_action != EMERGENCY_ACTION_NONE)
764 fprintf(f, "%s\tJob Timeout Action: %s\n", prefix, emergency_action_to_string(u->job_timeout_action));
765
766 if (u->job_timeout_reboot_arg)
767 fprintf(f, "%s\tJob Timeout Reboot Argument: %s\n", prefix, u->job_timeout_reboot_arg);
768
769 condition_dump_list(u->conditions, f, prefix, condition_type_to_string);
770 condition_dump_list(u->asserts, f, prefix, assert_type_to_string);
771
772 if (dual_timestamp_is_set(&u->condition_timestamp))
773 fprintf(f,
774 "%s\tCondition Timestamp: %s\n"
775 "%s\tCondition Result: %s\n",
04f5c018 776 prefix, strna(FORMAT_TIMESTAMP(u->condition_timestamp.realtime)),
2d3b784d
ZJS
777 prefix, yes_no(u->condition_result));
778
779 if (dual_timestamp_is_set(&u->assert_timestamp))
780 fprintf(f,
781 "%s\tAssert Timestamp: %s\n"
782 "%s\tAssert Result: %s\n",
04f5c018 783 prefix, strna(FORMAT_TIMESTAMP(u->assert_timestamp.realtime)),
2d3b784d
ZJS
784 prefix, yes_no(u->assert_result));
785
786 for (UnitDependency d = 0; d < _UNIT_DEPENDENCY_MAX; d++) {
787 UnitDependencyInfo di;
788 Unit *other;
789
15ed3c3a 790 HASHMAP_FOREACH_KEY(di.data, other, unit_get_dependencies(u, d)) {
2d3b784d
ZJS
791 bool space = false;
792
793 fprintf(f, "%s\t%s: %s (", prefix, unit_dependency_to_string(d), other->id);
794
795 print_unit_dependency_mask(f, "origin", di.origin_mask, &space);
796 print_unit_dependency_mask(f, "destination", di.destination_mask, &space);
797
798 fputs(")\n", f);
799 }
800 }
801
802 if (!hashmap_isempty(u->requires_mounts_for)) {
803 UnitDependencyInfo di;
804 const char *path;
805
806 HASHMAP_FOREACH_KEY(di.data, path, u->requires_mounts_for) {
807 bool space = false;
808
809 fprintf(f, "%s\tRequiresMountsFor: %s (", prefix, path);
810
811 print_unit_dependency_mask(f, "origin", di.origin_mask, &space);
812 print_unit_dependency_mask(f, "destination", di.destination_mask, &space);
813
814 fputs(")\n", f);
815 }
816 }
817
818 if (u->load_state == UNIT_LOADED) {
819
820 fprintf(f,
821 "%s\tStopWhenUnneeded: %s\n"
822 "%s\tRefuseManualStart: %s\n"
823 "%s\tRefuseManualStop: %s\n"
824 "%s\tDefaultDependencies: %s\n"
294446dc 825 "%s\tOnSuccessJobMode: %s\n"
2d3b784d
ZJS
826 "%s\tOnFailureJobMode: %s\n"
827 "%s\tIgnoreOnIsolate: %s\n",
828 prefix, yes_no(u->stop_when_unneeded),
829 prefix, yes_no(u->refuse_manual_start),
830 prefix, yes_no(u->refuse_manual_stop),
831 prefix, yes_no(u->default_dependencies),
294446dc 832 prefix, job_mode_to_string(u->on_success_job_mode),
2d3b784d
ZJS
833 prefix, job_mode_to_string(u->on_failure_job_mode),
834 prefix, yes_no(u->ignore_on_isolate));
835
836 if (UNIT_VTABLE(u)->dump)
837 UNIT_VTABLE(u)->dump(u, f, prefix2);
838
839 } else if (u->load_state == UNIT_MERGED)
840 fprintf(f,
841 "%s\tMerged into: %s\n",
842 prefix, u->merged_into->id);
843 else if (u->load_state == UNIT_ERROR)
844 fprintf(f, "%s\tLoad Error Code: %s\n", prefix, strerror_safe(u->load_error));
845
846 for (const char *n = sd_bus_track_first(u->bus_track); n; n = sd_bus_track_next(u->bus_track))
847 fprintf(f, "%s\tBus Ref: %s\n", prefix, n);
848
849 if (u->job)
850 job_dump(u->job, f, prefix2);
851
852 if (u->nop_job)
853 job_dump(u->nop_job, f, prefix2);
854}