1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "fileio-label.h"
7 #include "format-util.h"
8 #include "parse-util.h"
10 #include "socket-bind.h"
11 #include "string-table.h"
12 #include "unit-serialize.h"
13 #include "user-util.h"
15 static int serialize_cgroup_mask(FILE *f
, const char *key
, CGroupMask mask
) {
16 _cleanup_free_
char *s
= NULL
;
25 r
= cg_mask_to_string(mask
, &s
);
27 return log_error_errno(r
, "Failed to format cgroup mask: %m");
29 return serialize_item(f
, key
, s
);
32 /* Make sure out values fit in the bitfield. */
33 assert_cc(_UNIT_MARKER_MAX
<= sizeof(((Unit
){}).markers
) * 8);
35 static int serialize_markers(FILE *f
, unsigned markers
) {
42 for (UnitMarker m
= 0; m
< _UNIT_MARKER_MAX
; m
++)
43 if (FLAGS_SET(markers
, 1u << m
))
44 fputs(unit_marker_to_string(m
), f
);
49 static int deserialize_markers(Unit
*u
, const char *value
) {
54 for (const char *p
= value
;;) {
55 _cleanup_free_
char *word
= NULL
;
57 r
= extract_first_word(&p
, &word
, NULL
, 0);
61 UnitMarker m
= unit_marker_from_string(word
);
63 log_unit_debug_errno(u
, m
, "Unknown unit marker \"%s\", ignoring.", word
);
67 u
->markers
|= 1u << m
;
71 static const char *const ip_accounting_metric_field
[_CGROUP_IP_ACCOUNTING_METRIC_MAX
] = {
72 [CGROUP_IP_INGRESS_BYTES
] = "ip-accounting-ingress-bytes",
73 [CGROUP_IP_INGRESS_PACKETS
] = "ip-accounting-ingress-packets",
74 [CGROUP_IP_EGRESS_BYTES
] = "ip-accounting-egress-bytes",
75 [CGROUP_IP_EGRESS_PACKETS
] = "ip-accounting-egress-packets",
78 static const char *const io_accounting_metric_field_base
[_CGROUP_IO_ACCOUNTING_METRIC_MAX
] = {
79 [CGROUP_IO_READ_BYTES
] = "io-accounting-read-bytes-base",
80 [CGROUP_IO_WRITE_BYTES
] = "io-accounting-write-bytes-base",
81 [CGROUP_IO_READ_OPERATIONS
] = "io-accounting-read-operations-base",
82 [CGROUP_IO_WRITE_OPERATIONS
] = "io-accounting-write-operations-base",
85 static const char *const io_accounting_metric_field_last
[_CGROUP_IO_ACCOUNTING_METRIC_MAX
] = {
86 [CGROUP_IO_READ_BYTES
] = "io-accounting-read-bytes-last",
87 [CGROUP_IO_WRITE_BYTES
] = "io-accounting-write-bytes-last",
88 [CGROUP_IO_READ_OPERATIONS
] = "io-accounting-read-operations-last",
89 [CGROUP_IO_WRITE_OPERATIONS
] = "io-accounting-write-operations-last",
92 int unit_serialize(Unit
*u
, FILE *f
, FDSet
*fds
, bool serialize_jobs
) {
99 if (unit_can_serialize(u
)) {
100 r
= UNIT_VTABLE(u
)->serialize(u
, f
, fds
);
105 (void) serialize_dual_timestamp(f
, "state-change-timestamp", &u
->state_change_timestamp
);
107 (void) serialize_dual_timestamp(f
, "inactive-exit-timestamp", &u
->inactive_exit_timestamp
);
108 (void) serialize_dual_timestamp(f
, "active-enter-timestamp", &u
->active_enter_timestamp
);
109 (void) serialize_dual_timestamp(f
, "active-exit-timestamp", &u
->active_exit_timestamp
);
110 (void) serialize_dual_timestamp(f
, "inactive-enter-timestamp", &u
->inactive_enter_timestamp
);
112 (void) serialize_dual_timestamp(f
, "condition-timestamp", &u
->condition_timestamp
);
113 (void) serialize_dual_timestamp(f
, "assert-timestamp", &u
->assert_timestamp
);
115 if (dual_timestamp_is_set(&u
->condition_timestamp
))
116 (void) serialize_bool(f
, "condition-result", u
->condition_result
);
118 if (dual_timestamp_is_set(&u
->assert_timestamp
))
119 (void) serialize_bool(f
, "assert-result", u
->assert_result
);
121 (void) serialize_bool(f
, "transient", u
->transient
);
122 (void) serialize_bool(f
, "in-audit", u
->in_audit
);
124 (void) serialize_bool(f
, "exported-invocation-id", u
->exported_invocation_id
);
125 (void) serialize_bool(f
, "exported-log-level-max", u
->exported_log_level_max
);
126 (void) serialize_bool(f
, "exported-log-extra-fields", u
->exported_log_extra_fields
);
127 (void) serialize_bool(f
, "exported-log-rate-limit-interval", u
->exported_log_ratelimit_interval
);
128 (void) serialize_bool(f
, "exported-log-rate-limit-burst", u
->exported_log_ratelimit_burst
);
130 (void) serialize_item_format(f
, "cpu-usage-base", "%" PRIu64
, u
->cpu_usage_base
);
131 if (u
->cpu_usage_last
!= NSEC_INFINITY
)
132 (void) serialize_item_format(f
, "cpu-usage-last", "%" PRIu64
, u
->cpu_usage_last
);
134 if (u
->managed_oom_kill_last
> 0)
135 (void) serialize_item_format(f
, "managed-oom-kill-last", "%" PRIu64
, u
->managed_oom_kill_last
);
137 if (u
->oom_kill_last
> 0)
138 (void) serialize_item_format(f
, "oom-kill-last", "%" PRIu64
, u
->oom_kill_last
);
140 for (CGroupIOAccountingMetric im
= 0; im
< _CGROUP_IO_ACCOUNTING_METRIC_MAX
; im
++) {
141 (void) serialize_item_format(f
, io_accounting_metric_field_base
[im
], "%" PRIu64
, u
->io_accounting_base
[im
]);
143 if (u
->io_accounting_last
[im
] != UINT64_MAX
)
144 (void) serialize_item_format(f
, io_accounting_metric_field_last
[im
], "%" PRIu64
, u
->io_accounting_last
[im
]);
148 (void) serialize_item(f
, "cgroup", u
->cgroup_path
);
150 (void) serialize_bool(f
, "cgroup-realized", u
->cgroup_realized
);
151 (void) serialize_cgroup_mask(f
, "cgroup-realized-mask", u
->cgroup_realized_mask
);
152 (void) serialize_cgroup_mask(f
, "cgroup-enabled-mask", u
->cgroup_enabled_mask
);
153 (void) serialize_cgroup_mask(f
, "cgroup-invalidated-mask", u
->cgroup_invalidated_mask
);
155 (void) serialize_socket_bind(u
, f
, fds
);
157 if (uid_is_valid(u
->ref_uid
))
158 (void) serialize_item_format(f
, "ref-uid", UID_FMT
, u
->ref_uid
);
159 if (gid_is_valid(u
->ref_gid
))
160 (void) serialize_item_format(f
, "ref-gid", GID_FMT
, u
->ref_gid
);
162 if (!sd_id128_is_null(u
->invocation_id
))
163 (void) serialize_item_format(f
, "invocation-id", SD_ID128_FORMAT_STR
, SD_ID128_FORMAT_VAL(u
->invocation_id
));
165 (void) serialize_item_format(f
, "freezer-state", "%s", freezer_state_to_string(unit_freezer_state(u
)));
166 (void) serialize_markers(f
, u
->markers
);
168 bus_track_serialize(u
->bus_track
, f
, "ref");
170 for (CGroupIPAccountingMetric m
= 0; m
< _CGROUP_IP_ACCOUNTING_METRIC_MAX
; m
++) {
173 r
= unit_get_ip_accounting(u
, m
, &v
);
175 (void) serialize_item_format(f
, ip_accounting_metric_field
[m
], "%" PRIu64
, v
);
178 if (serialize_jobs
) {
181 job_serialize(u
->job
, f
);
186 job_serialize(u
->nop_job
, f
);
195 static int unit_deserialize_job(Unit
*u
, FILE *f
) {
196 _cleanup_(job_freep
) Job
*j
= NULL
;
206 r
= job_deserialize(j
, f
);
210 r
= job_install_deserialized(j
);
218 #define MATCH_DESERIALIZE(key, l, v, parse_func, target) \
220 bool _deserialize_matched = streq(l, key); \
221 if (_deserialize_matched) { \
222 int _deserialize_r = parse_func(v); \
223 if (_deserialize_r < 0) \
224 log_unit_debug_errno(u, _deserialize_r, \
225 "Failed to parse \"%s=%s\", ignoring.", l, v); \
227 target = _deserialize_r; \
229 _deserialize_matched; \
232 #define MATCH_DESERIALIZE_IMMEDIATE(key, l, v, parse_func, target) \
234 bool _deserialize_matched = streq(l, key); \
235 if (_deserialize_matched) { \
236 int _deserialize_r = parse_func(v, &target); \
237 if (_deserialize_r < 0) \
238 log_unit_debug_errno(u, _deserialize_r, \
239 "Failed to parse \"%s=%s\", ignoring", l, v); \
241 _deserialize_matched; \
244 int unit_deserialize(Unit
*u
, FILE *f
, FDSet
*fds
) {
252 _cleanup_free_
char *line
= NULL
;
257 r
= read_line(f
, LONG_LINE_MAX
, &line
);
259 return log_error_errno(r
, "Failed to read serialization line: %m");
260 if (r
== 0) /* eof */
264 if (isempty(l
)) /* End marker */
275 if (streq(l
, "job")) {
277 /* New-style serialized job */
278 r
= unit_deserialize_job(u
, f
);
281 } else /* Legacy for pre-44 */
282 log_unit_warning(u
, "Update from too old systemd versions are unsupported, cannot deserialize job: %s", v
);
284 } else if (streq(l
, "state-change-timestamp")) {
285 (void) deserialize_dual_timestamp(v
, &u
->state_change_timestamp
);
287 } else if (streq(l
, "inactive-exit-timestamp")) {
288 (void) deserialize_dual_timestamp(v
, &u
->inactive_exit_timestamp
);
290 } else if (streq(l
, "active-enter-timestamp")) {
291 (void) deserialize_dual_timestamp(v
, &u
->active_enter_timestamp
);
293 } else if (streq(l
, "active-exit-timestamp")) {
294 (void) deserialize_dual_timestamp(v
, &u
->active_exit_timestamp
);
296 } else if (streq(l
, "inactive-enter-timestamp")) {
297 (void) deserialize_dual_timestamp(v
, &u
->inactive_enter_timestamp
);
299 } else if (streq(l
, "condition-timestamp")) {
300 (void) deserialize_dual_timestamp(v
, &u
->condition_timestamp
);
302 } else if (streq(l
, "assert-timestamp")) {
303 (void) deserialize_dual_timestamp(v
, &u
->assert_timestamp
);
306 } else if (MATCH_DESERIALIZE("condition-result", l
, v
, parse_boolean
, u
->condition_result
))
309 else if (MATCH_DESERIALIZE("assert-result", l
, v
, parse_boolean
, u
->assert_result
))
312 else if (MATCH_DESERIALIZE("transient", l
, v
, parse_boolean
, u
->transient
))
315 else if (MATCH_DESERIALIZE("in-audit", l
, v
, parse_boolean
, u
->in_audit
))
318 else if (MATCH_DESERIALIZE("exported-invocation-id", l
, v
, parse_boolean
, u
->exported_invocation_id
))
321 else if (MATCH_DESERIALIZE("exported-log-level-max", l
, v
, parse_boolean
, u
->exported_log_level_max
))
324 else if (MATCH_DESERIALIZE("exported-log-extra-fields", l
, v
, parse_boolean
, u
->exported_log_extra_fields
))
327 else if (MATCH_DESERIALIZE("exported-log-rate-limit-interval", l
, v
, parse_boolean
, u
->exported_log_ratelimit_interval
))
330 else if (MATCH_DESERIALIZE("exported-log-rate-limit-burst", l
, v
, parse_boolean
, u
->exported_log_ratelimit_burst
))
333 else if (MATCH_DESERIALIZE_IMMEDIATE("cpu-usage-base", l
, v
, safe_atou64
, u
->cpu_usage_base
) ||
334 MATCH_DESERIALIZE_IMMEDIATE("cpuacct-usage-base", l
, v
, safe_atou64
, u
->cpu_usage_base
))
337 else if (MATCH_DESERIALIZE_IMMEDIATE("cpu-usage-last", l
, v
, safe_atou64
, u
->cpu_usage_last
))
340 else if (MATCH_DESERIALIZE_IMMEDIATE("managed-oom-kill-last", l
, v
, safe_atou64
, u
->managed_oom_kill_last
))
343 else if (MATCH_DESERIALIZE_IMMEDIATE("oom-kill-last", l
, v
, safe_atou64
, u
->oom_kill_last
))
346 else if (streq(l
, "cgroup")) {
347 r
= unit_set_cgroup_path(u
, v
);
349 log_unit_debug_errno(u
, r
, "Failed to set cgroup path %s, ignoring: %m", v
);
351 (void) unit_watch_cgroup(u
);
352 (void) unit_watch_cgroup_memory(u
);
356 } else if (MATCH_DESERIALIZE("cgroup-realized", l
, v
, parse_boolean
, u
->cgroup_realized
))
359 else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-realized-mask", l
, v
, cg_mask_from_string
, u
->cgroup_realized_mask
))
362 else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-enabled-mask", l
, v
, cg_mask_from_string
, u
->cgroup_enabled_mask
))
365 else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-invalidated-mask", l
, v
, cg_mask_from_string
, u
->cgroup_invalidated_mask
))
368 else if (STR_IN_SET(l
, "ipv4-socket-bind-bpf-link-fd", "ipv6-socket-bind-bpf-link-fd")) {
371 if (safe_atoi(v
, &fd
) < 0 || fd
< 0 || !fdset_contains(fds
, fd
))
372 log_unit_debug(u
, "Failed to parse %s value: %s, ignoring.", l
, v
);
374 if (fdset_remove(fds
, fd
) < 0) {
375 log_unit_debug(u
, "Failed to remove %s value=%d from fdset", l
, fd
);
380 (void) socket_bind_add_initial_link_fd(u
, fd
);
385 else if (streq(l
, "ref-uid")) {
388 r
= parse_uid(v
, &uid
);
390 log_unit_debug(u
, "Failed to parse \"%s=%s\", ignoring.", l
, v
);
392 unit_ref_uid_gid(u
, uid
, GID_INVALID
);
395 } else if (streq(l
, "ref-gid")) {
398 r
= parse_gid(v
, &gid
);
400 log_unit_debug(u
, "Failed to parse \"%s=%s\", ignoring.", l
, v
);
402 unit_ref_uid_gid(u
, UID_INVALID
, gid
);
405 } else if (streq(l
, "ref")) {
406 r
= strv_extend(&u
->deserialized_refs
, v
);
411 } else if (streq(l
, "invocation-id")) {
414 r
= sd_id128_from_string(v
, &id
);
416 log_unit_debug(u
, "Failed to parse \"%s=%s\", ignoring.", l
, v
);
418 r
= unit_set_invocation_id(u
, id
);
420 log_unit_warning_errno(u
, r
, "Failed to set invocation ID for unit: %m");
425 } else if (MATCH_DESERIALIZE("freezer-state", l
, v
, freezer_state_from_string
, u
->freezer_state
))
428 else if (streq(l
, "markers")) {
429 r
= deserialize_markers(u
, v
);
431 log_unit_debug_errno(u
, r
, "Failed to deserialize \"%s=%s\", ignoring: %m", l
, v
);
435 /* Check if this is an IP accounting metric serialization field */
436 m
= string_table_lookup(ip_accounting_metric_field
, ELEMENTSOF(ip_accounting_metric_field
), l
);
440 r
= safe_atou64(v
, &c
);
442 log_unit_debug(u
, "Failed to parse IP accounting value %s, ignoring.", v
);
444 u
->ip_accounting_extra
[m
] = c
;
448 m
= string_table_lookup(io_accounting_metric_field_base
, ELEMENTSOF(io_accounting_metric_field_base
), l
);
452 r
= safe_atou64(v
, &c
);
454 log_unit_debug(u
, "Failed to parse IO accounting base value %s, ignoring.", v
);
456 u
->io_accounting_base
[m
] = c
;
460 m
= string_table_lookup(io_accounting_metric_field_last
, ELEMENTSOF(io_accounting_metric_field_last
), l
);
464 r
= safe_atou64(v
, &c
);
466 log_unit_debug(u
, "Failed to parse IO accounting last value %s, ignoring.", v
);
468 u
->io_accounting_last
[m
] = c
;
472 if (unit_can_serialize(u
)) {
473 r
= exec_runtime_deserialize_compat(u
, l
, v
, fds
);
475 log_unit_warning(u
, "Failed to deserialize runtime parameter '%s', ignoring.", l
);
479 /* Returns positive if key was handled by the call */
483 r
= UNIT_VTABLE(u
)->deserialize_item(u
, l
, v
, fds
);
485 log_unit_warning(u
, "Failed to deserialize unit parameter '%s', ignoring.", l
);
489 /* Versions before 228 did not carry a state change timestamp. In this case, take the current
490 * time. This is useful, so that timeouts based on this timestamp don't trigger too early, and is
491 * in-line with the logic from before 228 where the base for timeouts was not persistent across
494 if (!dual_timestamp_is_set(&u
->state_change_timestamp
))
495 dual_timestamp_get(&u
->state_change_timestamp
);
497 /* Let's make sure that everything that is deserialized also gets any potential new cgroup settings
498 * applied after we are done. For that we invalidate anything already realized, so that we can
499 * realize it again. */
500 unit_invalidate_cgroup(u
, _CGROUP_MASK_ALL
);
501 unit_invalidate_cgroup_bpf(u
);
506 int unit_deserialize_skip(FILE *f
) {
510 /* Skip serialized data for this unit. We don't know what it is. */
513 _cleanup_free_
char *line
= NULL
;
516 r
= read_line(f
, LONG_LINE_MAX
, &line
);
518 return log_error_errno(r
, "Failed to read serialization line: %m");
530 static void print_unit_dependency_mask(FILE *f
, const char *kind
, UnitDependencyMask mask
, bool *space
) {
532 UnitDependencyMask mask
;
535 { UNIT_DEPENDENCY_FILE
, "file" },
536 { UNIT_DEPENDENCY_IMPLICIT
, "implicit" },
537 { UNIT_DEPENDENCY_DEFAULT
, "default" },
538 { UNIT_DEPENDENCY_UDEV
, "udev" },
539 { UNIT_DEPENDENCY_PATH
, "path" },
540 { UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT
, "mountinfo-implicit" },
541 { UNIT_DEPENDENCY_MOUNTINFO_DEFAULT
, "mountinfo-default" },
542 { UNIT_DEPENDENCY_PROC_SWAP
, "proc-swap" },
549 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++) {
554 if (FLAGS_SET(mask
, table
[i
].mask
)) {
562 fputs(table
[i
].name
, f
);
564 mask
&= ~table
[i
].mask
;
571 void unit_dump(Unit
*u
, FILE *f
, const char *prefix
) {
574 char timestamp
[5][FORMAT_TIMESTAMP_MAX
], timespan
[FORMAT_TIMESPAN_MAX
];
576 _cleanup_set_free_ Set
*following_set
= NULL
;
581 assert(u
->type
>= 0);
583 prefix
= strempty(prefix
);
584 prefix2
= strjoina(prefix
, "\t");
590 SET_FOREACH(t
, u
->aliases
)
591 fprintf(f
, "%s\tAlias: %s\n", prefix
, t
);
594 "%s\tDescription: %s\n"
596 "%s\tUnit Load State: %s\n"
597 "%s\tUnit Active State: %s\n"
598 "%s\tState Change Timestamp: %s\n"
599 "%s\tInactive Exit Timestamp: %s\n"
600 "%s\tActive Enter Timestamp: %s\n"
601 "%s\tActive Exit Timestamp: %s\n"
602 "%s\tInactive Enter Timestamp: %s\n"
604 "%s\tNeed Daemon Reload: %s\n"
605 "%s\tTransient: %s\n"
606 "%s\tPerpetual: %s\n"
607 "%s\tGarbage Collection Mode: %s\n",
608 prefix
, unit_description(u
),
609 prefix
, strna(u
->instance
),
610 prefix
, unit_load_state_to_string(u
->load_state
),
611 prefix
, unit_active_state_to_string(unit_active_state(u
)),
612 prefix
, strna(format_timestamp(timestamp
[0], sizeof(timestamp
[0]), u
->state_change_timestamp
.realtime
)),
613 prefix
, strna(format_timestamp(timestamp
[1], sizeof(timestamp
[1]), u
->inactive_exit_timestamp
.realtime
)),
614 prefix
, strna(format_timestamp(timestamp
[2], sizeof(timestamp
[2]), u
->active_enter_timestamp
.realtime
)),
615 prefix
, strna(format_timestamp(timestamp
[3], sizeof(timestamp
[3]), u
->active_exit_timestamp
.realtime
)),
616 prefix
, strna(format_timestamp(timestamp
[4], sizeof(timestamp
[4]), u
->inactive_enter_timestamp
.realtime
)),
617 prefix
, yes_no(unit_may_gc(u
)),
618 prefix
, yes_no(unit_need_daemon_reload(u
)),
619 prefix
, yes_no(u
->transient
),
620 prefix
, yes_no(u
->perpetual
),
621 prefix
, collect_mode_to_string(u
->collect_mode
));
623 if (u
->markers
!= 0) {
624 fprintf(f
, "%s\tMarkers:", prefix
);
626 for (UnitMarker marker
= 0; marker
< _UNIT_MARKER_MAX
; marker
++)
627 if (FLAGS_SET(u
->markers
, 1u << marker
))
628 fprintf(f
, " %s", unit_marker_to_string(marker
));
632 if (UNIT_HAS_CGROUP_CONTEXT(u
)) {
636 "%s\tCGroup realized: %s\n",
637 prefix
, strna(unit_slice_name(u
)),
638 prefix
, strna(u
->cgroup_path
),
639 prefix
, yes_no(u
->cgroup_realized
));
641 if (u
->cgroup_realized_mask
!= 0) {
642 _cleanup_free_
char *s
= NULL
;
643 (void) cg_mask_to_string(u
->cgroup_realized_mask
, &s
);
644 fprintf(f
, "%s\tCGroup realized mask: %s\n", prefix
, strnull(s
));
647 if (u
->cgroup_enabled_mask
!= 0) {
648 _cleanup_free_
char *s
= NULL
;
649 (void) cg_mask_to_string(u
->cgroup_enabled_mask
, &s
);
650 fprintf(f
, "%s\tCGroup enabled mask: %s\n", prefix
, strnull(s
));
653 m
= unit_get_own_mask(u
);
655 _cleanup_free_
char *s
= NULL
;
656 (void) cg_mask_to_string(m
, &s
);
657 fprintf(f
, "%s\tCGroup own mask: %s\n", prefix
, strnull(s
));
660 m
= unit_get_members_mask(u
);
662 _cleanup_free_
char *s
= NULL
;
663 (void) cg_mask_to_string(m
, &s
);
664 fprintf(f
, "%s\tCGroup members mask: %s\n", prefix
, strnull(s
));
667 m
= unit_get_delegate_mask(u
);
669 _cleanup_free_
char *s
= NULL
;
670 (void) cg_mask_to_string(m
, &s
);
671 fprintf(f
, "%s\tCGroup delegate mask: %s\n", prefix
, strnull(s
));
675 if (!sd_id128_is_null(u
->invocation_id
))
676 fprintf(f
, "%s\tInvocation ID: " SD_ID128_FORMAT_STR
"\n",
677 prefix
, SD_ID128_FORMAT_VAL(u
->invocation_id
));
679 STRV_FOREACH(j
, u
->documentation
)
680 fprintf(f
, "%s\tDocumentation: %s\n", prefix
, *j
);
682 following
= unit_following(u
);
684 fprintf(f
, "%s\tFollowing: %s\n", prefix
, following
->id
);
686 r
= unit_following_set(u
, &following_set
);
690 SET_FOREACH(other
, following_set
)
691 fprintf(f
, "%s\tFollowing Set Member: %s\n", prefix
, other
->id
);
694 if (u
->fragment_path
)
695 fprintf(f
, "%s\tFragment Path: %s\n", prefix
, u
->fragment_path
);
698 fprintf(f
, "%s\tSource Path: %s\n", prefix
, u
->source_path
);
700 STRV_FOREACH(j
, u
->dropin_paths
)
701 fprintf(f
, "%s\tDropIn Path: %s\n", prefix
, *j
);
703 if (u
->failure_action
!= EMERGENCY_ACTION_NONE
)
704 fprintf(f
, "%s\tFailure Action: %s\n", prefix
, emergency_action_to_string(u
->failure_action
));
705 if (u
->failure_action_exit_status
>= 0)
706 fprintf(f
, "%s\tFailure Action Exit Status: %i\n", prefix
, u
->failure_action_exit_status
);
707 if (u
->success_action
!= EMERGENCY_ACTION_NONE
)
708 fprintf(f
, "%s\tSuccess Action: %s\n", prefix
, emergency_action_to_string(u
->success_action
));
709 if (u
->success_action_exit_status
>= 0)
710 fprintf(f
, "%s\tSuccess Action Exit Status: %i\n", prefix
, u
->success_action_exit_status
);
712 if (u
->job_timeout
!= USEC_INFINITY
)
713 fprintf(f
, "%s\tJob Timeout: %s\n", prefix
, format_timespan(timespan
, sizeof(timespan
), u
->job_timeout
, 0));
715 if (u
->job_timeout_action
!= EMERGENCY_ACTION_NONE
)
716 fprintf(f
, "%s\tJob Timeout Action: %s\n", prefix
, emergency_action_to_string(u
->job_timeout_action
));
718 if (u
->job_timeout_reboot_arg
)
719 fprintf(f
, "%s\tJob Timeout Reboot Argument: %s\n", prefix
, u
->job_timeout_reboot_arg
);
721 condition_dump_list(u
->conditions
, f
, prefix
, condition_type_to_string
);
722 condition_dump_list(u
->asserts
, f
, prefix
, assert_type_to_string
);
724 if (dual_timestamp_is_set(&u
->condition_timestamp
))
726 "%s\tCondition Timestamp: %s\n"
727 "%s\tCondition Result: %s\n",
728 prefix
, strna(format_timestamp(timestamp
[0], sizeof(timestamp
[0]), u
->condition_timestamp
.realtime
)),
729 prefix
, yes_no(u
->condition_result
));
731 if (dual_timestamp_is_set(&u
->assert_timestamp
))
733 "%s\tAssert Timestamp: %s\n"
734 "%s\tAssert Result: %s\n",
735 prefix
, strna(format_timestamp(timestamp
[0], sizeof(timestamp
[0]), u
->assert_timestamp
.realtime
)),
736 prefix
, yes_no(u
->assert_result
));
738 for (UnitDependency d
= 0; d
< _UNIT_DEPENDENCY_MAX
; d
++) {
739 UnitDependencyInfo di
;
742 HASHMAP_FOREACH_KEY(di
.data
, other
, unit_get_dependencies(u
, d
)) {
745 fprintf(f
, "%s\t%s: %s (", prefix
, unit_dependency_to_string(d
), other
->id
);
747 print_unit_dependency_mask(f
, "origin", di
.origin_mask
, &space
);
748 print_unit_dependency_mask(f
, "destination", di
.destination_mask
, &space
);
754 if (!hashmap_isempty(u
->requires_mounts_for
)) {
755 UnitDependencyInfo di
;
758 HASHMAP_FOREACH_KEY(di
.data
, path
, u
->requires_mounts_for
) {
761 fprintf(f
, "%s\tRequiresMountsFor: %s (", prefix
, path
);
763 print_unit_dependency_mask(f
, "origin", di
.origin_mask
, &space
);
764 print_unit_dependency_mask(f
, "destination", di
.destination_mask
, &space
);
770 if (u
->load_state
== UNIT_LOADED
) {
773 "%s\tStopWhenUnneeded: %s\n"
774 "%s\tRefuseManualStart: %s\n"
775 "%s\tRefuseManualStop: %s\n"
776 "%s\tDefaultDependencies: %s\n"
777 "%s\tOnFailureJobMode: %s\n"
778 "%s\tIgnoreOnIsolate: %s\n",
779 prefix
, yes_no(u
->stop_when_unneeded
),
780 prefix
, yes_no(u
->refuse_manual_start
),
781 prefix
, yes_no(u
->refuse_manual_stop
),
782 prefix
, yes_no(u
->default_dependencies
),
783 prefix
, job_mode_to_string(u
->on_failure_job_mode
),
784 prefix
, yes_no(u
->ignore_on_isolate
));
786 if (UNIT_VTABLE(u
)->dump
)
787 UNIT_VTABLE(u
)->dump(u
, f
, prefix2
);
789 } else if (u
->load_state
== UNIT_MERGED
)
791 "%s\tMerged into: %s\n",
792 prefix
, u
->merged_into
->id
);
793 else if (u
->load_state
== UNIT_ERROR
)
794 fprintf(f
, "%s\tLoad Error Code: %s\n", prefix
, strerror_safe(u
->load_error
));
796 for (const char *n
= sd_bus_track_first(u
->bus_track
); n
; n
= sd_bus_track_next(u
->bus_track
))
797 fprintf(f
, "%s\tBus Ref: %s\n", prefix
, n
);
800 job_dump(u
->job
, f
, prefix2
);
803 job_dump(u
->nop_job
, f
, prefix2
);