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