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