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