]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/systemctl/systemctl.c
update TODO
[thirdparty/systemd.git] / src / systemctl / systemctl.c
CommitLineData
d6c9574f 1/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
7e4249b9
LP
2
3/***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
f459b602 7 Copyright 2013 Marc-Antoine Perennou
7e4249b9
LP
8
9 systemd is free software; you can redistribute it and/or modify it
5430f7f2
LP
10 under the terms of the GNU Lesser General Public License as published by
11 the Free Software Foundation; either version 2.1 of the License, or
7e4249b9
LP
12 (at your option) any later version.
13
14 systemd is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5430f7f2 17 Lesser General Public License for more details.
7e4249b9 18
5430f7f2 19 You should have received a copy of the GNU Lesser General Public License
7e4249b9
LP
20 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21***/
22
e4b61340 23#include <sys/reboot.h>
37185ec8
WC
24#include <linux/reboot.h>
25#include <sys/syscall.h>
7e4249b9
LP
26#include <stdio.h>
27#include <getopt.h>
a9cdc94f 28#include <locale.h>
7e4249b9
LP
29#include <stdbool.h>
30#include <string.h>
31#include <errno.h>
32#include <sys/ioctl.h>
33#include <termios.h>
34#include <unistd.h>
eb22ac37 35#include <fcntl.h>
f1c5860b 36#include <sys/socket.h>
ee5762e3 37#include <sys/stat.h>
0e098b15 38#include <stddef.h>
501fc174 39#include <sys/prctl.h>
d8fba7c6 40#include <fnmatch.h>
81527be1 41
f459b602
MAP
42#include "sd-daemon.h"
43#include "sd-shutdown.h"
44#include "sd-login.h"
45#include "sd-bus.h"
7e4249b9
LP
46#include "log.h"
47#include "util.h"
48#include "macro.h"
49#include "set.h"
e4b61340 50#include "utmp-wtmp.h"
514f4ef5 51#include "special.h"
eb22ac37 52#include "initreq.h"
9eb977db 53#include "path-util.h"
e4a9373f 54#include "strv.h"
ab35fb1b 55#include "cgroup-show.h"
c6c18be3 56#include "cgroup-util.h"
582a507f 57#include "list.h"
ee5762e3
LP
58#include "path-lookup.h"
59#include "conf-parser.h"
d06dacd0 60#include "exit-status.h"
7d568925 61#include "build.h"
71fad675 62#include "unit-name.h"
1968a360 63#include "pager.h"
6bb92a16
LP
64#include "spawn-ask-password-agent.h"
65#include "spawn-polkit-agent.h"
729e3769 66#include "install.h"
86aa7ba4 67#include "logs-show.h"
67445f4e 68#include "socket-util.h"
a5c32cff 69#include "fileio.h"
cda134ab 70#include "copy.h"
ac3efa8a 71#include "env-util.h"
f459b602
MAP
72#include "bus-util.h"
73#include "bus-message.h"
74#include "bus-error.h"
718db961 75#include "bus-errors.h"
7e4249b9 76
20b3f379 77static char **arg_types = NULL;
9b9b3d36 78static char **arg_states = NULL;
20b3f379 79static char **arg_properties = NULL;
7e4249b9 80static bool arg_all = false;
afba4199
ZJS
81static enum dependency {
82 DEPENDENCY_FORWARD,
83 DEPENDENCY_REVERSE,
84 DEPENDENCY_AFTER,
85 DEPENDENCY_BEFORE,
071066a5 86 _DEPENDENCY_MAX
afba4199 87} arg_dependency = DEPENDENCY_FORWARD;
e67c3609 88static const char *arg_job_mode = "replace";
729e3769 89static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
ee5762e3 90static bool arg_no_block = false;
ebed32bf 91static bool arg_no_legend = false;
0736af98 92static bool arg_no_pager = false;
e4b61340 93static bool arg_no_wtmp = false;
514f4ef5 94static bool arg_no_wall = false;
ee5762e3 95static bool arg_no_reload = false;
991f2a39 96static bool arg_show_types = false;
b37844d3 97static bool arg_ignore_inhibitors = false;
e4b61340 98static bool arg_dry = false;
0183528f 99static bool arg_quiet = false;
ee5762e3 100static bool arg_full = false;
1238ee09 101static bool arg_recursive = false;
e606bb61 102static int arg_force = 0;
6bb92a16 103static bool arg_ask_password = true;
729e3769 104static bool arg_runtime = false;
d309c1c3 105static UnitFilePresetMode arg_preset_mode = UNIT_FILE_PRESET_FULL;
e4b61340 106static char **arg_wall = NULL;
8a0867d6 107static const char *arg_kill_who = NULL;
8a0867d6 108static int arg_signal = SIGTERM;
69fc152f 109static const char *arg_root = NULL;
f6144808 110static usec_t arg_when = 0;
4445a875 111static enum action {
f459b602 112 _ACTION_INVALID,
e4b61340
LP
113 ACTION_SYSTEMCTL,
114 ACTION_HALT,
115 ACTION_POWEROFF,
116 ACTION_REBOOT,
20b09ca7
LP
117 ACTION_KEXEC,
118 ACTION_EXIT,
6edd7d0a
LP
119 ACTION_SUSPEND,
120 ACTION_HIBERNATE,
6524990f 121 ACTION_HYBRID_SLEEP,
e4b61340
LP
122 ACTION_RUNLEVEL2,
123 ACTION_RUNLEVEL3,
124 ACTION_RUNLEVEL4,
125 ACTION_RUNLEVEL5,
126 ACTION_RESCUE,
514f4ef5
LP
127 ACTION_EMERGENCY,
128 ACTION_DEFAULT,
e4b61340
LP
129 ACTION_RELOAD,
130 ACTION_REEXEC,
131 ACTION_RUNLEVEL,
f6144808 132 ACTION_CANCEL_SHUTDOWN,
e4b61340
LP
133 _ACTION_MAX
134} arg_action = ACTION_SYSTEMCTL;
f459b602 135static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
7085053a 136static char *arg_host = NULL;
df50185b
LP
137static unsigned arg_lines = 10;
138static OutputMode arg_output = OUTPUT_SHORT;
5d0c05e5 139static bool arg_plain = false;
1238ee09 140
1238ee09
LP
141static bool original_stdout_is_tty;
142
f459b602 143static int daemon_reload(sd_bus *bus, char **args);
477def80 144static int halt_now(enum action a);
dbc2c080
LP
145static int check_one_unit(sd_bus *bus, const char *name, const char *good_states, bool quiet);
146
d8fba7c6
ZJS
147static char** strv_skip_first(char **strv) {
148 if (strv_length(strv) > 0)
149 return strv + 1;
150 return NULL;
151}
152
3b0727f5 153static void pager_open_if_enabled(void) {
f8440af5 154
729e3769
LP
155 if (arg_no_pager)
156 return;
3b0727f5 157
1b12a7b5 158 pager_open(false);
729e3769 159}
c0f9c7da 160
6bb92a16 161static void ask_password_agent_open_if_enabled(void) {
501fc174 162
729e3769 163 /* Open the password agent as a child process if necessary */
501fc174
LP
164
165 if (!arg_ask_password)
166 return;
715554e7 167
729e3769 168 if (arg_scope != UNIT_FILE_SYSTEM)
501fc174
LP
169 return;
170
cbc9fbd1
LP
171 if (arg_transport != BUS_TRANSPORT_LOCAL)
172 return;
173
6bb92a16
LP
174 ask_password_agent_open();
175}
176
ba1261bc 177#ifdef HAVE_LOGIND
6bb92a16
LP
178static void polkit_agent_open_if_enabled(void) {
179
180 /* Open the polkit agent as a child process if necessary */
181
182 if (!arg_ask_password)
183 return;
184
185 if (arg_scope != UNIT_FILE_SYSTEM)
186 return;
187
f459b602
MAP
188 if (arg_transport != BUS_TRANSPORT_LOCAL)
189 return;
190
6bb92a16 191 polkit_agent_open();
501fc174 192}
ba1261bc 193#endif
501fc174 194
f459b602 195static int translate_bus_error_to_exit_status(int r, const sd_bus_error *error) {
22f4096c
LP
196 assert(error);
197
f459b602 198 if (!sd_bus_error_is_set(error))
22f4096c
LP
199 return r;
200
f459b602
MAP
201 if (sd_bus_error_has_name(error, SD_BUS_ERROR_ACCESS_DENIED) ||
202 sd_bus_error_has_name(error, BUS_ERROR_ONLY_BY_DEPENDENCY) ||
203 sd_bus_error_has_name(error, BUS_ERROR_NO_ISOLATION) ||
204 sd_bus_error_has_name(error, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE))
22f4096c
LP
205 return EXIT_NOPERMISSION;
206
f459b602 207 if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT))
22f4096c
LP
208 return EXIT_NOTINSTALLED;
209
f459b602 210 if (sd_bus_error_has_name(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE) ||
718db961 211 sd_bus_error_has_name(error, SD_BUS_ERROR_NOT_SUPPORTED))
22f4096c
LP
212 return EXIT_NOTIMPLEMENTED;
213
f459b602 214 if (sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED))
22f4096c
LP
215 return EXIT_NOTCONFIGURED;
216
217 if (r != 0)
218 return r;
219
220 return EXIT_FAILURE;
221}
222
4c80c73c 223static void warn_wall(enum action a) {
ef2f1067 224 static const char *table[_ACTION_MAX] = {
dfcc5c33
MS
225 [ACTION_HALT] = "The system is going down for system halt NOW!",
226 [ACTION_REBOOT] = "The system is going down for reboot NOW!",
227 [ACTION_POWEROFF] = "The system is going down for power-off NOW!",
228 [ACTION_KEXEC] = "The system is going down for kexec reboot NOW!",
229 [ACTION_RESCUE] = "The system is going down to rescue mode NOW!",
230 [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!",
231 [ACTION_CANCEL_SHUTDOWN] = "The system shutdown has been cancelled NOW!"
ef2f1067
LP
232 };
233
514f4ef5
LP
234 if (arg_no_wall)
235 return;
236
e4a9373f 237 if (arg_wall) {
f84190d8 238 _cleanup_free_ char *p;
e4a9373f 239
7e59bfcb
LP
240 p = strv_join(arg_wall, " ");
241 if (!p) {
f84190d8 242 log_oom();
e4a9373f
LP
243 return;
244 }
245
246 if (*p) {
9003d9b0 247 utmp_wall(p, NULL, NULL);
e4a9373f
LP
248 return;
249 }
e4a9373f
LP
250 }
251
4c80c73c 252 if (!table[a])
ef2f1067
LP
253 return;
254
9003d9b0 255 utmp_wall(table[a], NULL, NULL);
ef2f1067
LP
256}
257
729e3769
LP
258static bool avoid_bus(void) {
259
260 if (running_in_chroot() > 0)
261 return true;
262
263 if (sd_booted() <= 0)
264 return true;
265
266 if (!isempty(arg_root))
267 return true;
268
269 if (arg_scope == UNIT_FILE_GLOBAL)
270 return true;
271
272 return false;
273}
274
36c32ba2 275static int compare_unit_info(const void *a, const void *b) {
f459b602 276 const UnitInfo *u = a, *v = b;
36c32ba2 277 const char *d1, *d2;
1238ee09
LP
278 int r;
279
280 /* First, order by machine */
281 if (!u->machine && v->machine)
282 return -1;
283 if (u->machine && !v->machine)
284 return 1;
285 if (u->machine && v->machine) {
286 r = strcasecmp(u->machine, v->machine);
287 if (r != 0)
288 return r;
289 }
36c32ba2 290
1238ee09 291 /* Second, order by unit type */
36c32ba2
LP
292 d1 = strrchr(u->id, '.');
293 d2 = strrchr(v->id, '.');
36c32ba2 294 if (d1 && d2) {
f84190d8
LP
295 r = strcasecmp(d1, d2);
296 if (r != 0)
36c32ba2
LP
297 return r;
298 }
299
1238ee09 300 /* Third, order by name */
a2a3a5b9 301 return strcasecmp(u->id, v->id);
36c32ba2
LP
302}
303
d8fba7c6 304static bool output_show_unit(const UnitInfo *u, char **patterns) {
d8fba7c6
ZJS
305 if (!strv_isempty(patterns)) {
306 char **pattern;
307
308 STRV_FOREACH(pattern, patterns)
309 if (fnmatch(*pattern, u->id, FNM_NOESCAPE) == 0)
6c71341a 310 goto next;
d8fba7c6
ZJS
311 return false;
312 }
313
6c71341a
ZJS
314next:
315 if (arg_types) {
316 const char *dot;
317
318 dot = strrchr(u->id, '.');
319 if (!dot)
320 return false;
321
322 if (!strv_find(arg_types, dot+1))
323 return false;
324 }
325
326 if (arg_all)
327 return true;
328
329 if (u->job_id > 0)
330 return true;
331
332 if (streq(u->active_state, "inactive") || u->following[0])
333 return false;
334
335 return true;
33330222
ZJS
336}
337
1238ee09 338static int output_units_list(const UnitInfo *unit_infos, unsigned c) {
90c3f79d 339 unsigned circle_len = 0, id_len, max_id_len, load_len, active_len, sub_len, job_len, desc_len;
f459b602 340 const UnitInfo *u;
a1074881 341 unsigned n_shown = 0;
ccd41387 342 int job_count = 0;
33330222 343
f8294e41
JT
344 max_id_len = strlen("UNIT");
345 load_len = strlen("LOAD");
346 active_len = strlen("ACTIVE");
347 sub_len = strlen("SUB");
348 job_len = strlen("JOB");
4deb3b93 349 desc_len = 0;
b036fc00
LP
350
351 for (u = unit_infos; u < unit_infos + c; u++) {
1238ee09 352 max_id_len = MAX(max_id_len, strlen(u->id) + (u->machine ? strlen(u->machine)+1 : 0));
a1074881 353 load_len = MAX(load_len, strlen(u->load_state));
b036fc00
LP
354 active_len = MAX(active_len, strlen(u->active_state));
355 sub_len = MAX(sub_len, strlen(u->sub_state));
f459b602 356
ccd41387 357 if (u->job_id != 0) {
b036fc00 358 job_len = MAX(job_len, strlen(u->job_type));
ccd41387
ZJS
359 job_count++;
360 }
90c3f79d
LP
361
362 if (!arg_no_legend &&
363 (streq(u->active_state, "failed") ||
364 STR_IN_SET(u->load_state, "error", "not-found", "masked")))
365 circle_len = 2;
33330222
ZJS
366 }
367
184ecaf7 368 if (!arg_full && original_stdout_is_tty) {
4deb3b93 369 unsigned basic_len;
cbc9fbd1 370
9607d947 371 id_len = MIN(max_id_len, 25u);
90c3f79d 372 basic_len = circle_len + 5 + id_len + 5 + active_len + sub_len;
cbc9fbd1 373
ccd41387
ZJS
374 if (job_count)
375 basic_len += job_len + 1;
cbc9fbd1 376
4deb3b93
MS
377 if (basic_len < (unsigned) columns()) {
378 unsigned extra_len, incr;
379 extra_len = columns() - basic_len;
cbc9fbd1 380
4deb3b93
MS
381 /* Either UNIT already got 25, or is fully satisfied.
382 * Grant up to 25 to DESC now. */
9607d947 383 incr = MIN(extra_len, 25u);
4deb3b93
MS
384 desc_len += incr;
385 extra_len -= incr;
cbc9fbd1 386
4deb3b93
MS
387 /* split the remaining space between UNIT and DESC,
388 * but do not give UNIT more than it needs. */
389 if (extra_len > 0) {
390 incr = MIN(extra_len / 2, max_id_len - id_len);
391 id_len += incr;
392 desc_len += extra_len - incr;
393 }
394 }
395 } else
396 id_len = max_id_len;
397
b036fc00 398 for (u = unit_infos; u < unit_infos + c; u++) {
1238ee09 399 _cleanup_free_ char *e = NULL, *j = NULL;
90c3f79d
LP
400 const char *on_loaded = "", *off_loaded = "";
401 const char *on_active = "", *off_active = "";
402 const char *on_circle = "", *off_circle = "";
1238ee09 403 const char *id;
90c3f79d 404 bool circle = false;
b036fc00 405
ad94ad63 406 if (!n_shown && !arg_no_legend) {
90c3f79d
LP
407
408 if (circle_len > 0)
409 fputs(" ", stdout);
410
cbc9fbd1
LP
411 printf("%-*s %-*s %-*s %-*s ",
412 id_len, "UNIT",
413 load_len, "LOAD",
414 active_len, "ACTIVE",
415 sub_len, "SUB");
416
ccd41387
ZJS
417 if (job_count)
418 printf("%-*s ", job_len, "JOB");
cbc9fbd1 419
ad94ad63
ZJS
420 if (!arg_full && arg_no_pager)
421 printf("%.*s\n", desc_len, "DESCRIPTION");
422 else
423 printf("%s\n", "DESCRIPTION");
424 }
425
688c6725
LP
426 n_shown++;
427
90c3f79d
LP
428 if (STR_IN_SET(u->load_state, "error", "not-found", "masked")) {
429 on_loaded = ansi_highlight_red();
430 on_circle = ansi_highlight_yellow();
431 off_loaded = off_circle = ansi_highlight_off();
432 circle = true;
433 }
b036fc00
LP
434
435 if (streq(u->active_state, "failed")) {
90c3f79d
LP
436 on_circle = on_active = ansi_highlight_red();
437 off_circle = off_active = ansi_highlight_off();
438 circle = true;
439 }
eb68c413 440
1238ee09
LP
441 if (u->machine) {
442 j = strjoin(u->machine, ":", u->id, NULL);
443 if (!j)
444 return log_oom();
445
446 id = j;
447 } else
448 id = u->id;
449
450 if (arg_full) {
451 e = ellipsize(id, id_len, 33);
452 if (!e)
453 return log_oom();
454
455 id = e;
456 }
eb68c413 457
90c3f79d 458 if (circle_len > 0)
b7bbdabe 459 printf("%s%s%s ", on_circle, circle ? draw_special_char(DRAW_BLACK_CIRCLE) : " ", off_circle);
90c3f79d 460
a1074881 461 printf("%s%-*s%s %s%-*s%s %s%-*s %-*s%s %-*s",
90c3f79d 462 on_active, id_len, id, off_active,
a1074881 463 on_loaded, load_len, u->load_state, off_loaded,
b036fc00
LP
464 on_active, active_len, u->active_state,
465 sub_len, u->sub_state, off_active,
ccd41387 466 job_count ? job_len + 1 : 0, u->job_id ? u->job_type : "");
cbc9fbd1 467
184ecaf7 468 if (desc_len > 0)
798e258d
MS
469 printf("%.*s\n", desc_len, u->description);
470 else
471 printf("%s\n", u->description);
eb68c413
ZJS
472 }
473
ebed32bf 474 if (!arg_no_legend) {
57f7ae4f
ZJS
475 const char *on, *off;
476
477 if (n_shown) {
90c3f79d
LP
478 puts("\n"
479 "LOAD = Reflects whether the unit definition was properly loaded.\n"
e3e0314b
ZJS
480 "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n"
481 "SUB = The low-level unit activation state, values depend on unit type.");
482 puts(job_count ? "JOB = Pending job for the unit.\n" : "");
0b5a519c
DS
483 on = ansi_highlight();
484 off = ansi_highlight_off();
57f7ae4f 485 } else {
0b5a519c
DS
486 on = ansi_highlight_red();
487 off = ansi_highlight_off();
57f7ae4f 488 }
eb68c413
ZJS
489
490 if (arg_all)
48c2826b 491 printf("%s%u loaded units listed.%s\n"
57f7ae4f
ZJS
492 "To show all installed unit files use 'systemctl list-unit-files'.\n",
493 on, n_shown, off);
eb68c413 494 else
48c2826b 495 printf("%s%u loaded units listed.%s Pass --all to see loaded but inactive units, too.\n"
57f7ae4f
ZJS
496 "To show all installed unit files use 'systemctl list-unit-files'.\n",
497 on, n_shown, off);
eb68c413 498 }
1238ee09
LP
499
500 return 0;
eb68c413
ZJS
501}
502
a00963a2 503static int get_unit_list(
f459b602 504 sd_bus *bus,
1238ee09
LP
505 const char *machine,
506 char **patterns,
507 UnitInfo **unit_infos,
508 int c,
509 sd_bus_message **_reply) {
a00963a2 510
cdc06ed7 511 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
f459b602
MAP
512 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
513 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
ca2d3784 514 size_t size = c;
1238ee09 515 int r;
f459b602 516 UnitInfo u;
7e4249b9 517
265a7a2a 518 assert(bus);
1238ee09 519 assert(unit_infos);
f459b602 520 assert(_reply);
1238ee09 521
cdc06ed7 522 r = sd_bus_message_new_method_call(
f22f08cd 523 bus,
cdc06ed7 524 &m,
f22f08cd
SP
525 "org.freedesktop.systemd1",
526 "/org/freedesktop/systemd1",
527 "org.freedesktop.systemd1.Manager",
cdc06ed7
DS
528 "ListUnitsFiltered");
529
530 if (r < 0)
531 return bus_log_create_error(r);
532
533 r = sd_bus_message_append_strv(m, arg_states);
534 if (r < 0)
535 return bus_log_create_error(r);
536
537 r = sd_bus_call(bus, m, 0, &error, &reply);
f459b602
MAP
538 if (r < 0) {
539 log_error("Failed to list units: %s", bus_error_message(&error, r));
f84190d8 540 return r;
7e4249b9
LP
541 }
542
f459b602
MAP
543 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssssouso)");
544 if (r < 0)
545 return bus_log_parse_error(r);
7e4249b9 546
f459b602 547 while ((r = bus_parse_unit_info(reply, &u)) > 0) {
1238ee09
LP
548 u.machine = machine;
549
8d5ba5a9
ZJS
550 if (!output_show_unit(&u, patterns))
551 continue;
7e4249b9 552
1238ee09 553 if (!GREEDY_REALLOC(*unit_infos, size, c+1))
f459b602 554 return log_oom();
36c32ba2 555
1238ee09 556 (*unit_infos)[c++] = u;
991f2a39 557 }
f459b602
MAP
558 if (r < 0)
559 return bus_log_parse_error(r);
991f2a39 560
f459b602
MAP
561 r = sd_bus_message_exit_container(reply);
562 if (r < 0)
563 return bus_log_parse_error(r);
564
565 *_reply = reply;
566 reply = NULL;
567
1238ee09
LP
568 return c;
569}
570
571static void message_set_freep(Set **set) {
572 sd_bus_message *m;
573
574 while ((m = set_steal_first(*set)))
575 sd_bus_message_unref(m);
576
577 set_free(*set);
578}
579
580static int get_unit_list_recursive(
581 sd_bus *bus,
582 char **patterns,
583 UnitInfo **_unit_infos,
584 Set **_replies,
585 char ***_machines) {
586
587 _cleanup_free_ UnitInfo *unit_infos = NULL;
588 _cleanup_(message_set_freep) Set *replies;
589 sd_bus_message *reply;
590 int c, r;
591
592 assert(bus);
593 assert(_replies);
594 assert(_unit_infos);
595 assert(_machines);
596
d5099efc 597 replies = set_new(NULL);
1238ee09
LP
598 if (!replies)
599 return log_oom();
600
601 c = get_unit_list(bus, NULL, patterns, &unit_infos, 0, &reply);
602 if (c < 0)
603 return c;
604
605 r = set_put(replies, reply);
606 if (r < 0) {
607 sd_bus_message_unref(reply);
608 return r;
609 }
610
611 if (arg_recursive) {
612 _cleanup_strv_free_ char **machines = NULL;
613 char **i;
614
615 r = sd_get_machine_names(&machines);
616 if (r < 0)
617 return r;
618
619 STRV_FOREACH(i, machines) {
24996861 620 _cleanup_bus_close_unref_ sd_bus *container = NULL;
1238ee09
LP
621 int k;
622
623 r = sd_bus_open_system_container(&container, *i);
624 if (r < 0) {
625 log_error("Failed to connect to container %s: %s", *i, strerror(-r));
626 continue;
627 }
628
629 k = get_unit_list(container, *i, patterns, &unit_infos, c, &reply);
630 if (k < 0)
631 return k;
632
633 c = k;
634
635 r = set_put(replies, reply);
636 if (r < 0) {
637 sd_bus_message_unref(reply);
638 return r;
639 }
640 }
641
642 *_machines = machines;
643 machines = NULL;
644 } else
645 *_machines = NULL;
646
f459b602
MAP
647 *_unit_infos = unit_infos;
648 unit_infos = NULL;
649
1238ee09
LP
650 *_replies = replies;
651 replies = NULL;
652
f459b602 653 return c;
991f2a39
ZJS
654}
655
f459b602 656static int list_units(sd_bus *bus, char **args) {
f459b602 657 _cleanup_free_ UnitInfo *unit_infos = NULL;
1238ee09
LP
658 _cleanup_(message_set_freep) Set *replies = NULL;
659 _cleanup_strv_free_ char **machines = NULL;
991f2a39
ZJS
660 int r;
661
662 pager_open_if_enabled();
663
1238ee09 664 r = get_unit_list_recursive(bus, strv_skip_first(args), &unit_infos, &replies, &machines);
991f2a39
ZJS
665 if (r < 0)
666 return r;
667
f459b602 668 qsort_safe(unit_infos, r, sizeof(UnitInfo), compare_unit_info);
1238ee09 669 return output_units_list(unit_infos, r);
991f2a39
ZJS
670}
671
a00963a2 672static int get_triggered_units(
f459b602
MAP
673 sd_bus *bus,
674 const char* path,
675 char*** ret) {
a00963a2 676
f459b602 677 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
991f2a39
ZJS
678 int r;
679
f459b602
MAP
680 r = sd_bus_get_property_strv(
681 bus,
682 "org.freedesktop.systemd1",
683 path,
684 "org.freedesktop.systemd1.Unit",
685 "Triggers",
686 &error,
687 ret);
991f2a39 688
f459b602
MAP
689 if (r < 0)
690 log_error("Failed to determine triggers: %s", bus_error_message(&error, r));
991f2a39
ZJS
691
692 return 0;
693}
694
f459b602
MAP
695static int get_listening(
696 sd_bus *bus,
697 const char* unit_path,
cbb76c29 698 char*** listening) {
f459b602 699
f459b602 700 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
cbb76c29 701 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
f459b602 702 const char *type, *path;
cbb76c29 703 int r, n = 0;
991f2a39 704
f459b602
MAP
705 r = sd_bus_get_property(
706 bus,
707 "org.freedesktop.systemd1",
708 unit_path,
709 "org.freedesktop.systemd1.Socket",
710 "Listen",
711 &error,
712 &reply,
713 "a(ss)");
714 if (r < 0) {
715 log_error("Failed to get list of listening sockets: %s", bus_error_message(&error, r));
991f2a39 716 return r;
991f2a39
ZJS
717 }
718
f459b602
MAP
719 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ss)");
720 if (r < 0)
721 return bus_log_parse_error(r);
991f2a39 722
f459b602 723 while ((r = sd_bus_message_read(reply, "(ss)", &type, &path)) > 0) {
36c32ba2 724
0d95178e 725 r = strv_extend(listening, type);
f459b602
MAP
726 if (r < 0)
727 return log_oom();
991f2a39 728
0d95178e 729 r = strv_extend(listening, path);
f459b602
MAP
730 if (r < 0)
731 return log_oom();
7e4249b9 732
cbb76c29 733 n++;
36c32ba2 734 }
f459b602
MAP
735 if (r < 0)
736 return bus_log_parse_error(r);
737
738 r = sd_bus_message_exit_container(reply);
739 if (r < 0)
740 return bus_log_parse_error(r);
36c32ba2 741
cbb76c29 742 return n;
991f2a39
ZJS
743}
744
745struct socket_info {
0cfc3525 746 const char *machine;
991f2a39
ZJS
747 const char* id;
748
749 char* type;
750 char* path;
751
752 /* Note: triggered is a list here, although it almost certainly
753 * will always be one unit. Nevertheless, dbus API allows for multiple
754 * values, so let's follow that.*/
755 char** triggered;
756
757 /* The strv above is shared. free is set only in the first one. */
758 bool own_triggered;
759};
760
cbb76c29 761static int socket_info_compare(const struct socket_info *a, const struct socket_info *b) {
cbc9fbd1
LP
762 int o;
763
cbb76c29
LP
764 assert(a);
765 assert(b);
766
0cfc3525
TA
767 if (!a->machine && b->machine)
768 return -1;
769 if (a->machine && !b->machine)
770 return 1;
771 if (a->machine && b->machine) {
772 o = strcasecmp(a->machine, b->machine);
773 if (o != 0)
774 return o;
775 }
776
cbc9fbd1 777 o = strcmp(a->path, b->path);
991f2a39
ZJS
778 if (o == 0)
779 o = strcmp(a->type, b->type);
cbc9fbd1 780
991f2a39
ZJS
781 return o;
782}
783
784static int output_sockets_list(struct socket_info *socket_infos, unsigned cs) {
785 struct socket_info *s;
f8294e41
JT
786 unsigned pathlen = strlen("LISTEN"),
787 typelen = strlen("TYPE") * arg_show_types,
788 socklen = strlen("UNIT"),
789 servlen = strlen("ACTIVATES");
991f2a39
ZJS
790 const char *on, *off;
791
792 for (s = socket_infos; s < socket_infos + cs; s++) {
991f2a39 793 unsigned tmp = 0;
cbc9fbd1 794 char **a;
991f2a39
ZJS
795
796 socklen = MAX(socklen, strlen(s->id));
797 if (arg_show_types)
798 typelen = MAX(typelen, strlen(s->type));
0cfc3525 799 pathlen = MAX(pathlen, strlen(s->path) + (s->machine ? strlen(s->machine)+1 : 0));
991f2a39
ZJS
800
801 STRV_FOREACH(a, s->triggered)
802 tmp += strlen(*a) + 2*(a != s->triggered);
803 servlen = MAX(servlen, tmp);
804 }
805
806 if (cs) {
571bfc6c
MM
807 if (!arg_no_legend)
808 printf("%-*s %-*.*s%-*s %s\n",
809 pathlen, "LISTEN",
810 typelen + arg_show_types, typelen + arg_show_types, "TYPE ",
811 socklen, "UNIT",
812 "ACTIVATES");
991f2a39
ZJS
813
814 for (s = socket_infos; s < socket_infos + cs; s++) {
0cfc3525
TA
815 _cleanup_free_ char *j = NULL;
816 const char *path;
991f2a39
ZJS
817 char **a;
818
0cfc3525
TA
819 if (s->machine) {
820 j = strjoin(s->machine, ":", s->path, NULL);
821 if (!j)
822 return log_oom();
823 path = j;
824 } else
825 path = s->path;
826
991f2a39
ZJS
827 if (arg_show_types)
828 printf("%-*s %-*s %-*s",
0cfc3525 829 pathlen, path, typelen, s->type, socklen, s->id);
991f2a39
ZJS
830 else
831 printf("%-*s %-*s",
0cfc3525 832 pathlen, path, socklen, s->id);
991f2a39
ZJS
833 STRV_FOREACH(a, s->triggered)
834 printf("%s %s",
835 a == s->triggered ? "" : ",", *a);
836 printf("\n");
837 }
838
0b5a519c
DS
839 on = ansi_highlight();
840 off = ansi_highlight_off();
571bfc6c
MM
841 if (!arg_no_legend)
842 printf("\n");
991f2a39 843 } else {
0b5a519c
DS
844 on = ansi_highlight_red();
845 off = ansi_highlight_off();
991f2a39
ZJS
846 }
847
571bfc6c
MM
848 if (!arg_no_legend) {
849 printf("%s%u sockets listed.%s\n", on, cs, off);
850 if (!arg_all)
851 printf("Pass --all to see loaded but inactive sockets, too.\n");
852 }
265a7a2a
ZJS
853
854 return 0;
855}
856
f459b602 857static int list_sockets(sd_bus *bus, char **args) {
0cfc3525
TA
858 _cleanup_(message_set_freep) Set *replies = NULL;
859 _cleanup_strv_free_ char **machines = NULL;
f459b602 860 _cleanup_free_ UnitInfo *unit_infos = NULL;
8d5ba5a9 861 _cleanup_free_ struct socket_info *socket_infos = NULL;
f459b602 862 const UnitInfo *u;
991f2a39 863 struct socket_info *s;
cbb76c29 864 unsigned cs = 0;
991f2a39 865 size_t size = 0;
ad83b4c4 866 int r = 0, n;
265a7a2a
ZJS
867
868 pager_open_if_enabled();
869
0cfc3525 870 n = get_unit_list_recursive(bus, strv_skip_first(args), &unit_infos, &replies, &machines);
cbb76c29
LP
871 if (n < 0)
872 return n;
265a7a2a 873
cbb76c29 874 for (u = unit_infos; u < unit_infos + n; u++) {
0d95178e 875 _cleanup_strv_free_ char **listening = NULL, **triggered = NULL;
cbb76c29 876 int i, c;
991f2a39 877
cbc9fbd1 878 if (!endswith(u->id, ".socket"))
991f2a39
ZJS
879 continue;
880
881 r = get_triggered_units(bus, u->unit_path, &triggered);
882 if (r < 0)
883 goto cleanup;
884
cbb76c29
LP
885 c = get_listening(bus, u->unit_path, &listening);
886 if (c < 0) {
887 r = c;
991f2a39 888 goto cleanup;
cbb76c29 889 }
991f2a39
ZJS
890
891 if (!GREEDY_REALLOC(socket_infos, size, cs + c)) {
892 r = log_oom();
893 goto cleanup;
894 }
895
896 for (i = 0; i < c; i++)
897 socket_infos[cs + i] = (struct socket_info) {
0cfc3525 898 .machine = u->machine,
991f2a39 899 .id = u->id,
0d95178e
KS
900 .type = listening[i*2],
901 .path = listening[i*2 + 1],
991f2a39
ZJS
902 .triggered = triggered,
903 .own_triggered = i==0,
904 };
905
906 /* from this point on we will cleanup those socket_infos */
907 cs += c;
0d95178e
KS
908 free(listening);
909 listening = triggered = NULL; /* avoid cleanup */
991f2a39
ZJS
910 }
911
7ff7394d
ZJS
912 qsort_safe(socket_infos, cs, sizeof(struct socket_info),
913 (__compar_fn_t) socket_info_compare);
991f2a39
ZJS
914
915 output_sockets_list(socket_infos, cs);
916
917 cleanup:
918 assert(cs == 0 || socket_infos);
919 for (s = socket_infos; s < socket_infos + cs; s++) {
920 free(s->type);
921 free(s->path);
922 if (s->own_triggered)
923 strv_free(s->triggered);
924 }
4445a875 925
872c8faa 926 return r;
4445a875
LP
927}
928
cbb76c29
LP
929static int get_next_elapse(
930 sd_bus *bus,
931 const char *path,
932 dual_timestamp *next) {
933
934 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
cbb76c29
LP
935 dual_timestamp t;
936 int r;
937
938 assert(bus);
939 assert(path);
940 assert(next);
941
942 r = sd_bus_get_property_trivial(
943 bus,
944 "org.freedesktop.systemd1",
945 path,
946 "org.freedesktop.systemd1.Timer",
947 "NextElapseUSecMonotonic",
948 &error,
949 't',
950 &t.monotonic);
951 if (r < 0) {
952 log_error("Failed to get next elapsation time: %s", bus_error_message(&error, r));
953 return r;
954 }
955
956 r = sd_bus_get_property_trivial(
957 bus,
958 "org.freedesktop.systemd1",
959 path,
960 "org.freedesktop.systemd1.Timer",
961 "NextElapseUSecRealtime",
962 &error,
963 't',
964 &t.realtime);
965 if (r < 0) {
966 log_error("Failed to get next elapsation time: %s", bus_error_message(&error, r));
967 return r;
968 }
969
970 *next = t;
971 return 0;
972}
973
d784e2db
LP
974static int get_last_trigger(
975 sd_bus *bus,
976 const char *path,
977 usec_t *last) {
978
979 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
980 int r;
981
982 assert(bus);
983 assert(path);
984 assert(last);
985
986 r = sd_bus_get_property_trivial(
987 bus,
988 "org.freedesktop.systemd1",
989 path,
990 "org.freedesktop.systemd1.Timer",
dedabea4 991 "LastTriggerUSec",
d784e2db
LP
992 &error,
993 't',
994 last);
995 if (r < 0) {
996 log_error("Failed to get last trigger time: %s", bus_error_message(&error, r));
997 return r;
998 }
999
1000 return 0;
1001}
1002
cbb76c29 1003struct timer_info {
806a37e7 1004 const char* machine;
cbb76c29
LP
1005 const char* id;
1006 usec_t next_elapse;
d784e2db 1007 usec_t last_trigger;
cbb76c29
LP
1008 char** triggered;
1009};
1010
1011static int timer_info_compare(const struct timer_info *a, const struct timer_info *b) {
806a37e7
TA
1012 int o;
1013
cbb76c29
LP
1014 assert(a);
1015 assert(b);
1016
806a37e7
TA
1017 if (!a->machine && b->machine)
1018 return -1;
1019 if (a->machine && !b->machine)
1020 return 1;
1021 if (a->machine && b->machine) {
1022 o = strcasecmp(a->machine, b->machine);
1023 if (o != 0)
1024 return o;
1025 }
1026
cbb76c29
LP
1027 if (a->next_elapse < b->next_elapse)
1028 return -1;
1029 if (a->next_elapse > b->next_elapse)
1030 return 1;
1031
1032 return strcmp(a->id, b->id);
1033}
1034
1035static int output_timers_list(struct timer_info *timer_infos, unsigned n) {
1036 struct timer_info *t;
1037 unsigned
f8294e41
JT
1038 nextlen = strlen("NEXT"),
1039 leftlen = strlen("LEFT"),
d784e2db
LP
1040 lastlen = strlen("LAST"),
1041 passedlen = strlen("PASSED"),
f8294e41
JT
1042 unitlen = strlen("UNIT"),
1043 activatelen = strlen("ACTIVATES");
cbb76c29
LP
1044
1045 const char *on, *off;
1046
1047 assert(timer_infos || n == 0);
1048
1049 for (t = timer_infos; t < timer_infos + n; t++) {
1050 unsigned ul = 0;
1051 char **a;
1052
1053 if (t->next_elapse > 0) {
1054 char tstamp[FORMAT_TIMESTAMP_MAX] = "", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = "";
1055
1056 format_timestamp(tstamp, sizeof(tstamp), t->next_elapse);
1057 nextlen = MAX(nextlen, strlen(tstamp) + 1);
1058
1059 format_timestamp_relative(trel, sizeof(trel), t->next_elapse);
1060 leftlen = MAX(leftlen, strlen(trel));
1061 }
1062
d784e2db
LP
1063 if (t->last_trigger > 0) {
1064 char tstamp[FORMAT_TIMESTAMP_MAX] = "", trel[FORMAT_TIMESTAMP_RELATIVE_MAX] = "";
1065
1066 format_timestamp(tstamp, sizeof(tstamp), t->last_trigger);
1067 lastlen = MAX(lastlen, strlen(tstamp) + 1);
1068
1069 format_timestamp_relative(trel, sizeof(trel), t->last_trigger);
1070 passedlen = MAX(passedlen, strlen(trel));
1071 }
1072
806a37e7 1073 unitlen = MAX(unitlen, strlen(t->id) + (t->machine ? strlen(t->machine)+1 : 0));
cbb76c29
LP
1074
1075 STRV_FOREACH(a, t->triggered)
1076 ul += strlen(*a) + 2*(a != t->triggered);
d784e2db 1077
cbb76c29
LP
1078 activatelen = MAX(activatelen, ul);
1079 }
1080
1081 if (n > 0) {
1082 if (!arg_no_legend)
d784e2db
LP
1083 printf("%-*s %-*s %-*s %-*s %-*s %s\n",
1084 nextlen, "NEXT",
1085 leftlen, "LEFT",
1086 lastlen, "LAST",
1087 passedlen, "PASSED",
1088 unitlen, "UNIT",
1089 "ACTIVATES");
cbb76c29
LP
1090
1091 for (t = timer_infos; t < timer_infos + n; t++) {
806a37e7
TA
1092 _cleanup_free_ char *j = NULL;
1093 const char *unit;
d784e2db
LP
1094 char tstamp1[FORMAT_TIMESTAMP_MAX] = "n/a", trel1[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a";
1095 char tstamp2[FORMAT_TIMESTAMP_MAX] = "n/a", trel2[FORMAT_TIMESTAMP_RELATIVE_MAX] = "n/a";
cbb76c29
LP
1096 char **a;
1097
d784e2db
LP
1098 format_timestamp(tstamp1, sizeof(tstamp1), t->next_elapse);
1099 format_timestamp_relative(trel1, sizeof(trel1), t->next_elapse);
cbb76c29 1100
d784e2db
LP
1101 format_timestamp(tstamp2, sizeof(tstamp2), t->last_trigger);
1102 format_timestamp_relative(trel2, sizeof(trel2), t->last_trigger);
1103
806a37e7
TA
1104 if (t->machine) {
1105 j = strjoin(t->machine, ":", t->id, NULL);
1106 if (!j)
1107 return log_oom();
1108 unit = j;
1109 } else
1110 unit = t->id;
1111
d784e2db 1112 printf("%-*s %-*s %-*s %-*s %-*s",
806a37e7 1113 nextlen, tstamp1, leftlen, trel1, lastlen, tstamp2, passedlen, trel2, unitlen, unit);
cbb76c29
LP
1114
1115 STRV_FOREACH(a, t->triggered)
1116 printf("%s %s",
1117 a == t->triggered ? "" : ",", *a);
1118 printf("\n");
1119 }
1120
1121 on = ansi_highlight();
1122 off = ansi_highlight_off();
1123 if (!arg_no_legend)
1124 printf("\n");
1125 } else {
1126 on = ansi_highlight_red();
1127 off = ansi_highlight_off();
1128 }
1129
1130 if (!arg_no_legend) {
1131 printf("%s%u timers listed.%s\n", on, n, off);
1132 if (!arg_all)
1133 printf("Pass --all to see loaded but inactive timers, too.\n");
1134 }
1135
1136 return 0;
1137}
1138
f5080e73
DH
1139static usec_t calc_next_elapse(dual_timestamp *nw, dual_timestamp *next) {
1140 usec_t next_elapse;
1141
1142 assert(nw);
1143 assert(next);
1144
3a43da28 1145 if (next->monotonic != USEC_INFINITY && next->monotonic > 0) {
f5080e73
DH
1146 usec_t converted;
1147
1148 if (next->monotonic > nw->monotonic)
1149 converted = nw->realtime + (next->monotonic - nw->monotonic);
1150 else
1151 converted = nw->realtime - (nw->monotonic - next->monotonic);
1152
3a43da28 1153 if (next->realtime != USEC_INFINITY && next->realtime > 0)
f5080e73
DH
1154 next_elapse = MIN(converted, next->realtime);
1155 else
1156 next_elapse = converted;
1157
1158 } else
1159 next_elapse = next->realtime;
1160
1161 return next_elapse;
1162}
1163
cbb76c29 1164static int list_timers(sd_bus *bus, char **args) {
806a37e7
TA
1165 _cleanup_(message_set_freep) Set *replies = NULL;
1166 _cleanup_strv_free_ char **machines = NULL;
cbb76c29
LP
1167 _cleanup_free_ struct timer_info *timer_infos = NULL;
1168 _cleanup_free_ UnitInfo *unit_infos = NULL;
1169 struct timer_info *t;
1170 const UnitInfo *u;
1171 size_t size = 0;
1823b86e 1172 int n, c = 0;
cbb76c29 1173 dual_timestamp nw;
1823b86e 1174 int r = 0;
cbb76c29
LP
1175
1176 pager_open_if_enabled();
1177
806a37e7 1178 n = get_unit_list_recursive(bus, strv_skip_first(args), &unit_infos, &replies, &machines);
cbb76c29
LP
1179 if (n < 0)
1180 return n;
1181
1182 dual_timestamp_get(&nw);
1183
1184 for (u = unit_infos; u < unit_infos + n; u++) {
1185 _cleanup_strv_free_ char **triggered = NULL;
9e82ffa0 1186 dual_timestamp next = {};
d784e2db 1187 usec_t m, last = 0;
cbb76c29 1188
cbb76c29
LP
1189 if (!endswith(u->id, ".timer"))
1190 continue;
1191
1192 r = get_triggered_units(bus, u->unit_path, &triggered);
1193 if (r < 0)
1194 goto cleanup;
1195
1196 r = get_next_elapse(bus, u->unit_path, &next);
1197 if (r < 0)
1198 goto cleanup;
1199
d784e2db
LP
1200 get_last_trigger(bus, u->unit_path, &last);
1201
cbb76c29
LP
1202 if (!GREEDY_REALLOC(timer_infos, size, c+1)) {
1203 r = log_oom();
1204 goto cleanup;
1205 }
1206
f5080e73
DH
1207 m = calc_next_elapse(&nw, &next);
1208
cbb76c29 1209 timer_infos[c++] = (struct timer_info) {
806a37e7 1210 .machine = u->machine,
cbb76c29
LP
1211 .id = u->id,
1212 .next_elapse = m,
d784e2db 1213 .last_trigger = last,
cbb76c29
LP
1214 .triggered = triggered,
1215 };
1216
1217 triggered = NULL; /* avoid cleanup */
1218 }
1219
1220 qsort_safe(timer_infos, c, sizeof(struct timer_info),
1221 (__compar_fn_t) timer_info_compare);
1222
1223 output_timers_list(timer_infos, c);
1224
1225 cleanup:
1226 for (t = timer_infos; t < timer_infos + c; t++)
1227 strv_free(t->triggered);
1228
1229 return r;
1230}
1231
729e3769
LP
1232static int compare_unit_file_list(const void *a, const void *b) {
1233 const char *d1, *d2;
1234 const UnitFileList *u = a, *v = b;
1235
1236 d1 = strrchr(u->path, '.');
1237 d2 = strrchr(v->path, '.');
1238
1239 if (d1 && d2) {
1240 int r;
1241
1242 r = strcasecmp(d1, d2);
1243 if (r != 0)
1244 return r;
1245 }
1246
2b6bf07d 1247 return strcasecmp(basename(u->path), basename(v->path));
729e3769
LP
1248}
1249
d8fba7c6 1250static bool output_show_unit_file(const UnitFileList *u, char **patterns) {
d8fba7c6
ZJS
1251 if (!strv_isempty(patterns)) {
1252 char **pattern;
1253
1254 STRV_FOREACH(pattern, patterns)
1255 if (fnmatch(*pattern, basename(u->path), FNM_NOESCAPE) == 0)
6c71341a 1256 goto next;
d8fba7c6
ZJS
1257 return false;
1258 }
1259
6c71341a
ZJS
1260next:
1261 if (!strv_isempty(arg_types)) {
1262 const char *dot;
1263
1264 dot = strrchr(u->path, '.');
1265 if (!dot)
1266 return false;
1267
1268 if (!strv_find(arg_types, dot+1))
1269 return false;
1270 }
1271
fec1530e
ZJS
1272 if (!strv_isempty(arg_states)) {
1273 if (!strv_find(arg_states, unit_file_state_to_string(u->state)))
1274 return false;
1275 }
1276
6c71341a 1277 return true;
729e3769
LP
1278}
1279
8d5ba5a9 1280static void output_unit_file_list(const UnitFileList *units, unsigned c) {
0d292f5e 1281 unsigned max_id_len, id_cols, state_cols;
729e3769
LP
1282 const UnitFileList *u;
1283
f8294e41
JT
1284 max_id_len = strlen("UNIT FILE");
1285 state_cols = strlen("STATE");
cbc9fbd1 1286
1c0a113f 1287 for (u = units; u < units + c; u++) {
2b6bf07d 1288 max_id_len = MAX(max_id_len, strlen(basename(u->path)));
1c0a113f
ZJS
1289 state_cols = MAX(state_cols, strlen(unit_file_state_to_string(u->state)));
1290 }
1291
1292 if (!arg_full) {
1293 unsigned basic_cols;
cbc9fbd1 1294
9607d947 1295 id_cols = MIN(max_id_len, 25u);
1c0a113f
ZJS
1296 basic_cols = 1 + id_cols + state_cols;
1297 if (basic_cols < (unsigned) columns())
1298 id_cols += MIN(columns() - basic_cols, max_id_len - id_cols);
1299 } else
1300 id_cols = max_id_len;
1301
1302 if (!arg_no_legend)
cbc9fbd1
LP
1303 printf("%-*s %-*s\n",
1304 id_cols, "UNIT FILE",
1305 state_cols, "STATE");
729e3769
LP
1306
1307 for (u = units; u < units + c; u++) {
7fd1b19b 1308 _cleanup_free_ char *e = NULL;
729e3769
LP
1309 const char *on, *off;
1310 const char *id;
1311
729e3769
LP
1312 if (u->state == UNIT_FILE_MASKED ||
1313 u->state == UNIT_FILE_MASKED_RUNTIME ||
b5b46d59
LP
1314 u->state == UNIT_FILE_DISABLED ||
1315 u->state == UNIT_FILE_INVALID) {
0b5a519c
DS
1316 on = ansi_highlight_red();
1317 off = ansi_highlight_off();
729e3769 1318 } else if (u->state == UNIT_FILE_ENABLED) {
0b5a519c
DS
1319 on = ansi_highlight_green();
1320 off = ansi_highlight_off();
729e3769
LP
1321 } else
1322 on = off = "";
1323
2b6bf07d 1324 id = basename(u->path);
729e3769 1325
1c0a113f 1326 e = arg_full ? NULL : ellipsize(id, id_cols, 33);
729e3769 1327
1c0a113f
ZJS
1328 printf("%-*s %s%-*s%s\n",
1329 id_cols, e ? e : id,
1330 on, state_cols, unit_file_state_to_string(u->state), off);
729e3769
LP
1331 }
1332
1c0a113f 1333 if (!arg_no_legend)
0d292f5e 1334 printf("\n%u unit files listed.\n", c);
729e3769
LP
1335}
1336
f459b602
MAP
1337static int list_unit_files(sd_bus *bus, char **args) {
1338 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1339 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
f84190d8 1340 _cleanup_free_ UnitFileList *units = NULL;
8d5ba5a9
ZJS
1341 UnitFileList *unit;
1342 size_t size = 0;
f459b602
MAP
1343 unsigned c = 0;
1344 const char *state;
1345 char *path;
f84190d8 1346 int r;
729e3769 1347
729e3769
LP
1348 pager_open_if_enabled();
1349
1350 if (avoid_bus()) {
1351 Hashmap *h;
1352 UnitFileList *u;
1353 Iterator i;
f459b602 1354 unsigned n_units;
729e3769 1355
d5099efc 1356 h = hashmap_new(&string_hash_ops);
0d0f0c50
SL
1357 if (!h)
1358 return log_oom();
729e3769
LP
1359
1360 r = unit_file_get_list(arg_scope, arg_root, h);
1361 if (r < 0) {
8ea913b2 1362 unit_file_list_free(h);
729e3769
LP
1363 log_error("Failed to get unit file list: %s", strerror(-r));
1364 return r;
1365 }
1366
1367 n_units = hashmap_size(h);
fdbdf6ec 1368
729e3769 1369 units = new(UnitFileList, n_units);
4fe1be9c 1370 if (!units && n_units > 0) {
729e3769 1371 unit_file_list_free(h);
0d0f0c50 1372 return log_oom();
729e3769
LP
1373 }
1374
1375 HASHMAP_FOREACH(u, h, i) {
8d5ba5a9
ZJS
1376 if (!output_show_unit_file(u, strv_skip_first(args)))
1377 continue;
1378
1379 units[c++] = *u;
729e3769
LP
1380 free(u);
1381 }
1382
8d5ba5a9 1383 assert(c <= n_units);
729e3769
LP
1384 hashmap_free(h);
1385 } else {
f459b602 1386 r = sd_bus_call_method(
f22f08cd 1387 bus,
729e3769
LP
1388 "org.freedesktop.systemd1",
1389 "/org/freedesktop/systemd1",
1390 "org.freedesktop.systemd1.Manager",
f22f08cd 1391 "ListUnitFiles",
f459b602 1392 &error,
f22f08cd 1393 &reply,
f459b602
MAP
1394 NULL);
1395 if (r < 0) {
1396 log_error("Failed to list unit files: %s", bus_error_message(&error, r));
f84190d8 1397 return r;
729e3769
LP
1398 }
1399
f459b602
MAP
1400 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ss)");
1401 if (r < 0)
1402 return bus_log_parse_error(r);
729e3769 1403
f459b602 1404 while ((r = sd_bus_message_read(reply, "(ss)", &path, &state)) > 0) {
729e3769 1405
f459b602
MAP
1406 if (!GREEDY_REALLOC(units, size, c + 1))
1407 return log_oom();
729e3769 1408
8d5ba5a9 1409 units[c] = (struct UnitFileList) {
f459b602
MAP
1410 path,
1411 unit_file_state_from_string(state)
1412 };
8d5ba5a9
ZJS
1413
1414 if (output_show_unit_file(&units[c], strv_skip_first(args)))
1415 c ++;
1416
729e3769 1417 }
f459b602
MAP
1418 if (r < 0)
1419 return bus_log_parse_error(r);
1420
1421 r = sd_bus_message_exit_container(reply);
1422 if (r < 0)
1423 return bus_log_parse_error(r);
729e3769
LP
1424 }
1425
4fe1be9c
LP
1426 qsort_safe(units, c, sizeof(UnitFileList), compare_unit_file_list);
1427 output_unit_file_list(units, c);
729e3769 1428
4fe1be9c 1429 if (avoid_bus()) {
8d5ba5a9
ZJS
1430 for (unit = units; unit < units + c; unit++)
1431 free(unit->path);
4fe1be9c 1432 }
8d5ba5a9 1433
f84190d8 1434 return 0;
729e3769
LP
1435}
1436
55c0b89c 1437static int list_dependencies_print(const char *name, int level, unsigned int branches, bool last) {
55c0b89c 1438 _cleanup_free_ char *n = NULL;
9607d947 1439 size_t max_len = MAX(columns(),20u);
cbc9fbd1
LP
1440 size_t len = 0;
1441 int i;
55c0b89c 1442
5d0c05e5 1443 if (!arg_plain) {
cbc9fbd1 1444
5d0c05e5
LN
1445 for (i = level - 1; i >= 0; i--) {
1446 len += 2;
f168c273 1447 if (len > max_len - 3 && !arg_full) {
5d0c05e5
LN
1448 printf("%s...\n",max_len % 2 ? "" : " ");
1449 return 0;
1450 }
6b01f1d3 1451 printf("%s", draw_special_char(branches & (1 << i) ? DRAW_TREE_VERTICAL : DRAW_TREE_SPACE));
5d0c05e5 1452 }
55c0b89c 1453 len += 2;
cbc9fbd1 1454
f168c273 1455 if (len > max_len - 3 && !arg_full) {
55c0b89c
LN
1456 printf("%s...\n",max_len % 2 ? "" : " ");
1457 return 0;
1458 }
cbc9fbd1 1459
5d0c05e5 1460 printf("%s", draw_special_char(last ? DRAW_TREE_RIGHT : DRAW_TREE_BRANCH));
55c0b89c 1461 }
55c0b89c 1462
cbc9fbd1 1463 if (arg_full){
55c0b89c
LN
1464 printf("%s\n", name);
1465 return 0;
1466 }
1467
1468 n = ellipsize(name, max_len-len, 100);
f168c273 1469 if (!n)
55c0b89c
LN
1470 return log_oom();
1471
1472 printf("%s\n", n);
1473 return 0;
1474}
1475
f459b602
MAP
1476static int list_dependencies_get_dependencies(sd_bus *bus, const char *name, char ***deps) {
1477
071066a5 1478 static const char *dependencies[_DEPENDENCY_MAX] = {
afba4199
ZJS
1479 [DEPENDENCY_FORWARD] = "Requires\0"
1480 "RequiresOverridable\0"
1481 "Requisite\0"
1482 "RequisiteOverridable\0"
1483 "Wants\0",
1484 [DEPENDENCY_REVERSE] = "RequiredBy\0"
1485 "RequiredByOverridable\0"
1486 "WantedBy\0"
1487 "PartOf\0",
1488 [DEPENDENCY_AFTER] = "After\0",
1489 [DEPENDENCY_BEFORE] = "Before\0",
1490 };
55c0b89c 1491
f459b602
MAP
1492 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1493 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1494 _cleanup_strv_free_ char **ret = NULL;
1495 _cleanup_free_ char *path = NULL;
1496 int r;
55c0b89c
LN
1497
1498 assert(bus);
1499 assert(name);
1500 assert(deps);
071066a5 1501 assert_cc(ELEMENTSOF(dependencies) == _DEPENDENCY_MAX);
55c0b89c
LN
1502
1503 path = unit_dbus_path_from_name(name);
f459b602
MAP
1504 if (!path)
1505 return log_oom();
55c0b89c 1506
f459b602
MAP
1507 r = sd_bus_call_method(
1508 bus,
1509 "org.freedesktop.systemd1",
1510 path,
1511 "org.freedesktop.DBus.Properties",
1512 "GetAll",
1513 &error,
1514 &reply,
1515 "s", "org.freedesktop.systemd1.Unit");
1516 if (r < 0) {
1517 log_error("Failed to get properties of %s: %s", name, bus_error_message(&error, r));
1518 return r;
55c0b89c
LN
1519 }
1520
f459b602
MAP
1521 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
1522 if (r < 0)
1523 return bus_log_parse_error(r);
55c0b89c 1524
f459b602 1525 while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
55c0b89c
LN
1526 const char *prop;
1527
f459b602
MAP
1528 r = sd_bus_message_read(reply, "s", &prop);
1529 if (r < 0)
1530 return bus_log_parse_error(r);
55c0b89c 1531
f459b602
MAP
1532 if (!nulstr_contains(dependencies[arg_dependency], prop)) {
1533 r = sd_bus_message_skip(reply, "v");
1534 if (r < 0)
1535 return bus_log_parse_error(r);
1536 } else {
55c0b89c 1537
f459b602
MAP
1538 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, "as");
1539 if (r < 0)
1540 return bus_log_parse_error(r);
55c0b89c 1541
f459b602
MAP
1542 r = bus_message_read_strv_extend(reply, &ret);
1543 if (r < 0)
1544 return bus_log_parse_error(r);
55c0b89c 1545
f459b602
MAP
1546 r = sd_bus_message_exit_container(reply);
1547 if (r < 0)
1548 return bus_log_parse_error(r);
1549 }
55c0b89c 1550
f459b602
MAP
1551 r = sd_bus_message_exit_container(reply);
1552 if (r < 0)
1553 return bus_log_parse_error(r);
55c0b89c 1554
f459b602
MAP
1555 }
1556 if (r < 0)
1557 return bus_log_parse_error(r);
55c0b89c 1558
f459b602
MAP
1559 r = sd_bus_message_exit_container(reply);
1560 if (r < 0)
1561 return bus_log_parse_error(r);
540e7dbe 1562
f459b602
MAP
1563 *deps = ret;
1564 ret = NULL;
540e7dbe 1565
f459b602 1566 return 0;
55c0b89c
LN
1567}
1568
1569static int list_dependencies_compare(const void *_a, const void *_b) {
1570 const char **a = (const char**) _a, **b = (const char**) _b;
cbc9fbd1 1571
55c0b89c
LN
1572 if (unit_name_to_type(*a) == UNIT_TARGET && unit_name_to_type(*b) != UNIT_TARGET)
1573 return 1;
1574 if (unit_name_to_type(*a) != UNIT_TARGET && unit_name_to_type(*b) == UNIT_TARGET)
1575 return -1;
cbc9fbd1 1576
55c0b89c
LN
1577 return strcasecmp(*a, *b);
1578}
1579
f459b602
MAP
1580static int list_dependencies_one(
1581 sd_bus *bus,
1582 const char *name,
1583 int level,
1584 char ***units,
1585 unsigned int branches) {
1586
e3e45d4f 1587 _cleanup_strv_free_ char **deps = NULL;
55c0b89c 1588 char **c;
55c0b89c
LN
1589 int r = 0;
1590
cbc9fbd1
LP
1591 assert(bus);
1592 assert(name);
1593 assert(units);
1594
e3e45d4f
SP
1595 r = strv_extend(units, name);
1596 if (r < 0)
55c0b89c
LN
1597 return log_oom();
1598
1599 r = list_dependencies_get_dependencies(bus, name, &deps);
1600 if (r < 0)
cec7eda5 1601 return r;
55c0b89c 1602
7ff7394d 1603 qsort_safe(deps, strv_length(deps), sizeof (char*), list_dependencies_compare);
55c0b89c
LN
1604
1605 STRV_FOREACH(c, deps) {
dbc2c080
LP
1606 int state;
1607
e3e45d4f 1608 if (strv_contains(*units, *c)) {
5d0c05e5
LN
1609 if (!arg_plain) {
1610 r = list_dependencies_print("...", level + 1, (branches << 1) | (c[1] == NULL ? 0 : 1), 1);
1611 if (r < 0)
1612 return r;
1613 }
55c0b89c
LN
1614 continue;
1615 }
1616
dbc2c080
LP
1617 state = check_one_unit(bus, *c, "activating\0active\0reloading\0", true);
1618 if (state > 0)
6b01f1d3 1619 printf("%s%s%s ", ansi_highlight_green(), draw_special_char(DRAW_BLACK_CIRCLE), ansi_highlight_off());
dbc2c080 1620 else
6b01f1d3 1621 printf("%s%s%s ", ansi_highlight_red(), draw_special_char(DRAW_BLACK_CIRCLE), ansi_highlight_off());
dbc2c080 1622
55c0b89c 1623 r = list_dependencies_print(*c, level, branches, c[1] == NULL);
cec7eda5
ZJS
1624 if (r < 0)
1625 return r;
55c0b89c
LN
1626
1627 if (arg_all || unit_name_to_type(*c) == UNIT_TARGET) {
e3e45d4f 1628 r = list_dependencies_one(bus, *c, level + 1, units, (branches << 1) | (c[1] == NULL ? 0 : 1));
f168c273 1629 if (r < 0)
cec7eda5 1630 return r;
55c0b89c
LN
1631 }
1632 }
f459b602 1633
e3e45d4f
SP
1634 if (!arg_plain)
1635 strv_remove(*units, name);
f459b602 1636
cec7eda5 1637 return 0;
55c0b89c
LN
1638}
1639
f459b602 1640static int list_dependencies(sd_bus *bus, char **args) {
5d0c05e5 1641 _cleanup_strv_free_ char **units = NULL;
f459b602 1642 _cleanup_free_ char *unit = NULL;
e31165b2 1643 const char *u;
55c0b89c
LN
1644
1645 assert(bus);
55c0b89c 1646
e31165b2 1647 if (args[1]) {
f78e6385 1648 unit = unit_name_mangle(args[1], MANGLE_NOGLOB);
e31165b2
LP
1649 if (!unit)
1650 return log_oom();
1651 u = unit;
1652 } else
1653 u = SPECIAL_DEFAULT_TARGET;
55c0b89c
LN
1654
1655 pager_open_if_enabled();
e31165b2
LP
1656
1657 puts(u);
1658
5d0c05e5 1659 return list_dependencies_one(bus, u, 0, &units, 0);
55c0b89c
LN
1660}
1661
0d292f5e
LP
1662struct machine_info {
1663 bool is_host;
1664 char *name;
0d292f5e 1665 char *state;
8fcf784d
LP
1666 char *control_group;
1667 uint32_t n_failed_units;
1668 uint32_t n_jobs;
1669 usec_t timestamp;
1670};
1671
1672static const struct bus_properties_map machine_info_property_map[] = {
1673 { "SystemState", "s", NULL, offsetof(struct machine_info, state) },
1674 { "NJobs", "u", NULL, offsetof(struct machine_info, n_jobs) },
1675 { "NFailedUnits", "u", NULL, offsetof(struct machine_info, n_failed_units) },
1676 { "ControlGroup", "s", NULL, offsetof(struct machine_info, control_group) },
1677 { "UserspaceTimestamp", "t", NULL, offsetof(struct machine_info, timestamp) },
1678 {}
0d292f5e
LP
1679};
1680
1681static void free_machines_list(struct machine_info *machine_infos, int n) {
1682 int i;
1683
1684 if (!machine_infos)
1685 return;
1686
1687 for (i = 0; i < n; i++) {
1688 free(machine_infos[i].name);
1689 free(machine_infos[i].state);
8fcf784d 1690 free(machine_infos[i].control_group);
0d292f5e
LP
1691 }
1692
1693 free(machine_infos);
1694}
1695
1696static int compare_machine_info(const void *a, const void *b) {
1697 const struct machine_info *u = a, *v = b;
1698
1699 if (u->is_host != v->is_host)
50933da0 1700 return u->is_host > v->is_host ? -1 : 1;
0d292f5e
LP
1701
1702 return strcasecmp(u->name, v->name);
1703}
1704
1705static int get_machine_properties(sd_bus *bus, struct machine_info *mi) {
24996861 1706 _cleanup_bus_close_unref_ sd_bus *container = NULL;
0d292f5e
LP
1707 int r;
1708
1709 assert(mi);
1710
1711 if (!bus) {
1712 r = sd_bus_open_system_container(&container, mi->name);
1713 if (r < 0)
1714 return r;
1715
1716 bus = container;
1717 }
1718
8fcf784d 1719 r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, mi);
0d292f5e
LP
1720 if (r < 0)
1721 return r;
1722
1723 return 0;
1724}
1725
1726static bool output_show_machine(const char *name, char **patterns) {
1727 char **i;
1728
1729 assert(name);
1730
1731 if (strv_isempty(patterns))
1732 return true;
1733
1734 STRV_FOREACH(i, patterns)
1735 if (fnmatch(*i, name, FNM_NOESCAPE) == 0)
1736 return true;
1737
1738 return false;
1739}
1740
1741static int get_machine_list(
1742 sd_bus *bus,
1743 struct machine_info **_machine_infos,
1744 char **patterns) {
1745
1746 struct machine_info *machine_infos = NULL;
1747 _cleanup_strv_free_ char **m = NULL;
1748 _cleanup_free_ char *hn = NULL;
1749 size_t sz = 0;
1750 char **i;
1751 int c = 0;
1752
1753 hn = gethostname_malloc();
1754 if (!hn)
1755 return log_oom();
1756
1757 if (output_show_machine(hn, patterns)) {
1758 if (!GREEDY_REALLOC0(machine_infos, sz, c+1))
1759 return log_oom();
1760
1761 machine_infos[c].is_host = true;
1762 machine_infos[c].name = hn;
1763 hn = NULL;
1764
1765 get_machine_properties(bus, &machine_infos[c]);
1766 c++;
1767 }
1768
1769 sd_get_machine_names(&m);
1770 STRV_FOREACH(i, m) {
1771 _cleanup_free_ char *class = NULL;
1772
1773 if (!output_show_machine(*i, patterns))
1774 continue;
1775
1776 sd_machine_get_class(*i, &class);
1777 if (!streq_ptr(class, "container"))
1778 continue;
1779
1780 if (!GREEDY_REALLOC0(machine_infos, sz, c+1)) {
1781 free_machines_list(machine_infos, c);
1782 return log_oom();
1783 }
1784
1785 machine_infos[c].is_host = false;
1786 machine_infos[c].name = strdup(*i);
1787 if (!machine_infos[c].name) {
1788 free_machines_list(machine_infos, c);
1789 return log_oom();
1790 }
1791
1792 get_machine_properties(NULL, &machine_infos[c]);
1793 c++;
1794 }
1795
1796 *_machine_infos = machine_infos;
1797 return c;
1798}
1799
1800static void output_machines_list(struct machine_info *machine_infos, unsigned n) {
1801 struct machine_info *m;
1802 unsigned
90c3f79d 1803 circle_len = 0,
0d292f5e
LP
1804 namelen = sizeof("NAME") - 1,
1805 statelen = sizeof("STATE") - 1,
1806 failedlen = sizeof("FAILED") - 1,
1807 jobslen = sizeof("JOBS") - 1;
1808
1809 assert(machine_infos || n == 0);
1810
1811 for (m = machine_infos; m < machine_infos + n; m++) {
1812 namelen = MAX(namelen, strlen(m->name) + (m->is_host ? sizeof(" (host)") - 1 : 0));
1813 statelen = MAX(statelen, m->state ? strlen(m->state) : 0);
1814 failedlen = MAX(failedlen, DECIMAL_STR_WIDTH(m->n_failed_units));
1815 jobslen = MAX(jobslen, DECIMAL_STR_WIDTH(m->n_jobs));
90c3f79d
LP
1816
1817 if (!arg_no_legend && !streq_ptr(m->state, "running"))
1818 circle_len = 2;
0d292f5e
LP
1819 }
1820
90c3f79d
LP
1821 if (!arg_no_legend) {
1822 if (circle_len > 0)
1823 fputs(" ", stdout);
1824
0d292f5e
LP
1825 printf("%-*s %-*s %-*s %-*s\n",
1826 namelen, "NAME",
1827 statelen, "STATE",
1828 failedlen, "FAILED",
1829 jobslen, "JOBS");
90c3f79d 1830 }
0d292f5e
LP
1831
1832 for (m = machine_infos; m < machine_infos + n; m++) {
90c3f79d
LP
1833 const char *on_state = "", *off_state = "";
1834 const char *on_failed = "", *off_failed = "";
1835 bool circle = false;
0d292f5e
LP
1836
1837 if (streq_ptr(m->state, "degraded")) {
1838 on_state = ansi_highlight_red();
1839 off_state = ansi_highlight_off();
90c3f79d 1840 circle = true;
0d292f5e
LP
1841 } else if (!streq_ptr(m->state, "running")) {
1842 on_state = ansi_highlight_yellow();
1843 off_state = ansi_highlight_off();
90c3f79d
LP
1844 circle = true;
1845 }
0d292f5e
LP
1846
1847 if (m->n_failed_units > 0) {
1848 on_failed = ansi_highlight_red();
1849 off_failed = ansi_highlight_off();
1850 } else
1851 on_failed = off_failed = "";
1852
90c3f79d 1853 if (circle_len > 0)
6b01f1d3 1854 printf("%s%s%s ", on_state, circle ? draw_special_char(DRAW_BLACK_CIRCLE) : " ", off_state);
90c3f79d 1855
0d292f5e
LP
1856 if (m->is_host)
1857 printf("%-*s (host) %s%-*s%s %s%*u%s %*u\n",
1858 (int) (namelen - (sizeof(" (host)")-1)), strna(m->name),
1859 on_state, statelen, strna(m->state), off_state,
1860 on_failed, failedlen, m->n_failed_units, off_failed,
1861 jobslen, m->n_jobs);
1862 else
1863 printf("%-*s %s%-*s%s %s%*u%s %*u\n",
1864 namelen, strna(m->name),
1865 on_state, statelen, strna(m->state), off_state,
1866 on_failed, failedlen, m->n_failed_units, off_failed,
1867 jobslen, m->n_jobs);
1868 }
1869
1870 if (!arg_no_legend)
1871 printf("\n%u machines listed.\n", n);
1872}
1873
1874static int list_machines(sd_bus *bus, char **args) {
1875 struct machine_info *machine_infos = NULL;
1876 int r;
1877
1878 assert(bus);
1879
1880 if (geteuid() != 0) {
1881 log_error("Must be root.");
1882 return -EPERM;
1883 }
1884
1885 pager_open_if_enabled();
1886
1887 r = get_machine_list(bus, &machine_infos, strv_skip_first(args));
1888 if (r < 0)
1889 return r;
1890
1891 qsort_safe(machine_infos, r, sizeof(struct machine_info), compare_machine_info);
1892 output_machines_list(machine_infos, r);
1893 free_machines_list(machine_infos, r);
1894
1895 return 0;
1896}
1897
f459b602
MAP
1898static int get_default(sd_bus *bus, char **args) {
1899 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
1900 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1901 _cleanup_free_ char *_path = NULL;
1902 const char *path;
99504dd4 1903 int r;
99504dd4
VP
1904
1905 if (!bus || avoid_bus()) {
f459b602 1906 r = unit_file_get_default(arg_scope, arg_root, &_path);
99504dd4 1907 if (r < 0) {
f459b602
MAP
1908 log_error("Failed to get default target: %s", strerror(-r));
1909 return r;
99504dd4 1910 }
f459b602 1911 path = _path;
99504dd4 1912
99504dd4 1913 } else {
f459b602
MAP
1914 r = sd_bus_call_method(
1915 bus,
1916 "org.freedesktop.systemd1",
1917 "/org/freedesktop/systemd1",
1918 "org.freedesktop.systemd1.Manager",
1919 "GetDefaultTarget",
1920 &error,
1921 &reply,
1922 NULL);
99504dd4 1923 if (r < 0) {
f459b602
MAP
1924 log_error("Failed to get default target: %s", bus_error_message(&error, -r));
1925 return r;
99504dd4
VP
1926 }
1927
f459b602
MAP
1928 r = sd_bus_message_read(reply, "s", &path);
1929 if (r < 0)
1930 return bus_log_parse_error(r);
99504dd4
VP
1931 }
1932
1933 if (path)
1934 printf("%s\n", path);
1935
f459b602 1936 return 0;
99504dd4
VP
1937}
1938
718db961
LP
1939static void dump_unit_file_changes(const UnitFileChange *changes, unsigned n_changes) {
1940 unsigned i;
1941
1942 assert(changes || n_changes == 0);
1943
1944 for (i = 0; i < n_changes; i++) {
1945 if (changes[i].type == UNIT_FILE_SYMLINK)
735a1a2e 1946 log_info("Created symlink from %s to %s.", changes[i].path, changes[i].source);
718db961 1947 else
749ebb2d 1948 log_info("Removed symlink %s.", changes[i].path);
718db961
LP
1949 }
1950}
1951
1952static int deserialize_and_dump_unit_file_changes(sd_bus_message *m) {
1953 const char *type, *path, *source;
1954 int r;
1955
1956 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sss)");
1957 if (r < 0)
1958 return bus_log_parse_error(r);
1959
1960 while ((r = sd_bus_message_read(m, "(sss)", &type, &path, &source)) > 0) {
1961 if (!arg_quiet) {
1962 if (streq(type, "symlink"))
735a1a2e 1963 log_info("Created symlink from %s to %s.", path, source);
718db961 1964 else
749ebb2d 1965 log_info("Removed symlink %s.", path);
718db961
LP
1966 }
1967 }
1968 if (r < 0)
1969 return bus_log_parse_error(r);
1970
1971 r = sd_bus_message_exit_container(m);
1972 if (r < 0)
1973 return bus_log_parse_error(r);
1974
1975 return 0;
1976}
1977
1978static int set_default(sd_bus *bus, char **args) {
1979 _cleanup_free_ char *unit = NULL;
1980 UnitFileChange *changes = NULL;
1981 unsigned n_changes = 0;
1982 int r;
1983
f78e6385 1984 unit = unit_name_mangle_with_suffix(args[1], MANGLE_NOGLOB, ".target");
718db961
LP
1985 if (!unit)
1986 return log_oom();
1987
1988 if (!bus || avoid_bus()) {
a1484a21 1989 r = unit_file_set_default(arg_scope, arg_root, unit, true, &changes, &n_changes);
718db961
LP
1990 if (r < 0) {
1991 log_error("Failed to set default target: %s", strerror(-r));
1992 return r;
1993 }
1994
1995 if (!arg_quiet)
1996 dump_unit_file_changes(changes, n_changes);
1997
1998 r = 0;
1999 } else {
342641fb 2000 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
718db961
LP
2001 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2002
342641fb 2003 r = sd_bus_message_new_method_call(
718db961 2004 bus,
342641fb 2005 &m,
718db961
LP
2006 "org.freedesktop.systemd1",
2007 "/org/freedesktop/systemd1",
2008 "org.freedesktop.systemd1.Manager",
342641fb
LP
2009 "SetDefaultTarget");
2010 if (r < 0)
2011 return bus_log_create_error(r);
2012
2013 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
2014 if (r < 0)
2015 return bus_log_create_error(r);
2016
2017 r = sd_bus_message_append(m, "sb", unit, 1);
2018 if (r < 0)
2019 return bus_log_create_error(r);
2020
2021 r = sd_bus_call(bus, m, 0, &error, &reply);
718db961
LP
2022 if (r < 0) {
2023 log_error("Failed to set default target: %s", bus_error_message(&error, -r));
2024 return r;
2025 }
2026
2027 r = deserialize_and_dump_unit_file_changes(reply);
2028 if (r < 0)
2029 return r;
2030
93c941e3 2031 /* Try to reload if enabled */
718db961
LP
2032 if (!arg_no_reload)
2033 r = daemon_reload(bus, args);
2034 else
2035 r = 0;
2036 }
2037
2038 unit_file_changes_free(changes, n_changes);
2039
2040 return r;
2041}
2042
75add28a
ZJS
2043struct job_info {
2044 uint32_t id;
f459b602 2045 const char *name, *type, *state;
75add28a
ZJS
2046};
2047
d8fba7c6 2048static void output_jobs_list(const struct job_info* jobs, unsigned n, bool skipped) {
f459b602
MAP
2049 unsigned id_len, unit_len, type_len, state_len;
2050 const struct job_info *j;
75add28a
ZJS
2051 const char *on, *off;
2052 bool shorten = false;
2053
2054 assert(n == 0 || jobs);
2055
2056 if (n == 0) {
0b5a519c
DS
2057 on = ansi_highlight_green();
2058 off = ansi_highlight_off();
75add28a 2059
d8fba7c6 2060 printf("%sNo jobs %s.%s\n", on, skipped ? "listed" : "running", off);
75add28a
ZJS
2061 return;
2062 }
2063
2064 pager_open_if_enabled();
2065
f8294e41
JT
2066 id_len = strlen("JOB");
2067 unit_len = strlen("UNIT");
2068 type_len = strlen("TYPE");
2069 state_len = strlen("STATE");
6d6d40c9 2070
f459b602
MAP
2071 for (j = jobs; j < jobs + n; j++) {
2072 uint32_t id = j->id;
2073 assert(j->name && j->type && j->state);
75add28a 2074
f459b602
MAP
2075 id_len = MAX(id_len, DECIMAL_STR_WIDTH(id));
2076 unit_len = MAX(unit_len, strlen(j->name));
2077 type_len = MAX(type_len, strlen(j->type));
2078 state_len = MAX(state_len, strlen(j->state));
2079 }
75add28a 2080
f459b602
MAP
2081 if (!arg_full && id_len + 1 + unit_len + type_len + 1 + state_len > columns()) {
2082 unit_len = MAX(33u, columns() - id_len - type_len - state_len - 3);
2083 shorten = true;
2084 }
75add28a 2085
6ce774fd
MM
2086 if (!arg_no_legend)
2087 printf("%*s %-*s %-*s %-*s\n",
2088 id_len, "JOB",
2089 unit_len, "UNIT",
2090 type_len, "TYPE",
2091 state_len, "STATE");
75add28a 2092
f459b602
MAP
2093 for (j = jobs; j < jobs + n; j++) {
2094 _cleanup_free_ char *e = NULL;
75add28a 2095
f459b602
MAP
2096 if (streq(j->state, "running")) {
2097 on = ansi_highlight();
2098 off = ansi_highlight_off();
2099 } else
2100 on = off = "";
2101
2102 e = shorten ? ellipsize(j->name, unit_len, 33) : NULL;
2103 printf("%*u %s%-*s%s %-*s %s%-*s%s\n",
2104 id_len, j->id,
2105 on, unit_len, e ? e : j->name, off,
2106 type_len, j->type,
2107 on, state_len, j->state, off);
75add28a
ZJS
2108 }
2109
6ce774fd
MM
2110 if (!arg_no_legend) {
2111 on = ansi_highlight();
2112 off = ansi_highlight_off();
75add28a 2113
6ce774fd
MM
2114 printf("\n%s%u jobs listed%s.\n", on, n, off);
2115 }
75add28a
ZJS
2116}
2117
d8fba7c6 2118static bool output_show_job(struct job_info *job, char **patterns) {
0d292f5e 2119 char **pattern;
d8fba7c6 2120
0d292f5e 2121 assert(job);
d8fba7c6 2122
0d292f5e
LP
2123 if (strv_isempty(patterns))
2124 return true;
2125
2126 STRV_FOREACH(pattern, patterns)
2127 if (fnmatch(*pattern, job->name, FNM_NOESCAPE) == 0)
2128 return true;
2129 return false;
d8fba7c6
ZJS
2130}
2131
f459b602
MAP
2132static int list_jobs(sd_bus *bus, char **args) {
2133 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2134 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
2135 const char *name, *type, *state, *job_path, *unit_path;
2136 _cleanup_free_ struct job_info *jobs = NULL;
2137 size_t size = 0;
2138 unsigned c = 0;
2139 uint32_t id;
f84190d8 2140 int r;
d8fba7c6 2141 bool skipped = false;
ec14911e 2142
f459b602 2143 r = sd_bus_call_method(
f22f08cd
SP
2144 bus,
2145 "org.freedesktop.systemd1",
2146 "/org/freedesktop/systemd1",
2147 "org.freedesktop.systemd1.Manager",
2148 "ListJobs",
f459b602 2149 &error,
f22f08cd 2150 &reply,
f459b602
MAP
2151 NULL);
2152 if (r < 0) {
2153 log_error("Failed to list jobs: %s", bus_error_message(&error, r));
f84190d8 2154 return r;
7e4249b9
LP
2155 }
2156
f459b602
MAP
2157 r = sd_bus_message_enter_container(reply, 'a', "(usssoo)");
2158 if (r < 0)
2159 return bus_log_parse_error(r);
7e4249b9 2160
f459b602 2161 while ((r = sd_bus_message_read(reply, "(usssoo)", &id, &name, &type, &state, &job_path, &unit_path)) > 0) {
d8fba7c6
ZJS
2162 struct job_info job = { id, name, type, state };
2163
2164 if (!output_show_job(&job, strv_skip_first(args))) {
2165 skipped = true;
2166 continue;
2167 }
8fe914ec 2168
f459b602
MAP
2169 if (!GREEDY_REALLOC(jobs, size, c + 1))
2170 return log_oom();
7e4249b9 2171
d8fba7c6 2172 jobs[c++] = job;
7e4249b9 2173 }
f459b602
MAP
2174 if (r < 0)
2175 return bus_log_parse_error(r);
7e4249b9 2176
f459b602
MAP
2177 r = sd_bus_message_exit_container(reply);
2178 if (r < 0)
2179 return bus_log_parse_error(r);
f73e33d9 2180
d8fba7c6 2181 output_jobs_list(jobs, c, skipped);
c6581cc1 2182 return r;
7e4249b9
LP
2183}
2184
f459b602
MAP
2185static int cancel_job(sd_bus *bus, char **args) {
2186 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
729e3769 2187 char **name;
342641fb 2188 int r = 0;
7e4249b9 2189
514f4ef5
LP
2190 assert(args);
2191
729e3769
LP
2192 if (strv_length(args) <= 1)
2193 return daemon_reload(bus, args);
ee5762e3 2194
729e3769 2195 STRV_FOREACH(name, args+1) {
342641fb 2196 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
5dd9014f 2197 uint32_t id;
342641fb 2198 int q;
7e4249b9 2199
342641fb
LP
2200 q = safe_atou32(*name, &id);
2201 if (q < 0) {
2202 log_error("Failed to parse job id \"%s\": %s", *name, strerror(-q));
2203 return q;
7e4249b9 2204 }
7e4249b9 2205
342641fb 2206 q = sd_bus_message_new_method_call(
f22f08cd 2207 bus,
342641fb 2208 &m,
f22f08cd
SP
2209 "org.freedesktop.systemd1",
2210 "/org/freedesktop/systemd1",
2211 "org.freedesktop.systemd1.Manager",
342641fb
LP
2212 "CancelJob");
2213 if (q < 0)
2214 return bus_log_create_error(q);
2215
2216 q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
2217 if (q < 0)
2218 return bus_log_create_error(1);
2219
2220 q = sd_bus_message_append(m, "u", id);
2221 if (q < 0)
2222 return bus_log_create_error(q);
2223
2224 q = sd_bus_call(bus, m, 0, &error, NULL);
2225 if (q < 0) {
2226 log_error("Failed to cancel job %"PRIu32": %s", id, bus_error_message(&error, q));
2227 if (r == 0)
2228 r = q;
f459b602 2229 }
7e4249b9
LP
2230 }
2231
342641fb 2232 return r;
7e4249b9
LP
2233}
2234
f459b602
MAP
2235static int need_daemon_reload(sd_bus *bus, const char *unit) {
2236 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
f459b602
MAP
2237 const char *path;
2238 int b, r;
94c01aeb 2239
f459b602
MAP
2240 /* We ignore all errors here, since this is used to show a
2241 * warning only */
45fb0699 2242
f459b602
MAP
2243 /* We don't use unit_dbus_path_from_name() directly since we
2244 * don't want to load the unit if it isn't loaded. */
f84190d8 2245
f459b602 2246 r = sd_bus_call_method(
f22f08cd
SP
2247 bus,
2248 "org.freedesktop.systemd1",
2249 "/org/freedesktop/systemd1",
2250 "org.freedesktop.systemd1.Manager",
2251 "GetUnit",
f459b602 2252 NULL,
f22f08cd 2253 &reply,
e3e0314b 2254 "s", unit);
f84190d8
LP
2255 if (r < 0)
2256 return r;
45fb0699 2257
f459b602
MAP
2258 r = sd_bus_message_read(reply, "o", &path);
2259 if (r < 0)
2260 return r;
f84190d8 2261
f459b602 2262 r = sd_bus_get_property_trivial(
f22f08cd 2263 bus,
b0193f1c
LP
2264 "org.freedesktop.systemd1",
2265 path,
f459b602
MAP
2266 "org.freedesktop.systemd1.Unit",
2267 "NeedDaemonReload",
2268 NULL,
2269 'b', &b);
f84190d8
LP
2270 if (r < 0)
2271 return r;
45fb0699 2272
45fb0699
LP
2273 return b;
2274}
2275
5e374895
LP
2276typedef struct WaitData {
2277 Set *set;
67f3c402
LP
2278
2279 char *name;
5d44db4a 2280 char *result;
5e374895
LP
2281} WaitData;
2282
ebcf1f97 2283static int wait_filter(sd_bus *bus, sd_bus_message *m, void *data, sd_bus_error *error) {
5e374895 2284 WaitData *d = data;
7e4249b9 2285
f459b602
MAP
2286 assert(bus);
2287 assert(m);
5e374895 2288 assert(d);
7e4249b9 2289
54165a39 2290 log_debug("Got D-Bus request: %s.%s() on %s",
f459b602
MAP
2291 sd_bus_message_get_interface(m),
2292 sd_bus_message_get_member(m),
2293 sd_bus_message_get_path(m));
7e4249b9 2294
f459b602 2295 if (sd_bus_message_is_signal(m, "org.freedesktop.DBus.Local", "Disconnected")) {
7e4249b9 2296 log_error("Warning! D-Bus connection terminated.");
f459b602
MAP
2297 sd_bus_close(bus);
2298 } else if (sd_bus_message_is_signal(m, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
7e4249b9 2299 uint32_t id;
6e869e18 2300 const char *path, *result, *unit;
f459b602
MAP
2301 char *ret;
2302 int r;
7e4249b9 2303
f459b602
MAP
2304 r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result);
2305 if (r >= 0) {
2306 ret = set_remove(d->set, (char*) path);
2307 if (!ret)
2308 return 0;
c1e784fe 2309
f459b602 2310 free(ret);
5d44db4a 2311
67f3c402 2312 if (!isempty(result))
5d44db4a
LP
2313 d->result = strdup(result);
2314
67f3c402
LP
2315 if (!isempty(unit))
2316 d->name = strdup(unit);
2317
f459b602 2318 return 0;
5d44db4a 2319 }
286ca485 2320#ifndef NOLEGACY
f459b602
MAP
2321 r = sd_bus_message_read(m, "uos", &id, &path, &result);
2322 if (r >= 0) {
2323 ret = set_remove(d->set, (char*) path);
2324 if (!ret)
2325 return 0;
2326
2327 free(ret);
5d44db4a 2328
06dab8e1
LP
2329 if (*result)
2330 d->result = strdup(result);
2331
f459b602 2332 return 0;
06dab8e1 2333 }
5d44db4a
LP
2334#endif
2335
ebcf1f97 2336 bus_log_parse_error(r);
7e4249b9
LP
2337 }
2338
f459b602 2339 return 0;
7e4249b9
LP
2340}
2341
f459b602
MAP
2342static int enable_wait_for_jobs(sd_bus *bus) {
2343 int r;
7e4249b9
LP
2344
2345 assert(bus);
7e4249b9 2346
f459b602
MAP
2347 r = sd_bus_add_match(
2348 bus,
19befb2d 2349 NULL,
f459b602
MAP
2350 "type='signal',"
2351 "sender='org.freedesktop.systemd1',"
2352 "interface='org.freedesktop.systemd1.Manager',"
2353 "member='JobRemoved',"
2354 "path='/org/freedesktop/systemd1'",
2355 NULL, NULL);
2356 if (r < 0) {
2357 log_error("Failed to add match");
a567261a 2358 return -EIO;
7e4249b9
LP
2359 }
2360
479ef5d3 2361 /* This is slightly dirty, since we don't undo the match registrations. */
a567261a 2362 return 0;
7e4249b9
LP
2363}
2364
4c49ab0e
TA
2365static int bus_process_wait(sd_bus *bus) {
2366 int r;
2367
2368 for (;;) {
2369 r = sd_bus_process(bus, NULL);
2370 if (r < 0)
2371 return r;
2372 if (r > 0)
2373 return 0;
2374 r = sd_bus_wait(bus, (uint64_t) -1);
2375 if (r < 0)
2376 return r;
2377 }
2378}
2379
2380static int check_wait_response(WaitData *d) {
2381 int r = 0;
2382
2383 assert(d->result);
2384
2385 if (!arg_quiet) {
2386 if (streq(d->result, "timeout"))
2387 log_error("Job for %s timed out.", strna(d->name));
2388 else if (streq(d->result, "canceled"))
2389 log_error("Job for %s canceled.", strna(d->name));
2390 else if (streq(d->result, "dependency"))
b923047d 2391 log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name));
8e07fc41
ZJS
2392 else if (!streq(d->result, "done") && !streq(d->result, "skipped")) {
2393 if (d->name) {
2394 bool quotes;
2395
2396 quotes = chars_intersect(d->name, SHELL_NEED_QUOTES);
2397
b923047d 2398 log_error("Job for %s failed. See \"systemctl status %s%s%s\" and \"journalctl -xe\" for details.",
8e07fc41
ZJS
2399 d->name,
2400 quotes ? "'" : "", d->name, quotes ? "'" : "");
2401 } else
b923047d 2402 log_error("Job failed. See \"journalctl -xe\" for details.");
8e07fc41 2403 }
4c49ab0e
TA
2404 }
2405
2406 if (streq(d->result, "timeout"))
2407 r = -ETIME;
2408 else if (streq(d->result, "canceled"))
2409 r = -ECANCELED;
2410 else if (streq(d->result, "dependency"))
2411 r = -EIO;
2412 else if (!streq(d->result, "done") && !streq(d->result, "skipped"))
2413 r = -EIO;
2414
2415 return r;
2416}
2417
f459b602 2418static int wait_for_jobs(sd_bus *bus, Set *s) {
19befb2d 2419 _cleanup_bus_slot_unref_ sd_bus_slot *slot = NULL;
b92bea5d 2420 WaitData d = { .set = s };
4c49ab0e 2421 int r = 0, q;
479ef5d3
LP
2422
2423 assert(bus);
2424 assert(s);
2425
19befb2d 2426 q = sd_bus_add_filter(bus, &slot, wait_filter, &d);
4c49ab0e 2427 if (q < 0)
67f3c402 2428 return log_oom();
479ef5d3 2429
67f3c402 2430 while (!set_isempty(s)) {
4c49ab0e 2431 q = bus_process_wait(bus);
e3e0314b 2432 if (q < 0) {
498cfc23 2433 log_error("Failed to wait for response: %s", strerror(-q));
4c49ab0e 2434 return q;
e3e0314b 2435 }
4c49ab0e
TA
2436
2437 if (d.result) {
2438 q = check_wait_response(&d);
2439 /* Return the first error as it is most likely to be
2440 * meaningful. */
2441 if (q < 0 && r == 0)
2442 r = q;
e3e0314b
ZJS
2443 log_debug("Got result %s/%s for job %s",
2444 strna(d.result), strerror(-q), strna(d.name));
67f3c402 2445 }
479ef5d3 2446
4c49ab0e
TA
2447 free(d.name);
2448 d.name = NULL;
67f3c402
LP
2449
2450 free(d.result);
2451 d.result = NULL;
67f3c402 2452 }
479ef5d3 2453
4c49ab0e 2454 return r;
479ef5d3
LP
2455}
2456
f459b602
MAP
2457static int check_one_unit(sd_bus *bus, const char *name, const char *good_states, bool quiet) {
2458 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
2459 _cleanup_free_ char *n = NULL, *state = NULL;
2460 const char *path;
f22f08cd 2461 int r;
701cdcb9 2462
31be1221 2463 assert(name);
701cdcb9 2464
f78e6385 2465 n = unit_name_mangle(name, MANGLE_NOGLOB);
60f9ba0b
LP
2466 if (!n)
2467 return log_oom();
2468
f459b602
MAP
2469 /* We don't use unit_dbus_path_from_name() directly since we
2470 * don't want to load the unit if it isn't loaded. */
2471
2472 r = sd_bus_call_method(
f22f08cd
SP
2473 bus,
2474 "org.freedesktop.systemd1",
2475 "/org/freedesktop/systemd1",
2476 "org.freedesktop.systemd1.Manager",
2477 "GetUnit",
f459b602 2478 NULL,
f22f08cd 2479 &reply,
f459b602 2480 "s", n);
60f9ba0b 2481 if (r < 0) {
60f9ba0b 2482 if (!quiet)
f22f08cd 2483 puts("unknown");
60f9ba0b 2484 return 0;
f22f08cd 2485 }
e61a3135 2486
f459b602
MAP
2487 r = sd_bus_message_read(reply, "o", &path);
2488 if (r < 0)
2489 return bus_log_parse_error(r);
60f9ba0b 2490
f459b602 2491 r = sd_bus_get_property_string(
f22f08cd
SP
2492 bus,
2493 "org.freedesktop.systemd1",
2494 path,
f459b602
MAP
2495 "org.freedesktop.systemd1.Unit",
2496 "ActiveState",
f22f08cd 2497 NULL,
f459b602 2498 &state);
60f9ba0b
LP
2499 if (r < 0) {
2500 if (!quiet)
2501 puts("unknown");
2502 return 0;
2503 }
701cdcb9 2504
31be1221
MS
2505 if (!quiet)
2506 puts(state);
2507
f459b602 2508 return nulstr_contains(good_states, state);
701cdcb9
MS
2509}
2510
f459b602
MAP
2511static int check_triggering_units(
2512 sd_bus *bus,
2513 const char *name) {
2514
2515 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
2516 _cleanup_free_ char *path = NULL, *n = NULL, *state = NULL;
2517 _cleanup_strv_free_ char **triggered_by = NULL;
e61a3135 2518 bool print_warning_label = true;
f459b602 2519 char **i;
f22f08cd 2520 int r;
701cdcb9 2521
f78e6385 2522 n = unit_name_mangle(name, MANGLE_NOGLOB);
f459b602
MAP
2523 if (!n)
2524 return log_oom();
d3b52baf 2525
f459b602
MAP
2526 path = unit_dbus_path_from_name(n);
2527 if (!path)
2528 return log_oom();
701cdcb9 2529
f459b602 2530 r = sd_bus_get_property_string(
d0a5cdb2
JJ
2531 bus,
2532 "org.freedesktop.systemd1",
f459b602
MAP
2533 path,
2534 "org.freedesktop.systemd1.Unit",
2535 "LoadState",
2536 &error,
2537 &state);
2538 if (r < 0) {
2539 log_error("Failed to get load state of %s: %s", n, bus_error_message(&error, r));
2540 return r;
d0a5cdb2
JJ
2541 }
2542
d0a5cdb2 2543 if (streq(state, "masked"))
f459b602 2544 return 0;
701cdcb9 2545
f459b602
MAP
2546 r = sd_bus_get_property_strv(
2547 bus,
2548 "org.freedesktop.systemd1",
2549 path,
2550 "org.freedesktop.systemd1.Unit",
2551 "TriggeredBy",
2552 &error,
2553 &triggered_by);
2554 if (r < 0) {
2555 log_error("Failed to get triggered by array of %s: %s", n, bus_error_message(&error, r));
2556 return r;
2557 }
701cdcb9 2558
f459b602
MAP
2559 STRV_FOREACH(i, triggered_by) {
2560 r = check_one_unit(bus, *i, "active\0reloading\0", true);
2561 if (r < 0) {
2562 log_error("Failed to check unit: %s", strerror(-r));
2563 return r;
701cdcb9
MS
2564 }
2565
f459b602
MAP
2566 if (r == 0)
2567 continue;
60f9ba0b 2568
f459b602
MAP
2569 if (print_warning_label) {
2570 log_warning("Warning: Stopping %s, but it can still be activated by:", n);
2571 print_warning_label = false;
701cdcb9 2572 }
1c291cf3 2573
f459b602 2574 log_warning(" %s", *i);
701cdcb9 2575 }
f459b602
MAP
2576
2577 return 0;
701cdcb9
MS
2578}
2579
2fc9a280
LP
2580static const struct {
2581 const char *verb;
2582 const char *method;
2583} unit_actions[] = {
2584 { "start", "StartUnit" },
2585 { "stop", "StopUnit" },
2586 { "condstop", "StopUnit" },
2587 { "reload", "ReloadUnit" },
2588 { "restart", "RestartUnit" },
2589 { "try-restart", "TryRestartUnit" },
2590 { "condrestart", "TryRestartUnit" },
2591 { "reload-or-restart", "ReloadOrRestartUnit" },
2592 { "reload-or-try-restart", "ReloadOrTryRestartUnit" },
2593 { "condreload", "ReloadOrTryRestartUnit" },
2594 { "force-reload", "ReloadOrTryRestartUnit" }
2595};
2596
39602c39
TA
2597static const char *verb_to_method(const char *verb) {
2598 uint i;
2599
2600 for (i = 0; i < ELEMENTSOF(unit_actions); i++)
2601 if (streq_ptr(unit_actions[i].verb, verb))
2602 return unit_actions[i].method;
2603
2604 return "StartUnit";
2605}
2606
2607static const char *method_to_verb(const char *method) {
2608 uint i;
2609
2610 for (i = 0; i < ELEMENTSOF(unit_actions); i++)
2611 if (streq_ptr(unit_actions[i].method, method))
2612 return unit_actions[i].verb;
2613
2614 return "n/a";
2615}
2616
e4b61340 2617static int start_unit_one(
f459b602 2618 sd_bus *bus,
e4b61340
LP
2619 const char *method,
2620 const char *name,
2621 const char *mode,
f459b602 2622 sd_bus_error *error,
e4b61340 2623 Set *s) {
7e4249b9 2624
342641fb 2625 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
45fb0699 2626 const char *path;
7e4249b9 2627 int r;
7e4249b9 2628
e4b61340
LP
2629 assert(method);
2630 assert(name);
2631 assert(mode);
22f4096c 2632 assert(error);
7e4249b9 2633
e3e0314b 2634 log_debug("Calling manager for %s on %s, %s", method, name, mode);
342641fb
LP
2635
2636 r = sd_bus_message_new_method_call(
f22f08cd 2637 bus,
342641fb 2638 &m,
b0193f1c
LP
2639 "org.freedesktop.systemd1",
2640 "/org/freedesktop/systemd1",
2641 "org.freedesktop.systemd1.Manager",
342641fb
LP
2642 method);
2643 if (r < 0)
2644 return bus_log_create_error(r);
2645
2646 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
2647 if (r < 0)
2648 return bus_log_create_error(r);
2649
2650 r = sd_bus_message_append(m, "ss", name, mode);
2651 if (r < 0)
2652 return bus_log_create_error(r);
2653
2654 r = sd_bus_call(bus, m, 0, error, &reply);
f459b602 2655 if (r < 0) {
39602c39
TA
2656 const char *verb;
2657
67f3c402 2658 if (r == -ENOENT && arg_action != ACTION_SYSTEMCTL)
e4b61340
LP
2659 /* There's always a fallback possible for
2660 * legacy actions. */
f459b602 2661 return -EADDRNOTAVAIL;
67f3c402 2662
39602c39
TA
2663 verb = method_to_verb(method);
2664
2665 log_error("Failed to %s %s: %s", verb, name, bus_error_message(error, r));
46eddbb5 2666 return r;
7e4249b9
LP
2667 }
2668
f459b602
MAP
2669 r = sd_bus_message_read(reply, "o", &path);
2670 if (r < 0)
2671 return bus_log_parse_error(r);
45fb0699 2672
e3e0314b 2673 if (need_daemon_reload(bus, name) > 0)
f459b602 2674 log_warning("Warning: Unit file of %s changed on disk, 'systemctl%s daemon-reload' recommended.",
e3e0314b 2675 name, arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
45fb0699 2676
67f3c402 2677 if (s) {
f84190d8
LP
2678 char *p;
2679
67f3c402 2680 p = strdup(path);
46eddbb5
ZJS
2681 if (!p)
2682 return log_oom();
7e4249b9 2683
e3e0314b 2684 log_debug("Adding %s to the set", p);
ef42202a 2685 r = set_consume(s, p);
cbc9fbd1
LP
2686 if (r < 0)
2687 return log_oom();
e4b61340 2688 }
7e4249b9 2689
46eddbb5 2690 return 0;
7e4249b9
LP
2691}
2692
e3e0314b
ZJS
2693static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***ret) {
2694
e3e0314b
ZJS
2695 _cleanup_strv_free_ char **mangled = NULL, **globs = NULL;
2696 char **name;
2697 int r = 0, i;
2698
2699 STRV_FOREACH(name, names) {
2700 char *t;
2701
2702 if (suffix)
f78e6385 2703 t = unit_name_mangle_with_suffix(*name, MANGLE_GLOB, suffix);
e3e0314b 2704 else
f78e6385 2705 t = unit_name_mangle(*name, MANGLE_GLOB);
e3e0314b
ZJS
2706 if (!t)
2707 return log_oom();
2708
2709 if (string_is_glob(t))
6e18964d 2710 r = strv_consume(&globs, t);
e3e0314b 2711 else
6e18964d
ZJS
2712 r = strv_consume(&mangled, t);
2713 if (r < 0)
e3e0314b 2714 return log_oom();
e3e0314b
ZJS
2715 }
2716
2717 /* Query the manager only if any of the names are a glob, since
2718 * this is fairly expensive */
2719 if (!strv_isempty(globs)) {
1238ee09 2720 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
e3e0314b
ZJS
2721 _cleanup_free_ UnitInfo *unit_infos = NULL;
2722
1238ee09 2723 r = get_unit_list(bus, NULL, globs, &unit_infos, 0, &reply);
e3e0314b
ZJS
2724 if (r < 0)
2725 return r;
2726
2727 for (i = 0; i < r; i++)
2728 if (strv_extend(&mangled, unit_infos[i].id) < 0)
2729 return log_oom();
2730 }
2731
2732 *ret = mangled;
2733 mangled = NULL; /* do not free */
1238ee09 2734
e3e0314b
ZJS
2735 return 0;
2736}
2737
47a0eaa6
MS
2738static const struct {
2739 const char *target;
2740 const char *verb;
2741 const char *mode;
2742} action_table[_ACTION_MAX] = {
2743 [ACTION_HALT] = { SPECIAL_HALT_TARGET, "halt", "replace-irreversibly" },
2744 [ACTION_POWEROFF] = { SPECIAL_POWEROFF_TARGET, "poweroff", "replace-irreversibly" },
2745 [ACTION_REBOOT] = { SPECIAL_REBOOT_TARGET, "reboot", "replace-irreversibly" },
2746 [ACTION_KEXEC] = { SPECIAL_KEXEC_TARGET, "kexec", "replace-irreversibly" },
2747 [ACTION_RUNLEVEL2] = { SPECIAL_RUNLEVEL2_TARGET, NULL, "isolate" },
2748 [ACTION_RUNLEVEL3] = { SPECIAL_RUNLEVEL3_TARGET, NULL, "isolate" },
2749 [ACTION_RUNLEVEL4] = { SPECIAL_RUNLEVEL4_TARGET, NULL, "isolate" },
2750 [ACTION_RUNLEVEL5] = { SPECIAL_RUNLEVEL5_TARGET, NULL, "isolate" },
2751 [ACTION_RESCUE] = { SPECIAL_RESCUE_TARGET, "rescue", "isolate" },
2752 [ACTION_EMERGENCY] = { SPECIAL_EMERGENCY_TARGET, "emergency", "isolate" },
2753 [ACTION_DEFAULT] = { SPECIAL_DEFAULT_TARGET, "default", "isolate" },
2754 [ACTION_EXIT] = { SPECIAL_EXIT_TARGET, "exit", "replace-irreversibly" },
2755 [ACTION_SUSPEND] = { SPECIAL_SUSPEND_TARGET, "suspend", "replace-irreversibly" },
2756 [ACTION_HIBERNATE] = { SPECIAL_HIBERNATE_TARGET, "hibernate", "replace-irreversibly" },
2757 [ACTION_HYBRID_SLEEP] = { SPECIAL_HYBRID_SLEEP_TARGET, "hybrid-sleep", "replace-irreversibly" },
2758};
2759
514f4ef5 2760static enum action verb_to_action(const char *verb) {
47a0eaa6
MS
2761 enum action i;
2762
f459b602
MAP
2763 for (i = _ACTION_INVALID; i < _ACTION_MAX; i++)
2764 if (streq_ptr(action_table[i].verb, verb))
47a0eaa6 2765 return i;
514f4ef5 2766
f459b602
MAP
2767 return _ACTION_INVALID;
2768}
e4b61340 2769
f459b602 2770static int start_unit(sd_bus *bus, char **args) {
7fd1b19b 2771 _cleanup_set_free_free_ Set *s = NULL;
e3e0314b 2772 _cleanup_strv_free_ char **names = NULL;
08073121 2773 const char *method, *mode, *one_name, *suffix = NULL;
729e3769 2774 char **name;
b6520546 2775 int r = 0;
e4b61340 2776
514f4ef5
LP
2777 assert(bus);
2778
6bb92a16 2779 ask_password_agent_open_if_enabled();
501fc174 2780
e4b61340 2781 if (arg_action == ACTION_SYSTEMCTL) {
47a0eaa6 2782 enum action action;
39602c39 2783 method = verb_to_method(args[0]);
47a0eaa6 2784 action = verb_to_action(args[0]);
e4b61340 2785
08073121
LP
2786 if (streq(args[0], "isolate")) {
2787 mode = "isolate";
2788 suffix = ".target";
2789 } else
2790 mode = action_table[action].mode ?: arg_job_mode;
e4b61340 2791
e3e0314b 2792 one_name = action_table[action].target;
e4b61340 2793 } else {
47a0eaa6
MS
2794 assert(arg_action < ELEMENTSOF(action_table));
2795 assert(action_table[arg_action].target);
e4b61340
LP
2796
2797 method = "StartUnit";
514f4ef5 2798
47a0eaa6 2799 mode = action_table[arg_action].mode;
e3e0314b 2800 one_name = action_table[arg_action].target;
514f4ef5
LP
2801 }
2802
e3e0314b
ZJS
2803 if (one_name)
2804 names = strv_new(one_name, NULL);
2805 else {
08073121 2806 r = expand_names(bus, args + 1, suffix, &names);
e3e0314b
ZJS
2807 if (r < 0)
2808 log_error("Failed to expand names: %s", strerror(-r));
2809 }
b6520546 2810
6e905d93 2811 if (!arg_no_block) {
f459b602
MAP
2812 r = enable_wait_for_jobs(bus);
2813 if (r < 0) {
2814 log_error("Could not watch jobs: %s", strerror(-r));
2815 return r;
514f4ef5
LP
2816 }
2817
d5099efc 2818 s = set_new(&string_hash_ops);
cec7eda5
ZJS
2819 if (!s)
2820 return log_oom();
e4b61340
LP
2821 }
2822
b6520546 2823 STRV_FOREACH(name, names) {
e3e0314b 2824 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
b6520546 2825 int q;
f459b602 2826
b6520546 2827 q = start_unit_one(bus, method, *name, mode, &error, s);
e3e0314b 2828 if (r >= 0 && q < 0)
b6520546 2829 r = translate_bus_error_to_exit_status(q, &error);
e4b61340
LP
2830 }
2831
67f3c402 2832 if (!arg_no_block) {
f459b602
MAP
2833 int q;
2834
2835 q = wait_for_jobs(bus, s);
2836 if (q < 0)
2837 return q;
49111a70
ZJS
2838
2839 /* When stopping units, warn if they can still be triggered by
2840 * another active unit (socket, path, timer) */
b6520546
ZJS
2841 if (!arg_quiet && streq(method, "StopUnit"))
2842 STRV_FOREACH(name, names)
2843 check_triggering_units(bus, *name);
67f3c402 2844 }
514f4ef5 2845
f459b602 2846 return r;
e4b61340
LP
2847}
2848
7e59bfcb
LP
2849/* Ask systemd-logind, which might grant access to unprivileged users
2850 * through PolicyKit */
f459b602 2851static int reboot_with_logind(sd_bus *bus, enum action a) {
4c80c73c 2852#ifdef HAVE_LOGIND
f459b602 2853 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
4c80c73c 2854 const char *method;
f459b602 2855 int r;
4c80c73c 2856
d255133d
LP
2857 if (!bus)
2858 return -EIO;
2859
6bb92a16
LP
2860 polkit_agent_open_if_enabled();
2861
4c80c73c
KS
2862 switch (a) {
2863
2864 case ACTION_REBOOT:
2865 method = "Reboot";
2866 break;
2867
2868 case ACTION_POWEROFF:
2869 method = "PowerOff";
2870 break;
2871
d889a206
LP
2872 case ACTION_SUSPEND:
2873 method = "Suspend";
2874 break;
2875
2876 case ACTION_HIBERNATE:
2877 method = "Hibernate";
2878 break;
2879
6524990f
LP
2880 case ACTION_HYBRID_SLEEP:
2881 method = "HybridSleep";
2882 break;
2883
4c80c73c
KS
2884 default:
2885 return -EINVAL;
2886 }
2887
f459b602 2888 r = sd_bus_call_method(
f22f08cd
SP
2889 bus,
2890 "org.freedesktop.login1",
2891 "/org/freedesktop/login1",
2892 "org.freedesktop.login1.Manager",
2893 method,
f459b602 2894 &error,
f22f08cd 2895 NULL,
342641fb 2896 "b", arg_ask_password);
f459b602
MAP
2897 if (r < 0)
2898 log_error("Failed to execute operation: %s", bus_error_message(&error, r));
2899
2900 return r;
4c80c73c
KS
2901#else
2902 return -ENOSYS;
2903#endif
2904}
2905
f459b602 2906static int check_inhibitors(sd_bus *bus, enum action a) {
b37844d3 2907#ifdef HAVE_LOGIND
f459b602 2908 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
59164be4 2909 _cleanup_strv_free_ char **sessions = NULL;
f459b602
MAP
2910 const char *what, *who, *why, *mode;
2911 uint32_t uid, pid;
2912 unsigned c = 0;
59164be4 2913 char **s;
f459b602 2914 int r;
b37844d3 2915
748ebafa
LP
2916 if (!bus)
2917 return 0;
2918
2919 if (arg_ignore_inhibitors || arg_force > 0)
2920 return 0;
2921
2922 if (arg_when > 0)
2923 return 0;
2924
2925 if (geteuid() == 0)
b37844d3
LP
2926 return 0;
2927
2928 if (!on_tty())
2929 return 0;
2930
f459b602 2931 r = sd_bus_call_method(
b37844d3
LP
2932 bus,
2933 "org.freedesktop.login1",
2934 "/org/freedesktop/login1",
2935 "org.freedesktop.login1.Manager",
2936 "ListInhibitors",
b37844d3 2937 NULL,
f459b602
MAP
2938 &reply,
2939 NULL);
b37844d3
LP
2940 if (r < 0)
2941 /* If logind is not around, then there are no inhibitors... */
2942 return 0;
2943
4aa2beac 2944 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "(ssssuu)");
f459b602
MAP
2945 if (r < 0)
2946 return bus_log_parse_error(r);
b37844d3 2947
4aa2beac 2948 while ((r = sd_bus_message_read(reply, "(ssssuu)", &what, &who, &why, &mode, &uid, &pid)) > 0) {
59164be4 2949 _cleanup_free_ char *comm = NULL, *user = NULL;
f459b602 2950 _cleanup_strv_free_ char **sv = NULL;
b37844d3
LP
2951
2952 if (!streq(mode, "block"))
f459b602 2953 continue;
b37844d3
LP
2954
2955 sv = strv_split(what, ":");
2956 if (!sv)
2957 return log_oom();
2958
2959 if (!strv_contains(sv,
2960 a == ACTION_HALT ||
2961 a == ACTION_POWEROFF ||
2962 a == ACTION_REBOOT ||
2963 a == ACTION_KEXEC ? "shutdown" : "sleep"))
f459b602 2964 continue;
b37844d3
LP
2965
2966 get_process_comm(pid, &comm);
59164be4 2967 user = uid_to_name(uid);
f459b602 2968
de0671ee
ZJS
2969 log_warning("Operation inhibited by \"%s\" (PID "PID_FMT" \"%s\", user %s), reason is \"%s\".",
2970 who, pid, strna(comm), strna(user), why);
b37844d3 2971
f459b602 2972 c++;
b37844d3 2973 }
f459b602
MAP
2974 if (r < 0)
2975 return bus_log_parse_error(r);
b37844d3 2976
f459b602
MAP
2977 r = sd_bus_message_exit_container(reply);
2978 if (r < 0)
2979 return bus_log_parse_error(r);
b37844d3 2980
59164be4
LP
2981 /* Check for current sessions */
2982 sd_get_sessions(&sessions);
2983 STRV_FOREACH(s, sessions) {
59164be4
LP
2984 _cleanup_free_ char *type = NULL, *tty = NULL, *seat = NULL, *user = NULL, *service = NULL, *class = NULL;
2985
2986 if (sd_session_get_uid(*s, &uid) < 0 || uid == getuid())
2987 continue;
2988
2989 if (sd_session_get_class(*s, &class) < 0 || !streq(class, "user"))
2990 continue;
2991
2992 if (sd_session_get_type(*s, &type) < 0 || (!streq(type, "x11") && !streq(type, "tty")))
2993 continue;
2994
2995 sd_session_get_tty(*s, &tty);
2996 sd_session_get_seat(*s, &seat);
2997 sd_session_get_service(*s, &service);
2998 user = uid_to_name(uid);
2999
3000 log_warning("User %s is logged in on %s.", strna(user), isempty(tty) ? (isempty(seat) ? strna(service) : seat) : tty);
3001 c++;
3002 }
3003
b37844d3
LP
3004 if (c <= 0)
3005 return 0;
3006
59164be4 3007 log_error("Please retry operation after closing inhibitors and logging out other users.\nAlternatively, ignore inhibitors and users with 'systemctl %s -i'.",
47a0eaa6 3008 action_table[a].verb);
b37844d3
LP
3009
3010 return -EPERM;
3011#else
3012 return 0;
3013#endif
3014}
3015
f459b602 3016static int start_special(sd_bus *bus, char **args) {
4c80c73c 3017 enum action a;
983d9c90
LP
3018 int r;
3019
514f4ef5
LP
3020 assert(args);
3021
4c80c73c
KS
3022 a = verb_to_action(args[0]);
3023
748ebafa
LP
3024 r = check_inhibitors(bus, a);
3025 if (r < 0)
3026 return r;
3027
c32b90de
LP
3028 if (arg_force >= 2 && geteuid() != 0) {
3029 log_error("Must be root.");
3030 return -EPERM;
3031 }
3032
7e59bfcb
LP
3033 if (arg_force >= 2 &&
3034 (a == ACTION_HALT ||
3035 a == ACTION_POWEROFF ||
3036 a == ACTION_REBOOT))
477def80 3037 return halt_now(a);
e606bb61 3038
7e59bfcb 3039 if (arg_force >= 1 &&
4c80c73c
KS
3040 (a == ACTION_HALT ||
3041 a == ACTION_POWEROFF ||
3042 a == ACTION_REBOOT ||
3043 a == ACTION_KEXEC ||
3044 a == ACTION_EXIT))
729e3769 3045 return daemon_reload(bus, args);
20b09ca7 3046
7e59bfcb
LP
3047 /* first try logind, to allow authentication with polkit */
3048 if (geteuid() != 0 &&
3049 (a == ACTION_POWEROFF ||
d889a206
LP
3050 a == ACTION_REBOOT ||
3051 a == ACTION_SUSPEND ||
6524990f
LP
3052 a == ACTION_HIBERNATE ||
3053 a == ACTION_HYBRID_SLEEP)) {
7e59bfcb
LP
3054 r = reboot_with_logind(bus, a);
3055 if (r >= 0)
3056 return r;
4c80c73c 3057 }
983d9c90 3058
4c80c73c 3059 r = start_unit(bus, args);
f6bb13ab 3060 if (r == EXIT_SUCCESS)
4c80c73c 3061 warn_wall(a);
514f4ef5 3062
983d9c90 3063 return r;
514f4ef5
LP
3064}
3065
e3e0314b 3066static int check_unit_generic(sd_bus *bus, int code, const char *good_states, char **args) {
e3e0314b 3067 _cleanup_strv_free_ char **names = NULL;
729e3769 3068 char **name;
5a1aece5 3069 int r;
0183528f
LP
3070
3071 assert(bus);
3072 assert(args);
3073
e3e0314b 3074 r = expand_names(bus, args, NULL, &names);
5a1aece5 3075 if (r < 0) {
e3e0314b 3076 log_error("Failed to expand names: %s", strerror(-r));
5a1aece5
DR
3077 return r;
3078 }
e3e0314b
ZJS
3079
3080 STRV_FOREACH(name, names) {
60f9ba0b
LP
3081 int state;
3082
e3e0314b 3083 state = check_one_unit(bus, *name, good_states, arg_quiet);
1a0fce45
TA
3084 if (state < 0)
3085 return state;
5a1aece5
DR
3086 if (state == 0)
3087 r = code;
1a0fce45
TA
3088 }
3089
3090 return r;
3091}
3092
e3e0314b
ZJS
3093static int check_unit_active(sd_bus *bus, char **args) {
3094 /* According to LSB: 3, "program is not running" */
3095 return check_unit_generic(bus, 3, "active\0reloading\0", args + 1);
3096}
0183528f 3097
e3e0314b
ZJS
3098static int check_unit_failed(sd_bus *bus, char **args) {
3099 return check_unit_generic(bus, 1, "failed\0", args + 1);
48220598
LP
3100}
3101
f459b602
MAP
3102static int kill_unit(sd_bus *bus, char **args) {
3103 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e3e0314b 3104 _cleanup_strv_free_ char **names = NULL;
60f9ba0b 3105 char **name;
e3e0314b 3106 int r, q;
8a0867d6 3107
60f9ba0b 3108 assert(bus);
8a0867d6
LP
3109 assert(args);
3110
8a0867d6
LP
3111 if (!arg_kill_who)
3112 arg_kill_who = "all";
3113
e3e0314b
ZJS
3114 r = expand_names(bus, args + 1, NULL, &names);
3115 if (r < 0)
3116 log_error("Failed to expand names: %s", strerror(-r));
60f9ba0b 3117
e3e0314b 3118 STRV_FOREACH(name, names) {
342641fb
LP
3119 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
3120
3121 q = sd_bus_message_new_method_call(
f22f08cd 3122 bus,
342641fb 3123 &m,
b0193f1c
LP
3124 "org.freedesktop.systemd1",
3125 "/org/freedesktop/systemd1",
3126 "org.freedesktop.systemd1.Manager",
342641fb
LP
3127 "KillUnit");
3128 if (q < 0)
3129 return bus_log_create_error(q);
3130
3131 q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
3132 if (q < 0)
3133 return bus_log_create_error(q);
3134
3135 q = sd_bus_message_append(m, "ssi", *names, arg_kill_who, arg_signal);
3136 if (q < 0)
3137 return bus_log_create_error(q);
3138
3139 q = sd_bus_call(bus, m, 0, &error, NULL);
e3e0314b 3140 if (q < 0) {
342641fb 3141 log_error("Failed to kill unit %s: %s", *names, bus_error_message(&error, q));
e3e0314b
ZJS
3142 if (r == 0)
3143 r = q;
f459b602 3144 }
8a0867d6 3145 }
f459b602 3146
e3e0314b 3147 return r;
8a0867d6
LP
3148}
3149
582a507f 3150typedef struct ExecStatusInfo {
0129173a
LP
3151 char *name;
3152
582a507f
LP
3153 char *path;
3154 char **argv;
3155
b708e7ce
LP
3156 bool ignore;
3157
582a507f
LP
3158 usec_t start_timestamp;
3159 usec_t exit_timestamp;
3160 pid_t pid;
3161 int code;
3162 int status;
3163
3164 LIST_FIELDS(struct ExecStatusInfo, exec);
3165} ExecStatusInfo;
3166
3167static void exec_status_info_free(ExecStatusInfo *i) {
3168 assert(i);
3169
0129173a 3170 free(i->name);
582a507f
LP
3171 free(i->path);
3172 strv_free(i->argv);
3173 free(i);
3174}
3175
f459b602 3176static int exec_status_info_deserialize(sd_bus_message *m, ExecStatusInfo *i) {
b21a0ef8 3177 uint64_t start_timestamp, exit_timestamp, start_timestamp_monotonic, exit_timestamp_monotonic;
f459b602 3178 const char *path;
582a507f
LP
3179 uint32_t pid;
3180 int32_t code, status;
f459b602 3181 int ignore, r;
582a507f 3182
f459b602 3183 assert(m);
582a507f 3184 assert(i);
582a507f 3185
f459b602
MAP
3186 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, "sasbttttuii");
3187 if (r < 0)
3188 return bus_log_parse_error(r);
3189 else if (r == 0)
3190 return 0;
582a507f 3191
f459b602
MAP
3192 r = sd_bus_message_read(m, "s", &path);
3193 if (r < 0)
3194 return bus_log_parse_error(r);
582a507f 3195
f84190d8
LP
3196 i->path = strdup(path);
3197 if (!i->path)
f459b602 3198 return log_oom();
582a507f 3199
f459b602
MAP
3200 r = sd_bus_message_read_strv(m, &i->argv);
3201 if (r < 0)
3202 return bus_log_parse_error(r);
3203
3204 r = sd_bus_message_read(m,
3205 "bttttuii",
3206 &ignore,
3207 &start_timestamp, &start_timestamp_monotonic,
3208 &exit_timestamp, &exit_timestamp_monotonic,
3209 &pid,
3210 &code, &status);
3211 if (r < 0)
3212 return bus_log_parse_error(r);
582a507f 3213
b708e7ce 3214 i->ignore = ignore;
582a507f
LP
3215 i->start_timestamp = (usec_t) start_timestamp;
3216 i->exit_timestamp = (usec_t) exit_timestamp;
3217 i->pid = (pid_t) pid;
3218 i->code = code;
3219 i->status = status;
3220
f459b602
MAP
3221 r = sd_bus_message_exit_container(m);
3222 if (r < 0)
3223 return bus_log_parse_error(r);
3224
3225 return 1;
582a507f
LP
3226}
3227
61cbdc4b
LP
3228typedef struct UnitStatusInfo {
3229 const char *id;
3230 const char *load_state;
3231 const char *active_state;
3232 const char *sub_state;
a4375746 3233 const char *unit_file_state;
61cbdc4b
LP
3234
3235 const char *description;
4a9e2fff 3236 const char *following;
61cbdc4b 3237
49dbfa7b
LP
3238 char **documentation;
3239
1b64d026
LP
3240 const char *fragment_path;
3241 const char *source_path;
4ad49000 3242 const char *control_group;
61cbdc4b 3243
76d14b87
OS
3244 char **dropin_paths;
3245
9f39404c 3246 const char *load_error;
f42806df 3247 const char *result;
9f39404c 3248
584be568 3249 usec_t inactive_exit_timestamp;
df50185b 3250 usec_t inactive_exit_timestamp_monotonic;
584be568
LP
3251 usec_t active_enter_timestamp;
3252 usec_t active_exit_timestamp;
3253 usec_t inactive_enter_timestamp;
3254
45fb0699
LP
3255 bool need_daemon_reload;
3256
61cbdc4b
LP
3257 /* Service */
3258 pid_t main_pid;
3259 pid_t control_pid;
3260 const char *status_text;
175728c4 3261 const char *pid_file;
d06dacd0 3262 bool running:1;
b4af5a80 3263 int status_errno;
61cbdc4b
LP
3264
3265 usec_t start_timestamp;
3266 usec_t exit_timestamp;
3267
3268 int exit_code, exit_status;
3269
90bbc946
LP
3270 usec_t condition_timestamp;
3271 bool condition_result;
52990c2e
ZJS
3272 bool failed_condition_trigger;
3273 bool failed_condition_negate;
3274 const char *failed_condition;
59fccdc5
LP
3275 const char *failed_condition_parameter;
3276
3277 usec_t assert_timestamp;
3278 bool assert_result;
3279 bool failed_assert_trigger;
3280 bool failed_assert_negate;
3281 const char *failed_assert;
3282 const char *failed_assert_parameter;
90bbc946 3283
61cbdc4b
LP
3284 /* Socket */
3285 unsigned n_accepted;
3286 unsigned n_connections;
b8131a87 3287 bool accept;
61cbdc4b 3288
13160134 3289 /* Pairs of type, path */
67419600
OS
3290 char **listen;
3291
61cbdc4b
LP
3292 /* Device */
3293 const char *sysfs_path;
3294
3295 /* Mount, Automount */
3296 const char *where;
3297
3298 /* Swap */
3299 const char *what;
582a507f
LP
3300
3301 LIST_HEAD(ExecStatusInfo, exec);
61cbdc4b
LP
3302} UnitStatusInfo;
3303
f459b602
MAP
3304static void print_status_info(
3305 UnitStatusInfo *i,
3306 bool *ellipsized) {
3307
582a507f 3308 ExecStatusInfo *p;
b0d14c69 3309 const char *active_on, *active_off, *on, *off, *ss;
584be568 3310 usec_t timestamp;
9185c8e6 3311 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
584be568 3312 char since2[FORMAT_TIMESTAMP_MAX], *s2;
1b64d026 3313 const char *path;
9bdbc2e2
LN
3314 int flags =
3315 arg_all * OUTPUT_SHOW_ALL |
3316 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
3317 on_tty() * OUTPUT_COLOR |
3318 !arg_quiet * OUTPUT_WARN_CUTOFF |
3319 arg_full * OUTPUT_FULL_WIDTH;
13160134 3320 char **t, **t2;
582a507f 3321
61cbdc4b
LP
3322 assert(i);
3323
3324 /* This shows pretty information about a unit. See
3325 * print_property() for a low-level property printer */
3326
b0d14c69
LP
3327 if (streq_ptr(i->active_state, "failed")) {
3328 active_on = ansi_highlight_red();
3329 active_off = ansi_highlight_off();
3330 } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
3331 active_on = ansi_highlight_green();
3332 active_off = ansi_highlight_off();
3333 } else
3334 active_on = active_off = "";
3335
6b01f1d3 3336 printf("%s%s%s %s", active_on, draw_special_char(DRAW_BLACK_CIRCLE), active_off, strna(i->id));
61cbdc4b
LP
3337
3338 if (i->description && !streq_ptr(i->id, i->description))
3339 printf(" - %s", i->description);
3340
3341 printf("\n");
3342
4a9e2fff 3343 if (i->following)
856323c9 3344 printf(" Follow: unit currently follows state of %s\n", i->following);
4a9e2fff 3345
f7b9e331 3346 if (streq_ptr(i->load_state, "error")) {
0b5a519c
DS
3347 on = ansi_highlight_red();
3348 off = ansi_highlight_off();
c31b4423
LP
3349 } else
3350 on = off = "";
3351
1b64d026
LP
3352 path = i->source_path ? i->source_path : i->fragment_path;
3353
9f39404c 3354 if (i->load_error)
856323c9
ZJS
3355 printf(" Loaded: %s%s%s (Reason: %s)\n",
3356 on, strna(i->load_state), off, i->load_error);
1b64d026 3357 else if (path && i->unit_file_state)
856323c9
ZJS
3358 printf(" Loaded: %s%s%s (%s; %s)\n",
3359 on, strna(i->load_state), off, path, i->unit_file_state);
1b64d026 3360 else if (path)
856323c9
ZJS
3361 printf(" Loaded: %s%s%s (%s)\n",
3362 on, strna(i->load_state), off, path);
61cbdc4b 3363 else
856323c9
ZJS
3364 printf(" Loaded: %s%s%s\n",
3365 on, strna(i->load_state), off);
61cbdc4b 3366
76d14b87 3367 if (!strv_isempty(i->dropin_paths)) {
f459b602 3368 _cleanup_free_ char *dir = NULL;
76d14b87 3369 bool last = false;
f459b602 3370 char ** dropin;
76d14b87
OS
3371
3372 STRV_FOREACH(dropin, i->dropin_paths) {
3373 if (! dir || last) {
856323c9 3374 printf(dir ? " " : " Drop-In: ");
76d14b87
OS
3375
3376 free(dir);
f459b602 3377 dir = NULL;
76d14b87
OS
3378
3379 if (path_get_parent(*dropin, &dir) < 0) {
3380 log_oom();
3381 return;
3382 }
3383
856323c9 3384 printf("%s\n %s", dir,
76d14b87
OS
3385 draw_special_char(DRAW_TREE_RIGHT));
3386 }
3387
3388 last = ! (*(dropin + 1) && startswith(*(dropin + 1), dir));
3389
2b6bf07d 3390 printf("%s%s", basename(*dropin), last ? "\n" : ", ");
76d14b87 3391 }
76d14b87
OS
3392 }
3393
2ee68f72 3394 ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
2ee68f72 3395 if (ss)
856323c9 3396 printf(" Active: %s%s (%s)%s",
b0d14c69 3397 active_on, strna(i->active_state), ss, active_off);
2ee68f72 3398 else
856323c9 3399 printf(" Active: %s%s%s",
b0d14c69 3400 active_on, strna(i->active_state), active_off);
61cbdc4b 3401
f42806df
LP
3402 if (!isempty(i->result) && !streq(i->result, "success"))
3403 printf(" (Result: %s)", i->result);
3404
584be568
LP
3405 timestamp = (streq_ptr(i->active_state, "active") ||
3406 streq_ptr(i->active_state, "reloading")) ? i->active_enter_timestamp :
3407 (streq_ptr(i->active_state, "inactive") ||
fdf20a31 3408 streq_ptr(i->active_state, "failed")) ? i->inactive_enter_timestamp :
584be568
LP
3409 streq_ptr(i->active_state, "activating") ? i->inactive_exit_timestamp :
3410 i->active_exit_timestamp;
3411
bbb8486e 3412 s1 = format_timestamp_relative(since1, sizeof(since1), timestamp);
584be568
LP
3413 s2 = format_timestamp(since2, sizeof(since2), timestamp);
3414
3415 if (s1)
538da63d 3416 printf(" since %s; %s\n", s2, s1);
584be568 3417 else if (s2)
538da63d 3418 printf(" since %s\n", s2);
584be568
LP
3419 else
3420 printf("\n");
3421
90bbc946 3422 if (!i->condition_result && i->condition_timestamp > 0) {
bbb8486e 3423 s1 = format_timestamp_relative(since1, sizeof(since1), i->condition_timestamp);
90bbc946
LP
3424 s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp);
3425
59fccdc5
LP
3426 printf("Condition: start %scondition failed%s at %s%s%s\n",
3427 ansi_highlight_yellow(), ansi_highlight_off(),
52990c2e
ZJS
3428 s2, s1 ? "; " : "", s1 ? s1 : "");
3429 if (i->failed_condition_trigger)
3430 printf(" none of the trigger conditions were met\n");
3431 else if (i->failed_condition)
3432 printf(" %s=%s%s was not met\n",
3433 i->failed_condition,
3434 i->failed_condition_negate ? "!" : "",
59fccdc5
LP
3435 i->failed_condition_parameter);
3436 }
3437
3438 if (!i->assert_result && i->assert_timestamp > 0) {
3439 s1 = format_timestamp_relative(since1, sizeof(since1), i->assert_timestamp);
3440 s2 = format_timestamp(since2, sizeof(since2), i->assert_timestamp);
3441
3442 printf(" Assert: start %sassertion failed%s at %s%s%s\n",
3443 ansi_highlight_red(), ansi_highlight_off(),
3444 s2, s1 ? "; " : "", s1 ? s1 : "");
3445 if (i->failed_assert_trigger)
3446 printf(" none of the trigger assertions were met\n");
3447 else if (i->failed_assert)
3448 printf(" %s=%s%s was not met\n",
3449 i->failed_assert,
3450 i->failed_assert_negate ? "!" : "",
3451 i->failed_assert_parameter);
90bbc946
LP
3452 }
3453
61cbdc4b 3454 if (i->sysfs_path)
856323c9 3455 printf(" Device: %s\n", i->sysfs_path);
9feeba4b 3456 if (i->where)
856323c9 3457 printf(" Where: %s\n", i->where);
9feeba4b 3458 if (i->what)
856323c9 3459 printf(" What: %s\n", i->what);
49dbfa7b 3460
13160134 3461 STRV_FOREACH(t, i->documentation)
856323c9 3462 printf(" %*s %s\n", 9, t == i->documentation ? "Docs:" : "", *t);
49dbfa7b 3463
13160134 3464 STRV_FOREACH_PAIR(t, t2, i->listen)
856323c9 3465 printf(" %*s %s (%s)\n", 9, t == i->listen ? "Listen:" : "", *t2, *t);
67419600 3466
b8131a87 3467 if (i->accept)
856323c9 3468 printf(" Accepted: %u; Connected: %u\n", i->n_accepted, i->n_connections);
61cbdc4b 3469
582a507f 3470 LIST_FOREACH(exec, p, i->exec) {
13160134 3471 _cleanup_free_ char *argv = NULL;
9a57c629 3472 bool good;
582a507f
LP
3473
3474 /* Only show exited processes here */
3475 if (p->code == 0)
3476 continue;
3477
13160134 3478 argv = strv_join(p->argv, " ");
856323c9 3479 printf(" Process: %u %s=%s ", p->pid, p->name, strna(argv));
582a507f 3480
96342de6 3481 good = is_clean_exit_lsb(p->code, p->status, NULL);
9a57c629 3482 if (!good) {
0b5a519c
DS
3483 on = ansi_highlight_red();
3484 off = ansi_highlight_off();
9a57c629
LP
3485 } else
3486 on = off = "";
3487
3488 printf("%s(code=%s, ", on, sigchld_code_to_string(p->code));
3489
d06dacd0
LP
3490 if (p->code == CLD_EXITED) {
3491 const char *c;
3492
582a507f 3493 printf("status=%i", p->status);
d06dacd0 3494
1b64d026
LP
3495 c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD);
3496 if (c)
d06dacd0
LP
3497 printf("/%s", c);
3498
3499 } else
582a507f 3500 printf("signal=%s", signal_to_string(p->status));
9a57c629
LP
3501
3502 printf(")%s\n", off);
3503
582a507f
LP
3504 if (i->main_pid == p->pid &&
3505 i->start_timestamp == p->start_timestamp &&
3506 i->exit_timestamp == p->start_timestamp)
3507 /* Let's not show this twice */
3508 i->main_pid = 0;
3509
3510 if (p->pid == i->control_pid)
3511 i->control_pid = 0;
3512 }
3513
61cbdc4b 3514 if (i->main_pid > 0 || i->control_pid > 0) {
61cbdc4b 3515 if (i->main_pid > 0) {
8c06592f 3516 printf(" Main PID: "PID_FMT, i->main_pid);
61cbdc4b
LP
3517
3518 if (i->running) {
13160134
ZJS
3519 _cleanup_free_ char *comm = NULL;
3520 get_process_comm(i->main_pid, &comm);
3521 if (comm)
3522 printf(" (%s)", comm);
6d4fc029 3523 } else if (i->exit_code > 0) {
61cbdc4b
LP
3524 printf(" (code=%s, ", sigchld_code_to_string(i->exit_code));
3525
d06dacd0
LP
3526 if (i->exit_code == CLD_EXITED) {
3527 const char *c;
3528
61cbdc4b 3529 printf("status=%i", i->exit_status);
d06dacd0 3530
1b64d026
LP
3531 c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD);
3532 if (c)
d06dacd0
LP
3533 printf("/%s", c);
3534
3535 } else
582a507f 3536 printf("signal=%s", signal_to_string(i->exit_status));
6d4fc029
LP
3537 printf(")");
3538 }
61cbdc4b 3539
13160134
ZJS
3540 if (i->control_pid > 0)
3541 printf(";");
3542 }
61cbdc4b
LP
3543
3544 if (i->control_pid > 0) {
13160134 3545 _cleanup_free_ char *c = NULL;
61cbdc4b 3546
8c06592f 3547 printf(" %8s: "PID_FMT, i->main_pid ? "" : " Control", i->control_pid);
61cbdc4b 3548
13160134
ZJS
3549 get_process_comm(i->control_pid, &c);
3550 if (c)
3551 printf(" (%s)", c);
61cbdc4b
LP
3552 }
3553
3554 printf("\n");
3555 }
3556
17bb7382 3557 if (i->status_text)
856323c9 3558 printf(" Status: \"%s\"\n", i->status_text);
b4af5a80
LP
3559 if (i->status_errno > 0)
3560 printf(" Error: %i (%s)\n", i->status_errno, strerror(i->status_errno));
17bb7382 3561
4ad49000 3562 if (i->control_group &&
8fcf784d
LP
3563 (i->main_pid > 0 || i->control_pid > 0 ||
3564 ((arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_CONTAINER) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group, false) == 0))) {
ab35fb1b
LP
3565 unsigned c;
3566
4ad49000 3567 printf(" CGroup: %s\n", i->control_group);
ab35fb1b 3568
7af5a806 3569 if (arg_transport == BUS_TRANSPORT_LOCAL || arg_transport == BUS_TRANSPORT_CONTAINER) {
b69d29ce
LP
3570 unsigned k = 0;
3571 pid_t extra[2];
8fcf784d 3572 static const char prefix[] = " ";
b69d29ce
LP
3573
3574 c = columns();
e8853816
ZJS
3575 if (c > sizeof(prefix) - 1)
3576 c -= sizeof(prefix) - 1;
a8f11321
LP
3577 else
3578 c = 0;
ab35fb1b 3579
b69d29ce
LP
3580 if (i->main_pid > 0)
3581 extra[k++] = i->main_pid;
3582
3583 if (i->control_pid > 0)
3584 extra[k++] = i->control_pid;
3585
8fcf784d 3586 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, false, extra, k, flags);
a8f11321 3587 }
c59760ee 3588 }
45fb0699 3589
f459b602 3590 if (i->id && arg_transport == BUS_TRANSPORT_LOCAL) {
886a64fe
ZJS
3591 show_journal_by_unit(stdout,
3592 i->id,
3593 arg_output,
3594 0,
3595 i->inactive_exit_timestamp_monotonic,
3596 arg_lines,
3597 getuid(),
ea6c2dd1 3598 flags | OUTPUT_BEGIN_NEWLINE,
94e0bd7d
ZJS
3599 arg_scope == UNIT_FILE_SYSTEM,
3600 ellipsized);
6f003b43 3601 }
86aa7ba4 3602
45fb0699 3603 if (i->need_daemon_reload)
1058cbf2 3604 printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %sdaemon-reload' recommended.\n",
0b5a519c
DS
3605 ansi_highlight_red(),
3606 ansi_highlight_off(),
1058cbf2 3607 arg_scope == UNIT_FILE_SYSTEM ? "" : "--user ");
61cbdc4b
LP
3608}
3609
b43f208f 3610static void show_unit_help(UnitStatusInfo *i) {
256425cc
LP
3611 char **p;
3612
3613 assert(i);
3614
3615 if (!i->documentation) {
3616 log_info("Documentation for %s not known.", i->id);
3617 return;
3618 }
3619
78002a67
ZJS
3620 STRV_FOREACH(p, i->documentation)
3621 if (startswith(*p, "man:"))
3622 show_man_page(*p + 4, false);
3623 else
0315fe37 3624 log_info("Can't show: %s", *p);
256425cc
LP
3625}
3626
f459b602
MAP
3627static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *i, const char *contents) {
3628 int r;
61cbdc4b 3629
a4c279f8 3630 assert(name);
f459b602 3631 assert(m);
a4c279f8
LP
3632 assert(i);
3633
f459b602 3634 switch (contents[0]) {
61cbdc4b 3635
f459b602 3636 case SD_BUS_TYPE_STRING: {
61cbdc4b
LP
3637 const char *s;
3638
f459b602
MAP
3639 r = sd_bus_message_read(m, "s", &s);
3640 if (r < 0)
3641 return bus_log_parse_error(r);
61cbdc4b 3642
a4c279f8 3643 if (!isempty(s)) {
61cbdc4b
LP
3644 if (streq(name, "Id"))
3645 i->id = s;
3646 else if (streq(name, "LoadState"))
3647 i->load_state = s;
3648 else if (streq(name, "ActiveState"))
3649 i->active_state = s;
3650 else if (streq(name, "SubState"))
3651 i->sub_state = s;
3652 else if (streq(name, "Description"))
3653 i->description = s;
3654 else if (streq(name, "FragmentPath"))
1b64d026
LP
3655 i->fragment_path = s;
3656 else if (streq(name, "SourcePath"))
3657 i->source_path = s;
286ca485 3658#ifndef NOLEGACY
a00963a2
LP
3659 else if (streq(name, "DefaultControlGroup")) {
3660 const char *e;
3661 e = startswith(s, SYSTEMD_CGROUP_CONTROLLER ":");
3662 if (e)
3663 i->control_group = e;
3664 }
4ad49000
LP
3665#endif
3666 else if (streq(name, "ControlGroup"))
3667 i->control_group = s;
61cbdc4b
LP
3668 else if (streq(name, "StatusText"))
3669 i->status_text = s;
175728c4
HH
3670 else if (streq(name, "PIDFile"))
3671 i->pid_file = s;
61cbdc4b
LP
3672 else if (streq(name, "SysFSPath"))
3673 i->sysfs_path = s;
3674 else if (streq(name, "Where"))
3675 i->where = s;
3676 else if (streq(name, "What"))
3677 i->what = s;
4a9e2fff
LP
3678 else if (streq(name, "Following"))
3679 i->following = s;
a4375746
LP
3680 else if (streq(name, "UnitFileState"))
3681 i->unit_file_state = s;
f42806df
LP
3682 else if (streq(name, "Result"))
3683 i->result = s;
61cbdc4b
LP
3684 }
3685
3686 break;
3687 }
3688
f459b602
MAP
3689 case SD_BUS_TYPE_BOOLEAN: {
3690 int b;
b8131a87 3691
f459b602
MAP
3692 r = sd_bus_message_read(m, "b", &b);
3693 if (r < 0)
3694 return bus_log_parse_error(r);
b8131a87
LP
3695
3696 if (streq(name, "Accept"))
3697 i->accept = b;
45fb0699
LP
3698 else if (streq(name, "NeedDaemonReload"))
3699 i->need_daemon_reload = b;
90bbc946
LP
3700 else if (streq(name, "ConditionResult"))
3701 i->condition_result = b;
59fccdc5
LP
3702 else if (streq(name, "AssertResult"))
3703 i->assert_result = b;
b8131a87
LP
3704
3705 break;
3706 }
3707
f459b602 3708 case SD_BUS_TYPE_UINT32: {
61cbdc4b
LP
3709 uint32_t u;
3710
f459b602
MAP
3711 r = sd_bus_message_read(m, "u", &u);
3712 if (r < 0)
3713 return bus_log_parse_error(r);
61cbdc4b
LP
3714
3715 if (streq(name, "MainPID")) {
3716 if (u > 0) {
3717 i->main_pid = (pid_t) u;
3718 i->running = true;
3719 }
3720 } else if (streq(name, "ControlPID"))
3721 i->control_pid = (pid_t) u;
3722 else if (streq(name, "ExecMainPID")) {
3723 if (u > 0)
3724 i->main_pid = (pid_t) u;
3725 } else if (streq(name, "NAccepted"))
3726 i->n_accepted = u;
3727 else if (streq(name, "NConnections"))
3728 i->n_connections = u;
3729
3730 break;
3731 }
3732
f459b602 3733 case SD_BUS_TYPE_INT32: {
61cbdc4b
LP
3734 int32_t j;
3735
f459b602
MAP
3736 r = sd_bus_message_read(m, "i", &j);
3737 if (r < 0)
3738 return bus_log_parse_error(r);
61cbdc4b
LP
3739
3740 if (streq(name, "ExecMainCode"))
3741 i->exit_code = (int) j;
3742 else if (streq(name, "ExecMainStatus"))
3743 i->exit_status = (int) j;
b4af5a80
LP
3744 else if (streq(name, "StatusErrno"))
3745 i->status_errno = (int) j;
61cbdc4b
LP
3746
3747 break;
3748 }
3749
f459b602 3750 case SD_BUS_TYPE_UINT64: {
61cbdc4b
LP
3751 uint64_t u;
3752
f459b602
MAP
3753 r = sd_bus_message_read(m, "t", &u);
3754 if (r < 0)
3755 return bus_log_parse_error(r);
61cbdc4b
LP
3756
3757 if (streq(name, "ExecMainStartTimestamp"))
3758 i->start_timestamp = (usec_t) u;
3759 else if (streq(name, "ExecMainExitTimestamp"))
3760 i->exit_timestamp = (usec_t) u;
584be568
LP
3761 else if (streq(name, "ActiveEnterTimestamp"))
3762 i->active_enter_timestamp = (usec_t) u;
3763 else if (streq(name, "InactiveEnterTimestamp"))
3764 i->inactive_enter_timestamp = (usec_t) u;
3765 else if (streq(name, "InactiveExitTimestamp"))
3766 i->inactive_exit_timestamp = (usec_t) u;
df50185b
LP
3767 else if (streq(name, "InactiveExitTimestampMonotonic"))
3768 i->inactive_exit_timestamp_monotonic = (usec_t) u;
584be568
LP
3769 else if (streq(name, "ActiveExitTimestamp"))
3770 i->active_exit_timestamp = (usec_t) u;
90bbc946
LP
3771 else if (streq(name, "ConditionTimestamp"))
3772 i->condition_timestamp = (usec_t) u;
59fccdc5
LP
3773 else if (streq(name, "AssertTimestamp"))
3774 i->assert_timestamp = (usec_t) u;
61cbdc4b
LP
3775
3776 break;
3777 }
582a507f 3778
f459b602 3779 case SD_BUS_TYPE_ARRAY:
582a507f 3780
f459b602
MAP
3781 if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && startswith(name, "Exec")) {
3782 _cleanup_free_ ExecStatusInfo *info = NULL;
582a507f 3783
f459b602
MAP
3784 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sasbttttuii)");
3785 if (r < 0)
3786 return bus_log_parse_error(r);
582a507f 3787
f459b602
MAP
3788 info = new0(ExecStatusInfo, 1);
3789 if (!info)
3790 return log_oom();
582a507f 3791
f459b602 3792 while ((r = exec_status_info_deserialize(m, info)) > 0) {
0129173a 3793
f459b602
MAP
3794 info->name = strdup(name);
3795 if (!info->name)
3796 log_oom();
582a507f 3797
71fda00f 3798 LIST_PREPEND(exec, i->exec, info);
582a507f 3799
f459b602
MAP
3800 info = new0(ExecStatusInfo, 1);
3801 if (!info)
3802 log_oom();
49dbfa7b 3803 }
67419600 3804
f459b602
MAP
3805 if (r < 0)
3806 return bus_log_parse_error(r);
67419600 3807
f459b602
MAP
3808 r = sd_bus_message_exit_container(m);
3809 if (r < 0)
3810 return bus_log_parse_error(r);
67419600 3811
f459b602 3812 return 0;
67419600 3813
f459b602
MAP
3814 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Listen")) {
3815 const char *type, *path;
13160134 3816
f459b602
MAP
3817 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
3818 if (r < 0)
3819 return bus_log_parse_error(r);
67419600 3820
f459b602 3821 while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) {
67419600 3822
f459b602
MAP
3823 r = strv_extend(&i->listen, type);
3824 if (r < 0)
3825 return r;
67419600 3826
f459b602
MAP
3827 r = strv_extend(&i->listen, path);
3828 if (r < 0)
3829 return r;
3830 }
76d14b87 3831 if (r < 0)
f459b602 3832 return bus_log_parse_error(r);
76d14b87 3833
f459b602
MAP
3834 r = sd_bus_message_exit_container(m);
3835 if (r < 0)
3836 return bus_log_parse_error(r);
49dbfa7b 3837
f459b602 3838 return 0;
49dbfa7b 3839
f459b602 3840 } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "DropInPaths")) {
49dbfa7b 3841
f459b602
MAP
3842 r = sd_bus_message_read_strv(m, &i->dropin_paths);
3843 if (r < 0)
3844 return bus_log_parse_error(r);
49dbfa7b 3845
f459b602 3846 } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "Documentation")) {
49dbfa7b 3847
f459b602
MAP
3848 r = sd_bus_message_read_strv(m, &i->documentation);
3849 if (r < 0)
3850 return bus_log_parse_error(r);
52990c2e 3851
f459b602
MAP
3852 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Conditions")) {
3853 const char *cond, *param;
3854 int trigger, negate;
3855 int32_t state;
52990c2e 3856
f459b602
MAP
3857 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sbbsi)");
3858 if (r < 0)
3859 return bus_log_parse_error(r);
3860
3861 while ((r = sd_bus_message_read(m, "(sbbsi)", &cond, &trigger, &negate, &param, &state)) > 0) {
3862 log_debug("%s %d %d %s %d", cond, trigger, negate, param, state);
3863 if (state < 0 && (!trigger || !i->failed_condition)) {
3864 i->failed_condition = cond;
3865 i->failed_condition_trigger = trigger;
3866 i->failed_condition_negate = negate;
59fccdc5
LP
3867 i->failed_condition_parameter = param;
3868 }
3869 }
3870 if (r < 0)
3871 return bus_log_parse_error(r);
3872
3873 r = sd_bus_message_exit_container(m);
3874 if (r < 0)
3875 return bus_log_parse_error(r);
3876
3877 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Asserts")) {
3878 const char *cond, *param;
3879 int trigger, negate;
3880 int32_t state;
3881
3882 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sbbsi)");
3883 if (r < 0)
3884 return bus_log_parse_error(r);
3885
3886 while ((r = sd_bus_message_read(m, "(sbbsi)", &cond, &trigger, &negate, &param, &state)) > 0) {
3887 log_debug("%s %d %d %s %d", cond, trigger, negate, param, state);
3888 if (state < 0 && (!trigger || !i->failed_assert)) {
3889 i->failed_assert = cond;
3890 i->failed_assert_trigger = trigger;
3891 i->failed_assert_negate = negate;
3892 i->failed_assert_parameter = param;
f459b602 3893 }
582a507f 3894 }
f459b602
MAP
3895 if (r < 0)
3896 return bus_log_parse_error(r);
3897
3898 r = sd_bus_message_exit_container(m);
3899 if (r < 0)
3900 return bus_log_parse_error(r);
3901
3902 } else
3903 goto skip;
582a507f
LP
3904
3905 break;
9f39404c 3906
f459b602 3907 case SD_BUS_TYPE_STRUCT_BEGIN:
9f39404c
LP
3908
3909 if (streq(name, "LoadError")) {
9f39404c 3910 const char *n, *message;
9f39404c 3911
f459b602 3912 r = sd_bus_message_read(m, "(ss)", &n, &message);
9f39404c 3913 if (r < 0)
f459b602 3914 return bus_log_parse_error(r);
9f39404c
LP
3915
3916 if (!isempty(message))
3917 i->load_error = message;
f459b602
MAP
3918 } else
3919 goto skip;
9f39404c
LP
3920
3921 break;
f459b602
MAP
3922
3923 default:
3924 goto skip;
9f39404c 3925 }
f459b602
MAP
3926
3927 return 0;
3928
3929skip:
3930 r = sd_bus_message_skip(m, contents);
3931 if (r < 0)
3932 return bus_log_parse_error(r);
61cbdc4b
LP
3933
3934 return 0;
3935}
3936
f459b602
MAP
3937static int print_property(const char *name, sd_bus_message *m, const char *contents) {
3938 int r;
3939
48220598 3940 assert(name);
f459b602 3941 assert(m);
48220598 3942
61cbdc4b
LP
3943 /* This is a low-level property printer, see
3944 * print_status_info() for the nicer output */
3945
852c1b4d
ZJS
3946 if (arg_properties && !strv_find(arg_properties, name)) {
3947 /* skip what we didn't read */
3948 r = sd_bus_message_skip(m, contents);
3949 return r;
3950 }
48220598 3951
f459b602 3952 switch (contents[0]) {
48220598 3953
f459b602 3954 case SD_BUS_TYPE_STRUCT_BEGIN:
48220598 3955
f459b602 3956 if (contents[1] == SD_BUS_TYPE_UINT32 && streq(name, "Job")) {
48220598
LP
3957 uint32_t u;
3958
f459b602
MAP
3959 r = sd_bus_message_read(m, "(uo)", &u, NULL);
3960 if (r < 0)
3961 return bus_log_parse_error(r);
48220598 3962
f459b602 3963 if (u > 0)
8c06592f 3964 printf("%s=%"PRIu32"\n", name, u);
48220598
LP
3965 else if (arg_all)
3966 printf("%s=\n", name);
3967
3968 return 0;
f459b602
MAP
3969
3970 } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "Unit")) {
48220598
LP
3971 const char *s;
3972
f459b602
MAP
3973 r = sd_bus_message_read(m, "(so)", &s, NULL);
3974 if (r < 0)
3975 return bus_log_parse_error(r);
48220598 3976
f459b602 3977 if (arg_all || !isempty(s))
48220598
LP
3978 printf("%s=%s\n", name, s);
3979
3980 return 0;
f459b602
MAP
3981
3982 } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "LoadError")) {
9f39404c
LP
3983 const char *a = NULL, *b = NULL;
3984
f459b602
MAP
3985 r = sd_bus_message_read(m, "(ss)", &a, &b);
3986 if (r < 0)
3987 return bus_log_parse_error(r);
9f39404c
LP
3988
3989 if (arg_all || !isempty(a) || !isempty(b))
3990 printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b));
f786e80d 3991
57183d11
LP
3992 return 0;
3993 } else if (streq_ptr(name, "SystemCallFilter")) {
3994 _cleanup_strv_free_ char **l = NULL;
3995 int whitelist;
3996
3997 r = sd_bus_message_enter_container(m, 'r', "bas");
3998 if (r < 0)
3999 return bus_log_parse_error(r);
4000
4001 r = sd_bus_message_read(m, "b", &whitelist);
4002 if (r < 0)
4003 return bus_log_parse_error(r);
4004
4005 r = sd_bus_message_read_strv(m, &l);
4006 if (r < 0)
4007 return bus_log_parse_error(r);
4008
4009 r = sd_bus_message_exit_container(m);
4010 if (r < 0)
4011 return bus_log_parse_error(r);
4012
4013 if (arg_all || whitelist || !strv_isempty(l)) {
4014 bool first = true;
4015 char **i;
4016
4017 fputs(name, stdout);
4018 fputc('=', stdout);
4019
4020 if (!whitelist)
4021 fputc('~', stdout);
4022
4023 STRV_FOREACH(i, l) {
4024 if (first)
4025 first = false;
4026 else
4027 fputc(' ', stdout);
4028
4029 fputs(*i, stdout);
4030 }
4031 fputc('\n', stdout);
4032 }
4033
f786e80d 4034 return 0;
48220598
LP
4035 }
4036
4037 break;
48220598 4038
f459b602 4039 case SD_BUS_TYPE_ARRAY:
48220598 4040
f459b602
MAP
4041 if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "EnvironmentFiles")) {
4042 const char *path;
4043 int ignore;
8c7be95e 4044
f459b602
MAP
4045 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sb)");
4046 if (r < 0)
4047 return bus_log_parse_error(r);
8c7be95e 4048
f459b602
MAP
4049 while ((r = sd_bus_message_read(m, "(sb)", &path, &ignore)) > 0)
4050 printf("EnvironmentFile=%s (ignore_errors=%s)\n", path, yes_no(ignore));
8c7be95e 4051
f459b602
MAP
4052 if (r < 0)
4053 return bus_log_parse_error(r);
8c7be95e 4054
f459b602
MAP
4055 r = sd_bus_message_exit_container(m);
4056 if (r < 0)
4057 return bus_log_parse_error(r);
8c7be95e
LP
4058
4059 return 0;
4060
f459b602
MAP
4061 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Paths")) {
4062 const char *type, *path;
67419600 4063
f459b602
MAP
4064 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
4065 if (r < 0)
4066 return bus_log_parse_error(r);
ebf57b80 4067
f459b602
MAP
4068 while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0)
4069 printf("%s=%s\n", type, path);
4070 if (r < 0)
4071 return bus_log_parse_error(r);
ebf57b80 4072
f459b602
MAP
4073 r = sd_bus_message_exit_container(m);
4074 if (r < 0)
4075 return bus_log_parse_error(r);
ebf57b80 4076
707e5e52 4077 return 0;
582a507f 4078
f459b602
MAP
4079 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Listen")) {
4080 const char *type, *path;
67419600 4081
f459b602
MAP
4082 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
4083 if (r < 0)
4084 return bus_log_parse_error(r);
67419600 4085
f459b602
MAP
4086 while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0)
4087 printf("Listen%s=%s\n", type, path);
4088 if (r < 0)
4089 return bus_log_parse_error(r);
67419600 4090
f459b602
MAP
4091 r = sd_bus_message_exit_container(m);
4092 if (r < 0)
4093 return bus_log_parse_error(r);
67419600
OS
4094
4095 return 0;
4096
f459b602
MAP
4097 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Timers")) {
4098 const char *base;
4099 uint64_t value, next_elapse;
707e5e52 4100
f459b602
MAP
4101 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(stt)");
4102 if (r < 0)
4103 return bus_log_parse_error(r);
552e4331 4104
f459b602
MAP
4105 while ((r = sd_bus_message_read(m, "(stt)", &base, &value, &next_elapse)) > 0) {
4106 char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX];
fe68089d 4107
f459b602
MAP
4108 printf("%s={ value=%s ; next_elapse=%s }\n",
4109 base,
4110 format_timespan(timespan1, sizeof(timespan1), value, 0),
4111 format_timespan(timespan2, sizeof(timespan2), next_elapse, 0));
fe68089d 4112 }
f459b602
MAP
4113 if (r < 0)
4114 return bus_log_parse_error(r);
4115
4116 r = sd_bus_message_exit_container(m);
4117 if (r < 0)
4118 return bus_log_parse_error(r);
fe68089d
LP
4119
4120 return 0;
fe68089d 4121
f459b602
MAP
4122 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && startswith(name, "Exec")) {
4123 ExecStatusInfo info = {};
4124
4125 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sasbttttuii)");
4126 if (r < 0)
4127 return bus_log_parse_error(r);
4128
4129 while ((r = exec_status_info_deserialize(m, &info)) > 0) {
4130 char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
4131 _cleanup_free_ char *tt;
4132
4133 tt = strv_join(info.argv, " ");
4134
8c06592f 4135 printf("%s={ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid="PID_FMT" ; code=%s ; status=%i%s%s }\n",
f459b602
MAP
4136 name,
4137 strna(info.path),
4138 strna(tt),
4139 yes_no(info.ignore),
4140 strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
4141 strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
8c06592f 4142 info.pid,
f459b602
MAP
4143 sigchld_code_to_string(info.code),
4144 info.status,
4145 info.code == CLD_EXITED ? "" : "/",
4146 strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
fe68089d 4147
582a507f
LP
4148 free(info.path);
4149 strv_free(info.argv);
f459b602 4150 zero(info);
707e5e52
LP
4151 }
4152
f459b602
MAP
4153 r = sd_bus_message_exit_container(m);
4154 if (r < 0)
4155 return bus_log_parse_error(r);
4156
48220598 4157 return 0;
4ad49000 4158
f459b602
MAP
4159 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "DeviceAllow")) {
4160 const char *path, *rwm;
4ad49000 4161
f459b602
MAP
4162 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
4163 if (r < 0)
4164 return bus_log_parse_error(r);
4ad49000 4165
f459b602
MAP
4166 while ((r = sd_bus_message_read(m, "(ss)", &path, &rwm)) > 0)
4167 printf("%s=%s %s\n", name, strna(path), strna(rwm));
4168 if (r < 0)
4169 return bus_log_parse_error(r);
4ad49000 4170
f459b602
MAP
4171 r = sd_bus_message_exit_container(m);
4172 if (r < 0)
4173 return bus_log_parse_error(r);
4ad49000 4174
4ad49000
LP
4175 return 0;
4176
f459b602
MAP
4177 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "BlockIODeviceWeight")) {
4178 const char *path;
4179 uint64_t weight;
b8ab2dc6 4180
f459b602
MAP
4181 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(st)");
4182 if (r < 0)
4183 return bus_log_parse_error(r);
b8ab2dc6 4184
f459b602
MAP
4185 while ((r = sd_bus_message_read(m, "(st)", &path, &weight)) > 0)
4186 printf("%s=%s %" PRIu64 "\n", name, strna(path), weight);
4187 if (r < 0)
4188 return bus_log_parse_error(r);
b8ab2dc6 4189
f459b602
MAP
4190 r = sd_bus_message_exit_container(m);
4191 if (r < 0)
4192 return bus_log_parse_error(r);
b8ab2dc6 4193
b8ab2dc6
G
4194 return 0;
4195
f459b602
MAP
4196 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth"))) {
4197 const char *path;
4198 uint64_t bandwidth;
4ad49000 4199
f459b602
MAP
4200 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(st)");
4201 if (r < 0)
4202 return bus_log_parse_error(r);
4ad49000 4203
f459b602
MAP
4204 while ((r = sd_bus_message_read(m, "(st)", &path, &bandwidth)) > 0)
4205 printf("%s=%s %" PRIu64 "\n", name, strna(path), bandwidth);
4206 if (r < 0)
4207 return bus_log_parse_error(r);
4ad49000 4208
f459b602
MAP
4209 r = sd_bus_message_exit_container(m);
4210 if (r < 0)
4211 return bus_log_parse_error(r);
4ad49000 4212
4ad49000 4213 return 0;
48220598
LP
4214 }
4215
4216 break;
4217 }
4218
f459b602
MAP
4219 r = bus_print_property(name, m, arg_all);
4220 if (r < 0)
4221 return bus_log_parse_error(r);
4222
4223 if (r == 0) {
4224 r = sd_bus_message_skip(m, contents);
4225 if (r < 0)
4226 return bus_log_parse_error(r);
a4c279f8 4227
f459b602
MAP
4228 if (arg_all)
4229 printf("%s=[unprintable]\n", name);
4230 }
48220598
LP
4231
4232 return 0;
4233}
4234
f459b602
MAP
4235static int show_one(
4236 const char *verb,
4237 sd_bus *bus,
4238 const char *path,
4239 bool show_properties,
4240 bool *new_line,
4241 bool *ellipsized) {
4242
4243 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
4244 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
b92bea5d 4245 UnitStatusInfo info = {};
582a507f 4246 ExecStatusInfo *p;
f459b602 4247 int r;
48220598 4248
48220598 4249 assert(path);
61cbdc4b 4250 assert(new_line);
48220598 4251
e3e0314b
ZJS
4252 log_debug("Showing one %s", path);
4253
f459b602 4254 r = sd_bus_call_method(
f22f08cd
SP
4255 bus,
4256 "org.freedesktop.systemd1",
4257 path,
4258 "org.freedesktop.DBus.Properties",
4259 "GetAll",
f459b602 4260 &error,
f22f08cd 4261 &reply,
f459b602
MAP
4262 "s", "");
4263 if (r < 0) {
4264 log_error("Failed to get properties: %s", bus_error_message(&error, r));
f84190d8 4265 return r;
48220598
LP
4266 }
4267
f459b602
MAP
4268 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
4269 if (r < 0)
4270 return bus_log_parse_error(r);
48220598 4271
61cbdc4b
LP
4272 if (*new_line)
4273 printf("\n");
4274
4275 *new_line = true;
4276
f459b602
MAP
4277 while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
4278 const char *name, *contents;
0183528f 4279
f459b602
MAP
4280 r = sd_bus_message_read(reply, "s", &name);
4281 if (r < 0)
4282 return bus_log_parse_error(r);
48220598 4283
f459b602
MAP
4284 r = sd_bus_message_peek_type(reply, NULL, &contents);
4285 if (r < 0)
4286 return bus_log_parse_error(r);
4287
4288 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
4289 if (r < 0)
4290 return bus_log_parse_error(r);
48220598 4291
61cbdc4b 4292 if (show_properties)
f459b602 4293 r = print_property(name, reply, contents);
61cbdc4b 4294 else
f459b602
MAP
4295 r = status_property(name, reply, &info, contents);
4296 if (r < 0)
4297 return r;
48220598 4298
f459b602
MAP
4299 r = sd_bus_message_exit_container(reply);
4300 if (r < 0)
4301 return bus_log_parse_error(r);
4302
4303 r = sd_bus_message_exit_container(reply);
4304 if (r < 0)
4305 return bus_log_parse_error(r);
48220598 4306 }
f459b602
MAP
4307 if (r < 0)
4308 return bus_log_parse_error(r);
4309
4310 r = sd_bus_message_exit_container(reply);
4311 if (r < 0)
4312 return bus_log_parse_error(r);
48220598 4313
f1e36d67
LP
4314 r = 0;
4315
256425cc 4316 if (!show_properties) {
b43f208f
KS
4317 if (streq(verb, "help"))
4318 show_unit_help(&info);
256425cc 4319 else
94e0bd7d 4320 print_status_info(&info, ellipsized);
256425cc 4321 }
f1e36d67 4322
49dbfa7b 4323 strv_free(info.documentation);
76d14b87 4324 strv_free(info.dropin_paths);
67419600 4325 strv_free(info.listen);
49dbfa7b 4326
22f4096c 4327 if (!streq_ptr(info.active_state, "active") &&
be8088a2 4328 !streq_ptr(info.active_state, "reloading") &&
3b05b8b3 4329 streq(verb, "status")) {
22f4096c 4330 /* According to LSB: "program not running" */
175728c4 4331 /* 0: program is running or service is OK
41a55c46
ZJS
4332 * 1: program is dead and /run PID file exists
4333 * 2: program is dead and /run/lock lock file exists
175728c4
HH
4334 * 3: program is not running
4335 * 4: program or service status is unknown
4336 */
3b05b8b3 4337 if (info.pid_file && access(info.pid_file, F_OK) == 0)
175728c4
HH
4338 r = 1;
4339 else
4340 r = 3;
e9c1ea9d 4341 }
61cbdc4b 4342
582a507f 4343 while ((p = info.exec)) {
71fda00f 4344 LIST_REMOVE(exec, info.exec, p);
582a507f
LP
4345 exec_status_info_free(p);
4346 }
4347
48220598
LP
4348 return r;
4349}
4350
f74294c1 4351static int get_unit_dbus_path_by_pid(
f459b602
MAP
4352 sd_bus *bus,
4353 uint32_t pid,
f74294c1 4354 char **unit) {
f459b602
MAP
4355
4356 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
4357 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
373d32c9 4358 char *u;
a223b325
MS
4359 int r;
4360
f459b602 4361 r = sd_bus_call_method(
f22f08cd
SP
4362 bus,
4363 "org.freedesktop.systemd1",
4364 "/org/freedesktop/systemd1",
4365 "org.freedesktop.systemd1.Manager",
4366 "GetUnitByPID",
f459b602 4367 &error,
f22f08cd 4368 &reply,
f459b602
MAP
4369 "u", pid);
4370 if (r < 0) {
de0671ee 4371 log_error("Failed to get unit for PID "PID_FMT": %s", pid, bus_error_message(&error, r));
cec7eda5 4372 return r;
a223b325
MS
4373 }
4374
373d32c9 4375 r = sd_bus_message_read(reply, "o", &u);
f459b602
MAP
4376 if (r < 0)
4377 return bus_log_parse_error(r);
4378
373d32c9
LP
4379 u = strdup(u);
4380 if (!u)
4381 return log_oom();
4382
4383 *unit = u;
f74294c1 4384 return 0;
a223b325
MS
4385}
4386
f459b602
MAP
4387static int show_all(
4388 const char* verb,
4389 sd_bus *bus,
4390 bool show_properties,
4391 bool *new_line,
4392 bool *ellipsized) {
4393
f459b602
MAP
4394 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
4395 _cleanup_free_ UnitInfo *unit_infos = NULL;
4396 const UnitInfo *u;
4397 unsigned c;
5bb75bc7 4398 int r, ret = 0;
265a7a2a 4399
1238ee09 4400 r = get_unit_list(bus, NULL, NULL, &unit_infos, 0, &reply);
265a7a2a
ZJS
4401 if (r < 0)
4402 return r;
4403
dbed408b
LP
4404 pager_open_if_enabled();
4405
f459b602
MAP
4406 c = (unsigned) r;
4407
4408 qsort_safe(unit_infos, c, sizeof(UnitInfo), compare_unit_info);
991f2a39 4409
265a7a2a 4410 for (u = unit_infos; u < unit_infos + c; u++) {
7fd1b19b 4411 _cleanup_free_ char *p = NULL;
265a7a2a 4412
265a7a2a
ZJS
4413 p = unit_dbus_path_from_name(u->id);
4414 if (!p)
4415 return log_oom();
4416
94e0bd7d 4417 r = show_one(verb, bus, p, show_properties, new_line, ellipsized);
3df538da 4418 if (r < 0)
265a7a2a 4419 return r;
5bb75bc7
ZJS
4420 else if (r > 0 && ret == 0)
4421 ret = r;
265a7a2a
ZJS
4422 }
4423
5bb75bc7 4424 return ret;
265a7a2a
ZJS
4425}
4426
8fcf784d
LP
4427static int show_system_status(sd_bus *bus) {
4428 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], since2[FORMAT_TIMESTAMP_MAX];
4429 _cleanup_free_ char *hn = NULL;
4430 struct machine_info mi = {};
4431 const char *on, *off;
4432 int r;
4433
4434 hn = gethostname_malloc();
4435 if (!hn)
4436 return log_oom();
4437
4438 r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, &mi);
4439 if (r < 0) {
4440 log_error("Failed to read server status: %s", strerror(-r));
4441 return r;
4442 }
4443
8fcf784d
LP
4444 if (streq_ptr(mi.state, "degraded")) {
4445 on = ansi_highlight_red();
4446 off = ansi_highlight_off();
4447 } else if (!streq_ptr(mi.state, "running")) {
4448 on = ansi_highlight_yellow();
4449 off = ansi_highlight_off();
4450 } else
4451 on = off = "";
4452
6b01f1d3 4453 printf("%s%s%s %s\n", on, draw_special_char(DRAW_BLACK_CIRCLE), off, arg_host ? arg_host : hn);
b0d14c69 4454
8fcf784d
LP
4455 printf(" State: %s%s%s\n",
4456 on, strna(mi.state), off);
4457
4458 printf(" Jobs: %u queued\n", mi.n_jobs);
4459 printf(" Failed: %u units\n", mi.n_failed_units);
4460
4461 printf(" Since: %s; %s\n",
4462 format_timestamp(since2, sizeof(since2), mi.timestamp),
4463 format_timestamp_relative(since1, sizeof(since1), mi.timestamp));
4464
4465 printf(" CGroup: %s\n", mi.control_group ?: "/");
4466 if (arg_transport == BUS_TRANSPORT_LOCAL || arg_transport == BUS_TRANSPORT_CONTAINER) {
4467 int flags =
4468 arg_all * OUTPUT_SHOW_ALL |
4469 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
4470 on_tty() * OUTPUT_COLOR |
4471 !arg_quiet * OUTPUT_WARN_CUTOFF |
4472 arg_full * OUTPUT_FULL_WIDTH;
4473
4474 static const char prefix[] = " ";
4475 unsigned c;
4476
4477 c = columns();
4478 if (c > sizeof(prefix) - 1)
4479 c -= sizeof(prefix) - 1;
4480 else
4481 c = 0;
4482
4483 show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, false, flags);
4484 }
4485
4486 free(mi.state);
4487 free(mi.control_group);
4488
4489 return 0;
4490}
4491
f459b602 4492static int show(sd_bus *bus, char **args) {
265a7a2a 4493 bool show_properties, show_status, new_line = false;
94e0bd7d 4494 bool ellipsized = false;
e3e0314b 4495 int r, ret = 0;
48220598
LP
4496
4497 assert(bus);
4498 assert(args);
4499
256425cc 4500 show_properties = streq(args[0], "show");
265a7a2a 4501 show_status = streq(args[0], "status");
61cbdc4b 4502
ec14911e 4503 if (show_properties)
1968a360 4504 pager_open_if_enabled();
ec14911e 4505
f84190d8 4506 /* If no argument is specified inspect the manager itself */
48220598 4507
f84190d8 4508 if (show_properties && strv_length(args) <= 1)
94e0bd7d 4509 return show_one(args[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line, &ellipsized);
48220598 4510
8fcf784d
LP
4511 if (show_status && strv_length(args) <= 1) {
4512
c3441de0 4513 pager_open_if_enabled();
8fcf784d
LP
4514 show_system_status(bus);
4515 new_line = true;
4516
4517 if (arg_all)
4518 ret = show_all(args[0], bus, false, &new_line, &ellipsized);
4519 } else {
e3e0314b
ZJS
4520 _cleanup_free_ char **patterns = NULL;
4521 char **name;
4522
4523 STRV_FOREACH(name, args + 1) {
f74294c1 4524 _cleanup_free_ char *unit = NULL;
94e0bd7d 4525 uint32_t id;
48220598 4526
94e0bd7d 4527 if (safe_atou32(*name, &id) < 0) {
e3e0314b 4528 if (strv_push(&patterns, *name) < 0)
94e0bd7d 4529 return log_oom();
48220598 4530
e3e0314b 4531 continue;
94e0bd7d 4532 } else if (show_properties) {
94e0bd7d 4533 /* Interpret as job id */
f74294c1 4534 if (asprintf(&unit, "/org/freedesktop/systemd1/job/%u", id) < 0)
94e0bd7d 4535 return log_oom();
48220598 4536
94e0bd7d
ZJS
4537 } else {
4538 /* Interpret as PID */
f74294c1 4539 r = get_unit_dbus_path_by_pid(bus, id, &unit);
373d32c9 4540 if (r < 0) {
94e0bd7d 4541 ret = r;
373d32c9
LP
4542 continue;
4543 }
94e0bd7d 4544 }
f74294c1 4545
5bb75bc7
ZJS
4546 r = show_one(args[0], bus, unit, show_properties,
4547 &new_line, &ellipsized);
4548 if (r < 0)
4549 return r;
4550 else if (r > 0 && ret == 0)
4551 ret = r;
48220598 4552 }
94e0bd7d 4553
e3e0314b
ZJS
4554 if (!strv_isempty(patterns)) {
4555 _cleanup_strv_free_ char **names = NULL;
4556
4557 r = expand_names(bus, patterns, NULL, &names);
4558 if (r < 0)
4559 log_error("Failed to expand names: %s", strerror(-r));
4560
4561 STRV_FOREACH(name, names) {
4562 _cleanup_free_ char *unit;
4563
4564 unit = unit_dbus_path_from_name(*name);
4565 if (!unit)
4566 return log_oom();
4567
5bb75bc7
ZJS
4568 r = show_one(args[0], bus, unit, show_properties,
4569 &new_line, &ellipsized);
4570 if (r < 0)
4571 return r;
4572 else if (r > 0 && ret == 0)
4573 ret = r;
e3e0314b
ZJS
4574 }
4575 }
4576 }
4577
94e0bd7d
ZJS
4578 if (ellipsized && !arg_quiet)
4579 printf("Hint: Some lines were ellipsized, use -l to show in full.\n");
48220598 4580
22f4096c 4581 return ret;
0183528f
LP
4582}
4583
15ef1144 4584static int cat(sd_bus *bus, char **args) {
15ef1144
LP
4585 _cleanup_strv_free_ char **names = NULL;
4586 char **name;
4587 bool first = true;
4588 int r = 0;
4589
4590 assert(bus);
4591 assert(args);
4592
4593 r = expand_names(bus, args + 1, NULL, &names);
4594 if (r < 0)
4595 log_error("Failed to expand names: %s", strerror(-r));
4596
4597 pager_open_if_enabled();
4598
4599 STRV_FOREACH(name, names) {
4600 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
4601 _cleanup_strv_free_ char **dropin_paths = NULL;
cc19881a 4602 _cleanup_free_ char *fragment_path = NULL, *unit = NULL;
15ef1144
LP
4603 char **path;
4604
4605 unit = unit_dbus_path_from_name(*name);
4606 if (!unit)
4607 return log_oom();
4608
4609 if (need_daemon_reload(bus, *name) > 0)
4610 log_warning("Unit file of %s changed on disk. Run 'systemctl%s daemon-reload'.",
4611 *name, arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
4612
4613 r = sd_bus_get_property_string(
4614 bus,
4615 "org.freedesktop.systemd1",
4616 unit,
4617 "org.freedesktop.systemd1.Unit",
4618 "FragmentPath",
4619 &error,
4620 &fragment_path);
4621 if (r < 0) {
4622 log_warning("Failed to get FragmentPath: %s", bus_error_message(&error, r));
4623 continue;
4624 }
4625
4626 r = sd_bus_get_property_strv(
4627 bus,
4628 "org.freedesktop.systemd1",
4629 unit,
4630 "org.freedesktop.systemd1.Unit",
4631 "DropInPaths",
4632 &error,
4633 &dropin_paths);
4634 if (r < 0) {
4635 log_warning("Failed to get DropInPaths: %s", bus_error_message(&error, r));
4636 continue;
4637 }
4638
4639 if (first)
4640 first = false;
4641 else
4642 puts("");
4643
4644 if (!isempty(fragment_path)) {
4645 printf("%s# %s%s\n",
4646 ansi_highlight_blue(),
4647 fragment_path,
4648 ansi_highlight_off());
4649 fflush(stdout);
4650
cda134ab 4651 r = copy_file_fd(fragment_path, STDOUT_FILENO);
15ef1144
LP
4652 if (r < 0) {
4653 log_warning("Failed to cat %s: %s", fragment_path, strerror(-r));
4654 continue;
4655 }
4656 }
4657
4658 STRV_FOREACH(path, dropin_paths) {
4659 printf("%s%s# %s%s\n",
4660 isempty(fragment_path) && path == dropin_paths ? "" : "\n",
4661 ansi_highlight_blue(),
4662 *path,
4663 ansi_highlight_off());
4664 fflush(stdout);
4665
cda134ab 4666 r = copy_file_fd(*path, STDOUT_FILENO);
15ef1144
LP
4667 if (r < 0) {
4668 log_warning("Failed to cat %s: %s", *path, strerror(-r));
4669 continue;
4670 }
4671 }
4672 }
4673
4674 return r < 0 ? r : 0;
4675}
4676
f459b602
MAP
4677static int set_property(sd_bus *bus, char **args) {
4678 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
4679 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
68372da6 4680 _cleanup_free_ char *n = NULL;
8e2af478
LP
4681 char **i;
4682 int r;
4683
f459b602
MAP
4684 r = sd_bus_message_new_method_call(
4685 bus,
151b9b96 4686 &m,
8e2af478
LP
4687 "org.freedesktop.systemd1",
4688 "/org/freedesktop/systemd1",
4689 "org.freedesktop.systemd1.Manager",
151b9b96 4690 "SetUnitProperties");
f459b602
MAP
4691 if (r < 0)
4692 return bus_log_create_error(r);
8e2af478 4693
342641fb
LP
4694 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
4695 if (r < 0)
4696 return bus_log_create_error(r);
4697
f78e6385 4698 n = unit_name_mangle(args[1], MANGLE_NOGLOB);
68372da6
LP
4699 if (!n)
4700 return log_oom();
4701
f459b602
MAP
4702 r = sd_bus_message_append(m, "sb", n, arg_runtime);
4703 if (r < 0)
4704 return bus_log_create_error(r);
8e2af478 4705
f459b602
MAP
4706 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, "(sv)");
4707 if (r < 0)
4708 return bus_log_create_error(r);
8e2af478 4709
f459b602
MAP
4710 STRV_FOREACH(i, args + 2) {
4711 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
4712 if (r < 0)
4713 return bus_log_create_error(r);
8e2af478 4714
df31a6c0 4715 r = bus_append_unit_property_assignment(m, *i);
8e2af478
LP
4716 if (r < 0)
4717 return r;
4718
f459b602
MAP
4719 r = sd_bus_message_close_container(m);
4720 if (r < 0)
4721 return bus_log_create_error(r);
8e2af478
LP
4722 }
4723
f459b602
MAP
4724 r = sd_bus_message_close_container(m);
4725 if (r < 0)
4726 return bus_log_create_error(r);
8e2af478 4727
c49b30a2 4728 r = sd_bus_call(bus, m, 0, &error, NULL);
f459b602
MAP
4729 if (r < 0) {
4730 log_error("Failed to set unit properties on %s: %s", n, bus_error_message(&error, r));
4731 return r;
8e2af478
LP
4732 }
4733
4734 return 0;
4735}
4736
f459b602
MAP
4737static int snapshot(sd_bus *bus, char **args) {
4738 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
342641fb 4739 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
f459b602
MAP
4740 _cleanup_free_ char *n = NULL, *id = NULL;
4741 const char *path;
7e4249b9 4742 int r;
7e4249b9 4743
1dcf6065 4744 if (strv_length(args) > 1)
f78e6385 4745 n = unit_name_mangle_with_suffix(args[1], MANGLE_NOGLOB, ".snapshot");
1dcf6065
LP
4746 else
4747 n = strdup("");
4748 if (!n)
4749 return log_oom();
7e4249b9 4750
342641fb 4751 r = sd_bus_message_new_method_call(
f22f08cd 4752 bus,
342641fb 4753 &m,
f22f08cd
SP
4754 "org.freedesktop.systemd1",
4755 "/org/freedesktop/systemd1",
4756 "org.freedesktop.systemd1.Manager",
342641fb
LP
4757 "CreateSnapshot");
4758 if (r < 0)
4759 return bus_log_create_error(r);
4760
4761 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
4762 if (r < 0)
4763 return bus_log_create_error(r);
4764
4765 r = sd_bus_message_append(m, "sb", n, false);
4766 if (r < 0)
4767 return bus_log_create_error(r);
4768
4769 r = sd_bus_call(bus, m, 0, &error, &reply);
f459b602
MAP
4770 if (r < 0) {
4771 log_error("Failed to create snapshot: %s", bus_error_message(&error, r));
1dcf6065 4772 return r;
7e4249b9
LP
4773 }
4774
f459b602
MAP
4775 r = sd_bus_message_read(reply, "o", &path);
4776 if (r < 0)
4777 return bus_log_parse_error(r);
5dd9014f 4778
f459b602 4779 r = sd_bus_get_property_string(
f22f08cd
SP
4780 bus,
4781 "org.freedesktop.systemd1",
4782 path,
f459b602
MAP
4783 "org.freedesktop.systemd1.Unit",
4784 "Id",
4785 &error,
4786 &id);
4787 if (r < 0) {
4788 log_error("Failed to get ID of snapshot: %s", bus_error_message(&error, r));
1dcf6065 4789 return r;
7e4249b9
LP
4790 }
4791
0183528f
LP
4792 if (!arg_quiet)
4793 puts(id);
7e4249b9 4794
1dcf6065 4795 return 0;
7e4249b9
LP
4796}
4797
f459b602
MAP
4798static int delete_snapshot(sd_bus *bus, char **args) {
4799 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e3e0314b 4800 _cleanup_strv_free_ char **names = NULL;
729e3769 4801 char **name;
342641fb 4802 int r;
6759e7a7 4803
6759e7a7
LP
4804 assert(args);
4805
e3e0314b
ZJS
4806 r = expand_names(bus, args + 1, ".snapshot", &names);
4807 if (r < 0)
4808 log_error("Failed to expand names: %s", strerror(-r));
1dcf6065 4809
e3e0314b 4810 STRV_FOREACH(name, names) {
342641fb
LP
4811 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
4812 int q;
4813
4814 q = sd_bus_message_new_method_call(
f22f08cd 4815 bus,
342641fb 4816 &m,
b0193f1c
LP
4817 "org.freedesktop.systemd1",
4818 "/org/freedesktop/systemd1",
4819 "org.freedesktop.systemd1.Manager",
342641fb
LP
4820 "RemoveSnapshot");
4821 if (q < 0)
4822 return bus_log_create_error(q);
4823
4824 q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
4825 if (q < 0)
4826 return bus_log_create_error(q);
4827
4828 q = sd_bus_message_append(m, "s", *name);
4829 if (q < 0)
4830 return bus_log_create_error(q);
4831
4832 q = sd_bus_call(bus, m, 0, &error, NULL);
e3e0314b 4833 if (q < 0) {
342641fb 4834 log_error("Failed to remove snapshot %s: %s", *name, bus_error_message(&error, q));
e3e0314b
ZJS
4835 if (r == 0)
4836 r = q;
f459b602 4837 }
6759e7a7
LP
4838 }
4839
e3e0314b 4840 return r;
6759e7a7
LP
4841}
4842
f459b602
MAP
4843static int daemon_reload(sd_bus *bus, char **args) {
4844 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
342641fb 4845 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
7e4249b9 4846 const char *method;
f459b602 4847 int r;
7e4249b9 4848
e4b61340
LP
4849 if (arg_action == ACTION_RELOAD)
4850 method = "Reload";
4851 else if (arg_action == ACTION_REEXEC)
4852 method = "Reexecute";
4853 else {
4854 assert(arg_action == ACTION_SYSTEMCTL);
4855
4856 method =
20b09ca7
LP
4857 streq(args[0], "clear-jobs") ||
4858 streq(args[0], "cancel") ? "ClearJobs" :
4859 streq(args[0], "daemon-reexec") ? "Reexecute" :
4860 streq(args[0], "reset-failed") ? "ResetFailed" :
4861 streq(args[0], "halt") ? "Halt" :
4862 streq(args[0], "poweroff") ? "PowerOff" :
4863 streq(args[0], "reboot") ? "Reboot" :
4864 streq(args[0], "kexec") ? "KExec" :
4865 streq(args[0], "exit") ? "Exit" :
4866 /* "daemon-reload" */ "Reload";
e4b61340 4867 }
7e4249b9 4868
342641fb 4869 r = sd_bus_message_new_method_call(
f22f08cd 4870 bus,
342641fb 4871 &m,
f22f08cd
SP
4872 "org.freedesktop.systemd1",
4873 "/org/freedesktop/systemd1",
4874 "org.freedesktop.systemd1.Manager",
342641fb
LP
4875 method);
4876 if (r < 0)
4877 return bus_log_create_error(r);
f22f08cd 4878
342641fb
LP
4879 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
4880 if (r < 0)
4881 return bus_log_create_error(r);
4882
4883 r = sd_bus_call(bus, m, 0, &error, NULL);
f22f08cd
SP
4884 if (r == -ENOENT && arg_action != ACTION_SYSTEMCTL)
4885 /* There's always a fallback possible for
4886 * legacy actions. */
4887 r = -EADDRNOTAVAIL;
d0ede8f1
LP
4888 else if ((r == -ETIMEDOUT || r == -ECONNRESET) && streq(method, "Reexecute"))
4889 /* On reexecution, we expect a disconnect, not a
4890 * reply */
f22f08cd 4891 r = 0;
1dcf6065 4892 else if (r < 0)
f459b602 4893 log_error("Failed to execute operation: %s", bus_error_message(&error, r));
7e4249b9 4894
0a9776c2 4895 return r < 0 ? r : 0;
7e4249b9
LP
4896}
4897
f459b602
MAP
4898static int reset_failed(sd_bus *bus, char **args) {
4899 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e3e0314b 4900 _cleanup_strv_free_ char **names = NULL;
f84190d8 4901 char **name;
e3e0314b 4902 int r, q;
5632e374 4903
729e3769
LP
4904 if (strv_length(args) <= 1)
4905 return daemon_reload(bus, args);
5632e374 4906
e3e0314b
ZJS
4907 r = expand_names(bus, args + 1, NULL, &names);
4908 if (r < 0)
4909 log_error("Failed to expand names: %s", strerror(-r));
f84190d8 4910
e3e0314b 4911 STRV_FOREACH(name, names) {
342641fb
LP
4912 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
4913
4914 q = sd_bus_message_new_method_call(
f22f08cd 4915 bus,
342641fb 4916 &m,
b0193f1c
LP
4917 "org.freedesktop.systemd1",
4918 "/org/freedesktop/systemd1",
4919 "org.freedesktop.systemd1.Manager",
342641fb
LP
4920 "ResetFailedUnit");
4921 if (q < 0)
4922 return bus_log_create_error(q);
4923
4924 q = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
4925 if (q < 0)
4926 return bus_log_create_error(q);
4927
4928 q = sd_bus_message_append(m, "s", *name);
4929 if (q < 0)
4930 return bus_log_create_error(q);
4931
4932 q = sd_bus_call(bus, m, 0, &error, NULL);
e3e0314b 4933 if (q < 0) {
342641fb 4934 log_error("Failed to reset failed state of unit %s: %s", *name, bus_error_message(&error, q));
e3e0314b
ZJS
4935 if (r == 0)
4936 r = q;
f459b602 4937 }
5632e374
LP
4938 }
4939
e3e0314b 4940 return r;
5632e374
LP
4941}
4942
f459b602
MAP
4943static int show_environment(sd_bus *bus, char **args) {
4944 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
4945 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
4946 const char *text;
7e4249b9 4947 int r;
7e4249b9 4948
1968a360 4949 pager_open_if_enabled();
ec14911e 4950
f459b602 4951 r = sd_bus_get_property(
f22f08cd
SP
4952 bus,
4953 "org.freedesktop.systemd1",
4954 "/org/freedesktop/systemd1",
f459b602
MAP
4955 "org.freedesktop.systemd1.Manager",
4956 "Environment",
4957 &error,
f22f08cd 4958 &reply,
f459b602
MAP
4959 "as");
4960 if (r < 0) {
4961 log_error("Failed to get environment: %s", bus_error_message(&error, r));
f84190d8 4962 return r;
7e4249b9
LP
4963 }
4964
f459b602
MAP
4965 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "s");
4966 if (r < 0)
4967 return bus_log_parse_error(r);
7e4249b9 4968
f459b602 4969 while ((r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &text)) > 0)
f84190d8 4970 puts(text);
f459b602
MAP
4971 if (r < 0)
4972 return bus_log_parse_error(r);
7e4249b9 4973
f459b602
MAP
4974 r = sd_bus_message_exit_container(reply);
4975 if (r < 0)
4976 return bus_log_parse_error(r);
7e4249b9 4977
f84190d8 4978 return 0;
7e4249b9
LP
4979}
4980
f459b602
MAP
4981static int switch_root(sd_bus *bus, char **args) {
4982 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
f39d4a08
HH
4983 _cleanup_free_ char *cmdline_init = NULL;
4984 const char *root, *init;
f459b602
MAP
4985 unsigned l;
4986 int r;
957eb8ca
LP
4987
4988 l = strv_length(args);
4989 if (l < 2 || l > 3) {
4990 log_error("Wrong number of arguments.");
4991 return -EINVAL;
4992 }
4993
4994 root = args[1];
13068da8
TG
4995
4996 if (l >= 3)
f39d4a08 4997 init = args[2];
13068da8 4998 else {
f39d4a08
HH
4999 r = parse_env_file("/proc/cmdline", WHITESPACE,
5000 "init", &cmdline_init,
5001 NULL);
5002 if (r < 0)
5003 log_debug("Failed to parse /proc/cmdline: %s", strerror(-r));
13068da8 5004
f39d4a08 5005 init = cmdline_init;
13068da8 5006 }
f459b602 5007
f39d4a08
HH
5008 if (isempty(init))
5009 init = NULL;
5010
5011 if (init) {
5012 const char *root_systemd_path = NULL, *root_init_path = NULL;
5013
5014 root_systemd_path = strappenda(root, "/" SYSTEMD_BINARY_PATH);
8085f163 5015 root_init_path = strappenda(root, "/", init);
f39d4a08
HH
5016
5017 /* If the passed init is actually the same as the
5018 * systemd binary, then let's suppress it. */
5019 if (files_same(root_init_path, root_systemd_path) > 0)
5020 init = NULL;
5021 }
13068da8 5022
f39d4a08 5023 log_debug("Switching root - root: %s; init: %s", root, strna(init));
957eb8ca 5024
f459b602 5025 r = sd_bus_call_method(
f22f08cd 5026 bus,
957eb8ca
LP
5027 "org.freedesktop.systemd1",
5028 "/org/freedesktop/systemd1",
5029 "org.freedesktop.systemd1.Manager",
f22f08cd 5030 "SwitchRoot",
f459b602 5031 &error,
f22f08cd 5032 NULL,
f459b602
MAP
5033 "ss", root, init);
5034 if (r < 0) {
5035 log_error("Failed to switch root: %s", bus_error_message(&error, r));
5036 return r;
5037 }
5038
5039 return 0;
957eb8ca
LP
5040}
5041
f459b602
MAP
5042static int set_environment(sd_bus *bus, char **args) {
5043 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
5044 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
7e4249b9 5045 const char *method;
31e767f7
LP
5046 int r;
5047
5048 assert(bus);
60f9ba0b 5049 assert(args);
7e4249b9 5050
7e4249b9
LP
5051 method = streq(args[0], "set-environment")
5052 ? "SetEnvironment"
5053 : "UnsetEnvironment";
5054
f459b602
MAP
5055 r = sd_bus_message_new_method_call(
5056 bus,
151b9b96 5057 &m,
31e767f7
LP
5058 "org.freedesktop.systemd1",
5059 "/org/freedesktop/systemd1",
5060 "org.freedesktop.systemd1.Manager",
151b9b96 5061 method);
f459b602
MAP
5062 if (r < 0)
5063 return bus_log_create_error(r);
7e4249b9 5064
342641fb
LP
5065 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
5066 if (r < 0)
5067 return bus_log_create_error(r);
5068
f459b602 5069 r = sd_bus_message_append_strv(m, args + 1);
31e767f7 5070 if (r < 0)
f459b602 5071 return bus_log_create_error(r);
7e4249b9 5072
c49b30a2 5073 r = sd_bus_call(bus, m, 0, &error, NULL);
f459b602
MAP
5074 if (r < 0) {
5075 log_error("Failed to set environment: %s", bus_error_message(&error, r));
5076 return r;
7e4249b9
LP
5077 }
5078
f84190d8 5079 return 0;
7e4249b9
LP
5080}
5081
ac3efa8a
LP
5082static int import_environment(sd_bus *bus, char **args) {
5083 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
5084 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
5085 int r;
5086
5087 assert(bus);
5088 assert(args);
5089
5090 r = sd_bus_message_new_method_call(
5091 bus,
151b9b96 5092 &m,
ac3efa8a
LP
5093 "org.freedesktop.systemd1",
5094 "/org/freedesktop/systemd1",
5095 "org.freedesktop.systemd1.Manager",
151b9b96 5096 "SetEnvironment");
ac3efa8a
LP
5097 if (r < 0)
5098 return bus_log_create_error(r);
5099
342641fb
LP
5100 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
5101 if (r < 0)
5102 return bus_log_create_error(r);
5103
ac3efa8a
LP
5104 if (strv_isempty(args + 1))
5105 r = sd_bus_message_append_strv(m, environ);
5106 else {
5107 char **a, **b;
5108
5109 r = sd_bus_message_open_container(m, 'a', "s");
5110 if (r < 0)
5111 return bus_log_create_error(r);
5112
5113 STRV_FOREACH(a, args + 1) {
5114
5115 if (!env_name_is_valid(*a)) {
5116 log_error("Not a valid environment variable name: %s", *a);
5117 return -EINVAL;
5118 }
5119
5120 STRV_FOREACH(b, environ) {
5121 const char *eq;
5122
5123 eq = startswith(*b, *a);
5124 if (eq && *eq == '=') {
5125
5126 r = sd_bus_message_append(m, "s", *b);
5127 if (r < 0)
5128 return bus_log_create_error(r);
5129
5130 break;
5131 }
5132 }
5133 }
5134
5135 r = sd_bus_message_close_container(m);
5136 }
5137 if (r < 0)
5138 return bus_log_create_error(r);
5139
5140 r = sd_bus_call(bus, m, 0, &error, NULL);
5141 if (r < 0) {
5142 log_error("Failed to import environment: %s", bus_error_message(&error, r));
5143 return r;
5144 }
5145
5146 return 0;
5147}
5148
cbb13b2a 5149static int enable_sysv_units(const char *verb, char **args) {
729e3769 5150 int r = 0;
ee5762e3 5151
77e68fa2 5152#if defined(HAVE_SYSV_COMPAT) && defined(HAVE_CHKCONFIG)
729e3769 5153 unsigned f = 1, t = 1;
fb15be83 5154 _cleanup_lookup_paths_free_ LookupPaths paths = {};
ee5762e3 5155
729e3769
LP
5156 if (arg_scope != UNIT_FILE_SYSTEM)
5157 return 0;
ee5762e3 5158
729e3769
LP
5159 if (!streq(verb, "enable") &&
5160 !streq(verb, "disable") &&
5161 !streq(verb, "is-enabled"))
5162 return 0;
ee5762e3 5163
729e3769
LP
5164 /* Processes all SysV units, and reshuffles the array so that
5165 * afterwards only the native units remain */
ee5762e3 5166
12ed81d9 5167 r = lookup_paths_init(&paths, SYSTEMD_SYSTEM, false, arg_root, NULL, NULL, NULL);
729e3769
LP
5168 if (r < 0)
5169 return r;
ee5762e3 5170
729e3769 5171 r = 0;
cbb13b2a 5172 for (f = 0; args[f]; f++) {
729e3769 5173 const char *name;
05cae7f3 5174 _cleanup_free_ char *p = NULL, *q = NULL, *l = NULL;
729e3769
LP
5175 bool found_native = false, found_sysv;
5176 unsigned c = 1;
5177 const char *argv[6] = { "/sbin/chkconfig", NULL, NULL, NULL, NULL };
05cae7f3 5178 char **k;
729e3769
LP
5179 int j;
5180 pid_t pid;
5181 siginfo_t status;
ee5762e3 5182
729e3769 5183 name = args[f];
ee5762e3 5184
729e3769
LP
5185 if (!endswith(name, ".service"))
5186 continue;
ee5762e3 5187
729e3769
LP
5188 if (path_is_absolute(name))
5189 continue;
ee5762e3 5190
729e3769 5191 STRV_FOREACH(k, paths.unit_path) {
05cae7f3
ZJS
5192 _cleanup_free_ char *path = NULL;
5193
0c6ea3a4
ZJS
5194 path = path_join(arg_root, *k, name);
5195 if (!path)
60731f32 5196 return log_oom();
ee5762e3 5197
4723e4b2 5198 found_native = access(path, F_OK) >= 0;
729e3769
LP
5199 if (found_native)
5200 break;
5201 }
ee5762e3 5202
729e3769
LP
5203 if (found_native)
5204 continue;
ee5762e3 5205
0c6ea3a4
ZJS
5206 p = path_join(arg_root, SYSTEM_SYSVINIT_PATH, name);
5207 if (!p)
60731f32 5208 return log_oom();
ee5762e3 5209
05cae7f3 5210 p[strlen(p) - strlen(".service")] = 0;
729e3769 5211 found_sysv = access(p, F_OK) >= 0;
4b6756a8 5212 if (!found_sysv)
729e3769 5213 continue;
71fad675 5214
729e3769
LP
5215 /* Mark this entry, so that we don't try enabling it as native unit */
5216 args[f] = (char*) "";
ee5762e3 5217
729e3769 5218 log_info("%s is not a native service, redirecting to /sbin/chkconfig.", name);
ee5762e3 5219
729e3769
LP
5220 if (!isempty(arg_root))
5221 argv[c++] = q = strappend("--root=", arg_root);
ee5762e3 5222
2b6bf07d 5223 argv[c++] = basename(p);
729e3769
LP
5224 argv[c++] =
5225 streq(verb, "enable") ? "on" :
5226 streq(verb, "disable") ? "off" : "--level=5";
5227 argv[c] = NULL;
ee5762e3 5228
729e3769 5229 l = strv_join((char**)argv, " ");
60731f32
ZJS
5230 if (!l)
5231 return log_oom();
ee5762e3 5232
729e3769 5233 log_info("Executing %s", l);
ee5762e3 5234
729e3769
LP
5235 pid = fork();
5236 if (pid < 0) {
5237 log_error("Failed to fork: %m");
60731f32 5238 return -errno;
729e3769
LP
5239 } else if (pid == 0) {
5240 /* Child */
ee5762e3 5241
729e3769
LP
5242 execv(argv[0], (char**) argv);
5243 _exit(EXIT_FAILURE);
5244 }
ee5762e3 5245
729e3769
LP
5246 j = wait_for_terminate(pid, &status);
5247 if (j < 0) {
5248 log_error("Failed to wait for child: %s", strerror(-r));
60731f32 5249 return j;
729e3769 5250 }
ee5762e3 5251
729e3769
LP
5252 if (status.si_code == CLD_EXITED) {
5253 if (streq(verb, "is-enabled")) {
5254 if (status.si_status == 0) {
5255 if (!arg_quiet)
5256 puts("enabled");
5257 r = 1;
5258 } else {
5259 if (!arg_quiet)
5260 puts("disabled");
5261 }
ee5762e3 5262
60731f32
ZJS
5263 } else if (status.si_status != 0)
5264 return -EINVAL;
5265 } else
5266 return -EPROTO;
ee5762e3
LP
5267 }
5268
729e3769 5269 /* Drop all SysV units */
cbb13b2a 5270 for (f = 0, t = 0; args[f]; f++) {
ee5762e3 5271
729e3769 5272 if (isempty(args[f]))
ee5762e3
LP
5273 continue;
5274
729e3769
LP
5275 args[t++] = args[f];
5276 }
ee5762e3 5277
729e3769 5278 args[t] = NULL;
ee5762e3 5279
729e3769
LP
5280#endif
5281 return r;
5282}
ee5762e3 5283
37370d0c 5284static int mangle_names(char **original_names, char ***mangled_names) {
a33fdebb 5285 char **i, **l, **name;
37370d0c 5286
a33fdebb
LP
5287 l = new(char*, strv_length(original_names) + 1);
5288 if (!l)
37370d0c
VP
5289 return log_oom();
5290
a33fdebb 5291 i = l;
37370d0c 5292 STRV_FOREACH(name, original_names) {
44386fc1
LN
5293
5294 /* When enabling units qualified path names are OK,
5295 * too, hence allow them explicitly. */
5296
5297 if (is_path(*name))
5298 *i = strdup(*name);
5299 else
f78e6385 5300 *i = unit_name_mangle(*name, MANGLE_NOGLOB);
44386fc1 5301
a33fdebb
LP
5302 if (!*i) {
5303 strv_free(l);
37370d0c 5304 return log_oom();
a33fdebb
LP
5305 }
5306
5307 i++;
37370d0c 5308 }
a33fdebb
LP
5309
5310 *i = NULL;
5311 *mangled_names = l;
37370d0c
VP
5312
5313 return 0;
5314}
5315
f459b602 5316static int enable_unit(sd_bus *bus, char **args) {
e3e0314b 5317 _cleanup_strv_free_ char **names = NULL;
729e3769
LP
5318 const char *verb = args[0];
5319 UnitFileChange *changes = NULL;
718db961 5320 unsigned n_changes = 0;
729e3769 5321 int carries_install_info = -1;
729e3769 5322 int r;
ee5762e3 5323
ab5919fa
MS
5324 if (!args[1])
5325 return 0;
5326
e3e0314b 5327 r = mangle_names(args+1, &names);
3a05c0f9 5328 if (r < 0)
cbb13b2a
VP
5329 return r;
5330
e3e0314b 5331 r = enable_sysv_units(verb, names);
cbb13b2a
VP
5332 if (r < 0)
5333 return r;
3a05c0f9 5334
67d66210
LP
5335 /* If the operation was fully executed by the SysV compat,
5336 * let's finish early */
5337 if (strv_isempty(names))
5338 return 0;
5339
729e3769
LP
5340 if (!bus || avoid_bus()) {
5341 if (streq(verb, "enable")) {
e3e0314b 5342 r = unit_file_enable(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
729e3769
LP
5343 carries_install_info = r;
5344 } else if (streq(verb, "disable"))
e3e0314b 5345 r = unit_file_disable(arg_scope, arg_runtime, arg_root, names, &changes, &n_changes);
729e3769 5346 else if (streq(verb, "reenable")) {
e3e0314b 5347 r = unit_file_reenable(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
729e3769
LP
5348 carries_install_info = r;
5349 } else if (streq(verb, "link"))
e3e0314b 5350 r = unit_file_link(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
729e3769 5351 else if (streq(verb, "preset")) {
d309c1c3 5352 r = unit_file_preset(arg_scope, arg_runtime, arg_root, names, arg_preset_mode, arg_force, &changes, &n_changes);
729e3769
LP
5353 carries_install_info = r;
5354 } else if (streq(verb, "mask"))
e3e0314b 5355 r = unit_file_mask(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
729e3769 5356 else if (streq(verb, "unmask"))
e3e0314b 5357 r = unit_file_unmask(arg_scope, arg_runtime, arg_root, names, &changes, &n_changes);
729e3769
LP
5358 else
5359 assert_not_reached("Unknown verb");
ee5762e3 5360
729e3769
LP
5361 if (r < 0) {
5362 log_error("Operation failed: %s", strerror(-r));
5363 goto finish;
ee5762e3
LP
5364 }
5365
718db961
LP
5366 if (!arg_quiet)
5367 dump_unit_file_changes(changes, n_changes);
ee5762e3 5368
df77cdf0 5369 r = 0;
729e3769 5370 } else {
718db961
LP
5371 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
5372 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
f459b602 5373 int expect_carries_install_info = false;
d309c1c3 5374 bool send_force = true, send_preset_mode = false;
718db961 5375 const char *method;
729e3769
LP
5376
5377 if (streq(verb, "enable")) {
5378 method = "EnableUnitFiles";
5379 expect_carries_install_info = true;
5380 } else if (streq(verb, "disable")) {
5381 method = "DisableUnitFiles";
5382 send_force = false;
5383 } else if (streq(verb, "reenable")) {
5384 method = "ReenableUnitFiles";
5385 expect_carries_install_info = true;
5386 } else if (streq(verb, "link"))
5387 method = "LinkUnitFiles";
5388 else if (streq(verb, "preset")) {
d309c1c3
LP
5389
5390 if (arg_preset_mode != UNIT_FILE_PRESET_FULL) {
5391 method = "PresetUnitFilesWithMode";
5392 send_preset_mode = true;
5393 } else
5394 method = "PresetUnitFiles";
5395
729e3769
LP
5396 expect_carries_install_info = true;
5397 } else if (streq(verb, "mask"))
5398 method = "MaskUnitFiles";
5399 else if (streq(verb, "unmask")) {
5400 method = "UnmaskUnitFiles";
5401 send_force = false;
5402 } else
5403 assert_not_reached("Unknown verb");
5404
f459b602
MAP
5405 r = sd_bus_message_new_method_call(
5406 bus,
151b9b96 5407 &m,
729e3769
LP
5408 "org.freedesktop.systemd1",
5409 "/org/freedesktop/systemd1",
5410 "org.freedesktop.systemd1.Manager",
151b9b96 5411 method);
f459b602
MAP
5412 if (r < 0)
5413 return bus_log_create_error(r);
ee5762e3 5414
342641fb
LP
5415 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
5416 if (r < 0)
5417 return bus_log_create_error(r);
5418
e3e0314b 5419 r = sd_bus_message_append_strv(m, names);
f459b602
MAP
5420 if (r < 0)
5421 return bus_log_create_error(r);
ee5762e3 5422
d309c1c3
LP
5423 if (send_preset_mode) {
5424 r = sd_bus_message_append(m, "s", unit_file_preset_mode_to_string(arg_preset_mode));
5425 if (r < 0)
5426 return bus_log_create_error(r);
5427 }
5428
f459b602
MAP
5429 r = sd_bus_message_append(m, "b", arg_runtime);
5430 if (r < 0)
5431 return bus_log_create_error(r);
ee5762e3 5432
729e3769 5433 if (send_force) {
f459b602
MAP
5434 r = sd_bus_message_append(m, "b", arg_force);
5435 if (r < 0)
5436 return bus_log_create_error(r);
ee5762e3
LP
5437 }
5438
c49b30a2 5439 r = sd_bus_call(bus, m, 0, &error, &reply);
f459b602
MAP
5440 if (r < 0) {
5441 log_error("Failed to execute operation: %s", bus_error_message(&error, r));
5442 return r;
729e3769 5443 }
be394c48 5444
729e3769 5445 if (expect_carries_install_info) {
f459b602
MAP
5446 r = sd_bus_message_read(reply, "b", &carries_install_info);
5447 if (r < 0)
5448 return bus_log_parse_error(r);
ee5762e3
LP
5449 }
5450
cc3f2093 5451 r = deserialize_and_dump_unit_file_changes(reply);
f459b602 5452 if (r < 0)
718db961 5453 return r;
b77398f7 5454
93c941e3 5455 /* Try to reload if enabled */
d6cb60c7 5456 if (!arg_no_reload)
729e3769 5457 r = daemon_reload(bus, args);
f459b602
MAP
5458 else
5459 r = 0;
b647f10d 5460 }
3d3961f2 5461
729e3769 5462 if (carries_install_info == 0)
416389f7
LP
5463 log_warning("The unit files have no [Install] section. They are not meant to be enabled\n"
5464 "using systemctl.\n"
5465 "Possible reasons for having this kind of units are:\n"
5466 "1) A unit may be statically enabled by being symlinked from another unit's\n"
5467 " .wants/ or .requires/ directory.\n"
5468 "2) A unit's purpose may be to act as a helper for some other unit which has\n"
5469 " a requirement dependency on it.\n"
5470 "3) A unit may be started when needed via activation (socket, path, timer,\n"
5471 " D-Bus, udev, scripted systemctl call, ...).\n");
ee5762e3 5472
729e3769 5473finish:
729e3769 5474 unit_file_changes_free(changes, n_changes);
ee5762e3 5475
729e3769 5476 return r;
ee5762e3
LP
5477}
5478
e94937df
LN
5479static int add_dependency(sd_bus *bus, char **args) {
5480 _cleanup_strv_free_ char **names = NULL;
5481 _cleanup_free_ char *target = NULL;
5482 const char *verb = args[0];
5483 UnitDependency dep;
5484 int r = 0;
5485
5486 if (!args[1])
5487 return 0;
5488
5489 target = unit_name_mangle_with_suffix(args[1], MANGLE_NOGLOB, ".target");
5490 if (!target)
5491 return log_oom();
5492
5493 r = mangle_names(args+2, &names);
5494 if (r < 0)
5495 return r;
5496
5497 if (streq(verb, "add-wants"))
5498 dep = UNIT_WANTS;
5499 else if (streq(verb, "add-requires"))
5500 dep = UNIT_REQUIRES;
5501 else
5502 assert_not_reached("Unknown verb");
5503
5504 if (!bus || avoid_bus()) {
5505 UnitFileChange *changes = NULL;
5506 unsigned n_changes = 0;
5507
5508 r = unit_file_add_dependency(arg_scope, arg_runtime, arg_root, names, target, dep, arg_force, &changes, &n_changes);
5509
5510 if (r < 0) {
5511 log_error("Can't add dependency: %s", strerror(-r));
5512 return r;
5513 }
5514
5515 if (!arg_quiet)
5516 dump_unit_file_changes(changes, n_changes);
5517
5518 unit_file_changes_free(changes, n_changes);
5519
5520 } else {
5521 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
5522 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
5523
5524 r = sd_bus_message_new_method_call(
5525 bus,
5526 &m,
5527 "org.freedesktop.systemd1",
5528 "/org/freedesktop/systemd1",
5529 "org.freedesktop.systemd1.Manager",
5530 "AddDependencyUnitFiles");
5531 if (r < 0)
5532 return bus_log_create_error(r);
5533
342641fb 5534 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
e94937df
LN
5535 if (r < 0)
5536 return bus_log_create_error(r);
5537
342641fb 5538 r = sd_bus_message_append_strv(m, names);
e94937df
LN
5539 if (r < 0)
5540 return bus_log_create_error(r);
5541
342641fb 5542 r = sd_bus_message_append(m, "ssbb", target, unit_dependency_to_string(dep), arg_runtime, arg_force);
e94937df
LN
5543 if (r < 0)
5544 return bus_log_create_error(r);
5545
5546 r = sd_bus_call(bus, m, 0, &error, &reply);
5547 if (r < 0) {
5548 log_error("Failed to execute operation: %s", bus_error_message(&error, r));
5549 return r;
5550 }
5551
5552 r = deserialize_and_dump_unit_file_changes(reply);
5553 if (r < 0)
5554 return r;
5555
5556 if (!arg_no_reload)
5557 r = daemon_reload(bus, args);
5558 else
5559 r = 0;
5560 }
5561
5562 return r;
5563}
5564
d309c1c3
LP
5565static int preset_all(sd_bus *bus, char **args) {
5566 UnitFileChange *changes = NULL;
5567 unsigned n_changes = 0;
5568 int r;
5569
5570 if (!bus || avoid_bus()) {
5571
5572 r = unit_file_preset_all(arg_scope, arg_runtime, arg_root, arg_preset_mode, arg_force, &changes, &n_changes);
5573 if (r < 0) {
5574 log_error("Operation failed: %s", strerror(-r));
5575 goto finish;
5576 }
5577
5578 if (!arg_quiet)
5579 dump_unit_file_changes(changes, n_changes);
5580
5581 r = 0;
5582
5583 } else {
342641fb 5584 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
d309c1c3
LP
5585 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
5586
342641fb 5587 r = sd_bus_message_new_method_call(
d309c1c3 5588 bus,
342641fb 5589 &m,
d309c1c3
LP
5590 "org.freedesktop.systemd1",
5591 "/org/freedesktop/systemd1",
5592 "org.freedesktop.systemd1.Manager",
342641fb
LP
5593 "PresetAllUnitFiles");
5594 if (r < 0)
5595 return bus_log_create_error(r);
5596
5597 r = sd_bus_message_set_allow_interactive_authorization(m, arg_ask_password);
5598 if (r < 0)
5599 return bus_log_create_error(r);
5600
5601 r = sd_bus_message_append(
5602 m,
d309c1c3
LP
5603 "sbb",
5604 unit_file_preset_mode_to_string(arg_preset_mode),
5605 arg_runtime,
5606 arg_force);
342641fb
LP
5607 if (r < 0)
5608 return bus_log_create_error(r);
5609
5610 r = sd_bus_call(bus, m, 0, &error, &reply);
d309c1c3
LP
5611 if (r < 0) {
5612 log_error("Failed to execute operation: %s", bus_error_message(&error, r));
5613 return r;
5614 }
5615
5616 r = deserialize_and_dump_unit_file_changes(reply);
5617 if (r < 0)
5618 return r;
5619
5620 if (!arg_no_reload)
5621 r = daemon_reload(bus, args);
5622 else
5623 r = 0;
5624 }
5625
5626finish:
5627 unit_file_changes_free(changes, n_changes);
5628
5629 return r;
5630}
5631
f459b602
MAP
5632static int unit_is_enabled(sd_bus *bus, char **args) {
5633
5634 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e3e0314b 5635 _cleanup_strv_free_ char **names = NULL;
729e3769
LP
5636 bool enabled;
5637 char **name;
f459b602 5638 int r;
ee5762e3 5639
e3e0314b 5640 r = mangle_names(args+1, &names);
cbb13b2a
VP
5641 if (r < 0)
5642 return r;
5643
e3e0314b 5644 r = enable_sysv_units(args[0], names);
729e3769
LP
5645 if (r < 0)
5646 return r;
ee5762e3 5647
729e3769 5648 enabled = r > 0;
ee5762e3 5649
729e3769 5650 if (!bus || avoid_bus()) {
ee5762e3 5651
e3e0314b 5652 STRV_FOREACH(name, names) {
729e3769 5653 UnitFileState state;
ee5762e3 5654
cbb13b2a 5655 state = unit_file_get_state(arg_scope, arg_root, *name);
cbc9fbd1
LP
5656 if (state < 0) {
5657 log_error("Failed to get unit file state for %s: %s", *name, strerror(-state));
cec7eda5 5658 return state;
cbc9fbd1 5659 }
ee5762e3 5660
729e3769
LP
5661 if (state == UNIT_FILE_ENABLED ||
5662 state == UNIT_FILE_ENABLED_RUNTIME ||
5663 state == UNIT_FILE_STATIC)
5664 enabled = true;
5665
5666 if (!arg_quiet)
5667 puts(unit_file_state_to_string(state));
71fad675 5668 }
ee5762e3 5669
729e3769 5670 } else {
e3e0314b 5671 STRV_FOREACH(name, names) {
f459b602 5672 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
729e3769 5673 const char *s;
63a723f3 5674
f459b602 5675 r = sd_bus_call_method(
f22f08cd 5676 bus,
729e3769
LP
5677 "org.freedesktop.systemd1",
5678 "/org/freedesktop/systemd1",
5679 "org.freedesktop.systemd1.Manager",
f22f08cd 5680 "GetUnitFileState",
f459b602 5681 &error,
f22f08cd 5682 &reply,
04504f93 5683 "s", *name);
f459b602
MAP
5684 if (r < 0) {
5685 log_error("Failed to get unit file state for %s: %s", *name, bus_error_message(&error, r));
cec7eda5 5686 return r;
ee5762e3
LP
5687 }
5688
f459b602
MAP
5689 r = sd_bus_message_read(reply, "s", &s);
5690 if (r < 0)
5691 return bus_log_parse_error(r);
ee5762e3 5692
729e3769
LP
5693 if (streq(s, "enabled") ||
5694 streq(s, "enabled-runtime") ||
5695 streq(s, "static"))
5696 enabled = true;
5697
5698 if (!arg_quiet)
5699 puts(s);
560d8f23 5700 }
ee5762e3
LP
5701 }
5702
f459b602 5703 return !enabled;
ee5762e3
LP
5704}
5705
99813a19
LP
5706static int is_system_running(sd_bus *bus, char **args) {
5707 _cleanup_free_ char *state = NULL;
5708 int r;
5709
5710 r = sd_bus_get_property_string(
5711 bus,
5712 "org.freedesktop.systemd1",
5713 "/org/freedesktop/systemd1",
5714 "org.freedesktop.systemd1.Manager",
5715 "SystemState",
5716 NULL,
5717 &state);
5718 if (r < 0) {
5719 if (!arg_quiet)
5720 puts("unknown");
5721 return 0;
5722 }
5723
5724 if (!arg_quiet)
5725 puts(state);
5726
5727 return streq(state, "running") ? EXIT_SUCCESS : EXIT_FAILURE;
5728}
5729
601185b4 5730static void systemctl_help(void) {
7e4249b9 5731
729e3769
LP
5732 pager_open_if_enabled();
5733
2e33c433 5734 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
729e3769 5735 "Query or send control commands to the systemd manager.\n\n"
8a0867d6
LP
5736 " -h --help Show this help\n"
5737 " --version Show package version\n"
f459b602
MAP
5738 " --system Connect to system manager\n"
5739 " --user Connect to user service manager\n"
5740 " -H --host=[USER@]HOST\n"
5741 " Operate on remote host\n"
5742 " -M --machine=CONTAINER\n"
5743 " Operate on local container\n"
8a0867d6 5744 " -t --type=TYPE List only units of a particular type\n"
a521ae4a 5745 " --state=STATE List only units with particular LOAD or SUB or ACTIVE state\n"
8a0867d6 5746 " -p --property=NAME Show only properties by this name\n"
a5e4972c
LP
5747 " -a --all Show all loaded units/properties, including dead/empty\n"
5748 " ones. To list all units installed on the system, use\n"
5749 " the 'list-unit-files' command instead.\n"
98a6e132 5750 " -l --full Don't ellipsize unit names on output\n"
1238ee09 5751 " -r --recursive Show unit list of host and local containers\n"
4dc5b821
LP
5752 " --reverse Show reverse dependencies with 'list-dependencies'\n"
5753 " --job-mode=MODE Specify how to deal with already queued jobs, when\n"
5754 " queueing a new job\n"
a521ae4a 5755 " --show-types When showing sockets, explicitly show their type\n"
b37844d3
LP
5756 " -i --ignore-inhibitors\n"
5757 " When shutting down or sleeping, ignore inhibitors\n"
a8f11321
LP
5758 " --kill-who=WHO Who to send signal to\n"
5759 " -s --signal=SIGNAL Which signal to send\n"
8a0867d6
LP
5760 " -q --quiet Suppress output\n"
5761 " --no-block Do not wait until operation finished\n"
8a0867d6 5762 " --no-wall Don't send wall message before halt/power-off/reboot\n"
8a0867d6
LP
5763 " --no-reload When enabling/disabling unit files, don't reload daemon\n"
5764 " configuration\n"
ebed32bf 5765 " --no-legend Do not print a legend (column headers and hints)\n"
69fc152f 5766 " --no-pager Do not pipe output into a pager\n"
501fc174
LP
5767 " --no-ask-password\n"
5768 " Do not ask for system passwords\n"
a8f11321 5769 " --global Enable/disable unit files globally\n"
a521ae4a 5770 " --runtime Enable unit files only temporarily until next reboot\n"
8a0867d6
LP
5771 " -f --force When enabling unit files, override existing symlinks\n"
5772 " When shutting down, execute action immediately\n"
d309c1c3
LP
5773 " --preset-mode= Specifies whether fully apply presets, or only enable,\n"
5774 " or only disable\n"
729e3769 5775 " --root=PATH Enable unit files in the specified root directory\n"
76fdc966 5776 " -n --lines=INTEGER Number of journal entries to show\n"
d3f2bdbf 5777 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
815ebc54
DH
5778 " verbose, export, json, json-pretty, json-sse, cat)\n"
5779 " --plain Print unit dependencies as a list instead of a tree\n\n"
34c4b47b 5780 "Unit Commands:\n"
d8fba7c6
ZJS
5781 " list-units [PATTERN...] List loaded units\n"
5782 " list-sockets [PATTERN...] List loaded sockets ordered by address\n"
5783 " list-timers [PATTERN...] List loaded timers ordered by next elapse\n"
4f8f66cb
ZJS
5784 " start NAME... Start (activate) one or more units\n"
5785 " stop NAME... Stop (deactivate) one or more units\n"
5786 " reload NAME... Reload one or more units\n"
5787 " restart NAME... Start or restart one or more units\n"
5788 " try-restart NAME... Restart one or more units if active\n"
5789 " reload-or-restart NAME... Reload one or more units if possible,\n"
6f28c033 5790 " otherwise start or restart\n"
4f8f66cb 5791 " reload-or-try-restart NAME... Reload one or more units if possible,\n"
6f28c033 5792 " otherwise restart if active\n"
4f8f66cb
ZJS
5793 " isolate NAME Start one unit and stop all others\n"
5794 " kill NAME... Send signal to processes of a unit\n"
b3ae710c
ZJS
5795 " is-active PATTERN... Check whether units are active\n"
5796 " is-failed PATTERN... Check whether units are failed\n"
5797 " status [PATTERN...|PID...] Show runtime status of one or more units\n"
5798 " show [PATTERN...|JOB...] Show properties of one or more\n"
ee5762e3 5799 " units/jobs or the manager\n"
b3ae710c 5800 " cat PATTERN... Show files and drop-ins of one or more units\n"
4f8f66cb 5801 " set-property NAME ASSIGNMENT... Sets one or more properties of a unit\n"
b3ae710c
ZJS
5802 " help PATTERN...|PID... Show manual for one or more units\n"
5803 " reset-failed [PATTERN...] Reset failed state for all, one, or more\n"
fdf20a31 5804 " units\n"
55c0b89c 5805 " list-dependencies [NAME] Recursively show units which are required\n"
afba4199
ZJS
5806 " or wanted by this unit or by which this\n"
5807 " unit is required or wanted\n\n"
34c4b47b 5808 "Unit File Commands:\n"
d8fba7c6 5809 " list-unit-files [PATTERN...] List installed unit files\n"
4f8f66cb
ZJS
5810 " enable NAME... Enable one or more unit files\n"
5811 " disable NAME... Disable one or more unit files\n"
5812 " reenable NAME... Reenable one or more unit files\n"
5813 " preset NAME... Enable/disable one or more unit files\n"
729e3769 5814 " based on preset configuration\n"
d309c1c3
LP
5815 " preset-all Enable/disable all unit files based on\n"
5816 " preset configuration\n"
4f8f66cb
ZJS
5817 " is-enabled NAME... Check whether unit files are enabled\n\n"
5818 " mask NAME... Mask one or more units\n"
5819 " unmask NAME... Unmask one or more units\n"
5820 " link PATH... Link one or more units files into\n"
729e3769 5821 " the search path\n"
e94937df
LN
5822 " add-wants TARGET NAME... Add 'Wants' dependency for the target\n"
5823 " on specified one or more units\n"
5824 " add-requires TARGET NAME... Add 'Requires' dependency for the target\n"
5825 " on specified one or more units\n"
99504dd4 5826 " get-default Get the name of the default target\n"
f535088e 5827 " set-default NAME Set the default target\n\n"
0d292f5e
LP
5828 "Machine Commands:\n"
5829 " list-machines [PATTERN...] List local containers and host\n\n"
34c4b47b 5830 "Job Commands:\n"
d8fba7c6 5831 " list-jobs [PATTERN...] List jobs\n"
34c4b47b 5832 " cancel [JOB...] Cancel all, one, or more jobs\n\n"
34c4b47b 5833 "Snapshot Commands:\n"
7e4249b9 5834 " snapshot [NAME] Create a snapshot\n"
4f8f66cb 5835 " delete NAME... Remove one or more snapshots\n\n"
34c4b47b 5836 "Environment Commands:\n"
7e4249b9 5837 " show-environment Dump environment\n"
4f8f66cb 5838 " set-environment NAME=VALUE... Set one or more environment variables\n"
ac3efa8a
LP
5839 " unset-environment NAME... Unset one or more environment variables\n"
5840 " import-environment NAME... Import all, one or more environment variables\n\n"
34c4b47b
LP
5841 "Manager Lifecycle Commands:\n"
5842 " daemon-reload Reload systemd manager configuration\n"
5843 " daemon-reexec Reexecute systemd manager\n\n"
5844 "System Commands:\n"
99813a19 5845 " is-system-running Check whether system is fully running\n"
20b09ca7
LP
5846 " default Enter system default mode\n"
5847 " rescue Enter system rescue mode\n"
5848 " emergency Enter system emergency mode\n"
514f4ef5 5849 " halt Shut down and halt the system\n"
2e33c433 5850 " poweroff Shut down and power-off the system\n"
37185ec8 5851 " reboot [ARG] Shut down and reboot the system\n"
20b09ca7 5852 " kexec Shut down and reboot the system with kexec\n"
6edd7d0a 5853 " exit Request user instance exit\n"
4f8f66cb 5854 " switch-root ROOT [INIT] Change to a different root file system\n"
6edd7d0a 5855 " suspend Suspend the system\n"
6524990f
LP
5856 " hibernate Hibernate the system\n"
5857 " hybrid-sleep Hibernate and suspend the system\n",
5b6319dc 5858 program_invocation_short_name);
7e4249b9
LP
5859}
5860
601185b4 5861static void halt_help(void) {
37185ec8 5862 printf("%s [OPTIONS...]%s\n\n"
e4b61340
LP
5863 "%s the system.\n\n"
5864 " --help Show this help\n"
5865 " --halt Halt the machine\n"
5866 " -p --poweroff Switch off the machine\n"
5867 " --reboot Reboot the machine\n"
2e33c433
LP
5868 " -f --force Force immediate halt/power-off/reboot\n"
5869 " -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
e4b61340 5870 " -d --no-wtmp Don't write wtmp record\n"
2e33c433 5871 " --no-wall Don't send wall message before halt/power-off/reboot\n",
e4b61340 5872 program_invocation_short_name,
37185ec8 5873 arg_action == ACTION_REBOOT ? " [ARG]" : "",
e4b61340
LP
5874 arg_action == ACTION_REBOOT ? "Reboot" :
5875 arg_action == ACTION_POWEROFF ? "Power off" :
5876 "Halt");
e4b61340
LP
5877}
5878
601185b4 5879static void shutdown_help(void) {
08e4b1c5 5880 printf("%s [OPTIONS...] [TIME] [WALL...]\n\n"
e4b61340
LP
5881 "Shut down the system.\n\n"
5882 " --help Show this help\n"
5883 " -H --halt Halt the machine\n"
5884 " -P --poweroff Power-off the machine\n"
5885 " -r --reboot Reboot the machine\n"
386da858 5886 " -h Equivalent to --poweroff, overridden by --halt\n"
2e33c433 5887 " -k Don't halt/power-off/reboot, just send warnings\n"
f6144808 5888 " --no-wall Don't send wall message before halt/power-off/reboot\n"
f6144808 5889 " -c Cancel a pending shutdown\n",
e4b61340 5890 program_invocation_short_name);
e4b61340
LP
5891}
5892
601185b4 5893static void telinit_help(void) {
2e33c433 5894 printf("%s [OPTIONS...] {COMMAND}\n\n"
514f4ef5
LP
5895 "Send control commands to the init daemon.\n\n"
5896 " --help Show this help\n"
2e33c433 5897 " --no-wall Don't send wall message before halt/power-off/reboot\n\n"
e4b61340
LP
5898 "Commands:\n"
5899 " 0 Power-off the machine\n"
5900 " 6 Reboot the machine\n"
514f4ef5
LP
5901 " 2, 3, 4, 5 Start runlevelX.target unit\n"
5902 " 1, s, S Enter rescue mode\n"
5903 " q, Q Reload init daemon configuration\n"
5904 " u, U Reexecute init daemon\n",
e4b61340 5905 program_invocation_short_name);
e4b61340
LP
5906}
5907
601185b4 5908static void runlevel_help(void) {
2e33c433 5909 printf("%s [OPTIONS...]\n\n"
e4b61340
LP
5910 "Prints the previous and current runlevel of the init system.\n\n"
5911 " --help Show this help\n",
5912 program_invocation_short_name);
e4b61340
LP
5913}
5914
b93312f5 5915static void help_types(void) {
45c0c61d 5916 int i;
830f01f0 5917 const char *t;
45c0c61d 5918
b93312f5
ZJS
5919 if (!arg_no_legend)
5920 puts("Available unit types:");
f168c273 5921 for (i = 0; i < _UNIT_TYPE_MAX; i++) {
830f01f0
LP
5922 t = unit_type_to_string(i);
5923 if (t)
5924 puts(t);
5925 }
45c0c61d
ZJS
5926}
5927
e4b61340 5928static int systemctl_parse_argv(int argc, char *argv[]) {
7e4249b9
LP
5929
5930 enum {
90d473a1 5931 ARG_FAIL = 0x100,
afba4199
ZJS
5932 ARG_REVERSE,
5933 ARG_AFTER,
5934 ARG_BEFORE,
991f2a39 5935 ARG_SHOW_TYPES,
23ade460 5936 ARG_IRREVERSIBLE,
e67c3609 5937 ARG_IGNORE_DEPENDENCIES,
35df8f27 5938 ARG_VERSION,
af2d49f7 5939 ARG_USER,
7e4249b9 5940 ARG_SYSTEM,
ee5762e3 5941 ARG_GLOBAL,
6e905d93 5942 ARG_NO_BLOCK,
ebed32bf 5943 ARG_NO_LEGEND,
611efaac 5944 ARG_NO_PAGER,
4445a875 5945 ARG_NO_WALL,
be394c48 5946 ARG_ROOT,
ee5762e3 5947 ARG_NO_RELOAD,
501fc174 5948 ARG_KILL_WHO,
30732560 5949 ARG_NO_ASK_PASSWORD,
729e3769 5950 ARG_FAILED,
df50185b 5951 ARG_RUNTIME,
5d0c05e5 5952 ARG_FORCE,
9b9b3d36 5953 ARG_PLAIN,
4dc5b821 5954 ARG_STATE,
d309c1c3
LP
5955 ARG_JOB_MODE,
5956 ARG_PRESET_MODE,
7e4249b9
LP
5957 };
5958
5959 static const struct option options[] = {
9ea9d4cf
LP
5960 { "help", no_argument, NULL, 'h' },
5961 { "version", no_argument, NULL, ARG_VERSION },
5962 { "type", required_argument, NULL, 't' },
5963 { "property", required_argument, NULL, 'p' },
5964 { "all", no_argument, NULL, 'a' },
5965 { "reverse", no_argument, NULL, ARG_REVERSE },
5966 { "after", no_argument, NULL, ARG_AFTER },
5967 { "before", no_argument, NULL, ARG_BEFORE },
5968 { "show-types", no_argument, NULL, ARG_SHOW_TYPES },
5969 { "failed", no_argument, NULL, ARG_FAILED }, /* compatibility only */
5970 { "full", no_argument, NULL, 'l' },
4dc5b821
LP
5971 { "job-mode", required_argument, NULL, ARG_JOB_MODE },
5972 { "fail", no_argument, NULL, ARG_FAIL }, /* compatibility only */
5973 { "irreversible", no_argument, NULL, ARG_IRREVERSIBLE }, /* compatibility only */
5974 { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES }, /* compatibility only */
9ea9d4cf
LP
5975 { "ignore-inhibitors", no_argument, NULL, 'i' },
5976 { "user", no_argument, NULL, ARG_USER },
5977 { "system", no_argument, NULL, ARG_SYSTEM },
5978 { "global", no_argument, NULL, ARG_GLOBAL },
5979 { "no-block", no_argument, NULL, ARG_NO_BLOCK },
5980 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
5981 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
5982 { "no-wall", no_argument, NULL, ARG_NO_WALL },
5983 { "quiet", no_argument, NULL, 'q' },
5984 { "root", required_argument, NULL, ARG_ROOT },
5985 { "force", no_argument, NULL, ARG_FORCE },
5986 { "no-reload", no_argument, NULL, ARG_NO_RELOAD },
5987 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
5988 { "signal", required_argument, NULL, 's' },
5989 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
5990 { "host", required_argument, NULL, 'H' },
f459b602 5991 { "machine", required_argument, NULL, 'M' },
9ea9d4cf
LP
5992 { "runtime", no_argument, NULL, ARG_RUNTIME },
5993 { "lines", required_argument, NULL, 'n' },
5994 { "output", required_argument, NULL, 'o' },
5995 { "plain", no_argument, NULL, ARG_PLAIN },
5996 { "state", required_argument, NULL, ARG_STATE },
1238ee09 5997 { "recursive", no_argument, NULL, 'r' },
d309c1c3 5998 { "preset-mode", required_argument, NULL, ARG_PRESET_MODE },
eb9da376 5999 {}
7e4249b9
LP
6000 };
6001
6002 int c;
6003
e4b61340 6004 assert(argc >= 0);
7e4249b9
LP
6005 assert(argv);
6006
601185b4 6007 while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:ir", options, NULL)) >= 0)
7e4249b9
LP
6008
6009 switch (c) {
6010
6011 case 'h':
601185b4
ZJS
6012 systemctl_help();
6013 return 0;
35df8f27
LP
6014
6015 case ARG_VERSION:
6016 puts(PACKAGE_STRING);
7d568925 6017 puts(SYSTEMD_FEATURES);
35df8f27 6018 return 0;
7e4249b9 6019
20b3f379 6020 case 't': {
a2a5291b 6021 const char *word, *state;
20b3f379 6022 size_t size;
45c0c61d 6023
20b3f379 6024 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
7fd1b19b 6025 _cleanup_free_ char *type;
20b3f379
ZJS
6026
6027 type = strndup(word, size);
6028 if (!type)
6029 return -ENOMEM;
6030
6031 if (streq(type, "help")) {
6032 help_types();
6033 return 0;
6034 }
6035
6036 if (unit_type_from_string(type) >= 0) {
6037 if (strv_push(&arg_types, type))
6038 return log_oom();
6039 type = NULL;
6040 continue;
6041 }
6042
9b9b3d36
MW
6043 /* It's much nicer to use --state= for
6044 * load states, but let's support this
6045 * in --types= too for compatibility
6046 * with old versions */
20b3f379 6047 if (unit_load_state_from_string(optarg) >= 0) {
9b9b3d36 6048 if (strv_push(&arg_states, type) < 0)
20b3f379
ZJS
6049 return log_oom();
6050 type = NULL;
6051 continue;
6052 }
6053
ab06eef8 6054 log_error("Unknown unit type or load state '%s'.", type);
20b3f379
ZJS
6055 log_info("Use -t help to see a list of allowed values.");
6056 return -EINVAL;
c147dc42 6057 }
20b3f379
ZJS
6058
6059 break;
6060 }
6061
ea4a240d 6062 case 'p': {
033a842c
ZJS
6063 /* Make sure that if the empty property list
6064 was specified, we won't show any properties. */
20b3f379 6065 if (isempty(optarg) && !arg_properties) {
cbc9fbd1 6066 arg_properties = new0(char*, 1);
20b3f379
ZJS
6067 if (!arg_properties)
6068 return log_oom();
6069 } else {
a2a5291b 6070 const char *word, *state;
20b3f379 6071 size_t size;
033a842c 6072
20b3f379
ZJS
6073 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
6074 char *prop;
033a842c 6075
20b3f379
ZJS
6076 prop = strndup(word, size);
6077 if (!prop)
6078 return log_oom();
ea4a240d 6079
6e18964d 6080 if (strv_consume(&arg_properties, prop) < 0)
20b3f379 6081 return log_oom();
20b3f379 6082 }
033a842c 6083 }
48220598
LP
6084
6085 /* If the user asked for a particular
6086 * property, show it to him, even if it is
6087 * empty. */
6088 arg_all = true;
033a842c 6089
48220598 6090 break;
ea4a240d 6091 }
48220598 6092
7e4249b9
LP
6093 case 'a':
6094 arg_all = true;
6095 break;
6096
afba4199
ZJS
6097 case ARG_REVERSE:
6098 arg_dependency = DEPENDENCY_REVERSE;
6099 break;
6100
6101 case ARG_AFTER:
6102 arg_dependency = DEPENDENCY_AFTER;
6103 break;
6104
6105 case ARG_BEFORE:
6106 arg_dependency = DEPENDENCY_BEFORE;
6107 break;
6108
991f2a39
ZJS
6109 case ARG_SHOW_TYPES:
6110 arg_show_types = true;
6111 break;
6112
4dc5b821
LP
6113 case ARG_JOB_MODE:
6114 arg_job_mode = optarg;
6115 break;
6116
90d473a1 6117 case ARG_FAIL:
e67c3609
LP
6118 arg_job_mode = "fail";
6119 break;
6120
23ade460
MS
6121 case ARG_IRREVERSIBLE:
6122 arg_job_mode = "replace-irreversibly";
6123 break;
6124
e67c3609
LP
6125 case ARG_IGNORE_DEPENDENCIES:
6126 arg_job_mode = "ignore-dependencies";
7e4249b9
LP
6127 break;
6128
af2d49f7 6129 case ARG_USER:
729e3769 6130 arg_scope = UNIT_FILE_USER;
7e4249b9
LP
6131 break;
6132
6133 case ARG_SYSTEM:
729e3769
LP
6134 arg_scope = UNIT_FILE_SYSTEM;
6135 break;
6136
6137 case ARG_GLOBAL:
6138 arg_scope = UNIT_FILE_GLOBAL;
7e4249b9
LP
6139 break;
6140
6e905d93
LP
6141 case ARG_NO_BLOCK:
6142 arg_no_block = true;
7e4249b9
LP
6143 break;
6144
ebed32bf
MS
6145 case ARG_NO_LEGEND:
6146 arg_no_legend = true;
6147 break;
6148
611efaac
LP
6149 case ARG_NO_PAGER:
6150 arg_no_pager = true;
6151 break;
0736af98 6152
514f4ef5
LP
6153 case ARG_NO_WALL:
6154 arg_no_wall = true;
6155 break;
6156
be394c48
FC
6157 case ARG_ROOT:
6158 arg_root = optarg;
6159 break;
6160
98a6e132 6161 case 'l':
8fe914ec
LP
6162 arg_full = true;
6163 break;
6164
30732560 6165 case ARG_FAILED:
9b9b3d36
MW
6166 if (strv_extend(&arg_states, "failed") < 0)
6167 return log_oom();
6168
30732560
LP
6169 break;
6170
0183528f
LP
6171 case 'q':
6172 arg_quiet = true;
6173 break;
6174
568b679f
LP
6175 case ARG_FORCE:
6176 arg_force ++;
6177 break;
6178
b4f27ccc 6179 case 'f':
e606bb61 6180 arg_force ++;
ee5762e3
LP
6181 break;
6182
6183 case ARG_NO_RELOAD:
6184 arg_no_reload = true;
6185 break;
6186
8a0867d6
LP
6187 case ARG_KILL_WHO:
6188 arg_kill_who = optarg;
6189 break;
6190
8a0867d6
LP
6191 case 's':
6192 if ((arg_signal = signal_from_string_try_harder(optarg)) < 0) {
6193 log_error("Failed to parse signal string %s.", optarg);
6194 return -EINVAL;
6195 }
6196 break;
6197
501fc174
LP
6198 case ARG_NO_ASK_PASSWORD:
6199 arg_ask_password = false;
6200 break;
6201
f459b602
MAP
6202 case 'H':
6203 arg_transport = BUS_TRANSPORT_REMOTE;
6204 arg_host = optarg;
a8f11321
LP
6205 break;
6206
f459b602
MAP
6207 case 'M':
6208 arg_transport = BUS_TRANSPORT_CONTAINER;
6209 arg_host = optarg;
a8f11321
LP
6210 break;
6211
729e3769
LP
6212 case ARG_RUNTIME:
6213 arg_runtime = true;
6214 break;
6215
df50185b
LP
6216 case 'n':
6217 if (safe_atou(optarg, &arg_lines) < 0) {
6218 log_error("Failed to parse lines '%s'", optarg);
6219 return -EINVAL;
6220 }
6221 break;
6222
df50185b
LP
6223 case 'o':
6224 arg_output = output_mode_from_string(optarg);
6225 if (arg_output < 0) {
6226 log_error("Unknown output '%s'.", optarg);
6227 return -EINVAL;
6228 }
6229 break;
6230
b37844d3
LP
6231 case 'i':
6232 arg_ignore_inhibitors = true;
6233 break;
6234
5d0c05e5
LN
6235 case ARG_PLAIN:
6236 arg_plain = true;
6237 break;
6238
9b9b3d36 6239 case ARG_STATE: {
a2a5291b 6240 const char *word, *state;
9b9b3d36
MW
6241 size_t size;
6242
6243 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
6244 char *s;
6245
6246 s = strndup(word, size);
6247 if (!s)
6248 return log_oom();
6249
6e18964d 6250 if (strv_consume(&arg_states, s) < 0)
9b9b3d36 6251 return log_oom();
9b9b3d36
MW
6252 }
6253 break;
6254 }
6255
1238ee09
LP
6256 case 'r':
6257 if (geteuid() != 0) {
f1721625 6258 log_error("--recursive requires root privileges.");
1238ee09
LP
6259 return -EPERM;
6260 }
6261
6262 arg_recursive = true;
6263 break;
6264
d309c1c3
LP
6265 case ARG_PRESET_MODE:
6266
6267 arg_preset_mode = unit_file_preset_mode_from_string(optarg);
6268 if (arg_preset_mode < 0) {
6269 log_error("Failed to parse preset mode: %s.", optarg);
6270 return -EINVAL;
6271 }
6272
6273 break;
6274
7e4249b9
LP
6275 case '?':
6276 return -EINVAL;
6277
6278 default:
eb9da376 6279 assert_not_reached("Unhandled option");
7e4249b9 6280 }
7e4249b9 6281
f459b602 6282 if (arg_transport != BUS_TRANSPORT_LOCAL && arg_scope != UNIT_FILE_SYSTEM) {
a8f11321
LP
6283 log_error("Cannot access user instance remotely.");
6284 return -EINVAL;
6285 }
6286
7e4249b9
LP
6287 return 1;
6288}
6289
e4b61340
LP
6290static int halt_parse_argv(int argc, char *argv[]) {
6291
6292 enum {
6293 ARG_HELP = 0x100,
6294 ARG_HALT,
514f4ef5
LP
6295 ARG_REBOOT,
6296 ARG_NO_WALL
e4b61340
LP
6297 };
6298
6299 static const struct option options[] = {
6300 { "help", no_argument, NULL, ARG_HELP },
6301 { "halt", no_argument, NULL, ARG_HALT },
6302 { "poweroff", no_argument, NULL, 'p' },
6303 { "reboot", no_argument, NULL, ARG_REBOOT },
6304 { "force", no_argument, NULL, 'f' },
6305 { "wtmp-only", no_argument, NULL, 'w' },
6306 { "no-wtmp", no_argument, NULL, 'd' },
514f4ef5 6307 { "no-wall", no_argument, NULL, ARG_NO_WALL },
eb9da376 6308 {}
e4b61340
LP
6309 };
6310
37185ec8 6311 int c, r, runlevel;
e4b61340
LP
6312
6313 assert(argc >= 0);
6314 assert(argv);
6315
6316 if (utmp_get_runlevel(&runlevel, NULL) >= 0)
6317 if (runlevel == '0' || runlevel == '6')
65491fd8 6318 arg_force = 2;
e4b61340 6319
601185b4 6320 while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0)
e4b61340
LP
6321 switch (c) {
6322
6323 case ARG_HELP:
601185b4
ZJS
6324 halt_help();
6325 return 0;
e4b61340
LP
6326
6327 case ARG_HALT:
6328 arg_action = ACTION_HALT;
6329 break;
6330
6331 case 'p':
a042efad
MS
6332 if (arg_action != ACTION_REBOOT)
6333 arg_action = ACTION_POWEROFF;
e4b61340
LP
6334 break;
6335
6336 case ARG_REBOOT:
6337 arg_action = ACTION_REBOOT;
6338 break;
6339
6340 case 'f':
65491fd8 6341 arg_force = 2;
e4b61340
LP
6342 break;
6343
6344 case 'w':
6345 arg_dry = true;
6346 break;
6347
6348 case 'd':
6349 arg_no_wtmp = true;
6350 break;
6351
514f4ef5
LP
6352 case ARG_NO_WALL:
6353 arg_no_wall = true;
6354 break;
6355
e4b61340
LP
6356 case 'i':
6357 case 'h':
57371e58 6358 case 'n':
e4b61340
LP
6359 /* Compatibility nops */
6360 break;
6361
6362 case '?':
6363 return -EINVAL;
6364
6365 default:
eb9da376 6366 assert_not_reached("Unhandled option");
e4b61340 6367 }
e4b61340 6368
c5220a94
MO
6369 if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) {
6370 r = update_reboot_param_file(argc == optind + 1 ? argv[optind] : NULL);
6371 if (r < 0)
37185ec8 6372 return r;
37185ec8 6373 } else if (optind < argc) {
e4b61340
LP
6374 log_error("Too many arguments.");
6375 return -EINVAL;
6376 }
6377
6378 return 1;
6379}
6380
f6144808
LP
6381static int parse_time_spec(const char *t, usec_t *_u) {
6382 assert(t);
6383 assert(_u);
6384
6385 if (streq(t, "now"))
6386 *_u = 0;
1a639877 6387 else if (!strchr(t, ':')) {
f6144808
LP
6388 uint64_t u;
6389
1a639877 6390 if (safe_atou64(t, &u) < 0)
f6144808
LP
6391 return -EINVAL;
6392
6393 *_u = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u;
6394 } else {
6395 char *e = NULL;
6396 long hour, minute;
b92bea5d 6397 struct tm tm = {};
f6144808
LP
6398 time_t s;
6399 usec_t n;
6400
6401 errno = 0;
6402 hour = strtol(t, &e, 10);
8333c77e 6403 if (errno > 0 || *e != ':' || hour < 0 || hour > 23)
f6144808
LP
6404 return -EINVAL;
6405
6406 minute = strtol(e+1, &e, 10);
8333c77e 6407 if (errno > 0 || *e != 0 || minute < 0 || minute > 59)
f6144808
LP
6408 return -EINVAL;
6409
6410 n = now(CLOCK_REALTIME);
08e4b1c5
LP
6411 s = (time_t) (n / USEC_PER_SEC);
6412
f6144808
LP
6413 assert_se(localtime_r(&s, &tm));
6414
6415 tm.tm_hour = (int) hour;
6416 tm.tm_min = (int) minute;
08e4b1c5 6417 tm.tm_sec = 0;
f6144808
LP
6418
6419 assert_se(s = mktime(&tm));
6420
6421 *_u = (usec_t) s * USEC_PER_SEC;
6422
6423 while (*_u <= n)
6424 *_u += USEC_PER_DAY;
6425 }
6426
6427 return 0;
6428}
6429
e4b61340
LP
6430static int shutdown_parse_argv(int argc, char *argv[]) {
6431
6432 enum {
6433 ARG_HELP = 0x100,
514f4ef5 6434 ARG_NO_WALL
e4b61340
LP
6435 };
6436
6437 static const struct option options[] = {
6438 { "help", no_argument, NULL, ARG_HELP },
6439 { "halt", no_argument, NULL, 'H' },
6440 { "poweroff", no_argument, NULL, 'P' },
6441 { "reboot", no_argument, NULL, 'r' },
04ebb595 6442 { "kexec", no_argument, NULL, 'K' }, /* not documented extension */
514f4ef5 6443 { "no-wall", no_argument, NULL, ARG_NO_WALL },
eb9da376 6444 {}
e4b61340
LP
6445 };
6446
f6144808 6447 int c, r;
e4b61340
LP
6448
6449 assert(argc >= 0);
6450 assert(argv);
6451
601185b4 6452 while ((c = getopt_long(argc, argv, "HPrhkt:afFc", options, NULL)) >= 0)
e4b61340
LP
6453 switch (c) {
6454
6455 case ARG_HELP:
601185b4
ZJS
6456 shutdown_help();
6457 return 0;
e4b61340
LP
6458
6459 case 'H':
6460 arg_action = ACTION_HALT;
6461 break;
6462
6463 case 'P':
6464 arg_action = ACTION_POWEROFF;
6465 break;
6466
6467 case 'r':
5622dde3
KS
6468 if (kexec_loaded())
6469 arg_action = ACTION_KEXEC;
6470 else
6471 arg_action = ACTION_REBOOT;
e4b61340
LP
6472 break;
6473
04ebb595
LP
6474 case 'K':
6475 arg_action = ACTION_KEXEC;
6476 break;
6477
e4b61340
LP
6478 case 'h':
6479 if (arg_action != ACTION_HALT)
6480 arg_action = ACTION_POWEROFF;
6481 break;
6482
6483 case 'k':
6484 arg_dry = true;
6485 break;
6486
514f4ef5
LP
6487 case ARG_NO_WALL:
6488 arg_no_wall = true;
6489 break;
6490
e4b61340
LP
6491 case 't':
6492 case 'a':
6493 /* Compatibility nops */
6494 break;
6495
f6144808
LP
6496 case 'c':
6497 arg_action = ACTION_CANCEL_SHUTDOWN;
6498 break;
6499
e4b61340
LP
6500 case '?':
6501 return -EINVAL;
6502
6503 default:
eb9da376 6504 assert_not_reached("Unhandled option");
e4b61340 6505 }
e4b61340 6506
dfcc5c33 6507 if (argc > optind && arg_action != ACTION_CANCEL_SHUTDOWN) {
7e59bfcb
LP
6508 r = parse_time_spec(argv[optind], &arg_when);
6509 if (r < 0) {
f6144808
LP
6510 log_error("Failed to parse time specification: %s", argv[optind]);
6511 return r;
6512 }
6b5ad000 6513 } else
08e4b1c5 6514 arg_when = now(CLOCK_REALTIME) + USEC_PER_MINUTE;
442b9094 6515
dfcc5c33
MS
6516 if (argc > optind && arg_action == ACTION_CANCEL_SHUTDOWN)
6517 /* No time argument for shutdown cancel */
6518 arg_wall = argv + optind;
6519 else if (argc > optind + 1)
6520 /* We skip the time argument */
e4b61340
LP
6521 arg_wall = argv + optind + 1;
6522
6523 optind = argc;
6524
6525 return 1;
e4b61340
LP
6526}
6527
6528static int telinit_parse_argv(int argc, char *argv[]) {
6529
6530 enum {
6531 ARG_HELP = 0x100,
514f4ef5 6532 ARG_NO_WALL
e4b61340
LP
6533 };
6534
6535 static const struct option options[] = {
6536 { "help", no_argument, NULL, ARG_HELP },
514f4ef5 6537 { "no-wall", no_argument, NULL, ARG_NO_WALL },
eb9da376 6538 {}
e4b61340
LP
6539 };
6540
6541 static const struct {
6542 char from;
6543 enum action to;
6544 } table[] = {
6545 { '0', ACTION_POWEROFF },
6546 { '6', ACTION_REBOOT },
ef2f1067 6547 { '1', ACTION_RESCUE },
e4b61340
LP
6548 { '2', ACTION_RUNLEVEL2 },
6549 { '3', ACTION_RUNLEVEL3 },
6550 { '4', ACTION_RUNLEVEL4 },
6551 { '5', ACTION_RUNLEVEL5 },
6552 { 's', ACTION_RESCUE },
6553 { 'S', ACTION_RESCUE },
6554 { 'q', ACTION_RELOAD },
6555 { 'Q', ACTION_RELOAD },
6556 { 'u', ACTION_REEXEC },
6557 { 'U', ACTION_REEXEC }
6558 };
6559
6560 unsigned i;
6561 int c;
6562
6563 assert(argc >= 0);
6564 assert(argv);
6565
601185b4 6566 while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0)
e4b61340
LP
6567 switch (c) {
6568
6569 case ARG_HELP:
601185b4
ZJS
6570 telinit_help();
6571 return 0;
e4b61340 6572
514f4ef5
LP
6573 case ARG_NO_WALL:
6574 arg_no_wall = true;
6575 break;
6576
e4b61340
LP
6577 case '?':
6578 return -EINVAL;
6579
6580 default:
eb9da376 6581 assert_not_reached("Unhandled option");
e4b61340 6582 }
e4b61340
LP
6583
6584 if (optind >= argc) {
601185b4
ZJS
6585 log_error("%s: required argument missing.",
6586 program_invocation_short_name);
e4b61340
LP
6587 return -EINVAL;
6588 }
6589
6590 if (optind + 1 < argc) {
6591 log_error("Too many arguments.");
6592 return -EINVAL;
6593 }
6594
6595 if (strlen(argv[optind]) != 1) {
6596 log_error("Expected single character argument.");
6597 return -EINVAL;
6598 }
6599
6600 for (i = 0; i < ELEMENTSOF(table); i++)
6601 if (table[i].from == argv[optind][0])
6602 break;
6603
6604 if (i >= ELEMENTSOF(table)) {
b0193f1c 6605 log_error("Unknown command '%s'.", argv[optind]);
e4b61340
LP
6606 return -EINVAL;
6607 }
6608
6609 arg_action = table[i].to;
6610
6611 optind ++;
6612
6613 return 1;
6614}
6615
6616static int runlevel_parse_argv(int argc, char *argv[]) {
6617
6618 enum {
6619 ARG_HELP = 0x100,
6620 };
6621
6622 static const struct option options[] = {
6623 { "help", no_argument, NULL, ARG_HELP },
eb9da376 6624 {}
e4b61340
LP
6625 };
6626
6627 int c;
6628
6629 assert(argc >= 0);
6630 assert(argv);
6631
601185b4 6632 while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0)
e4b61340
LP
6633 switch (c) {
6634
6635 case ARG_HELP:
601185b4
ZJS
6636 runlevel_help();
6637 return 0;
e4b61340
LP
6638
6639 case '?':
6640 return -EINVAL;
6641
6642 default:
eb9da376 6643 assert_not_reached("Unhandled option");
e4b61340 6644 }
e4b61340
LP
6645
6646 if (optind < argc) {
6647 log_error("Too many arguments.");
6648 return -EINVAL;
6649 }
6650
6651 return 1;
6652}
6653
6654static int parse_argv(int argc, char *argv[]) {
6655 assert(argc >= 0);
6656 assert(argv);
6657
6658 if (program_invocation_short_name) {
6659
6660 if (strstr(program_invocation_short_name, "halt")) {
6661 arg_action = ACTION_HALT;
6662 return halt_parse_argv(argc, argv);
6663 } else if (strstr(program_invocation_short_name, "poweroff")) {
6664 arg_action = ACTION_POWEROFF;
6665 return halt_parse_argv(argc, argv);
6666 } else if (strstr(program_invocation_short_name, "reboot")) {
5622dde3
KS
6667 if (kexec_loaded())
6668 arg_action = ACTION_KEXEC;
6669 else
6670 arg_action = ACTION_REBOOT;
e4b61340
LP
6671 return halt_parse_argv(argc, argv);
6672 } else if (strstr(program_invocation_short_name, "shutdown")) {
6673 arg_action = ACTION_POWEROFF;
6674 return shutdown_parse_argv(argc, argv);
6675 } else if (strstr(program_invocation_short_name, "init")) {
d5ca5f11
LP
6676
6677 if (sd_booted() > 0) {
f459b602 6678 arg_action = _ACTION_INVALID;
d5ca5f11
LP
6679 return telinit_parse_argv(argc, argv);
6680 } else {
6681 /* Hmm, so some other init system is
6682 * running, we need to forward this
6683 * request to it. For now we simply
6684 * guess that it is Upstart. */
6685
4ad61fd1 6686 execv(TELINIT, argv);
d5ca5f11
LP
6687
6688 log_error("Couldn't find an alternative telinit implementation to spawn.");
6689 return -EIO;
6690 }
6691
e4b61340
LP
6692 } else if (strstr(program_invocation_short_name, "runlevel")) {
6693 arg_action = ACTION_RUNLEVEL;
6694 return runlevel_parse_argv(argc, argv);
6695 }
6696 }
6697
6698 arg_action = ACTION_SYSTEMCTL;
6699 return systemctl_parse_argv(argc, argv);
6700}
6701
44a6b1b6 6702_pure_ static int action_to_runlevel(void) {
eb22ac37
LP
6703
6704 static const char table[_ACTION_MAX] = {
6705 [ACTION_HALT] = '0',
6706 [ACTION_POWEROFF] = '0',
6707 [ACTION_REBOOT] = '6',
6708 [ACTION_RUNLEVEL2] = '2',
6709 [ACTION_RUNLEVEL3] = '3',
6710 [ACTION_RUNLEVEL4] = '4',
6711 [ACTION_RUNLEVEL5] = '5',
6712 [ACTION_RESCUE] = '1'
6713 };
6714
d55ae9e6
LP
6715 assert(arg_action < _ACTION_MAX);
6716
6717 return table[arg_action];
6718}
6719
d55ae9e6 6720static int talk_initctl(void) {
cbc9fbd1
LP
6721
6722 struct init_request request = {
6723 .magic = INIT_MAGIC,
6724 .sleeptime = 0,
6725 .cmd = INIT_CMD_RUNLVL
6726 };
6727
7fd1b19b 6728 _cleanup_close_ int fd = -1;
d55ae9e6 6729 char rl;
cbc9fbd1 6730 int r;
eb22ac37 6731
427b47c4
ZJS
6732 rl = action_to_runlevel();
6733 if (!rl)
eb22ac37
LP
6734 return 0;
6735
d55ae9e6
LP
6736 request.runlevel = rl;
6737
427b47c4
ZJS
6738 fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY);
6739 if (fd < 0) {
d55ae9e6
LP
6740 if (errno == ENOENT)
6741 return 0;
eb22ac37 6742
d55ae9e6 6743 log_error("Failed to open "INIT_FIFO": %m");
eb22ac37 6744 return -errno;
d55ae9e6 6745 }
eb22ac37 6746
d55ae9e6 6747 errno = 0;
eb22ac37 6748 r = loop_write(fd, &request, sizeof(request), false) != sizeof(request);
427b47c4 6749 if (r) {
d55ae9e6 6750 log_error("Failed to write to "INIT_FIFO": %m");
bcb161b0 6751 return errno > 0 ? -errno : -EIO;
d55ae9e6 6752 }
eb22ac37
LP
6753
6754 return 1;
e4b61340
LP
6755}
6756
41dd15e4 6757static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
7e4249b9 6758
7e4249b9
LP
6759 static const struct {
6760 const char* verb;
6761 const enum {
6762 MORE,
6763 LESS,
6764 EQUAL
6765 } argc_cmp;
6766 const int argc;
f459b602 6767 int (* const dispatch)(sd_bus *bus, char **args);
d08e75ed
ZJS
6768 const enum {
6769 NOBUS = 1,
6770 FORCE,
6771 } bus;
7e4249b9 6772 } verbs[] = {
d8fba7c6 6773 { "list-units", MORE, 0, list_units },
d08e75ed 6774 { "list-unit-files", MORE, 1, list_unit_files, NOBUS },
d8fba7c6
ZJS
6775 { "list-sockets", MORE, 1, list_sockets },
6776 { "list-timers", MORE, 1, list_timers },
6777 { "list-jobs", MORE, 1, list_jobs },
0d292f5e 6778 { "list-machines", MORE, 1, list_machines },
ee5762e3 6779 { "clear-jobs", EQUAL, 1, daemon_reload },
ee5762e3
LP
6780 { "cancel", MORE, 2, cancel_job },
6781 { "start", MORE, 2, start_unit },
6782 { "stop", MORE, 2, start_unit },
a76f7be2 6783 { "condstop", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
ee5762e3
LP
6784 { "reload", MORE, 2, start_unit },
6785 { "restart", MORE, 2, start_unit },
6786 { "try-restart", MORE, 2, start_unit },
6787 { "reload-or-restart", MORE, 2, start_unit },
6788 { "reload-or-try-restart", MORE, 2, start_unit },
6789 { "force-reload", MORE, 2, start_unit }, /* For compatibility with SysV */
64e5f1b7 6790 { "condreload", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
ee5762e3
LP
6791 { "condrestart", MORE, 2, start_unit }, /* For compatibility with RH */
6792 { "isolate", EQUAL, 2, start_unit },
8a0867d6 6793 { "kill", MORE, 2, kill_unit },
1a0fce45
TA
6794 { "is-active", MORE, 2, check_unit_active },
6795 { "check", MORE, 2, check_unit_active },
6796 { "is-failed", MORE, 2, check_unit_failed },
ee5762e3 6797 { "show", MORE, 1, show },
e93c33d4 6798 { "cat", MORE, 2, cat },
265a7a2a 6799 { "status", MORE, 1, show },
b43f208f 6800 { "help", MORE, 2, show },
ee5762e3
LP
6801 { "snapshot", LESS, 2, snapshot },
6802 { "delete", MORE, 2, delete_snapshot },
6803 { "daemon-reload", EQUAL, 1, daemon_reload },
6804 { "daemon-reexec", EQUAL, 1, daemon_reload },
f459b602 6805 { "show-environment", EQUAL, 1, show_environment },
ee5762e3
LP
6806 { "set-environment", MORE, 2, set_environment },
6807 { "unset-environment", MORE, 2, set_environment },
ac3efa8a 6808 { "import-environment", MORE, 1, import_environment},
d08e75ed
ZJS
6809 { "halt", EQUAL, 1, start_special, FORCE },
6810 { "poweroff", EQUAL, 1, start_special, FORCE },
6811 { "reboot", EQUAL, 1, start_special, FORCE },
20b09ca7 6812 { "kexec", EQUAL, 1, start_special },
6edd7d0a
LP
6813 { "suspend", EQUAL, 1, start_special },
6814 { "hibernate", EQUAL, 1, start_special },
6524990f 6815 { "hybrid-sleep", EQUAL, 1, start_special },
ee5762e3
LP
6816 { "default", EQUAL, 1, start_special },
6817 { "rescue", EQUAL, 1, start_special },
6818 { "emergency", EQUAL, 1, start_special },
20b09ca7 6819 { "exit", EQUAL, 1, start_special },
fdf20a31 6820 { "reset-failed", MORE, 1, reset_failed },
d08e75ed
ZJS
6821 { "enable", MORE, 2, enable_unit, NOBUS },
6822 { "disable", MORE, 2, enable_unit, NOBUS },
6823 { "is-enabled", MORE, 2, unit_is_enabled, NOBUS },
6824 { "reenable", MORE, 2, enable_unit, NOBUS },
6825 { "preset", MORE, 2, enable_unit, NOBUS },
d309c1c3 6826 { "preset-all", EQUAL, 1, preset_all, NOBUS },
d08e75ed
ZJS
6827 { "mask", MORE, 2, enable_unit, NOBUS },
6828 { "unmask", MORE, 2, enable_unit, NOBUS },
6829 { "link", MORE, 2, enable_unit, NOBUS },
957eb8ca 6830 { "switch-root", MORE, 2, switch_root },
e31165b2 6831 { "list-dependencies", LESS, 2, list_dependencies },
d08e75ed
ZJS
6832 { "set-default", EQUAL, 2, set_default, NOBUS },
6833 { "get-default", EQUAL, 1, get_default, NOBUS },
8e2af478 6834 { "set-property", MORE, 3, set_property },
99813a19 6835 { "is-system-running", EQUAL, 1, is_system_running },
e94937df
LN
6836 { "add-wants", MORE, 3, add_dependency, NOBUS },
6837 { "add-requires", MORE, 3, add_dependency, NOBUS },
d08e75ed
ZJS
6838 {}
6839 }, *verb = verbs;
7e4249b9 6840
e4b61340 6841 int left;
7e4249b9 6842
e4b61340
LP
6843 assert(argc >= 0);
6844 assert(argv);
7e4249b9
LP
6845
6846 left = argc - optind;
6847
d08e75ed
ZJS
6848 /* Special rule: no arguments (left == 0) means "list-units" */
6849 if (left > 0) {
b43f208f
KS
6850 if (streq(argv[optind], "help") && !argv[optind+1]) {
6851 log_error("This command expects one or more "
6852 "unit names. Did you mean --help?");
6853 return -EINVAL;
0183528f
LP
6854 }
6855
d08e75ed
ZJS
6856 for (; verb->verb; verb++)
6857 if (streq(argv[optind], verb->verb))
6858 goto found;
7e4249b9 6859
d08e75ed
ZJS
6860 log_error("Unknown operation '%s'.", argv[optind]);
6861 return -EINVAL;
7e4249b9 6862 }
d08e75ed 6863found:
7e4249b9 6864
d08e75ed 6865 switch (verb->argc_cmp) {
7e4249b9
LP
6866
6867 case EQUAL:
d08e75ed 6868 if (left != verb->argc) {
7e4249b9 6869 log_error("Invalid number of arguments.");
e4b61340 6870 return -EINVAL;
7e4249b9
LP
6871 }
6872
6873 break;
6874
6875 case MORE:
d08e75ed 6876 if (left < verb->argc) {
7e4249b9 6877 log_error("Too few arguments.");
e4b61340 6878 return -EINVAL;
7e4249b9
LP
6879 }
6880
6881 break;
6882
6883 case LESS:
d08e75ed 6884 if (left > verb->argc) {
7e4249b9 6885 log_error("Too many arguments.");
e4b61340 6886 return -EINVAL;
7e4249b9
LP
6887 }
6888
6889 break;
6890
6891 default:
6892 assert_not_reached("Unknown comparison operator.");
6893 }
6894
ee5762e3
LP
6895 /* Require a bus connection for all operations but
6896 * enable/disable */
d08e75ed
ZJS
6897 if (verb->bus == NOBUS) {
6898 if (!bus && !avoid_bus()) {
6899 log_error("Failed to get D-Bus connection: %s", strerror(-bus_error));
6900 return -EIO;
6901 }
82e23ddd 6902
d08e75ed 6903 } else {
82e23ddd
LP
6904 if (running_in_chroot() > 0) {
6905 log_info("Running in chroot, ignoring request.");
6906 return 0;
6907 }
6908
d08e75ed
ZJS
6909 if ((verb->bus != FORCE || arg_force <= 0) && !bus) {
6910 log_error("Failed to get D-Bus connection: %s", strerror(-bus_error));
82e23ddd
LP
6911 return -EIO;
6912 }
ee5762e3
LP
6913 }
6914
d08e75ed 6915 return verb->dispatch(bus, argv + optind);
e4b61340
LP
6916}
6917
52c00215 6918static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
cbc9fbd1 6919
b92bea5d
ZJS
6920 struct sd_shutdown_command c = {
6921 .usec = t,
6922 .mode = mode,
6923 .dry_run = dry_run,
6924 .warn_wall = warn,
6925 };
cbc9fbd1 6926
b92bea5d
ZJS
6927 union sockaddr_union sockaddr = {
6928 .un.sun_family = AF_UNIX,
6929 .un.sun_path = "/run/systemd/shutdownd",
6930 };
cbc9fbd1
LP
6931
6932 struct iovec iovec[2] = {{
6933 .iov_base = (char*) &c,
b92bea5d 6934 .iov_len = offsetof(struct sd_shutdown_command, wall_message),
cbc9fbd1
LP
6935 }};
6936
b92bea5d
ZJS
6937 struct msghdr msghdr = {
6938 .msg_name = &sockaddr,
6939 .msg_namelen = offsetof(struct sockaddr_un, sun_path)
f8294e41 6940 + strlen("/run/systemd/shutdownd"),
b92bea5d
ZJS
6941 .msg_iov = iovec,
6942 .msg_iovlen = 1,
6943 };
04ebb595 6944
cbc9fbd1
LP
6945 _cleanup_close_ int fd;
6946
04ebb595
LP
6947 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
6948 if (fd < 0)
6949 return -errno;
f6144808 6950
b92bea5d 6951 if (!isempty(message)) {
04ebb595
LP
6952 iovec[1].iov_base = (char*) message;
6953 iovec[1].iov_len = strlen(message);
b92bea5d 6954 msghdr.msg_iovlen++;
04ebb595 6955 }
f6144808 6956
cec7eda5 6957 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0)
f6144808 6958 return -errno;
f6144808 6959
f6144808
LP
6960 return 0;
6961}
6962
f459b602 6963static int reload_with_fallback(sd_bus *bus) {
e4b61340
LP
6964
6965 if (bus) {
6966 /* First, try systemd via D-Bus. */
d76702a7 6967 if (daemon_reload(bus, NULL) >= 0)
e4b61340
LP
6968 return 0;
6969 }
6970
6971 /* Nothing else worked, so let's try signals */
6972 assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC);
6973
6974 if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) {
6975 log_error("kill() failed: %m");
6976 return -errno;
6977 }
6978
6979 return 0;
6980}
6981
f459b602 6982static int start_with_fallback(sd_bus *bus) {
e4b61340
LP
6983
6984 if (bus) {
6985 /* First, try systemd via D-Bus. */
729e3769 6986 if (start_unit(bus, NULL) >= 0)
983d9c90 6987 goto done;
e4b61340
LP
6988 }
6989
6990 /* Nothing else worked, so let's try
6991 * /dev/initctl */
fbc43921 6992 if (talk_initctl() > 0)
983d9c90 6993 goto done;
d55ae9e6
LP
6994
6995 log_error("Failed to talk to init daemon.");
6996 return -EIO;
983d9c90
LP
6997
6998done:
6999 warn_wall(arg_action);
7000 return 0;
e4b61340
LP
7001}
7002
477def80 7003static int halt_now(enum action a) {
e606bb61 7004
4a3ad399
LP
7005 /* The kernel will automaticall flush ATA disks and suchlike
7006 * on reboot(), but the file systems need to be synce'd
7007 * explicitly in advance. */
7008 sync();
7009
7010 /* Make sure C-A-D is handled by the kernel from this point
7011 * on... */
e606bb61
LP
7012 reboot(RB_ENABLE_CAD);
7013
4c80c73c 7014 switch (a) {
e606bb61
LP
7015
7016 case ACTION_HALT:
7017 log_info("Halting.");
7018 reboot(RB_HALT_SYSTEM);
477def80 7019 return -errno;
e606bb61
LP
7020
7021 case ACTION_POWEROFF:
7022 log_info("Powering off.");
7023 reboot(RB_POWER_OFF);
477def80 7024 return -errno;
e606bb61 7025
477def80
LP
7026 case ACTION_REBOOT: {
7027 _cleanup_free_ char *param = NULL;
cbc9fbd1 7028
477def80
LP
7029 if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
7030 log_info("Rebooting with argument '%s'.", param);
37185ec8
WC
7031 syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
7032 LINUX_REBOOT_CMD_RESTART2, param);
37185ec8 7033 }
e606bb61 7034
477def80
LP
7035 log_info("Rebooting.");
7036 reboot(RB_AUTOBOOT);
7037 return -errno;
e606bb61
LP
7038 }
7039
477def80
LP
7040 default:
7041 assert_not_reached("Unknown action.");
7042 }
e606bb61
LP
7043}
7044
f459b602 7045static int halt_main(sd_bus *bus) {
e4b61340
LP
7046 int r;
7047
748ebafa
LP
7048 r = check_inhibitors(bus, arg_action);
7049 if (r < 0)
7050 return r;
b37844d3 7051
bc8c2f5c 7052 if (geteuid() != 0) {
7e59bfcb
LP
7053 /* Try logind if we are a normal user and no special
7054 * mode applies. Maybe PolicyKit allows us to shutdown
7055 * the machine. */
7056
7057 if (arg_when <= 0 &&
7058 !arg_dry &&
b37844d3 7059 arg_force <= 0 &&
7e59bfcb
LP
7060 (arg_action == ACTION_POWEROFF ||
7061 arg_action == ACTION_REBOOT)) {
4c80c73c
KS
7062 r = reboot_with_logind(bus, arg_action);
7063 if (r >= 0)
7064 return r;
7065 }
7066
cc8a7a61 7067 log_error("Must be root.");
bc8c2f5c
LP
7068 return -EPERM;
7069 }
7070
f6144808 7071 if (arg_when > 0) {
7fd1b19b 7072 _cleanup_free_ char *m;
9be9828c
LP
7073
7074 m = strv_join(arg_wall, " ");
cbc9fbd1
LP
7075 if (!m)
7076 return log_oom();
7077
9be9828c
LP
7078 r = send_shutdownd(arg_when,
7079 arg_action == ACTION_HALT ? 'H' :
7080 arg_action == ACTION_POWEROFF ? 'P' :
04ebb595 7081 arg_action == ACTION_KEXEC ? 'K' :
9be9828c 7082 'r',
52c00215 7083 arg_dry,
9be9828c
LP
7084 !arg_no_wall,
7085 m);
9be9828c
LP
7086
7087 if (r < 0)
f6144808 7088 log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r));
08e4b1c5 7089 else {
7e59bfcb
LP
7090 char date[FORMAT_TIMESTAMP_MAX];
7091
08e4b1c5
LP
7092 log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.",
7093 format_timestamp(date, sizeof(date), arg_when));
f6144808 7094 return 0;
08e4b1c5 7095 }
f6144808
LP
7096 }
7097
65491fd8 7098 if (!arg_dry && !arg_force)
e4b61340
LP
7099 return start_with_fallback(bus);
7100
d90e1a30
LP
7101 if (!arg_no_wtmp) {
7102 if (sd_booted() > 0)
7103 log_debug("Not writing utmp record, assuming that systemd-update-utmp is used.");
7e59bfcb
LP
7104 else {
7105 r = utmp_put_shutdown();
7106 if (r < 0)
7107 log_warning("Failed to write utmp record: %s", strerror(-r));
7108 }
d90e1a30 7109 }
e4b61340 7110
e4b61340
LP
7111 if (arg_dry)
7112 return 0;
7113
477def80
LP
7114 r = halt_now(arg_action);
7115 log_error("Failed to reboot: %s", strerror(-r));
7116
7117 return r;
e4b61340
LP
7118}
7119
7120static int runlevel_main(void) {
7121 int r, runlevel, previous;
7122
729e3769
LP
7123 r = utmp_get_runlevel(&runlevel, &previous);
7124 if (r < 0) {
7125 puts("unknown");
e4b61340
LP
7126 return r;
7127 }
7128
7129 printf("%c %c\n",
7130 previous <= 0 ? 'N' : previous,
7131 runlevel <= 0 ? 'N' : runlevel);
7132
7133 return 0;
7134}
7135
7136int main(int argc, char*argv[]) {
24996861 7137 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
f459b602 7138 int r;
e4b61340 7139
a9cdc94f 7140 setlocale(LC_ALL, "");
e4b61340 7141 log_parse_environment();
2396fb04 7142 log_open();
e4b61340 7143
184ecaf7
DR
7144 /* Explicitly not on_tty() to avoid setting cached value.
7145 * This becomes relevant for piping output which might be
7146 * ellipsized. */
7147 original_stdout_is_tty = isatty(STDOUT_FILENO);
7148
04ebb595 7149 r = parse_argv(argc, argv);
f459b602 7150 if (r <= 0)
e4b61340 7151 goto finish;
7e4249b9 7152
e4b61340
LP
7153 /* /sbin/runlevel doesn't need to communicate via D-Bus, so
7154 * let's shortcut this */
7155 if (arg_action == ACTION_RUNLEVEL) {
22f4096c 7156 r = runlevel_main();
e4b61340
LP
7157 goto finish;
7158 }
7159
82e23ddd
LP
7160 if (running_in_chroot() > 0 && arg_action != ACTION_SYSTEMCTL) {
7161 log_info("Running in chroot, ignoring request.");
f459b602 7162 r = 0;
82e23ddd
LP
7163 goto finish;
7164 }
7165
41dd15e4
LP
7166 if (!avoid_bus())
7167 r = bus_open_transport_systemd(arg_transport, arg_host, arg_scope != UNIT_FILE_SYSTEM, &bus);
7168
7169 /* systemctl_main() will print an error message for the bus
7170 * connection, but only if it needs to */
e4b61340
LP
7171
7172 switch (arg_action) {
7173
22f4096c 7174 case ACTION_SYSTEMCTL:
f459b602 7175 r = systemctl_main(bus, argc, argv, r);
e4b61340 7176 break;
e4b61340
LP
7177
7178 case ACTION_HALT:
7179 case ACTION_POWEROFF:
7180 case ACTION_REBOOT:
5622dde3 7181 case ACTION_KEXEC:
22f4096c 7182 r = halt_main(bus);
e4b61340
LP
7183 break;
7184
e4b61340
LP
7185 case ACTION_RUNLEVEL2:
7186 case ACTION_RUNLEVEL3:
7187 case ACTION_RUNLEVEL4:
7188 case ACTION_RUNLEVEL5:
7189 case ACTION_RESCUE:
514f4ef5 7190 case ACTION_EMERGENCY:
eb22ac37 7191 case ACTION_DEFAULT:
22f4096c 7192 r = start_with_fallback(bus);
e4b61340 7193 break;
7e4249b9 7194
e4b61340
LP
7195 case ACTION_RELOAD:
7196 case ACTION_REEXEC:
22f4096c 7197 r = reload_with_fallback(bus);
e4b61340
LP
7198 break;
7199
dfcc5c33 7200 case ACTION_CANCEL_SHUTDOWN: {
f459b602 7201 _cleanup_free_ char *m = NULL;
dfcc5c33
MS
7202
7203 if (arg_wall) {
7204 m = strv_join(arg_wall, " ");
7205 if (!m) {
f459b602 7206 r = log_oom();
dfcc5c33
MS
7207 goto finish;
7208 }
7209 }
f459b602 7210
dfcc5c33
MS
7211 r = send_shutdownd(arg_when, SD_SHUTDOWN_NONE, false, !arg_no_wall, m);
7212 if (r < 0)
7213 log_warning("Failed to talk to shutdownd, shutdown hasn't been cancelled: %s", strerror(-r));
f6144808 7214 break;
dfcc5c33 7215 }
f6144808 7216
eb22ac37 7217 case ACTION_RUNLEVEL:
f459b602 7218 case _ACTION_INVALID:
e4b61340
LP
7219 default:
7220 assert_not_reached("Unknown action");
7221 }
7e4249b9
LP
7222
7223finish:
f459b602
MAP
7224 pager_close();
7225 ask_password_agent_close();
7226 polkit_agent_close();
7e4249b9 7227
20b3f379 7228 strv_free(arg_types);
9b9b3d36 7229 strv_free(arg_states);
20b3f379 7230 strv_free(arg_properties);
ea4a240d 7231
f459b602 7232 return r < 0 ? EXIT_FAILURE : r;
7e4249b9 7233}