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