]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/systemctl.c
pager: add a trivial internal pager
[thirdparty/systemd.git] / src / 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
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
e4b61340 22#include <sys/reboot.h>
7e4249b9
LP
23#include <stdio.h>
24#include <getopt.h>
25#include <stdbool.h>
26#include <string.h>
27#include <errno.h>
28#include <sys/ioctl.h>
29#include <termios.h>
30#include <unistd.h>
eb22ac37 31#include <fcntl.h>
f1c5860b 32#include <sys/socket.h>
ee5762e3 33#include <sys/stat.h>
0e098b15 34#include <stddef.h>
501fc174 35#include <sys/prctl.h>
7e4249b9
LP
36#include <dbus/dbus.h>
37
38#include "log.h"
39#include "util.h"
40#include "macro.h"
41#include "set.h"
e4b61340 42#include "utmp-wtmp.h"
514f4ef5 43#include "special.h"
eb22ac37 44#include "initreq.h"
e4a9373f 45#include "strv.h"
9a1ac7b9 46#include "dbus-common.h"
ab35fb1b 47#include "cgroup-show.h"
c6c18be3 48#include "cgroup-util.h"
582a507f 49#include "list.h"
ee5762e3
LP
50#include "path-lookup.h"
51#include "conf-parser.h"
52#include "sd-daemon.h"
f6144808 53#include "shutdownd.h"
d06dacd0 54#include "exit-status.h"
22f4096c 55#include "bus-errors.h"
7d568925 56#include "build.h"
71fad675 57#include "unit-name.h"
1968a360 58#include "pager.h"
729e3769
LP
59#include "spawn-agent.h"
60#include "install.h"
7e4249b9
LP
61
62static const char *arg_type = NULL;
ea4a240d 63static char **arg_property = NULL;
7e4249b9 64static bool arg_all = false;
e67c3609 65static const char *arg_job_mode = "replace";
729e3769 66static UnitFileScope arg_scope = UNIT_FILE_SYSTEM;
e4b61340 67static bool arg_immediate = false;
ee5762e3 68static bool arg_no_block = false;
0736af98 69static bool arg_no_pager = false;
e4b61340
LP
70static bool arg_no_wtmp = false;
71static bool arg_no_sync = false;
514f4ef5 72static bool arg_no_wall = false;
ee5762e3 73static bool arg_no_reload = false;
e4b61340 74static bool arg_dry = false;
0183528f 75static bool arg_quiet = false;
ee5762e3
LP
76static bool arg_full = false;
77static bool arg_force = false;
501fc174 78static bool arg_ask_password = false;
30732560 79static bool arg_failed = false;
729e3769 80static bool arg_runtime = false;
e4b61340 81static char **arg_wall = NULL;
8a0867d6
LP
82static const char *arg_kill_who = NULL;
83static const char *arg_kill_mode = NULL;
84static int arg_signal = SIGTERM;
69fc152f 85static const char *arg_root = NULL;
f6144808 86static usec_t arg_when = 0;
4445a875 87static enum action {
e4b61340
LP
88 ACTION_INVALID,
89 ACTION_SYSTEMCTL,
90 ACTION_HALT,
91 ACTION_POWEROFF,
92 ACTION_REBOOT,
20b09ca7
LP
93 ACTION_KEXEC,
94 ACTION_EXIT,
e4b61340
LP
95 ACTION_RUNLEVEL2,
96 ACTION_RUNLEVEL3,
97 ACTION_RUNLEVEL4,
98 ACTION_RUNLEVEL5,
99 ACTION_RESCUE,
514f4ef5
LP
100 ACTION_EMERGENCY,
101 ACTION_DEFAULT,
e4b61340
LP
102 ACTION_RELOAD,
103 ACTION_REEXEC,
104 ACTION_RUNLEVEL,
f6144808 105 ACTION_CANCEL_SHUTDOWN,
e4b61340
LP
106 _ACTION_MAX
107} arg_action = ACTION_SYSTEMCTL;
4445a875
LP
108static enum dot {
109 DOT_ALL,
110 DOT_ORDER,
111 DOT_REQUIRE
112} arg_dot = DOT_ALL;
a8f11321
LP
113static enum transport {
114 TRANSPORT_NORMAL,
115 TRANSPORT_SSH,
116 TRANSPORT_POLKIT
117} arg_transport = TRANSPORT_NORMAL;
118static const char *arg_host = NULL;
e4b61340 119
f4579ce7
LP
120static bool private_bus = false;
121
729e3769 122static int daemon_reload(DBusConnection *bus, char **args);
1968a360 123
2ee68f72 124static bool on_tty(void) {
2cc59dbf
LP
125 static int t = -1;
126
75d12d57
LP
127 /* Note that this is invoked relatively early, before we start
128 * the pager. That means the value we return reflects whether
129 * we originally were started on a tty, not if we currently
060ed82e 130 * are. But this is intended, since we want colour and so on
75d12d57
LP
131 * when run in our own pager. */
132
2cc59dbf
LP
133 if (_unlikely_(t < 0))
134 t = isatty(STDOUT_FILENO) > 0;
135
2ee68f72
LP
136 return t;
137}
138
3b0727f5 139static void pager_open_if_enabled(void) {
f8440af5
LP
140
141 /* Cache result before we open the pager */
3b0727f5
LP
142 on_tty();
143
729e3769
LP
144 if (arg_no_pager)
145 return;
3b0727f5 146
729e3769
LP
147 pager_open();
148}
c0f9c7da 149
729e3769 150static void agent_open_if_enabled(void) {
501fc174 151
729e3769 152 /* Open the password agent as a child process if necessary */
501fc174
LP
153
154 if (!arg_ask_password)
155 return;
715554e7 156
729e3769 157 if (arg_scope != UNIT_FILE_SYSTEM)
501fc174
LP
158 return;
159
729e3769 160 agent_open();
501fc174
LP
161}
162
2ee68f72
LP
163static const char *ansi_highlight(bool b) {
164
165 if (!on_tty())
2cc59dbf
LP
166 return "";
167
168 return b ? ANSI_HIGHLIGHT_ON : ANSI_HIGHLIGHT_OFF;
169}
170
2ee68f72
LP
171static const char *ansi_highlight_green(bool b) {
172
173 if (!on_tty())
174 return "";
175
176 return b ? ANSI_HIGHLIGHT_GREEN_ON : ANSI_HIGHLIGHT_OFF;
177}
178
f73e33d9 179static bool error_is_no_service(const DBusError *error) {
514f4ef5
LP
180 assert(error);
181
e4b61340
LP
182 if (!dbus_error_is_set(error))
183 return false;
184
185 if (dbus_error_has_name(error, DBUS_ERROR_NAME_HAS_NO_OWNER))
186 return true;
187
188 if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN))
189 return true;
190
191 return startswith(error->name, "org.freedesktop.DBus.Error.Spawn.");
192}
7e4249b9 193
22f4096c
LP
194static int translate_bus_error_to_exit_status(int r, const DBusError *error) {
195 assert(error);
196
197 if (!dbus_error_is_set(error))
198 return r;
199
200 if (dbus_error_has_name(error, DBUS_ERROR_ACCESS_DENIED) ||
201 dbus_error_has_name(error, BUS_ERROR_ONLY_BY_DEPENDENCY) ||
202 dbus_error_has_name(error, BUS_ERROR_NO_ISOLATION) ||
203 dbus_error_has_name(error, BUS_ERROR_TRANSACTION_IS_DESTRUCTIVE))
204 return EXIT_NOPERMISSION;
205
206 if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT))
207 return EXIT_NOTINSTALLED;
208
209 if (dbus_error_has_name(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE) ||
210 dbus_error_has_name(error, BUS_ERROR_NOT_SUPPORTED))
211 return EXIT_NOTIMPLEMENTED;
212
213 if (dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED))
214 return EXIT_NOTCONFIGURED;
215
216 if (r != 0)
217 return r;
218
219 return EXIT_FAILURE;
220}
221
514f4ef5 222static void warn_wall(enum action action) {
ef2f1067 223 static const char *table[_ACTION_MAX] = {
514f4ef5
LP
224 [ACTION_HALT] = "The system is going down for system halt NOW!",
225 [ACTION_REBOOT] = "The system is going down for reboot NOW!",
226 [ACTION_POWEROFF] = "The system is going down for power-off NOW!",
20b09ca7 227 [ACTION_KEXEC] = "The system is going down for kexec reboot NOW!",
514f4ef5
LP
228 [ACTION_RESCUE] = "The system is going down to rescue mode NOW!",
229 [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!"
ef2f1067
LP
230 };
231
514f4ef5
LP
232 if (arg_no_wall)
233 return;
234
e4a9373f
LP
235 if (arg_wall) {
236 char *p;
237
238 if (!(p = strv_join(arg_wall, " "))) {
239 log_error("Failed to join strings.");
240 return;
241 }
242
243 if (*p) {
7af53310 244 utmp_wall(p, NULL);
e4a9373f
LP
245 free(p);
246 return;
247 }
248
249 free(p);
250 }
251
514f4ef5 252 if (!table[action])
ef2f1067
LP
253 return;
254
7af53310 255 utmp_wall(table[action], NULL);
ef2f1067
LP
256}
257
729e3769
LP
258static bool avoid_bus(void) {
259
260 if (running_in_chroot() > 0)
261 return true;
262
263 if (sd_booted() <= 0)
264 return true;
265
266 if (!isempty(arg_root))
267 return true;
268
269 if (arg_scope == UNIT_FILE_GLOBAL)
270 return true;
271
272 return false;
273}
274
36c32ba2
LP
275struct unit_info {
276 const char *id;
277 const char *description;
278 const char *load_state;
279 const char *active_state;
280 const char *sub_state;
281 const char *following;
282 const char *unit_path;
283 uint32_t job_id;
284 const char *job_type;
285 const char *job_path;
286};
287
288static int compare_unit_info(const void *a, const void *b) {
289 const char *d1, *d2;
290 const struct unit_info *u = a, *v = b;
291
292 d1 = strrchr(u->id, '.');
293 d2 = strrchr(v->id, '.');
294
295 if (d1 && d2) {
296 int r;
297
a2a3a5b9 298 if ((r = strcasecmp(d1, d2)) != 0)
36c32ba2
LP
299 return r;
300 }
301
a2a3a5b9 302 return strcasecmp(u->id, v->id);
36c32ba2
LP
303}
304
30732560 305static bool output_show_unit(const struct unit_info *u) {
33330222 306 const char *dot;
b036fc00 307
30732560
LP
308 if (arg_failed)
309 return streq(u->active_state, "failed");
310
33330222
ZJS
311 return (!arg_type || ((dot = strrchr(u->id, '.')) &&
312 streq(dot+1, arg_type))) &&
313 (arg_all || !(streq(u->active_state, "inactive") || u->following[0]) || u->job_id > 0);
314}
315
eb68c413 316static void output_units_list(const struct unit_info *unit_infos, unsigned c) {
688c6725 317 unsigned active_len, sub_len, job_len, n_shown = 0;
b036fc00 318 const struct unit_info *u;
33330222 319
b036fc00
LP
320 active_len = sizeof("ACTIVE")-1;
321 sub_len = sizeof("SUB")-1;
322 job_len = sizeof("JOB")-1;
323
324 for (u = unit_infos; u < unit_infos + c; u++) {
30732560 325 if (!output_show_unit(u))
b036fc00
LP
326 continue;
327
328 active_len = MAX(active_len, strlen(u->active_state));
329 sub_len = MAX(sub_len, strlen(u->sub_state));
330 if (u->job_id != 0)
331 job_len = MAX(job_len, strlen(u->job_type));
33330222
ZJS
332 }
333
eb68c413 334 if (on_tty()) {
33330222
ZJS
335 printf("%-25s %-6s %-*s %-*s %-*s", "UNIT", "LOAD",
336 active_len, "ACTIVE", sub_len, "SUB", job_len, "JOB");
964d124e 337 if (columns() >= 80+12 || arg_full || !arg_no_pager)
33330222 338 printf(" %s\n", "DESCRIPTION");
eb68c413 339 else
33330222 340 printf("\n");
eb68c413
ZJS
341 }
342
b036fc00
LP
343 for (u = unit_infos; u < unit_infos + c; u++) {
344 char *e;
345 int a = 0, b = 0;
346 const char *on_loaded, *off_loaded;
347 const char *on_active, *off_active;
348
30732560 349 if (!output_show_unit(u))
b036fc00
LP
350 continue;
351
688c6725
LP
352 n_shown++;
353
00dc5d76
LP
354 if (!streq(u->load_state, "loaded") &&
355 !streq(u->load_state, "banned")) {
b036fc00
LP
356 on_loaded = ansi_highlight(true);
357 off_loaded = ansi_highlight(false);
358 } else
359 on_loaded = off_loaded = "";
360
361 if (streq(u->active_state, "failed")) {
362 on_active = ansi_highlight(true);
363 off_active = ansi_highlight(false);
364 } else
365 on_active = off_active = "";
eb68c413 366
b036fc00 367 e = arg_full ? NULL : ellipsize(u->id, 25, 33);
eb68c413 368
b036fc00
LP
369 printf("%-25s %s%-6s%s %s%-*s %-*s%s%n",
370 e ? e : u->id,
371 on_loaded, u->load_state, off_loaded,
372 on_active, active_len, u->active_state,
373 sub_len, u->sub_state, off_active,
374 &a);
eb68c413 375
b036fc00
LP
376 free(e);
377
378 a -= strlen(on_loaded) + strlen(off_loaded);
379 a -= strlen(on_active) + strlen(off_active);
380
381 if (u->job_id != 0)
382 printf(" %-*s", job_len, u->job_type);
383 else
384 b = 1 + job_len;
385
386 if (a + b + 1 < columns()) {
387 if (u->job_id == 0)
388 printf(" %-*s", job_len, "");
389
964d124e 390 if (arg_full || !arg_no_pager)
b036fc00
LP
391 printf(" %s", u->description);
392 else
393 printf(" %.*s", columns() - a - b - 1, u->description);
eb68c413 394 }
b036fc00
LP
395
396 fputs("\n", stdout);
eb68c413
ZJS
397 }
398
9b2fcafb 399 if (on_tty()) {
eb68c413
ZJS
400 printf("\nLOAD = Reflects whether the unit definition was properly loaded.\n"
401 "ACTIVE = The high-level unit activation state, i.e. generalization of SUB.\n"
402 "SUB = The low-level unit activation state, values depend on unit type.\n"
403 "JOB = Pending job for the unit.\n");
404
405 if (arg_all)
688c6725 406 printf("\n%u units listed.\n", n_shown);
eb68c413 407 else
688c6725 408 printf("\n%u units listed. Pass --all to see inactive units, too.\n", n_shown);
eb68c413
ZJS
409 }
410}
411
729e3769 412static int list_units(DBusConnection *bus, char **args) {
7e4249b9
LP
413 DBusMessage *m = NULL, *reply = NULL;
414 DBusError error;
415 int r;
416 DBusMessageIter iter, sub, sub2;
eb68c413 417 unsigned c = 0, n_units = 0;
36c32ba2 418 struct unit_info *unit_infos = NULL;
7e4249b9
LP
419
420 dbus_error_init(&error);
421
514f4ef5
LP
422 assert(bus);
423
1968a360 424 pager_open_if_enabled();
ec14911e 425
7e4249b9
LP
426 if (!(m = dbus_message_new_method_call(
427 "org.freedesktop.systemd1",
428 "/org/freedesktop/systemd1",
429 "org.freedesktop.systemd1.Manager",
430 "ListUnits"))) {
431 log_error("Could not allocate message.");
432 return -ENOMEM;
433 }
434
435 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 436 log_error("Failed to issue method call: %s", bus_error_message(&error));
7e4249b9
LP
437 r = -EIO;
438 goto finish;
439 }
440
441 if (!dbus_message_iter_init(reply, &iter) ||
442 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
443 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
444 log_error("Failed to parse reply.");
445 r = -EIO;
446 goto finish;
447 }
448
449 dbus_message_iter_recurse(&iter, &sub);
450
7e4249b9 451 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
36c32ba2 452 struct unit_info *u;
7e4249b9
LP
453
454 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
455 log_error("Failed to parse reply.");
456 r = -EIO;
457 goto finish;
458 }
459
36c32ba2
LP
460 if (c >= n_units) {
461 struct unit_info *w;
462
463 n_units = MAX(2*c, 16);
464 w = realloc(unit_infos, sizeof(struct unit_info) * n_units);
465
466 if (!w) {
467 log_error("Failed to allocate unit array.");
468 r = -ENOMEM;
469 goto finish;
470 }
471
472 unit_infos = w;
473 }
474
475 u = unit_infos+c;
476
7e4249b9
LP
477 dbus_message_iter_recurse(&sub, &sub2);
478
36c32ba2
LP
479 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->id, true) < 0 ||
480 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->description, true) < 0 ||
481 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->load_state, true) < 0 ||
482 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->active_state, true) < 0 ||
483 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->sub_state, true) < 0 ||
484 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->following, true) < 0 ||
485 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->unit_path, true) < 0 ||
486 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &u->job_id, true) < 0 ||
487 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->job_type, true) < 0 ||
488 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &u->job_path, false) < 0) {
7e4249b9
LP
489 log_error("Failed to parse reply.");
490 r = -EIO;
491 goto finish;
492 }
493
36c32ba2
LP
494 dbus_message_iter_next(&sub);
495 c++;
496 }
497
bd40a2d8
LP
498 if (c > 0) {
499 qsort(unit_infos, c, sizeof(struct unit_info), compare_unit_info);
500 output_units_list(unit_infos, c);
501 }
4445a875
LP
502
503 r = 0;
504
505finish:
506 if (m)
507 dbus_message_unref(m);
508
509 if (reply)
510 dbus_message_unref(reply);
511
36c32ba2
LP
512 free(unit_infos);
513
4445a875
LP
514 dbus_error_free(&error);
515
516 return r;
517}
518
729e3769
LP
519static int compare_unit_file_list(const void *a, const void *b) {
520 const char *d1, *d2;
521 const UnitFileList *u = a, *v = b;
522
523 d1 = strrchr(u->path, '.');
524 d2 = strrchr(v->path, '.');
525
526 if (d1 && d2) {
527 int r;
528
529 r = strcasecmp(d1, d2);
530 if (r != 0)
531 return r;
532 }
533
534 return strcasecmp(file_name_from_path(u->path), file_name_from_path(v->path));
535}
536
537static bool output_show_unit_file(const UnitFileList *u) {
538 const char *dot;
539
540 return !arg_type || ((dot = strrchr(u->path, '.')) && streq(dot+1, arg_type));
541}
542
543static void output_unit_file_list(const UnitFileList *units, unsigned c) {
544 unsigned n_shown = 0;
545 const UnitFileList *u;
546
547 if (on_tty())
548 printf("%-25s %-6s\n", "UNIT FILE", "STATE");
549
550 for (u = units; u < units + c; u++) {
551 char *e;
552 const char *on, *off;
553 const char *id;
554
555 if (!output_show_unit_file(u))
556 continue;
557
558 n_shown++;
559
560 if (u->state == UNIT_FILE_MASKED ||
561 u->state == UNIT_FILE_MASKED_RUNTIME ||
562 u->state == UNIT_FILE_DISABLED) {
563 on = ansi_highlight(true);
564 off = ansi_highlight(false);
565 } else if (u->state == UNIT_FILE_ENABLED) {
566 on = ansi_highlight_green(true);
567 off = ansi_highlight_green(false);
568 } else
569 on = off = "";
570
571 id = file_name_from_path(u->path);
572
573 e = arg_full ? NULL : ellipsize(id, 25, 33);
574
575 printf("%-25s %s%-6s%s\n",
576 e ? e : id,
577 on, unit_file_state_to_string(u->state), off);
578
579 free(e);
580 }
581
582 if (on_tty())
583 printf("\n%u unit files listed.\n", n_shown);
584}
585
586static int list_unit_files(DBusConnection *bus, char **args) {
587 DBusMessage *m = NULL, *reply = NULL;
588 DBusError error;
589 int r;
590 DBusMessageIter iter, sub, sub2;
591 unsigned c = 0, n_units = 0;
592 UnitFileList *units = NULL;
593
594 dbus_error_init(&error);
595
596 assert(bus);
597
598 pager_open_if_enabled();
599
600 if (avoid_bus()) {
601 Hashmap *h;
602 UnitFileList *u;
603 Iterator i;
604
605 h = hashmap_new(string_hash_func, string_compare_func);
606 if (!h) {
607 log_error("Out of memory");
608 return -ENOMEM;
609 }
610
611 r = unit_file_get_list(arg_scope, arg_root, h);
612 if (r < 0) {
8ea913b2 613 unit_file_list_free(h);
729e3769
LP
614 log_error("Failed to get unit file list: %s", strerror(-r));
615 return r;
616 }
617
618 n_units = hashmap_size(h);
619 units = new(UnitFileList, n_units);
620 if (!units) {
621 unit_file_list_free(h);
622 log_error("Out of memory");
623 return -ENOMEM;
624 }
625
626 HASHMAP_FOREACH(u, h, i) {
627 memcpy(units + c++, u, sizeof(UnitFileList));
628 free(u);
629 }
630
631 hashmap_free(h);
632 } else {
633 m = dbus_message_new_method_call(
634 "org.freedesktop.systemd1",
635 "/org/freedesktop/systemd1",
636 "org.freedesktop.systemd1.Manager",
637 "ListUnitFiles");
638 if (!m) {
639 log_error("Could not allocate message.");
640 return -ENOMEM;
641 }
642
643 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
644 if (!reply) {
645 log_error("Failed to issue method call: %s", bus_error_message(&error));
646 r = -EIO;
647 goto finish;
648 }
649
650 if (!dbus_message_iter_init(reply, &iter) ||
651 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
652 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
653 log_error("Failed to parse reply.");
654 r = -EIO;
655 goto finish;
656 }
657
658 dbus_message_iter_recurse(&iter, &sub);
659
660 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
661 UnitFileList *u;
662 const char *state;
663
664 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
665 log_error("Failed to parse reply.");
666 r = -EIO;
667 goto finish;
668 }
669
670 if (c >= n_units) {
671 UnitFileList *w;
672
673 n_units = MAX(2*c, 16);
674 w = realloc(units, sizeof(struct UnitFileList) * n_units);
675
676 if (!w) {
677 log_error("Failed to allocate unit array.");
678 r = -ENOMEM;
679 goto finish;
680 }
681
682 units = w;
683 }
684
685 u = units+c;
686
687 dbus_message_iter_recurse(&sub, &sub2);
688
689 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &u->path, true) < 0 ||
690 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, false) < 0) {
691 log_error("Failed to parse reply.");
692 r = -EIO;
693 goto finish;
694 }
695
696 u->state = unit_file_state_from_string(state);
697
698 dbus_message_iter_next(&sub);
699 c++;
700 }
701 }
702
703 if (c > 0) {
704 qsort(units, c, sizeof(UnitFileList), compare_unit_file_list);
705 output_unit_file_list(units, c);
706 }
707
708 r = 0;
709
710finish:
711 if (m)
712 dbus_message_unref(m);
713
714 if (reply)
715 dbus_message_unref(reply);
716
717 free(units);
718
719 dbus_error_free(&error);
720
721 return r;
722}
723
4445a875
LP
724static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
725 static const char * const colors[] = {
726 "Requires", "[color=\"black\"]",
727 "RequiresOverridable", "[color=\"black\"]",
728 "Requisite", "[color=\"darkblue\"]",
729 "RequisiteOverridable", "[color=\"darkblue\"]",
730 "Wants", "[color=\"darkgrey\"]",
731 "Conflicts", "[color=\"red\"]",
69dd2852 732 "ConflictedBy", "[color=\"red\"]",
4445a875
LP
733 "After", "[color=\"green\"]"
734 };
735
736 const char *c = NULL;
737 unsigned i;
738
739 assert(name);
740 assert(prop);
741 assert(iter);
742
743 for (i = 0; i < ELEMENTSOF(colors); i += 2)
744 if (streq(colors[i], prop)) {
745 c = colors[i+1];
746 break;
747 }
748
749 if (!c)
750 return 0;
751
752 if (arg_dot != DOT_ALL)
753 if ((arg_dot == DOT_ORDER) != streq(prop, "After"))
754 return 0;
755
756 switch (dbus_message_iter_get_arg_type(iter)) {
757
758 case DBUS_TYPE_ARRAY:
759
760 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
761 DBusMessageIter sub;
762
763 dbus_message_iter_recurse(iter, &sub);
764
765 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
766 const char *s;
767
768 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
769 dbus_message_iter_get_basic(&sub, &s);
770 printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
771
772 dbus_message_iter_next(&sub);
773 }
774
775 return 0;
776 }
777 }
778
779 return 0;
780}
781
782static int dot_one(DBusConnection *bus, const char *name, const char *path) {
783 DBusMessage *m = NULL, *reply = NULL;
784 const char *interface = "org.freedesktop.systemd1.Unit";
785 int r;
786 DBusError error;
787 DBusMessageIter iter, sub, sub2, sub3;
788
789 assert(bus);
790 assert(path);
791
792 dbus_error_init(&error);
793
794 if (!(m = dbus_message_new_method_call(
795 "org.freedesktop.systemd1",
796 path,
797 "org.freedesktop.DBus.Properties",
798 "GetAll"))) {
799 log_error("Could not allocate message.");
800 r = -ENOMEM;
801 goto finish;
802 }
803
804 if (!dbus_message_append_args(m,
805 DBUS_TYPE_STRING, &interface,
806 DBUS_TYPE_INVALID)) {
807 log_error("Could not append arguments to message.");
808 r = -ENOMEM;
809 goto finish;
810 }
811
812 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 813 log_error("Failed to issue method call: %s", bus_error_message(&error));
4445a875
LP
814 r = -EIO;
815 goto finish;
816 }
817
818 if (!dbus_message_iter_init(reply, &iter) ||
819 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
820 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
821 log_error("Failed to parse reply.");
822 r = -EIO;
823 goto finish;
824 }
825
826 dbus_message_iter_recurse(&iter, &sub);
827
828 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
829 const char *prop;
830
831 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
832 log_error("Failed to parse reply.");
833 r = -EIO;
834 goto finish;
835 }
836
837 dbus_message_iter_recurse(&sub, &sub2);
838
839 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0) {
840 log_error("Failed to parse reply.");
841 r = -EIO;
842 goto finish;
843 }
844
845 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
846 log_error("Failed to parse reply.");
847 r = -EIO;
848 goto finish;
849 }
850
851 dbus_message_iter_recurse(&sub2, &sub3);
852
853 if (dot_one_property(name, prop, &sub3)) {
854 log_error("Failed to parse reply.");
855 r = -EIO;
856 goto finish;
857 }
858
859 dbus_message_iter_next(&sub);
860 }
861
e364ad06
LP
862 r = 0;
863
4445a875
LP
864finish:
865 if (m)
866 dbus_message_unref(m);
867
868 if (reply)
869 dbus_message_unref(reply);
870
871 dbus_error_free(&error);
872
873 return r;
874}
875
729e3769 876static int dot(DBusConnection *bus, char **args) {
4445a875
LP
877 DBusMessage *m = NULL, *reply = NULL;
878 DBusError error;
879 int r;
880 DBusMessageIter iter, sub, sub2;
881
882 dbus_error_init(&error);
883
884 assert(bus);
885
886 if (!(m = dbus_message_new_method_call(
887 "org.freedesktop.systemd1",
888 "/org/freedesktop/systemd1",
889 "org.freedesktop.systemd1.Manager",
890 "ListUnits"))) {
891 log_error("Could not allocate message.");
892 return -ENOMEM;
893 }
894
895 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 896 log_error("Failed to issue method call: %s", bus_error_message(&error));
4445a875
LP
897 r = -EIO;
898 goto finish;
899 }
900
901 if (!dbus_message_iter_init(reply, &iter) ||
902 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
903 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
904 log_error("Failed to parse reply.");
905 r = -EIO;
906 goto finish;
907 }
908
909 printf("digraph systemd {\n");
910
911 dbus_message_iter_recurse(&iter, &sub);
912 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
4a4d6b4b 913 const char *id, *description, *load_state, *active_state, *sub_state, *following, *unit_path;
4445a875
LP
914
915 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
916 log_error("Failed to parse reply.");
917 r = -EIO;
918 goto finish;
919 }
920
921 dbus_message_iter_recurse(&sub, &sub2);
922
923 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
924 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &description, true) < 0 ||
925 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 ||
926 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 ||
927 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 ||
4a4d6b4b 928 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &following, true) < 0 ||
4445a875
LP
929 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, true) < 0) {
930 log_error("Failed to parse reply.");
931 r = -EIO;
932 goto finish;
933 }
934
935 if ((r = dot_one(bus, id, unit_path)) < 0)
936 goto finish;
937
938 /* printf("\t\"%s\";\n", id); */
939 dbus_message_iter_next(&sub);
940 }
941
942 printf("}\n");
943
944 log_info(" Color legend: black = Requires\n"
945 " dark blue = Requisite\n"
946 " dark grey = Wants\n"
947 " red = Conflicts\n"
948 " green = After\n");
949
ef3a24de 950 if (on_tty())
4445a875
LP
951 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
952 "-- Try a shell pipeline like 'systemctl dot | dot -Tsvg > systemd.svg'!\n");
7e4249b9
LP
953
954 r = 0;
955
956finish:
957 if (m)
958 dbus_message_unref(m);
959
960 if (reply)
961 dbus_message_unref(reply);
962
963 dbus_error_free(&error);
964
965 return r;
966}
967
729e3769 968static int list_jobs(DBusConnection *bus, char **args) {
7e4249b9
LP
969 DBusMessage *m = NULL, *reply = NULL;
970 DBusError error;
971 int r;
972 DBusMessageIter iter, sub, sub2;
973 unsigned k = 0;
974
975 dbus_error_init(&error);
976
514f4ef5
LP
977 assert(bus);
978
1968a360 979 pager_open_if_enabled();
ec14911e 980
7e4249b9
LP
981 if (!(m = dbus_message_new_method_call(
982 "org.freedesktop.systemd1",
983 "/org/freedesktop/systemd1",
984 "org.freedesktop.systemd1.Manager",
985 "ListJobs"))) {
986 log_error("Could not allocate message.");
987 return -ENOMEM;
988 }
989
990 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 991 log_error("Failed to issue method call: %s", bus_error_message(&error));
7e4249b9
LP
992 r = -EIO;
993 goto finish;
994 }
995
996 if (!dbus_message_iter_init(reply, &iter) ||
997 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
998 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
999 log_error("Failed to parse reply.");
1000 r = -EIO;
1001 goto finish;
1002 }
1003
1004 dbus_message_iter_recurse(&iter, &sub);
1005
ef3a24de 1006 if (on_tty())
f73e33d9 1007 printf("%4s %-25s %-15s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
7e4249b9
LP
1008
1009 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1010 const char *name, *type, *state, *job_path, *unit_path;
1011 uint32_t id;
8fe914ec 1012 char *e;
7e4249b9
LP
1013
1014 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
1015 log_error("Failed to parse reply.");
1016 r = -EIO;
1017 goto finish;
1018 }
1019
1020 dbus_message_iter_recurse(&sub, &sub2);
1021
1022 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &id, true) < 0 ||
1023 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
1024 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
1025 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, true) < 0 ||
1026 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, true) < 0 ||
1027 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, false) < 0) {
1028 log_error("Failed to parse reply.");
1029 r = -EIO;
1030 goto finish;
1031 }
1032
f73e33d9
LP
1033 e = arg_full ? NULL : ellipsize(name, 25, 33);
1034 printf("%4u %-25s %-15s %-7s\n", id, e ? e : name, type, state);
8fe914ec
LP
1035 free(e);
1036
7e4249b9
LP
1037 k++;
1038
1039 dbus_message_iter_next(&sub);
1040 }
1041
ef3a24de 1042 if (on_tty())
f73e33d9
LP
1043 printf("\n%u jobs listed.\n", k);
1044
7e4249b9
LP
1045 r = 0;
1046
1047finish:
1048 if (m)
1049 dbus_message_unref(m);
1050
1051 if (reply)
1052 dbus_message_unref(reply);
1053
1054 dbus_error_free(&error);
1055
1056 return r;
1057}
1058
729e3769 1059static int load_unit(DBusConnection *bus, char **args) {
f8440af5 1060 DBusMessage *m = NULL;
7e4249b9
LP
1061 DBusError error;
1062 int r;
729e3769 1063 char **name;
7e4249b9
LP
1064
1065 dbus_error_init(&error);
1066
514f4ef5
LP
1067 assert(bus);
1068 assert(args);
1069
729e3769 1070 STRV_FOREACH(name, args+1) {
f8440af5 1071 DBusMessage *reply;
7e4249b9
LP
1072
1073 if (!(m = dbus_message_new_method_call(
1074 "org.freedesktop.systemd1",
1075 "/org/freedesktop/systemd1",
1076 "org.freedesktop.systemd1.Manager",
1077 "LoadUnit"))) {
1078 log_error("Could not allocate message.");
1079 r = -ENOMEM;
1080 goto finish;
1081 }
1082
1083 if (!dbus_message_append_args(m,
729e3769 1084 DBUS_TYPE_STRING, name,
7e4249b9
LP
1085 DBUS_TYPE_INVALID)) {
1086 log_error("Could not append arguments to message.");
1087 r = -ENOMEM;
1088 goto finish;
1089 }
1090
1091 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 1092 log_error("Failed to issue method call: %s", bus_error_message(&error));
7e4249b9
LP
1093 r = -EIO;
1094 goto finish;
1095 }
1096
1097 dbus_message_unref(m);
1098 dbus_message_unref(reply);
1099
1100 m = reply = NULL;
1101 }
1102
1103 r = 0;
1104
1105finish:
1106 if (m)
1107 dbus_message_unref(m);
1108
7e4249b9
LP
1109 dbus_error_free(&error);
1110
1111 return r;
1112}
1113
729e3769 1114static int cancel_job(DBusConnection *bus, char **args) {
7e4249b9
LP
1115 DBusMessage *m = NULL, *reply = NULL;
1116 DBusError error;
1117 int r;
729e3769 1118 char **name;
7e4249b9
LP
1119
1120 dbus_error_init(&error);
1121
514f4ef5
LP
1122 assert(bus);
1123 assert(args);
1124
729e3769
LP
1125 if (strv_length(args) <= 1)
1126 return daemon_reload(bus, args);
ee5762e3 1127
729e3769 1128 STRV_FOREACH(name, args+1) {
7e4249b9
LP
1129 unsigned id;
1130 const char *path;
1131
1132 if (!(m = dbus_message_new_method_call(
1133 "org.freedesktop.systemd1",
1134 "/org/freedesktop/systemd1",
1135 "org.freedesktop.systemd1.Manager",
1136 "GetJob"))) {
1137 log_error("Could not allocate message.");
1138 r = -ENOMEM;
1139 goto finish;
1140 }
1141
729e3769 1142 if ((r = safe_atou(*name, &id)) < 0) {
7e4249b9
LP
1143 log_error("Failed to parse job id: %s", strerror(-r));
1144 goto finish;
1145 }
1146
1147 assert_cc(sizeof(uint32_t) == sizeof(id));
1148 if (!dbus_message_append_args(m,
1149 DBUS_TYPE_UINT32, &id,
1150 DBUS_TYPE_INVALID)) {
1151 log_error("Could not append arguments to message.");
1152 r = -ENOMEM;
1153 goto finish;
1154 }
1155
1156 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 1157 log_error("Failed to issue method call: %s", bus_error_message(&error));
7e4249b9
LP
1158 r = -EIO;
1159 goto finish;
1160 }
1161
1162 if (!dbus_message_get_args(reply, &error,
1163 DBUS_TYPE_OBJECT_PATH, &path,
1164 DBUS_TYPE_INVALID)) {
4cf5d675 1165 log_error("Failed to parse reply: %s", bus_error_message(&error));
7e4249b9
LP
1166 r = -EIO;
1167 goto finish;
1168 }
1169
1170 dbus_message_unref(m);
1171 if (!(m = dbus_message_new_method_call(
1172 "org.freedesktop.systemd1",
1173 path,
1174 "org.freedesktop.systemd1.Job",
1175 "Cancel"))) {
1176 log_error("Could not allocate message.");
1177 r = -ENOMEM;
1178 goto finish;
1179 }
1180
1181 dbus_message_unref(reply);
1182 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 1183 log_error("Failed to issue method call: %s", bus_error_message(&error));
7e4249b9
LP
1184 r = -EIO;
1185 goto finish;
1186 }
1187
1188 dbus_message_unref(m);
1189 dbus_message_unref(reply);
1190 m = reply = NULL;
1191 }
1192
1193 r = 0;
1194
1195finish:
1196 if (m)
1197 dbus_message_unref(m);
1198
1199 if (reply)
1200 dbus_message_unref(reply);
1201
1202 dbus_error_free(&error);
1203
1204 return r;
1205}
1206
ee5762e3 1207static bool need_daemon_reload(DBusConnection *bus, const char *unit) {
e364ad06 1208 DBusMessage *m = NULL, *reply = NULL;
45fb0699
LP
1209 dbus_bool_t b = FALSE;
1210 DBusMessageIter iter, sub;
1211 const char
1212 *interface = "org.freedesktop.systemd1.Unit",
1213 *property = "NeedDaemonReload",
1214 *path;
1215
1216 /* We ignore all errors here, since this is used to show a warning only */
1217
1218 if (!(m = dbus_message_new_method_call(
1219 "org.freedesktop.systemd1",
1220 "/org/freedesktop/systemd1",
1221 "org.freedesktop.systemd1.Manager",
1222 "GetUnit")))
1223 goto finish;
1224
1225 if (!dbus_message_append_args(m,
1226 DBUS_TYPE_STRING, &unit,
1227 DBUS_TYPE_INVALID))
1228 goto finish;
1229
1230 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL)))
1231 goto finish;
1232
1233 if (!dbus_message_get_args(reply, NULL,
1234 DBUS_TYPE_OBJECT_PATH, &path,
1235 DBUS_TYPE_INVALID))
1236 goto finish;
1237
1238 dbus_message_unref(m);
1239 if (!(m = dbus_message_new_method_call(
1240 "org.freedesktop.systemd1",
1241 path,
1242 "org.freedesktop.DBus.Properties",
1243 "Get")))
1244 goto finish;
1245
1246 if (!dbus_message_append_args(m,
1247 DBUS_TYPE_STRING, &interface,
1248 DBUS_TYPE_STRING, &property,
1249 DBUS_TYPE_INVALID)) {
1250 goto finish;
1251 }
1252
1253 dbus_message_unref(reply);
1254 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL)))
1255 goto finish;
1256
1257 if (!dbus_message_iter_init(reply, &iter) ||
1258 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
1259 goto finish;
1260
1261 dbus_message_iter_recurse(&iter, &sub);
1262
1263 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
1264 goto finish;
1265
1266 dbus_message_iter_get_basic(&sub, &b);
1267
1268finish:
1269 if (m)
1270 dbus_message_unref(m);
1271
1272 if (reply)
1273 dbus_message_unref(reply);
1274
1275 return b;
1276}
1277
5e374895
LP
1278typedef struct WaitData {
1279 Set *set;
5d44db4a 1280 char *result;
5e374895
LP
1281} WaitData;
1282
7e4249b9
LP
1283static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) {
1284 DBusError error;
5e374895 1285 WaitData *d = data;
7e4249b9
LP
1286
1287 assert(connection);
1288 assert(message);
5e374895 1289 assert(d);
7e4249b9
LP
1290
1291 dbus_error_init(&error);
1292
54165a39
LP
1293 log_debug("Got D-Bus request: %s.%s() on %s",
1294 dbus_message_get_interface(message),
1295 dbus_message_get_member(message),
1296 dbus_message_get_path(message));
7e4249b9
LP
1297
1298 if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
1299 log_error("Warning! D-Bus connection terminated.");
1300 dbus_connection_close(connection);
1301
1302 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
1303 uint32_t id;
5d44db4a 1304 const char *path, *result;
5e374895 1305 dbus_bool_t success = true;
7e4249b9 1306
5d44db4a
LP
1307 if (dbus_message_get_args(message, &error,
1308 DBUS_TYPE_UINT32, &id,
1309 DBUS_TYPE_OBJECT_PATH, &path,
1310 DBUS_TYPE_STRING, &result,
1311 DBUS_TYPE_INVALID)) {
7e4249b9
LP
1312 char *p;
1313
5d44db4a
LP
1314 if ((p = set_remove(d->set, (char*) path)))
1315 free(p);
1316
1317 if (*result)
1318 d->result = strdup(result);
1319
1320 goto finish;
1321 }
1322#ifndef LEGACY
1323 dbus_error_free(&error);
1324
1325 if (dbus_message_get_args(message, &error,
1326 DBUS_TYPE_UINT32, &id,
1327 DBUS_TYPE_OBJECT_PATH, &path,
1328 DBUS_TYPE_BOOLEAN, &success,
1329 DBUS_TYPE_INVALID)) {
1330 char *p;
1331
1332 /* Compatibility with older systemd versions <
1333 * 19 during upgrades. This should be dropped
1334 * one day */
1335
5e374895 1336 if ((p = set_remove(d->set, (char*) path)))
7e4249b9 1337 free(p);
5e374895
LP
1338
1339 if (!success)
5d44db4a
LP
1340 d->result = strdup("failed");
1341
1342 goto finish;
7e4249b9 1343 }
5d44db4a
LP
1344#endif
1345
1346 log_error("Failed to parse message: %s", bus_error_message(&error));
7e4249b9
LP
1347 }
1348
5d44db4a 1349finish:
7e4249b9
LP
1350 dbus_error_free(&error);
1351 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1352}
1353
479ef5d3 1354static int enable_wait_for_jobs(DBusConnection *bus) {
7e4249b9 1355 DBusError error;
7e4249b9
LP
1356
1357 assert(bus);
7e4249b9 1358
f4579ce7
LP
1359 if (private_bus)
1360 return 0;
1361
7e4249b9 1362 dbus_error_init(&error);
7e4249b9
LP
1363 dbus_bus_add_match(bus,
1364 "type='signal',"
1365 "sender='org.freedesktop.systemd1',"
1366 "interface='org.freedesktop.systemd1.Manager',"
1367 "member='JobRemoved',"
1368 "path='/org/freedesktop/systemd1'",
1369 &error);
1370
1371 if (dbus_error_is_set(&error)) {
4cf5d675 1372 log_error("Failed to add match: %s", bus_error_message(&error));
a567261a
LP
1373 dbus_error_free(&error);
1374 return -EIO;
7e4249b9
LP
1375 }
1376
479ef5d3 1377 /* This is slightly dirty, since we don't undo the match registrations. */
a567261a 1378 return 0;
7e4249b9
LP
1379}
1380
479ef5d3
LP
1381static int wait_for_jobs(DBusConnection *bus, Set *s) {
1382 int r;
5e374895 1383 WaitData d;
479ef5d3
LP
1384
1385 assert(bus);
1386 assert(s);
1387
5e374895
LP
1388 zero(d);
1389 d.set = s;
5e374895
LP
1390
1391 if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL)) {
479ef5d3
LP
1392 log_error("Failed to add filter.");
1393 r = -ENOMEM;
1394 goto finish;
1395 }
1396
1397 while (!set_isempty(s) &&
1398 dbus_connection_read_write_dispatch(bus, -1))
1399 ;
1400
5d44db4a
LP
1401 if (!arg_quiet && d.result) {
1402 if (streq(d.result, "timeout"))
1403 log_error("Job timed out.");
1404 else if (streq(d.result, "canceled"))
1405 log_error("Job canceled.");
1406 else if (streq(d.result, "dependency"))
1407 log_error("A dependency job failed. See system logs for details.");
d68201e9 1408 else if (!streq(d.result, "done") && !streq(d.result, "skipped"))
5d44db4a
LP
1409 log_error("Job failed. See system logs and 'systemctl status' for details.");
1410 }
5e374895 1411
8e20e31a
LP
1412 if (streq_ptr(d.result, "timeout"))
1413 r = -ETIME;
1414 else if (streq_ptr(d.result, "canceled"))
1415 r = -ECANCELED;
d68201e9 1416 else if (!streq_ptr(d.result, "done") && !streq_ptr(d.result, "skipped"))
8e20e31a
LP
1417 r = -EIO;
1418 else
1419 r = 0;
1420
5d44db4a 1421 free(d.result);
479ef5d3
LP
1422
1423finish:
1424 /* This is slightly dirty, since we don't undo the filter registration. */
1425
1426 return r;
1427}
1428
e4b61340
LP
1429static int start_unit_one(
1430 DBusConnection *bus,
1431 const char *method,
1432 const char *name,
1433 const char *mode,
22f4096c 1434 DBusError *error,
e4b61340 1435 Set *s) {
7e4249b9 1436
7e4249b9 1437 DBusMessage *m = NULL, *reply = NULL;
45fb0699 1438 const char *path;
7e4249b9 1439 int r;
7e4249b9 1440
e4b61340
LP
1441 assert(bus);
1442 assert(method);
1443 assert(name);
1444 assert(mode);
22f4096c 1445 assert(error);
6e905d93 1446 assert(arg_no_block || s);
7e4249b9
LP
1447
1448 if (!(m = dbus_message_new_method_call(
1449 "org.freedesktop.systemd1",
1450 "/org/freedesktop/systemd1",
1451 "org.freedesktop.systemd1.Manager",
e4b61340 1452 method))) {
7e4249b9
LP
1453 log_error("Could not allocate message.");
1454 r = -ENOMEM;
1455 goto finish;
1456 }
1457
1458 if (!dbus_message_append_args(m,
e4b61340 1459 DBUS_TYPE_STRING, &name,
7e4249b9
LP
1460 DBUS_TYPE_STRING, &mode,
1461 DBUS_TYPE_INVALID)) {
1462 log_error("Could not append arguments to message.");
1463 r = -ENOMEM;
1464 goto finish;
1465 }
1466
22f4096c 1467 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, error))) {
e4b61340 1468
22f4096c 1469 if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(error)) {
e4b61340
LP
1470 /* There's always a fallback possible for
1471 * legacy actions. */
706900b7 1472 r = -EADDRNOTAVAIL;
e4b61340
LP
1473 goto finish;
1474 }
1475
22f4096c 1476 log_error("Failed to issue method call: %s", bus_error_message(error));
7e4249b9
LP
1477 r = -EIO;
1478 goto finish;
1479 }
1480
22f4096c 1481 if (!dbus_message_get_args(reply, error,
45fb0699
LP
1482 DBUS_TYPE_OBJECT_PATH, &path,
1483 DBUS_TYPE_INVALID)) {
22f4096c 1484 log_error("Failed to parse reply: %s", bus_error_message(error));
45fb0699
LP
1485 r = -EIO;
1486 goto finish;
1487 }
1488
ee5762e3 1489 if (need_daemon_reload(bus, name))
729e3769
LP
1490 log_warning("Warning: Unit file of created job changed on disk, 'systemctl %s daemon-reload' recommended.",
1491 arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
45fb0699 1492
6e905d93 1493 if (!arg_no_block) {
e4b61340 1494 char *p;
7e4249b9 1495
7e4249b9
LP
1496 if (!(p = strdup(path))) {
1497 log_error("Failed to duplicate path.");
1498 r = -ENOMEM;
1499 goto finish;
1500 }
1501
1502 if ((r = set_put(s, p)) < 0) {
e4b61340 1503 free(p);
7e4249b9
LP
1504 log_error("Failed to add path to set.");
1505 goto finish;
1506 }
e4b61340 1507 }
7e4249b9 1508
706900b7 1509 r = 0;
22f4096c 1510
7e4249b9 1511finish:
7e4249b9
LP
1512 if (m)
1513 dbus_message_unref(m);
1514
1515 if (reply)
1516 dbus_message_unref(reply);
1517
7e4249b9
LP
1518 return r;
1519}
1520
514f4ef5
LP
1521static enum action verb_to_action(const char *verb) {
1522 if (streq(verb, "halt"))
1523 return ACTION_HALT;
1524 else if (streq(verb, "poweroff"))
1525 return ACTION_POWEROFF;
1526 else if (streq(verb, "reboot"))
1527 return ACTION_REBOOT;
20b09ca7
LP
1528 else if (streq(verb, "kexec"))
1529 return ACTION_KEXEC;
514f4ef5
LP
1530 else if (streq(verb, "rescue"))
1531 return ACTION_RESCUE;
1532 else if (streq(verb, "emergency"))
1533 return ACTION_EMERGENCY;
1534 else if (streq(verb, "default"))
1535 return ACTION_DEFAULT;
20b09ca7
LP
1536 else if (streq(verb, "exit"))
1537 return ACTION_EXIT;
514f4ef5
LP
1538 else
1539 return ACTION_INVALID;
1540}
1541
729e3769 1542static int start_unit(DBusConnection *bus, char **args) {
e4b61340
LP
1543
1544 static const char * const table[_ACTION_MAX] = {
514f4ef5
LP
1545 [ACTION_HALT] = SPECIAL_HALT_TARGET,
1546 [ACTION_POWEROFF] = SPECIAL_POWEROFF_TARGET,
1547 [ACTION_REBOOT] = SPECIAL_REBOOT_TARGET,
20b09ca7 1548 [ACTION_KEXEC] = SPECIAL_KEXEC_TARGET,
514f4ef5
LP
1549 [ACTION_RUNLEVEL2] = SPECIAL_RUNLEVEL2_TARGET,
1550 [ACTION_RUNLEVEL3] = SPECIAL_RUNLEVEL3_TARGET,
1551 [ACTION_RUNLEVEL4] = SPECIAL_RUNLEVEL4_TARGET,
1552 [ACTION_RUNLEVEL5] = SPECIAL_RUNLEVEL5_TARGET,
1553 [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET,
f057408c 1554 [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_TARGET,
20b09ca7
LP
1555 [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET,
1556 [ACTION_EXIT] = SPECIAL_EXIT_TARGET
e4b61340
LP
1557 };
1558
22f4096c 1559 int r, ret = 0;
514f4ef5 1560 const char *method, *mode, *one_name;
e4b61340 1561 Set *s = NULL;
22f4096c 1562 DBusError error;
729e3769 1563 char **name;
22f4096c
LP
1564
1565 dbus_error_init(&error);
e4b61340 1566
514f4ef5
LP
1567 assert(bus);
1568
729e3769 1569 agent_open_if_enabled();
501fc174 1570
e4b61340
LP
1571 if (arg_action == ACTION_SYSTEMCTL) {
1572 method =
a76f7be2
LP
1573 streq(args[0], "stop") ||
1574 streq(args[0], "condstop") ? "StopUnit" :
6f28c033
LP
1575 streq(args[0], "reload") ? "ReloadUnit" :
1576 streq(args[0], "restart") ? "RestartUnit" :
d68201e9 1577
aa5939a3
MS
1578 streq(args[0], "try-restart") ||
1579 streq(args[0], "condrestart") ? "TryRestartUnit" :
d68201e9 1580
6f28c033 1581 streq(args[0], "reload-or-restart") ? "ReloadOrRestartUnit" :
d68201e9 1582
9d8a57ff 1583 streq(args[0], "reload-or-try-restart") ||
64e5f1b7 1584 streq(args[0], "condreload") ||
d68201e9 1585
aa5939a3 1586 streq(args[0], "force-reload") ? "ReloadOrTryRestartUnit" :
6f28c033 1587 "StartUnit";
e4b61340
LP
1588
1589 mode =
514f4ef5
LP
1590 (streq(args[0], "isolate") ||
1591 streq(args[0], "rescue") ||
e67c3609 1592 streq(args[0], "emergency")) ? "isolate" : arg_job_mode;
e4b61340 1593
514f4ef5 1594 one_name = table[verb_to_action(args[0])];
e4b61340 1595
e4b61340
LP
1596 } else {
1597 assert(arg_action < ELEMENTSOF(table));
1598 assert(table[arg_action]);
1599
1600 method = "StartUnit";
514f4ef5
LP
1601
1602 mode = (arg_action == ACTION_EMERGENCY ||
6f0d624e
LP
1603 arg_action == ACTION_RESCUE ||
1604 arg_action == ACTION_RUNLEVEL2 ||
1605 arg_action == ACTION_RUNLEVEL3 ||
1606 arg_action == ACTION_RUNLEVEL4 ||
1607 arg_action == ACTION_RUNLEVEL5) ? "isolate" : "replace";
514f4ef5
LP
1608
1609 one_name = table[arg_action];
1610 }
1611
6e905d93 1612 if (!arg_no_block) {
22f4096c
LP
1613 if ((ret = enable_wait_for_jobs(bus)) < 0) {
1614 log_error("Could not watch jobs: %s", strerror(-ret));
514f4ef5
LP
1615 goto finish;
1616 }
1617
1618 if (!(s = set_new(string_hash_func, string_compare_func))) {
1619 log_error("Failed to allocate set.");
22f4096c 1620 ret = -ENOMEM;
514f4ef5
LP
1621 goto finish;
1622 }
e4b61340
LP
1623 }
1624
514f4ef5 1625 if (one_name) {
22f4096c 1626 if ((ret = start_unit_one(bus, method, one_name, mode, &error, s)) <= 0)
514f4ef5
LP
1627 goto finish;
1628 } else {
729e3769
LP
1629 STRV_FOREACH(name, args+1)
1630 if ((r = start_unit_one(bus, method, *name, mode, &error, s)) != 0) {
706900b7 1631 ret = translate_bus_error_to_exit_status(r, &error);
22f4096c
LP
1632 dbus_error_free(&error);
1633 }
e4b61340
LP
1634 }
1635
6e905d93 1636 if (!arg_no_block)
22f4096c
LP
1637 if ((r = wait_for_jobs(bus, s)) < 0) {
1638 ret = r;
fbc43921 1639 goto finish;
22f4096c 1640 }
514f4ef5 1641
e4b61340
LP
1642finish:
1643 if (s)
1644 set_free_free(s);
1645
22f4096c
LP
1646 dbus_error_free(&error);
1647
1648 return ret;
e4b61340
LP
1649}
1650
729e3769 1651static int start_special(DBusConnection *bus, char **args) {
983d9c90
LP
1652 int r;
1653
514f4ef5
LP
1654 assert(bus);
1655 assert(args);
1656
20b09ca7
LP
1657 if (arg_force &&
1658 (streq(args[0], "halt") ||
1659 streq(args[0], "poweroff") ||
1660 streq(args[0], "reboot") ||
1661 streq(args[0], "kexec") ||
1662 streq(args[0], "exit")))
729e3769 1663 return daemon_reload(bus, args);
20b09ca7 1664
729e3769 1665 r = start_unit(bus, args);
983d9c90
LP
1666
1667 if (r >= 0)
1668 warn_wall(verb_to_action(args[0]));
514f4ef5 1669
983d9c90 1670 return r;
514f4ef5
LP
1671}
1672
729e3769 1673static int check_unit(DBusConnection *bus, char **args) {
0183528f
LP
1674 DBusMessage *m = NULL, *reply = NULL;
1675 const char
1676 *interface = "org.freedesktop.systemd1.Unit",
1677 *property = "ActiveState";
22f4096c 1678 int r = 3; /* According to LSB: "program is not running" */
0183528f 1679 DBusError error;
729e3769 1680 char **name;
0183528f
LP
1681
1682 assert(bus);
1683 assert(args);
1684
1685 dbus_error_init(&error);
1686
729e3769 1687 STRV_FOREACH(name, args+1) {
0183528f
LP
1688 const char *path = NULL;
1689 const char *state;
1690 DBusMessageIter iter, sub;
1691
1692 if (!(m = dbus_message_new_method_call(
1693 "org.freedesktop.systemd1",
1694 "/org/freedesktop/systemd1",
1695 "org.freedesktop.systemd1.Manager",
1696 "GetUnit"))) {
1697 log_error("Could not allocate message.");
1698 r = -ENOMEM;
1699 goto finish;
1700 }
1701
1702 if (!dbus_message_append_args(m,
729e3769 1703 DBUS_TYPE_STRING, name,
0183528f
LP
1704 DBUS_TYPE_INVALID)) {
1705 log_error("Could not append arguments to message.");
1706 r = -ENOMEM;
1707 goto finish;
1708 }
1709
1710 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1711
1712 /* Hmm, cannot figure out anything about this unit... */
1713 if (!arg_quiet)
1714 puts("unknown");
1715
ed2d7a44 1716 dbus_error_free(&error);
8a0867d6 1717 dbus_message_unref(m);
78e39b43 1718 m = NULL;
0183528f
LP
1719 continue;
1720 }
1721
1722 if (!dbus_message_get_args(reply, &error,
1723 DBUS_TYPE_OBJECT_PATH, &path,
1724 DBUS_TYPE_INVALID)) {
4cf5d675 1725 log_error("Failed to parse reply: %s", bus_error_message(&error));
0183528f
LP
1726 r = -EIO;
1727 goto finish;
1728 }
1729
1730 dbus_message_unref(m);
1731 if (!(m = dbus_message_new_method_call(
1732 "org.freedesktop.systemd1",
1733 path,
1734 "org.freedesktop.DBus.Properties",
1735 "Get"))) {
1736 log_error("Could not allocate message.");
1737 r = -ENOMEM;
1738 goto finish;
1739 }
1740
1741 if (!dbus_message_append_args(m,
1742 DBUS_TYPE_STRING, &interface,
1743 DBUS_TYPE_STRING, &property,
1744 DBUS_TYPE_INVALID)) {
1745 log_error("Could not append arguments to message.");
1746 r = -ENOMEM;
1747 goto finish;
1748 }
1749
1750 dbus_message_unref(reply);
1751 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 1752 log_error("Failed to issue method call: %s", bus_error_message(&error));
0183528f
LP
1753 r = -EIO;
1754 goto finish;
1755 }
1756
1757 if (!dbus_message_iter_init(reply, &iter) ||
1758 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
1759 log_error("Failed to parse reply.");
1760 r = -EIO;
1761 goto finish;
1762 }
1763
1764 dbus_message_iter_recurse(&iter, &sub);
1765
1766 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
1767 log_error("Failed to parse reply.");
1768 r = -EIO;
1769 goto finish;
1770 }
1771
1772 dbus_message_iter_get_basic(&sub, &state);
1773
1774 if (!arg_quiet)
1775 puts(state);
1776
f1e36d67 1777 if (streq(state, "active") || streq(state, "reloading"))
0183528f
LP
1778 r = 0;
1779
1780 dbus_message_unref(m);
1781 dbus_message_unref(reply);
1782 m = reply = NULL;
1783 }
1784
1785finish:
1786 if (m)
1787 dbus_message_unref(m);
1788
1789 if (reply)
1790 dbus_message_unref(reply);
1791
1792 dbus_error_free(&error);
1793
1794 return r;
48220598
LP
1795}
1796
729e3769 1797static int kill_unit(DBusConnection *bus, char **args) {
f8440af5 1798 DBusMessage *m = NULL;
8a0867d6
LP
1799 int r = 0;
1800 DBusError error;
729e3769 1801 char **name;
8a0867d6
LP
1802
1803 assert(bus);
1804 assert(args);
1805
1806 dbus_error_init(&error);
1807
1808 if (!arg_kill_who)
1809 arg_kill_who = "all";
1810
1811 if (!arg_kill_mode)
1812 arg_kill_mode = streq(arg_kill_who, "all") ? "control-group" : "process";
1813
729e3769 1814 STRV_FOREACH(name, args+1) {
f8440af5 1815 DBusMessage *reply;
8a0867d6
LP
1816
1817 if (!(m = dbus_message_new_method_call(
1818 "org.freedesktop.systemd1",
1819 "/org/freedesktop/systemd1",
1820 "org.freedesktop.systemd1.Manager",
1821 "KillUnit"))) {
1822 log_error("Could not allocate message.");
1823 r = -ENOMEM;
1824 goto finish;
1825 }
1826
1827 if (!dbus_message_append_args(m,
729e3769 1828 DBUS_TYPE_STRING, name,
8a0867d6
LP
1829 DBUS_TYPE_STRING, &arg_kill_who,
1830 DBUS_TYPE_STRING, &arg_kill_mode,
1831 DBUS_TYPE_INT32, &arg_signal,
1832 DBUS_TYPE_INVALID)) {
1833 log_error("Could not append arguments to message.");
1834 r = -ENOMEM;
1835 goto finish;
1836 }
1837
1838 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1839 log_error("Failed to issue method call: %s", bus_error_message(&error));
1840 dbus_error_free(&error);
1841 r = -EIO;
1842 }
1843
1844 dbus_message_unref(m);
1845
1846 if (reply)
1847 dbus_message_unref(reply);
1848 m = reply = NULL;
1849 }
1850
1851finish:
1852 if (m)
1853 dbus_message_unref(m);
1854
8a0867d6
LP
1855 dbus_error_free(&error);
1856
1857 return r;
1858}
1859
582a507f 1860typedef struct ExecStatusInfo {
0129173a
LP
1861 char *name;
1862
582a507f
LP
1863 char *path;
1864 char **argv;
1865
b708e7ce
LP
1866 bool ignore;
1867
582a507f
LP
1868 usec_t start_timestamp;
1869 usec_t exit_timestamp;
1870 pid_t pid;
1871 int code;
1872 int status;
1873
1874 LIST_FIELDS(struct ExecStatusInfo, exec);
1875} ExecStatusInfo;
1876
1877static void exec_status_info_free(ExecStatusInfo *i) {
1878 assert(i);
1879
0129173a 1880 free(i->name);
582a507f
LP
1881 free(i->path);
1882 strv_free(i->argv);
1883 free(i);
1884}
1885
1886static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i) {
b21a0ef8 1887 uint64_t start_timestamp, exit_timestamp, start_timestamp_monotonic, exit_timestamp_monotonic;
582a507f
LP
1888 DBusMessageIter sub2, sub3;
1889 const char*path;
1890 unsigned n;
1891 uint32_t pid;
1892 int32_t code, status;
b708e7ce 1893 dbus_bool_t ignore;
582a507f
LP
1894
1895 assert(i);
1896 assert(i);
1897
1898 if (dbus_message_iter_get_arg_type(sub) != DBUS_TYPE_STRUCT)
1899 return -EIO;
1900
1901 dbus_message_iter_recurse(sub, &sub2);
1902
1903 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0)
1904 return -EIO;
1905
1906 if (!(i->path = strdup(path)))
1907 return -ENOMEM;
1908
1909 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_ARRAY ||
1910 dbus_message_iter_get_element_type(&sub2) != DBUS_TYPE_STRING)
1911 return -EIO;
1912
1913 n = 0;
1914 dbus_message_iter_recurse(&sub2, &sub3);
1915 while (dbus_message_iter_get_arg_type(&sub3) != DBUS_TYPE_INVALID) {
1916 assert(dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_STRING);
1917 dbus_message_iter_next(&sub3);
1918 n++;
1919 }
1920
1921
1922 if (!(i->argv = new0(char*, n+1)))
1923 return -ENOMEM;
1924
1925 n = 0;
1926 dbus_message_iter_recurse(&sub2, &sub3);
1927 while (dbus_message_iter_get_arg_type(&sub3) != DBUS_TYPE_INVALID) {
1928 const char *s;
1929
1930 assert(dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_STRING);
1931 dbus_message_iter_get_basic(&sub3, &s);
1932 dbus_message_iter_next(&sub3);
1933
1934 if (!(i->argv[n++] = strdup(s)))
1935 return -ENOMEM;
1936 }
1937
1938 if (!dbus_message_iter_next(&sub2) ||
b708e7ce 1939 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, true) < 0 ||
582a507f 1940 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &start_timestamp, true) < 0 ||
b21a0ef8 1941 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &start_timestamp_monotonic, true) < 0 ||
582a507f 1942 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &exit_timestamp, true) < 0 ||
b21a0ef8 1943 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &exit_timestamp_monotonic, true) < 0 ||
582a507f
LP
1944 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, true) < 0 ||
1945 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &code, true) < 0 ||
1946 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &status, false) < 0)
1947 return -EIO;
1948
b708e7ce 1949 i->ignore = ignore;
582a507f
LP
1950 i->start_timestamp = (usec_t) start_timestamp;
1951 i->exit_timestamp = (usec_t) exit_timestamp;
1952 i->pid = (pid_t) pid;
1953 i->code = code;
1954 i->status = status;
1955
1956 return 0;
1957}
1958
61cbdc4b
LP
1959typedef struct UnitStatusInfo {
1960 const char *id;
1961 const char *load_state;
1962 const char *active_state;
1963 const char *sub_state;
a4375746 1964 const char *unit_file_state;
61cbdc4b
LP
1965
1966 const char *description;
4a9e2fff 1967 const char *following;
61cbdc4b 1968
c31b4423 1969 const char *path;
61cbdc4b
LP
1970 const char *default_control_group;
1971
9f39404c
LP
1972 const char *load_error;
1973
584be568
LP
1974 usec_t inactive_exit_timestamp;
1975 usec_t active_enter_timestamp;
1976 usec_t active_exit_timestamp;
1977 usec_t inactive_enter_timestamp;
1978
45fb0699
LP
1979 bool need_daemon_reload;
1980
61cbdc4b
LP
1981 /* Service */
1982 pid_t main_pid;
1983 pid_t control_pid;
1984 const char *status_text;
d06dacd0 1985 bool running:1;
07459bb6 1986#ifdef HAVE_SYSV_COMPAT
d06dacd0 1987 bool is_sysv:1;
07459bb6 1988#endif
61cbdc4b
LP
1989
1990 usec_t start_timestamp;
1991 usec_t exit_timestamp;
1992
1993 int exit_code, exit_status;
1994
90bbc946
LP
1995 usec_t condition_timestamp;
1996 bool condition_result;
1997
61cbdc4b
LP
1998 /* Socket */
1999 unsigned n_accepted;
2000 unsigned n_connections;
b8131a87 2001 bool accept;
61cbdc4b
LP
2002
2003 /* Device */
2004 const char *sysfs_path;
2005
2006 /* Mount, Automount */
2007 const char *where;
2008
2009 /* Swap */
2010 const char *what;
582a507f
LP
2011
2012 LIST_HEAD(ExecStatusInfo, exec);
61cbdc4b
LP
2013} UnitStatusInfo;
2014
2015static void print_status_info(UnitStatusInfo *i) {
582a507f 2016 ExecStatusInfo *p;
2ee68f72 2017 const char *on, *off, *ss;
584be568
LP
2018 usec_t timestamp;
2019 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
2020 char since2[FORMAT_TIMESTAMP_MAX], *s2;
582a507f 2021
61cbdc4b
LP
2022 assert(i);
2023
2024 /* This shows pretty information about a unit. See
2025 * print_property() for a low-level property printer */
2026
2027 printf("%s", strna(i->id));
2028
2029 if (i->description && !streq_ptr(i->id, i->description))
2030 printf(" - %s", i->description);
2031
2032 printf("\n");
2033
4a9e2fff
LP
2034 if (i->following)
2035 printf("\t Follow: unit currently follows state of %s\n", i->following);
2036
00dc5d76
LP
2037 if (streq_ptr(i->load_state, "failed") ||
2038 streq_ptr(i->load_state, "banned")) {
c31b4423
LP
2039 on = ansi_highlight(true);
2040 off = ansi_highlight(false);
2041 } else
2042 on = off = "";
2043
9f39404c
LP
2044 if (i->load_error)
2045 printf("\t Loaded: %s%s%s (Reason: %s)\n", on, strna(i->load_state), off, i->load_error);
a4375746
LP
2046 else if (i->path && i->unit_file_state)
2047 printf("\t Loaded: %s%s%s (%s; %s)\n", on, strna(i->load_state), off, i->path, i->unit_file_state);
9f39404c 2048 else if (i->path)
c31b4423 2049 printf("\t Loaded: %s%s%s (%s)\n", on, strna(i->load_state), off, i->path);
61cbdc4b 2050 else
c31b4423 2051 printf("\t Loaded: %s%s%s\n", on, strna(i->load_state), off);
61cbdc4b 2052
2ee68f72
LP
2053 ss = streq_ptr(i->active_state, i->sub_state) ? NULL : i->sub_state;
2054
fdf20a31 2055 if (streq_ptr(i->active_state, "failed")) {
2ee68f72
LP
2056 on = ansi_highlight(true);
2057 off = ansi_highlight(false);
2058 } else if (streq_ptr(i->active_state, "active") || streq_ptr(i->active_state, "reloading")) {
2059 on = ansi_highlight_green(true);
2060 off = ansi_highlight_green(false);
2061 } else
2062 on = off = "";
2063
2064 if (ss)
584be568 2065 printf("\t Active: %s%s (%s)%s",
2ee68f72
LP
2066 on,
2067 strna(i->active_state),
2068 ss,
2069 off);
2070 else
584be568 2071 printf("\t Active: %s%s%s",
2ee68f72
LP
2072 on,
2073 strna(i->active_state),
2074 off);
61cbdc4b 2075
584be568
LP
2076 timestamp = (streq_ptr(i->active_state, "active") ||
2077 streq_ptr(i->active_state, "reloading")) ? i->active_enter_timestamp :
2078 (streq_ptr(i->active_state, "inactive") ||
fdf20a31 2079 streq_ptr(i->active_state, "failed")) ? i->inactive_enter_timestamp :
584be568
LP
2080 streq_ptr(i->active_state, "activating") ? i->inactive_exit_timestamp :
2081 i->active_exit_timestamp;
2082
2083 s1 = format_timestamp_pretty(since1, sizeof(since1), timestamp);
2084 s2 = format_timestamp(since2, sizeof(since2), timestamp);
2085
2086 if (s1)
538da63d 2087 printf(" since %s; %s\n", s2, s1);
584be568 2088 else if (s2)
538da63d 2089 printf(" since %s\n", s2);
584be568
LP
2090 else
2091 printf("\n");
2092
90bbc946
LP
2093 if (!i->condition_result && i->condition_timestamp > 0) {
2094 s1 = format_timestamp_pretty(since1, sizeof(since1), i->condition_timestamp);
2095 s2 = format_timestamp(since2, sizeof(since2), i->condition_timestamp);
2096
2097 if (s1)
2098 printf("\t start condition failed at %s; %s\n", s2, s1);
2099 else if (s2)
2100 printf("\t start condition failed at %s\n", s2);
2101 }
2102
61cbdc4b
LP
2103 if (i->sysfs_path)
2104 printf("\t Device: %s\n", i->sysfs_path);
9feeba4b 2105 if (i->where)
61cbdc4b 2106 printf("\t Where: %s\n", i->where);
9feeba4b 2107 if (i->what)
61cbdc4b
LP
2108 printf("\t What: %s\n", i->what);
2109
b8131a87 2110 if (i->accept)
61cbdc4b
LP
2111 printf("\tAccepted: %u; Connected: %u\n", i->n_accepted, i->n_connections);
2112
582a507f
LP
2113 LIST_FOREACH(exec, p, i->exec) {
2114 char *t;
9a57c629 2115 bool good;
582a507f
LP
2116
2117 /* Only show exited processes here */
2118 if (p->code == 0)
2119 continue;
2120
2121 t = strv_join(p->argv, " ");
9a57c629 2122 printf("\t Process: %u %s=%s ", p->pid, p->name, strna(t));
582a507f
LP
2123 free(t);
2124
9a57c629
LP
2125#ifdef HAVE_SYSV_COMPAT
2126 if (i->is_sysv)
2127 good = is_clean_exit_lsb(p->code, p->status);
2128 else
2129#endif
2130 good = is_clean_exit(p->code, p->status);
2131
2132 if (!good) {
2133 on = ansi_highlight(true);
2134 off = ansi_highlight(false);
2135 } else
2136 on = off = "";
2137
2138 printf("%s(code=%s, ", on, sigchld_code_to_string(p->code));
2139
d06dacd0
LP
2140 if (p->code == CLD_EXITED) {
2141 const char *c;
2142
582a507f 2143 printf("status=%i", p->status);
d06dacd0 2144
07459bb6 2145#ifdef HAVE_SYSV_COMPAT
d06dacd0 2146 if ((c = exit_status_to_string(p->status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
07459bb6
FF
2147#else
2148 if ((c = exit_status_to_string(p->status, EXIT_STATUS_SYSTEMD)))
2149#endif
d06dacd0
LP
2150 printf("/%s", c);
2151
2152 } else
582a507f 2153 printf("signal=%s", signal_to_string(p->status));
9a57c629
LP
2154
2155 printf(")%s\n", off);
2156
582a507f
LP
2157 if (i->main_pid == p->pid &&
2158 i->start_timestamp == p->start_timestamp &&
2159 i->exit_timestamp == p->start_timestamp)
2160 /* Let's not show this twice */
2161 i->main_pid = 0;
2162
2163 if (p->pid == i->control_pid)
2164 i->control_pid = 0;
2165 }
2166
61cbdc4b
LP
2167 if (i->main_pid > 0 || i->control_pid > 0) {
2168 printf("\t");
2169
2170 if (i->main_pid > 0) {
f3d41013 2171 printf("Main PID: %u", (unsigned) i->main_pid);
61cbdc4b
LP
2172
2173 if (i->running) {
2174 char *t = NULL;
2175 get_process_name(i->main_pid, &t);
2176 if (t) {
2177 printf(" (%s)", t);
2178 free(t);
2179 }
6d4fc029 2180 } else if (i->exit_code > 0) {
61cbdc4b
LP
2181 printf(" (code=%s, ", sigchld_code_to_string(i->exit_code));
2182
d06dacd0
LP
2183 if (i->exit_code == CLD_EXITED) {
2184 const char *c;
2185
61cbdc4b 2186 printf("status=%i", i->exit_status);
d06dacd0 2187
07459bb6 2188#ifdef HAVE_SYSV_COMPAT
d06dacd0 2189 if ((c = exit_status_to_string(i->exit_status, i->is_sysv ? EXIT_STATUS_LSB : EXIT_STATUS_SYSTEMD)))
07459bb6
FF
2190#else
2191 if ((c = exit_status_to_string(i->exit_status, EXIT_STATUS_SYSTEMD)))
2192#endif
d06dacd0
LP
2193 printf("/%s", c);
2194
2195 } else
582a507f 2196 printf("signal=%s", signal_to_string(i->exit_status));
6d4fc029
LP
2197 printf(")");
2198 }
61cbdc4b
LP
2199 }
2200
2201 if (i->main_pid > 0 && i->control_pid > 0)
2202 printf(";");
2203
2204 if (i->control_pid > 0) {
2205 char *t = NULL;
2206
2207 printf(" Control: %u", (unsigned) i->control_pid);
2208
2209 get_process_name(i->control_pid, &t);
2210 if (t) {
2211 printf(" (%s)", t);
2212 free(t);
2213 }
2214 }
2215
2216 printf("\n");
2217 }
2218
17bb7382
LP
2219 if (i->status_text)
2220 printf("\t Status: \"%s\"\n", i->status_text);
2221
c59760ee 2222 if (i->default_control_group) {
ab35fb1b
LP
2223 unsigned c;
2224
61cbdc4b 2225 printf("\t CGroup: %s\n", i->default_control_group);
ab35fb1b 2226
a8f11321
LP
2227 if (arg_transport != TRANSPORT_SSH) {
2228 if ((c = columns()) > 18)
2229 c -= 18;
2230 else
2231 c = 0;
ab35fb1b 2232
a8f11321
LP
2233 show_cgroup_by_path(i->default_control_group, "\t\t ", c);
2234 }
c59760ee 2235 }
45fb0699
LP
2236
2237 if (i->need_daemon_reload)
2cc59dbf
LP
2238 printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
2239 ansi_highlight(true),
2240 ansi_highlight(false),
729e3769 2241 arg_scope == UNIT_FILE_SYSTEM ? "--system" : "--user");
61cbdc4b
LP
2242}
2243
2244static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) {
2245
a4c279f8
LP
2246 assert(name);
2247 assert(iter);
2248 assert(i);
2249
61cbdc4b
LP
2250 switch (dbus_message_iter_get_arg_type(iter)) {
2251
2252 case DBUS_TYPE_STRING: {
2253 const char *s;
2254
2255 dbus_message_iter_get_basic(iter, &s);
2256
a4c279f8 2257 if (!isempty(s)) {
61cbdc4b
LP
2258 if (streq(name, "Id"))
2259 i->id = s;
2260 else if (streq(name, "LoadState"))
2261 i->load_state = s;
2262 else if (streq(name, "ActiveState"))
2263 i->active_state = s;
2264 else if (streq(name, "SubState"))
2265 i->sub_state = s;
2266 else if (streq(name, "Description"))
2267 i->description = s;
2268 else if (streq(name, "FragmentPath"))
c31b4423 2269 i->path = s;
07459bb6 2270#ifdef HAVE_SYSV_COMPAT
d06dacd0
LP
2271 else if (streq(name, "SysVPath")) {
2272 i->is_sysv = true;
c31b4423 2273 i->path = s;
07459bb6
FF
2274 }
2275#endif
2276 else if (streq(name, "DefaultControlGroup"))
61cbdc4b
LP
2277 i->default_control_group = s;
2278 else if (streq(name, "StatusText"))
2279 i->status_text = s;
2280 else if (streq(name, "SysFSPath"))
2281 i->sysfs_path = s;
2282 else if (streq(name, "Where"))
2283 i->where = s;
2284 else if (streq(name, "What"))
2285 i->what = s;
4a9e2fff
LP
2286 else if (streq(name, "Following"))
2287 i->following = s;
a4375746
LP
2288 else if (streq(name, "UnitFileState"))
2289 i->unit_file_state = s;
61cbdc4b
LP
2290 }
2291
2292 break;
2293 }
2294
b8131a87
LP
2295 case DBUS_TYPE_BOOLEAN: {
2296 dbus_bool_t b;
2297
2298 dbus_message_iter_get_basic(iter, &b);
2299
2300 if (streq(name, "Accept"))
2301 i->accept = b;
45fb0699
LP
2302 else if (streq(name, "NeedDaemonReload"))
2303 i->need_daemon_reload = b;
90bbc946
LP
2304 else if (streq(name, "ConditionResult"))
2305 i->condition_result = b;
b8131a87
LP
2306
2307 break;
2308 }
2309
61cbdc4b
LP
2310 case DBUS_TYPE_UINT32: {
2311 uint32_t u;
2312
2313 dbus_message_iter_get_basic(iter, &u);
2314
2315 if (streq(name, "MainPID")) {
2316 if (u > 0) {
2317 i->main_pid = (pid_t) u;
2318 i->running = true;
2319 }
2320 } else if (streq(name, "ControlPID"))
2321 i->control_pid = (pid_t) u;
2322 else if (streq(name, "ExecMainPID")) {
2323 if (u > 0)
2324 i->main_pid = (pid_t) u;
2325 } else if (streq(name, "NAccepted"))
2326 i->n_accepted = u;
2327 else if (streq(name, "NConnections"))
2328 i->n_connections = u;
2329
2330 break;
2331 }
2332
2333 case DBUS_TYPE_INT32: {
2334 int32_t j;
2335
2336 dbus_message_iter_get_basic(iter, &j);
2337
2338 if (streq(name, "ExecMainCode"))
2339 i->exit_code = (int) j;
2340 else if (streq(name, "ExecMainStatus"))
2341 i->exit_status = (int) j;
2342
2343 break;
2344 }
2345
2346 case DBUS_TYPE_UINT64: {
2347 uint64_t u;
2348
2349 dbus_message_iter_get_basic(iter, &u);
2350
2351 if (streq(name, "ExecMainStartTimestamp"))
2352 i->start_timestamp = (usec_t) u;
2353 else if (streq(name, "ExecMainExitTimestamp"))
2354 i->exit_timestamp = (usec_t) u;
584be568
LP
2355 else if (streq(name, "ActiveEnterTimestamp"))
2356 i->active_enter_timestamp = (usec_t) u;
2357 else if (streq(name, "InactiveEnterTimestamp"))
2358 i->inactive_enter_timestamp = (usec_t) u;
2359 else if (streq(name, "InactiveExitTimestamp"))
2360 i->inactive_exit_timestamp = (usec_t) u;
2361 else if (streq(name, "ActiveExitTimestamp"))
2362 i->active_exit_timestamp = (usec_t) u;
90bbc946
LP
2363 else if (streq(name, "ConditionTimestamp"))
2364 i->condition_timestamp = (usec_t) u;
61cbdc4b
LP
2365
2366 break;
2367 }
582a507f
LP
2368
2369 case DBUS_TYPE_ARRAY: {
2370
2371 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT &&
2372 startswith(name, "Exec")) {
2373 DBusMessageIter sub;
2374
2375 dbus_message_iter_recurse(iter, &sub);
2376 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
2377 ExecStatusInfo *info;
2378 int r;
2379
2380 if (!(info = new0(ExecStatusInfo, 1)))
2381 return -ENOMEM;
2382
0129173a
LP
2383 if (!(info->name = strdup(name))) {
2384 free(info);
2385 return -ENOMEM;
2386 }
2387
582a507f
LP
2388 if ((r = exec_status_info_deserialize(&sub, info)) < 0) {
2389 free(info);
2390 return r;
2391 }
2392
2393 LIST_PREPEND(ExecStatusInfo, exec, i->exec, info);
2394
2395 dbus_message_iter_next(&sub);
2396 }
2397 }
2398
2399 break;
2400 }
9f39404c
LP
2401
2402 case DBUS_TYPE_STRUCT: {
2403
2404 if (streq(name, "LoadError")) {
2405 DBusMessageIter sub;
2406 const char *n, *message;
2407 int r;
2408
2409 dbus_message_iter_recurse(iter, &sub);
2410
2411 r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &n, true);
2412 if (r < 0)
2413 return r;
2414
2415 r = bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &message, false);
2416 if (r < 0)
2417 return r;
2418
2419 if (!isempty(message))
2420 i->load_error = message;
2421 }
2422
2423 break;
2424 }
61cbdc4b
LP
2425 }
2426
2427 return 0;
2428}
2429
48220598
LP
2430static int print_property(const char *name, DBusMessageIter *iter) {
2431 assert(name);
2432 assert(iter);
2433
61cbdc4b
LP
2434 /* This is a low-level property printer, see
2435 * print_status_info() for the nicer output */
2436
ea4a240d 2437 if (arg_property && !strv_find(arg_property, name))
48220598
LP
2438 return 0;
2439
2440 switch (dbus_message_iter_get_arg_type(iter)) {
2441
48220598
LP
2442 case DBUS_TYPE_STRUCT: {
2443 DBusMessageIter sub;
2444 dbus_message_iter_recurse(iter, &sub);
2445
ebf57b80 2446 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "Job")) {
48220598
LP
2447 uint32_t u;
2448
2449 dbus_message_iter_get_basic(&sub, &u);
2450
2451 if (u)
2452 printf("%s=%u\n", name, (unsigned) u);
2453 else if (arg_all)
2454 printf("%s=\n", name);
2455
2456 return 0;
ebf57b80 2457 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Unit")) {
48220598
LP
2458 const char *s;
2459
2460 dbus_message_iter_get_basic(&sub, &s);
2461
2462 if (arg_all || s[0])
2463 printf("%s=%s\n", name, s);
2464
2465 return 0;
9f39404c
LP
2466 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "LoadError")) {
2467 const char *a = NULL, *b = NULL;
2468
f786e80d 2469 if (bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &a, true) >= 0)
9f39404c
LP
2470 bus_iter_get_basic_and_next(&sub, DBUS_TYPE_STRING, &b, false);
2471
2472 if (arg_all || !isempty(a) || !isempty(b))
2473 printf("%s=%s \"%s\"\n", name, strempty(a), strempty(b));
f786e80d
LP
2474
2475 return 0;
48220598
LP
2476 }
2477
2478 break;
2479 }
2480
2481 case DBUS_TYPE_ARRAY:
2482
a4c279f8 2483 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "EnvironmentFiles")) {
8c7be95e
LP
2484 DBusMessageIter sub, sub2;
2485
2486 dbus_message_iter_recurse(iter, &sub);
2487 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
2488 const char *path;
2489 dbus_bool_t ignore;
2490
2491 dbus_message_iter_recurse(&sub, &sub2);
2492
2493 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) >= 0 &&
2494 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, false) >= 0)
ecdcbc5e 2495 printf("EnvironmentFile=%s (ignore_errors=%s)\n", path, yes_no(ignore));
8c7be95e
LP
2496
2497 dbus_message_iter_next(&sub);
2498 }
2499
2500 return 0;
2501
ebf57b80
LP
2502 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Paths")) {
2503 DBusMessageIter sub, sub2;
2504
2505 dbus_message_iter_recurse(iter, &sub);
ebf57b80
LP
2506 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
2507 const char *type, *path;
2508
2509 dbus_message_iter_recurse(&sub, &sub2);
2510
2511 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) >= 0 &&
2512 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, false) >= 0)
2513 printf("%s=%s\n", type, path);
2514
2515 dbus_message_iter_next(&sub);
2516 }
2517
707e5e52 2518 return 0;
582a507f 2519
707e5e52
LP
2520 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Timers")) {
2521 DBusMessageIter sub, sub2;
2522
2523 dbus_message_iter_recurse(iter, &sub);
707e5e52
LP
2524 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
2525 const char *base;
2526 uint64_t value, next_elapse;
2527
2528 dbus_message_iter_recurse(&sub, &sub2);
2529
2530 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &base, true) >= 0 &&
2531 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &value, true) >= 0 &&
552e4331
LP
2532 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &next_elapse, false) >= 0) {
2533 char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX];
2534
2535 printf("%s={ value=%s ; next_elapse=%s }\n",
fe68089d 2536 base,
552e4331
LP
2537 format_timespan(timespan1, sizeof(timespan1), value),
2538 format_timespan(timespan2, sizeof(timespan2), next_elapse));
2539 }
fe68089d
LP
2540
2541 dbus_message_iter_next(&sub);
2542 }
2543
2544 return 0;
fe68089d 2545
d8bbda91
LP
2546 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "ControlGroupAttributes")) {
2547 DBusMessageIter sub, sub2;
2548
2549 dbus_message_iter_recurse(iter, &sub);
2550 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
2551 const char *controller, *attr, *value;
2552
2553 dbus_message_iter_recurse(&sub, &sub2);
2554
2555 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &controller, true) >= 0 &&
2556 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &attr, true) >= 0 &&
2557 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &value, false) >= 0) {
2558
2559 printf("ControlGroupAttribute={ controller=%s ; attribute=%s ; value=\"%s\" }\n",
2560 controller,
2561 attr,
2562 value);
2563 }
2564
2565 dbus_message_iter_next(&sub);
2566 }
2567
2568 return 0;
2569
582a507f
LP
2570 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && startswith(name, "Exec")) {
2571 DBusMessageIter sub;
fe68089d
LP
2572
2573 dbus_message_iter_recurse(iter, &sub);
fe68089d 2574 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
582a507f 2575 ExecStatusInfo info;
fe68089d 2576
582a507f
LP
2577 zero(info);
2578 if (exec_status_info_deserialize(&sub, &info) >= 0) {
fe68089d 2579 char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
582a507f
LP
2580 char *t;
2581
2582 t = strv_join(info.argv, " ");
2583
ecdcbc5e 2584 printf("%s={ path=%s ; argv[]=%s ; ignore_errors=%s ; start_time=[%s] ; stop_time=[%s] ; pid=%u ; code=%s ; status=%i%s%s }\n",
582a507f
LP
2585 name,
2586 strna(info.path),
2587 strna(t),
b708e7ce 2588 yes_no(info.ignore),
582a507f
LP
2589 strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
2590 strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
2591 (unsigned) info. pid,
2592 sigchld_code_to_string(info.code),
2593 info.status,
2594 info.code == CLD_EXITED ? "" : "/",
2595 strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
fe68089d 2596
582a507f 2597 free(t);
fe68089d
LP
2598 }
2599
582a507f
LP
2600 free(info.path);
2601 strv_free(info.argv);
707e5e52
LP
2602
2603 dbus_message_iter_next(&sub);
2604 }
2605
48220598
LP
2606 return 0;
2607 }
2608
2609 break;
2610 }
2611
a4c279f8
LP
2612 if (generic_print_property(name, iter, arg_all) > 0)
2613 return 0;
2614
48220598
LP
2615 if (arg_all)
2616 printf("%s=[unprintable]\n", name);
2617
2618 return 0;
2619}
2620
be8088a2 2621static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
48220598
LP
2622 DBusMessage *m = NULL, *reply = NULL;
2623 const char *interface = "";
2624 int r;
2625 DBusError error;
2626 DBusMessageIter iter, sub, sub2, sub3;
61cbdc4b 2627 UnitStatusInfo info;
582a507f 2628 ExecStatusInfo *p;
48220598
LP
2629
2630 assert(bus);
2631 assert(path);
61cbdc4b 2632 assert(new_line);
48220598 2633
61cbdc4b 2634 zero(info);
48220598
LP
2635 dbus_error_init(&error);
2636
2637 if (!(m = dbus_message_new_method_call(
2638 "org.freedesktop.systemd1",
2639 path,
2640 "org.freedesktop.DBus.Properties",
2641 "GetAll"))) {
2642 log_error("Could not allocate message.");
2643 r = -ENOMEM;
2644 goto finish;
2645 }
2646
2647 if (!dbus_message_append_args(m,
2648 DBUS_TYPE_STRING, &interface,
2649 DBUS_TYPE_INVALID)) {
2650 log_error("Could not append arguments to message.");
2651 r = -ENOMEM;
2652 goto finish;
2653 }
2654
2655 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 2656 log_error("Failed to issue method call: %s", bus_error_message(&error));
48220598
LP
2657 r = -EIO;
2658 goto finish;
2659 }
2660
2661 if (!dbus_message_iter_init(reply, &iter) ||
2662 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
2663 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
2664 log_error("Failed to parse reply.");
2665 r = -EIO;
2666 goto finish;
2667 }
2668
2669 dbus_message_iter_recurse(&iter, &sub);
2670
61cbdc4b
LP
2671 if (*new_line)
2672 printf("\n");
2673
2674 *new_line = true;
2675
48220598
LP
2676 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
2677 const char *name;
2678
2679 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
2680 log_error("Failed to parse reply.");
2681 r = -EIO;
2682 goto finish;
2683 }
2684
2685 dbus_message_iter_recurse(&sub, &sub2);
0183528f 2686
48220598
LP
2687 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
2688 log_error("Failed to parse reply.");
2689 r = -EIO;
2690 goto finish;
2691 }
2692
2693 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
2694 log_error("Failed to parse reply.");
2695 r = -EIO;
2696 goto finish;
2697 }
2698
2699 dbus_message_iter_recurse(&sub2, &sub3);
2700
61cbdc4b
LP
2701 if (show_properties)
2702 r = print_property(name, &sub3);
2703 else
2704 r = status_property(name, &sub3, &info);
2705
2706 if (r < 0) {
48220598
LP
2707 log_error("Failed to parse reply.");
2708 r = -EIO;
2709 goto finish;
2710 }
2711
2712 dbus_message_iter_next(&sub);
2713 }
2714
f1e36d67
LP
2715 r = 0;
2716
22f4096c
LP
2717 if (!show_properties)
2718 print_status_info(&info);
f1e36d67 2719
22f4096c 2720 if (!streq_ptr(info.active_state, "active") &&
be8088a2
LP
2721 !streq_ptr(info.active_state, "reloading") &&
2722 streq(verb, "status"))
22f4096c
LP
2723 /* According to LSB: "program not running" */
2724 r = 3;
61cbdc4b 2725
582a507f
LP
2726 while ((p = info.exec)) {
2727 LIST_REMOVE(ExecStatusInfo, exec, info.exec, p);
2728 exec_status_info_free(p);
2729 }
2730
48220598
LP
2731finish:
2732 if (m)
2733 dbus_message_unref(m);
2734
2735 if (reply)
2736 dbus_message_unref(reply);
2737
2738 dbus_error_free(&error);
2739
2740 return r;
2741}
2742
729e3769 2743static int show(DBusConnection *bus, char **args) {
48220598 2744 DBusMessage *m = NULL, *reply = NULL;
22f4096c 2745 int r, ret = 0;
48220598 2746 DBusError error;
61cbdc4b 2747 bool show_properties, new_line = false;
729e3769 2748 char **name;
48220598
LP
2749
2750 assert(bus);
2751 assert(args);
2752
2753 dbus_error_init(&error);
2754
61cbdc4b
LP
2755 show_properties = !streq(args[0], "status");
2756
ec14911e 2757 if (show_properties)
1968a360 2758 pager_open_if_enabled();
ec14911e 2759
729e3769 2760 if (show_properties && strv_length(args) <= 1) {
48220598
LP
2761 /* If not argument is specified inspect the manager
2762 * itself */
2763
be8088a2 2764 ret = show_one(args[0], bus, "/org/freedesktop/systemd1", show_properties, &new_line);
48220598
LP
2765 goto finish;
2766 }
2767
729e3769 2768 STRV_FOREACH(name, args+1) {
48220598
LP
2769 const char *path = NULL;
2770 uint32_t id;
2771
729e3769 2772 if (safe_atou32(*name, &id) < 0) {
598b557b
LP
2773
2774 /* Interpret as unit name */
48220598
LP
2775
2776 if (!(m = dbus_message_new_method_call(
2777 "org.freedesktop.systemd1",
2778 "/org/freedesktop/systemd1",
2779 "org.freedesktop.systemd1.Manager",
e87d1818 2780 "LoadUnit"))) {
48220598 2781 log_error("Could not allocate message.");
22f4096c 2782 ret = -ENOMEM;
48220598
LP
2783 goto finish;
2784 }
2785
2786 if (!dbus_message_append_args(m,
729e3769 2787 DBUS_TYPE_STRING, name,
48220598
LP
2788 DBUS_TYPE_INVALID)) {
2789 log_error("Could not append arguments to message.");
22f4096c 2790 ret = -ENOMEM;
48220598
LP
2791 goto finish;
2792 }
2793
ed2d7a44
LP
2794 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2795
2796 if (!dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) {
4cf5d675 2797 log_error("Failed to issue method call: %s", bus_error_message(&error));
22f4096c 2798 ret = -EIO;
ed2d7a44
LP
2799 goto finish;
2800 }
2801
2802 dbus_error_free(&error);
2803
2804 dbus_message_unref(m);
2805 if (!(m = dbus_message_new_method_call(
2806 "org.freedesktop.systemd1",
2807 "/org/freedesktop/systemd1",
2808 "org.freedesktop.systemd1.Manager",
2809 "GetUnit"))) {
2810 log_error("Could not allocate message.");
22f4096c 2811 ret = -ENOMEM;
ed2d7a44
LP
2812 goto finish;
2813 }
2814
2815 if (!dbus_message_append_args(m,
729e3769 2816 DBUS_TYPE_STRING, name,
ed2d7a44
LP
2817 DBUS_TYPE_INVALID)) {
2818 log_error("Could not append arguments to message.");
22f4096c 2819 ret = -ENOMEM;
ed2d7a44
LP
2820 goto finish;
2821 }
2822
2823 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 2824 log_error("Failed to issue method call: %s", bus_error_message(&error));
22f4096c
LP
2825
2826 if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT))
2827 ret = 4; /* According to LSB: "program or service status is unknown" */
2828 else
2829 ret = -EIO;
ed2d7a44
LP
2830 goto finish;
2831 }
2832 }
2833
598b557b
LP
2834 } else if (show_properties) {
2835
2836 /* Interpret as job id */
48220598
LP
2837
2838 if (!(m = dbus_message_new_method_call(
2839 "org.freedesktop.systemd1",
2840 "/org/freedesktop/systemd1",
2841 "org.freedesktop.systemd1.Manager",
2842 "GetJob"))) {
2843 log_error("Could not allocate message.");
22f4096c 2844 ret = -ENOMEM;
48220598
LP
2845 goto finish;
2846 }
2847
2848 if (!dbus_message_append_args(m,
2849 DBUS_TYPE_UINT32, &id,
2850 DBUS_TYPE_INVALID)) {
2851 log_error("Could not append arguments to message.");
22f4096c 2852 ret = -ENOMEM;
48220598
LP
2853 goto finish;
2854 }
48220598 2855
598b557b 2856 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 2857 log_error("Failed to issue method call: %s", bus_error_message(&error));
22f4096c 2858 ret = -EIO;
598b557b
LP
2859 goto finish;
2860 }
2861 } else {
2862
2863 /* Interpret as PID */
2864
2865 if (!(m = dbus_message_new_method_call(
2866 "org.freedesktop.systemd1",
2867 "/org/freedesktop/systemd1",
2868 "org.freedesktop.systemd1.Manager",
2869 "GetUnitByPID"))) {
2870 log_error("Could not allocate message.");
22f4096c 2871 ret = -ENOMEM;
598b557b
LP
2872 goto finish;
2873 }
2874
2875 if (!dbus_message_append_args(m,
2876 DBUS_TYPE_UINT32, &id,
2877 DBUS_TYPE_INVALID)) {
2878 log_error("Could not append arguments to message.");
22f4096c 2879 ret = -ENOMEM;
598b557b
LP
2880 goto finish;
2881 }
2882
ed2d7a44 2883 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 2884 log_error("Failed to issue method call: %s", bus_error_message(&error));
22f4096c 2885 ret = -EIO;
ed2d7a44
LP
2886 goto finish;
2887 }
48220598
LP
2888 }
2889
2890 if (!dbus_message_get_args(reply, &error,
2891 DBUS_TYPE_OBJECT_PATH, &path,
2892 DBUS_TYPE_INVALID)) {
4cf5d675 2893 log_error("Failed to parse reply: %s", bus_error_message(&error));
22f4096c 2894 ret = -EIO;
48220598
LP
2895 goto finish;
2896 }
2897
be8088a2 2898 if ((r = show_one(args[0], bus, path, show_properties, &new_line)) != 0)
22f4096c 2899 ret = r;
48220598
LP
2900
2901 dbus_message_unref(m);
2902 dbus_message_unref(reply);
2903 m = reply = NULL;
2904 }
2905
48220598
LP
2906finish:
2907 if (m)
2908 dbus_message_unref(m);
2909
2910 if (reply)
2911 dbus_message_unref(reply);
2912
2913 dbus_error_free(&error);
2914
22f4096c 2915 return ret;
0183528f
LP
2916}
2917
729e3769 2918static int dump(DBusConnection *bus, char **args) {
7e4249b9
LP
2919 DBusMessage *m = NULL, *reply = NULL;
2920 DBusError error;
2921 int r;
2922 const char *text;
2923
2924 dbus_error_init(&error);
2925
1968a360 2926 pager_open_if_enabled();
ec14911e 2927
7e4249b9
LP
2928 if (!(m = dbus_message_new_method_call(
2929 "org.freedesktop.systemd1",
2930 "/org/freedesktop/systemd1",
2931 "org.freedesktop.systemd1.Manager",
2932 "Dump"))) {
2933 log_error("Could not allocate message.");
2934 return -ENOMEM;
2935 }
2936
2937 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 2938 log_error("Failed to issue method call: %s", bus_error_message(&error));
7e4249b9
LP
2939 r = -EIO;
2940 goto finish;
2941 }
2942
2943 if (!dbus_message_get_args(reply, &error,
2944 DBUS_TYPE_STRING, &text,
2945 DBUS_TYPE_INVALID)) {
4cf5d675 2946 log_error("Failed to parse reply: %s", bus_error_message(&error));
7e4249b9
LP
2947 r = -EIO;
2948 goto finish;
2949 }
2950
2951 fputs(text, stdout);
2952
2953 r = 0;
2954
2955finish:
2956 if (m)
2957 dbus_message_unref(m);
2958
2959 if (reply)
2960 dbus_message_unref(reply);
2961
2962 dbus_error_free(&error);
2963
2964 return r;
2965}
2966
729e3769 2967static int snapshot(DBusConnection *bus, char **args) {
7e4249b9
LP
2968 DBusMessage *m = NULL, *reply = NULL;
2969 DBusError error;
2970 int r;
2971 const char *name = "", *path, *id;
2972 dbus_bool_t cleanup = FALSE;
2973 DBusMessageIter iter, sub;
2974 const char
2975 *interface = "org.freedesktop.systemd1.Unit",
2976 *property = "Id";
2977
2978 dbus_error_init(&error);
2979
2980 if (!(m = dbus_message_new_method_call(
2981 "org.freedesktop.systemd1",
2982 "/org/freedesktop/systemd1",
2983 "org.freedesktop.systemd1.Manager",
2984 "CreateSnapshot"))) {
2985 log_error("Could not allocate message.");
2986 return -ENOMEM;
2987 }
2988
729e3769 2989 if (strv_length(args) > 1)
7e4249b9
LP
2990 name = args[1];
2991
2992 if (!dbus_message_append_args(m,
2993 DBUS_TYPE_STRING, &name,
2994 DBUS_TYPE_BOOLEAN, &cleanup,
2995 DBUS_TYPE_INVALID)) {
2996 log_error("Could not append arguments to message.");
2997 r = -ENOMEM;
2998 goto finish;
2999 }
3000
3001 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 3002 log_error("Failed to issue method call: %s", bus_error_message(&error));
7e4249b9
LP
3003 r = -EIO;
3004 goto finish;
3005 }
3006
3007 if (!dbus_message_get_args(reply, &error,
3008 DBUS_TYPE_OBJECT_PATH, &path,
3009 DBUS_TYPE_INVALID)) {
4cf5d675 3010 log_error("Failed to parse reply: %s", bus_error_message(&error));
7e4249b9
LP
3011 r = -EIO;
3012 goto finish;
3013 }
3014
3015 dbus_message_unref(m);
3016 if (!(m = dbus_message_new_method_call(
3017 "org.freedesktop.systemd1",
3018 path,
3019 "org.freedesktop.DBus.Properties",
3020 "Get"))) {
3021 log_error("Could not allocate message.");
3022 return -ENOMEM;
3023 }
3024
3025 if (!dbus_message_append_args(m,
3026 DBUS_TYPE_STRING, &interface,
3027 DBUS_TYPE_STRING, &property,
3028 DBUS_TYPE_INVALID)) {
3029 log_error("Could not append arguments to message.");
3030 r = -ENOMEM;
3031 goto finish;
3032 }
3033
3034 dbus_message_unref(reply);
3035 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 3036 log_error("Failed to issue method call: %s", bus_error_message(&error));
7e4249b9
LP
3037 r = -EIO;
3038 goto finish;
3039 }
3040
3041 if (!dbus_message_iter_init(reply, &iter) ||
3042 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
3043 log_error("Failed to parse reply.");
3044 r = -EIO;
3045 goto finish;
3046 }
3047
3048 dbus_message_iter_recurse(&iter, &sub);
3049
3050 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
3051 log_error("Failed to parse reply.");
3052 r = -EIO;
3053 goto finish;
3054 }
3055
3056 dbus_message_iter_get_basic(&sub, &id);
0183528f
LP
3057
3058 if (!arg_quiet)
3059 puts(id);
7e4249b9
LP
3060 r = 0;
3061
3062finish:
3063 if (m)
3064 dbus_message_unref(m);
3065
3066 if (reply)
3067 dbus_message_unref(reply);
3068
3069 dbus_error_free(&error);
3070
3071 return r;
3072}
3073
729e3769 3074static int delete_snapshot(DBusConnection *bus, char **args) {
6759e7a7
LP
3075 DBusMessage *m = NULL, *reply = NULL;
3076 int r;
3077 DBusError error;
729e3769 3078 char **name;
6759e7a7
LP
3079
3080 assert(bus);
3081 assert(args);
3082
3083 dbus_error_init(&error);
3084
729e3769 3085 STRV_FOREACH(name, args+1) {
6759e7a7
LP
3086 const char *path = NULL;
3087
3088 if (!(m = dbus_message_new_method_call(
3089 "org.freedesktop.systemd1",
3090 "/org/freedesktop/systemd1",
3091 "org.freedesktop.systemd1.Manager",
3092 "GetUnit"))) {
3093 log_error("Could not allocate message.");
3094 r = -ENOMEM;
3095 goto finish;
3096 }
3097
3098 if (!dbus_message_append_args(m,
729e3769 3099 DBUS_TYPE_STRING, name,
6759e7a7
LP
3100 DBUS_TYPE_INVALID)) {
3101 log_error("Could not append arguments to message.");
3102 r = -ENOMEM;
3103 goto finish;
3104 }
3105
3106 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 3107 log_error("Failed to issue method call: %s", bus_error_message(&error));
6759e7a7
LP
3108 r = -EIO;
3109 goto finish;
3110 }
3111
3112 if (!dbus_message_get_args(reply, &error,
3113 DBUS_TYPE_OBJECT_PATH, &path,
3114 DBUS_TYPE_INVALID)) {
4cf5d675 3115 log_error("Failed to parse reply: %s", bus_error_message(&error));
6759e7a7
LP
3116 r = -EIO;
3117 goto finish;
3118 }
3119
3120 dbus_message_unref(m);
3121 if (!(m = dbus_message_new_method_call(
3122 "org.freedesktop.systemd1",
3123 path,
3124 "org.freedesktop.systemd1.Snapshot",
3125 "Remove"))) {
3126 log_error("Could not allocate message.");
3127 r = -ENOMEM;
3128 goto finish;
3129 }
3130
3131 dbus_message_unref(reply);
3132 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 3133 log_error("Failed to issue method call: %s", bus_error_message(&error));
6759e7a7
LP
3134 r = -EIO;
3135 goto finish;
3136 }
3137
3138 dbus_message_unref(m);
3139 dbus_message_unref(reply);
3140 m = reply = NULL;
3141 }
3142
3143 r = 0;
3144
3145finish:
3146 if (m)
3147 dbus_message_unref(m);
3148
3149 if (reply)
3150 dbus_message_unref(reply);
3151
3152 dbus_error_free(&error);
3153
3154 return r;
3155}
3156
729e3769 3157static int daemon_reload(DBusConnection *bus, char **args) {
7e4249b9
LP
3158 DBusMessage *m = NULL, *reply = NULL;
3159 DBusError error;
3160 int r;
3161 const char *method;
3162
3163 dbus_error_init(&error);
3164
e4b61340
LP
3165 if (arg_action == ACTION_RELOAD)
3166 method = "Reload";
3167 else if (arg_action == ACTION_REEXEC)
3168 method = "Reexecute";
3169 else {
3170 assert(arg_action == ACTION_SYSTEMCTL);
3171
3172 method =
20b09ca7
LP
3173 streq(args[0], "clear-jobs") ||
3174 streq(args[0], "cancel") ? "ClearJobs" :
3175 streq(args[0], "daemon-reexec") ? "Reexecute" :
3176 streq(args[0], "reset-failed") ? "ResetFailed" :
3177 streq(args[0], "halt") ? "Halt" :
3178 streq(args[0], "poweroff") ? "PowerOff" :
3179 streq(args[0], "reboot") ? "Reboot" :
3180 streq(args[0], "kexec") ? "KExec" :
3181 streq(args[0], "exit") ? "Exit" :
3182 /* "daemon-reload" */ "Reload";
e4b61340 3183 }
7e4249b9
LP
3184
3185 if (!(m = dbus_message_new_method_call(
3186 "org.freedesktop.systemd1",
3187 "/org/freedesktop/systemd1",
3188 "org.freedesktop.systemd1.Manager",
3189 method))) {
3190 log_error("Could not allocate message.");
3191 return -ENOMEM;
3192 }
3193
3194 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
e4b61340
LP
3195
3196 if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) {
3197 /* There's always a fallback possible for
3198 * legacy actions. */
aabd9b11 3199 r = -EADDRNOTAVAIL;
e4b61340
LP
3200 goto finish;
3201 }
3202
b23de6af
LP
3203 if (streq(method, "Reexecute") && dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY)) {
3204 /* On reexecution, we expect a disconnect, not
3205 * a reply */
3206 r = 0;
3207 goto finish;
3208 }
3209
4cf5d675 3210 log_error("Failed to issue method call: %s", bus_error_message(&error));
7e4249b9
LP
3211 r = -EIO;
3212 goto finish;
3213 }
3214
aabd9b11 3215 r = 0;
7e4249b9
LP
3216
3217finish:
3218 if (m)
3219 dbus_message_unref(m);
3220
3221 if (reply)
3222 dbus_message_unref(reply);
3223
3224 dbus_error_free(&error);
3225
3226 return r;
3227}
3228
729e3769 3229static int reset_failed(DBusConnection *bus, char **args) {
f8440af5 3230 DBusMessage *m = NULL;
5632e374
LP
3231 int r;
3232 DBusError error;
729e3769 3233 char **name;
5632e374
LP
3234
3235 assert(bus);
3236 dbus_error_init(&error);
3237
729e3769
LP
3238 if (strv_length(args) <= 1)
3239 return daemon_reload(bus, args);
5632e374 3240
729e3769 3241 STRV_FOREACH(name, args+1) {
f8440af5 3242 DBusMessage *reply;
5632e374
LP
3243
3244 if (!(m = dbus_message_new_method_call(
3245 "org.freedesktop.systemd1",
3246 "/org/freedesktop/systemd1",
3247 "org.freedesktop.systemd1.Manager",
fdf20a31 3248 "ResetFailedUnit"))) {
5632e374
LP
3249 log_error("Could not allocate message.");
3250 r = -ENOMEM;
3251 goto finish;
3252 }
3253
3254 if (!dbus_message_append_args(m,
729e3769 3255 DBUS_TYPE_STRING, name,
5632e374
LP
3256 DBUS_TYPE_INVALID)) {
3257 log_error("Could not append arguments to message.");
3258 r = -ENOMEM;
3259 goto finish;
3260 }
3261
3262 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 3263 log_error("Failed to issue method call: %s", bus_error_message(&error));
5632e374
LP
3264 r = -EIO;
3265 goto finish;
3266 }
3267
3268 dbus_message_unref(m);
3269 dbus_message_unref(reply);
3270 m = reply = NULL;
3271 }
3272
3273 r = 0;
3274
3275finish:
3276 if (m)
3277 dbus_message_unref(m);
3278
5632e374
LP
3279 dbus_error_free(&error);
3280
3281 return r;
3282}
3283
729e3769 3284static int show_enviroment(DBusConnection *bus, char **args) {
7e4249b9
LP
3285 DBusMessage *m = NULL, *reply = NULL;
3286 DBusError error;
3287 DBusMessageIter iter, sub, sub2;
3288 int r;
3289 const char
3290 *interface = "org.freedesktop.systemd1.Manager",
3291 *property = "Environment";
3292
3293 dbus_error_init(&error);
3294
1968a360 3295 pager_open_if_enabled();
ec14911e 3296
7e4249b9
LP
3297 if (!(m = dbus_message_new_method_call(
3298 "org.freedesktop.systemd1",
3299 "/org/freedesktop/systemd1",
3300 "org.freedesktop.DBus.Properties",
3301 "Get"))) {
3302 log_error("Could not allocate message.");
3303 return -ENOMEM;
3304 }
3305
3306 if (!dbus_message_append_args(m,
3307 DBUS_TYPE_STRING, &interface,
3308 DBUS_TYPE_STRING, &property,
3309 DBUS_TYPE_INVALID)) {
3310 log_error("Could not append arguments to message.");
3311 r = -ENOMEM;
3312 goto finish;
3313 }
3314
3315 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 3316 log_error("Failed to issue method call: %s", bus_error_message(&error));
7e4249b9
LP
3317 r = -EIO;
3318 goto finish;
3319 }
3320
3321 if (!dbus_message_iter_init(reply, &iter) ||
3322 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
3323 log_error("Failed to parse reply.");
3324 r = -EIO;
3325 goto finish;
3326 }
3327
3328 dbus_message_iter_recurse(&iter, &sub);
3329
3330 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY ||
3331 dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_STRING) {
3332 log_error("Failed to parse reply.");
3333 r = -EIO;
3334 goto finish;
3335 }
3336
3337 dbus_message_iter_recurse(&sub, &sub2);
3338
3339 while (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_INVALID) {
3340 const char *text;
3341
3342 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) {
3343 log_error("Failed to parse reply.");
3344 r = -EIO;
3345 goto finish;
3346 }
3347
3348 dbus_message_iter_get_basic(&sub2, &text);
3349 printf("%s\n", text);
3350
3351 dbus_message_iter_next(&sub2);
3352 }
3353
3354 r = 0;
3355
3356finish:
3357 if (m)
3358 dbus_message_unref(m);
3359
3360 if (reply)
3361 dbus_message_unref(reply);
3362
3363 dbus_error_free(&error);
3364
3365 return r;
3366}
3367
729e3769 3368static int set_environment(DBusConnection *bus, char **args) {
7e4249b9
LP
3369 DBusMessage *m = NULL, *reply = NULL;
3370 DBusError error;
3371 int r;
3372 const char *method;
3373 DBusMessageIter iter, sub;
729e3769 3374 char **name;
7e4249b9
LP
3375
3376 dbus_error_init(&error);
3377
3378 method = streq(args[0], "set-environment")
3379 ? "SetEnvironment"
3380 : "UnsetEnvironment";
3381
3382 if (!(m = dbus_message_new_method_call(
3383 "org.freedesktop.systemd1",
3384 "/org/freedesktop/systemd1",
3385 "org.freedesktop.systemd1.Manager",
3386 method))) {
3387
3388 log_error("Could not allocate message.");
3389 return -ENOMEM;
3390 }
3391
3392 dbus_message_iter_init_append(m, &iter);
3393
3394 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
3395 log_error("Could not append arguments to message.");
3396 r = -ENOMEM;
3397 goto finish;
3398 }
3399
729e3769
LP
3400 STRV_FOREACH(name, args+1)
3401 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, name)) {
7e4249b9
LP
3402 log_error("Could not append arguments to message.");
3403 r = -ENOMEM;
3404 goto finish;
3405 }
3406
3407 if (!dbus_message_iter_close_container(&iter, &sub)) {
3408 log_error("Could not append arguments to message.");
3409 r = -ENOMEM;
3410 goto finish;
3411 }
3412
3413 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4cf5d675 3414 log_error("Failed to issue method call: %s", bus_error_message(&error));
7e4249b9
LP
3415 r = -EIO;
3416 goto finish;
3417 }
3418
3419 r = 0;
3420
3421finish:
3422 if (m)
3423 dbus_message_unref(m);
3424
3425 if (reply)
3426 dbus_message_unref(reply);
3427
3428 dbus_error_free(&error);
3429
3430 return r;
3431}
3432
729e3769
LP
3433static int enable_sysv_units(char **args) {
3434 int r = 0;
ee5762e3 3435
729e3769
LP
3436#if defined (HAVE_SYSV_COMPAT) && (defined(TARGET_FEDORA) || defined(TARGET_MANDRIVA) || defined(TARGET_SUSE) || defined(TARGET_MEEGO) || defined(TARGET_ALTLINUX))
3437 const char *verb = args[0];
3438 unsigned f = 1, t = 1;
3439 LookupPaths paths;
ee5762e3 3440
729e3769
LP
3441 if (arg_scope != UNIT_FILE_SYSTEM)
3442 return 0;
ee5762e3 3443
729e3769
LP
3444 if (!streq(verb, "enable") &&
3445 !streq(verb, "disable") &&
3446 !streq(verb, "is-enabled"))
3447 return 0;
ee5762e3 3448
729e3769
LP
3449 /* Processes all SysV units, and reshuffles the array so that
3450 * afterwards only the native units remain */
ee5762e3 3451
729e3769
LP
3452 zero(paths);
3453 r = lookup_paths_init(&paths, MANAGER_SYSTEM, false);
3454 if (r < 0)
3455 return r;
ee5762e3 3456
729e3769 3457 r = 0;
ee5762e3 3458
729e3769
LP
3459 for (f = 1; args[f]; f++) {
3460 const char *name;
3461 char *p;
3462 bool found_native = false, found_sysv;
3463 unsigned c = 1;
3464 const char *argv[6] = { "/sbin/chkconfig", NULL, NULL, NULL, NULL };
3465 char **k, *l, *q = NULL;
3466 int j;
3467 pid_t pid;
3468 siginfo_t status;
ee5762e3 3469
729e3769 3470 name = args[f];
ee5762e3 3471
729e3769
LP
3472 if (!endswith(name, ".service"))
3473 continue;
ee5762e3 3474
729e3769
LP
3475 if (path_is_absolute(name))
3476 continue;
ee5762e3 3477
729e3769
LP
3478 STRV_FOREACH(k, paths.unit_path) {
3479 p = NULL;
ee5762e3 3480
729e3769
LP
3481 if (!isempty(arg_root))
3482 asprintf(&p, "%s/%s/%s", arg_root, *k, name);
3483 else
3484 asprintf(&p, "%s/%s", *k, name);
ee5762e3 3485
729e3769
LP
3486 if (!p) {
3487 log_error("No memory");
3488 r = -ENOMEM;
3489 goto finish;
3490 }
ee5762e3 3491
729e3769
LP
3492 found_native = access(p, F_OK) >= 0;
3493 free(p);
ee5762e3 3494
729e3769
LP
3495 if (found_native)
3496 break;
3497 }
ee5762e3 3498
729e3769
LP
3499 if (found_native)
3500 continue;
ee5762e3 3501
729e3769
LP
3502 p = NULL;
3503 if (!isempty(arg_root))
3504 asprintf(&p, "%s/" SYSTEM_SYSVINIT_PATH "/%s", arg_root, name);
3505 else
3506 asprintf(&p, SYSTEM_SYSVINIT_PATH "/%s", name);
3507 if (!p) {
3508 log_error("No memory");
3509 r = -ENOMEM;
3510 goto finish;
3511 }
ee5762e3 3512
729e3769
LP
3513 p[strlen(p) - sizeof(".service") + 1] = 0;
3514 found_sysv = access(p, F_OK) >= 0;
ee5762e3 3515
729e3769
LP
3516 if (!found_sysv) {
3517 free(p);
3518 continue;
71fad675
LP
3519 }
3520
729e3769
LP
3521 /* Mark this entry, so that we don't try enabling it as native unit */
3522 args[f] = (char*) "";
ee5762e3 3523
729e3769 3524 log_info("%s is not a native service, redirecting to /sbin/chkconfig.", name);
ee5762e3 3525
729e3769
LP
3526 if (!isempty(arg_root))
3527 argv[c++] = q = strappend("--root=", arg_root);
ee5762e3 3528
729e3769
LP
3529 argv[c++] = file_name_from_path(p);
3530 argv[c++] =
3531 streq(verb, "enable") ? "on" :
3532 streq(verb, "disable") ? "off" : "--level=5";
3533 argv[c] = NULL;
ee5762e3 3534
729e3769
LP
3535 l = strv_join((char**)argv, " ");
3536 if (!l) {
3537 log_error("No memory.");
3538 free(q);
3539 free(p);
3540 r = -ENOMEM;
3541 goto finish;
3542 }
ee5762e3 3543
729e3769
LP
3544 log_info("Executing %s", l);
3545 free(l);
ee5762e3 3546
729e3769
LP
3547 pid = fork();
3548 if (pid < 0) {
3549 log_error("Failed to fork: %m");
3550 free(p);
3551 free(q);
3552 r = -errno;
3553 goto finish;
3554 } else if (pid == 0) {
3555 /* Child */
ee5762e3 3556
729e3769
LP
3557 execv(argv[0], (char**) argv);
3558 _exit(EXIT_FAILURE);
3559 }
ee5762e3 3560
729e3769
LP
3561 free(p);
3562 free(q);
ee5762e3 3563
729e3769
LP
3564 j = wait_for_terminate(pid, &status);
3565 if (j < 0) {
3566 log_error("Failed to wait for child: %s", strerror(-r));
3567 r = j;
3568 goto finish;
3569 }
ee5762e3 3570
729e3769
LP
3571 if (status.si_code == CLD_EXITED) {
3572 if (streq(verb, "is-enabled")) {
3573 if (status.si_status == 0) {
3574 if (!arg_quiet)
3575 puts("enabled");
3576 r = 1;
3577 } else {
3578 if (!arg_quiet)
3579 puts("disabled");
3580 }
ee5762e3 3581
729e3769
LP
3582 } else if (status.si_status != 0) {
3583 r = -EINVAL;
3584 goto finish;
3585 }
3586 } else {
3587 r = -EPROTO;
3588 goto finish;
3589 }
ee5762e3
LP
3590 }
3591
729e3769
LP
3592finish:
3593 lookup_paths_free(&paths);
ee5762e3 3594
729e3769
LP
3595 /* Drop all SysV units */
3596 for (f = 1, t = 1; args[f]; f++) {
ee5762e3 3597
729e3769 3598 if (isempty(args[f]))
ee5762e3
LP
3599 continue;
3600
729e3769
LP
3601 args[t++] = args[f];
3602 }
ee5762e3 3603
729e3769 3604 args[t] = NULL;
ee5762e3 3605
729e3769
LP
3606#endif
3607 return r;
3608}
ee5762e3 3609
729e3769
LP
3610static int enable_unit(DBusConnection *bus, char **args) {
3611 const char *verb = args[0];
3612 UnitFileChange *changes = NULL;
3613 unsigned n_changes = 0, i;
3614 int carries_install_info = -1;
3615 DBusMessage *m = NULL, *reply = NULL;
3616 int r;
3617 DBusError error;
ee5762e3 3618
729e3769 3619 dbus_error_init(&error);
ee5762e3 3620
729e3769
LP
3621 r = enable_sysv_units(args);
3622 if (r < 0)
3623 return r;
ee5762e3 3624
729e3769
LP
3625 if (!bus || avoid_bus()) {
3626 if (streq(verb, "enable")) {
3627 r = unit_file_enable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
3628 carries_install_info = r;
3629 } else if (streq(verb, "disable"))
3630 r = unit_file_disable(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
3631 else if (streq(verb, "reenable")) {
3632 r = unit_file_reenable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
3633 carries_install_info = r;
3634 } else if (streq(verb, "link"))
3635 r = unit_file_link(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
3636 else if (streq(verb, "preset")) {
3637 r = unit_file_preset(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
3638 carries_install_info = r;
3639 } else if (streq(verb, "mask"))
3640 r = unit_file_mask(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
3641 else if (streq(verb, "unmask"))
3642 r = unit_file_unmask(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
3643 else
3644 assert_not_reached("Unknown verb");
ee5762e3 3645
729e3769
LP
3646 if (r < 0) {
3647 log_error("Operation failed: %s", strerror(-r));
3648 goto finish;
ee5762e3
LP
3649 }
3650
729e3769
LP
3651 for (i = 0; i < n_changes; i++) {
3652 if (changes[i].type == UNIT_FILE_SYMLINK)
3653 log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path);
3654 else
3655 log_info("rm '%s'", changes[i].path);
ee5762e3
LP
3656 }
3657
729e3769
LP
3658 } else {
3659 const char *method;
3660 bool send_force = true, expect_carries_install_info = false;
3661 dbus_bool_t a, b;
3662 DBusMessageIter iter, sub, sub2;
3663
3664 if (streq(verb, "enable")) {
3665 method = "EnableUnitFiles";
3666 expect_carries_install_info = true;
3667 } else if (streq(verb, "disable")) {
3668 method = "DisableUnitFiles";
3669 send_force = false;
3670 } else if (streq(verb, "reenable")) {
3671 method = "ReenableUnitFiles";
3672 expect_carries_install_info = true;
3673 } else if (streq(verb, "link"))
3674 method = "LinkUnitFiles";
3675 else if (streq(verb, "preset")) {
3676 method = "PresetUnitFiles";
3677 expect_carries_install_info = true;
3678 } else if (streq(verb, "mask"))
3679 method = "MaskUnitFiles";
3680 else if (streq(verb, "unmask")) {
3681 method = "UnmaskUnitFiles";
3682 send_force = false;
3683 } else
3684 assert_not_reached("Unknown verb");
3685
3686 m = dbus_message_new_method_call(
3687 "org.freedesktop.systemd1",
3688 "/org/freedesktop/systemd1",
3689 "org.freedesktop.systemd1.Manager",
3690 method);
3691 if (!m) {
ee5762e3
LP
3692 log_error("Out of memory");
3693 r = -ENOMEM;
3694 goto finish;
3695 }
3696
729e3769 3697 dbus_message_iter_init_append(m, &iter);
ee5762e3 3698
729e3769
LP
3699 r = bus_append_strv_iter(&iter, args+1);
3700 if (r < 0) {
3701 log_error("Failed to append unit files.");
ee5762e3
LP
3702 goto finish;
3703 }
3704
729e3769
LP
3705 a = arg_runtime;
3706 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &a)) {
3707 log_error("Failed to append runtime boolean.");
ee5762e3
LP
3708 r = -ENOMEM;
3709 goto finish;
3710 }
3711
729e3769
LP
3712 if (send_force) {
3713 b = arg_force;
be394c48 3714
729e3769
LP
3715 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b)) {
3716 log_error("Failed to append force boolean.");
3717 r = -ENOMEM;
3718 goto finish;
3719 }
09adcdf7 3720 }
ee5762e3 3721
729e3769
LP
3722 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
3723 if (!reply) {
3724 log_error("Failed to issue method call: %s", bus_error_message(&error));
3725 r = -EIO;
3726 goto finish;
ee5762e3
LP
3727 }
3728
729e3769
LP
3729 if (!dbus_message_iter_init(reply, &iter)) {
3730 log_error("Failed to initialize iterator.");
3731 goto finish;
3732 }
be394c48 3733
729e3769
LP
3734 if (expect_carries_install_info) {
3735 r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &b, true);
3736 if (r < 0) {
3737 log_error("Failed to parse reply.");
3738 goto finish;
3739 }
ee5762e3 3740
729e3769 3741 carries_install_info = b;
ee5762e3
LP
3742 }
3743
729e3769
LP
3744 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
3745 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
3746 log_error("Failed to parse reply.");
3747 r = -EIO;
3748 goto finish;
ee5762e3
LP
3749 }
3750
729e3769
LP
3751 dbus_message_iter_recurse(&iter, &sub);
3752 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
3753 const char *type, *path, *source;
c8b2e52c 3754
729e3769
LP
3755 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
3756 log_error("Failed to parse reply.");
3757 r = -EIO;
3758 goto finish;
c8b2e52c
LP
3759 }
3760
729e3769 3761 dbus_message_iter_recurse(&sub, &sub2);
c8b2e52c 3762
729e3769
LP
3763 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
3764 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
3765 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &source, false) < 0) {
3766 log_error("Failed to parse reply.");
3767 r = -EIO;
3768 goto finish;
c8b2e52c
LP
3769 }
3770
729e3769
LP
3771 if (streq(type, "symlink"))
3772 log_info("ln -s '%s' '%s'", source, path);
3773 else
3774 log_info("rm '%s'", path);
b77398f7 3775
729e3769
LP
3776 dbus_message_iter_next(&sub);
3777 }
b77398f7 3778
729e3769
LP
3779 /* Try to reload if enabeld */
3780 if (!arg_no_reload)
3781 r = daemon_reload(bus, args);
b647f10d 3782 }
3d3961f2 3783
729e3769
LP
3784 if (carries_install_info == 0)
3785 log_warning("Warning: unit files do not carry install information. No operation executed.");
ee5762e3 3786
729e3769
LP
3787finish:
3788 if (m)
3789 dbus_message_unref(m);
ee5762e3 3790
729e3769
LP
3791 if (reply)
3792 dbus_message_unref(reply);
ee5762e3 3793
729e3769 3794 unit_file_changes_free(changes, n_changes);
ee5762e3 3795
729e3769
LP
3796 dbus_error_free(&error);
3797 return r;
ee5762e3
LP
3798}
3799
729e3769 3800static int unit_is_enabled(DBusConnection *bus, char **args) {
ee5762e3
LP
3801 DBusError error;
3802 int r;
729e3769
LP
3803 DBusMessage *m = NULL, *reply = NULL;
3804 bool enabled;
3805 char **name;
ee5762e3
LP
3806
3807 dbus_error_init(&error);
3808
729e3769
LP
3809 r = enable_sysv_units(args);
3810 if (r < 0)
3811 return r;
ee5762e3 3812
729e3769 3813 enabled = r > 0;
ee5762e3 3814
729e3769 3815 if (!bus || avoid_bus()) {
ee5762e3 3816
729e3769
LP
3817 STRV_FOREACH(name, args+1) {
3818 UnitFileState state;
ee5762e3 3819
729e3769
LP
3820 state = unit_file_get_state(arg_scope, arg_root, *name);
3821 if (state < 0) {
3822 r = state;
3823 goto finish;
3824 }
ee5762e3 3825
729e3769
LP
3826 if (state == UNIT_FILE_ENABLED ||
3827 state == UNIT_FILE_ENABLED_RUNTIME ||
3828 state == UNIT_FILE_STATIC)
3829 enabled = true;
3830
3831 if (!arg_quiet)
3832 puts(unit_file_state_to_string(state));
71fad675 3833 }
ee5762e3 3834
729e3769
LP
3835 } else {
3836 STRV_FOREACH(name, args+1) {
3837 const char *s;
63a723f3 3838
729e3769
LP
3839 m = dbus_message_new_method_call(
3840 "org.freedesktop.systemd1",
3841 "/org/freedesktop/systemd1",
3842 "org.freedesktop.systemd1.Manager",
3843 "GetUnitFileState");
3844 if (!m) {
3845 log_error("Out of memory");
3846 r = -ENOMEM;
3847 goto finish;
3848 }
ee5762e3 3849
729e3769
LP
3850 if (!dbus_message_append_args(m,
3851 DBUS_TYPE_STRING, name,
3852 DBUS_TYPE_INVALID)) {
3853 log_error("Could not append arguments to message.");
3854 r = -ENOMEM;
3855 goto finish;
3856 }
ee5762e3 3857
729e3769
LP
3858 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
3859 if (!reply) {
3860 log_error("Failed to issue method call: %s", bus_error_message(&error));
3861 r = -EIO;
3862 goto finish;
3863 }
ee5762e3 3864
729e3769
LP
3865 if (!dbus_message_get_args(reply, &error,
3866 DBUS_TYPE_STRING, &s,
3867 DBUS_TYPE_INVALID)) {
3868 log_error("Failed to parse reply: %s", bus_error_message(&error));
3869 r = -EIO;
ee5762e3
LP
3870 goto finish;
3871 }
3872
729e3769
LP
3873 dbus_message_unref(m);
3874 dbus_message_unref(reply);
3875 m = reply = NULL;
ee5762e3 3876
729e3769
LP
3877 if (streq(s, "enabled") ||
3878 streq(s, "enabled-runtime") ||
3879 streq(s, "static"))
3880 enabled = true;
3881
3882 if (!arg_quiet)
3883 puts(s);
560d8f23 3884 }
ee5762e3
LP
3885 }
3886
729e3769 3887 r = enabled ? 0 : 1;
ee5762e3 3888
729e3769
LP
3889finish:
3890 if (m)
3891 dbus_message_unref(m);
ee5762e3 3892
729e3769
LP
3893 if (reply)
3894 dbus_message_unref(reply);
ee5762e3 3895
729e3769 3896 dbus_error_free(&error);
ee5762e3
LP
3897 return r;
3898}
3899
e4b61340 3900static int systemctl_help(void) {
7e4249b9 3901
729e3769
LP
3902 pager_open_if_enabled();
3903
2e33c433 3904 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
729e3769 3905 "Query or send control commands to the systemd manager.\n\n"
8a0867d6
LP
3906 " -h --help Show this help\n"
3907 " --version Show package version\n"
3908 " -t --type=TYPE List only units of a particular type\n"
3909 " -p --property=NAME Show only properties by this name\n"
3910 " -a --all Show all units/properties, including dead/empty ones\n"
30732560 3911 " --failed Show only failed units\n"
8a0867d6
LP
3912 " --full Don't ellipsize unit names on output\n"
3913 " --fail When queueing a new job, fail if conflicting jobs are\n"
3914 " pending\n"
e67c3609
LP
3915 " --ignore-dependencies\n"
3916 " When queueing a new job, ignore all its dependencies\n"
a8f11321
LP
3917 " --kill-who=WHO Who to send signal to\n"
3918 " -s --signal=SIGNAL Which signal to send\n"
aca4c786 3919 " -H --host=[USER@]HOST\n"
a8f11321
LP
3920 " Show information for remote host\n"
3921 " -P --privileged Acquire privileges before execution\n"
8a0867d6
LP
3922 " -q --quiet Suppress output\n"
3923 " --no-block Do not wait until operation finished\n"
8a0867d6 3924 " --no-wall Don't send wall message before halt/power-off/reboot\n"
8a0867d6
LP
3925 " --no-reload When enabling/disabling unit files, don't reload daemon\n"
3926 " configuration\n"
69fc152f 3927 " --no-pager Do not pipe output into a pager\n"
501fc174
LP
3928 " --no-ask-password\n"
3929 " Do not ask for system passwords\n"
a8f11321
LP
3930 " --order When generating graph for dot, show only order\n"
3931 " --require When generating graph for dot, show only requirement\n"
3932 " --system Connect to system manager\n"
3933 " --user Connect to user service manager\n"
3934 " --global Enable/disable unit files globally\n"
8a0867d6
LP
3935 " -f --force When enabling unit files, override existing symlinks\n"
3936 " When shutting down, execute action immediately\n"
729e3769
LP
3937 " --root=PATH Enable unit files in the specified root directory\n"
3938 " --runtime Enable unit files only temporarily until next reboot\n\n"
34c4b47b 3939 "Unit Commands:\n"
729e3769 3940 " list-units List loaded units\n"
ee5762e3
LP
3941 " start [NAME...] Start (activate) one or more units\n"
3942 " stop [NAME...] Stop (deactivate) one or more units\n"
7e4249b9 3943 " reload [NAME...] Reload one or more units\n"
6f28c033
LP
3944 " restart [NAME...] Start or restart one or more units\n"
3945 " try-restart [NAME...] Restart one or more units if active\n"
3946 " reload-or-restart [NAME...] Reload one or more units is possible,\n"
3947 " otherwise start or restart\n"
3948 " reload-or-try-restart [NAME...] Reload one or more units is possible,\n"
3949 " otherwise restart if active\n"
7e4249b9 3950 " isolate [NAME] Start one unit and stop all others\n"
8a0867d6 3951 " kill [NAME...] Send signal to processes of a unit\n"
ee5762e3 3952 " is-active [NAME...] Check whether units are active\n"
75676b72 3953 " status [NAME...|PID...] Show runtime status of one or more units\n"
6f28c033 3954 " show [NAME...|JOB...] Show properties of one or more\n"
ee5762e3 3955 " units/jobs or the manager\n"
fdf20a31
MM
3956 " reset-failed [NAME...] Reset failed state for all, one, or more\n"
3957 " units\n"
34c4b47b
LP
3958 " load [NAME...] Load one or more units\n\n"
3959 "Unit File Commands:\n"
729e3769 3960 " list-unit-files List installed unit files\n"
ee5762e3
LP
3961 " enable [NAME...] Enable one or more unit files\n"
3962 " disable [NAME...] Disable one or more unit files\n"
729e3769
LP
3963 " reenable [NAME...] Reenable one or more unit files\n"
3964 " preset [NAME...] Enable/disable one or more unit files\n"
3965 " based on preset configuration\n"
3966 " mask [NAME...] Mask one or more units\n"
3967 " unmask [NAME...] Unmask one or more units\n"
3968 " link [PATH...] Link one or more units files into\n"
3969 " the search path\n"
34c4b47b
LP
3970 " is-enabled [NAME...] Check whether unit files are enabled\n\n"
3971 "Job Commands:\n"
48220598 3972 " list-jobs List jobs\n"
34c4b47b
LP
3973 " cancel [JOB...] Cancel all, one, or more jobs\n\n"
3974 "Status Commands:\n"
7e4249b9 3975 " dump Dump server status\n"
34c4b47b
LP
3976 " dot Dump dependency graph for dot(1)\n\n"
3977 "Snapshot Commands:\n"
7e4249b9 3978 " snapshot [NAME] Create a snapshot\n"
34c4b47b
LP
3979 " delete [NAME...] Remove one or more snapshots\n\n"
3980 "Environment Commands:\n"
7e4249b9
LP
3981 " show-environment Dump environment\n"
3982 " set-environment [NAME=VALUE...] Set one or more environment variables\n"
34c4b47b
LP
3983 " unset-environment [NAME...] Unset one or more environment variables\n\n"
3984 "Manager Lifecycle Commands:\n"
3985 " daemon-reload Reload systemd manager configuration\n"
3986 " daemon-reexec Reexecute systemd manager\n\n"
3987 "System Commands:\n"
20b09ca7
LP
3988 " default Enter system default mode\n"
3989 " rescue Enter system rescue mode\n"
3990 " emergency Enter system emergency mode\n"
514f4ef5 3991 " halt Shut down and halt the system\n"
2e33c433 3992 " poweroff Shut down and power-off the system\n"
514f4ef5 3993 " reboot Shut down and reboot the system\n"
20b09ca7 3994 " kexec Shut down and reboot the system with kexec\n"
af2d49f7 3995 " exit Ask for user instance termination\n",
5b6319dc 3996 program_invocation_short_name);
7e4249b9
LP
3997
3998 return 0;
3999}
4000
e4b61340
LP
4001static int halt_help(void) {
4002
2e33c433 4003 printf("%s [OPTIONS...]\n\n"
e4b61340
LP
4004 "%s the system.\n\n"
4005 " --help Show this help\n"
4006 " --halt Halt the machine\n"
4007 " -p --poweroff Switch off the machine\n"
4008 " --reboot Reboot the machine\n"
2e33c433
LP
4009 " -f --force Force immediate halt/power-off/reboot\n"
4010 " -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
e4b61340 4011 " -d --no-wtmp Don't write wtmp record\n"
2e33c433
LP
4012 " -n --no-sync Don't sync before halt/power-off/reboot\n"
4013 " --no-wall Don't send wall message before halt/power-off/reboot\n",
e4b61340
LP
4014 program_invocation_short_name,
4015 arg_action == ACTION_REBOOT ? "Reboot" :
4016 arg_action == ACTION_POWEROFF ? "Power off" :
4017 "Halt");
4018
4019 return 0;
4020}
4021
4022static int shutdown_help(void) {
4023
08e4b1c5 4024 printf("%s [OPTIONS...] [TIME] [WALL...]\n\n"
e4b61340
LP
4025 "Shut down the system.\n\n"
4026 " --help Show this help\n"
4027 " -H --halt Halt the machine\n"
4028 " -P --poweroff Power-off the machine\n"
4029 " -r --reboot Reboot the machine\n"
4030 " -h Equivalent to --poweroff, overriden by --halt\n"
2e33c433 4031 " -k Don't halt/power-off/reboot, just send warnings\n"
f6144808 4032 " --no-wall Don't send wall message before halt/power-off/reboot\n"
f6144808 4033 " -c Cancel a pending shutdown\n",
e4b61340
LP
4034 program_invocation_short_name);
4035
4036 return 0;
4037}
4038
4039static int telinit_help(void) {
4040
2e33c433 4041 printf("%s [OPTIONS...] {COMMAND}\n\n"
514f4ef5
LP
4042 "Send control commands to the init daemon.\n\n"
4043 " --help Show this help\n"
2e33c433 4044 " --no-wall Don't send wall message before halt/power-off/reboot\n\n"
e4b61340
LP
4045 "Commands:\n"
4046 " 0 Power-off the machine\n"
4047 " 6 Reboot the machine\n"
514f4ef5
LP
4048 " 2, 3, 4, 5 Start runlevelX.target unit\n"
4049 " 1, s, S Enter rescue mode\n"
4050 " q, Q Reload init daemon configuration\n"
4051 " u, U Reexecute init daemon\n",
e4b61340
LP
4052 program_invocation_short_name);
4053
4054 return 0;
4055}
4056
4057static int runlevel_help(void) {
4058
2e33c433 4059 printf("%s [OPTIONS...]\n\n"
e4b61340
LP
4060 "Prints the previous and current runlevel of the init system.\n\n"
4061 " --help Show this help\n",
4062 program_invocation_short_name);
4063
4064 return 0;
4065}
4066
4067static int systemctl_parse_argv(int argc, char *argv[]) {
7e4249b9
LP
4068
4069 enum {
90d473a1 4070 ARG_FAIL = 0x100,
e67c3609 4071 ARG_IGNORE_DEPENDENCIES,
35df8f27 4072 ARG_VERSION,
af2d49f7 4073 ARG_USER,
7e4249b9 4074 ARG_SYSTEM,
ee5762e3 4075 ARG_GLOBAL,
6e905d93 4076 ARG_NO_BLOCK,
611efaac 4077 ARG_NO_PAGER,
4445a875
LP
4078 ARG_NO_WALL,
4079 ARG_ORDER,
8fe914ec 4080 ARG_REQUIRE,
be394c48 4081 ARG_ROOT,
ee5762e3 4082 ARG_FULL,
ee5762e3 4083 ARG_NO_RELOAD,
8a0867d6 4084 ARG_KILL_MODE,
501fc174 4085 ARG_KILL_WHO,
30732560 4086 ARG_NO_ASK_PASSWORD,
729e3769
LP
4087 ARG_FAILED,
4088 ARG_RUNTIME
7e4249b9
LP
4089 };
4090
4091 static const struct option options[] = {
ee5762e3 4092 { "help", no_argument, NULL, 'h' },
35df8f27 4093 { "version", no_argument, NULL, ARG_VERSION },
ee5762e3
LP
4094 { "type", required_argument, NULL, 't' },
4095 { "property", required_argument, NULL, 'p' },
4096 { "all", no_argument, NULL, 'a' },
30732560 4097 { "failed", no_argument, NULL, ARG_FAILED },
ee5762e3
LP
4098 { "full", no_argument, NULL, ARG_FULL },
4099 { "fail", no_argument, NULL, ARG_FAIL },
e67c3609 4100 { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES },
af2d49f7 4101 { "user", no_argument, NULL, ARG_USER },
ee5762e3
LP
4102 { "system", no_argument, NULL, ARG_SYSTEM },
4103 { "global", no_argument, NULL, ARG_GLOBAL },
4104 { "no-block", no_argument, NULL, ARG_NO_BLOCK },
0736af98 4105 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
ee5762e3
LP
4106 { "no-wall", no_argument, NULL, ARG_NO_WALL },
4107 { "quiet", no_argument, NULL, 'q' },
4108 { "order", no_argument, NULL, ARG_ORDER },
4109 { "require", no_argument, NULL, ARG_REQUIRE },
be394c48 4110 { "root", required_argument, NULL, ARG_ROOT },
b4f27ccc 4111 { "force", no_argument, NULL, 'f' },
ee5762e3 4112 { "no-reload", no_argument, NULL, ARG_NO_RELOAD },
69fc152f 4113 { "kill-mode", required_argument, NULL, ARG_KILL_MODE }, /* undocumented on purpose */
8a0867d6
LP
4114 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
4115 { "signal", required_argument, NULL, 's' },
501fc174 4116 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
a8f11321
LP
4117 { "host", required_argument, NULL, 'H' },
4118 { "privileged",no_argument, NULL, 'P' },
729e3769 4119 { "runtime", no_argument, NULL, ARG_RUNTIME },
ee5762e3 4120 { NULL, 0, NULL, 0 }
7e4249b9
LP
4121 };
4122
4123 int c;
4124
e4b61340 4125 assert(argc >= 0);
7e4249b9
LP
4126 assert(argv);
4127
501fc174
LP
4128 /* Only when running as systemctl we ask for passwords */
4129 arg_ask_password = true;
4130
a8f11321 4131 while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:P", options, NULL)) >= 0) {
7e4249b9
LP
4132
4133 switch (c) {
4134
4135 case 'h':
e4b61340 4136 systemctl_help();
7e4249b9 4137 return 0;
35df8f27
LP
4138
4139 case ARG_VERSION:
4140 puts(PACKAGE_STRING);
7d568925
LP
4141 puts(DISTRIBUTION);
4142 puts(SYSTEMD_FEATURES);
35df8f27 4143 return 0;
7e4249b9
LP
4144
4145 case 't':
4146 arg_type = optarg;
4147 break;
4148
ea4a240d
LP
4149 case 'p': {
4150 char **l;
4151
4152 if (!(l = strv_append(arg_property, optarg)))
4153 return -ENOMEM;
4154
4155 strv_free(arg_property);
4156 arg_property = l;
48220598
LP
4157
4158 /* If the user asked for a particular
4159 * property, show it to him, even if it is
4160 * empty. */
4161 arg_all = true;
4162 break;
ea4a240d 4163 }
48220598 4164
7e4249b9
LP
4165 case 'a':
4166 arg_all = true;
4167 break;
4168
90d473a1 4169 case ARG_FAIL:
e67c3609
LP
4170 arg_job_mode = "fail";
4171 break;
4172
4173 case ARG_IGNORE_DEPENDENCIES:
4174 arg_job_mode = "ignore-dependencies";
7e4249b9
LP
4175 break;
4176
af2d49f7 4177 case ARG_USER:
729e3769 4178 arg_scope = UNIT_FILE_USER;
7e4249b9
LP
4179 break;
4180
4181 case ARG_SYSTEM:
729e3769
LP
4182 arg_scope = UNIT_FILE_SYSTEM;
4183 break;
4184
4185 case ARG_GLOBAL:
4186 arg_scope = UNIT_FILE_GLOBAL;
7e4249b9
LP
4187 break;
4188
6e905d93
LP
4189 case ARG_NO_BLOCK:
4190 arg_no_block = true;
7e4249b9
LP
4191 break;
4192
611efaac
LP
4193 case ARG_NO_PAGER:
4194 arg_no_pager = true;
4195 break;
0736af98 4196
514f4ef5
LP
4197 case ARG_NO_WALL:
4198 arg_no_wall = true;
4199 break;
4200
4445a875
LP
4201 case ARG_ORDER:
4202 arg_dot = DOT_ORDER;
4203 break;
4204
4205 case ARG_REQUIRE:
4206 arg_dot = DOT_REQUIRE;
4207 break;
4208
be394c48
FC
4209 case ARG_ROOT:
4210 arg_root = optarg;
4211 break;
4212
8fe914ec
LP
4213 case ARG_FULL:
4214 arg_full = true;
4215 break;
4216
30732560
LP
4217 case ARG_FAILED:
4218 arg_failed = true;
4219 break;
4220
0183528f
LP
4221 case 'q':
4222 arg_quiet = true;
4223 break;
4224
b4f27ccc 4225 case 'f':
ee5762e3
LP
4226 arg_force = true;
4227 break;
4228
4229 case ARG_NO_RELOAD:
4230 arg_no_reload = true;
4231 break;
4232
8a0867d6
LP
4233 case ARG_KILL_WHO:
4234 arg_kill_who = optarg;
4235 break;
4236
4237 case ARG_KILL_MODE:
4238 arg_kill_mode = optarg;
4239 break;
4240
4241 case 's':
4242 if ((arg_signal = signal_from_string_try_harder(optarg)) < 0) {
4243 log_error("Failed to parse signal string %s.", optarg);
4244 return -EINVAL;
4245 }
4246 break;
4247
501fc174
LP
4248 case ARG_NO_ASK_PASSWORD:
4249 arg_ask_password = false;
4250 break;
4251
a8f11321
LP
4252 case 'P':
4253 arg_transport = TRANSPORT_POLKIT;
4254 break;
4255
4256 case 'H':
4257 arg_transport = TRANSPORT_SSH;
4258 arg_host = optarg;
4259 break;
4260
729e3769
LP
4261 case ARG_RUNTIME:
4262 arg_runtime = true;
4263 break;
4264
7e4249b9
LP
4265 case '?':
4266 return -EINVAL;
4267
4268 default:
4269 log_error("Unknown option code %c", c);
4270 return -EINVAL;
4271 }
4272 }
4273
729e3769 4274 if (arg_transport != TRANSPORT_NORMAL && arg_scope != UNIT_FILE_SYSTEM) {
a8f11321
LP
4275 log_error("Cannot access user instance remotely.");
4276 return -EINVAL;
4277 }
4278
7e4249b9
LP
4279 return 1;
4280}
4281
e4b61340
LP
4282static int halt_parse_argv(int argc, char *argv[]) {
4283
4284 enum {
4285 ARG_HELP = 0x100,
4286 ARG_HALT,
514f4ef5
LP
4287 ARG_REBOOT,
4288 ARG_NO_WALL
e4b61340
LP
4289 };
4290
4291 static const struct option options[] = {
4292 { "help", no_argument, NULL, ARG_HELP },
4293 { "halt", no_argument, NULL, ARG_HALT },
4294 { "poweroff", no_argument, NULL, 'p' },
4295 { "reboot", no_argument, NULL, ARG_REBOOT },
4296 { "force", no_argument, NULL, 'f' },
4297 { "wtmp-only", no_argument, NULL, 'w' },
4298 { "no-wtmp", no_argument, NULL, 'd' },
4299 { "no-sync", no_argument, NULL, 'n' },
514f4ef5 4300 { "no-wall", no_argument, NULL, ARG_NO_WALL },
e4b61340
LP
4301 { NULL, 0, NULL, 0 }
4302 };
4303
4304 int c, runlevel;
4305
4306 assert(argc >= 0);
4307 assert(argv);
4308
4309 if (utmp_get_runlevel(&runlevel, NULL) >= 0)
4310 if (runlevel == '0' || runlevel == '6')
4311 arg_immediate = true;
4312
4313 while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) {
4314 switch (c) {
4315
4316 case ARG_HELP:
4317 halt_help();
4318 return 0;
4319
4320 case ARG_HALT:
4321 arg_action = ACTION_HALT;
4322 break;
4323
4324 case 'p':
a042efad
MS
4325 if (arg_action != ACTION_REBOOT)
4326 arg_action = ACTION_POWEROFF;
e4b61340
LP
4327 break;
4328
4329 case ARG_REBOOT:
4330 arg_action = ACTION_REBOOT;
4331 break;
4332
4333 case 'f':
4334 arg_immediate = true;
4335 break;
4336
4337 case 'w':
4338 arg_dry = true;
4339 break;
4340
4341 case 'd':
4342 arg_no_wtmp = true;
4343 break;
4344
4345 case 'n':
4346 arg_no_sync = true;
4347 break;
4348
514f4ef5
LP
4349 case ARG_NO_WALL:
4350 arg_no_wall = true;
4351 break;
4352
e4b61340
LP
4353 case 'i':
4354 case 'h':
4355 /* Compatibility nops */
4356 break;
4357
4358 case '?':
4359 return -EINVAL;
4360
4361 default:
4362 log_error("Unknown option code %c", c);
4363 return -EINVAL;
4364 }
4365 }
4366
4367 if (optind < argc) {
4368 log_error("Too many arguments.");
4369 return -EINVAL;
4370 }
4371
4372 return 1;
4373}
4374
f6144808
LP
4375static int parse_time_spec(const char *t, usec_t *_u) {
4376 assert(t);
4377 assert(_u);
4378
4379 if (streq(t, "now"))
4380 *_u = 0;
1a639877 4381 else if (!strchr(t, ':')) {
f6144808
LP
4382 uint64_t u;
4383
1a639877 4384 if (safe_atou64(t, &u) < 0)
f6144808
LP
4385 return -EINVAL;
4386
4387 *_u = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u;
4388 } else {
4389 char *e = NULL;
4390 long hour, minute;
4391 struct tm tm;
4392 time_t s;
4393 usec_t n;
4394
4395 errno = 0;
4396 hour = strtol(t, &e, 10);
4397 if (errno != 0 || *e != ':' || hour < 0 || hour > 23)
4398 return -EINVAL;
4399
4400 minute = strtol(e+1, &e, 10);
4401 if (errno != 0 || *e != 0 || minute < 0 || minute > 59)
4402 return -EINVAL;
4403
4404 n = now(CLOCK_REALTIME);
08e4b1c5
LP
4405 s = (time_t) (n / USEC_PER_SEC);
4406
4407 zero(tm);
f6144808
LP
4408 assert_se(localtime_r(&s, &tm));
4409
4410 tm.tm_hour = (int) hour;
4411 tm.tm_min = (int) minute;
08e4b1c5 4412 tm.tm_sec = 0;
f6144808
LP
4413
4414 assert_se(s = mktime(&tm));
4415
4416 *_u = (usec_t) s * USEC_PER_SEC;
4417
4418 while (*_u <= n)
4419 *_u += USEC_PER_DAY;
4420 }
4421
4422 return 0;
4423}
4424
e4b61340
LP
4425static int shutdown_parse_argv(int argc, char *argv[]) {
4426
4427 enum {
4428 ARG_HELP = 0x100,
514f4ef5 4429 ARG_NO_WALL
e4b61340
LP
4430 };
4431
4432 static const struct option options[] = {
4433 { "help", no_argument, NULL, ARG_HELP },
4434 { "halt", no_argument, NULL, 'H' },
4435 { "poweroff", no_argument, NULL, 'P' },
4436 { "reboot", no_argument, NULL, 'r' },
514f4ef5 4437 { "no-wall", no_argument, NULL, ARG_NO_WALL },
e4b61340
LP
4438 { NULL, 0, NULL, 0 }
4439 };
4440
f6144808 4441 int c, r;
e4b61340
LP
4442
4443 assert(argc >= 0);
4444 assert(argv);
4445
f6144808 4446 while ((c = getopt_long(argc, argv, "HPrhkt:afFc", options, NULL)) >= 0) {
e4b61340
LP
4447 switch (c) {
4448
4449 case ARG_HELP:
4450 shutdown_help();
4451 return 0;
4452
4453 case 'H':
4454 arg_action = ACTION_HALT;
4455 break;
4456
4457 case 'P':
4458 arg_action = ACTION_POWEROFF;
4459 break;
4460
4461 case 'r':
5622dde3
KS
4462 if (kexec_loaded())
4463 arg_action = ACTION_KEXEC;
4464 else
4465 arg_action = ACTION_REBOOT;
e4b61340
LP
4466 break;
4467
4468 case 'h':
4469 if (arg_action != ACTION_HALT)
4470 arg_action = ACTION_POWEROFF;
4471 break;
4472
4473 case 'k':
4474 arg_dry = true;
4475 break;
4476
514f4ef5
LP
4477 case ARG_NO_WALL:
4478 arg_no_wall = true;
4479 break;
4480
e4b61340
LP
4481 case 't':
4482 case 'a':
4483 /* Compatibility nops */
4484 break;
4485
f6144808
LP
4486 case 'c':
4487 arg_action = ACTION_CANCEL_SHUTDOWN;
4488 break;
4489
e4b61340
LP
4490 case '?':
4491 return -EINVAL;
4492
4493 default:
4494 log_error("Unknown option code %c", c);
4495 return -EINVAL;
4496 }
4497 }
4498
6b5ad000 4499 if (argc > optind) {
f6144808
LP
4500 if ((r = parse_time_spec(argv[optind], &arg_when)) < 0) {
4501 log_error("Failed to parse time specification: %s", argv[optind]);
4502 return r;
4503 }
6b5ad000 4504 } else
08e4b1c5 4505 arg_when = now(CLOCK_REALTIME) + USEC_PER_MINUTE;
442b9094 4506
f6144808 4507 /* We skip the time argument */
e4b61340
LP
4508 if (argc > optind + 1)
4509 arg_wall = argv + optind + 1;
4510
4511 optind = argc;
4512
4513 return 1;
e4b61340
LP
4514}
4515
4516static int telinit_parse_argv(int argc, char *argv[]) {
4517
4518 enum {
4519 ARG_HELP = 0x100,
514f4ef5 4520 ARG_NO_WALL
e4b61340
LP
4521 };
4522
4523 static const struct option options[] = {
4524 { "help", no_argument, NULL, ARG_HELP },
514f4ef5 4525 { "no-wall", no_argument, NULL, ARG_NO_WALL },
e4b61340
LP
4526 { NULL, 0, NULL, 0 }
4527 };
4528
4529 static const struct {
4530 char from;
4531 enum action to;
4532 } table[] = {
4533 { '0', ACTION_POWEROFF },
4534 { '6', ACTION_REBOOT },
ef2f1067 4535 { '1', ACTION_RESCUE },
e4b61340
LP
4536 { '2', ACTION_RUNLEVEL2 },
4537 { '3', ACTION_RUNLEVEL3 },
4538 { '4', ACTION_RUNLEVEL4 },
4539 { '5', ACTION_RUNLEVEL5 },
4540 { 's', ACTION_RESCUE },
4541 { 'S', ACTION_RESCUE },
4542 { 'q', ACTION_RELOAD },
4543 { 'Q', ACTION_RELOAD },
4544 { 'u', ACTION_REEXEC },
4545 { 'U', ACTION_REEXEC }
4546 };
4547
4548 unsigned i;
4549 int c;
4550
4551 assert(argc >= 0);
4552 assert(argv);
4553
4554 while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
4555 switch (c) {
4556
4557 case ARG_HELP:
4558 telinit_help();
4559 return 0;
4560
514f4ef5
LP
4561 case ARG_NO_WALL:
4562 arg_no_wall = true;
4563 break;
4564
e4b61340
LP
4565 case '?':
4566 return -EINVAL;
4567
4568 default:
4569 log_error("Unknown option code %c", c);
4570 return -EINVAL;
4571 }
4572 }
4573
4574 if (optind >= argc) {
2f02ce40 4575 telinit_help();
e4b61340
LP
4576 return -EINVAL;
4577 }
4578
4579 if (optind + 1 < argc) {
4580 log_error("Too many arguments.");
4581 return -EINVAL;
4582 }
4583
4584 if (strlen(argv[optind]) != 1) {
4585 log_error("Expected single character argument.");
4586 return -EINVAL;
4587 }
4588
4589 for (i = 0; i < ELEMENTSOF(table); i++)
4590 if (table[i].from == argv[optind][0])
4591 break;
4592
4593 if (i >= ELEMENTSOF(table)) {
4594 log_error("Unknown command %s.", argv[optind]);
4595 return -EINVAL;
4596 }
4597
4598 arg_action = table[i].to;
4599
4600 optind ++;
4601
4602 return 1;
4603}
4604
4605static int runlevel_parse_argv(int argc, char *argv[]) {
4606
4607 enum {
4608 ARG_HELP = 0x100,
4609 };
4610
4611 static const struct option options[] = {
4612 { "help", no_argument, NULL, ARG_HELP },
4613 { NULL, 0, NULL, 0 }
4614 };
4615
4616 int c;
4617
4618 assert(argc >= 0);
4619 assert(argv);
4620
4621 while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
4622 switch (c) {
4623
4624 case ARG_HELP:
4625 runlevel_help();
4626 return 0;
4627
4628 case '?':
4629 return -EINVAL;
4630
4631 default:
4632 log_error("Unknown option code %c", c);
4633 return -EINVAL;
4634 }
4635 }
4636
4637 if (optind < argc) {
4638 log_error("Too many arguments.");
4639 return -EINVAL;
4640 }
4641
4642 return 1;
4643}
4644
4645static int parse_argv(int argc, char *argv[]) {
4646 assert(argc >= 0);
4647 assert(argv);
4648
4649 if (program_invocation_short_name) {
4650
4651 if (strstr(program_invocation_short_name, "halt")) {
4652 arg_action = ACTION_HALT;
4653 return halt_parse_argv(argc, argv);
4654 } else if (strstr(program_invocation_short_name, "poweroff")) {
4655 arg_action = ACTION_POWEROFF;
4656 return halt_parse_argv(argc, argv);
4657 } else if (strstr(program_invocation_short_name, "reboot")) {
5622dde3
KS
4658 if (kexec_loaded())
4659 arg_action = ACTION_KEXEC;
4660 else
4661 arg_action = ACTION_REBOOT;
e4b61340
LP
4662 return halt_parse_argv(argc, argv);
4663 } else if (strstr(program_invocation_short_name, "shutdown")) {
4664 arg_action = ACTION_POWEROFF;
4665 return shutdown_parse_argv(argc, argv);
4666 } else if (strstr(program_invocation_short_name, "init")) {
d5ca5f11
LP
4667
4668 if (sd_booted() > 0) {
4669 arg_action = ACTION_INVALID;
4670 return telinit_parse_argv(argc, argv);
4671 } else {
4672 /* Hmm, so some other init system is
4673 * running, we need to forward this
4674 * request to it. For now we simply
4675 * guess that it is Upstart. */
4676
4677 execv("/lib/upstart/telinit", argv);
4678
4679 log_error("Couldn't find an alternative telinit implementation to spawn.");
4680 return -EIO;
4681 }
4682
e4b61340
LP
4683 } else if (strstr(program_invocation_short_name, "runlevel")) {
4684 arg_action = ACTION_RUNLEVEL;
4685 return runlevel_parse_argv(argc, argv);
4686 }
4687 }
4688
4689 arg_action = ACTION_SYSTEMCTL;
4690 return systemctl_parse_argv(argc, argv);
4691}
4692
d55ae9e6 4693static int action_to_runlevel(void) {
eb22ac37
LP
4694
4695 static const char table[_ACTION_MAX] = {
4696 [ACTION_HALT] = '0',
4697 [ACTION_POWEROFF] = '0',
4698 [ACTION_REBOOT] = '6',
4699 [ACTION_RUNLEVEL2] = '2',
4700 [ACTION_RUNLEVEL3] = '3',
4701 [ACTION_RUNLEVEL4] = '4',
4702 [ACTION_RUNLEVEL5] = '5',
4703 [ACTION_RESCUE] = '1'
4704 };
4705
d55ae9e6
LP
4706 assert(arg_action < _ACTION_MAX);
4707
4708 return table[arg_action];
4709}
4710
f1c5860b 4711static int talk_upstart(void) {
d55ae9e6
LP
4712 DBusMessage *m = NULL, *reply = NULL;
4713 DBusError error;
4714 int previous, rl, r;
4715 char
4716 env1_buf[] = "RUNLEVEL=X",
4717 env2_buf[] = "PREVLEVEL=X";
4718 char *env1 = env1_buf, *env2 = env2_buf;
4719 const char *emit = "runlevel";
4720 dbus_bool_t b_false = FALSE;
4721 DBusMessageIter iter, sub;
f1c5860b 4722 DBusConnection *bus;
d55ae9e6
LP
4723
4724 dbus_error_init(&error);
4725
4726 if (!(rl = action_to_runlevel()))
4727 return 0;
4728
4729 if (utmp_get_runlevel(&previous, NULL) < 0)
4730 previous = 'N';
4731
b574246b 4732 if (!(bus = dbus_connection_open_private("unix:abstract=/com/ubuntu/upstart", &error))) {
f1c5860b
LP
4733 if (dbus_error_has_name(&error, DBUS_ERROR_NO_SERVER)) {
4734 r = 0;
4735 goto finish;
4736 }
4737
4cf5d675 4738 log_error("Failed to connect to Upstart bus: %s", bus_error_message(&error));
f1c5860b
LP
4739 r = -EIO;
4740 goto finish;
4741 }
4742
4743 if ((r = bus_check_peercred(bus)) < 0) {
4744 log_error("Failed to verify owner of bus.");
4745 goto finish;
4746 }
4747
d55ae9e6
LP
4748 if (!(m = dbus_message_new_method_call(
4749 "com.ubuntu.Upstart",
4750 "/com/ubuntu/Upstart",
4751 "com.ubuntu.Upstart0_6",
4752 "EmitEvent"))) {
4753
4754 log_error("Could not allocate message.");
f1c5860b
LP
4755 r = -ENOMEM;
4756 goto finish;
d55ae9e6
LP
4757 }
4758
4759 dbus_message_iter_init_append(m, &iter);
4760
4761 env1_buf[sizeof(env1_buf)-2] = rl;
4762 env2_buf[sizeof(env2_buf)-2] = previous;
4763
4764 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &emit) ||
4765 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub) ||
4766 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env1) ||
4767 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env2) ||
4768 !dbus_message_iter_close_container(&iter, &sub) ||
4769 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b_false)) {
4770 log_error("Could not append arguments to message.");
4771 r = -ENOMEM;
4772 goto finish;
4773 }
4774
4775 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4776
4777 if (error_is_no_service(&error)) {
aabd9b11 4778 r = -EADDRNOTAVAIL;
d55ae9e6
LP
4779 goto finish;
4780 }
4781
4cf5d675 4782 log_error("Failed to issue method call: %s", bus_error_message(&error));
d55ae9e6
LP
4783 r = -EIO;
4784 goto finish;
4785 }
4786
0a55b298 4787 r = 1;
d55ae9e6
LP
4788
4789finish:
4790 if (m)
4791 dbus_message_unref(m);
4792
4793 if (reply)
4794 dbus_message_unref(reply);
4795
b574246b 4796 if (bus) {
5d452f9c 4797 dbus_connection_flush(bus);
b574246b 4798 dbus_connection_close(bus);
f1c5860b 4799 dbus_connection_unref(bus);
b574246b 4800 }
f1c5860b 4801
d55ae9e6
LP
4802 dbus_error_free(&error);
4803
4804 return r;
4805}
4806
4807static int talk_initctl(void) {
eb22ac37
LP
4808 struct init_request request;
4809 int r, fd;
d55ae9e6 4810 char rl;
eb22ac37 4811
d55ae9e6 4812 if (!(rl = action_to_runlevel()))
eb22ac37
LP
4813 return 0;
4814
4815 zero(request);
4816 request.magic = INIT_MAGIC;
4817 request.sleeptime = 0;
4818 request.cmd = INIT_CMD_RUNLVL;
d55ae9e6
LP
4819 request.runlevel = rl;
4820
4821 if ((fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY)) < 0) {
4822
4823 if (errno == ENOENT)
4824 return 0;
eb22ac37 4825
d55ae9e6 4826 log_error("Failed to open "INIT_FIFO": %m");
eb22ac37 4827 return -errno;
d55ae9e6 4828 }
eb22ac37 4829
d55ae9e6 4830 errno = 0;
eb22ac37
LP
4831 r = loop_write(fd, &request, sizeof(request), false) != sizeof(request);
4832 close_nointr_nofail(fd);
4833
d55ae9e6
LP
4834 if (r < 0) {
4835 log_error("Failed to write to "INIT_FIFO": %m");
eb22ac37 4836 return errno ? -errno : -EIO;
d55ae9e6 4837 }
eb22ac37
LP
4838
4839 return 1;
e4b61340
LP
4840}
4841
ee5762e3 4842static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
7e4249b9 4843
7e4249b9
LP
4844 static const struct {
4845 const char* verb;
4846 const enum {
4847 MORE,
4848 LESS,
4849 EQUAL
4850 } argc_cmp;
4851 const int argc;
729e3769 4852 int (* const dispatch)(DBusConnection *bus, char **args);
7e4249b9 4853 } verbs[] = {
ee5762e3 4854 { "list-units", LESS, 1, list_units },
729e3769 4855 { "list-unit-files", EQUAL, 1, list_unit_files },
ee5762e3
LP
4856 { "list-jobs", EQUAL, 1, list_jobs },
4857 { "clear-jobs", EQUAL, 1, daemon_reload },
4858 { "load", MORE, 2, load_unit },
4859 { "cancel", MORE, 2, cancel_job },
4860 { "start", MORE, 2, start_unit },
4861 { "stop", MORE, 2, start_unit },
a76f7be2 4862 { "condstop", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
ee5762e3
LP
4863 { "reload", MORE, 2, start_unit },
4864 { "restart", MORE, 2, start_unit },
4865 { "try-restart", MORE, 2, start_unit },
4866 { "reload-or-restart", MORE, 2, start_unit },
4867 { "reload-or-try-restart", MORE, 2, start_unit },
4868 { "force-reload", MORE, 2, start_unit }, /* For compatibility with SysV */
64e5f1b7 4869 { "condreload", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
ee5762e3
LP
4870 { "condrestart", MORE, 2, start_unit }, /* For compatibility with RH */
4871 { "isolate", EQUAL, 2, start_unit },
8a0867d6 4872 { "kill", MORE, 2, kill_unit },
ee5762e3
LP
4873 { "is-active", MORE, 2, check_unit },
4874 { "check", MORE, 2, check_unit },
4875 { "show", MORE, 1, show },
4876 { "status", MORE, 2, show },
ee5762e3
LP
4877 { "dump", EQUAL, 1, dump },
4878 { "dot", EQUAL, 1, dot },
4879 { "snapshot", LESS, 2, snapshot },
4880 { "delete", MORE, 2, delete_snapshot },
4881 { "daemon-reload", EQUAL, 1, daemon_reload },
4882 { "daemon-reexec", EQUAL, 1, daemon_reload },
ee5762e3
LP
4883 { "show-environment", EQUAL, 1, show_enviroment },
4884 { "set-environment", MORE, 2, set_environment },
4885 { "unset-environment", MORE, 2, set_environment },
4886 { "halt", EQUAL, 1, start_special },
4887 { "poweroff", EQUAL, 1, start_special },
4888 { "reboot", EQUAL, 1, start_special },
20b09ca7 4889 { "kexec", EQUAL, 1, start_special },
ee5762e3
LP
4890 { "default", EQUAL, 1, start_special },
4891 { "rescue", EQUAL, 1, start_special },
4892 { "emergency", EQUAL, 1, start_special },
20b09ca7 4893 { "exit", EQUAL, 1, start_special },
fdf20a31 4894 { "reset-failed", MORE, 1, reset_failed },
ee5762e3
LP
4895 { "enable", MORE, 2, enable_unit },
4896 { "disable", MORE, 2, enable_unit },
729e3769
LP
4897 { "is-enabled", MORE, 2, unit_is_enabled },
4898 { "reenable", MORE, 2, enable_unit },
4899 { "preset", MORE, 2, enable_unit },
4900 { "mask", MORE, 2, enable_unit },
4901 { "unmask", MORE, 2, enable_unit },
4902 { "link", MORE, 2, enable_unit }
7e4249b9
LP
4903 };
4904
e4b61340 4905 int left;
7e4249b9 4906 unsigned i;
7e4249b9 4907
e4b61340
LP
4908 assert(argc >= 0);
4909 assert(argv);
ee5762e3 4910 assert(error);
7e4249b9
LP
4911
4912 left = argc - optind;
4913
4914 if (left <= 0)
4915 /* Special rule: no arguments means "list-units" */
4916 i = 0;
4917 else {
0183528f
LP
4918 if (streq(argv[optind], "help")) {
4919 systemctl_help();
4920 return 0;
4921 }
4922
7e4249b9
LP
4923 for (i = 0; i < ELEMENTSOF(verbs); i++)
4924 if (streq(argv[optind], verbs[i].verb))
4925 break;
4926
4927 if (i >= ELEMENTSOF(verbs)) {
4928 log_error("Unknown operation %s", argv[optind]);
e4b61340 4929 return -EINVAL;
7e4249b9
LP
4930 }
4931 }
4932
4933 switch (verbs[i].argc_cmp) {
4934
4935 case EQUAL:
4936 if (left != verbs[i].argc) {
4937 log_error("Invalid number of arguments.");
e4b61340 4938 return -EINVAL;
7e4249b9
LP
4939 }
4940
4941 break;
4942
4943 case MORE:
4944 if (left < verbs[i].argc) {
4945 log_error("Too few arguments.");
e4b61340 4946 return -EINVAL;
7e4249b9
LP
4947 }
4948
4949 break;
4950
4951 case LESS:
4952 if (left > verbs[i].argc) {
4953 log_error("Too many arguments.");
e4b61340 4954 return -EINVAL;
7e4249b9
LP
4955 }
4956
4957 break;
4958
4959 default:
4960 assert_not_reached("Unknown comparison operator.");
4961 }
4962
ee5762e3
LP
4963 /* Require a bus connection for all operations but
4964 * enable/disable */
729e3769
LP
4965 if (!streq(verbs[i].verb, "enable") &&
4966 !streq(verbs[i].verb, "disable") &&
4967 !streq(verbs[i].verb, "is-enable") &&
4968 !streq(verbs[i].verb, "reenable") &&
4969 !streq(verbs[i].verb, "preset") &&
4970 !streq(verbs[i].verb, "mask") &&
4971 !streq(verbs[i].verb, "unmask") &&
4972 !streq(verbs[i].verb, "link")) {
82e23ddd
LP
4973
4974 if (running_in_chroot() > 0) {
4975 log_info("Running in chroot, ignoring request.");
4976 return 0;
4977 }
4978
729e3769 4979 if (!bus && !avoid_bus()) {
82e23ddd
LP
4980 log_error("Failed to get D-Bus connection: %s", error->message);
4981 return -EIO;
4982 }
ee5762e3
LP
4983 }
4984
729e3769 4985 return verbs[i].dispatch(bus, argv + optind);
e4b61340
LP
4986}
4987
52c00215 4988static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
f6144808
LP
4989 int fd = -1;
4990 struct msghdr msghdr;
4991 struct iovec iovec;
4992 union sockaddr_union sockaddr;
f6144808
LP
4993 struct shutdownd_command c;
4994
4995 zero(c);
4996 c.elapse = t;
4997 c.mode = mode;
52c00215 4998 c.dry_run = dry_run;
9be9828c
LP
4999 c.warn_wall = warn;
5000
5001 if (message)
5002 strncpy(c.wall_message, message, sizeof(c.wall_message));
f6144808
LP
5003
5004 if ((fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0)) < 0)
5005 return -errno;
5006
5007 zero(sockaddr);
5008 sockaddr.sa.sa_family = AF_UNIX;
5009 sockaddr.un.sun_path[0] = 0;
2b583ce6 5010 strncpy(sockaddr.un.sun_path, "/run/systemd/shutdownd", sizeof(sockaddr.un.sun_path));
f6144808
LP
5011
5012 zero(iovec);
5013 iovec.iov_base = (char*) &c;
5014 iovec.iov_len = sizeof(c);
5015
f6144808
LP
5016 zero(msghdr);
5017 msghdr.msg_name = &sockaddr;
2b583ce6 5018 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/systemd/shutdownd") - 1;
f6144808
LP
5019
5020 msghdr.msg_iov = &iovec;
5021 msghdr.msg_iovlen = 1;
f6144808
LP
5022
5023 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
5024 close_nointr_nofail(fd);
5025 return -errno;
5026 }
5027
5028 close_nointr_nofail(fd);
5029 return 0;
5030}
5031
e4b61340 5032static int reload_with_fallback(DBusConnection *bus) {
e4b61340
LP
5033
5034 if (bus) {
5035 /* First, try systemd via D-Bus. */
d76702a7 5036 if (daemon_reload(bus, NULL) >= 0)
e4b61340
LP
5037 return 0;
5038 }
5039
5040 /* Nothing else worked, so let's try signals */
5041 assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC);
5042
5043 if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) {
5044 log_error("kill() failed: %m");
5045 return -errno;
5046 }
5047
5048 return 0;
5049}
5050
5051static int start_with_fallback(DBusConnection *bus) {
e4b61340
LP
5052
5053 if (bus) {
5054 /* First, try systemd via D-Bus. */
729e3769 5055 if (start_unit(bus, NULL) >= 0)
983d9c90 5056 goto done;
e4b61340
LP
5057 }
5058
ec7f7f20
LP
5059 /* Hmm, talking to systemd via D-Bus didn't work. Then
5060 * let's try to talk to Upstart via D-Bus. */
e364ad06 5061 if (talk_upstart() > 0)
ec7f7f20
LP
5062 goto done;
5063
e4b61340
LP
5064 /* Nothing else worked, so let's try
5065 * /dev/initctl */
fbc43921 5066 if (talk_initctl() > 0)
983d9c90 5067 goto done;
d55ae9e6
LP
5068
5069 log_error("Failed to talk to init daemon.");
5070 return -EIO;
983d9c90
LP
5071
5072done:
5073 warn_wall(arg_action);
5074 return 0;
e4b61340
LP
5075}
5076
5077static int halt_main(DBusConnection *bus) {
5078 int r;
5079
bc8c2f5c 5080 if (geteuid() != 0) {
cc8a7a61 5081 log_error("Must be root.");
bc8c2f5c
LP
5082 return -EPERM;
5083 }
5084
f6144808 5085 if (arg_when > 0) {
9be9828c 5086 char *m;
08e4b1c5 5087 char date[FORMAT_TIMESTAMP_MAX];
9be9828c
LP
5088
5089 m = strv_join(arg_wall, " ");
5090 r = send_shutdownd(arg_when,
5091 arg_action == ACTION_HALT ? 'H' :
5092 arg_action == ACTION_POWEROFF ? 'P' :
5093 'r',
52c00215 5094 arg_dry,
9be9828c
LP
5095 !arg_no_wall,
5096 m);
5097 free(m);
5098
5099 if (r < 0)
f6144808 5100 log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r));
08e4b1c5
LP
5101 else {
5102 log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.",
5103 format_timestamp(date, sizeof(date), arg_when));
f6144808 5104 return 0;
08e4b1c5 5105 }
f6144808
LP
5106 }
5107
d47b555b 5108 if (!arg_dry && !arg_immediate)
e4b61340
LP
5109 return start_with_fallback(bus);
5110
d90e1a30
LP
5111 if (!arg_no_wtmp) {
5112 if (sd_booted() > 0)
5113 log_debug("Not writing utmp record, assuming that systemd-update-utmp is used.");
5114 else if ((r = utmp_put_shutdown(0)) < 0)
e4b61340 5115 log_warning("Failed to write utmp record: %s", strerror(-r));
d90e1a30 5116 }
e4b61340
LP
5117
5118 if (!arg_no_sync)
5119 sync();
5120
5121 if (arg_dry)
5122 return 0;
5123
5124 /* Make sure C-A-D is handled by the kernel from this
5125 * point on... */
5126 reboot(RB_ENABLE_CAD);
5127
5128 switch (arg_action) {
5129
5130 case ACTION_HALT:
3059b1c1 5131 log_info("Halting.");
e4b61340
LP
5132 reboot(RB_HALT_SYSTEM);
5133 break;
5134
5135 case ACTION_POWEROFF:
3059b1c1 5136 log_info("Powering off.");
e4b61340
LP
5137 reboot(RB_POWER_OFF);
5138 break;
5139
5140 case ACTION_REBOOT:
3059b1c1 5141 log_info("Rebooting.");
e4b61340
LP
5142 reboot(RB_AUTOBOOT);
5143 break;
5144
5145 default:
5146 assert_not_reached("Unknown halt action.");
5147 }
5148
5149 /* We should never reach this. */
5150 return -ENOSYS;
5151}
5152
5153static int runlevel_main(void) {
5154 int r, runlevel, previous;
5155
729e3769
LP
5156 r = utmp_get_runlevel(&runlevel, &previous);
5157 if (r < 0) {
5158 puts("unknown");
e4b61340
LP
5159 return r;
5160 }
5161
5162 printf("%c %c\n",
5163 previous <= 0 ? 'N' : previous,
5164 runlevel <= 0 ? 'N' : runlevel);
5165
5166 return 0;
5167}
5168
5169int main(int argc, char*argv[]) {
22f4096c 5170 int r, retval = EXIT_FAILURE;
e4b61340
LP
5171 DBusConnection *bus = NULL;
5172 DBusError error;
5173
5174 dbus_error_init(&error);
5175
5176 log_parse_environment();
2396fb04 5177 log_open();
e4b61340
LP
5178
5179 if ((r = parse_argv(argc, argv)) < 0)
5180 goto finish;
5181 else if (r == 0) {
22f4096c 5182 retval = EXIT_SUCCESS;
7e4249b9
LP
5183 goto finish;
5184 }
5185
e4b61340
LP
5186 /* /sbin/runlevel doesn't need to communicate via D-Bus, so
5187 * let's shortcut this */
5188 if (arg_action == ACTION_RUNLEVEL) {
22f4096c
LP
5189 r = runlevel_main();
5190 retval = r < 0 ? EXIT_FAILURE : r;
e4b61340
LP
5191 goto finish;
5192 }
5193
82e23ddd
LP
5194 if (running_in_chroot() > 0 && arg_action != ACTION_SYSTEMCTL) {
5195 log_info("Running in chroot, ignoring request.");
5196 retval = 0;
5197 goto finish;
5198 }
5199
729e3769
LP
5200 if (!avoid_bus()) {
5201 if (arg_transport == TRANSPORT_NORMAL)
5202 bus_connect(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &bus, &private_bus, &error);
5203 else if (arg_transport == TRANSPORT_POLKIT) {
5204 bus_connect_system_polkit(&bus, &error);
5205 private_bus = false;
5206 } else if (arg_transport == TRANSPORT_SSH) {
5207 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
5208 private_bus = false;
5209 } else
5210 assert_not_reached("Uh, invalid transport...");
5211 }
e4b61340
LP
5212
5213 switch (arg_action) {
5214
22f4096c
LP
5215 case ACTION_SYSTEMCTL:
5216 r = systemctl_main(bus, argc, argv, &error);
e4b61340 5217 break;
e4b61340
LP
5218
5219 case ACTION_HALT:
5220 case ACTION_POWEROFF:
5221 case ACTION_REBOOT:
5622dde3 5222 case ACTION_KEXEC:
22f4096c 5223 r = halt_main(bus);
e4b61340
LP
5224 break;
5225
e4b61340
LP
5226 case ACTION_RUNLEVEL2:
5227 case ACTION_RUNLEVEL3:
5228 case ACTION_RUNLEVEL4:
5229 case ACTION_RUNLEVEL5:
5230 case ACTION_RESCUE:
514f4ef5 5231 case ACTION_EMERGENCY:
eb22ac37 5232 case ACTION_DEFAULT:
22f4096c 5233 r = start_with_fallback(bus);
e4b61340 5234 break;
7e4249b9 5235
e4b61340
LP
5236 case ACTION_RELOAD:
5237 case ACTION_REEXEC:
22f4096c 5238 r = reload_with_fallback(bus);
e4b61340
LP
5239 break;
5240
f6144808 5241 case ACTION_CANCEL_SHUTDOWN:
52c00215 5242 r = send_shutdownd(0, 0, false, false, NULL);
f6144808
LP
5243 break;
5244
eb22ac37
LP
5245 case ACTION_INVALID:
5246 case ACTION_RUNLEVEL:
e4b61340
LP
5247 default:
5248 assert_not_reached("Unknown action");
5249 }
7e4249b9 5250
22f4096c
LP
5251 retval = r < 0 ? EXIT_FAILURE : r;
5252
7e4249b9 5253finish:
b574246b 5254 if (bus) {
5d452f9c 5255 dbus_connection_flush(bus);
b574246b 5256 dbus_connection_close(bus);
7e4249b9 5257 dbus_connection_unref(bus);
b574246b 5258 }
7e4249b9 5259
e4b61340
LP
5260 dbus_error_free(&error);
5261
7e4249b9
LP
5262 dbus_shutdown();
5263
ea4a240d
LP
5264 strv_free(arg_property);
5265
1888c907 5266 pager_close();
c0f9c7da 5267 agent_close();
1888c907 5268
7e4249b9
LP
5269 return retval;
5270}