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