]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/systemctl/systemctl.c
timesyncd: do not start in virtualized environments
[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
LP
458 if (circle_len > 0)
459 printf("%s%s%s", on_circle, circle ? draw_special_char(DRAW_BLACK_CIRCLE) : " ", off_circle);
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
1145 if (next->monotonic != (usec_t) -1 && next->monotonic > 0) {
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
1153 if (next->realtime != (usec_t) -1 && next->realtime > 0)
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)
749ebb2d 1931 log_info("Created symlink from %s to %s.", changes[i].source, changes[i].path);
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"))
749ebb2d 1948 log_info("Created symlink from %s to %s.", source, path);
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;
61cbdc4b
LP
3173
3174 usec_t start_timestamp;
3175 usec_t exit_timestamp;
3176
3177 int exit_code, exit_status;
3178
90bbc946
LP
3179 usec_t condition_timestamp;
3180 bool condition_result;
52990c2e
ZJS
3181 bool failed_condition_trigger;
3182 bool failed_condition_negate;
3183 const char *failed_condition;
3184 const char *failed_condition_param;
90bbc946 3185
61cbdc4b
LP
3186 /* Socket */
3187 unsigned n_accepted;
3188 unsigned n_connections;
b8131a87 3189 bool accept;
61cbdc4b 3190
13160134 3191 /* Pairs of type, path */
67419600
OS
3192 char **listen;
3193
61cbdc4b
LP
3194 /* Device */
3195 const char *sysfs_path;
3196
3197 /* Mount, Automount */
3198 const char *where;
3199
3200 /* Swap */
3201 const char *what;
582a507f
LP
3202
3203 LIST_HEAD(ExecStatusInfo, exec);
61cbdc4b
LP
3204} UnitStatusInfo;
3205
f459b602
MAP
3206static void print_status_info(
3207 UnitStatusInfo *i,
3208 bool *ellipsized) {
3209
582a507f 3210 ExecStatusInfo *p;
b0d14c69 3211 const char *active_on, *active_off, *on, *off, *ss;
584be568 3212 usec_t timestamp;
9185c8e6 3213 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
584be568 3214 char since2[FORMAT_TIMESTAMP_MAX], *s2;
1b64d026 3215 const char *path;
9bdbc2e2
LN
3216 int flags =
3217 arg_all * OUTPUT_SHOW_ALL |
3218 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
3219 on_tty() * OUTPUT_COLOR |
3220 !arg_quiet * OUTPUT_WARN_CUTOFF |
3221 arg_full * OUTPUT_FULL_WIDTH;
13160134 3222 char **t, **t2;
582a507f 3223
61cbdc4b
LP
3224 assert(i);
3225
3226 /* This shows pretty information about a unit. See
3227 * print_property() for a low-level property printer */
3228
b0d14c69
LP
3229 if (streq_ptr(i->active_state, "failed")) {
3230 active_on = ansi_highlight_red();
3231 active_off = ansi_highlight_off();
3232 } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
3233 active_on = ansi_highlight_green();
3234 active_off = ansi_highlight_off();
3235 } else
3236 active_on = active_off = "";
3237
6b01f1d3 3238 printf("%s%s%s %s", active_on, draw_special_char(DRAW_BLACK_CIRCLE), active_off, strna(i->id));
61cbdc4b
LP
3239
3240 if (i->description && !streq_ptr(i->id, i->description))
3241 printf(" - %s", i->description);
3242
3243 printf("\n");
3244
4a9e2fff 3245 if (i->following)
856323c9 3246 printf(" Follow: unit currently follows state of %s\n", i->following);
4a9e2fff 3247
f7b9e331 3248 if (streq_ptr(i->load_state, "error")) {
0b5a519c
DS
3249 on = ansi_highlight_red();
3250 off = ansi_highlight_off();
c31b4423
LP
3251 } else
3252 on = off = "";
3253
1b64d026
LP
3254 path = i->source_path ? i->source_path : i->fragment_path;
3255
9f39404c 3256 if (i->load_error)
856323c9
ZJS
3257 printf(" Loaded: %s%s%s (Reason: %s)\n",
3258 on, strna(i->load_state), off, i->load_error);
1b64d026 3259 else if (path && i->unit_file_state)
856323c9
ZJS
3260 printf(" Loaded: %s%s%s (%s; %s)\n",
3261 on, strna(i->load_state), off, path, i->unit_file_state);
1b64d026 3262 else if (path)
856323c9
ZJS
3263 printf(" Loaded: %s%s%s (%s)\n",
3264 on, strna(i->load_state), off, path);
61cbdc4b 3265 else
856323c9
ZJS
3266 printf(" Loaded: %s%s%s\n",
3267 on, strna(i->load_state), off);
61cbdc4b 3268
76d14b87 3269 if (!strv_isempty(i->dropin_paths)) {
f459b602 3270 _cleanup_free_ char *dir = NULL;
76d14b87 3271 bool last = false;
f459b602 3272 char ** dropin;
76d14b87
OS
3273
3274 STRV_FOREACH(dropin, i->dropin_paths) {
3275 if (! dir || last) {
856323c9 3276 printf(dir ? " " : " Drop-In: ");
76d14b87
OS
3277
3278 free(dir);
f459b602 3279 dir = NULL;
76d14b87
OS
3280
3281 if (path_get_parent(*dropin, &dir) < 0) {
3282 log_oom();
3283 return;
3284 }
3285
856323c9 3286 printf("%s\n %s", dir,
76d14b87
OS
3287 draw_special_char(DRAW_TREE_RIGHT));
3288 }
3289
3290 last = ! (*(dropin + 1) && startswith(*(dropin + 1), dir));
3291
2b6bf07d 3292 printf("%s%s", basename(*dropin), last ? "\n" : ", ");
76d14b87 3293 }
76d14b87
OS
3294 }
3295
2ee68f72 3296 ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
2ee68f72 3297 if (ss)
856323c9 3298 printf(" Active: %s%s (%s)%s",
b0d14c69 3299 active_on, strna(i->active_state), ss, active_off);
2ee68f72 3300 else
856323c9 3301 printf(" Active: %s%s%s",
b0d14c69 3302 active_on, strna(i->active_state), active_off);
61cbdc4b 3303
f42806df
LP
3304 if (!isempty(i->result) && !streq(i->result, "success"))
3305 printf(" (Result: %s)", i->result);
3306
584be568
LP
3307 timestamp = (streq_ptr(i->active_state, "active") ||
3308 streq_ptr(i->active_state, "reloading")) ? i->active_enter_timestamp :
3309 (streq_ptr(i->active_state, "inactive") ||
fdf20a31 3310 streq_ptr(i->active_state, "failed")) ? i->inactive_enter_timestamp :
584be568
LP
3311 streq_ptr(i->active_state, "activating") ? i->inactive_exit_timestamp :
3312 i->active_exit_timestamp;
3313
bbb8486e 3314 s1 = format_timestamp_relative(since1, sizeof(since1), timestamp);
584be568
LP
3315 s2 = format_timestamp(since2, sizeof(since2), timestamp);
3316
3317 if (s1)
538da63d 3318 printf(" since %s; %s\n", s2, s1);
584be568 3319 else if (s2)
538da63d 3320 printf(" since %s\n", s2);
584be568
LP
3321 else
3322 printf("\n");
3323
90bbc946 3324 if (!i->condition_result && i->condition_timestamp > 0) {
bbb8486e 3325 s1 = format_timestamp_relative(since1, sizeof(since1), i->condition_timestamp);
90bbc946
LP
3326 s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp);
3327
52990c2e
ZJS
3328 printf(" start condition failed at %s%s%s\n",
3329 s2, s1 ? "; " : "", s1 ? s1 : "");
3330 if (i->failed_condition_trigger)
3331 printf(" none of the trigger conditions were met\n");
3332 else if (i->failed_condition)
3333 printf(" %s=%s%s was not met\n",
3334 i->failed_condition,
3335 i->failed_condition_negate ? "!" : "",
3336 i->failed_condition_param);
90bbc946
LP
3337 }
3338
61cbdc4b 3339 if (i->sysfs_path)
856323c9 3340 printf(" Device: %s\n", i->sysfs_path);
9feeba4b 3341 if (i->where)
856323c9 3342 printf(" Where: %s\n", i->where);
9feeba4b 3343 if (i->what)
856323c9 3344 printf(" What: %s\n", i->what);
49dbfa7b 3345
13160134 3346 STRV_FOREACH(t, i->documentation)
856323c9 3347 printf(" %*s %s\n", 9, t == i->documentation ? "Docs:" : "", *t);
49dbfa7b 3348
13160134 3349 STRV_FOREACH_PAIR(t, t2, i->listen)
856323c9 3350 printf(" %*s %s (%s)\n", 9, t == i->listen ? "Listen:" : "", *t2, *t);
67419600 3351
b8131a87 3352 if (i->accept)
856323c9 3353 printf(" Accepted: %u; Connected: %u\n", i->n_accepted, i->n_connections);
61cbdc4b 3354
582a507f 3355 LIST_FOREACH(exec, p, i->exec) {
13160134 3356 _cleanup_free_ char *argv = NULL;
9a57c629 3357 bool good;
582a507f
LP
3358
3359 /* Only show exited processes here */
3360 if (p->code == 0)
3361 continue;
3362
13160134 3363 argv = strv_join(p->argv, " ");
856323c9 3364 printf(" Process: %u %s=%s ", p->pid, p->name, strna(argv));
582a507f 3365
96342de6 3366 good = is_clean_exit_lsb(p->code, p->status, NULL);
9a57c629 3367 if (!good) {
0b5a519c
DS
3368 on = ansi_highlight_red();
3369 off = ansi_highlight_off();
9a57c629
LP
3370 } else
3371 on = off = "";
3372
3373 printf("%s(code=%s, ", on, sigchld_code_to_string(p->code));
3374
d06dacd0
LP
3375 if (p->code == CLD_EXITED) {
3376 const char *c;
3377
582a507f 3378 printf("status=%i", p->status);
d06dacd0 3379
1b64d026
LP
3380 c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD);
3381 if (c)
d06dacd0
LP
3382 printf("/%s", c);
3383
3384 } else
582a507f 3385 printf("signal=%s", signal_to_string(p->status));
9a57c629
LP
3386
3387 printf(")%s\n", off);
3388
582a507f
LP
3389 if (i->main_pid == p->pid &&
3390 i->start_timestamp == p->start_timestamp &&
3391 i->exit_timestamp == p->start_timestamp)
3392 /* Let's not show this twice */
3393 i->main_pid = 0;
3394
3395 if (p->pid == i->control_pid)
3396 i->control_pid = 0;
3397 }
3398
61cbdc4b 3399 if (i->main_pid > 0 || i->control_pid > 0) {
61cbdc4b 3400 if (i->main_pid > 0) {
856323c9 3401 printf(" Main PID: %u", (unsigned) i->main_pid);
61cbdc4b
LP
3402
3403 if (i->running) {
13160134
ZJS
3404 _cleanup_free_ char *comm = NULL;
3405 get_process_comm(i->main_pid, &comm);
3406 if (comm)
3407 printf(" (%s)", comm);
6d4fc029 3408 } else if (i->exit_code > 0) {
61cbdc4b
LP
3409 printf(" (code=%s, ", sigchld_code_to_string(i->exit_code));
3410
d06dacd0
LP
3411 if (i->exit_code == CLD_EXITED) {
3412 const char *c;
3413
61cbdc4b 3414 printf("status=%i", i->exit_status);
d06dacd0 3415
1b64d026
LP
3416 c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD);
3417 if (c)
d06dacd0
LP
3418 printf("/%s", c);
3419
3420 } else
582a507f 3421 printf("signal=%s", signal_to_string(i->exit_status));
6d4fc029
LP
3422 printf(")");
3423 }
61cbdc4b 3424
13160134
ZJS
3425 if (i->control_pid > 0)
3426 printf(";");
3427 }
61cbdc4b
LP
3428
3429 if (i->control_pid > 0) {
13160134 3430 _cleanup_free_ char *c = NULL;
61cbdc4b 3431
856323c9 3432 printf(" %8s: %u", i->main_pid ? "" : " Control", (unsigned) i->control_pid);
61cbdc4b 3433
13160134
ZJS
3434 get_process_comm(i->control_pid, &c);
3435 if (c)
3436 printf(" (%s)", c);
61cbdc4b
LP
3437 }
3438
3439 printf("\n");
3440 }
3441
17bb7382 3442 if (i->status_text)
856323c9 3443 printf(" Status: \"%s\"\n", i->status_text);
17bb7382 3444
4ad49000 3445 if (i->control_group &&
8fcf784d
LP
3446 (i->main_pid > 0 || i->control_pid > 0 ||
3447 ((arg_transport != BUS_TRANSPORT_LOCAL && arg_transport != BUS_TRANSPORT_CONTAINER) || cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, i->control_group, false) == 0))) {
ab35fb1b
LP
3448 unsigned c;
3449
4ad49000 3450 printf(" CGroup: %s\n", i->control_group);
ab35fb1b 3451
7af5a806 3452 if (arg_transport == BUS_TRANSPORT_LOCAL || arg_transport == BUS_TRANSPORT_CONTAINER) {
b69d29ce
LP
3453 unsigned k = 0;
3454 pid_t extra[2];
8fcf784d 3455 static const char prefix[] = " ";
b69d29ce
LP
3456
3457 c = columns();
e8853816
ZJS
3458 if (c > sizeof(prefix) - 1)
3459 c -= sizeof(prefix) - 1;
a8f11321
LP
3460 else
3461 c = 0;
ab35fb1b 3462
b69d29ce
LP
3463 if (i->main_pid > 0)
3464 extra[k++] = i->main_pid;
3465
3466 if (i->control_pid > 0)
3467 extra[k++] = i->control_pid;
3468
8fcf784d 3469 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, i->control_group, prefix, c, false, extra, k, flags);
a8f11321 3470 }
c59760ee 3471 }
45fb0699 3472
f459b602 3473 if (i->id && arg_transport == BUS_TRANSPORT_LOCAL) {
886a64fe
ZJS
3474 show_journal_by_unit(stdout,
3475 i->id,
3476 arg_output,
3477 0,
3478 i->inactive_exit_timestamp_monotonic,
3479 arg_lines,
3480 getuid(),
ea6c2dd1 3481 flags | OUTPUT_BEGIN_NEWLINE,
94e0bd7d
ZJS
3482 arg_scope == UNIT_FILE_SYSTEM,
3483 ellipsized);
6f003b43 3484 }
86aa7ba4 3485
45fb0699 3486 if (i->need_daemon_reload)
1058cbf2 3487 printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %sdaemon-reload' recommended.\n",
0b5a519c
DS
3488 ansi_highlight_red(),
3489 ansi_highlight_off(),
1058cbf2 3490 arg_scope == UNIT_FILE_SYSTEM ? "" : "--user ");
61cbdc4b
LP
3491}
3492
b43f208f 3493static void show_unit_help(UnitStatusInfo *i) {
256425cc
LP
3494 char **p;
3495
3496 assert(i);
3497
3498 if (!i->documentation) {
3499 log_info("Documentation for %s not known.", i->id);
3500 return;
3501 }
3502
3503 STRV_FOREACH(p, i->documentation) {
3504
3505 if (startswith(*p, "man:")) {
256425cc 3506 const char *args[4] = { "man", NULL, NULL, NULL };
cbc9fbd1
LP
3507 _cleanup_free_ char *page = NULL, *section = NULL;
3508 char *e = NULL;
256425cc 3509 pid_t pid;
cbc9fbd1 3510 size_t k;
256425cc
LP
3511
3512 k = strlen(*p);
3513
3514 if ((*p)[k-1] == ')')
3515 e = strrchr(*p, '(');
3516
3517 if (e) {
3518 page = strndup((*p) + 4, e - *p - 4);
256425cc 3519 section = strndup(e + 1, *p + k - e - 2);
cec7eda5 3520 if (!page || !section) {
0d0f0c50 3521 log_oom();
256425cc
LP
3522 return;
3523 }
3524
3525 args[1] = section;
3526 args[2] = page;
3527 } else
3528 args[1] = *p + 4;
3529
3530 pid = fork();
3531 if (pid < 0) {
3532 log_error("Failed to fork: %m");
256425cc
LP
3533 continue;
3534 }
3535
3536 if (pid == 0) {
3537 /* Child */
3538 execvp(args[0], (char**) args);
3539 log_error("Failed to execute man: %m");
3540 _exit(EXIT_FAILURE);
3541 }
3542
256425cc
LP
3543 wait_for_terminate(pid, NULL);
3544 } else
0315fe37 3545 log_info("Can't show: %s", *p);
256425cc
LP
3546 }
3547}
3548
f459b602
MAP
3549static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo *i, const char *contents) {
3550 int r;
61cbdc4b 3551
a4c279f8 3552 assert(name);
f459b602 3553 assert(m);
a4c279f8
LP
3554 assert(i);
3555
f459b602 3556 switch (contents[0]) {
61cbdc4b 3557
f459b602 3558 case SD_BUS_TYPE_STRING: {
61cbdc4b
LP
3559 const char *s;
3560
f459b602
MAP
3561 r = sd_bus_message_read(m, "s", &s);
3562 if (r < 0)
3563 return bus_log_parse_error(r);
61cbdc4b 3564
a4c279f8 3565 if (!isempty(s)) {
61cbdc4b
LP
3566 if (streq(name, "Id"))
3567 i->id = s;
3568 else if (streq(name, "LoadState"))
3569 i->load_state = s;
3570 else if (streq(name, "ActiveState"))
3571 i->active_state = s;
3572 else if (streq(name, "SubState"))
3573 i->sub_state = s;
3574 else if (streq(name, "Description"))
3575 i->description = s;
3576 else if (streq(name, "FragmentPath"))
1b64d026
LP
3577 i->fragment_path = s;
3578 else if (streq(name, "SourcePath"))
3579 i->source_path = s;
286ca485 3580#ifndef NOLEGACY
a00963a2
LP
3581 else if (streq(name, "DefaultControlGroup")) {
3582 const char *e;
3583 e = startswith(s, SYSTEMD_CGROUP_CONTROLLER ":");
3584 if (e)
3585 i->control_group = e;
3586 }
4ad49000
LP
3587#endif
3588 else if (streq(name, "ControlGroup"))
3589 i->control_group = s;
61cbdc4b
LP
3590 else if (streq(name, "StatusText"))
3591 i->status_text = s;
175728c4
HH
3592 else if (streq(name, "PIDFile"))
3593 i->pid_file = s;
61cbdc4b
LP
3594 else if (streq(name, "SysFSPath"))
3595 i->sysfs_path = s;
3596 else if (streq(name, "Where"))
3597 i->where = s;
3598 else if (streq(name, "What"))
3599 i->what = s;
4a9e2fff
LP
3600 else if (streq(name, "Following"))
3601 i->following = s;
a4375746
LP
3602 else if (streq(name, "UnitFileState"))
3603 i->unit_file_state = s;
f42806df
LP
3604 else if (streq(name, "Result"))
3605 i->result = s;
61cbdc4b
LP
3606 }
3607
3608 break;
3609 }
3610
f459b602
MAP
3611 case SD_BUS_TYPE_BOOLEAN: {
3612 int b;
b8131a87 3613
f459b602
MAP
3614 r = sd_bus_message_read(m, "b", &b);
3615 if (r < 0)
3616 return bus_log_parse_error(r);
b8131a87
LP
3617
3618 if (streq(name, "Accept"))
3619 i->accept = b;
45fb0699
LP
3620 else if (streq(name, "NeedDaemonReload"))
3621 i->need_daemon_reload = b;
90bbc946
LP
3622 else if (streq(name, "ConditionResult"))
3623 i->condition_result = b;
b8131a87
LP
3624
3625 break;
3626 }
3627
f459b602 3628 case SD_BUS_TYPE_UINT32: {
61cbdc4b
LP
3629 uint32_t u;
3630
f459b602
MAP
3631 r = sd_bus_message_read(m, "u", &u);
3632 if (r < 0)
3633 return bus_log_parse_error(r);
61cbdc4b
LP
3634
3635 if (streq(name, "MainPID")) {
3636 if (u > 0) {
3637 i->main_pid = (pid_t) u;
3638 i->running = true;
3639 }
3640 } else if (streq(name, "ControlPID"))
3641 i->control_pid = (pid_t) u;
3642 else if (streq(name, "ExecMainPID")) {
3643 if (u > 0)
3644 i->main_pid = (pid_t) u;
3645 } else if (streq(name, "NAccepted"))
3646 i->n_accepted = u;
3647 else if (streq(name, "NConnections"))
3648 i->n_connections = u;
3649
3650 break;
3651 }
3652
f459b602 3653 case SD_BUS_TYPE_INT32: {
61cbdc4b
LP
3654 int32_t j;
3655
f459b602
MAP
3656 r = sd_bus_message_read(m, "i", &j);
3657 if (r < 0)
3658 return bus_log_parse_error(r);
61cbdc4b
LP
3659
3660 if (streq(name, "ExecMainCode"))
3661 i->exit_code = (int) j;
3662 else if (streq(name, "ExecMainStatus"))
3663 i->exit_status = (int) j;
3664
3665 break;
3666 }
3667
f459b602 3668 case SD_BUS_TYPE_UINT64: {
61cbdc4b
LP
3669 uint64_t u;
3670
f459b602
MAP
3671 r = sd_bus_message_read(m, "t", &u);
3672 if (r < 0)
3673 return bus_log_parse_error(r);
61cbdc4b
LP
3674
3675 if (streq(name, "ExecMainStartTimestamp"))
3676 i->start_timestamp = (usec_t) u;
3677 else if (streq(name, "ExecMainExitTimestamp"))
3678 i->exit_timestamp = (usec_t) u;
584be568
LP
3679 else if (streq(name, "ActiveEnterTimestamp"))
3680 i->active_enter_timestamp = (usec_t) u;
3681 else if (streq(name, "InactiveEnterTimestamp"))
3682 i->inactive_enter_timestamp = (usec_t) u;
3683 else if (streq(name, "InactiveExitTimestamp"))
3684 i->inactive_exit_timestamp = (usec_t) u;
df50185b
LP
3685 else if (streq(name, "InactiveExitTimestampMonotonic"))
3686 i->inactive_exit_timestamp_monotonic = (usec_t) u;
584be568
LP
3687 else if (streq(name, "ActiveExitTimestamp"))
3688 i->active_exit_timestamp = (usec_t) u;
90bbc946
LP
3689 else if (streq(name, "ConditionTimestamp"))
3690 i->condition_timestamp = (usec_t) u;
61cbdc4b
LP
3691
3692 break;
3693 }
582a507f 3694
f459b602 3695 case SD_BUS_TYPE_ARRAY:
582a507f 3696
f459b602
MAP
3697 if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && startswith(name, "Exec")) {
3698 _cleanup_free_ ExecStatusInfo *info = NULL;
582a507f 3699
f459b602
MAP
3700 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sasbttttuii)");
3701 if (r < 0)
3702 return bus_log_parse_error(r);
582a507f 3703
f459b602
MAP
3704 info = new0(ExecStatusInfo, 1);
3705 if (!info)
3706 return log_oom();
582a507f 3707
f459b602 3708 while ((r = exec_status_info_deserialize(m, info)) > 0) {
0129173a 3709
f459b602
MAP
3710 info->name = strdup(name);
3711 if (!info->name)
3712 log_oom();
582a507f 3713
71fda00f 3714 LIST_PREPEND(exec, i->exec, info);
582a507f 3715
f459b602
MAP
3716 info = new0(ExecStatusInfo, 1);
3717 if (!info)
3718 log_oom();
49dbfa7b 3719 }
67419600 3720
f459b602
MAP
3721 if (r < 0)
3722 return bus_log_parse_error(r);
67419600 3723
f459b602
MAP
3724 r = sd_bus_message_exit_container(m);
3725 if (r < 0)
3726 return bus_log_parse_error(r);
67419600 3727
f459b602 3728 return 0;
67419600 3729
f459b602
MAP
3730 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Listen")) {
3731 const char *type, *path;
13160134 3732
f459b602
MAP
3733 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
3734 if (r < 0)
3735 return bus_log_parse_error(r);
67419600 3736
f459b602 3737 while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) {
67419600 3738
f459b602
MAP
3739 r = strv_extend(&i->listen, type);
3740 if (r < 0)
3741 return r;
67419600 3742
f459b602
MAP
3743 r = strv_extend(&i->listen, path);
3744 if (r < 0)
3745 return r;
3746 }
76d14b87 3747 if (r < 0)
f459b602 3748 return bus_log_parse_error(r);
76d14b87 3749
f459b602
MAP
3750 r = sd_bus_message_exit_container(m);
3751 if (r < 0)
3752 return bus_log_parse_error(r);
49dbfa7b 3753
f459b602 3754 return 0;
49dbfa7b 3755
f459b602 3756 } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "DropInPaths")) {
49dbfa7b 3757
f459b602
MAP
3758 r = sd_bus_message_read_strv(m, &i->dropin_paths);
3759 if (r < 0)
3760 return bus_log_parse_error(r);
49dbfa7b 3761
f459b602 3762 } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "Documentation")) {
49dbfa7b 3763
f459b602
MAP
3764 r = sd_bus_message_read_strv(m, &i->documentation);
3765 if (r < 0)
3766 return bus_log_parse_error(r);
52990c2e 3767
f459b602
MAP
3768 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Conditions")) {
3769 const char *cond, *param;
3770 int trigger, negate;
3771 int32_t state;
52990c2e 3772
f459b602
MAP
3773 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sbbsi)");
3774 if (r < 0)
3775 return bus_log_parse_error(r);
3776
3777 while ((r = sd_bus_message_read(m, "(sbbsi)", &cond, &trigger, &negate, &param, &state)) > 0) {
3778 log_debug("%s %d %d %s %d", cond, trigger, negate, param, state);
3779 if (state < 0 && (!trigger || !i->failed_condition)) {
3780 i->failed_condition = cond;
3781 i->failed_condition_trigger = trigger;
3782 i->failed_condition_negate = negate;
3783 i->failed_condition_param = param;
3784 }
582a507f 3785 }
f459b602
MAP
3786 if (r < 0)
3787 return bus_log_parse_error(r);
3788
3789 r = sd_bus_message_exit_container(m);
3790 if (r < 0)
3791 return bus_log_parse_error(r);
3792
3793 } else
3794 goto skip;
582a507f
LP
3795
3796 break;
9f39404c 3797
f459b602 3798 case SD_BUS_TYPE_STRUCT_BEGIN:
9f39404c
LP
3799
3800 if (streq(name, "LoadError")) {
9f39404c 3801 const char *n, *message;
9f39404c 3802
f459b602 3803 r = sd_bus_message_read(m, "(ss)", &n, &message);
9f39404c 3804 if (r < 0)
f459b602 3805 return bus_log_parse_error(r);
9f39404c
LP
3806
3807 if (!isempty(message))
3808 i->load_error = message;
f459b602
MAP
3809 } else
3810 goto skip;
9f39404c
LP
3811
3812 break;
f459b602
MAP
3813
3814 default:
3815 goto skip;
9f39404c 3816 }
f459b602
MAP
3817
3818 return 0;
3819
3820skip:
3821 r = sd_bus_message_skip(m, contents);
3822 if (r < 0)
3823 return bus_log_parse_error(r);
61cbdc4b
LP
3824
3825 return 0;
3826}
3827
f459b602
MAP
3828static int print_property(const char *name, sd_bus_message *m, const char *contents) {
3829 int r;
3830
48220598 3831 assert(name);
f459b602 3832 assert(m);
48220598 3833
61cbdc4b
LP
3834 /* This is a low-level property printer, see
3835 * print_status_info() for the nicer output */
3836
852c1b4d
ZJS
3837 if (arg_properties && !strv_find(arg_properties, name)) {
3838 /* skip what we didn't read */
3839 r = sd_bus_message_skip(m, contents);
3840 return r;
3841 }
48220598 3842
f459b602 3843 switch (contents[0]) {
48220598 3844
f459b602 3845 case SD_BUS_TYPE_STRUCT_BEGIN:
48220598 3846
f459b602 3847 if (contents[1] == SD_BUS_TYPE_UINT32 && streq(name, "Job")) {
48220598
LP
3848 uint32_t u;
3849
f459b602
MAP
3850 r = sd_bus_message_read(m, "(uo)", &u, NULL);
3851 if (r < 0)
3852 return bus_log_parse_error(r);
48220598 3853
f459b602 3854 if (u > 0)
48220598
LP
3855 printf("%s=%u\n", name, (unsigned) u);
3856 else if (arg_all)
3857 printf("%s=\n", name);
3858
3859 return 0;
f459b602
MAP
3860
3861 } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "Unit")) {
48220598
LP
3862 const char *s;
3863
f459b602
MAP
3864 r = sd_bus_message_read(m, "(so)", &s, NULL);
3865 if (r < 0)
3866 return bus_log_parse_error(r);
48220598 3867
f459b602 3868 if (arg_all || !isempty(s))
48220598
LP
3869 printf("%s=%s\n", name, s);
3870
3871 return 0;
f459b602
MAP
3872
3873 } else if (contents[1] == SD_BUS_TYPE_STRING && streq(name, "LoadError")) {
9f39404c
LP
3874 const char *a = NULL, *b = NULL;
3875
f459b602
MAP
3876 r = sd_bus_message_read(m, "(ss)", &a, &b);
3877 if (r < 0)
3878 return bus_log_parse_error(r);
9f39404c
LP
3879
3880 if (arg_all || !isempty(a) || !isempty(b))
3881 printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b));
f786e80d 3882
57183d11
LP
3883 return 0;
3884 } else if (streq_ptr(name, "SystemCallFilter")) {
3885 _cleanup_strv_free_ char **l = NULL;
3886 int whitelist;
3887
3888 r = sd_bus_message_enter_container(m, 'r', "bas");
3889 if (r < 0)
3890 return bus_log_parse_error(r);
3891
3892 r = sd_bus_message_read(m, "b", &whitelist);
3893 if (r < 0)
3894 return bus_log_parse_error(r);
3895
3896 r = sd_bus_message_read_strv(m, &l);
3897 if (r < 0)
3898 return bus_log_parse_error(r);
3899
3900 r = sd_bus_message_exit_container(m);
3901 if (r < 0)
3902 return bus_log_parse_error(r);
3903
3904 if (arg_all || whitelist || !strv_isempty(l)) {
3905 bool first = true;
3906 char **i;
3907
3908 fputs(name, stdout);
3909 fputc('=', stdout);
3910
3911 if (!whitelist)
3912 fputc('~', stdout);
3913
3914 STRV_FOREACH(i, l) {
3915 if (first)
3916 first = false;
3917 else
3918 fputc(' ', stdout);
3919
3920 fputs(*i, stdout);
3921 }
3922 fputc('\n', stdout);
3923 }
3924
f786e80d 3925 return 0;
48220598
LP
3926 }
3927
3928 break;
48220598 3929
f459b602 3930 case SD_BUS_TYPE_ARRAY:
48220598 3931
f459b602
MAP
3932 if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "EnvironmentFiles")) {
3933 const char *path;
3934 int ignore;
8c7be95e 3935
f459b602
MAP
3936 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sb)");
3937 if (r < 0)
3938 return bus_log_parse_error(r);
8c7be95e 3939
f459b602
MAP
3940 while ((r = sd_bus_message_read(m, "(sb)", &path, &ignore)) > 0)
3941 printf("EnvironmentFile=%s (ignore_errors=%s)\n", path, yes_no(ignore));
8c7be95e 3942
f459b602
MAP
3943 if (r < 0)
3944 return bus_log_parse_error(r);
8c7be95e 3945
f459b602
MAP
3946 r = sd_bus_message_exit_container(m);
3947 if (r < 0)
3948 return bus_log_parse_error(r);
8c7be95e
LP
3949
3950 return 0;
3951
f459b602
MAP
3952 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Paths")) {
3953 const char *type, *path;
67419600 3954
f459b602
MAP
3955 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
3956 if (r < 0)
3957 return bus_log_parse_error(r);
ebf57b80 3958
f459b602
MAP
3959 while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0)
3960 printf("%s=%s\n", type, path);
3961 if (r < 0)
3962 return bus_log_parse_error(r);
ebf57b80 3963
f459b602
MAP
3964 r = sd_bus_message_exit_container(m);
3965 if (r < 0)
3966 return bus_log_parse_error(r);
ebf57b80 3967
707e5e52 3968 return 0;
582a507f 3969
f459b602
MAP
3970 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Listen")) {
3971 const char *type, *path;
67419600 3972
f459b602
MAP
3973 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
3974 if (r < 0)
3975 return bus_log_parse_error(r);
67419600 3976
f459b602
MAP
3977 while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0)
3978 printf("Listen%s=%s\n", type, path);
3979 if (r < 0)
3980 return bus_log_parse_error(r);
67419600 3981
f459b602
MAP
3982 r = sd_bus_message_exit_container(m);
3983 if (r < 0)
3984 return bus_log_parse_error(r);
67419600
OS
3985
3986 return 0;
3987
f459b602
MAP
3988 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "Timers")) {
3989 const char *base;
3990 uint64_t value, next_elapse;
707e5e52 3991
f459b602
MAP
3992 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(stt)");
3993 if (r < 0)
3994 return bus_log_parse_error(r);
552e4331 3995
f459b602
MAP
3996 while ((r = sd_bus_message_read(m, "(stt)", &base, &value, &next_elapse)) > 0) {
3997 char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX];
fe68089d 3998
f459b602
MAP
3999 printf("%s={ value=%s ; next_elapse=%s }\n",
4000 base,
4001 format_timespan(timespan1, sizeof(timespan1), value, 0),
4002 format_timespan(timespan2, sizeof(timespan2), next_elapse, 0));
fe68089d 4003 }
f459b602
MAP
4004 if (r < 0)
4005 return bus_log_parse_error(r);
4006
4007 r = sd_bus_message_exit_container(m);
4008 if (r < 0)
4009 return bus_log_parse_error(r);
fe68089d
LP
4010
4011 return 0;
fe68089d 4012
f459b602
MAP
4013 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && startswith(name, "Exec")) {
4014 ExecStatusInfo info = {};
4015
4016 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(sasbttttuii)");
4017 if (r < 0)
4018 return bus_log_parse_error(r);
4019
4020 while ((r = exec_status_info_deserialize(m, &info)) > 0) {
4021 char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
4022 _cleanup_free_ char *tt;
4023
4024 tt = strv_join(info.argv, " ");
4025
4026 printf("%s={ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid=%u ; code=%s ; status=%i%s%s }\n",
4027 name,
4028 strna(info.path),
4029 strna(tt),
4030 yes_no(info.ignore),
4031 strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
4032 strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
4033 (unsigned) info. pid,
4034 sigchld_code_to_string(info.code),
4035 info.status,
4036 info.code == CLD_EXITED ? "" : "/",
4037 strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
fe68089d 4038
582a507f
LP
4039 free(info.path);
4040 strv_free(info.argv);
f459b602 4041 zero(info);
707e5e52
LP
4042 }
4043
f459b602
MAP
4044 r = sd_bus_message_exit_container(m);
4045 if (r < 0)
4046 return bus_log_parse_error(r);
4047
48220598 4048 return 0;
4ad49000 4049
f459b602
MAP
4050 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "DeviceAllow")) {
4051 const char *path, *rwm;
4ad49000 4052
f459b602
MAP
4053 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)");
4054 if (r < 0)
4055 return bus_log_parse_error(r);
4ad49000 4056
f459b602
MAP
4057 while ((r = sd_bus_message_read(m, "(ss)", &path, &rwm)) > 0)
4058 printf("%s=%s %s\n", name, strna(path), strna(rwm));
4059 if (r < 0)
4060 return bus_log_parse_error(r);
4ad49000 4061
f459b602
MAP
4062 r = sd_bus_message_exit_container(m);
4063 if (r < 0)
4064 return bus_log_parse_error(r);
4ad49000 4065
4ad49000
LP
4066 return 0;
4067
f459b602
MAP
4068 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && streq(name, "BlockIODeviceWeight")) {
4069 const char *path;
4070 uint64_t weight;
b8ab2dc6 4071
f459b602
MAP
4072 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(st)");
4073 if (r < 0)
4074 return bus_log_parse_error(r);
b8ab2dc6 4075
f459b602
MAP
4076 while ((r = sd_bus_message_read(m, "(st)", &path, &weight)) > 0)
4077 printf("%s=%s %" PRIu64 "\n", name, strna(path), weight);
4078 if (r < 0)
4079 return bus_log_parse_error(r);
b8ab2dc6 4080
f459b602
MAP
4081 r = sd_bus_message_exit_container(m);
4082 if (r < 0)
4083 return bus_log_parse_error(r);
b8ab2dc6 4084
b8ab2dc6
G
4085 return 0;
4086
f459b602
MAP
4087 } else if (contents[1] == SD_BUS_TYPE_STRUCT_BEGIN && (streq(name, "BlockIOReadBandwidth") || streq(name, "BlockIOWriteBandwidth"))) {
4088 const char *path;
4089 uint64_t bandwidth;
4ad49000 4090
f459b602
MAP
4091 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(st)");
4092 if (r < 0)
4093 return bus_log_parse_error(r);
4ad49000 4094
f459b602
MAP
4095 while ((r = sd_bus_message_read(m, "(st)", &path, &bandwidth)) > 0)
4096 printf("%s=%s %" PRIu64 "\n", name, strna(path), bandwidth);
4097 if (r < 0)
4098 return bus_log_parse_error(r);
4ad49000 4099
f459b602
MAP
4100 r = sd_bus_message_exit_container(m);
4101 if (r < 0)
4102 return bus_log_parse_error(r);
4ad49000 4103
4ad49000 4104 return 0;
48220598
LP
4105 }
4106
4107 break;
4108 }
4109
f459b602
MAP
4110 r = bus_print_property(name, m, arg_all);
4111 if (r < 0)
4112 return bus_log_parse_error(r);
4113
4114 if (r == 0) {
4115 r = sd_bus_message_skip(m, contents);
4116 if (r < 0)
4117 return bus_log_parse_error(r);
a4c279f8 4118
f459b602
MAP
4119 if (arg_all)
4120 printf("%s=[unprintable]\n", name);
4121 }
48220598
LP
4122
4123 return 0;
4124}
4125
f459b602
MAP
4126static int show_one(
4127 const char *verb,
4128 sd_bus *bus,
4129 const char *path,
4130 bool show_properties,
4131 bool *new_line,
4132 bool *ellipsized) {
4133
4134 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
4135 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
b92bea5d 4136 UnitStatusInfo info = {};
582a507f 4137 ExecStatusInfo *p;
f459b602 4138 int r;
48220598 4139
48220598 4140 assert(path);
61cbdc4b 4141 assert(new_line);
48220598 4142
e3e0314b
ZJS
4143 log_debug("Showing one %s", path);
4144
f459b602 4145 r = sd_bus_call_method(
f22f08cd
SP
4146 bus,
4147 "org.freedesktop.systemd1",
4148 path,
4149 "org.freedesktop.DBus.Properties",
4150 "GetAll",
f459b602 4151 &error,
f22f08cd 4152 &reply,
f459b602
MAP
4153 "s", "");
4154 if (r < 0) {
4155 log_error("Failed to get properties: %s", bus_error_message(&error, r));
f84190d8 4156 return r;
48220598
LP
4157 }
4158
f459b602
MAP
4159 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "{sv}");
4160 if (r < 0)
4161 return bus_log_parse_error(r);
48220598 4162
61cbdc4b
LP
4163 if (*new_line)
4164 printf("\n");
4165
4166 *new_line = true;
4167
f459b602
MAP
4168 while ((r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_DICT_ENTRY, "sv")) > 0) {
4169 const char *name, *contents;
0183528f 4170
f459b602
MAP
4171 r = sd_bus_message_read(reply, "s", &name);
4172 if (r < 0)
4173 return bus_log_parse_error(r);
48220598 4174
f459b602
MAP
4175 r = sd_bus_message_peek_type(reply, NULL, &contents);
4176 if (r < 0)
4177 return bus_log_parse_error(r);
4178
4179 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_VARIANT, contents);
4180 if (r < 0)
4181 return bus_log_parse_error(r);
48220598 4182
61cbdc4b 4183 if (show_properties)
f459b602 4184 r = print_property(name, reply, contents);
61cbdc4b 4185 else
f459b602
MAP
4186 r = status_property(name, reply, &info, contents);
4187 if (r < 0)
4188 return r;
48220598 4189
f459b602
MAP
4190 r = sd_bus_message_exit_container(reply);
4191 if (r < 0)
4192 return bus_log_parse_error(r);
4193
4194 r = sd_bus_message_exit_container(reply);
4195 if (r < 0)
4196 return bus_log_parse_error(r);
48220598 4197 }
f459b602
MAP
4198 if (r < 0)
4199 return bus_log_parse_error(r);
4200
4201 r = sd_bus_message_exit_container(reply);
4202 if (r < 0)
4203 return bus_log_parse_error(r);
48220598 4204
f1e36d67
LP
4205 r = 0;
4206
256425cc 4207 if (!show_properties) {
b43f208f
KS
4208 if (streq(verb, "help"))
4209 show_unit_help(&info);
256425cc 4210 else
94e0bd7d 4211 print_status_info(&info, ellipsized);
256425cc 4212 }
f1e36d67 4213
49dbfa7b 4214 strv_free(info.documentation);
76d14b87 4215 strv_free(info.dropin_paths);
67419600 4216 strv_free(info.listen);
49dbfa7b 4217
22f4096c 4218 if (!streq_ptr(info.active_state, "active") &&
be8088a2 4219 !streq_ptr(info.active_state, "reloading") &&
3b05b8b3 4220 streq(verb, "status")) {
22f4096c 4221 /* According to LSB: "program not running" */
175728c4 4222 /* 0: program is running or service is OK
41a55c46
ZJS
4223 * 1: program is dead and /run PID file exists
4224 * 2: program is dead and /run/lock lock file exists
175728c4
HH
4225 * 3: program is not running
4226 * 4: program or service status is unknown
4227 */
3b05b8b3 4228 if (info.pid_file && access(info.pid_file, F_OK) == 0)
175728c4
HH
4229 r = 1;
4230 else
4231 r = 3;
e9c1ea9d 4232 }
61cbdc4b 4233
582a507f 4234 while ((p = info.exec)) {
71fda00f 4235 LIST_REMOVE(exec, info.exec, p);
582a507f
LP
4236 exec_status_info_free(p);
4237 }
4238
48220598
LP
4239 return r;
4240}
4241
f74294c1 4242static int get_unit_dbus_path_by_pid(
f459b602
MAP
4243 sd_bus *bus,
4244 uint32_t pid,
f74294c1 4245 char **unit) {
f459b602
MAP
4246
4247 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
4248 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
373d32c9 4249 char *u;
a223b325
MS
4250 int r;
4251
f459b602 4252 r = sd_bus_call_method(
f22f08cd
SP
4253 bus,
4254 "org.freedesktop.systemd1",
4255 "/org/freedesktop/systemd1",
4256 "org.freedesktop.systemd1.Manager",
4257 "GetUnitByPID",
f459b602 4258 &error,
f22f08cd 4259 &reply,
f459b602
MAP
4260 "u", pid);
4261 if (r < 0) {
de0671ee 4262 log_error("Failed to get unit for PID "PID_FMT": %s", pid, bus_error_message(&error, r));
cec7eda5 4263 return r;
a223b325
MS
4264 }
4265
373d32c9 4266 r = sd_bus_message_read(reply, "o", &u);
f459b602
MAP
4267 if (r < 0)
4268 return bus_log_parse_error(r);
4269
373d32c9
LP
4270 u = strdup(u);
4271 if (!u)
4272 return log_oom();
4273
4274 *unit = u;
f74294c1 4275 return 0;
a223b325
MS
4276}
4277
f459b602
MAP
4278static int show_all(
4279 const char* verb,
4280 sd_bus *bus,
4281 bool show_properties,
4282 bool *new_line,
4283 bool *ellipsized) {
4284
f459b602
MAP
4285 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
4286 _cleanup_free_ UnitInfo *unit_infos = NULL;
4287 const UnitInfo *u;
4288 unsigned c;
5bb75bc7 4289 int r, ret = 0;
265a7a2a 4290
1238ee09 4291 r = get_unit_list(bus, NULL, NULL, &unit_infos, 0, &reply);
265a7a2a
ZJS
4292 if (r < 0)
4293 return r;
4294
dbed408b
LP
4295 pager_open_if_enabled();
4296
f459b602
MAP
4297 c = (unsigned) r;
4298
4299 qsort_safe(unit_infos, c, sizeof(UnitInfo), compare_unit_info);
991f2a39 4300
265a7a2a 4301 for (u = unit_infos; u < unit_infos + c; u++) {
7fd1b19b 4302 _cleanup_free_ char *p = NULL;
265a7a2a 4303
265a7a2a
ZJS
4304 p = unit_dbus_path_from_name(u->id);
4305 if (!p)
4306 return log_oom();
4307
94e0bd7d 4308 r = show_one(verb, bus, p, show_properties, new_line, ellipsized);
3df538da 4309 if (r < 0)
265a7a2a 4310 return r;
5bb75bc7
ZJS
4311 else if (r > 0 && ret == 0)
4312 ret = r;
265a7a2a
ZJS
4313 }
4314
5bb75bc7 4315 return ret;
265a7a2a
ZJS
4316}
4317
8fcf784d
LP
4318static int show_system_status(sd_bus *bus) {
4319 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], since2[FORMAT_TIMESTAMP_MAX];
4320 _cleanup_free_ char *hn = NULL;
4321 struct machine_info mi = {};
4322 const char *on, *off;
4323 int r;
4324
4325 hn = gethostname_malloc();
4326 if (!hn)
4327 return log_oom();
4328
4329 r = bus_map_all_properties(bus, "org.freedesktop.systemd1", "/org/freedesktop/systemd1", machine_info_property_map, &mi);
4330 if (r < 0) {
4331 log_error("Failed to read server status: %s", strerror(-r));
4332 return r;
4333 }
4334
8fcf784d
LP
4335 if (streq_ptr(mi.state, "degraded")) {
4336 on = ansi_highlight_red();
4337 off = ansi_highlight_off();
4338 } else if (!streq_ptr(mi.state, "running")) {
4339 on = ansi_highlight_yellow();
4340 off = ansi_highlight_off();
4341 } else
4342 on = off = "";
4343
6b01f1d3 4344 printf("%s%s%s %s\n", on, draw_special_char(DRAW_BLACK_CIRCLE), off, arg_host ? arg_host : hn);
b0d14c69 4345
8fcf784d
LP
4346 printf(" State: %s%s%s\n",
4347 on, strna(mi.state), off);
4348
4349 printf(" Jobs: %u queued\n", mi.n_jobs);
4350 printf(" Failed: %u units\n", mi.n_failed_units);
4351
4352 printf(" Since: %s; %s\n",
4353 format_timestamp(since2, sizeof(since2), mi.timestamp),
4354 format_timestamp_relative(since1, sizeof(since1), mi.timestamp));
4355
4356 printf(" CGroup: %s\n", mi.control_group ?: "/");
4357 if (arg_transport == BUS_TRANSPORT_LOCAL || arg_transport == BUS_TRANSPORT_CONTAINER) {
4358 int flags =
4359 arg_all * OUTPUT_SHOW_ALL |
4360 (!on_tty() || pager_have()) * OUTPUT_FULL_WIDTH |
4361 on_tty() * OUTPUT_COLOR |
4362 !arg_quiet * OUTPUT_WARN_CUTOFF |
4363 arg_full * OUTPUT_FULL_WIDTH;
4364
4365 static const char prefix[] = " ";
4366 unsigned c;
4367
4368 c = columns();
4369 if (c > sizeof(prefix) - 1)
4370 c -= sizeof(prefix) - 1;
4371 else
4372 c = 0;
4373
4374 show_cgroup(SYSTEMD_CGROUP_CONTROLLER, strempty(mi.control_group), prefix, c, false, flags);
4375 }
4376
4377 free(mi.state);
4378 free(mi.control_group);
4379
4380 return 0;
4381}
4382
f459b602 4383static int show(sd_bus *bus, char **args) {
265a7a2a 4384 bool show_properties, show_status, new_line = false;
94e0bd7d 4385 bool ellipsized = false;
e3e0314b 4386 int r, ret = 0;
48220598
LP
4387
4388 assert(bus);
4389 assert(args);
4390
256425cc 4391 show_properties = streq(args[0], "show");
265a7a2a 4392 show_status = streq(args[0], "status");
61cbdc4b 4393
ec14911e 4394 if (show_properties)
1968a360 4395 pager_open_if_enabled();
ec14911e 4396
f84190d8 4397 /* If no argument is specified inspect the manager itself */
48220598 4398
f84190d8 4399 if (show_properties && strv_length(args) <= 1)
94e0bd7d 4400 return show_one(args[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line, &ellipsized);
48220598 4401
8fcf784d
LP
4402 if (show_status && strv_length(args) <= 1) {
4403
c3441de0 4404 pager_open_if_enabled();
8fcf784d
LP
4405 show_system_status(bus);
4406 new_line = true;
4407
4408 if (arg_all)
4409 ret = show_all(args[0], bus, false, &new_line, &ellipsized);
4410 } else {
e3e0314b
ZJS
4411 _cleanup_free_ char **patterns = NULL;
4412 char **name;
4413
4414 STRV_FOREACH(name, args + 1) {
f74294c1 4415 _cleanup_free_ char *unit = NULL;
94e0bd7d 4416 uint32_t id;
48220598 4417
94e0bd7d 4418 if (safe_atou32(*name, &id) < 0) {
e3e0314b 4419 if (strv_push(&patterns, *name) < 0)
94e0bd7d 4420 return log_oom();
48220598 4421
e3e0314b 4422 continue;
94e0bd7d 4423 } else if (show_properties) {
94e0bd7d 4424 /* Interpret as job id */
f74294c1 4425 if (asprintf(&unit, "/org/freedesktop/systemd1/job/%u", id) < 0)
94e0bd7d 4426 return log_oom();
48220598 4427
94e0bd7d
ZJS
4428 } else {
4429 /* Interpret as PID */
f74294c1 4430 r = get_unit_dbus_path_by_pid(bus, id, &unit);
373d32c9 4431 if (r < 0) {
94e0bd7d 4432 ret = r;
373d32c9
LP
4433 continue;
4434 }
94e0bd7d 4435 }
f74294c1 4436
5bb75bc7
ZJS
4437 r = show_one(args[0], bus, unit, show_properties,
4438 &new_line, &ellipsized);
4439 if (r < 0)
4440 return r;
4441 else if (r > 0 && ret == 0)
4442 ret = r;
48220598 4443 }
94e0bd7d 4444
e3e0314b
ZJS
4445 if (!strv_isempty(patterns)) {
4446 _cleanup_strv_free_ char **names = NULL;
4447
4448 r = expand_names(bus, patterns, NULL, &names);
4449 if (r < 0)
4450 log_error("Failed to expand names: %s", strerror(-r));
4451
4452 STRV_FOREACH(name, names) {
4453 _cleanup_free_ char *unit;
4454
4455 unit = unit_dbus_path_from_name(*name);
4456 if (!unit)
4457 return log_oom();
4458
5bb75bc7
ZJS
4459 r = show_one(args[0], bus, unit, show_properties,
4460 &new_line, &ellipsized);
4461 if (r < 0)
4462 return r;
4463 else if (r > 0 && ret == 0)
4464 ret = r;
e3e0314b
ZJS
4465 }
4466 }
4467 }
4468
94e0bd7d
ZJS
4469 if (ellipsized && !arg_quiet)
4470 printf("Hint: Some lines were ellipsized, use -l to show in full.\n");
48220598 4471
22f4096c 4472 return ret;
0183528f
LP
4473}
4474
15ef1144
LP
4475static int cat(sd_bus *bus, char **args) {
4476 _cleanup_free_ char *unit = NULL;
4477 _cleanup_strv_free_ char **names = NULL;
4478 char **name;
4479 bool first = true;
4480 int r = 0;
4481
4482 assert(bus);
4483 assert(args);
4484
4485 r = expand_names(bus, args + 1, NULL, &names);
4486 if (r < 0)
4487 log_error("Failed to expand names: %s", strerror(-r));
4488
4489 pager_open_if_enabled();
4490
4491 STRV_FOREACH(name, names) {
4492 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
4493 _cleanup_strv_free_ char **dropin_paths = NULL;
4494 _cleanup_free_ char *fragment_path = NULL;
4495 char **path;
4496
4497 unit = unit_dbus_path_from_name(*name);
4498 if (!unit)
4499 return log_oom();
4500
4501 if (need_daemon_reload(bus, *name) > 0)
4502 log_warning("Unit file of %s changed on disk. Run 'systemctl%s daemon-reload'.",
4503 *name, arg_scope == UNIT_FILE_SYSTEM ? "" : " --user");
4504
4505 r = sd_bus_get_property_string(
4506 bus,
4507 "org.freedesktop.systemd1",
4508 unit,
4509 "org.freedesktop.systemd1.Unit",
4510 "FragmentPath",
4511 &error,
4512 &fragment_path);
4513 if (r < 0) {
4514 log_warning("Failed to get FragmentPath: %s", bus_error_message(&error, r));
4515 continue;
4516 }
4517
4518 r = sd_bus_get_property_strv(
4519 bus,
4520 "org.freedesktop.systemd1",
4521 unit,
4522 "org.freedesktop.systemd1.Unit",
4523 "DropInPaths",
4524 &error,
4525 &dropin_paths);
4526 if (r < 0) {
4527 log_warning("Failed to get DropInPaths: %s", bus_error_message(&error, r));
4528 continue;
4529 }
4530
4531 if (first)
4532 first = false;
4533 else
4534 puts("");
4535
4536 if (!isempty(fragment_path)) {
4537 printf("%s# %s%s\n",
4538 ansi_highlight_blue(),
4539 fragment_path,
4540 ansi_highlight_off());
4541 fflush(stdout);
4542
4543 r = sendfile_full(STDOUT_FILENO, fragment_path);
4544 if (r < 0) {
4545 log_warning("Failed to cat %s: %s", fragment_path, strerror(-r));
4546 continue;
4547 }
4548 }
4549
4550 STRV_FOREACH(path, dropin_paths) {
4551 printf("%s%s# %s%s\n",
4552 isempty(fragment_path) && path == dropin_paths ? "" : "\n",
4553 ansi_highlight_blue(),
4554 *path,
4555 ansi_highlight_off());
4556 fflush(stdout);
4557
4558 r = sendfile_full(STDOUT_FILENO, *path);
4559 if (r < 0) {
4560 log_warning("Failed to cat %s: %s", *path, strerror(-r));
4561 continue;
4562 }
4563 }
4564 }
4565
4566 return r < 0 ? r : 0;
4567}
4568
f459b602
MAP
4569static int set_property(sd_bus *bus, char **args) {
4570 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
4571 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
68372da6 4572 _cleanup_free_ char *n = NULL;
8e2af478
LP
4573 char **i;
4574 int r;
4575
f459b602
MAP
4576 r = sd_bus_message_new_method_call(
4577 bus,
151b9b96 4578 &m,
8e2af478
LP
4579 "org.freedesktop.systemd1",
4580 "/org/freedesktop/systemd1",
4581 "org.freedesktop.systemd1.Manager",
151b9b96 4582 "SetUnitProperties");
f459b602
MAP
4583 if (r < 0)
4584 return bus_log_create_error(r);
8e2af478 4585
f78e6385 4586 n = unit_name_mangle(args[1], MANGLE_NOGLOB);
68372da6
LP
4587 if (!n)
4588 return log_oom();
4589
f459b602
MAP
4590 r = sd_bus_message_append(m, "sb", n, arg_runtime);
4591 if (r < 0)
4592 return bus_log_create_error(r);
8e2af478 4593
f459b602
MAP
4594 r = sd_bus_message_open_container(m, SD_BUS_TYPE_ARRAY, "(sv)");
4595 if (r < 0)
4596 return bus_log_create_error(r);
8e2af478 4597
f459b602
MAP
4598 STRV_FOREACH(i, args + 2) {
4599 r = sd_bus_message_open_container(m, SD_BUS_TYPE_STRUCT, "sv");
4600 if (r < 0)
4601 return bus_log_create_error(r);
8e2af478 4602
df31a6c0 4603 r = bus_append_unit_property_assignment(m, *i);
8e2af478
LP
4604 if (r < 0)
4605 return r;
4606
f459b602
MAP
4607 r = sd_bus_message_close_container(m);
4608 if (r < 0)
4609 return bus_log_create_error(r);
8e2af478
LP
4610 }
4611
f459b602
MAP
4612 r = sd_bus_message_close_container(m);
4613 if (r < 0)
4614 return bus_log_create_error(r);
8e2af478 4615
c49b30a2 4616 r = sd_bus_call(bus, m, 0, &error, NULL);
f459b602
MAP
4617 if (r < 0) {
4618 log_error("Failed to set unit properties on %s: %s", n, bus_error_message(&error, r));
4619 return r;
8e2af478
LP
4620 }
4621
4622 return 0;
4623}
4624
f459b602
MAP
4625static int snapshot(sd_bus *bus, char **args) {
4626 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
4627 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
4628 _cleanup_free_ char *n = NULL, *id = NULL;
4629 const char *path;
7e4249b9 4630 int r;
7e4249b9 4631
1dcf6065 4632 if (strv_length(args) > 1)
f78e6385 4633 n = unit_name_mangle_with_suffix(args[1], MANGLE_NOGLOB, ".snapshot");
1dcf6065
LP
4634 else
4635 n = strdup("");
4636 if (!n)
4637 return log_oom();
7e4249b9 4638
f459b602 4639 r = sd_bus_call_method(
f22f08cd
SP
4640 bus,
4641 "org.freedesktop.systemd1",
4642 "/org/freedesktop/systemd1",
4643 "org.freedesktop.systemd1.Manager",
4644 "CreateSnapshot",
f459b602 4645 &error,
f22f08cd 4646 &reply,
f459b602
MAP
4647 "sb", n, false);
4648 if (r < 0) {
4649 log_error("Failed to create snapshot: %s", bus_error_message(&error, r));
1dcf6065 4650 return r;
7e4249b9
LP
4651 }
4652
f459b602
MAP
4653 r = sd_bus_message_read(reply, "o", &path);
4654 if (r < 0)
4655 return bus_log_parse_error(r);
5dd9014f 4656
f459b602 4657 r = sd_bus_get_property_string(
f22f08cd
SP
4658 bus,
4659 "org.freedesktop.systemd1",
4660 path,
f459b602
MAP
4661 "org.freedesktop.systemd1.Unit",
4662 "Id",
4663 &error,
4664 &id);
4665 if (r < 0) {
4666 log_error("Failed to get ID of snapshot: %s", bus_error_message(&error, r));
1dcf6065 4667 return r;
7e4249b9
LP
4668 }
4669
0183528f
LP
4670 if (!arg_quiet)
4671 puts(id);
7e4249b9 4672
1dcf6065 4673 return 0;
7e4249b9
LP
4674}
4675
f459b602
MAP
4676static int delete_snapshot(sd_bus *bus, char **args) {
4677 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e3e0314b 4678 _cleanup_strv_free_ char **names = NULL;
729e3769 4679 char **name;
e3e0314b 4680 int r, q;
6759e7a7 4681
6759e7a7
LP
4682 assert(args);
4683
e3e0314b
ZJS
4684 r = expand_names(bus, args + 1, ".snapshot", &names);
4685 if (r < 0)
4686 log_error("Failed to expand names: %s", strerror(-r));
1dcf6065 4687
e3e0314b
ZJS
4688 STRV_FOREACH(name, names) {
4689 q = sd_bus_call_method(
f22f08cd 4690 bus,
b0193f1c
LP
4691 "org.freedesktop.systemd1",
4692 "/org/freedesktop/systemd1",
4693 "org.freedesktop.systemd1.Manager",
5dd9014f 4694 "RemoveSnapshot",
f459b602 4695 &error,
f22f08cd 4696 NULL,
e3e0314b
ZJS
4697 "s", *name);
4698 if (q < 0) {
4699 log_error("Failed to remove snapshot %s: %s",
4700 *name, bus_error_message(&error, r));
4701 if (r == 0)
4702 r = q;
f459b602 4703 }
6759e7a7
LP
4704 }
4705
e3e0314b 4706 return r;
6759e7a7
LP
4707}
4708
f459b602
MAP
4709static int daemon_reload(sd_bus *bus, char **args) {
4710 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
7e4249b9 4711 const char *method;
f459b602 4712 int r;
7e4249b9 4713
e4b61340
LP
4714 if (arg_action == ACTION_RELOAD)
4715 method = "Reload";
4716 else if (arg_action == ACTION_REEXEC)
4717 method = "Reexecute";
4718 else {
4719 assert(arg_action == ACTION_SYSTEMCTL);
4720
4721 method =
20b09ca7
LP
4722 streq(args[0], "clear-jobs") ||
4723 streq(args[0], "cancel") ? "ClearJobs" :
4724 streq(args[0], "daemon-reexec") ? "Reexecute" :
4725 streq(args[0], "reset-failed") ? "ResetFailed" :
4726 streq(args[0], "halt") ? "Halt" :
4727 streq(args[0], "poweroff") ? "PowerOff" :
4728 streq(args[0], "reboot") ? "Reboot" :
4729 streq(args[0], "kexec") ? "KExec" :
4730 streq(args[0], "exit") ? "Exit" :
4731 /* "daemon-reload" */ "Reload";
e4b61340 4732 }
7e4249b9 4733
f459b602 4734 r = sd_bus_call_method(
f22f08cd
SP
4735 bus,
4736 "org.freedesktop.systemd1",
4737 "/org/freedesktop/systemd1",
4738 "org.freedesktop.systemd1.Manager",
4739 method,
c516c8d1 4740 &error,
f459b602
MAP
4741 NULL,
4742 NULL);
f22f08cd
SP
4743
4744 if (r == -ENOENT && arg_action != ACTION_SYSTEMCTL)
4745 /* There's always a fallback possible for
4746 * legacy actions. */
4747 r = -EADDRNOTAVAIL;
d0ede8f1
LP
4748 else if ((r == -ETIMEDOUT || r == -ECONNRESET) && streq(method, "Reexecute"))
4749 /* On reexecution, we expect a disconnect, not a
4750 * reply */
f22f08cd 4751 r = 0;
1dcf6065 4752 else if (r < 0)
f459b602 4753 log_error("Failed to execute operation: %s", bus_error_message(&error, r));
7e4249b9 4754
0a9776c2 4755 return r < 0 ? r : 0;
7e4249b9
LP
4756}
4757
f459b602
MAP
4758static int reset_failed(sd_bus *bus, char **args) {
4759 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e3e0314b 4760 _cleanup_strv_free_ char **names = NULL;
f84190d8 4761 char **name;
e3e0314b 4762 int r, q;
5632e374 4763
729e3769
LP
4764 if (strv_length(args) <= 1)
4765 return daemon_reload(bus, args);
5632e374 4766
e3e0314b
ZJS
4767 r = expand_names(bus, args + 1, NULL, &names);
4768 if (r < 0)
4769 log_error("Failed to expand names: %s", strerror(-r));
f84190d8 4770
e3e0314b
ZJS
4771 STRV_FOREACH(name, names) {
4772 q = sd_bus_call_method(
f22f08cd 4773 bus,
b0193f1c
LP
4774 "org.freedesktop.systemd1",
4775 "/org/freedesktop/systemd1",
4776 "org.freedesktop.systemd1.Manager",
f22f08cd 4777 "ResetFailedUnit",
f459b602 4778 &error,
f22f08cd 4779 NULL,
e3e0314b
ZJS
4780 "s", *name);
4781 if (q < 0) {
4782 log_error("Failed to reset failed state of unit %s: %s",
4783 *name, bus_error_message(&error, r));
4784 if (r == 0)
4785 r = q;
f459b602 4786 }
5632e374
LP
4787 }
4788
e3e0314b 4789 return r;
5632e374
LP
4790}
4791
f459b602
MAP
4792static int show_environment(sd_bus *bus, char **args) {
4793 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
4794 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
4795 const char *text;
7e4249b9 4796 int r;
7e4249b9 4797
1968a360 4798 pager_open_if_enabled();
ec14911e 4799
f459b602 4800 r = sd_bus_get_property(
f22f08cd
SP
4801 bus,
4802 "org.freedesktop.systemd1",
4803 "/org/freedesktop/systemd1",
f459b602
MAP
4804 "org.freedesktop.systemd1.Manager",
4805 "Environment",
4806 &error,
f22f08cd 4807 &reply,
f459b602
MAP
4808 "as");
4809 if (r < 0) {
4810 log_error("Failed to get environment: %s", bus_error_message(&error, r));
f84190d8 4811 return r;
7e4249b9
LP
4812 }
4813
f459b602
MAP
4814 r = sd_bus_message_enter_container(reply, SD_BUS_TYPE_ARRAY, "s");
4815 if (r < 0)
4816 return bus_log_parse_error(r);
7e4249b9 4817
f459b602 4818 while ((r = sd_bus_message_read_basic(reply, SD_BUS_TYPE_STRING, &text)) > 0)
f84190d8 4819 puts(text);
f459b602
MAP
4820 if (r < 0)
4821 return bus_log_parse_error(r);
7e4249b9 4822
f459b602
MAP
4823 r = sd_bus_message_exit_container(reply);
4824 if (r < 0)
4825 return bus_log_parse_error(r);
7e4249b9 4826
f84190d8 4827 return 0;
7e4249b9
LP
4828}
4829
f459b602
MAP
4830static int switch_root(sd_bus *bus, char **args) {
4831 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
f39d4a08
HH
4832 _cleanup_free_ char *cmdline_init = NULL;
4833 const char *root, *init;
f459b602
MAP
4834 unsigned l;
4835 int r;
957eb8ca
LP
4836
4837 l = strv_length(args);
4838 if (l < 2 || l > 3) {
4839 log_error("Wrong number of arguments.");
4840 return -EINVAL;
4841 }
4842
4843 root = args[1];
13068da8
TG
4844
4845 if (l >= 3)
f39d4a08 4846 init = args[2];
13068da8 4847 else {
f39d4a08
HH
4848 r = parse_env_file("/proc/cmdline", WHITESPACE,
4849 "init", &cmdline_init,
4850 NULL);
4851 if (r < 0)
4852 log_debug("Failed to parse /proc/cmdline: %s", strerror(-r));
13068da8 4853
f39d4a08 4854 init = cmdline_init;
13068da8 4855 }
f459b602 4856
f39d4a08
HH
4857 if (isempty(init))
4858 init = NULL;
4859
4860 if (init) {
4861 const char *root_systemd_path = NULL, *root_init_path = NULL;
4862
4863 root_systemd_path = strappenda(root, "/" SYSTEMD_BINARY_PATH);
4864 root_init_path = strappenda3(root, "/", init);
4865
4866 /* If the passed init is actually the same as the
4867 * systemd binary, then let's suppress it. */
4868 if (files_same(root_init_path, root_systemd_path) > 0)
4869 init = NULL;
4870 }
13068da8 4871
f39d4a08 4872 log_debug("Switching root - root: %s; init: %s", root, strna(init));
957eb8ca 4873
f459b602 4874 r = sd_bus_call_method(
f22f08cd 4875 bus,
957eb8ca
LP
4876 "org.freedesktop.systemd1",
4877 "/org/freedesktop/systemd1",
4878 "org.freedesktop.systemd1.Manager",
f22f08cd 4879 "SwitchRoot",
f459b602 4880 &error,
f22f08cd 4881 NULL,
f459b602
MAP
4882 "ss", root, init);
4883 if (r < 0) {
4884 log_error("Failed to switch root: %s", bus_error_message(&error, r));
4885 return r;
4886 }
4887
4888 return 0;
957eb8ca
LP
4889}
4890
f459b602
MAP
4891static int set_environment(sd_bus *bus, char **args) {
4892 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
4893 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
7e4249b9 4894 const char *method;
31e767f7
LP
4895 int r;
4896
4897 assert(bus);
60f9ba0b 4898 assert(args);
7e4249b9 4899
7e4249b9
LP
4900 method = streq(args[0], "set-environment")
4901 ? "SetEnvironment"
4902 : "UnsetEnvironment";
4903
f459b602
MAP
4904 r = sd_bus_message_new_method_call(
4905 bus,
151b9b96 4906 &m,
31e767f7
LP
4907 "org.freedesktop.systemd1",
4908 "/org/freedesktop/systemd1",
4909 "org.freedesktop.systemd1.Manager",
151b9b96 4910 method);
f459b602
MAP
4911 if (r < 0)
4912 return bus_log_create_error(r);
7e4249b9 4913
f459b602 4914 r = sd_bus_message_append_strv(m, args + 1);
31e767f7 4915 if (r < 0)
f459b602 4916 return bus_log_create_error(r);
7e4249b9 4917
c49b30a2 4918 r = sd_bus_call(bus, m, 0, &error, NULL);
f459b602
MAP
4919 if (r < 0) {
4920 log_error("Failed to set environment: %s", bus_error_message(&error, r));
4921 return r;
7e4249b9
LP
4922 }
4923
f84190d8 4924 return 0;
7e4249b9
LP
4925}
4926
ac3efa8a
LP
4927static int import_environment(sd_bus *bus, char **args) {
4928 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
4929 _cleanup_bus_message_unref_ sd_bus_message *m = NULL;
4930 int r;
4931
4932 assert(bus);
4933 assert(args);
4934
4935 r = sd_bus_message_new_method_call(
4936 bus,
151b9b96 4937 &m,
ac3efa8a
LP
4938 "org.freedesktop.systemd1",
4939 "/org/freedesktop/systemd1",
4940 "org.freedesktop.systemd1.Manager",
151b9b96 4941 "SetEnvironment");
ac3efa8a
LP
4942 if (r < 0)
4943 return bus_log_create_error(r);
4944
4945 if (strv_isempty(args + 1))
4946 r = sd_bus_message_append_strv(m, environ);
4947 else {
4948 char **a, **b;
4949
4950 r = sd_bus_message_open_container(m, 'a', "s");
4951 if (r < 0)
4952 return bus_log_create_error(r);
4953
4954 STRV_FOREACH(a, args + 1) {
4955
4956 if (!env_name_is_valid(*a)) {
4957 log_error("Not a valid environment variable name: %s", *a);
4958 return -EINVAL;
4959 }
4960
4961 STRV_FOREACH(b, environ) {
4962 const char *eq;
4963
4964 eq = startswith(*b, *a);
4965 if (eq && *eq == '=') {
4966
4967 r = sd_bus_message_append(m, "s", *b);
4968 if (r < 0)
4969 return bus_log_create_error(r);
4970
4971 break;
4972 }
4973 }
4974 }
4975
4976 r = sd_bus_message_close_container(m);
4977 }
4978 if (r < 0)
4979 return bus_log_create_error(r);
4980
4981 r = sd_bus_call(bus, m, 0, &error, NULL);
4982 if (r < 0) {
4983 log_error("Failed to import environment: %s", bus_error_message(&error, r));
4984 return r;
4985 }
4986
4987 return 0;
4988}
4989
cbb13b2a 4990static int enable_sysv_units(const char *verb, char **args) {
729e3769 4991 int r = 0;
ee5762e3 4992
77e68fa2 4993#if defined(HAVE_SYSV_COMPAT) && defined(HAVE_CHKCONFIG)
729e3769 4994 unsigned f = 1, t = 1;
fb15be83 4995 _cleanup_lookup_paths_free_ LookupPaths paths = {};
ee5762e3 4996
729e3769
LP
4997 if (arg_scope != UNIT_FILE_SYSTEM)
4998 return 0;
ee5762e3 4999
729e3769
LP
5000 if (!streq(verb, "enable") &&
5001 !streq(verb, "disable") &&
5002 !streq(verb, "is-enabled"))
5003 return 0;
ee5762e3 5004
729e3769
LP
5005 /* Processes all SysV units, and reshuffles the array so that
5006 * afterwards only the native units remain */
ee5762e3 5007
12ed81d9 5008 r = lookup_paths_init(&paths, SYSTEMD_SYSTEM, false, arg_root, NULL, NULL, NULL);
729e3769
LP
5009 if (r < 0)
5010 return r;
ee5762e3 5011
729e3769 5012 r = 0;
cbb13b2a 5013 for (f = 0; args[f]; f++) {
729e3769 5014 const char *name;
05cae7f3 5015 _cleanup_free_ char *p = NULL, *q = NULL, *l = NULL;
729e3769
LP
5016 bool found_native = false, found_sysv;
5017 unsigned c = 1;
5018 const char *argv[6] = { "/sbin/chkconfig", NULL, NULL, NULL, NULL };
05cae7f3 5019 char **k;
729e3769
LP
5020 int j;
5021 pid_t pid;
5022 siginfo_t status;
ee5762e3 5023
729e3769 5024 name = args[f];
ee5762e3 5025
729e3769
LP
5026 if (!endswith(name, ".service"))
5027 continue;
ee5762e3 5028
729e3769
LP
5029 if (path_is_absolute(name))
5030 continue;
ee5762e3 5031
729e3769 5032 STRV_FOREACH(k, paths.unit_path) {
05cae7f3
ZJS
5033 _cleanup_free_ char *path = NULL;
5034
729e3769 5035 if (!isempty(arg_root))
4723e4b2 5036 asprintf(&path, "%s/%s/%s", arg_root, *k, name);
729e3769 5037 else
4723e4b2 5038 asprintf(&path, "%s/%s", *k, name);
ee5762e3 5039
4723e4b2 5040 if (!path) {
0d0f0c50 5041 r = log_oom();
729e3769
LP
5042 goto finish;
5043 }
ee5762e3 5044
4723e4b2 5045 found_native = access(path, F_OK) >= 0;
729e3769
LP
5046 if (found_native)
5047 break;
5048 }
ee5762e3 5049
729e3769
LP
5050 if (found_native)
5051 continue;
ee5762e3 5052
729e3769
LP
5053 if (!isempty(arg_root))
5054 asprintf(&p, "%s/" SYSTEM_SYSVINIT_PATH "/%s", arg_root, name);
5055 else
5056 asprintf(&p, SYSTEM_SYSVINIT_PATH "/%s", name);
5057 if (!p) {
0d0f0c50 5058 r = log_oom();
729e3769
LP
5059 goto finish;
5060 }
ee5762e3 5061
05cae7f3 5062 p[strlen(p) - strlen(".service")] = 0;
729e3769 5063 found_sysv = access(p, F_OK) >= 0;
4b6756a8 5064 if (!found_sysv)
729e3769 5065 continue;
71fad675 5066
729e3769
LP
5067 /* Mark this entry, so that we don't try enabling it as native unit */
5068 args[f] = (char*) "";
ee5762e3 5069
729e3769 5070 log_info("%s is not a native service, redirecting to /sbin/chkconfig.", name);
ee5762e3 5071
729e3769
LP
5072 if (!isempty(arg_root))
5073 argv[c++] = q = strappend("--root=", arg_root);
ee5762e3 5074
2b6bf07d 5075 argv[c++] = basename(p);
729e3769
LP
5076 argv[c++] =
5077 streq(verb, "enable") ? "on" :
5078 streq(verb, "disable") ? "off" : "--level=5";
5079 argv[c] = NULL;
ee5762e3 5080
729e3769
LP
5081 l = strv_join((char**)argv, " ");
5082 if (!l) {
0d0f0c50 5083 r = log_oom();
729e3769
LP
5084 goto finish;
5085 }
ee5762e3 5086
729e3769 5087 log_info("Executing %s", l);
ee5762e3 5088
729e3769
LP
5089 pid = fork();
5090 if (pid < 0) {
5091 log_error("Failed to fork: %m");
729e3769
LP
5092 r = -errno;
5093 goto finish;
5094 } else if (pid == 0) {
5095 /* Child */
ee5762e3 5096
729e3769
LP
5097 execv(argv[0], (char**) argv);
5098 _exit(EXIT_FAILURE);
5099 }
ee5762e3 5100
729e3769
LP
5101 j = wait_for_terminate(pid, &status);
5102 if (j < 0) {
5103 log_error("Failed to wait for child: %s", strerror(-r));
5104 r = j;
5105 goto finish;
5106 }
ee5762e3 5107
729e3769
LP
5108 if (status.si_code == CLD_EXITED) {
5109 if (streq(verb, "is-enabled")) {
5110 if (status.si_status == 0) {
5111 if (!arg_quiet)
5112 puts("enabled");
5113 r = 1;
5114 } else {
5115 if (!arg_quiet)
5116 puts("disabled");
5117 }
ee5762e3 5118
729e3769
LP
5119 } else if (status.si_status != 0) {
5120 r = -EINVAL;
5121 goto finish;
5122 }
5123 } else {
5124 r = -EPROTO;
5125 goto finish;
5126 }
ee5762e3
LP
5127 }
5128
729e3769 5129finish:
729e3769 5130 /* Drop all SysV units */
cbb13b2a 5131 for (f = 0, t = 0; args[f]; f++) {
ee5762e3 5132
729e3769 5133 if (isempty(args[f]))
ee5762e3
LP
5134 continue;
5135
729e3769
LP
5136 args[t++] = args[f];
5137 }
ee5762e3 5138
729e3769 5139 args[t] = NULL;
ee5762e3 5140
729e3769
LP
5141#endif
5142 return r;
5143}
ee5762e3 5144
37370d0c 5145static int mangle_names(char **original_names, char ***mangled_names) {
a33fdebb 5146 char **i, **l, **name;
37370d0c 5147
a33fdebb
LP
5148 l = new(char*, strv_length(original_names) + 1);
5149 if (!l)
37370d0c
VP
5150 return log_oom();
5151
a33fdebb 5152 i = l;
37370d0c 5153 STRV_FOREACH(name, original_names) {
44386fc1
LN
5154
5155 /* When enabling units qualified path names are OK,
5156 * too, hence allow them explicitly. */
5157
5158 if (is_path(*name))
5159 *i = strdup(*name);
5160 else
f78e6385 5161 *i = unit_name_mangle(*name, MANGLE_NOGLOB);
44386fc1 5162
a33fdebb
LP
5163 if (!*i) {
5164 strv_free(l);
37370d0c 5165 return log_oom();
a33fdebb
LP
5166 }
5167
5168 i++;
37370d0c 5169 }
a33fdebb
LP
5170
5171 *i = NULL;
5172 *mangled_names = l;
37370d0c
VP
5173
5174 return 0;
5175}
5176
f459b602 5177static int enable_unit(sd_bus *bus, char **args) {
e3e0314b 5178 _cleanup_strv_free_ char **names = NULL;
729e3769
LP
5179 const char *verb = args[0];
5180 UnitFileChange *changes = NULL;
718db961 5181 unsigned n_changes = 0;
729e3769 5182 int carries_install_info = -1;
729e3769 5183 int r;
ee5762e3 5184
ab5919fa
MS
5185 if (!args[1])
5186 return 0;
5187
e3e0314b 5188 r = mangle_names(args+1, &names);
3a05c0f9 5189 if (r < 0)
cbb13b2a
VP
5190 return r;
5191
e3e0314b 5192 r = enable_sysv_units(verb, names);
cbb13b2a
VP
5193 if (r < 0)
5194 return r;
3a05c0f9 5195
67d66210
LP
5196 /* If the operation was fully executed by the SysV compat,
5197 * let's finish early */
5198 if (strv_isempty(names))
5199 return 0;
5200
729e3769
LP
5201 if (!bus || avoid_bus()) {
5202 if (streq(verb, "enable")) {
e3e0314b 5203 r = unit_file_enable(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
729e3769
LP
5204 carries_install_info = r;
5205 } else if (streq(verb, "disable"))
e3e0314b 5206 r = unit_file_disable(arg_scope, arg_runtime, arg_root, names, &changes, &n_changes);
729e3769 5207 else if (streq(verb, "reenable")) {
e3e0314b 5208 r = unit_file_reenable(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
729e3769
LP
5209 carries_install_info = r;
5210 } else if (streq(verb, "link"))
e3e0314b 5211 r = unit_file_link(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
729e3769 5212 else if (streq(verb, "preset")) {
d309c1c3 5213 r = unit_file_preset(arg_scope, arg_runtime, arg_root, names, arg_preset_mode, arg_force, &changes, &n_changes);
729e3769
LP
5214 carries_install_info = r;
5215 } else if (streq(verb, "mask"))
e3e0314b 5216 r = unit_file_mask(arg_scope, arg_runtime, arg_root, names, arg_force, &changes, &n_changes);
729e3769 5217 else if (streq(verb, "unmask"))
e3e0314b 5218 r = unit_file_unmask(arg_scope, arg_runtime, arg_root, names, &changes, &n_changes);
729e3769
LP
5219 else
5220 assert_not_reached("Unknown verb");
ee5762e3 5221
729e3769
LP
5222 if (r < 0) {
5223 log_error("Operation failed: %s", strerror(-r));
5224 goto finish;
ee5762e3
LP
5225 }
5226
718db961
LP
5227 if (!arg_quiet)
5228 dump_unit_file_changes(changes, n_changes);
ee5762e3 5229
df77cdf0 5230 r = 0;
729e3769 5231 } else {
718db961
LP
5232 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL, *m = NULL;
5233 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
f459b602 5234 int expect_carries_install_info = false;
d309c1c3 5235 bool send_force = true, send_preset_mode = false;
718db961 5236 const char *method;
729e3769
LP
5237
5238 if (streq(verb, "enable")) {
5239 method = "EnableUnitFiles";
5240 expect_carries_install_info = true;
5241 } else if (streq(verb, "disable")) {
5242 method = "DisableUnitFiles";
5243 send_force = false;
5244 } else if (streq(verb, "reenable")) {
5245 method = "ReenableUnitFiles";
5246 expect_carries_install_info = true;
5247 } else if (streq(verb, "link"))
5248 method = "LinkUnitFiles";
5249 else if (streq(verb, "preset")) {
d309c1c3
LP
5250
5251 if (arg_preset_mode != UNIT_FILE_PRESET_FULL) {
5252 method = "PresetUnitFilesWithMode";
5253 send_preset_mode = true;
5254 } else
5255 method = "PresetUnitFiles";
5256
729e3769
LP
5257 expect_carries_install_info = true;
5258 } else if (streq(verb, "mask"))
5259 method = "MaskUnitFiles";
5260 else if (streq(verb, "unmask")) {
5261 method = "UnmaskUnitFiles";
5262 send_force = false;
5263 } else
5264 assert_not_reached("Unknown verb");
5265
f459b602
MAP
5266 r = sd_bus_message_new_method_call(
5267 bus,
151b9b96 5268 &m,
729e3769
LP
5269 "org.freedesktop.systemd1",
5270 "/org/freedesktop/systemd1",
5271 "org.freedesktop.systemd1.Manager",
151b9b96 5272 method);
f459b602
MAP
5273 if (r < 0)
5274 return bus_log_create_error(r);
ee5762e3 5275
e3e0314b 5276 r = sd_bus_message_append_strv(m, names);
f459b602
MAP
5277 if (r < 0)
5278 return bus_log_create_error(r);
ee5762e3 5279
d309c1c3
LP
5280 if (send_preset_mode) {
5281 r = sd_bus_message_append(m, "s", unit_file_preset_mode_to_string(arg_preset_mode));
5282 if (r < 0)
5283 return bus_log_create_error(r);
5284 }
5285
f459b602
MAP
5286 r = sd_bus_message_append(m, "b", arg_runtime);
5287 if (r < 0)
5288 return bus_log_create_error(r);
ee5762e3 5289
729e3769 5290 if (send_force) {
f459b602
MAP
5291 r = sd_bus_message_append(m, "b", arg_force);
5292 if (r < 0)
5293 return bus_log_create_error(r);
ee5762e3
LP
5294 }
5295
c49b30a2 5296 r = sd_bus_call(bus, m, 0, &error, &reply);
f459b602
MAP
5297 if (r < 0) {
5298 log_error("Failed to execute operation: %s", bus_error_message(&error, r));
5299 return r;
729e3769 5300 }
be394c48 5301
729e3769 5302 if (expect_carries_install_info) {
f459b602
MAP
5303 r = sd_bus_message_read(reply, "b", &carries_install_info);
5304 if (r < 0)
5305 return bus_log_parse_error(r);
ee5762e3
LP
5306 }
5307
cc3f2093 5308 r = deserialize_and_dump_unit_file_changes(reply);
f459b602 5309 if (r < 0)
718db961 5310 return r;
b77398f7 5311
93c941e3 5312 /* Try to reload if enabled */
d6cb60c7 5313 if (!arg_no_reload)
729e3769 5314 r = daemon_reload(bus, args);
f459b602
MAP
5315 else
5316 r = 0;
b647f10d 5317 }
3d3961f2 5318
729e3769 5319 if (carries_install_info == 0)
416389f7
LP
5320 log_warning("The unit files have no [Install] section. They are not meant to be enabled\n"
5321 "using systemctl.\n"
5322 "Possible reasons for having this kind of units are:\n"
5323 "1) A unit may be statically enabled by being symlinked from another unit's\n"
5324 " .wants/ or .requires/ directory.\n"
5325 "2) A unit's purpose may be to act as a helper for some other unit which has\n"
5326 " a requirement dependency on it.\n"
5327 "3) A unit may be started when needed via activation (socket, path, timer,\n"
5328 " D-Bus, udev, scripted systemctl call, ...).\n");
ee5762e3 5329
729e3769 5330finish:
729e3769 5331 unit_file_changes_free(changes, n_changes);
ee5762e3 5332
729e3769 5333 return r;
ee5762e3
LP
5334}
5335
d309c1c3
LP
5336static int preset_all(sd_bus *bus, char **args) {
5337 UnitFileChange *changes = NULL;
5338 unsigned n_changes = 0;
5339 int r;
5340
5341 if (!bus || avoid_bus()) {
5342
5343 r = unit_file_preset_all(arg_scope, arg_runtime, arg_root, arg_preset_mode, arg_force, &changes, &n_changes);
5344 if (r < 0) {
5345 log_error("Operation failed: %s", strerror(-r));
5346 goto finish;
5347 }
5348
5349 if (!arg_quiet)
5350 dump_unit_file_changes(changes, n_changes);
5351
5352 r = 0;
5353
5354 } else {
5355 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
5356 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
5357
5358 r = sd_bus_call_method(
5359 bus,
5360 "org.freedesktop.systemd1",
5361 "/org/freedesktop/systemd1",
5362 "org.freedesktop.systemd1.Manager",
5363 "PresetAllUnitFiles",
5364 &error,
5365 &reply,
5366 "sbb",
5367 unit_file_preset_mode_to_string(arg_preset_mode),
5368 arg_runtime,
5369 arg_force);
5370 if (r < 0) {
5371 log_error("Failed to execute operation: %s", bus_error_message(&error, r));
5372 return r;
5373 }
5374
5375 r = deserialize_and_dump_unit_file_changes(reply);
5376 if (r < 0)
5377 return r;
5378
5379 if (!arg_no_reload)
5380 r = daemon_reload(bus, args);
5381 else
5382 r = 0;
5383 }
5384
5385finish:
5386 unit_file_changes_free(changes, n_changes);
5387
5388 return r;
5389}
5390
f459b602
MAP
5391static int unit_is_enabled(sd_bus *bus, char **args) {
5392
5393 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
e3e0314b 5394 _cleanup_strv_free_ char **names = NULL;
729e3769
LP
5395 bool enabled;
5396 char **name;
f459b602 5397 int r;
ee5762e3 5398
e3e0314b 5399 r = mangle_names(args+1, &names);
cbb13b2a
VP
5400 if (r < 0)
5401 return r;
5402
e3e0314b 5403 r = enable_sysv_units(args[0], names);
729e3769
LP
5404 if (r < 0)
5405 return r;
ee5762e3 5406
729e3769 5407 enabled = r > 0;
ee5762e3 5408
729e3769 5409 if (!bus || avoid_bus()) {
ee5762e3 5410
e3e0314b 5411 STRV_FOREACH(name, names) {
729e3769 5412 UnitFileState state;
ee5762e3 5413
cbb13b2a 5414 state = unit_file_get_state(arg_scope, arg_root, *name);
cbc9fbd1
LP
5415 if (state < 0) {
5416 log_error("Failed to get unit file state for %s: %s", *name, strerror(-state));
cec7eda5 5417 return state;
cbc9fbd1 5418 }
ee5762e3 5419
729e3769
LP
5420 if (state == UNIT_FILE_ENABLED ||
5421 state == UNIT_FILE_ENABLED_RUNTIME ||
5422 state == UNIT_FILE_STATIC)
5423 enabled = true;
5424
5425 if (!arg_quiet)
5426 puts(unit_file_state_to_string(state));
71fad675 5427 }
ee5762e3 5428
729e3769 5429 } else {
e3e0314b 5430 STRV_FOREACH(name, names) {
f459b602 5431 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
729e3769 5432 const char *s;
63a723f3 5433
f459b602 5434 r = sd_bus_call_method(
f22f08cd 5435 bus,
729e3769
LP
5436 "org.freedesktop.systemd1",
5437 "/org/freedesktop/systemd1",
5438 "org.freedesktop.systemd1.Manager",
f22f08cd 5439 "GetUnitFileState",
f459b602 5440 &error,
f22f08cd 5441 &reply,
04504f93 5442 "s", *name);
f459b602
MAP
5443 if (r < 0) {
5444 log_error("Failed to get unit file state for %s: %s", *name, bus_error_message(&error, r));
cec7eda5 5445 return r;
ee5762e3
LP
5446 }
5447
f459b602
MAP
5448 r = sd_bus_message_read(reply, "s", &s);
5449 if (r < 0)
5450 return bus_log_parse_error(r);
ee5762e3 5451
729e3769
LP
5452 if (streq(s, "enabled") ||
5453 streq(s, "enabled-runtime") ||
5454 streq(s, "static"))
5455 enabled = true;
5456
5457 if (!arg_quiet)
5458 puts(s);
560d8f23 5459 }
ee5762e3
LP
5460 }
5461
f459b602 5462 return !enabled;
ee5762e3
LP
5463}
5464
e4b61340 5465static int systemctl_help(void) {
7e4249b9 5466
729e3769
LP
5467 pager_open_if_enabled();
5468
2e33c433 5469 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
729e3769 5470 "Query or send control commands to the systemd manager.\n\n"
8a0867d6
LP
5471 " -h --help Show this help\n"
5472 " --version Show package version\n"
f459b602
MAP
5473 " --system Connect to system manager\n"
5474 " --user Connect to user service manager\n"
5475 " -H --host=[USER@]HOST\n"
5476 " Operate on remote host\n"
5477 " -M --machine=CONTAINER\n"
5478 " Operate on local container\n"
8a0867d6 5479 " -t --type=TYPE List only units of a particular type\n"
a521ae4a 5480 " --state=STATE List only units with particular LOAD or SUB or ACTIVE state\n"
8a0867d6 5481 " -p --property=NAME Show only properties by this name\n"
a5e4972c
LP
5482 " -a --all Show all loaded units/properties, including dead/empty\n"
5483 " ones. To list all units installed on the system, use\n"
5484 " the 'list-unit-files' command instead.\n"
98a6e132 5485 " -l --full Don't ellipsize unit names on output\n"
1238ee09 5486 " -r --recursive Show unit list of host and local containers\n"
4dc5b821
LP
5487 " --reverse Show reverse dependencies with 'list-dependencies'\n"
5488 " --job-mode=MODE Specify how to deal with already queued jobs, when\n"
5489 " queueing a new job\n"
a521ae4a 5490 " --show-types When showing sockets, explicitly show their type\n"
b37844d3
LP
5491 " -i --ignore-inhibitors\n"
5492 " When shutting down or sleeping, ignore inhibitors\n"
a8f11321
LP
5493 " --kill-who=WHO Who to send signal to\n"
5494 " -s --signal=SIGNAL Which signal to send\n"
8a0867d6
LP
5495 " -q --quiet Suppress output\n"
5496 " --no-block Do not wait until operation finished\n"
8a0867d6 5497 " --no-wall Don't send wall message before halt/power-off/reboot\n"
8a0867d6
LP
5498 " --no-reload When enabling/disabling unit files, don't reload daemon\n"
5499 " configuration\n"
ebed32bf 5500 " --no-legend Do not print a legend (column headers and hints)\n"
69fc152f 5501 " --no-pager Do not pipe output into a pager\n"
501fc174
LP
5502 " --no-ask-password\n"
5503 " Do not ask for system passwords\n"
a8f11321 5504 " --global Enable/disable unit files globally\n"
a521ae4a 5505 " --runtime Enable unit files only temporarily until next reboot\n"
8a0867d6
LP
5506 " -f --force When enabling unit files, override existing symlinks\n"
5507 " When shutting down, execute action immediately\n"
d309c1c3
LP
5508 " --preset-mode= Specifies whether fully apply presets, or only enable,\n"
5509 " or only disable\n"
729e3769 5510 " --root=PATH Enable unit files in the specified root directory\n"
76fdc966 5511 " -n --lines=INTEGER Number of journal entries to show\n"
d3f2bdbf 5512 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
815ebc54
DH
5513 " verbose, export, json, json-pretty, json-sse, cat)\n"
5514 " --plain Print unit dependencies as a list instead of a tree\n\n"
34c4b47b 5515 "Unit Commands:\n"
d8fba7c6
ZJS
5516 " list-units [PATTERN...] List loaded units\n"
5517 " list-sockets [PATTERN...] List loaded sockets ordered by address\n"
5518 " list-timers [PATTERN...] List loaded timers ordered by next elapse\n"
4f8f66cb
ZJS
5519 " start NAME... Start (activate) one or more units\n"
5520 " stop NAME... Stop (deactivate) one or more units\n"
5521 " reload NAME... Reload one or more units\n"
5522 " restart NAME... Start or restart one or more units\n"
5523 " try-restart NAME... Restart one or more units if active\n"
5524 " reload-or-restart NAME... Reload one or more units if possible,\n"
6f28c033 5525 " otherwise start or restart\n"
4f8f66cb 5526 " reload-or-try-restart NAME... Reload one or more units if possible,\n"
6f28c033 5527 " otherwise restart if active\n"
4f8f66cb
ZJS
5528 " isolate NAME Start one unit and stop all others\n"
5529 " kill NAME... Send signal to processes of a unit\n"
b3ae710c
ZJS
5530 " is-active PATTERN... Check whether units are active\n"
5531 " is-failed PATTERN... Check whether units are failed\n"
5532 " status [PATTERN...|PID...] Show runtime status of one or more units\n"
5533 " show [PATTERN...|JOB...] Show properties of one or more\n"
ee5762e3 5534 " units/jobs or the manager\n"
b3ae710c 5535 " cat PATTERN... Show files and drop-ins of one or more units\n"
4f8f66cb 5536 " set-property NAME ASSIGNMENT... Sets one or more properties of a unit\n"
b3ae710c
ZJS
5537 " help PATTERN...|PID... Show manual for one or more units\n"
5538 " reset-failed [PATTERN...] Reset failed state for all, one, or more\n"
fdf20a31 5539 " units\n"
55c0b89c 5540 " list-dependencies [NAME] Recursively show units which are required\n"
afba4199
ZJS
5541 " or wanted by this unit or by which this\n"
5542 " unit is required or wanted\n\n"
34c4b47b 5543 "Unit File Commands:\n"
d8fba7c6 5544 " list-unit-files [PATTERN...] List installed unit files\n"
4f8f66cb
ZJS
5545 " enable NAME... Enable one or more unit files\n"
5546 " disable NAME... Disable one or more unit files\n"
5547 " reenable NAME... Reenable one or more unit files\n"
5548 " preset NAME... Enable/disable one or more unit files\n"
729e3769 5549 " based on preset configuration\n"
d309c1c3
LP
5550 " preset-all Enable/disable all unit files based on\n"
5551 " preset configuration\n"
4f8f66cb
ZJS
5552 " is-enabled NAME... Check whether unit files are enabled\n\n"
5553 " mask NAME... Mask one or more units\n"
5554 " unmask NAME... Unmask one or more units\n"
5555 " link PATH... Link one or more units files into\n"
729e3769 5556 " the search path\n"
99504dd4 5557 " get-default Get the name of the default target\n"
f535088e 5558 " set-default NAME Set the default target\n\n"
0d292f5e
LP
5559 "Machine Commands:\n"
5560 " list-machines [PATTERN...] List local containers and host\n\n"
34c4b47b 5561 "Job Commands:\n"
d8fba7c6 5562 " list-jobs [PATTERN...] List jobs\n"
34c4b47b 5563 " cancel [JOB...] Cancel all, one, or more jobs\n\n"
34c4b47b 5564 "Snapshot Commands:\n"
7e4249b9 5565 " snapshot [NAME] Create a snapshot\n"
4f8f66cb 5566 " delete NAME... Remove one or more snapshots\n\n"
34c4b47b 5567 "Environment Commands:\n"
7e4249b9 5568 " show-environment Dump environment\n"
4f8f66cb 5569 " set-environment NAME=VALUE... Set one or more environment variables\n"
ac3efa8a
LP
5570 " unset-environment NAME... Unset one or more environment variables\n"
5571 " import-environment NAME... Import all, one or more environment variables\n\n"
34c4b47b
LP
5572 "Manager Lifecycle Commands:\n"
5573 " daemon-reload Reload systemd manager configuration\n"
5574 " daemon-reexec Reexecute systemd manager\n\n"
5575 "System Commands:\n"
20b09ca7
LP
5576 " default Enter system default mode\n"
5577 " rescue Enter system rescue mode\n"
5578 " emergency Enter system emergency mode\n"
514f4ef5 5579 " halt Shut down and halt the system\n"
2e33c433 5580 " poweroff Shut down and power-off the system\n"
37185ec8 5581 " reboot [ARG] Shut down and reboot the system\n"
20b09ca7 5582 " kexec Shut down and reboot the system with kexec\n"
6edd7d0a 5583 " exit Request user instance exit\n"
4f8f66cb 5584 " switch-root ROOT [INIT] Change to a different root file system\n"
6edd7d0a 5585 " suspend Suspend the system\n"
6524990f
LP
5586 " hibernate Hibernate the system\n"
5587 " hybrid-sleep Hibernate and suspend the system\n",
5b6319dc 5588 program_invocation_short_name);
7e4249b9
LP
5589
5590 return 0;
5591}
5592
e4b61340
LP
5593static int halt_help(void) {
5594
37185ec8 5595 printf("%s [OPTIONS...]%s\n\n"
e4b61340
LP
5596 "%s the system.\n\n"
5597 " --help Show this help\n"
5598 " --halt Halt the machine\n"
5599 " -p --poweroff Switch off the machine\n"
5600 " --reboot Reboot the machine\n"
2e33c433
LP
5601 " -f --force Force immediate halt/power-off/reboot\n"
5602 " -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
e4b61340 5603 " -d --no-wtmp Don't write wtmp record\n"
2e33c433 5604 " --no-wall Don't send wall message before halt/power-off/reboot\n",
e4b61340 5605 program_invocation_short_name,
37185ec8 5606 arg_action == ACTION_REBOOT ? " [ARG]" : "",
e4b61340
LP
5607 arg_action == ACTION_REBOOT ? "Reboot" :
5608 arg_action == ACTION_POWEROFF ? "Power off" :
5609 "Halt");
5610
5611 return 0;
5612}
5613
5614static int shutdown_help(void) {
5615
08e4b1c5 5616 printf("%s [OPTIONS...] [TIME] [WALL...]\n\n"
e4b61340
LP
5617 "Shut down the system.\n\n"
5618 " --help Show this help\n"
5619 " -H --halt Halt the machine\n"
5620 " -P --poweroff Power-off the machine\n"
5621 " -r --reboot Reboot the machine\n"
386da858 5622 " -h Equivalent to --poweroff, overridden by --halt\n"
2e33c433 5623 " -k Don't halt/power-off/reboot, just send warnings\n"
f6144808 5624 " --no-wall Don't send wall message before halt/power-off/reboot\n"
f6144808 5625 " -c Cancel a pending shutdown\n",
e4b61340
LP
5626 program_invocation_short_name);
5627
5628 return 0;
5629}
5630
5631static int telinit_help(void) {
5632
2e33c433 5633 printf("%s [OPTIONS...] {COMMAND}\n\n"
514f4ef5
LP
5634 "Send control commands to the init daemon.\n\n"
5635 " --help Show this help\n"
2e33c433 5636 " --no-wall Don't send wall message before halt/power-off/reboot\n\n"
e4b61340
LP
5637 "Commands:\n"
5638 " 0 Power-off the machine\n"
5639 " 6 Reboot the machine\n"
514f4ef5
LP
5640 " 2, 3, 4, 5 Start runlevelX.target unit\n"
5641 " 1, s, S Enter rescue mode\n"
5642 " q, Q Reload init daemon configuration\n"
5643 " u, U Reexecute init daemon\n",
e4b61340
LP
5644 program_invocation_short_name);
5645
5646 return 0;
5647}
5648
5649static int runlevel_help(void) {
5650
2e33c433 5651 printf("%s [OPTIONS...]\n\n"
e4b61340
LP
5652 "Prints the previous and current runlevel of the init system.\n\n"
5653 " --help Show this help\n",
5654 program_invocation_short_name);
5655
5656 return 0;
5657}
5658
45c0c61d
ZJS
5659static int help_types(void) {
5660 int i;
830f01f0 5661 const char *t;
45c0c61d
ZJS
5662
5663 puts("Available unit types:");
f168c273 5664 for (i = 0; i < _UNIT_TYPE_MAX; i++) {
830f01f0
LP
5665 t = unit_type_to_string(i);
5666 if (t)
5667 puts(t);
5668 }
45c0c61d 5669
45c0c61d
ZJS
5670 return 0;
5671}
5672
e4b61340 5673static int systemctl_parse_argv(int argc, char *argv[]) {
7e4249b9
LP
5674
5675 enum {
90d473a1 5676 ARG_FAIL = 0x100,
afba4199
ZJS
5677 ARG_REVERSE,
5678 ARG_AFTER,
5679 ARG_BEFORE,
991f2a39 5680 ARG_SHOW_TYPES,
23ade460 5681 ARG_IRREVERSIBLE,
e67c3609 5682 ARG_IGNORE_DEPENDENCIES,
35df8f27 5683 ARG_VERSION,
af2d49f7 5684 ARG_USER,
7e4249b9 5685 ARG_SYSTEM,
ee5762e3 5686 ARG_GLOBAL,
6e905d93 5687 ARG_NO_BLOCK,
ebed32bf 5688 ARG_NO_LEGEND,
611efaac 5689 ARG_NO_PAGER,
4445a875 5690 ARG_NO_WALL,
be394c48 5691 ARG_ROOT,
ee5762e3 5692 ARG_NO_RELOAD,
501fc174 5693 ARG_KILL_WHO,
30732560 5694 ARG_NO_ASK_PASSWORD,
729e3769 5695 ARG_FAILED,
df50185b 5696 ARG_RUNTIME,
5d0c05e5 5697 ARG_FORCE,
9b9b3d36 5698 ARG_PLAIN,
4dc5b821 5699 ARG_STATE,
d309c1c3
LP
5700 ARG_JOB_MODE,
5701 ARG_PRESET_MODE,
7e4249b9
LP
5702 };
5703
5704 static const struct option options[] = {
9ea9d4cf
LP
5705 { "help", no_argument, NULL, 'h' },
5706 { "version", no_argument, NULL, ARG_VERSION },
5707 { "type", required_argument, NULL, 't' },
5708 { "property", required_argument, NULL, 'p' },
5709 { "all", no_argument, NULL, 'a' },
5710 { "reverse", no_argument, NULL, ARG_REVERSE },
5711 { "after", no_argument, NULL, ARG_AFTER },
5712 { "before", no_argument, NULL, ARG_BEFORE },
5713 { "show-types", no_argument, NULL, ARG_SHOW_TYPES },
5714 { "failed", no_argument, NULL, ARG_FAILED }, /* compatibility only */
5715 { "full", no_argument, NULL, 'l' },
4dc5b821
LP
5716 { "job-mode", required_argument, NULL, ARG_JOB_MODE },
5717 { "fail", no_argument, NULL, ARG_FAIL }, /* compatibility only */
5718 { "irreversible", no_argument, NULL, ARG_IRREVERSIBLE }, /* compatibility only */
5719 { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES }, /* compatibility only */
9ea9d4cf
LP
5720 { "ignore-inhibitors", no_argument, NULL, 'i' },
5721 { "user", no_argument, NULL, ARG_USER },
5722 { "system", no_argument, NULL, ARG_SYSTEM },
5723 { "global", no_argument, NULL, ARG_GLOBAL },
5724 { "no-block", no_argument, NULL, ARG_NO_BLOCK },
5725 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
5726 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
5727 { "no-wall", no_argument, NULL, ARG_NO_WALL },
5728 { "quiet", no_argument, NULL, 'q' },
5729 { "root", required_argument, NULL, ARG_ROOT },
5730 { "force", no_argument, NULL, ARG_FORCE },
5731 { "no-reload", no_argument, NULL, ARG_NO_RELOAD },
5732 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
5733 { "signal", required_argument, NULL, 's' },
5734 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
5735 { "host", required_argument, NULL, 'H' },
f459b602 5736 { "machine", required_argument, NULL, 'M' },
9ea9d4cf
LP
5737 { "runtime", no_argument, NULL, ARG_RUNTIME },
5738 { "lines", required_argument, NULL, 'n' },
5739 { "output", required_argument, NULL, 'o' },
5740 { "plain", no_argument, NULL, ARG_PLAIN },
5741 { "state", required_argument, NULL, ARG_STATE },
1238ee09 5742 { "recursive", no_argument, NULL, 'r' },
d309c1c3 5743 { "preset-mode", required_argument, NULL, ARG_PRESET_MODE },
eb9da376 5744 {}
7e4249b9
LP
5745 };
5746
5747 int c;
5748
e4b61340 5749 assert(argc >= 0);
7e4249b9
LP
5750 assert(argv);
5751
1238ee09 5752 while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:ir", options, NULL)) >= 0) {
7e4249b9
LP
5753
5754 switch (c) {
5755
5756 case 'h':
eb9da376 5757 return systemctl_help();
35df8f27
LP
5758
5759 case ARG_VERSION:
5760 puts(PACKAGE_STRING);
7d568925 5761 puts(SYSTEMD_FEATURES);
35df8f27 5762 return 0;
7e4249b9 5763
20b3f379
ZJS
5764 case 't': {
5765 char *word, *state;
5766 size_t size;
45c0c61d 5767
20b3f379 5768 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
7fd1b19b 5769 _cleanup_free_ char *type;
20b3f379
ZJS
5770
5771 type = strndup(word, size);
5772 if (!type)
5773 return -ENOMEM;
5774
5775 if (streq(type, "help")) {
5776 help_types();
5777 return 0;
5778 }
5779
5780 if (unit_type_from_string(type) >= 0) {
5781 if (strv_push(&arg_types, type))
5782 return log_oom();
5783 type = NULL;
5784 continue;
5785 }
5786
9b9b3d36
MW
5787 /* It's much nicer to use --state= for
5788 * load states, but let's support this
5789 * in --types= too for compatibility
5790 * with old versions */
20b3f379 5791 if (unit_load_state_from_string(optarg) >= 0) {
9b9b3d36 5792 if (strv_push(&arg_states, type) < 0)
20b3f379
ZJS
5793 return log_oom();
5794 type = NULL;
5795 continue;
5796 }
5797
ab06eef8 5798 log_error("Unknown unit type or load state '%s'.", type);
20b3f379
ZJS
5799 log_info("Use -t help to see a list of allowed values.");
5800 return -EINVAL;
c147dc42 5801 }
20b3f379
ZJS
5802
5803 break;
5804 }
5805
ea4a240d 5806 case 'p': {
033a842c
ZJS
5807 /* Make sure that if the empty property list
5808 was specified, we won't show any properties. */
20b3f379 5809 if (isempty(optarg) && !arg_properties) {
cbc9fbd1 5810 arg_properties = new0(char*, 1);
20b3f379
ZJS
5811 if (!arg_properties)
5812 return log_oom();
5813 } else {
5814 char *word, *state;
5815 size_t size;
033a842c 5816
20b3f379
ZJS
5817 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
5818 char *prop;
033a842c 5819
20b3f379
ZJS
5820 prop = strndup(word, size);
5821 if (!prop)
5822 return log_oom();
ea4a240d 5823
6e18964d 5824 if (strv_consume(&arg_properties, prop) < 0)
20b3f379 5825 return log_oom();
20b3f379 5826 }
033a842c 5827 }
48220598
LP
5828
5829 /* If the user asked for a particular
5830 * property, show it to him, even if it is
5831 * empty. */
5832 arg_all = true;
033a842c 5833
48220598 5834 break;
ea4a240d 5835 }
48220598 5836
7e4249b9
LP
5837 case 'a':
5838 arg_all = true;
5839 break;
5840
afba4199
ZJS
5841 case ARG_REVERSE:
5842 arg_dependency = DEPENDENCY_REVERSE;
5843 break;
5844
5845 case ARG_AFTER:
5846 arg_dependency = DEPENDENCY_AFTER;
5847 break;
5848
5849 case ARG_BEFORE:
5850 arg_dependency = DEPENDENCY_BEFORE;
5851 break;
5852
991f2a39
ZJS
5853 case ARG_SHOW_TYPES:
5854 arg_show_types = true;
5855 break;
5856
4dc5b821
LP
5857 case ARG_JOB_MODE:
5858 arg_job_mode = optarg;
5859 break;
5860
90d473a1 5861 case ARG_FAIL:
e67c3609
LP
5862 arg_job_mode = "fail";
5863 break;
5864
23ade460
MS
5865 case ARG_IRREVERSIBLE:
5866 arg_job_mode = "replace-irreversibly";
5867 break;
5868
e67c3609
LP
5869 case ARG_IGNORE_DEPENDENCIES:
5870 arg_job_mode = "ignore-dependencies";
7e4249b9
LP
5871 break;
5872
af2d49f7 5873 case ARG_USER:
729e3769 5874 arg_scope = UNIT_FILE_USER;
7e4249b9
LP
5875 break;
5876
5877 case ARG_SYSTEM:
729e3769
LP
5878 arg_scope = UNIT_FILE_SYSTEM;
5879 break;
5880
5881 case ARG_GLOBAL:
5882 arg_scope = UNIT_FILE_GLOBAL;
7e4249b9
LP
5883 break;
5884
6e905d93
LP
5885 case ARG_NO_BLOCK:
5886 arg_no_block = true;
7e4249b9
LP
5887 break;
5888
ebed32bf
MS
5889 case ARG_NO_LEGEND:
5890 arg_no_legend = true;
5891 break;
5892
611efaac
LP
5893 case ARG_NO_PAGER:
5894 arg_no_pager = true;
5895 break;
0736af98 5896
514f4ef5
LP
5897 case ARG_NO_WALL:
5898 arg_no_wall = true;
5899 break;
5900
be394c48
FC
5901 case ARG_ROOT:
5902 arg_root = optarg;
5903 break;
5904
98a6e132 5905 case 'l':
8fe914ec
LP
5906 arg_full = true;
5907 break;
5908
30732560 5909 case ARG_FAILED:
9b9b3d36
MW
5910 if (strv_extend(&arg_states, "failed") < 0)
5911 return log_oom();
5912
30732560
LP
5913 break;
5914
0183528f
LP
5915 case 'q':
5916 arg_quiet = true;
5917 break;
5918
568b679f
LP
5919 case ARG_FORCE:
5920 arg_force ++;
5921 break;
5922
b4f27ccc 5923 case 'f':
e606bb61 5924 arg_force ++;
ee5762e3
LP
5925 break;
5926
5927 case ARG_NO_RELOAD:
5928 arg_no_reload = true;
5929 break;
5930
8a0867d6
LP
5931 case ARG_KILL_WHO:
5932 arg_kill_who = optarg;
5933 break;
5934
8a0867d6
LP
5935 case 's':
5936 if ((arg_signal = signal_from_string_try_harder(optarg)) < 0) {
5937 log_error("Failed to parse signal string %s.", optarg);
5938 return -EINVAL;
5939 }
5940 break;
5941
501fc174
LP
5942 case ARG_NO_ASK_PASSWORD:
5943 arg_ask_password = false;
5944 break;
5945
f459b602
MAP
5946 case 'H':
5947 arg_transport = BUS_TRANSPORT_REMOTE;
5948 arg_host = optarg;
a8f11321
LP
5949 break;
5950
f459b602
MAP
5951 case 'M':
5952 arg_transport = BUS_TRANSPORT_CONTAINER;
5953 arg_host = optarg;
a8f11321
LP
5954 break;
5955
729e3769
LP
5956 case ARG_RUNTIME:
5957 arg_runtime = true;
5958 break;
5959
df50185b
LP
5960 case 'n':
5961 if (safe_atou(optarg, &arg_lines) < 0) {
5962 log_error("Failed to parse lines '%s'", optarg);
5963 return -EINVAL;
5964 }
5965 break;
5966
df50185b
LP
5967 case 'o':
5968 arg_output = output_mode_from_string(optarg);
5969 if (arg_output < 0) {
5970 log_error("Unknown output '%s'.", optarg);
5971 return -EINVAL;
5972 }
5973 break;
5974
b37844d3
LP
5975 case 'i':
5976 arg_ignore_inhibitors = true;
5977 break;
5978
5d0c05e5
LN
5979 case ARG_PLAIN:
5980 arg_plain = true;
5981 break;
5982
9b9b3d36
MW
5983 case ARG_STATE: {
5984 char *word, *state;
5985 size_t size;
5986
5987 FOREACH_WORD_SEPARATOR(word, size, optarg, ",", state) {
5988 char *s;
5989
5990 s = strndup(word, size);
5991 if (!s)
5992 return log_oom();
5993
6e18964d 5994 if (strv_consume(&arg_states, s) < 0)
9b9b3d36 5995 return log_oom();
9b9b3d36
MW
5996 }
5997 break;
5998 }
5999
1238ee09
LP
6000 case 'r':
6001 if (geteuid() != 0) {
f1721625 6002 log_error("--recursive requires root privileges.");
1238ee09
LP
6003 return -EPERM;
6004 }
6005
6006 arg_recursive = true;
6007 break;
6008
d309c1c3
LP
6009 case ARG_PRESET_MODE:
6010
6011 arg_preset_mode = unit_file_preset_mode_from_string(optarg);
6012 if (arg_preset_mode < 0) {
6013 log_error("Failed to parse preset mode: %s.", optarg);
6014 return -EINVAL;
6015 }
6016
6017 break;
6018
7e4249b9
LP
6019 case '?':
6020 return -EINVAL;
6021
6022 default:
eb9da376 6023 assert_not_reached("Unhandled option");
7e4249b9
LP
6024 }
6025 }
6026
f459b602 6027 if (arg_transport != BUS_TRANSPORT_LOCAL && arg_scope != UNIT_FILE_SYSTEM) {
a8f11321
LP
6028 log_error("Cannot access user instance remotely.");
6029 return -EINVAL;
6030 }
6031
7e4249b9
LP
6032 return 1;
6033}
6034
e4b61340
LP
6035static int halt_parse_argv(int argc, char *argv[]) {
6036
6037 enum {
6038 ARG_HELP = 0x100,
6039 ARG_HALT,
514f4ef5
LP
6040 ARG_REBOOT,
6041 ARG_NO_WALL
e4b61340
LP
6042 };
6043
6044 static const struct option options[] = {
6045 { "help", no_argument, NULL, ARG_HELP },
6046 { "halt", no_argument, NULL, ARG_HALT },
6047 { "poweroff", no_argument, NULL, 'p' },
6048 { "reboot", no_argument, NULL, ARG_REBOOT },
6049 { "force", no_argument, NULL, 'f' },
6050 { "wtmp-only", no_argument, NULL, 'w' },
6051 { "no-wtmp", no_argument, NULL, 'd' },
514f4ef5 6052 { "no-wall", no_argument, NULL, ARG_NO_WALL },
eb9da376 6053 {}
e4b61340
LP
6054 };
6055
37185ec8 6056 int c, r, runlevel;
e4b61340
LP
6057
6058 assert(argc >= 0);
6059 assert(argv);
6060
6061 if (utmp_get_runlevel(&runlevel, NULL) >= 0)
6062 if (runlevel == '0' || runlevel == '6')
65491fd8 6063 arg_force = 2;
e4b61340
LP
6064
6065 while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) {
6066 switch (c) {
6067
6068 case ARG_HELP:
eb9da376 6069 return halt_help();
e4b61340
LP
6070
6071 case ARG_HALT:
6072 arg_action = ACTION_HALT;
6073 break;
6074
6075 case 'p':
a042efad
MS
6076 if (arg_action != ACTION_REBOOT)
6077 arg_action = ACTION_POWEROFF;
e4b61340
LP
6078 break;
6079
6080 case ARG_REBOOT:
6081 arg_action = ACTION_REBOOT;
6082 break;
6083
6084 case 'f':
65491fd8 6085 arg_force = 2;
e4b61340
LP
6086 break;
6087
6088 case 'w':
6089 arg_dry = true;
6090 break;
6091
6092 case 'd':
6093 arg_no_wtmp = true;
6094 break;
6095
514f4ef5
LP
6096 case ARG_NO_WALL:
6097 arg_no_wall = true;
6098 break;
6099
e4b61340
LP
6100 case 'i':
6101 case 'h':
57371e58 6102 case 'n':
e4b61340
LP
6103 /* Compatibility nops */
6104 break;
6105
6106 case '?':
6107 return -EINVAL;
6108
6109 default:
eb9da376 6110 assert_not_reached("Unhandled option");
e4b61340
LP
6111 }
6112 }
6113
c5220a94
MO
6114 if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) {
6115 r = update_reboot_param_file(argc == optind + 1 ? argv[optind] : NULL);
6116 if (r < 0)
37185ec8 6117 return r;
37185ec8 6118 } else if (optind < argc) {
e4b61340
LP
6119 log_error("Too many arguments.");
6120 return -EINVAL;
6121 }
6122
6123 return 1;
6124}
6125
f6144808
LP
6126static int parse_time_spec(const char *t, usec_t *_u) {
6127 assert(t);
6128 assert(_u);
6129
6130 if (streq(t, "now"))
6131 *_u = 0;
1a639877 6132 else if (!strchr(t, ':')) {
f6144808
LP
6133 uint64_t u;
6134
1a639877 6135 if (safe_atou64(t, &u) < 0)
f6144808
LP
6136 return -EINVAL;
6137
6138 *_u = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u;
6139 } else {
6140 char *e = NULL;
6141 long hour, minute;
b92bea5d 6142 struct tm tm = {};
f6144808
LP
6143 time_t s;
6144 usec_t n;
6145
6146 errno = 0;
6147 hour = strtol(t, &e, 10);
8333c77e 6148 if (errno > 0 || *e != ':' || hour < 0 || hour > 23)
f6144808
LP
6149 return -EINVAL;
6150
6151 minute = strtol(e+1, &e, 10);
8333c77e 6152 if (errno > 0 || *e != 0 || minute < 0 || minute > 59)
f6144808
LP
6153 return -EINVAL;
6154
6155 n = now(CLOCK_REALTIME);
08e4b1c5
LP
6156 s = (time_t) (n / USEC_PER_SEC);
6157
f6144808
LP
6158 assert_se(localtime_r(&s, &tm));
6159
6160 tm.tm_hour = (int) hour;
6161 tm.tm_min = (int) minute;
08e4b1c5 6162 tm.tm_sec = 0;
f6144808
LP
6163
6164 assert_se(s = mktime(&tm));
6165
6166 *_u = (usec_t) s * USEC_PER_SEC;
6167
6168 while (*_u <= n)
6169 *_u += USEC_PER_DAY;
6170 }
6171
6172 return 0;
6173}
6174
e4b61340
LP
6175static int shutdown_parse_argv(int argc, char *argv[]) {
6176
6177 enum {
6178 ARG_HELP = 0x100,
514f4ef5 6179 ARG_NO_WALL
e4b61340
LP
6180 };
6181
6182 static const struct option options[] = {
6183 { "help", no_argument, NULL, ARG_HELP },
6184 { "halt", no_argument, NULL, 'H' },
6185 { "poweroff", no_argument, NULL, 'P' },
6186 { "reboot", no_argument, NULL, 'r' },
04ebb595 6187 { "kexec", no_argument, NULL, 'K' }, /* not documented extension */
514f4ef5 6188 { "no-wall", no_argument, NULL, ARG_NO_WALL },
eb9da376 6189 {}
e4b61340
LP
6190 };
6191
f6144808 6192 int c, r;
e4b61340
LP
6193
6194 assert(argc >= 0);
6195 assert(argv);
6196
f6144808 6197 while ((c = getopt_long(argc, argv, "HPrhkt:afFc", options, NULL)) >= 0) {
e4b61340
LP
6198 switch (c) {
6199
6200 case ARG_HELP:
eb9da376 6201 return shutdown_help();
e4b61340
LP
6202
6203 case 'H':
6204 arg_action = ACTION_HALT;
6205 break;
6206
6207 case 'P':
6208 arg_action = ACTION_POWEROFF;
6209 break;
6210
6211 case 'r':
5622dde3
KS
6212 if (kexec_loaded())
6213 arg_action = ACTION_KEXEC;
6214 else
6215 arg_action = ACTION_REBOOT;
e4b61340
LP
6216 break;
6217
04ebb595
LP
6218 case 'K':
6219 arg_action = ACTION_KEXEC;
6220 break;
6221
e4b61340
LP
6222 case 'h':
6223 if (arg_action != ACTION_HALT)
6224 arg_action = ACTION_POWEROFF;
6225 break;
6226
6227 case 'k':
6228 arg_dry = true;
6229 break;
6230
514f4ef5
LP
6231 case ARG_NO_WALL:
6232 arg_no_wall = true;
6233 break;
6234
e4b61340
LP
6235 case 't':
6236 case 'a':
6237 /* Compatibility nops */
6238 break;
6239
f6144808
LP
6240 case 'c':
6241 arg_action = ACTION_CANCEL_SHUTDOWN;
6242 break;
6243
e4b61340
LP
6244 case '?':
6245 return -EINVAL;
6246
6247 default:
eb9da376 6248 assert_not_reached("Unhandled option");
e4b61340
LP
6249 }
6250 }
6251
dfcc5c33 6252 if (argc > optind && arg_action != ACTION_CANCEL_SHUTDOWN) {
7e59bfcb
LP
6253 r = parse_time_spec(argv[optind], &arg_when);
6254 if (r < 0) {
f6144808
LP
6255 log_error("Failed to parse time specification: %s", argv[optind]);
6256 return r;
6257 }
6b5ad000 6258 } else
08e4b1c5 6259 arg_when = now(CLOCK_REALTIME) + USEC_PER_MINUTE;
442b9094 6260
dfcc5c33
MS
6261 if (argc > optind && arg_action == ACTION_CANCEL_SHUTDOWN)
6262 /* No time argument for shutdown cancel */
6263 arg_wall = argv + optind;
6264 else if (argc > optind + 1)
6265 /* We skip the time argument */
e4b61340
LP
6266 arg_wall = argv + optind + 1;
6267
6268 optind = argc;
6269
6270 return 1;
e4b61340
LP
6271}
6272
6273static int telinit_parse_argv(int argc, char *argv[]) {
6274
6275 enum {
6276 ARG_HELP = 0x100,
514f4ef5 6277 ARG_NO_WALL
e4b61340
LP
6278 };
6279
6280 static const struct option options[] = {
6281 { "help", no_argument, NULL, ARG_HELP },
514f4ef5 6282 { "no-wall", no_argument, NULL, ARG_NO_WALL },
eb9da376 6283 {}
e4b61340
LP
6284 };
6285
6286 static const struct {
6287 char from;
6288 enum action to;
6289 } table[] = {
6290 { '0', ACTION_POWEROFF },
6291 { '6', ACTION_REBOOT },
ef2f1067 6292 { '1', ACTION_RESCUE },
e4b61340
LP
6293 { '2', ACTION_RUNLEVEL2 },
6294 { '3', ACTION_RUNLEVEL3 },
6295 { '4', ACTION_RUNLEVEL4 },
6296 { '5', ACTION_RUNLEVEL5 },
6297 { 's', ACTION_RESCUE },
6298 { 'S', ACTION_RESCUE },
6299 { 'q', ACTION_RELOAD },
6300 { 'Q', ACTION_RELOAD },
6301 { 'u', ACTION_REEXEC },
6302 { 'U', ACTION_REEXEC }
6303 };
6304
6305 unsigned i;
6306 int c;
6307
6308 assert(argc >= 0);
6309 assert(argv);
6310
6311 while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
6312 switch (c) {
6313
6314 case ARG_HELP:
eb9da376 6315 return telinit_help();
e4b61340 6316
514f4ef5
LP
6317 case ARG_NO_WALL:
6318 arg_no_wall = true;
6319 break;
6320
e4b61340
LP
6321 case '?':
6322 return -EINVAL;
6323
6324 default:
eb9da376 6325 assert_not_reached("Unhandled option");
e4b61340
LP
6326 }
6327 }
6328
6329 if (optind >= argc) {
2f02ce40 6330 telinit_help();
e4b61340
LP
6331 return -EINVAL;
6332 }
6333
6334 if (optind + 1 < argc) {
6335 log_error("Too many arguments.");
6336 return -EINVAL;
6337 }
6338
6339 if (strlen(argv[optind]) != 1) {
6340 log_error("Expected single character argument.");
6341 return -EINVAL;
6342 }
6343
6344 for (i = 0; i < ELEMENTSOF(table); i++)
6345 if (table[i].from == argv[optind][0])
6346 break;
6347
6348 if (i >= ELEMENTSOF(table)) {
b0193f1c 6349 log_error("Unknown command '%s'.", argv[optind]);
e4b61340
LP
6350 return -EINVAL;
6351 }
6352
6353 arg_action = table[i].to;
6354
6355 optind ++;
6356
6357 return 1;
6358}
6359
6360static int runlevel_parse_argv(int argc, char *argv[]) {
6361
6362 enum {
6363 ARG_HELP = 0x100,
6364 };
6365
6366 static const struct option options[] = {
6367 { "help", no_argument, NULL, ARG_HELP },
eb9da376 6368 {}
e4b61340
LP
6369 };
6370
6371 int c;
6372
6373 assert(argc >= 0);
6374 assert(argv);
6375
6376 while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
6377 switch (c) {
6378
6379 case ARG_HELP:
eb9da376 6380 return runlevel_help();
e4b61340
LP
6381
6382 case '?':
6383 return -EINVAL;
6384
6385 default:
eb9da376 6386 assert_not_reached("Unhandled option");
e4b61340
LP
6387 }
6388 }
6389
6390 if (optind < argc) {
6391 log_error("Too many arguments.");
6392 return -EINVAL;
6393 }
6394
6395 return 1;
6396}
6397
6398static int parse_argv(int argc, char *argv[]) {
6399 assert(argc >= 0);
6400 assert(argv);
6401
6402 if (program_invocation_short_name) {
6403
6404 if (strstr(program_invocation_short_name, "halt")) {
6405 arg_action = ACTION_HALT;
6406 return halt_parse_argv(argc, argv);
6407 } else if (strstr(program_invocation_short_name, "poweroff")) {
6408 arg_action = ACTION_POWEROFF;
6409 return halt_parse_argv(argc, argv);
6410 } else if (strstr(program_invocation_short_name, "reboot")) {
5622dde3
KS
6411 if (kexec_loaded())
6412 arg_action = ACTION_KEXEC;
6413 else
6414 arg_action = ACTION_REBOOT;
e4b61340
LP
6415 return halt_parse_argv(argc, argv);
6416 } else if (strstr(program_invocation_short_name, "shutdown")) {
6417 arg_action = ACTION_POWEROFF;
6418 return shutdown_parse_argv(argc, argv);
6419 } else if (strstr(program_invocation_short_name, "init")) {
d5ca5f11
LP
6420
6421 if (sd_booted() > 0) {
f459b602 6422 arg_action = _ACTION_INVALID;
d5ca5f11
LP
6423 return telinit_parse_argv(argc, argv);
6424 } else {
6425 /* Hmm, so some other init system is
6426 * running, we need to forward this
6427 * request to it. For now we simply
6428 * guess that it is Upstart. */
6429
4ad61fd1 6430 execv(TELINIT, argv);
d5ca5f11
LP
6431
6432 log_error("Couldn't find an alternative telinit implementation to spawn.");
6433 return -EIO;
6434 }
6435
e4b61340
LP
6436 } else if (strstr(program_invocation_short_name, "runlevel")) {
6437 arg_action = ACTION_RUNLEVEL;
6438 return runlevel_parse_argv(argc, argv);
6439 }
6440 }
6441
6442 arg_action = ACTION_SYSTEMCTL;
6443 return systemctl_parse_argv(argc, argv);
6444}
6445
44a6b1b6 6446_pure_ static int action_to_runlevel(void) {
eb22ac37
LP
6447
6448 static const char table[_ACTION_MAX] = {
6449 [ACTION_HALT] = '0',
6450 [ACTION_POWEROFF] = '0',
6451 [ACTION_REBOOT] = '6',
6452 [ACTION_RUNLEVEL2] = '2',
6453 [ACTION_RUNLEVEL3] = '3',
6454 [ACTION_RUNLEVEL4] = '4',
6455 [ACTION_RUNLEVEL5] = '5',
6456 [ACTION_RESCUE] = '1'
6457 };
6458
d55ae9e6
LP
6459 assert(arg_action < _ACTION_MAX);
6460
6461 return table[arg_action];
6462}
6463
d55ae9e6 6464static int talk_initctl(void) {
cbc9fbd1
LP
6465
6466 struct init_request request = {
6467 .magic = INIT_MAGIC,
6468 .sleeptime = 0,
6469 .cmd = INIT_CMD_RUNLVL
6470 };
6471
7fd1b19b 6472 _cleanup_close_ int fd = -1;
d55ae9e6 6473 char rl;
cbc9fbd1 6474 int r;
eb22ac37 6475
427b47c4
ZJS
6476 rl = action_to_runlevel();
6477 if (!rl)
eb22ac37
LP
6478 return 0;
6479
d55ae9e6
LP
6480 request.runlevel = rl;
6481
427b47c4
ZJS
6482 fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY);
6483 if (fd < 0) {
d55ae9e6
LP
6484 if (errno == ENOENT)
6485 return 0;
eb22ac37 6486
d55ae9e6 6487 log_error("Failed to open "INIT_FIFO": %m");
eb22ac37 6488 return -errno;
d55ae9e6 6489 }
eb22ac37 6490
d55ae9e6 6491 errno = 0;
eb22ac37 6492 r = loop_write(fd, &request, sizeof(request), false) != sizeof(request);
427b47c4 6493 if (r) {
d55ae9e6 6494 log_error("Failed to write to "INIT_FIFO": %m");
bcb161b0 6495 return errno > 0 ? -errno : -EIO;
d55ae9e6 6496 }
eb22ac37
LP
6497
6498 return 1;
e4b61340
LP
6499}
6500
41dd15e4 6501static int systemctl_main(sd_bus *bus, int argc, char *argv[], int bus_error) {
7e4249b9 6502
7e4249b9
LP
6503 static const struct {
6504 const char* verb;
6505 const enum {
6506 MORE,
6507 LESS,
6508 EQUAL
6509 } argc_cmp;
6510 const int argc;
f459b602 6511 int (* const dispatch)(sd_bus *bus, char **args);
d08e75ed
ZJS
6512 const enum {
6513 NOBUS = 1,
6514 FORCE,
6515 } bus;
7e4249b9 6516 } verbs[] = {
d8fba7c6 6517 { "list-units", MORE, 0, list_units },
d08e75ed 6518 { "list-unit-files", MORE, 1, list_unit_files, NOBUS },
d8fba7c6
ZJS
6519 { "list-sockets", MORE, 1, list_sockets },
6520 { "list-timers", MORE, 1, list_timers },
6521 { "list-jobs", MORE, 1, list_jobs },
0d292f5e 6522 { "list-machines", MORE, 1, list_machines },
ee5762e3 6523 { "clear-jobs", EQUAL, 1, daemon_reload },
ee5762e3
LP
6524 { "cancel", MORE, 2, cancel_job },
6525 { "start", MORE, 2, start_unit },
6526 { "stop", MORE, 2, start_unit },
a76f7be2 6527 { "condstop", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
ee5762e3
LP
6528 { "reload", MORE, 2, start_unit },
6529 { "restart", MORE, 2, start_unit },
6530 { "try-restart", MORE, 2, start_unit },
6531 { "reload-or-restart", MORE, 2, start_unit },
6532 { "reload-or-try-restart", MORE, 2, start_unit },
6533 { "force-reload", MORE, 2, start_unit }, /* For compatibility with SysV */
64e5f1b7 6534 { "condreload", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
ee5762e3
LP
6535 { "condrestart", MORE, 2, start_unit }, /* For compatibility with RH */
6536 { "isolate", EQUAL, 2, start_unit },
8a0867d6 6537 { "kill", MORE, 2, kill_unit },
1a0fce45
TA
6538 { "is-active", MORE, 2, check_unit_active },
6539 { "check", MORE, 2, check_unit_active },
6540 { "is-failed", MORE, 2, check_unit_failed },
ee5762e3 6541 { "show", MORE, 1, show },
e93c33d4 6542 { "cat", MORE, 2, cat },
265a7a2a 6543 { "status", MORE, 1, show },
b43f208f 6544 { "help", MORE, 2, show },
ee5762e3
LP
6545 { "snapshot", LESS, 2, snapshot },
6546 { "delete", MORE, 2, delete_snapshot },
6547 { "daemon-reload", EQUAL, 1, daemon_reload },
6548 { "daemon-reexec", EQUAL, 1, daemon_reload },
f459b602 6549 { "show-environment", EQUAL, 1, show_environment },
ee5762e3
LP
6550 { "set-environment", MORE, 2, set_environment },
6551 { "unset-environment", MORE, 2, set_environment },
ac3efa8a 6552 { "import-environment", MORE, 1, import_environment},
d08e75ed
ZJS
6553 { "halt", EQUAL, 1, start_special, FORCE },
6554 { "poweroff", EQUAL, 1, start_special, FORCE },
6555 { "reboot", EQUAL, 1, start_special, FORCE },
20b09ca7 6556 { "kexec", EQUAL, 1, start_special },
6edd7d0a
LP
6557 { "suspend", EQUAL, 1, start_special },
6558 { "hibernate", EQUAL, 1, start_special },
6524990f 6559 { "hybrid-sleep", EQUAL, 1, start_special },
ee5762e3
LP
6560 { "default", EQUAL, 1, start_special },
6561 { "rescue", EQUAL, 1, start_special },
6562 { "emergency", EQUAL, 1, start_special },
20b09ca7 6563 { "exit", EQUAL, 1, start_special },
fdf20a31 6564 { "reset-failed", MORE, 1, reset_failed },
d08e75ed
ZJS
6565 { "enable", MORE, 2, enable_unit, NOBUS },
6566 { "disable", MORE, 2, enable_unit, NOBUS },
6567 { "is-enabled", MORE, 2, unit_is_enabled, NOBUS },
6568 { "reenable", MORE, 2, enable_unit, NOBUS },
6569 { "preset", MORE, 2, enable_unit, NOBUS },
d309c1c3 6570 { "preset-all", EQUAL, 1, preset_all, NOBUS },
d08e75ed
ZJS
6571 { "mask", MORE, 2, enable_unit, NOBUS },
6572 { "unmask", MORE, 2, enable_unit, NOBUS },
6573 { "link", MORE, 2, enable_unit, NOBUS },
957eb8ca 6574 { "switch-root", MORE, 2, switch_root },
e31165b2 6575 { "list-dependencies", LESS, 2, list_dependencies },
d08e75ed
ZJS
6576 { "set-default", EQUAL, 2, set_default, NOBUS },
6577 { "get-default", EQUAL, 1, get_default, NOBUS },
8e2af478 6578 { "set-property", MORE, 3, set_property },
d08e75ed
ZJS
6579 {}
6580 }, *verb = verbs;
7e4249b9 6581
e4b61340 6582 int left;
7e4249b9 6583
e4b61340
LP
6584 assert(argc >= 0);
6585 assert(argv);
7e4249b9
LP
6586
6587 left = argc - optind;
6588
d08e75ed
ZJS
6589 /* Special rule: no arguments (left == 0) means "list-units" */
6590 if (left > 0) {
b43f208f
KS
6591 if (streq(argv[optind], "help") && !argv[optind+1]) {
6592 log_error("This command expects one or more "
6593 "unit names. Did you mean --help?");
6594 return -EINVAL;
0183528f
LP
6595 }
6596
d08e75ed
ZJS
6597 for (; verb->verb; verb++)
6598 if (streq(argv[optind], verb->verb))
6599 goto found;
7e4249b9 6600
d08e75ed
ZJS
6601 log_error("Unknown operation '%s'.", argv[optind]);
6602 return -EINVAL;
7e4249b9 6603 }
d08e75ed 6604found:
7e4249b9 6605
d08e75ed 6606 switch (verb->argc_cmp) {
7e4249b9
LP
6607
6608 case EQUAL:
d08e75ed 6609 if (left != verb->argc) {
7e4249b9 6610 log_error("Invalid number of arguments.");
e4b61340 6611 return -EINVAL;
7e4249b9
LP
6612 }
6613
6614 break;
6615
6616 case MORE:
d08e75ed 6617 if (left < verb->argc) {
7e4249b9 6618 log_error("Too few arguments.");
e4b61340 6619 return -EINVAL;
7e4249b9
LP
6620 }
6621
6622 break;
6623
6624 case LESS:
d08e75ed 6625 if (left > verb->argc) {
7e4249b9 6626 log_error("Too many arguments.");
e4b61340 6627 return -EINVAL;
7e4249b9
LP
6628 }
6629
6630 break;
6631
6632 default:
6633 assert_not_reached("Unknown comparison operator.");
6634 }
6635
ee5762e3
LP
6636 /* Require a bus connection for all operations but
6637 * enable/disable */
d08e75ed
ZJS
6638 if (verb->bus == NOBUS) {
6639 if (!bus && !avoid_bus()) {
6640 log_error("Failed to get D-Bus connection: %s", strerror(-bus_error));
6641 return -EIO;
6642 }
82e23ddd 6643
d08e75ed 6644 } else {
82e23ddd
LP
6645 if (running_in_chroot() > 0) {
6646 log_info("Running in chroot, ignoring request.");
6647 return 0;
6648 }
6649
d08e75ed
ZJS
6650 if ((verb->bus != FORCE || arg_force <= 0) && !bus) {
6651 log_error("Failed to get D-Bus connection: %s", strerror(-bus_error));
82e23ddd
LP
6652 return -EIO;
6653 }
ee5762e3
LP
6654 }
6655
d08e75ed 6656 return verb->dispatch(bus, argv + optind);
e4b61340
LP
6657}
6658
52c00215 6659static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
cbc9fbd1 6660
b92bea5d
ZJS
6661 struct sd_shutdown_command c = {
6662 .usec = t,
6663 .mode = mode,
6664 .dry_run = dry_run,
6665 .warn_wall = warn,
6666 };
cbc9fbd1 6667
b92bea5d
ZJS
6668 union sockaddr_union sockaddr = {
6669 .un.sun_family = AF_UNIX,
6670 .un.sun_path = "/run/systemd/shutdownd",
6671 };
cbc9fbd1
LP
6672
6673 struct iovec iovec[2] = {{
6674 .iov_base = (char*) &c,
b92bea5d 6675 .iov_len = offsetof(struct sd_shutdown_command, wall_message),
cbc9fbd1
LP
6676 }};
6677
b92bea5d
ZJS
6678 struct msghdr msghdr = {
6679 .msg_name = &sockaddr,
6680 .msg_namelen = offsetof(struct sockaddr_un, sun_path)
f8294e41 6681 + strlen("/run/systemd/shutdownd"),
b92bea5d
ZJS
6682 .msg_iov = iovec,
6683 .msg_iovlen = 1,
6684 };
04ebb595 6685
cbc9fbd1
LP
6686 _cleanup_close_ int fd;
6687
04ebb595
LP
6688 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
6689 if (fd < 0)
6690 return -errno;
f6144808 6691
b92bea5d 6692 if (!isempty(message)) {
04ebb595
LP
6693 iovec[1].iov_base = (char*) message;
6694 iovec[1].iov_len = strlen(message);
b92bea5d 6695 msghdr.msg_iovlen++;
04ebb595 6696 }
f6144808 6697
cec7eda5 6698 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0)
f6144808 6699 return -errno;
f6144808 6700
f6144808
LP
6701 return 0;
6702}
6703
f459b602 6704static int reload_with_fallback(sd_bus *bus) {
e4b61340
LP
6705
6706 if (bus) {
6707 /* First, try systemd via D-Bus. */
d76702a7 6708 if (daemon_reload(bus, NULL) >= 0)
e4b61340
LP
6709 return 0;
6710 }
6711
6712 /* Nothing else worked, so let's try signals */
6713 assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC);
6714
6715 if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) {
6716 log_error("kill() failed: %m");
6717 return -errno;
6718 }
6719
6720 return 0;
6721}
6722
f459b602 6723static int start_with_fallback(sd_bus *bus) {
e4b61340
LP
6724
6725 if (bus) {
6726 /* First, try systemd via D-Bus. */
729e3769 6727 if (start_unit(bus, NULL) >= 0)
983d9c90 6728 goto done;
e4b61340
LP
6729 }
6730
6731 /* Nothing else worked, so let's try
6732 * /dev/initctl */
fbc43921 6733 if (talk_initctl() > 0)
983d9c90 6734 goto done;
d55ae9e6
LP
6735
6736 log_error("Failed to talk to init daemon.");
6737 return -EIO;
983d9c90
LP
6738
6739done:
6740 warn_wall(arg_action);
6741 return 0;
e4b61340
LP
6742}
6743
477def80 6744static int halt_now(enum action a) {
e606bb61 6745
477def80 6746/* Make sure C-A-D is handled by the kernel from this
e606bb61
LP
6747 * point on... */
6748 reboot(RB_ENABLE_CAD);
6749
4c80c73c 6750 switch (a) {
e606bb61
LP
6751
6752 case ACTION_HALT:
6753 log_info("Halting.");
6754 reboot(RB_HALT_SYSTEM);
477def80 6755 return -errno;
e606bb61
LP
6756
6757 case ACTION_POWEROFF:
6758 log_info("Powering off.");
6759 reboot(RB_POWER_OFF);
477def80 6760 return -errno;
e606bb61 6761
477def80
LP
6762 case ACTION_REBOOT: {
6763 _cleanup_free_ char *param = NULL;
cbc9fbd1 6764
477def80
LP
6765 if (read_one_line_file(REBOOT_PARAM_FILE, &param) >= 0) {
6766 log_info("Rebooting with argument '%s'.", param);
37185ec8
WC
6767 syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
6768 LINUX_REBOOT_CMD_RESTART2, param);
37185ec8 6769 }
e606bb61 6770
477def80
LP
6771 log_info("Rebooting.");
6772 reboot(RB_AUTOBOOT);
6773 return -errno;
e606bb61
LP
6774 }
6775
477def80
LP
6776 default:
6777 assert_not_reached("Unknown action.");
6778 }
e606bb61
LP
6779}
6780
f459b602 6781static int halt_main(sd_bus *bus) {
e4b61340
LP
6782 int r;
6783
748ebafa
LP
6784 r = check_inhibitors(bus, arg_action);
6785 if (r < 0)
6786 return r;
b37844d3 6787
bc8c2f5c 6788 if (geteuid() != 0) {
7e59bfcb
LP
6789 /* Try logind if we are a normal user and no special
6790 * mode applies. Maybe PolicyKit allows us to shutdown
6791 * the machine. */
6792
6793 if (arg_when <= 0 &&
6794 !arg_dry &&
b37844d3 6795 arg_force <= 0 &&
7e59bfcb
LP
6796 (arg_action == ACTION_POWEROFF ||
6797 arg_action == ACTION_REBOOT)) {
4c80c73c
KS
6798 r = reboot_with_logind(bus, arg_action);
6799 if (r >= 0)
6800 return r;
6801 }
6802
cc8a7a61 6803 log_error("Must be root.");
bc8c2f5c
LP
6804 return -EPERM;
6805 }
6806
f6144808 6807 if (arg_when > 0) {
7fd1b19b 6808 _cleanup_free_ char *m;
9be9828c
LP
6809
6810 m = strv_join(arg_wall, " ");
cbc9fbd1
LP
6811 if (!m)
6812 return log_oom();
6813
9be9828c
LP
6814 r = send_shutdownd(arg_when,
6815 arg_action == ACTION_HALT ? 'H' :
6816 arg_action == ACTION_POWEROFF ? 'P' :
04ebb595 6817 arg_action == ACTION_KEXEC ? 'K' :
9be9828c 6818 'r',
52c00215 6819 arg_dry,
9be9828c
LP
6820 !arg_no_wall,
6821 m);
9be9828c
LP
6822
6823 if (r < 0)
f6144808 6824 log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r));
08e4b1c5 6825 else {
7e59bfcb
LP
6826 char date[FORMAT_TIMESTAMP_MAX];
6827
08e4b1c5
LP
6828 log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.",
6829 format_timestamp(date, sizeof(date), arg_when));
f6144808 6830 return 0;
08e4b1c5 6831 }
f6144808
LP
6832 }
6833
65491fd8 6834 if (!arg_dry && !arg_force)
e4b61340
LP
6835 return start_with_fallback(bus);
6836
d90e1a30
LP
6837 if (!arg_no_wtmp) {
6838 if (sd_booted() > 0)
6839 log_debug("Not writing utmp record, assuming that systemd-update-utmp is used.");
7e59bfcb
LP
6840 else {
6841 r = utmp_put_shutdown();
6842 if (r < 0)
6843 log_warning("Failed to write utmp record: %s", strerror(-r));
6844 }
d90e1a30 6845 }
e4b61340 6846
e4b61340
LP
6847 if (arg_dry)
6848 return 0;
6849
477def80
LP
6850 r = halt_now(arg_action);
6851 log_error("Failed to reboot: %s", strerror(-r));
6852
6853 return r;
e4b61340
LP
6854}
6855
6856static int runlevel_main(void) {
6857 int r, runlevel, previous;
6858
729e3769
LP
6859 r = utmp_get_runlevel(&runlevel, &previous);
6860 if (r < 0) {
6861 puts("unknown");
e4b61340
LP
6862 return r;
6863 }
6864
6865 printf("%c %c\n",
6866 previous <= 0 ? 'N' : previous,
6867 runlevel <= 0 ? 'N' : runlevel);
6868
6869 return 0;
6870}
6871
6872int main(int argc, char*argv[]) {
f459b602
MAP
6873 _cleanup_bus_unref_ sd_bus *bus = NULL;
6874 int r;
e4b61340 6875
a9cdc94f 6876 setlocale(LC_ALL, "");
e4b61340 6877 log_parse_environment();
2396fb04 6878 log_open();
e4b61340 6879
184ecaf7
DR
6880 /* Explicitly not on_tty() to avoid setting cached value.
6881 * This becomes relevant for piping output which might be
6882 * ellipsized. */
6883 original_stdout_is_tty = isatty(STDOUT_FILENO);
6884
04ebb595 6885 r = parse_argv(argc, argv);
f459b602 6886 if (r <= 0)
e4b61340 6887 goto finish;
7e4249b9 6888
e4b61340
LP
6889 /* /sbin/runlevel doesn't need to communicate via D-Bus, so
6890 * let's shortcut this */
6891 if (arg_action == ACTION_RUNLEVEL) {
22f4096c 6892 r = runlevel_main();
e4b61340
LP
6893 goto finish;
6894 }
6895
82e23ddd
LP
6896 if (running_in_chroot() > 0 && arg_action != ACTION_SYSTEMCTL) {
6897 log_info("Running in chroot, ignoring request.");
f459b602 6898 r = 0;
82e23ddd
LP
6899 goto finish;
6900 }
6901
41dd15e4
LP
6902 if (!avoid_bus())
6903 r = bus_open_transport_systemd(arg_transport, arg_host, arg_scope != UNIT_FILE_SYSTEM, &bus);
6904
6905 /* systemctl_main() will print an error message for the bus
6906 * connection, but only if it needs to */
e4b61340
LP
6907
6908 switch (arg_action) {
6909
22f4096c 6910 case ACTION_SYSTEMCTL:
f459b602 6911 r = systemctl_main(bus, argc, argv, r);
e4b61340 6912 break;
e4b61340
LP
6913
6914 case ACTION_HALT:
6915 case ACTION_POWEROFF:
6916 case ACTION_REBOOT:
5622dde3 6917 case ACTION_KEXEC:
22f4096c 6918 r = halt_main(bus);
e4b61340
LP
6919 break;
6920
e4b61340
LP
6921 case ACTION_RUNLEVEL2:
6922 case ACTION_RUNLEVEL3:
6923 case ACTION_RUNLEVEL4:
6924 case ACTION_RUNLEVEL5:
6925 case ACTION_RESCUE:
514f4ef5 6926 case ACTION_EMERGENCY:
eb22ac37 6927 case ACTION_DEFAULT:
22f4096c 6928 r = start_with_fallback(bus);
e4b61340 6929 break;
7e4249b9 6930
e4b61340
LP
6931 case ACTION_RELOAD:
6932 case ACTION_REEXEC:
22f4096c 6933 r = reload_with_fallback(bus);
e4b61340
LP
6934 break;
6935
dfcc5c33 6936 case ACTION_CANCEL_SHUTDOWN: {
f459b602 6937 _cleanup_free_ char *m = NULL;
dfcc5c33
MS
6938
6939 if (arg_wall) {
6940 m = strv_join(arg_wall, " ");
6941 if (!m) {
f459b602 6942 r = log_oom();
dfcc5c33
MS
6943 goto finish;
6944 }
6945 }
f459b602 6946
dfcc5c33
MS
6947 r = send_shutdownd(arg_when, SD_SHUTDOWN_NONE, false, !arg_no_wall, m);
6948 if (r < 0)
6949 log_warning("Failed to talk to shutdownd, shutdown hasn't been cancelled: %s", strerror(-r));
f6144808 6950 break;
dfcc5c33 6951 }
f6144808 6952
eb22ac37 6953 case ACTION_RUNLEVEL:
f459b602 6954 case _ACTION_INVALID:
e4b61340
LP
6955 default:
6956 assert_not_reached("Unknown action");
6957 }
7e4249b9
LP
6958
6959finish:
f459b602
MAP
6960 pager_close();
6961 ask_password_agent_close();
6962 polkit_agent_close();
7e4249b9 6963
20b3f379 6964 strv_free(arg_types);
9b9b3d36 6965 strv_free(arg_states);
20b3f379 6966 strv_free(arg_properties);
ea4a240d 6967
f459b602 6968 return r < 0 ? EXIT_FAILURE : r;
7e4249b9 6969}