]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/systemctl/systemctl-util.c
Merge pull request #25168 from valentindavid/valentindavid/umount-move-recursive...
[thirdparty/systemd.git] / src / systemctl / systemctl-util.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
daf71ef6
LP
2
3#include <sys/reboot.h>
4#include <unistd.h>
5
6#include "sd-bus.h"
7#include "sd-daemon.h"
8
9#include "bus-common-errors.h"
10#include "bus-locator.h"
11#include "bus-map-properties.h"
12#include "bus-unit-util.h"
f4351959 13#include "chase-symlinks.h"
daf71ef6
LP
14#include "dropin.h"
15#include "env-util.h"
16#include "exit-status.h"
17#include "fs-util.h"
18#include "glob-util.h"
19#include "macro.h"
20#include "path-util.h"
21#include "reboot-util.h"
22#include "set.h"
23#include "spawn-ask-password-agent.h"
24#include "spawn-polkit-agent.h"
25#include "stat-util.h"
26#include "systemctl-util.h"
27#include "systemctl.h"
28#include "terminal-util.h"
29#include "verbs.h"
30
31static sd_bus *buses[_BUS_FOCUS_MAX] = {};
32
33int acquire_bus(BusFocus focus, sd_bus **ret) {
34 int r;
35
36 assert(focus < _BUS_FOCUS_MAX);
37 assert(ret);
38
39 /* We only go directly to the manager, if we are using a local transport */
40 if (arg_transport != BUS_TRANSPORT_LOCAL)
41 focus = BUS_FULL;
42
43 if (getenv_bool("SYSTEMCTL_FORCE_BUS") > 0)
44 focus = BUS_FULL;
45
46 if (!buses[focus]) {
47 bool user;
48
b380b643 49 user = arg_scope != LOOKUP_SCOPE_SYSTEM;
daf71ef6
LP
50
51 if (focus == BUS_MANAGER)
52 r = bus_connect_transport_systemd(arg_transport, arg_host, user, &buses[focus]);
53 else
54 r = bus_connect_transport(arg_transport, arg_host, user, &buses[focus]);
55 if (r < 0)
10a7340a 56 return bus_log_connect_error(r, arg_transport);
daf71ef6
LP
57
58 (void) sd_bus_set_allow_interactive_authorization(buses[focus], arg_ask_password);
59 }
60
61 *ret = buses[focus];
62 return 0;
63}
64
65void release_busses(void) {
deaf4b86 66 for (BusFocus w = 0; w < _BUS_FOCUS_MAX; w++)
daf71ef6
LP
67 buses[w] = sd_bus_flush_close_unref(buses[w]);
68}
69
70void ask_password_agent_open_maybe(void) {
71 /* Open the password agent as a child process if necessary */
72
73 if (arg_dry_run)
74 return;
75
b380b643 76 if (arg_scope != LOOKUP_SCOPE_SYSTEM)
daf71ef6
LP
77 return;
78
79 ask_password_agent_open_if_enabled(arg_transport, arg_ask_password);
80}
81
82void polkit_agent_open_maybe(void) {
83 /* Open the polkit agent as a child process if necessary */
84
b380b643 85 if (arg_scope != LOOKUP_SCOPE_SYSTEM)
daf71ef6
LP
86 return;
87
88 polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
89}
90
91int translate_bus_error_to_exit_status(int r, const sd_bus_error *error) {
92 assert(error);
93
94 if (!sd_bus_error_is_set(error))
95 return r;
96
97 if (sd_bus_error_has_names(error, SD_BUS_ERROR_ACCESS_DENIED,
98 BUS_ERROR_ONLY_BY_DEPENDENCY,
99 BUS_ERROR_NO_ISOLATION,
100 BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE))
101 return EXIT_NOPERMISSION;
102
103 if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT))
104 return EXIT_NOTINSTALLED;
105
106 if (sd_bus_error_has_names(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE,
107 SD_BUS_ERROR_NOT_SUPPORTED))
108 return EXIT_NOTIMPLEMENTED;
109
110 if (sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED))
111 return EXIT_NOTCONFIGURED;
112
113 if (r != 0)
114 return r;
115
116 return EXIT_FAILURE;
117}
118
119int get_state_one_unit(sd_bus *bus, const char *unit, UnitActiveState *ret_active_state) {
120 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
121 _cleanup_free_ char *buf = NULL, *dbus_path = NULL;
122 UnitActiveState state;
123 int r;
124
37299769 125 assert(bus);
daf71ef6
LP
126 assert(unit);
127 assert(ret_active_state);
128
129 dbus_path = unit_dbus_path_from_name(unit);
130 if (!dbus_path)
131 return log_oom();
132
133 r = sd_bus_get_property_string(
134 bus,
135 "org.freedesktop.systemd1",
136 dbus_path,
137 "org.freedesktop.systemd1.Unit",
138 "ActiveState",
139 &error,
140 &buf);
141 if (r < 0)
142 return log_error_errno(r, "Failed to retrieve unit state: %s", bus_error_message(&error, r));
143
144 state = unit_active_state_from_string(buf);
145 if (state < 0)
7211c853 146 return log_error_errno(state, "Invalid unit state '%s' for: %s", buf, unit);
daf71ef6
LP
147
148 *ret_active_state = state;
149 return 0;
150}
151
37299769
MY
152int get_sub_state_one_unit(sd_bus *bus, const char *unit, char **ret_sub_state) {
153 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
154 _cleanup_free_ char *sub_state = NULL, *dbus_path = NULL;
155 int r;
156
157 assert(bus);
158 assert(unit);
159 assert(ret_sub_state);
160
161 dbus_path = unit_dbus_path_from_name(unit);
162 if (!dbus_path)
163 return log_oom();
164
165 r = sd_bus_get_property_string(
166 bus,
167 "org.freedesktop.systemd1",
168 dbus_path,
169 "org.freedesktop.systemd1.Unit",
170 "SubState",
171 &error,
172 &sub_state);
173 if (r < 0)
174 return log_error_errno(r, "Failed to retrieve unit sub state: %s", bus_error_message(&error, r));
175
176 *ret_sub_state = TAKE_PTR(sub_state);
177 return 0;
178}
179
daf71ef6
LP
180int get_unit_list(
181 sd_bus *bus,
182 const char *machine,
183 char **patterns,
184 UnitInfo **unit_infos,
185 int c,
186 sd_bus_message **ret_reply) {
187
188 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
189 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
190 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
daf71ef6
LP
191 int r;
192 bool fallback = false;
193
194 assert(bus);
195 assert(unit_infos);
196 assert(ret_reply);
197
198 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitsByPatterns");
199 if (r < 0)
200 return bus_log_create_error(r);
201
202 r = sd_bus_message_append_strv(m, arg_states);
203 if (r < 0)
204 return bus_log_create_error(r);
205
206 r = sd_bus_message_append_strv(m, patterns);
207 if (r < 0)
208 return bus_log_create_error(r);
209
210 r = sd_bus_call(bus, m, 0, &error, &reply);
211 if (r < 0 && (sd_bus_error_has_names(&error, SD_BUS_ERROR_UNKNOWN_METHOD,
212 SD_BUS_ERROR_ACCESS_DENIED))) {
213 /* Fallback to legacy ListUnitsFiltered method */
214 fallback = true;
215 log_debug_errno(r, "Failed to list units: %s Falling back to ListUnitsFiltered method.", bus_error_message(&error, r));
216 m = sd_bus_message_unref(m);
217 sd_bus_error_free(&error);
218
219 r = bus_message_new_method_call(bus, &m, bus_systemd_mgr, "ListUnitsFiltered");
220 if (r < 0)
221 return bus_log_create_error(r);
222
223 r = sd_bus_message_append_strv(m, arg_states);
224 if (r < 0)
225 return bus_log_create_error(r);
226
227 r = sd_bus_call(bus, m, 0, &error, &reply);
228 }
229 if (r < 0)
230 return log_error_errno(r, "Failed to list units: %s", bus_error_message(&error, r));
231
232 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
233 if (r < 0)
234 return bus_log_parse_error(r);
235
236 for (;;) {
237 UnitInfo u;
238
239 r = bus_parse_unit_info(reply, &u);
240 if (r < 0)
241 return bus_log_parse_error(r);
242 if (r == 0)
243 break;
244
245 u.machine = machine;
246
247 if (!output_show_unit(&u, fallback ? patterns : NULL))
248 continue;
249
319a4f4b 250 if (!GREEDY_REALLOC(*unit_infos, c+1))
daf71ef6
LP
251 return log_oom();
252
253 (*unit_infos)[c++] = u;
254 }
255
256 r = sd_bus_message_exit_container(reply);
257 if (r < 0)
258 return bus_log_parse_error(r);
259
260 *ret_reply = TAKE_PTR(reply);
261 return c;
262}
263
264int expand_unit_names(sd_bus *bus, char **names, const char* suffix, char ***ret, bool *ret_expanded) {
265 _cleanup_strv_free_ char **mangled = NULL, **globs = NULL;
deaf4b86 266 int r;
daf71ef6
LP
267
268 assert(bus);
269 assert(ret);
270
271 STRV_FOREACH(name, names) {
272 UnitNameMangle options = UNIT_NAME_MANGLE_GLOB | (arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN);
273 char *t;
274
275 r = unit_name_mangle_with_suffix(*name, NULL, options, suffix ?: ".service", &t);
276 if (r < 0)
277 return log_error_errno(r, "Failed to mangle name: %m");
278
279 if (string_is_glob(t))
280 r = strv_consume(&globs, t);
281 else
282 r = strv_consume(&mangled, t);
283 if (r < 0)
284 return log_oom();
285 }
286
287 /* Query the manager only if any of the names are a glob, since this is fairly expensive */
288 bool expanded = !strv_isempty(globs);
289 if (expanded) {
290 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
291 _cleanup_free_ UnitInfo *unit_infos = NULL;
319a4f4b 292 size_t n;
daf71ef6
LP
293
294 r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply);
295 if (r < 0)
296 return r;
297
298 n = strv_length(mangled);
daf71ef6 299
deaf4b86 300 for (int i = 0; i < r; i++) {
319a4f4b 301 if (!GREEDY_REALLOC(mangled, n+2))
daf71ef6
LP
302 return log_oom();
303
304 mangled[n] = strdup(unit_infos[i].id);
305 if (!mangled[n])
306 return log_oom();
307
308 mangled[++n] = NULL;
309 }
310 }
311
312 if (ret_expanded)
313 *ret_expanded = expanded;
314
315 *ret = TAKE_PTR(mangled);
316 return 0;
317}
318
319int check_triggering_units(sd_bus *bus, const char *unit) {
320 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
321 _cleanup_free_ char *n = NULL, *dbus_path = NULL, *load_state = NULL;
322 _cleanup_strv_free_ char **triggered_by = NULL;
daf71ef6
LP
323 int r;
324
325 r = unit_name_mangle(unit, 0, &n);
326 if (r < 0)
327 return log_error_errno(r, "Failed to mangle unit name: %m");
328
329 r = unit_load_state(bus, n, &load_state);
330 if (r < 0)
331 return r;
332
333 if (streq(load_state, "masked"))
334 return 0;
335
336 dbus_path = unit_dbus_path_from_name(n);
337 if (!dbus_path)
338 return log_oom();
339
340 r = sd_bus_get_property_strv(
341 bus,
342 "org.freedesktop.systemd1",
343 dbus_path,
344 "org.freedesktop.systemd1.Unit",
345 "TriggeredBy",
346 &error,
347 &triggered_by);
348 if (r < 0)
349 return log_error_errno(r, "Failed to get triggered by array of %s: %s", n, bus_error_message(&error, r));
350
c4da8e90 351 bool first = true;
daf71ef6 352 STRV_FOREACH(i, triggered_by) {
c4da8e90
ZJS
353 UnitActiveState active_state;
354
daf71ef6
LP
355 r = get_state_one_unit(bus, *i, &active_state);
356 if (r < 0)
357 return r;
358
359 if (!IN_SET(active_state, UNIT_ACTIVE, UNIT_RELOADING))
360 continue;
361
c4da8e90 362 if (first) {
daf71ef6 363 log_warning("Warning: Stopping %s, but it can still be activated by:", n);
c4da8e90 364 first = false;
daf71ef6
LP
365 }
366
367 log_warning(" %s", *i);
368 }
369
370 return 0;
371}
372
373int need_daemon_reload(sd_bus *bus, const char *unit) {
374 _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL;
375 const char *path;
376 int b, r;
377
378 /* We ignore all errors here, since this is used to show a
379 * warning only */
380
381 /* We don't use unit_dbus_path_from_name() directly since we
382 * don't want to load the unit if it isn't loaded. */
383
384 r = bus_call_method(bus, bus_systemd_mgr, "GetUnit", NULL, &reply, "s", unit);
385 if (r < 0)
386 return r;
387
388 r = sd_bus_message_read(reply, "o", &path);
389 if (r < 0)
390 return r;
391
392 r = sd_bus_get_property_trivial(
393 bus,
394 "org.freedesktop.systemd1",
395 path,
396 "org.freedesktop.systemd1.Unit",
397 "NeedDaemonReload",
398 NULL,
399 'b', &b);
400 if (r < 0)
401 return r;
402
403 return b;
404}
405
406void warn_unit_file_changed(const char *unit) {
407 assert(unit);
408
55721877 409 log_warning("Warning: The unit file, source configuration file or drop-ins of %s changed on disk. Run 'systemctl%s daemon-reload' to reload units.",
daf71ef6 410 unit,
b380b643 411 arg_scope == LOOKUP_SCOPE_SYSTEM ? "" : " --user");
daf71ef6
LP
412}
413
414int unit_file_find_path(LookupPaths *lp, const char *unit_name, char **ret_unit_path) {
daf71ef6
LP
415 assert(lp);
416 assert(unit_name);
417
418 STRV_FOREACH(p, lp->search_path) {
419 _cleanup_free_ char *path = NULL, *lpath = NULL;
420 int r;
421
422 path = path_join(*p, unit_name);
423 if (!path)
424 return log_oom();
425
426 r = chase_symlinks(path, arg_root, 0, &lpath, NULL);
427 if (r == -ENOENT)
428 continue;
429 if (r == -ENOMEM)
430 return log_oom();
431 if (r < 0)
432 return log_error_errno(r, "Failed to access path \"%s\": %m", path);
433
434 if (ret_unit_path)
435 *ret_unit_path = TAKE_PTR(lpath);
436
437 return 1;
438 }
439
440 if (ret_unit_path)
441 *ret_unit_path = NULL;
442
443 return 0;
444}
445
446int unit_find_paths(
447 sd_bus *bus,
448 const char *unit_name,
449 LookupPaths *lp,
450 bool force_client_side,
451 Hashmap **cached_name_map,
452 Hashmap **cached_id_map,
453 char **ret_fragment_path,
454 char ***ret_dropin_paths) {
455
456 _cleanup_strv_free_ char **dropins = NULL;
457 _cleanup_free_ char *path = NULL;
458 int r;
459
460 /**
461 * Finds where the unit is defined on disk. Returns 0 if the unit is not found. Returns 1 if it is
462 * found, and sets:
1842c1b2 463 *
daf71ef6 464 * - the path to the unit in *ret_frament_path, if it exists on disk,
1842c1b2 465 *
daf71ef6
LP
466 * - and a strv of existing drop-ins in *ret_dropin_paths, if the arg is not NULL and any dropins
467 * were found.
468 *
469 * Returns -ERFKILL if the unit is masked, and -EKEYREJECTED if the unit file could not be loaded for
1842c1b2
LP
470 * some reason (the latter only applies if we are going through the service manager). As special
471 * exception it won't log for these two error cases.
daf71ef6
LP
472 */
473
474 assert(unit_name);
475 assert(ret_fragment_path);
476 assert(lp);
477
478 /* Go via the bus to acquire the path, unless we are explicitly told not to, or when the unit name is a template */
479 if (!force_client_side &&
480 !install_client_side() &&
481 !unit_name_is_valid(unit_name, UNIT_NAME_TEMPLATE)) {
482 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
483 _cleanup_free_ char *load_state = NULL, *dbus_path = NULL;
484
485 dbus_path = unit_dbus_path_from_name(unit_name);
486 if (!dbus_path)
487 return log_oom();
488
489 r = sd_bus_get_property_string(
490 bus,
491 "org.freedesktop.systemd1",
492 dbus_path,
493 "org.freedesktop.systemd1.Unit",
494 "LoadState",
495 &error,
496 &load_state);
497 if (r < 0)
498 return log_error_errno(r, "Failed to get LoadState: %s", bus_error_message(&error, r));
499
500 if (streq(load_state, "masked"))
1842c1b2 501 return -ERFKILL; /* special case: no logging */
daf71ef6
LP
502 if (streq(load_state, "not-found")) {
503 r = 0;
d5427dd2 504 goto finish;
daf71ef6
LP
505 }
506 if (!STR_IN_SET(load_state, "loaded", "bad-setting"))
1842c1b2 507 return -EKEYREJECTED; /* special case: no logging */
daf71ef6
LP
508
509 r = sd_bus_get_property_string(
510 bus,
511 "org.freedesktop.systemd1",
512 dbus_path,
513 "org.freedesktop.systemd1.Unit",
514 "FragmentPath",
515 &error,
516 &path);
517 if (r < 0)
518 return log_error_errno(r, "Failed to get FragmentPath: %s", bus_error_message(&error, r));
519
520 if (ret_dropin_paths) {
521 r = sd_bus_get_property_strv(
522 bus,
523 "org.freedesktop.systemd1",
524 dbus_path,
525 "org.freedesktop.systemd1.Unit",
526 "DropInPaths",
527 &error,
528 &dropins);
529 if (r < 0)
530 return log_error_errno(r, "Failed to get DropInPaths: %s", bus_error_message(&error, r));
531 }
532 } else {
533 const char *_path;
534 _cleanup_set_free_free_ Set *names = NULL;
535
536 if (!*cached_name_map) {
537 r = unit_file_build_name_map(lp, NULL, cached_id_map, cached_name_map, NULL);
538 if (r < 0)
539 return r;
540 }
541
542 r = unit_file_find_fragment(*cached_id_map, *cached_name_map, unit_name, &_path, &names);
543 if (r < 0)
b420e6f0 544 return log_error_errno(r, "Failed to find fragment for '%s': %m", unit_name);
daf71ef6
LP
545
546 if (_path) {
547 path = strdup(_path);
548 if (!path)
549 return log_oom();
550 }
551
552 if (ret_dropin_paths) {
553 r = unit_file_find_dropin_paths(arg_root, lp->search_path, NULL,
554 ".d", ".conf",
555 NULL, names, &dropins);
556 if (r < 0)
557 return r;
558 }
559 }
560
d5427dd2 561 finish:
daf71ef6
LP
562 if (isempty(path)) {
563 *ret_fragment_path = NULL;
564 r = 0;
565 } else {
566 *ret_fragment_path = TAKE_PTR(path);
567 r = 1;
568 }
569
570 if (ret_dropin_paths) {
571 if (!strv_isempty(dropins)) {
572 *ret_dropin_paths = TAKE_PTR(dropins);
573 r = 1;
574 } else
575 *ret_dropin_paths = NULL;
576 }
577
daf71ef6
LP
578 if (r == 0 && !arg_force)
579 log_error("No files found for %s.", unit_name);
580
581 return r;
582}
583
584static int unit_find_template_path(
585 const char *unit_name,
586 LookupPaths *lp,
587 char **ret_fragment_path,
588 char **ret_template) {
589
590 _cleanup_free_ char *t = NULL, *f = NULL;
591 int r;
592
593 /* Returns 1 if a fragment was found, 0 if not found, negative on error. */
594
595 r = unit_file_find_path(lp, unit_name, &f);
596 if (r < 0)
597 return r;
598 if (r > 0) {
599 if (ret_fragment_path)
600 *ret_fragment_path = TAKE_PTR(f);
601 if (ret_template)
602 *ret_template = NULL;
603 return r; /* found a real unit */
604 }
605
606 r = unit_name_template(unit_name, &t);
607 if (r == -EINVAL) {
608 if (ret_fragment_path)
609 *ret_fragment_path = NULL;
610 if (ret_template)
611 *ret_template = NULL;
612
613 return 0; /* not a template, does not exist */
614 }
615 if (r < 0)
616 return log_error_errno(r, "Failed to determine template name: %m");
617
618 r = unit_file_find_path(lp, t, ret_fragment_path);
619 if (r < 0)
620 return r;
621
622 if (ret_template)
623 *ret_template = r > 0 ? TAKE_PTR(t) : NULL;
624
625 return r;
626}
627
628int unit_is_masked(sd_bus *bus, LookupPaths *lp, const char *name) {
629 _cleanup_free_ char *load_state = NULL;
630 int r;
631
632 if (unit_name_is_valid(name, UNIT_NAME_TEMPLATE)) {
633 _cleanup_free_ char *path = NULL;
634
635 /* A template cannot be loaded, but it can be still masked, so
636 * we need to use a different method. */
637
638 r = unit_file_find_path(lp, name, &path);
639 if (r < 0)
640 return r;
641 if (r == 0)
642 return false;
643 return null_or_empty_path(path);
644 }
645
646 r = unit_load_state(bus, name, &load_state);
647 if (r < 0)
648 return r;
649
650 return streq(load_state, "masked");
651}
652
653int unit_exists(LookupPaths *lp, const char *unit) {
654 typedef struct UnitStateInfo {
655 const char *load_state;
656 const char *active_state;
657 } UnitStateInfo;
658
659 static const struct bus_properties_map property_map[] = {
660 { "LoadState", "s", NULL, offsetof(UnitStateInfo, load_state) },
661 { "ActiveState", "s", NULL, offsetof(UnitStateInfo, active_state) },
662 {},
663 };
664
665 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
666 _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
667 _cleanup_free_ char *path = NULL;
668 UnitStateInfo info = {};
669 sd_bus *bus;
670 int r;
671
672 if (unit_name_is_valid(unit, UNIT_NAME_TEMPLATE))
673 return unit_find_template_path(unit, lp, NULL, NULL);
674
675 path = unit_dbus_path_from_name(unit);
676 if (!path)
677 return log_oom();
678
679 r = acquire_bus(BUS_MANAGER, &bus);
680 if (r < 0)
681 return r;
682
683 r = bus_map_all_properties(bus, "org.freedesktop.systemd1", path, property_map, 0, &error, &m, &info);
684 if (r < 0)
685 return log_error_errno(r, "Failed to get properties: %s", bus_error_message(&error, r));
686
687 return !streq_ptr(info.load_state, "not-found") || !streq_ptr(info.active_state, "inactive");
688}
689
690
691int append_unit_dependencies(sd_bus *bus, char **names, char ***ret) {
692 _cleanup_strv_free_ char **with_deps = NULL;
daf71ef6
LP
693
694 assert(bus);
695 assert(ret);
696
697 STRV_FOREACH(name, names) {
698 _cleanup_strv_free_ char **deps = NULL;
699
700 if (strv_extend(&with_deps, *name) < 0)
701 return log_oom();
702
703 (void) unit_get_dependencies(bus, *name, &deps);
704
705 if (strv_extend_strv(&with_deps, deps, true) < 0)
706 return log_oom();
707 }
708
709 *ret = TAKE_PTR(with_deps);
710
711 return 0;
712}
713
714int maybe_extend_with_unit_dependencies(sd_bus *bus, char ***list) {
715 _cleanup_strv_free_ char **list_with_deps = NULL;
716 int r;
717
718 assert(bus);
719 assert(list);
720
721 if (!arg_with_dependencies)
722 return 0;
723
724 r = append_unit_dependencies(bus, *list, &list_with_deps);
725 if (r < 0)
726 return log_error_errno(r, "Failed to append unit dependencies: %m");
727
728 strv_free(*list);
729 *list = TAKE_PTR(list_with_deps);
730 return 0;
731}
732
733int unit_get_dependencies(sd_bus *bus, const char *name, char ***ret) {
734 _cleanup_strv_free_ char **deps = NULL;
735
cbc2593e 736 static const struct bus_properties_map map[_DEPENDENCY_MAX][7] = {
daf71ef6
LP
737 [DEPENDENCY_FORWARD] = {
738 { "Requires", "as", NULL, 0 },
739 { "Requisite", "as", NULL, 0 },
740 { "Wants", "as", NULL, 0 },
741 { "ConsistsOf", "as", NULL, 0 },
742 { "BindsTo", "as", NULL, 0 },
cbc2593e 743 { "Upholds", "as", NULL, 0 },
daf71ef6
LP
744 {}
745 },
746 [DEPENDENCY_REVERSE] = {
747 { "RequiredBy", "as", NULL, 0 },
748 { "RequisiteOf", "as", NULL, 0 },
749 { "WantedBy", "as", NULL, 0 },
750 { "PartOf", "as", NULL, 0 },
751 { "BoundBy", "as", NULL, 0 },
cbc2593e 752 { "UpheldBy", "as", NULL, 0 },
daf71ef6
LP
753 {}
754 },
755 [DEPENDENCY_AFTER] = {
756 { "After", "as", NULL, 0 },
757 {}
758 },
759 [DEPENDENCY_BEFORE] = {
760 { "Before", "as", NULL, 0 },
761 {}
762 },
763 };
764
765 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
766 _cleanup_free_ char *dbus_path = NULL;
767 int r;
768
769 assert(bus);
770 assert(name);
771 assert(ret);
772
773 dbus_path = unit_dbus_path_from_name(name);
774 if (!dbus_path)
775 return log_oom();
776
777 r = bus_map_all_properties(bus,
778 "org.freedesktop.systemd1",
779 dbus_path,
780 map[arg_dependency],
781 BUS_MAP_STRDUP,
782 &error,
783 NULL,
784 &deps);
785 if (r < 0)
786 return log_error_errno(r, "Failed to get properties of %s: %s", name, bus_error_message(&error, r));
787
788 strv_uniq(deps); /* Sometimes a unit might have multiple deps on the other unit,
789 * but we still want to show it just once. */
790 *ret = TAKE_PTR(deps);
791
792 return 0;
793}
794
795const char* unit_type_suffix(const char *unit) {
796 const char *dot;
797
798 dot = strrchr(unit, '.');
799 if (!dot)
800 return "";
801
802 return dot + 1;
803}
804
805bool output_show_unit(const UnitInfo *u, char **patterns) {
806 assert(u);
807
808 if (!strv_fnmatch_or_empty(patterns, u->id, FNM_NOESCAPE))
809 return false;
810
d29cc4d6 811 if (arg_types && !strv_contains(arg_types, unit_type_suffix(u->id)))
daf71ef6
LP
812 return false;
813
814 if (arg_all)
815 return true;
816
817 /* Note that '--all' is not purely a state filter, but also a filter that hides units that "follow"
818 * other units (which is used for device units that appear under different names). */
819 if (!isempty(u->following))
820 return false;
821
822 if (!strv_isempty(arg_states))
823 return true;
824
825 /* By default show all units except the ones in inactive state and with no pending job */
826 if (u->job_id > 0)
827 return true;
828
829 if (streq(u->active_state, "inactive"))
830 return false;
831
832 return true;
833}
834
835bool install_client_side(void) {
836 /* Decides when to execute enable/disable/... operations client-side rather than server-side. */
837
838 if (running_in_chroot_or_offline())
839 return true;
840
841 if (sd_booted() <= 0)
842 return true;
843
844 if (!isempty(arg_root))
845 return true;
846
b380b643 847 if (arg_scope == LOOKUP_SCOPE_GLOBAL)
daf71ef6
LP
848 return true;
849
850 /* Unsupported environment variable, mostly for debugging purposes */
851 if (getenv_bool("SYSTEMCTL_INSTALL_CLIENT_SIDE") > 0)
852 return true;
853
854 return false;
855}
856
857int output_table(Table *table) {
858 int r;
859
860 assert(table);
861
862 if (OUTPUT_MODE_IS_JSON(arg_output))
863 r = table_print_json(table, NULL, output_mode_to_json_format_flags(arg_output) | JSON_FORMAT_COLOR_AUTO);
864 else
865 r = table_print(table, NULL);
866 if (r < 0)
867 return table_log_print_error(r);
868
869 return 0;
870}
871
872bool show_preset_for_state(UnitFileState state) {
873 /* Don't show preset state in those unit file states, it'll only confuse users. */
874 return !IN_SET(state,
875 UNIT_FILE_ALIAS,
876 UNIT_FILE_STATIC,
877 UNIT_FILE_GENERATED,
878 UNIT_FILE_TRANSIENT);
879}
880
881UnitFileFlags unit_file_flags_from_args(void) {
882 return (arg_runtime ? UNIT_FILE_RUNTIME : 0) |
883 (arg_force ? UNIT_FILE_FORCE : 0);
884}
885
886int mangle_names(const char *operation, char **original_names, char ***ret_mangled_names) {
887 _cleanup_strv_free_ char **l = NULL;
de010b0b 888 char **i;
daf71ef6
LP
889 int r;
890
891 assert(ret_mangled_names);
892
893 l = i = new(char*, strv_length(original_names) + 1);
894 if (!l)
895 return log_oom();
896
897 STRV_FOREACH(name, original_names) {
898
899 /* When enabling units qualified path names are OK, too, hence allow them explicitly. */
900
23484e12
ZJS
901 if (is_path(*name))
902 r = path_make_absolute_cwd(*name, i);
903 else
daf71ef6
LP
904 r = unit_name_mangle_with_suffix(*name, operation,
905 arg_quiet ? 0 : UNIT_NAME_MANGLE_WARN,
906 ".service", i);
23484e12
ZJS
907 if (r < 0) {
908 *i = NULL;
909 return log_error_errno(r, "Failed to mangle unit name or path '%s': %m", *name);
daf71ef6
LP
910 }
911
912 i++;
913 }
914
915 *i = NULL;
916 *ret_mangled_names = TAKE_PTR(l);
917
918 return 0;
919}
920
921int halt_now(enum action a) {
922 /* The kernel will automatically flush ATA disks and suchlike on reboot(), but the file systems need
923 * to be synced explicitly in advance. */
924 if (!arg_no_sync && !arg_dry_run)
925 (void) sync();
926
927 /* Make sure C-A-D is handled by the kernel from this point on... */
928 if (!arg_dry_run)
929 (void) reboot(RB_ENABLE_CAD);
930
931 switch (a) {
932
933 case ACTION_HALT:
934 if (!arg_quiet)
935 log_info("Halting.");
936 if (arg_dry_run)
937 return 0;
938 (void) reboot(RB_HALT_SYSTEM);
939 return -errno;
940
941 case ACTION_POWEROFF:
942 if (!arg_quiet)
943 log_info("Powering off.");
944 if (arg_dry_run)
945 return 0;
946 (void) reboot(RB_POWER_OFF);
947 return -errno;
948
949 case ACTION_KEXEC:
950 case ACTION_REBOOT:
951 return reboot_with_parameter(REBOOT_FALLBACK |
952 (arg_quiet ? 0 : REBOOT_LOG) |
953 (arg_dry_run ? REBOOT_DRY_RUN : 0));
954
955 default:
04499a70 956 assert_not_reached();
daf71ef6
LP
957 }
958}