1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
5 #include "alloc-util.h"
10 #include "extract-word.h"
12 #include "format-util.h"
13 #include "glyph-util.h"
14 #include "parse-util.h"
15 #include "serialize.h"
17 #include "string-util.h"
20 #include "unit-serialize.h"
21 #include "user-util.h"
23 /* Make sure out values fit in the bitfield. */
24 assert_cc(_UNIT_MARKER_MAX
<= sizeof(((Unit
){}).markers
) * 8);
26 static int serialize_markers(FILE *f
, unsigned markers
) {
35 BIT_FOREACH(m
, markers
)
36 fputs_with_separator(f
, unit_marker_to_string(m
), /* separator = */ NULL
, &space
);
41 static int deserialize_markers(Unit
*u
, const char *value
) {
46 for (const char *p
= value
;;) {
47 _cleanup_free_
char *word
= NULL
;
49 r
= extract_first_word(&p
, &word
, NULL
, 0);
53 UnitMarker m
= unit_marker_from_string(word
);
55 log_unit_debug_errno(u
, m
, "Unknown unit marker \"%s\", ignoring.", word
);
59 u
->markers
|= 1u << m
;
63 int unit_serialize_state(Unit
*u
, FILE *f
, FDSet
*fds
, bool switching_root
) {
70 if (switching_root
&& UNIT_VTABLE(u
)->exclude_from_switch_root_serialization
) {
71 /* In the new root, paths for mounts and automounts will be different, so it doesn't make
72 * much sense to serialize things. API file systems will be moved to the new root, but we
73 * don't have mount units for those. */
74 log_unit_debug(u
, "not serializing before switch-root");
82 assert(!!UNIT_VTABLE(u
)->serialize
== !!UNIT_VTABLE(u
)->deserialize_item
);
84 if (UNIT_VTABLE(u
)->serialize
) {
85 r
= UNIT_VTABLE(u
)->serialize(u
, f
, fds
);
90 (void) serialize_dual_timestamp(f
, "state-change-timestamp", &u
->state_change_timestamp
);
92 (void) serialize_dual_timestamp(f
, "inactive-exit-timestamp", &u
->inactive_exit_timestamp
);
93 (void) serialize_dual_timestamp(f
, "active-enter-timestamp", &u
->active_enter_timestamp
);
94 (void) serialize_dual_timestamp(f
, "active-exit-timestamp", &u
->active_exit_timestamp
);
95 (void) serialize_dual_timestamp(f
, "inactive-enter-timestamp", &u
->inactive_enter_timestamp
);
97 (void) serialize_dual_timestamp(f
, "condition-timestamp", &u
->condition_timestamp
);
98 (void) serialize_dual_timestamp(f
, "assert-timestamp", &u
->assert_timestamp
);
100 (void) serialize_ratelimit(f
, "start-ratelimit", &u
->start_ratelimit
);
101 (void) serialize_ratelimit(f
, "auto-start-stop-ratelimit", &u
->auto_start_stop_ratelimit
);
103 if (dual_timestamp_is_set(&u
->condition_timestamp
))
104 (void) serialize_bool(f
, "condition-result", u
->condition_result
);
106 if (dual_timestamp_is_set(&u
->assert_timestamp
))
107 (void) serialize_bool(f
, "assert-result", u
->assert_result
);
109 (void) serialize_bool(f
, "transient", u
->transient
);
110 (void) serialize_bool(f
, "in-audit", u
->in_audit
);
112 (void) serialize_bool(f
, "debug-invocation", u
->debug_invocation
);
114 (void) serialize_bool(f
, "exported-invocation-id", u
->exported_invocation_id
);
115 (void) serialize_bool(f
, "exported-log-level-max", u
->exported_log_level_max
);
116 (void) serialize_bool(f
, "exported-log-extra-fields", u
->exported_log_extra_fields
);
117 (void) serialize_bool(f
, "exported-log-rate-limit-interval", u
->exported_log_ratelimit_interval
);
118 (void) serialize_bool(f
, "exported-log-rate-limit-burst", u
->exported_log_ratelimit_burst
);
120 (void) cgroup_runtime_serialize(u
, f
, fds
);
122 if (uid_is_valid(u
->ref_uid
))
123 (void) serialize_item_format(f
, "ref-uid", UID_FMT
, u
->ref_uid
);
124 if (gid_is_valid(u
->ref_gid
))
125 (void) serialize_item_format(f
, "ref-gid", GID_FMT
, u
->ref_gid
);
127 (void) serialize_id128(f
, "invocation-id", u
->invocation_id
);
129 (void) serialize_item(f
, "freezer-state", freezer_state_to_string(u
->freezer_state
));
131 (void) serialize_markers(f
, u
->markers
);
133 bus_track_serialize(u
->bus_track
, f
, "ref");
135 if (!switching_root
) {
138 job_serialize(u
->job
, f
);
143 job_serialize(u
->nop_job
, f
);
152 static int unit_deserialize_job(Unit
*u
, FILE *f
) {
153 _cleanup_(job_freep
) Job
*j
= NULL
;
163 r
= job_deserialize(j
, f
);
167 r
= job_install_deserialized(j
);
175 #define MATCH_DESERIALIZE(key, l, v, parse_func, target) \
177 bool _deserialize_matched = streq(l, key); \
178 if (_deserialize_matched) { \
179 int _deserialize_r = parse_func(v); \
180 if (_deserialize_r < 0) \
181 log_unit_debug_errno(u, _deserialize_r, \
182 "Failed to parse \"%s=%s\", ignoring.", l, v); \
184 target = _deserialize_r; \
186 _deserialize_matched; \
189 #define MATCH_DESERIALIZE_IMMEDIATE(key, l, v, parse_func, target) \
191 bool _deserialize_matched = streq(l, key); \
192 if (_deserialize_matched) { \
193 int _deserialize_r = parse_func(v, &target); \
194 if (_deserialize_r < 0) \
195 log_unit_debug_errno(u, _deserialize_r, \
196 "Failed to parse \"%s=%s\", ignoring", l, v); \
198 _deserialize_matched; \
201 int unit_deserialize_state(Unit
*u
, FILE *f
, FDSet
*fds
) {
209 _cleanup_free_
char *l
= NULL
;
213 r
= deserialize_read_line(f
, &l
);
216 if (r
== 0) /* eof or end marker */
227 if (streq(l
, "job")) {
229 /* New-style serialized job */
230 r
= unit_deserialize_job(u
, f
);
233 } else /* Legacy for pre-44 */
234 log_unit_warning(u
, "Update from too old systemd versions are unsupported, cannot deserialize job: %s", v
);
236 } else if (streq(l
, "state-change-timestamp")) {
237 (void) deserialize_dual_timestamp(v
, &u
->state_change_timestamp
);
239 } else if (streq(l
, "inactive-exit-timestamp")) {
240 (void) deserialize_dual_timestamp(v
, &u
->inactive_exit_timestamp
);
242 } else if (streq(l
, "active-enter-timestamp")) {
243 (void) deserialize_dual_timestamp(v
, &u
->active_enter_timestamp
);
245 } else if (streq(l
, "active-exit-timestamp")) {
246 (void) deserialize_dual_timestamp(v
, &u
->active_exit_timestamp
);
248 } else if (streq(l
, "inactive-enter-timestamp")) {
249 (void) deserialize_dual_timestamp(v
, &u
->inactive_enter_timestamp
);
251 } else if (streq(l
, "condition-timestamp")) {
252 (void) deserialize_dual_timestamp(v
, &u
->condition_timestamp
);
254 } else if (streq(l
, "assert-timestamp")) {
255 (void) deserialize_dual_timestamp(v
, &u
->assert_timestamp
);
258 } else if (streq(l
, "start-ratelimit")) {
259 deserialize_ratelimit(&u
->start_ratelimit
, l
, v
);
261 } else if (streq(l
, "auto-start-stop-ratelimit")) {
262 deserialize_ratelimit(&u
->auto_start_stop_ratelimit
, l
, v
);
265 } else if (MATCH_DESERIALIZE("condition-result", l
, v
, parse_boolean
, u
->condition_result
))
268 else if (MATCH_DESERIALIZE("assert-result", l
, v
, parse_boolean
, u
->assert_result
))
271 else if (MATCH_DESERIALIZE("transient", l
, v
, parse_boolean
, u
->transient
))
274 else if (MATCH_DESERIALIZE("in-audit", l
, v
, parse_boolean
, u
->in_audit
))
277 else if (MATCH_DESERIALIZE("debug-invocation", l
, v
, parse_boolean
, u
->debug_invocation
))
280 else if (MATCH_DESERIALIZE("exported-invocation-id", l
, v
, parse_boolean
, u
->exported_invocation_id
))
283 else if (MATCH_DESERIALIZE("exported-log-level-max", l
, v
, parse_boolean
, u
->exported_log_level_max
))
286 else if (MATCH_DESERIALIZE("exported-log-extra-fields", l
, v
, parse_boolean
, u
->exported_log_extra_fields
))
289 else if (MATCH_DESERIALIZE("exported-log-rate-limit-interval", l
, v
, parse_boolean
, u
->exported_log_ratelimit_interval
))
292 else if (MATCH_DESERIALIZE("exported-log-rate-limit-burst", l
, v
, parse_boolean
, u
->exported_log_ratelimit_burst
))
295 else if (streq(l
, "ref-uid")) {
298 r
= parse_uid(v
, &uid
);
300 log_unit_debug(u
, "Failed to parse \"%s=%s\", ignoring.", l
, v
);
302 unit_ref_uid_gid(u
, uid
, GID_INVALID
);
305 } else if (streq(l
, "ref-gid")) {
308 r
= parse_gid(v
, &gid
);
310 log_unit_debug(u
, "Failed to parse \"%s=%s\", ignoring.", l
, v
);
312 unit_ref_uid_gid(u
, UID_INVALID
, gid
);
315 } else if (streq(l
, "ref")) {
316 r
= strv_extend(&u
->deserialized_refs
, v
);
321 } else if (streq(l
, "invocation-id")) {
324 r
= sd_id128_from_string(v
, &id
);
326 log_unit_debug(u
, "Failed to parse \"%s=%s\", ignoring.", l
, v
);
328 r
= unit_set_invocation_id(u
, id
);
330 log_unit_warning_errno(u
, r
, "Failed to set invocation ID for unit: %m");
335 } else if (MATCH_DESERIALIZE("freezer-state", l
, v
, freezer_state_from_string
, u
->freezer_state
))
338 else if (streq(l
, "markers")) {
339 r
= deserialize_markers(u
, v
);
341 log_unit_debug_errno(u
, r
, "Failed to deserialize \"%s=%s\", ignoring: %m", l
, v
);
345 r
= exec_shared_runtime_deserialize_compat(u
, l
, v
, fds
);
347 log_unit_warning(u
, "Failed to deserialize runtime parameter '%s', ignoring.", l
);
350 /* Returns positive if key was handled by the call */
353 r
= cgroup_runtime_deserialize_one(u
, l
, v
, fds
);
355 log_unit_warning(u
, "Failed to deserialize cgroup runtime parameter '%s, ignoring.", l
);
358 continue; /* was handled */
360 if (UNIT_VTABLE(u
)->deserialize_item
) {
361 r
= UNIT_VTABLE(u
)->deserialize_item(u
, l
, v
, fds
);
363 log_unit_warning(u
, "Failed to deserialize unit parameter '%s', ignoring.", l
);
367 /* Let's make sure that everything that is deserialized also gets any potential new cgroup settings
368 * applied after we are done. For that we invalidate anything already realized, so that we can
369 * realize it again. */
370 CGroupRuntime
*crt
= unit_get_cgroup_runtime(u
);
371 if (crt
&& crt
->cgroup_path
) {
372 /* Since v258, CGroupRuntime.cgroup_path is coupled with cgroup realized state, which however
373 * wasn't the case in prior versions with the realized state tracked in a discrete field.
374 * Patch cgroup_realized == 0 back to no cgroup_path here hence. */
375 if (crt
->deserialized_cgroup_realized
== 0)
376 unit_release_cgroup(u
, /* drop_cgroup_runtime = */ false);
378 unit_invalidate_cgroup(u
, _CGROUP_MASK_ALL
);
379 unit_invalidate_cgroup_bpf(u
);
386 int unit_deserialize_state_skip(FILE *f
) {
391 /* Skip serialized data for this unit. We don't know what it is. */
394 _cleanup_free_
char *line
= NULL
;
396 r
= read_stripped_line(f
, LONG_LINE_MAX
, &line
);
398 return log_error_errno(r
, "Failed to read serialization line: %m");
408 static void print_unit_dependency_mask(FILE *f
, const char *kind
, UnitDependencyMask mask
, bool *space
) {
410 UnitDependencyMask mask
;
413 { UNIT_DEPENDENCY_FILE
, "file" },
414 { UNIT_DEPENDENCY_IMPLICIT
, "implicit" },
415 { UNIT_DEPENDENCY_DEFAULT
, "default" },
416 { UNIT_DEPENDENCY_UDEV
, "udev" },
417 { UNIT_DEPENDENCY_PATH
, "path" },
418 { UNIT_DEPENDENCY_MOUNT_FILE
, "mount-file" },
419 { UNIT_DEPENDENCY_MOUNTINFO
, "mountinfo" },
420 { UNIT_DEPENDENCY_PROC_SWAP
, "proc-swap" },
421 { UNIT_DEPENDENCY_SLICE_PROPERTY
, "slice-property" },
428 FOREACH_ELEMENT(i
, table
) {
432 if (FLAGS_SET(mask
, i
->mask
)) {
449 void unit_dump(Unit
*u
, FILE *f
, const char *prefix
) {
453 _cleanup_set_free_ Set
*following_set
= NULL
;
458 assert(u
->type
>= 0);
460 prefix
= strempty(prefix
);
461 prefix2
= strjoina(prefix
, "\t");
465 prefix
, glyph(GLYPH_ARROW_RIGHT
), u
->id
);
467 SET_FOREACH(t
, u
->aliases
)
468 fprintf(f
, "%s\tAlias: %s\n", prefix
, t
);
471 "%s\tDescription: %s\n"
473 "%s\tUnit Load State: %s\n"
474 "%s\tUnit Active State: %s\n"
475 "%s\tState Change Timestamp: %s\n"
476 "%s\tInactive Exit Timestamp: %s\n"
477 "%s\tActive Enter Timestamp: %s\n"
478 "%s\tActive Exit Timestamp: %s\n"
479 "%s\tInactive Enter Timestamp: %s\n"
481 "%s\tNeed Daemon Reload: %s\n"
482 "%s\tTransient: %s\n"
483 "%s\tPerpetual: %s\n"
484 "%s\tGarbage Collection Mode: %s\n",
485 prefix
, unit_description(u
),
486 prefix
, strna(u
->instance
),
487 prefix
, unit_load_state_to_string(u
->load_state
),
488 prefix
, unit_active_state_to_string(unit_active_state(u
)),
489 prefix
, strna(FORMAT_TIMESTAMP(u
->state_change_timestamp
.realtime
)),
490 prefix
, strna(FORMAT_TIMESTAMP(u
->inactive_exit_timestamp
.realtime
)),
491 prefix
, strna(FORMAT_TIMESTAMP(u
->active_enter_timestamp
.realtime
)),
492 prefix
, strna(FORMAT_TIMESTAMP(u
->active_exit_timestamp
.realtime
)),
493 prefix
, strna(FORMAT_TIMESTAMP(u
->inactive_enter_timestamp
.realtime
)),
494 prefix
, yes_no(unit_may_gc(u
)),
495 prefix
, yes_no(unit_need_daemon_reload(u
)),
496 prefix
, yes_no(u
->transient
),
497 prefix
, yes_no(u
->perpetual
),
498 prefix
, collect_mode_to_string(u
->collect_mode
));
500 if (u
->markers
!= 0) {
501 fprintf(f
, "%s\tMarkers:", prefix
);
503 BIT_FOREACH(marker
, u
->markers
)
504 fprintf(f
, " %s", unit_marker_to_string(marker
));
508 if (UNIT_HAS_CGROUP_CONTEXT(u
)) {
509 CGroupRuntime
*crt
= unit_get_cgroup_runtime(u
);
514 prefix
, strna(unit_slice_name(u
)),
515 prefix
, strna(crt
? crt
->cgroup_path
: NULL
));
517 if (crt
&& crt
->cgroup_realized_mask
!= 0) {
518 _cleanup_free_
char *s
= NULL
;
519 (void) cg_mask_to_string(crt
->cgroup_realized_mask
, &s
);
520 fprintf(f
, "%s\tCGroup realized mask: %s\n", prefix
, strnull(s
));
523 if (crt
&& crt
->cgroup_enabled_mask
!= 0) {
524 _cleanup_free_
char *s
= NULL
;
525 (void) cg_mask_to_string(crt
->cgroup_enabled_mask
, &s
);
526 fprintf(f
, "%s\tCGroup enabled mask: %s\n", prefix
, strnull(s
));
529 m
= unit_get_own_mask(u
);
531 _cleanup_free_
char *s
= NULL
;
532 (void) cg_mask_to_string(m
, &s
);
533 fprintf(f
, "%s\tCGroup own mask: %s\n", prefix
, strnull(s
));
536 m
= unit_get_members_mask(u
);
538 _cleanup_free_
char *s
= NULL
;
539 (void) cg_mask_to_string(m
, &s
);
540 fprintf(f
, "%s\tCGroup members mask: %s\n", prefix
, strnull(s
));
543 m
= unit_get_delegate_mask(u
);
545 _cleanup_free_
char *s
= NULL
;
546 (void) cg_mask_to_string(m
, &s
);
547 fprintf(f
, "%s\tCGroup delegate mask: %s\n", prefix
, strnull(s
));
551 if (!sd_id128_is_null(u
->invocation_id
))
552 fprintf(f
, "%s\tInvocation ID: " SD_ID128_FORMAT_STR
"\n",
553 prefix
, SD_ID128_FORMAT_VAL(u
->invocation_id
));
555 STRV_FOREACH(j
, u
->documentation
)
556 fprintf(f
, "%s\tDocumentation: %s\n", prefix
, *j
);
558 if (u
->access_selinux_context
)
559 fprintf(f
, "%s\tAccess SELinux Context: %s\n", prefix
, u
->access_selinux_context
);
561 following
= unit_following(u
);
563 fprintf(f
, "%s\tFollowing: %s\n", prefix
, following
->id
);
565 r
= unit_following_set(u
, &following_set
);
569 SET_FOREACH(other
, following_set
)
570 fprintf(f
, "%s\tFollowing Set Member: %s\n", prefix
, other
->id
);
573 if (u
->fragment_path
)
574 fprintf(f
, "%s\tFragment Path: %s\n", prefix
, u
->fragment_path
);
577 fprintf(f
, "%s\tSource Path: %s\n", prefix
, u
->source_path
);
579 STRV_FOREACH(j
, u
->dropin_paths
)
580 fprintf(f
, "%s\tDropIn Path: %s\n", prefix
, *j
);
582 if (u
->failure_action
!= EMERGENCY_ACTION_NONE
)
583 fprintf(f
, "%s\tFailure Action: %s\n", prefix
, emergency_action_to_string(u
->failure_action
));
584 if (u
->failure_action_exit_status
>= 0)
585 fprintf(f
, "%s\tFailure Action Exit Status: %i\n", prefix
, u
->failure_action_exit_status
);
586 if (u
->success_action
!= EMERGENCY_ACTION_NONE
)
587 fprintf(f
, "%s\tSuccess Action: %s\n", prefix
, emergency_action_to_string(u
->success_action
));
588 if (u
->success_action_exit_status
>= 0)
589 fprintf(f
, "%s\tSuccess Action Exit Status: %i\n", prefix
, u
->success_action_exit_status
);
591 if (u
->job_timeout
!= USEC_INFINITY
)
592 fprintf(f
, "%s\tJob Timeout: %s\n", prefix
, FORMAT_TIMESPAN(u
->job_timeout
, 0));
594 if (u
->job_timeout_action
!= EMERGENCY_ACTION_NONE
)
595 fprintf(f
, "%s\tJob Timeout Action: %s\n", prefix
, emergency_action_to_string(u
->job_timeout_action
));
597 if (u
->job_timeout_reboot_arg
)
598 fprintf(f
, "%s\tJob Timeout Reboot Argument: %s\n", prefix
, u
->job_timeout_reboot_arg
);
600 condition_dump_list(u
->conditions
, f
, prefix
, condition_type_to_string
);
601 condition_dump_list(u
->asserts
, f
, prefix
, assert_type_to_string
);
603 if (dual_timestamp_is_set(&u
->condition_timestamp
))
605 "%s\tCondition Timestamp: %s\n"
606 "%s\tCondition Result: %s\n",
607 prefix
, strna(FORMAT_TIMESTAMP(u
->condition_timestamp
.realtime
)),
608 prefix
, yes_no(u
->condition_result
));
610 if (dual_timestamp_is_set(&u
->assert_timestamp
))
612 "%s\tAssert Timestamp: %s\n"
613 "%s\tAssert Result: %s\n",
614 prefix
, strna(FORMAT_TIMESTAMP(u
->assert_timestamp
.realtime
)),
615 prefix
, yes_no(u
->assert_result
));
617 for (UnitDependency d
= 0; d
< _UNIT_DEPENDENCY_MAX
; d
++) {
618 UnitDependencyInfo di
;
621 HASHMAP_FOREACH_KEY(di
.data
, other
, unit_get_dependencies(u
, d
)) {
624 fprintf(f
, "%s\t%s: %s (", prefix
, unit_dependency_to_string(d
), other
->id
);
626 print_unit_dependency_mask(f
, "origin", di
.origin_mask
, &space
);
627 print_unit_dependency_mask(f
, "destination", di
.destination_mask
, &space
);
633 for (UnitMountDependencyType type
= 0; type
< _UNIT_MOUNT_DEPENDENCY_TYPE_MAX
; type
++)
634 if (!hashmap_isempty(u
->mounts_for
[type
])) {
635 UnitDependencyInfo di
;
638 HASHMAP_FOREACH_KEY(di
.data
, path
, u
->mounts_for
[type
]) {
644 unit_mount_dependency_type_to_string(type
),
647 print_unit_dependency_mask(f
, "origin", di
.origin_mask
, &space
);
648 print_unit_dependency_mask(f
, "destination", di
.destination_mask
, &space
);
654 if (u
->load_state
== UNIT_LOADED
) {
657 "%s\tStopWhenUnneeded: %s\n"
658 "%s\tRefuseManualStart: %s\n"
659 "%s\tRefuseManualStop: %s\n"
660 "%s\tDefaultDependencies: %s\n"
661 "%s\tSurviveFinalKillSignal: %s\n"
662 "%s\tOnSuccessJobMode: %s\n"
663 "%s\tOnFailureJobMode: %s\n"
664 "%s\tIgnoreOnIsolate: %s\n",
665 prefix
, yes_no(u
->stop_when_unneeded
),
666 prefix
, yes_no(u
->refuse_manual_start
),
667 prefix
, yes_no(u
->refuse_manual_stop
),
668 prefix
, yes_no(u
->default_dependencies
),
669 prefix
, yes_no(u
->survive_final_kill_signal
),
670 prefix
, job_mode_to_string(u
->on_success_job_mode
),
671 prefix
, job_mode_to_string(u
->on_failure_job_mode
),
672 prefix
, yes_no(u
->ignore_on_isolate
));
674 if (UNIT_VTABLE(u
)->dump
)
675 UNIT_VTABLE(u
)->dump(u
, f
, prefix2
);
677 } else if (u
->load_state
== UNIT_MERGED
)
679 "%s\tMerged into: %s\n",
680 prefix
, u
->merged_into
->id
);
681 else if (u
->load_state
== UNIT_ERROR
) {
682 errno
= ABS(u
->load_error
);
683 fprintf(f
, "%s\tLoad Error Code: %m\n", prefix
);
686 for (const char *n
= sd_bus_track_first(u
->bus_track
); n
; n
= sd_bus_track_next(u
->bus_track
))
687 fprintf(f
, "%s\tBus Ref: %s\n", prefix
, n
);
690 job_dump(u
->job
, f
, prefix2
);
693 job_dump(u
->nop_job
, f
, prefix2
);