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 "string-table.h"
11 #include "unit-serialize.h"
12 #include "user-util.h"
14 static int serialize_cgroup_mask(FILE *f
, const char *key
, CGroupMask mask
) {
15 _cleanup_free_
char *s
= NULL
;
24 r
= cg_mask_to_string(mask
, &s
);
26 return log_error_errno(r
, "Failed to format cgroup mask: %m");
28 return serialize_item(f
, key
, s
);
31 /* Make sure out values fit in the bitfield. */
32 assert_cc(_UNIT_MARKER_MAX
<= sizeof(((Unit
){}).markers
) * 8);
34 static int serialize_markers(FILE *f
, unsigned markers
) {
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
);
48 static int deserialize_markers(Unit
*u
, const char *value
) {
53 for (const char *p
= value
;;) {
54 _cleanup_free_
char *word
= NULL
;
56 r
= extract_first_word(&p
, &word
, NULL
, 0);
60 UnitMarker m
= unit_marker_from_string(word
);
62 log_unit_debug_errno(u
, m
, "Unknown unit marker \"%s\", ignoring.", word
);
66 u
->markers
|= 1u << m
;
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",
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",
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",
91 int unit_serialize(Unit
*u
, FILE *f
, FDSet
*fds
, bool serialize_jobs
) {
98 if (unit_can_serialize(u
)) {
99 r
= UNIT_VTABLE(u
)->serialize(u
, f
, fds
);
104 (void) serialize_dual_timestamp(f
, "state-change-timestamp", &u
->state_change_timestamp
);
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
);
111 (void) serialize_dual_timestamp(f
, "condition-timestamp", &u
->condition_timestamp
);
112 (void) serialize_dual_timestamp(f
, "assert-timestamp", &u
->assert_timestamp
);
114 if (dual_timestamp_is_set(&u
->condition_timestamp
))
115 (void) serialize_bool(f
, "condition-result", u
->condition_result
);
117 if (dual_timestamp_is_set(&u
->assert_timestamp
))
118 (void) serialize_bool(f
, "assert-result", u
->assert_result
);
120 (void) serialize_bool(f
, "transient", u
->transient
);
121 (void) serialize_bool(f
, "in-audit", u
->in_audit
);
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
);
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
);
133 if (u
->managed_oom_kill_last
> 0)
134 (void) serialize_item_format(f
, "managed-oom-kill-last", "%" PRIu64
, u
->managed_oom_kill_last
);
136 if (u
->oom_kill_last
> 0)
137 (void) serialize_item_format(f
, "oom-kill-last", "%" PRIu64
, u
->oom_kill_last
);
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
]);
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
]);
147 (void) serialize_item(f
, "cgroup", u
->cgroup_path
);
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
);
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
);
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
));
162 (void) serialize_item_format(f
, "freezer-state", "%s", freezer_state_to_string(unit_freezer_state(u
)));
163 (void) serialize_markers(f
, u
->markers
);
165 bus_track_serialize(u
->bus_track
, f
, "ref");
167 for (CGroupIPAccountingMetric m
= 0; m
< _CGROUP_IP_ACCOUNTING_METRIC_MAX
; m
++) {
170 r
= unit_get_ip_accounting(u
, m
, &v
);
172 (void) serialize_item_format(f
, ip_accounting_metric_field
[m
], "%" PRIu64
, v
);
175 if (serialize_jobs
) {
178 job_serialize(u
->job
, f
);
183 job_serialize(u
->nop_job
, f
);
192 static int unit_deserialize_job(Unit
*u
, FILE *f
) {
193 _cleanup_(job_freep
) Job
*j
= NULL
;
203 r
= job_deserialize(j
, f
);
207 r
= job_install_deserialized(j
);
215 #define MATCH_DESERIALIZE(key, l, v, parse_func, target) \
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); \
224 target = _deserialize_r; \
226 _deserialize_matched; \
229 #define MATCH_DESERIALIZE_IMMEDIATE(key, l, v, parse_func, target) \
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); \
238 _deserialize_matched; \
241 int unit_deserialize(Unit
*u
, FILE *f
, FDSet
*fds
) {
249 _cleanup_free_
char *line
= NULL
;
254 r
= read_line(f
, LONG_LINE_MAX
, &line
);
256 return log_error_errno(r
, "Failed to read serialization line: %m");
257 if (r
== 0) /* eof */
261 if (isempty(l
)) /* End marker */
272 if (streq(l
, "job")) {
274 /* New-style serialized job */
275 r
= unit_deserialize_job(u
, f
);
278 } else /* Legacy for pre-44 */
279 log_unit_warning(u
, "Update from too old systemd versions are unsupported, cannot deserialize job: %s", v
);
281 } else if (streq(l
, "state-change-timestamp")) {
282 (void) deserialize_dual_timestamp(v
, &u
->state_change_timestamp
);
284 } else if (streq(l
, "inactive-exit-timestamp")) {
285 (void) deserialize_dual_timestamp(v
, &u
->inactive_exit_timestamp
);
287 } else if (streq(l
, "active-enter-timestamp")) {
288 (void) deserialize_dual_timestamp(v
, &u
->active_enter_timestamp
);
290 } else if (streq(l
, "active-exit-timestamp")) {
291 (void) deserialize_dual_timestamp(v
, &u
->active_exit_timestamp
);
293 } else if (streq(l
, "inactive-enter-timestamp")) {
294 (void) deserialize_dual_timestamp(v
, &u
->inactive_enter_timestamp
);
296 } else if (streq(l
, "condition-timestamp")) {
297 (void) deserialize_dual_timestamp(v
, &u
->condition_timestamp
);
299 } else if (streq(l
, "assert-timestamp")) {
300 (void) deserialize_dual_timestamp(v
, &u
->assert_timestamp
);
303 } else if (MATCH_DESERIALIZE("condition-result", l
, v
, parse_boolean
, u
->condition_result
))
306 else if (MATCH_DESERIALIZE("assert-result", l
, v
, parse_boolean
, u
->assert_result
))
309 else if (MATCH_DESERIALIZE("transient", l
, v
, parse_boolean
, u
->transient
))
312 else if (MATCH_DESERIALIZE("in-audit", l
, v
, parse_boolean
, u
->in_audit
))
315 else if (MATCH_DESERIALIZE("exported-invocation-id", l
, v
, parse_boolean
, u
->exported_invocation_id
))
318 else if (MATCH_DESERIALIZE("exported-log-level-max", l
, v
, parse_boolean
, u
->exported_log_level_max
))
321 else if (MATCH_DESERIALIZE("exported-log-extra-fields", l
, v
, parse_boolean
, u
->exported_log_extra_fields
))
324 else if (MATCH_DESERIALIZE("exported-log-rate-limit-interval", l
, v
, parse_boolean
, u
->exported_log_ratelimit_interval
))
327 else if (MATCH_DESERIALIZE("exported-log-rate-limit-burst", l
, v
, parse_boolean
, u
->exported_log_ratelimit_burst
))
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
))
334 else if (MATCH_DESERIALIZE_IMMEDIATE("cpu-usage-last", l
, v
, safe_atou64
, u
->cpu_usage_last
))
337 else if (MATCH_DESERIALIZE_IMMEDIATE("managed-oom-kill-last", l
, v
, safe_atou64
, u
->managed_oom_kill_last
))
340 else if (MATCH_DESERIALIZE_IMMEDIATE("oom-kill-last", l
, v
, safe_atou64
, u
->oom_kill_last
))
343 else if (streq(l
, "cgroup")) {
344 r
= unit_set_cgroup_path(u
, v
);
346 log_unit_debug_errno(u
, r
, "Failed to set cgroup path %s, ignoring: %m", v
);
348 (void) unit_watch_cgroup(u
);
349 (void) unit_watch_cgroup_memory(u
);
353 } else if (MATCH_DESERIALIZE("cgroup-realized", l
, v
, parse_boolean
, u
->cgroup_realized
))
356 else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-realized-mask", l
, v
, cg_mask_from_string
, u
->cgroup_realized_mask
))
359 else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-enabled-mask", l
, v
, cg_mask_from_string
, u
->cgroup_enabled_mask
))
362 else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-invalidated-mask", l
, v
, cg_mask_from_string
, u
->cgroup_invalidated_mask
))
365 else if (streq(l
, "ref-uid")) {
368 r
= parse_uid(v
, &uid
);
370 log_unit_debug(u
, "Failed to parse \"%s=%s\", ignoring.", l
, v
);
372 unit_ref_uid_gid(u
, uid
, GID_INVALID
);
375 } else if (streq(l
, "ref-gid")) {
378 r
= parse_gid(v
, &gid
);
380 log_unit_debug(u
, "Failed to parse \"%s=%s\", ignoring.", l
, v
);
382 unit_ref_uid_gid(u
, UID_INVALID
, gid
);
385 } else if (streq(l
, "ref")) {
386 r
= strv_extend(&u
->deserialized_refs
, v
);
391 } else if (streq(l
, "invocation-id")) {
394 r
= sd_id128_from_string(v
, &id
);
396 log_unit_debug(u
, "Failed to parse \"%s=%s\", ignoring.", l
, v
);
398 r
= unit_set_invocation_id(u
, id
);
400 log_unit_warning_errno(u
, r
, "Failed to set invocation ID for unit: %m");
405 } else if (MATCH_DESERIALIZE("freezer-state", l
, v
, freezer_state_from_string
, u
->freezer_state
))
408 else if (streq(l
, "markers")) {
409 r
= deserialize_markers(u
, v
);
411 log_unit_debug_errno(u
, r
, "Failed to deserialize \"%s=%s\", ignoring: %m", l
, v
);
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
);
420 r
= safe_atou64(v
, &c
);
422 log_unit_debug(u
, "Failed to parse IP accounting value %s, ignoring.", v
);
424 u
->ip_accounting_extra
[m
] = c
;
428 m
= string_table_lookup(io_accounting_metric_field_base
, ELEMENTSOF(io_accounting_metric_field_base
), l
);
432 r
= safe_atou64(v
, &c
);
434 log_unit_debug(u
, "Failed to parse IO accounting base value %s, ignoring.", v
);
436 u
->io_accounting_base
[m
] = c
;
440 m
= string_table_lookup(io_accounting_metric_field_last
, ELEMENTSOF(io_accounting_metric_field_last
), l
);
444 r
= safe_atou64(v
, &c
);
446 log_unit_debug(u
, "Failed to parse IO accounting last value %s, ignoring.", v
);
448 u
->io_accounting_last
[m
] = c
;
452 if (unit_can_serialize(u
)) {
453 r
= exec_runtime_deserialize_compat(u
, l
, v
, fds
);
455 log_unit_warning(u
, "Failed to deserialize runtime parameter '%s', ignoring.", l
);
459 /* Returns positive if key was handled by the call */
463 r
= UNIT_VTABLE(u
)->deserialize_item(u
, l
, v
, fds
);
465 log_unit_warning(u
, "Failed to deserialize unit parameter '%s', ignoring.", l
);
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
474 if (!dual_timestamp_is_set(&u
->state_change_timestamp
))
475 dual_timestamp_get(&u
->state_change_timestamp
);
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
);
486 int unit_deserialize_skip(FILE *f
) {
490 /* Skip serialized data for this unit. We don't know what it is. */
493 _cleanup_free_
char *line
= NULL
;
496 r
= read_line(f
, LONG_LINE_MAX
, &line
);
498 return log_error_errno(r
, "Failed to read serialization line: %m");
510 static void print_unit_dependency_mask(FILE *f
, const char *kind
, UnitDependencyMask mask
, bool *space
) {
512 UnitDependencyMask mask
;
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" },
529 for (size_t i
= 0; i
< ELEMENTSOF(table
); i
++) {
534 if (FLAGS_SET(mask
, table
[i
].mask
)) {
542 fputs(table
[i
].name
, f
);
544 mask
&= ~table
[i
].mask
;
551 void unit_dump(Unit
*u
, FILE *f
, const char *prefix
) {
554 char timestamp
[5][FORMAT_TIMESTAMP_MAX
], timespan
[FORMAT_TIMESPAN_MAX
];
556 _cleanup_set_free_ Set
*following_set
= NULL
;
561 assert(u
->type
>= 0);
563 prefix
= strempty(prefix
);
564 prefix2
= strjoina(prefix
, "\t");
570 SET_FOREACH(t
, u
->aliases
)
571 fprintf(f
, "%s\tAlias: %s\n", prefix
, t
);
574 "%s\tDescription: %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"
584 "%s\tNeed Daemon Reload: %s\n"
585 "%s\tTransient: %s\n"
586 "%s\tPerpetual: %s\n"
587 "%s\tGarbage Collection Mode: %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
));
609 if (u
->markers
!= 0) {
610 fprintf(f
, "%s\tMarkers:", prefix
);
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
));
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
));
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
));
630 m
= unit_get_own_mask(u
);
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
));
637 m
= unit_get_members_mask(u
);
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
));
644 m
= unit_get_delegate_mask(u
);
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
));
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
));
655 STRV_FOREACH(j
, u
->documentation
)
656 fprintf(f
, "%s\tDocumentation: %s\n", prefix
, *j
);
658 following
= unit_following(u
);
660 fprintf(f
, "%s\tFollowing: %s\n", prefix
, following
->id
);
662 r
= unit_following_set(u
, &following_set
);
666 SET_FOREACH(other
, following_set
)
667 fprintf(f
, "%s\tFollowing Set Member: %s\n", prefix
, other
->id
);
670 if (u
->fragment_path
)
671 fprintf(f
, "%s\tFragment Path: %s\n", prefix
, u
->fragment_path
);
674 fprintf(f
, "%s\tSource Path: %s\n", prefix
, u
->source_path
);
676 STRV_FOREACH(j
, u
->dropin_paths
)
677 fprintf(f
, "%s\tDropIn Path: %s\n", prefix
, *j
);
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
);
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));
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
));
694 if (u
->job_timeout_reboot_arg
)
695 fprintf(f
, "%s\tJob Timeout Reboot Argument: %s\n", prefix
, u
->job_timeout_reboot_arg
);
697 condition_dump_list(u
->conditions
, f
, prefix
, condition_type_to_string
);
698 condition_dump_list(u
->asserts
, f
, prefix
, assert_type_to_string
);
700 if (dual_timestamp_is_set(&u
->condition_timestamp
))
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
));
707 if (dual_timestamp_is_set(&u
->assert_timestamp
))
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
));
714 for (UnitDependency d
= 0; d
< _UNIT_DEPENDENCY_MAX
; d
++) {
715 UnitDependencyInfo di
;
718 HASHMAP_FOREACH_KEY(di
.data
, other
, u
->dependencies
[d
]) {
721 fprintf(f
, "%s\t%s: %s (", prefix
, unit_dependency_to_string(d
), other
->id
);
723 print_unit_dependency_mask(f
, "origin", di
.origin_mask
, &space
);
724 print_unit_dependency_mask(f
, "destination", di
.destination_mask
, &space
);
730 if (!hashmap_isempty(u
->requires_mounts_for
)) {
731 UnitDependencyInfo di
;
734 HASHMAP_FOREACH_KEY(di
.data
, path
, u
->requires_mounts_for
) {
737 fprintf(f
, "%s\tRequiresMountsFor: %s (", prefix
, path
);
739 print_unit_dependency_mask(f
, "origin", di
.origin_mask
, &space
);
740 print_unit_dependency_mask(f
, "destination", di
.destination_mask
, &space
);
746 if (u
->load_state
== UNIT_LOADED
) {
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
));
762 if (UNIT_VTABLE(u
)->dump
)
763 UNIT_VTABLE(u
)->dump(u
, f
, prefix2
);
765 } else if (u
->load_state
== UNIT_MERGED
)
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
));
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
);
776 job_dump(u
->job
, f
, prefix2
);
779 job_dump(u
->nop_job
, f
, prefix2
);