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