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