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