]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/systemctl/systemctl.c
util: more modernizations
[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
37370d0c 3554static int mangle_names(char **original_names, char ***mangled_names) {
a33fdebb 3555 char **i, **l, **name;
37370d0c 3556
a33fdebb
LP
3557 l = new(char*, strv_length(original_names) + 1);
3558 if (!l)
37370d0c
VP
3559 return log_oom();
3560
a33fdebb 3561 i = l;
37370d0c 3562 STRV_FOREACH(name, original_names) {
a33fdebb
LP
3563 *i = unit_name_mangle(*name);
3564 if (!*i) {
3565 strv_free(l);
37370d0c 3566 return log_oom();
a33fdebb
LP
3567 }
3568
3569 i++;
37370d0c 3570 }
a33fdebb
LP
3571
3572 *i = NULL;
3573 *mangled_names = l;
37370d0c
VP
3574
3575 return 0;
3576}
3577
729e3769
LP
3578static int enable_unit(DBusConnection *bus, char **args) {
3579 const char *verb = args[0];
3580 UnitFileChange *changes = NULL;
3581 unsigned n_changes = 0, i;
3582 int carries_install_info = -1;
3583 DBusMessage *m = NULL, *reply = NULL;
3584 int r;
3585 DBusError error;
37370d0c 3586 char **mangled_names = NULL;
ee5762e3 3587
729e3769
LP
3588 r = enable_sysv_units(args);
3589 if (r < 0)
3590 return r;
ee5762e3 3591
ab5919fa
MS
3592 if (!args[1])
3593 return 0;
3594
3595 dbus_error_init(&error);
3596
729e3769
LP
3597 if (!bus || avoid_bus()) {
3598 if (streq(verb, "enable")) {
3599 r = unit_file_enable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
3600 carries_install_info = r;
3601 } else if (streq(verb, "disable"))
3602 r = unit_file_disable(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
3603 else if (streq(verb, "reenable")) {
3604 r = unit_file_reenable(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
3605 carries_install_info = r;
3606 } else if (streq(verb, "link"))
3607 r = unit_file_link(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
3608 else if (streq(verb, "preset")) {
3609 r = unit_file_preset(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
3610 carries_install_info = r;
3611 } else if (streq(verb, "mask"))
3612 r = unit_file_mask(arg_scope, arg_runtime, arg_root, args+1, arg_force, &changes, &n_changes);
3613 else if (streq(verb, "unmask"))
3614 r = unit_file_unmask(arg_scope, arg_runtime, arg_root, args+1, &changes, &n_changes);
3615 else
3616 assert_not_reached("Unknown verb");
ee5762e3 3617
729e3769
LP
3618 if (r < 0) {
3619 log_error("Operation failed: %s", strerror(-r));
3620 goto finish;
ee5762e3
LP
3621 }
3622
d1f262fa
LP
3623 if (!arg_quiet) {
3624 for (i = 0; i < n_changes; i++) {
3625 if (changes[i].type == UNIT_FILE_SYMLINK)
3626 log_info("ln -s '%s' '%s'", changes[i].source, changes[i].path);
3627 else
3628 log_info("rm '%s'", changes[i].path);
3629 }
ee5762e3
LP
3630 }
3631
729e3769
LP
3632 } else {
3633 const char *method;
3634 bool send_force = true, expect_carries_install_info = false;
3635 dbus_bool_t a, b;
3636 DBusMessageIter iter, sub, sub2;
3637
3638 if (streq(verb, "enable")) {
3639 method = "EnableUnitFiles";
3640 expect_carries_install_info = true;
3641 } else if (streq(verb, "disable")) {
3642 method = "DisableUnitFiles";
3643 send_force = false;
3644 } else if (streq(verb, "reenable")) {
3645 method = "ReenableUnitFiles";
3646 expect_carries_install_info = true;
3647 } else if (streq(verb, "link"))
3648 method = "LinkUnitFiles";
3649 else if (streq(verb, "preset")) {
3650 method = "PresetUnitFiles";
3651 expect_carries_install_info = true;
3652 } else if (streq(verb, "mask"))
3653 method = "MaskUnitFiles";
3654 else if (streq(verb, "unmask")) {
3655 method = "UnmaskUnitFiles";
3656 send_force = false;
3657 } else
3658 assert_not_reached("Unknown verb");
3659
3660 m = dbus_message_new_method_call(
3661 "org.freedesktop.systemd1",
3662 "/org/freedesktop/systemd1",
3663 "org.freedesktop.systemd1.Manager",
3664 method);
3665 if (!m) {
0d0f0c50 3666 r = log_oom();
ee5762e3
LP
3667 goto finish;
3668 }
3669
729e3769 3670 dbus_message_iter_init_append(m, &iter);
ee5762e3 3671
37370d0c
VP
3672 r = mangle_names(args+1, &mangled_names);
3673 if(r < 0)
3674 goto finish;
3675
3676 r = bus_append_strv_iter(&iter, mangled_names);
729e3769
LP
3677 if (r < 0) {
3678 log_error("Failed to append unit files.");
ee5762e3
LP
3679 goto finish;
3680 }
3681
729e3769
LP
3682 a = arg_runtime;
3683 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &a)) {
3684 log_error("Failed to append runtime boolean.");
ee5762e3
LP
3685 r = -ENOMEM;
3686 goto finish;
3687 }
3688
729e3769
LP
3689 if (send_force) {
3690 b = arg_force;
be394c48 3691
729e3769
LP
3692 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b)) {
3693 log_error("Failed to append force boolean.");
3694 r = -ENOMEM;
3695 goto finish;
3696 }
09adcdf7 3697 }
ee5762e3 3698
729e3769
LP
3699 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
3700 if (!reply) {
3701 log_error("Failed to issue method call: %s", bus_error_message(&error));
3702 r = -EIO;
3703 goto finish;
ee5762e3
LP
3704 }
3705
729e3769
LP
3706 if (!dbus_message_iter_init(reply, &iter)) {
3707 log_error("Failed to initialize iterator.");
3708 goto finish;
3709 }
be394c48 3710
729e3769
LP
3711 if (expect_carries_install_info) {
3712 r = bus_iter_get_basic_and_next(&iter, DBUS_TYPE_BOOLEAN, &b, true);
3713 if (r < 0) {
3714 log_error("Failed to parse reply.");
3715 goto finish;
3716 }
ee5762e3 3717
729e3769 3718 carries_install_info = b;
ee5762e3
LP
3719 }
3720
729e3769
LP
3721 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
3722 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
3723 log_error("Failed to parse reply.");
3724 r = -EIO;
3725 goto finish;
ee5762e3
LP
3726 }
3727
729e3769
LP
3728 dbus_message_iter_recurse(&iter, &sub);
3729 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
3730 const char *type, *path, *source;
c8b2e52c 3731
729e3769
LP
3732 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
3733 log_error("Failed to parse reply.");
3734 r = -EIO;
3735 goto finish;
c8b2e52c
LP
3736 }
3737
729e3769 3738 dbus_message_iter_recurse(&sub, &sub2);
c8b2e52c 3739
729e3769
LP
3740 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
3741 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0 ||
3742 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &source, false) < 0) {
3743 log_error("Failed to parse reply.");
3744 r = -EIO;
3745 goto finish;
c8b2e52c
LP
3746 }
3747
d1f262fa
LP
3748 if (!arg_quiet) {
3749 if (streq(type, "symlink"))
3750 log_info("ln -s '%s' '%s'", source, path);
3751 else
3752 log_info("rm '%s'", path);
3753 }
b77398f7 3754
729e3769
LP
3755 dbus_message_iter_next(&sub);
3756 }
b77398f7 3757
729e3769
LP
3758 /* Try to reload if enabeld */
3759 if (!arg_no_reload)
3760 r = daemon_reload(bus, args);
b647f10d 3761 }
3d3961f2 3762
729e3769 3763 if (carries_install_info == 0)
34cdc274 3764 log_warning("The unit files have no [Install] section. They are not meant to be enabled using systemctl.");
ee5762e3 3765
729e3769
LP
3766finish:
3767 if (m)
3768 dbus_message_unref(m);
ee5762e3 3769
729e3769
LP
3770 if (reply)
3771 dbus_message_unref(reply);
ee5762e3 3772
729e3769 3773 unit_file_changes_free(changes, n_changes);
ee5762e3 3774
729e3769 3775 dbus_error_free(&error);
37370d0c
VP
3776
3777 strv_free(mangled_names);
3778
729e3769 3779 return r;
ee5762e3
LP
3780}
3781
729e3769 3782static int unit_is_enabled(DBusConnection *bus, char **args) {
ee5762e3
LP
3783 DBusError error;
3784 int r;
f22f08cd 3785 DBusMessage *reply = NULL;
729e3769
LP
3786 bool enabled;
3787 char **name;
ee5762e3
LP
3788
3789 dbus_error_init(&error);
3790
729e3769
LP
3791 r = enable_sysv_units(args);
3792 if (r < 0)
3793 return r;
ee5762e3 3794
729e3769 3795 enabled = r > 0;
ee5762e3 3796
729e3769 3797 if (!bus || avoid_bus()) {
ee5762e3 3798
729e3769
LP
3799 STRV_FOREACH(name, args+1) {
3800 UnitFileState state;
ee5762e3 3801
729e3769
LP
3802 state = unit_file_get_state(arg_scope, arg_root, *name);
3803 if (state < 0) {
3804 r = state;
3805 goto finish;
3806 }
ee5762e3 3807
729e3769
LP
3808 if (state == UNIT_FILE_ENABLED ||
3809 state == UNIT_FILE_ENABLED_RUNTIME ||
3810 state == UNIT_FILE_STATIC)
3811 enabled = true;
3812
3813 if (!arg_quiet)
3814 puts(unit_file_state_to_string(state));
71fad675 3815 }
ee5762e3 3816
729e3769
LP
3817 } else {
3818 STRV_FOREACH(name, args+1) {
3819 const char *s;
63a723f3 3820
f22f08cd
SP
3821 r = bus_method_call_with_reply (
3822 bus,
729e3769
LP
3823 "org.freedesktop.systemd1",
3824 "/org/freedesktop/systemd1",
3825 "org.freedesktop.systemd1.Manager",
f22f08cd
SP
3826 "GetUnitFileState",
3827 &reply,
3828 NULL,
3829 DBUS_TYPE_STRING, name,
3830 DBUS_TYPE_INVALID);
3831 if (r)
729e3769 3832 goto finish;
ee5762e3 3833
729e3769
LP
3834 if (!dbus_message_get_args(reply, &error,
3835 DBUS_TYPE_STRING, &s,
3836 DBUS_TYPE_INVALID)) {
3837 log_error("Failed to parse reply: %s", bus_error_message(&error));
3838 r = -EIO;
ee5762e3
LP
3839 goto finish;
3840 }
3841
729e3769 3842 dbus_message_unref(reply);
f22f08cd 3843 reply = NULL;
ee5762e3 3844
729e3769
LP
3845 if (streq(s, "enabled") ||
3846 streq(s, "enabled-runtime") ||
3847 streq(s, "static"))
3848 enabled = true;
3849
3850 if (!arg_quiet)
3851 puts(s);
560d8f23 3852 }
ee5762e3
LP
3853 }
3854
729e3769 3855 r = enabled ? 0 : 1;
ee5762e3 3856
729e3769 3857finish:
729e3769
LP
3858 if (reply)
3859 dbus_message_unref(reply);
ee5762e3 3860
729e3769 3861 dbus_error_free(&error);
ee5762e3
LP
3862 return r;
3863}
3864
e4b61340 3865static int systemctl_help(void) {
7e4249b9 3866
729e3769
LP
3867 pager_open_if_enabled();
3868
2e33c433 3869 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
729e3769 3870 "Query or send control commands to the systemd manager.\n\n"
8a0867d6
LP
3871 " -h --help Show this help\n"
3872 " --version Show package version\n"
3873 " -t --type=TYPE List only units of a particular type\n"
3874 " -p --property=NAME Show only properties by this name\n"
3875 " -a --all Show all units/properties, including dead/empty ones\n"
30732560 3876 " --failed Show only failed units\n"
8a0867d6
LP
3877 " --full Don't ellipsize unit names on output\n"
3878 " --fail When queueing a new job, fail if conflicting jobs are\n"
3879 " pending\n"
e67c3609
LP
3880 " --ignore-dependencies\n"
3881 " When queueing a new job, ignore all its dependencies\n"
a8f11321
LP
3882 " --kill-who=WHO Who to send signal to\n"
3883 " -s --signal=SIGNAL Which signal to send\n"
aca4c786 3884 " -H --host=[USER@]HOST\n"
a8f11321
LP
3885 " Show information for remote host\n"
3886 " -P --privileged Acquire privileges before execution\n"
8a0867d6
LP
3887 " -q --quiet Suppress output\n"
3888 " --no-block Do not wait until operation finished\n"
8a0867d6 3889 " --no-wall Don't send wall message before halt/power-off/reboot\n"
8a0867d6
LP
3890 " --no-reload When enabling/disabling unit files, don't reload daemon\n"
3891 " configuration\n"
ebed32bf 3892 " --no-legend Do not print a legend (column headers and hints)\n"
69fc152f 3893 " --no-pager Do not pipe output into a pager\n"
501fc174
LP
3894 " --no-ask-password\n"
3895 " Do not ask for system passwords\n"
a8f11321
LP
3896 " --order When generating graph for dot, show only order\n"
3897 " --require When generating graph for dot, show only requirement\n"
3898 " --system Connect to system manager\n"
3899 " --user Connect to user service manager\n"
3900 " --global Enable/disable unit files globally\n"
8a0867d6
LP
3901 " -f --force When enabling unit files, override existing symlinks\n"
3902 " When shutting down, execute action immediately\n"
729e3769 3903 " --root=PATH Enable unit files in the specified root directory\n"
df50185b
LP
3904 " --runtime Enable unit files only temporarily until next reboot\n"
3905 " -n --lines=INTEGER Journal entries to show\n"
3906 " --follow Follow journal\n"
d3f2bdbf 3907 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
a6e87e90 3908 " verbose, export, json, json-pretty, cat)\n\n"
34c4b47b 3909 "Unit Commands:\n"
729e3769 3910 " list-units List loaded units\n"
ee5762e3
LP
3911 " start [NAME...] Start (activate) one or more units\n"
3912 " stop [NAME...] Stop (deactivate) one or more units\n"
7e4249b9 3913 " reload [NAME...] Reload one or more units\n"
6f28c033
LP
3914 " restart [NAME...] Start or restart one or more units\n"
3915 " try-restart [NAME...] Restart one or more units if active\n"
3916 " reload-or-restart [NAME...] Reload one or more units is possible,\n"
3917 " otherwise start or restart\n"
3918 " reload-or-try-restart [NAME...] Reload one or more units is possible,\n"
3919 " otherwise restart if active\n"
7e4249b9 3920 " isolate [NAME] Start one unit and stop all others\n"
8a0867d6 3921 " kill [NAME...] Send signal to processes of a unit\n"
ee5762e3 3922 " is-active [NAME...] Check whether units are active\n"
75676b72 3923 " status [NAME...|PID...] Show runtime status of one or more units\n"
6f28c033 3924 " show [NAME...|JOB...] Show properties of one or more\n"
ee5762e3 3925 " units/jobs or the manager\n"
b43f208f 3926 " help [NAME...|PID...] Show manual for one or more units\n"
fdf20a31
MM
3927 " reset-failed [NAME...] Reset failed state for all, one, or more\n"
3928 " units\n"
34c4b47b
LP
3929 " load [NAME...] Load one or more units\n\n"
3930 "Unit File Commands:\n"
729e3769 3931 " list-unit-files List installed unit files\n"
ee5762e3
LP
3932 " enable [NAME...] Enable one or more unit files\n"
3933 " disable [NAME...] Disable one or more unit files\n"
729e3769
LP
3934 " reenable [NAME...] Reenable one or more unit files\n"
3935 " preset [NAME...] Enable/disable one or more unit files\n"
3936 " based on preset configuration\n"
3937 " mask [NAME...] Mask one or more units\n"
3938 " unmask [NAME...] Unmask one or more units\n"
3939 " link [PATH...] Link one or more units files into\n"
3940 " the search path\n"
34c4b47b
LP
3941 " is-enabled [NAME...] Check whether unit files are enabled\n\n"
3942 "Job Commands:\n"
48220598 3943 " list-jobs List jobs\n"
34c4b47b
LP
3944 " cancel [JOB...] Cancel all, one, or more jobs\n\n"
3945 "Status Commands:\n"
7e4249b9 3946 " dump Dump server status\n"
34c4b47b
LP
3947 " dot Dump dependency graph for dot(1)\n\n"
3948 "Snapshot Commands:\n"
7e4249b9 3949 " snapshot [NAME] Create a snapshot\n"
34c4b47b
LP
3950 " delete [NAME...] Remove one or more snapshots\n\n"
3951 "Environment Commands:\n"
7e4249b9
LP
3952 " show-environment Dump environment\n"
3953 " set-environment [NAME=VALUE...] Set one or more environment variables\n"
34c4b47b
LP
3954 " unset-environment [NAME...] Unset one or more environment variables\n\n"
3955 "Manager Lifecycle Commands:\n"
3956 " daemon-reload Reload systemd manager configuration\n"
3957 " daemon-reexec Reexecute systemd manager\n\n"
3958 "System Commands:\n"
20b09ca7
LP
3959 " default Enter system default mode\n"
3960 " rescue Enter system rescue mode\n"
3961 " emergency Enter system emergency mode\n"
514f4ef5 3962 " halt Shut down and halt the system\n"
2e33c433 3963 " poweroff Shut down and power-off the system\n"
514f4ef5 3964 " reboot Shut down and reboot the system\n"
20b09ca7 3965 " kexec Shut down and reboot the system with kexec\n"
6edd7d0a 3966 " exit Request user instance exit\n"
957eb8ca 3967 " switch-root [ROOT] [INIT] Change to a different root file system\n"
6edd7d0a
LP
3968 " suspend Suspend the system\n"
3969 " hibernate Hibernate the system\n",
5b6319dc 3970 program_invocation_short_name);
7e4249b9
LP
3971
3972 return 0;
3973}
3974
e4b61340
LP
3975static int halt_help(void) {
3976
2e33c433 3977 printf("%s [OPTIONS...]\n\n"
e4b61340
LP
3978 "%s the system.\n\n"
3979 " --help Show this help\n"
3980 " --halt Halt the machine\n"
3981 " -p --poweroff Switch off the machine\n"
3982 " --reboot Reboot the machine\n"
2e33c433
LP
3983 " -f --force Force immediate halt/power-off/reboot\n"
3984 " -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
e4b61340 3985 " -d --no-wtmp Don't write wtmp record\n"
2e33c433 3986 " --no-wall Don't send wall message before halt/power-off/reboot\n",
e4b61340
LP
3987 program_invocation_short_name,
3988 arg_action == ACTION_REBOOT ? "Reboot" :
3989 arg_action == ACTION_POWEROFF ? "Power off" :
3990 "Halt");
3991
3992 return 0;
3993}
3994
3995static int shutdown_help(void) {
3996
08e4b1c5 3997 printf("%s [OPTIONS...] [TIME] [WALL...]\n\n"
e4b61340
LP
3998 "Shut down the system.\n\n"
3999 " --help Show this help\n"
4000 " -H --halt Halt the machine\n"
4001 " -P --poweroff Power-off the machine\n"
4002 " -r --reboot Reboot the machine\n"
386da858 4003 " -h Equivalent to --poweroff, overridden by --halt\n"
2e33c433 4004 " -k Don't halt/power-off/reboot, just send warnings\n"
f6144808 4005 " --no-wall Don't send wall message before halt/power-off/reboot\n"
f6144808 4006 " -c Cancel a pending shutdown\n",
e4b61340
LP
4007 program_invocation_short_name);
4008
4009 return 0;
4010}
4011
4012static int telinit_help(void) {
4013
2e33c433 4014 printf("%s [OPTIONS...] {COMMAND}\n\n"
514f4ef5
LP
4015 "Send control commands to the init daemon.\n\n"
4016 " --help Show this help\n"
2e33c433 4017 " --no-wall Don't send wall message before halt/power-off/reboot\n\n"
e4b61340
LP
4018 "Commands:\n"
4019 " 0 Power-off the machine\n"
4020 " 6 Reboot the machine\n"
514f4ef5
LP
4021 " 2, 3, 4, 5 Start runlevelX.target unit\n"
4022 " 1, s, S Enter rescue mode\n"
4023 " q, Q Reload init daemon configuration\n"
4024 " u, U Reexecute init daemon\n",
e4b61340
LP
4025 program_invocation_short_name);
4026
4027 return 0;
4028}
4029
4030static int runlevel_help(void) {
4031
2e33c433 4032 printf("%s [OPTIONS...]\n\n"
e4b61340
LP
4033 "Prints the previous and current runlevel of the init system.\n\n"
4034 " --help Show this help\n",
4035 program_invocation_short_name);
4036
4037 return 0;
4038}
4039
4040static int systemctl_parse_argv(int argc, char *argv[]) {
7e4249b9
LP
4041
4042 enum {
90d473a1 4043 ARG_FAIL = 0x100,
e67c3609 4044 ARG_IGNORE_DEPENDENCIES,
35df8f27 4045 ARG_VERSION,
af2d49f7 4046 ARG_USER,
7e4249b9 4047 ARG_SYSTEM,
ee5762e3 4048 ARG_GLOBAL,
6e905d93 4049 ARG_NO_BLOCK,
ebed32bf 4050 ARG_NO_LEGEND,
611efaac 4051 ARG_NO_PAGER,
4445a875
LP
4052 ARG_NO_WALL,
4053 ARG_ORDER,
8fe914ec 4054 ARG_REQUIRE,
be394c48 4055 ARG_ROOT,
ee5762e3 4056 ARG_FULL,
ee5762e3 4057 ARG_NO_RELOAD,
501fc174 4058 ARG_KILL_WHO,
30732560 4059 ARG_NO_ASK_PASSWORD,
729e3769 4060 ARG_FAILED,
df50185b 4061 ARG_RUNTIME,
568b679f
LP
4062 ARG_FOLLOW,
4063 ARG_FORCE
7e4249b9
LP
4064 };
4065
4066 static const struct option options[] = {
ee5762e3 4067 { "help", no_argument, NULL, 'h' },
35df8f27 4068 { "version", no_argument, NULL, ARG_VERSION },
ee5762e3
LP
4069 { "type", required_argument, NULL, 't' },
4070 { "property", required_argument, NULL, 'p' },
4071 { "all", no_argument, NULL, 'a' },
30732560 4072 { "failed", no_argument, NULL, ARG_FAILED },
ee5762e3
LP
4073 { "full", no_argument, NULL, ARG_FULL },
4074 { "fail", no_argument, NULL, ARG_FAIL },
e67c3609 4075 { "ignore-dependencies", no_argument, NULL, ARG_IGNORE_DEPENDENCIES },
af2d49f7 4076 { "user", no_argument, NULL, ARG_USER },
ee5762e3
LP
4077 { "system", no_argument, NULL, ARG_SYSTEM },
4078 { "global", no_argument, NULL, ARG_GLOBAL },
4079 { "no-block", no_argument, NULL, ARG_NO_BLOCK },
ebed32bf 4080 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
0736af98 4081 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
ee5762e3
LP
4082 { "no-wall", no_argument, NULL, ARG_NO_WALL },
4083 { "quiet", no_argument, NULL, 'q' },
4084 { "order", no_argument, NULL, ARG_ORDER },
4085 { "require", no_argument, NULL, ARG_REQUIRE },
be394c48 4086 { "root", required_argument, NULL, ARG_ROOT },
568b679f 4087 { "force", no_argument, NULL, ARG_FORCE },
ee5762e3 4088 { "no-reload", no_argument, NULL, ARG_NO_RELOAD },
8a0867d6
LP
4089 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
4090 { "signal", required_argument, NULL, 's' },
501fc174 4091 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
a8f11321
LP
4092 { "host", required_argument, NULL, 'H' },
4093 { "privileged",no_argument, NULL, 'P' },
729e3769 4094 { "runtime", no_argument, NULL, ARG_RUNTIME },
df50185b
LP
4095 { "lines", required_argument, NULL, 'n' },
4096 { "follow", no_argument, NULL, ARG_FOLLOW },
4097 { "output", required_argument, NULL, 'o' },
ee5762e3 4098 { NULL, 0, NULL, 0 }
7e4249b9
LP
4099 };
4100
4101 int c;
4102
e4b61340 4103 assert(argc >= 0);
7e4249b9
LP
4104 assert(argv);
4105
df50185b 4106 while ((c = getopt_long(argc, argv, "ht:p:aqfs:H:Pn:o:", options, NULL)) >= 0) {
7e4249b9
LP
4107
4108 switch (c) {
4109
4110 case 'h':
e4b61340 4111 systemctl_help();
7e4249b9 4112 return 0;
35df8f27
LP
4113
4114 case ARG_VERSION:
4115 puts(PACKAGE_STRING);
7d568925
LP
4116 puts(DISTRIBUTION);
4117 puts(SYSTEMD_FEATURES);
35df8f27 4118 return 0;
7e4249b9
LP
4119
4120 case 't':
c147dc42
ZJS
4121 if (unit_type_from_string(optarg) >= 0) {
4122 arg_type = optarg;
4123 break;
6d972808 4124 }
c147dc42
ZJS
4125 if (unit_load_state_from_string(optarg) >= 0) {
4126 arg_load_state = optarg;
4127 break;
4128 }
4129 log_error("Unkown unit type or load state '%s'.",
4130 optarg);
4131 return -EINVAL;
ea4a240d
LP
4132 case 'p': {
4133 char **l;
4134
4135 if (!(l = strv_append(arg_property, optarg)))
4136 return -ENOMEM;
4137
4138 strv_free(arg_property);
4139 arg_property = l;
48220598
LP
4140
4141 /* If the user asked for a particular
4142 * property, show it to him, even if it is
4143 * empty. */
4144 arg_all = true;
4145 break;
ea4a240d 4146 }
48220598 4147
7e4249b9
LP
4148 case 'a':
4149 arg_all = true;
4150 break;
4151
90d473a1 4152 case ARG_FAIL:
e67c3609
LP
4153 arg_job_mode = "fail";
4154 break;
4155
4156 case ARG_IGNORE_DEPENDENCIES:
4157 arg_job_mode = "ignore-dependencies";
7e4249b9
LP
4158 break;
4159
af2d49f7 4160 case ARG_USER:
729e3769 4161 arg_scope = UNIT_FILE_USER;
7e4249b9
LP
4162 break;
4163
4164 case ARG_SYSTEM:
729e3769
LP
4165 arg_scope = UNIT_FILE_SYSTEM;
4166 break;
4167
4168 case ARG_GLOBAL:
4169 arg_scope = UNIT_FILE_GLOBAL;
7e4249b9
LP
4170 break;
4171
6e905d93
LP
4172 case ARG_NO_BLOCK:
4173 arg_no_block = true;
7e4249b9
LP
4174 break;
4175
ebed32bf
MS
4176 case ARG_NO_LEGEND:
4177 arg_no_legend = true;
4178 break;
4179
611efaac
LP
4180 case ARG_NO_PAGER:
4181 arg_no_pager = true;
4182 break;
0736af98 4183
514f4ef5
LP
4184 case ARG_NO_WALL:
4185 arg_no_wall = true;
4186 break;
4187
4445a875
LP
4188 case ARG_ORDER:
4189 arg_dot = DOT_ORDER;
4190 break;
4191
4192 case ARG_REQUIRE:
4193 arg_dot = DOT_REQUIRE;
4194 break;
4195
be394c48
FC
4196 case ARG_ROOT:
4197 arg_root = optarg;
4198 break;
4199
8fe914ec
LP
4200 case ARG_FULL:
4201 arg_full = true;
4202 break;
4203
30732560
LP
4204 case ARG_FAILED:
4205 arg_failed = true;
4206 break;
4207
0183528f
LP
4208 case 'q':
4209 arg_quiet = true;
4210 break;
4211
568b679f
LP
4212 case ARG_FORCE:
4213 arg_force ++;
4214 break;
4215
4216 case ARG_FOLLOW:
4217 arg_follow = true;
4218 break;
4219
b4f27ccc 4220 case 'f':
568b679f 4221 /* -f is short for both --follow and --force! */
e606bb61 4222 arg_force ++;
568b679f 4223 arg_follow = true;
ee5762e3
LP
4224 break;
4225
4226 case ARG_NO_RELOAD:
4227 arg_no_reload = true;
4228 break;
4229
8a0867d6
LP
4230 case ARG_KILL_WHO:
4231 arg_kill_who = optarg;
4232 break;
4233
8a0867d6
LP
4234 case 's':
4235 if ((arg_signal = signal_from_string_try_harder(optarg)) < 0) {
4236 log_error("Failed to parse signal string %s.", optarg);
4237 return -EINVAL;
4238 }
4239 break;
4240
501fc174
LP
4241 case ARG_NO_ASK_PASSWORD:
4242 arg_ask_password = false;
4243 break;
4244
a8f11321
LP
4245 case 'P':
4246 arg_transport = TRANSPORT_POLKIT;
4247 break;
4248
4249 case 'H':
4250 arg_transport = TRANSPORT_SSH;
4251 arg_host = optarg;
4252 break;
4253
729e3769
LP
4254 case ARG_RUNTIME:
4255 arg_runtime = true;
4256 break;
4257
df50185b
LP
4258 case 'n':
4259 if (safe_atou(optarg, &arg_lines) < 0) {
4260 log_error("Failed to parse lines '%s'", optarg);
4261 return -EINVAL;
4262 }
4263 break;
4264
df50185b
LP
4265 case 'o':
4266 arg_output = output_mode_from_string(optarg);
4267 if (arg_output < 0) {
4268 log_error("Unknown output '%s'.", optarg);
4269 return -EINVAL;
4270 }
4271 break;
4272
7e4249b9
LP
4273 case '?':
4274 return -EINVAL;
4275
4276 default:
b0193f1c 4277 log_error("Unknown option code '%c'.", c);
7e4249b9
LP
4278 return -EINVAL;
4279 }
4280 }
4281
729e3769 4282 if (arg_transport != TRANSPORT_NORMAL && arg_scope != UNIT_FILE_SYSTEM) {
a8f11321
LP
4283 log_error("Cannot access user instance remotely.");
4284 return -EINVAL;
4285 }
4286
7e4249b9
LP
4287 return 1;
4288}
4289
e4b61340
LP
4290static int halt_parse_argv(int argc, char *argv[]) {
4291
4292 enum {
4293 ARG_HELP = 0x100,
4294 ARG_HALT,
514f4ef5
LP
4295 ARG_REBOOT,
4296 ARG_NO_WALL
e4b61340
LP
4297 };
4298
4299 static const struct option options[] = {
4300 { "help", no_argument, NULL, ARG_HELP },
4301 { "halt", no_argument, NULL, ARG_HALT },
4302 { "poweroff", no_argument, NULL, 'p' },
4303 { "reboot", no_argument, NULL, ARG_REBOOT },
4304 { "force", no_argument, NULL, 'f' },
4305 { "wtmp-only", no_argument, NULL, 'w' },
4306 { "no-wtmp", no_argument, NULL, 'd' },
514f4ef5 4307 { "no-wall", no_argument, NULL, ARG_NO_WALL },
e4b61340
LP
4308 { NULL, 0, NULL, 0 }
4309 };
4310
4311 int c, runlevel;
4312
4313 assert(argc >= 0);
4314 assert(argv);
4315
4316 if (utmp_get_runlevel(&runlevel, NULL) >= 0)
4317 if (runlevel == '0' || runlevel == '6')
65491fd8 4318 arg_force = 2;
e4b61340
LP
4319
4320 while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) {
4321 switch (c) {
4322
4323 case ARG_HELP:
4324 halt_help();
4325 return 0;
4326
4327 case ARG_HALT:
4328 arg_action = ACTION_HALT;
4329 break;
4330
4331 case 'p':
a042efad
MS
4332 if (arg_action != ACTION_REBOOT)
4333 arg_action = ACTION_POWEROFF;
e4b61340
LP
4334 break;
4335
4336 case ARG_REBOOT:
4337 arg_action = ACTION_REBOOT;
4338 break;
4339
4340 case 'f':
65491fd8 4341 arg_force = 2;
e4b61340
LP
4342 break;
4343
4344 case 'w':
4345 arg_dry = true;
4346 break;
4347
4348 case 'd':
4349 arg_no_wtmp = true;
4350 break;
4351
514f4ef5
LP
4352 case ARG_NO_WALL:
4353 arg_no_wall = true;
4354 break;
4355
e4b61340
LP
4356 case 'i':
4357 case 'h':
57371e58 4358 case 'n':
e4b61340
LP
4359 /* Compatibility nops */
4360 break;
4361
4362 case '?':
4363 return -EINVAL;
4364
4365 default:
b0193f1c 4366 log_error("Unknown option code '%c'.", c);
e4b61340
LP
4367 return -EINVAL;
4368 }
4369 }
4370
4371 if (optind < argc) {
4372 log_error("Too many arguments.");
4373 return -EINVAL;
4374 }
4375
4376 return 1;
4377}
4378
f6144808
LP
4379static int parse_time_spec(const char *t, usec_t *_u) {
4380 assert(t);
4381 assert(_u);
4382
4383 if (streq(t, "now"))
4384 *_u = 0;
1a639877 4385 else if (!strchr(t, ':')) {
f6144808
LP
4386 uint64_t u;
4387
1a639877 4388 if (safe_atou64(t, &u) < 0)
f6144808
LP
4389 return -EINVAL;
4390
4391 *_u = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u;
4392 } else {
4393 char *e = NULL;
4394 long hour, minute;
4395 struct tm tm;
4396 time_t s;
4397 usec_t n;
4398
4399 errno = 0;
4400 hour = strtol(t, &e, 10);
4401 if (errno != 0 || *e != ':' || hour < 0 || hour > 23)
4402 return -EINVAL;
4403
4404 minute = strtol(e+1, &e, 10);
4405 if (errno != 0 || *e != 0 || minute < 0 || minute > 59)
4406 return -EINVAL;
4407
4408 n = now(CLOCK_REALTIME);
08e4b1c5
LP
4409 s = (time_t) (n / USEC_PER_SEC);
4410
4411 zero(tm);
f6144808
LP
4412 assert_se(localtime_r(&s, &tm));
4413
4414 tm.tm_hour = (int) hour;
4415 tm.tm_min = (int) minute;
08e4b1c5 4416 tm.tm_sec = 0;
f6144808
LP
4417
4418 assert_se(s = mktime(&tm));
4419
4420 *_u = (usec_t) s * USEC_PER_SEC;
4421
4422 while (*_u <= n)
4423 *_u += USEC_PER_DAY;
4424 }
4425
4426 return 0;
4427}
4428
e4b61340
LP
4429static int shutdown_parse_argv(int argc, char *argv[]) {
4430
4431 enum {
4432 ARG_HELP = 0x100,
514f4ef5 4433 ARG_NO_WALL
e4b61340
LP
4434 };
4435
4436 static const struct option options[] = {
4437 { "help", no_argument, NULL, ARG_HELP },
4438 { "halt", no_argument, NULL, 'H' },
4439 { "poweroff", no_argument, NULL, 'P' },
4440 { "reboot", no_argument, NULL, 'r' },
04ebb595 4441 { "kexec", no_argument, NULL, 'K' }, /* not documented extension */
514f4ef5 4442 { "no-wall", no_argument, NULL, ARG_NO_WALL },
e4b61340
LP
4443 { NULL, 0, NULL, 0 }
4444 };
4445
f6144808 4446 int c, r;
e4b61340
LP
4447
4448 assert(argc >= 0);
4449 assert(argv);
4450
f6144808 4451 while ((c = getopt_long(argc, argv, "HPrhkt:afFc", options, NULL)) >= 0) {
e4b61340
LP
4452 switch (c) {
4453
4454 case ARG_HELP:
4455 shutdown_help();
4456 return 0;
4457
4458 case 'H':
4459 arg_action = ACTION_HALT;
4460 break;
4461
4462 case 'P':
4463 arg_action = ACTION_POWEROFF;
4464 break;
4465
4466 case 'r':
5622dde3
KS
4467 if (kexec_loaded())
4468 arg_action = ACTION_KEXEC;
4469 else
4470 arg_action = ACTION_REBOOT;
e4b61340
LP
4471 break;
4472
04ebb595
LP
4473 case 'K':
4474 arg_action = ACTION_KEXEC;
4475 break;
4476
e4b61340
LP
4477 case 'h':
4478 if (arg_action != ACTION_HALT)
4479 arg_action = ACTION_POWEROFF;
4480 break;
4481
4482 case 'k':
4483 arg_dry = true;
4484 break;
4485
514f4ef5
LP
4486 case ARG_NO_WALL:
4487 arg_no_wall = true;
4488 break;
4489
e4b61340
LP
4490 case 't':
4491 case 'a':
4492 /* Compatibility nops */
4493 break;
4494
f6144808
LP
4495 case 'c':
4496 arg_action = ACTION_CANCEL_SHUTDOWN;
4497 break;
4498
e4b61340
LP
4499 case '?':
4500 return -EINVAL;
4501
4502 default:
b0193f1c 4503 log_error("Unknown option code '%c'.", c);
e4b61340
LP
4504 return -EINVAL;
4505 }
4506 }
4507
dfcc5c33 4508 if (argc > optind && arg_action != ACTION_CANCEL_SHUTDOWN) {
7e59bfcb
LP
4509 r = parse_time_spec(argv[optind], &arg_when);
4510 if (r < 0) {
f6144808
LP
4511 log_error("Failed to parse time specification: %s", argv[optind]);
4512 return r;
4513 }
6b5ad000 4514 } else
08e4b1c5 4515 arg_when = now(CLOCK_REALTIME) + USEC_PER_MINUTE;
442b9094 4516
dfcc5c33
MS
4517 if (argc > optind && arg_action == ACTION_CANCEL_SHUTDOWN)
4518 /* No time argument for shutdown cancel */
4519 arg_wall = argv + optind;
4520 else if (argc > optind + 1)
4521 /* We skip the time argument */
e4b61340
LP
4522 arg_wall = argv + optind + 1;
4523
4524 optind = argc;
4525
4526 return 1;
e4b61340
LP
4527}
4528
4529static int telinit_parse_argv(int argc, char *argv[]) {
4530
4531 enum {
4532 ARG_HELP = 0x100,
514f4ef5 4533 ARG_NO_WALL
e4b61340
LP
4534 };
4535
4536 static const struct option options[] = {
4537 { "help", no_argument, NULL, ARG_HELP },
514f4ef5 4538 { "no-wall", no_argument, NULL, ARG_NO_WALL },
e4b61340
LP
4539 { NULL, 0, NULL, 0 }
4540 };
4541
4542 static const struct {
4543 char from;
4544 enum action to;
4545 } table[] = {
4546 { '0', ACTION_POWEROFF },
4547 { '6', ACTION_REBOOT },
ef2f1067 4548 { '1', ACTION_RESCUE },
e4b61340
LP
4549 { '2', ACTION_RUNLEVEL2 },
4550 { '3', ACTION_RUNLEVEL3 },
4551 { '4', ACTION_RUNLEVEL4 },
4552 { '5', ACTION_RUNLEVEL5 },
4553 { 's', ACTION_RESCUE },
4554 { 'S', ACTION_RESCUE },
4555 { 'q', ACTION_RELOAD },
4556 { 'Q', ACTION_RELOAD },
4557 { 'u', ACTION_REEXEC },
4558 { 'U', ACTION_REEXEC }
4559 };
4560
4561 unsigned i;
4562 int c;
4563
4564 assert(argc >= 0);
4565 assert(argv);
4566
4567 while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
4568 switch (c) {
4569
4570 case ARG_HELP:
4571 telinit_help();
4572 return 0;
4573
514f4ef5
LP
4574 case ARG_NO_WALL:
4575 arg_no_wall = true;
4576 break;
4577
e4b61340
LP
4578 case '?':
4579 return -EINVAL;
4580
4581 default:
b0193f1c 4582 log_error("Unknown option code '%c'.", c);
e4b61340
LP
4583 return -EINVAL;
4584 }
4585 }
4586
4587 if (optind >= argc) {
2f02ce40 4588 telinit_help();
e4b61340
LP
4589 return -EINVAL;
4590 }
4591
4592 if (optind + 1 < argc) {
4593 log_error("Too many arguments.");
4594 return -EINVAL;
4595 }
4596
4597 if (strlen(argv[optind]) != 1) {
4598 log_error("Expected single character argument.");
4599 return -EINVAL;
4600 }
4601
4602 for (i = 0; i < ELEMENTSOF(table); i++)
4603 if (table[i].from == argv[optind][0])
4604 break;
4605
4606 if (i >= ELEMENTSOF(table)) {
b0193f1c 4607 log_error("Unknown command '%s'.", argv[optind]);
e4b61340
LP
4608 return -EINVAL;
4609 }
4610
4611 arg_action = table[i].to;
4612
4613 optind ++;
4614
4615 return 1;
4616}
4617
4618static int runlevel_parse_argv(int argc, char *argv[]) {
4619
4620 enum {
4621 ARG_HELP = 0x100,
4622 };
4623
4624 static const struct option options[] = {
4625 { "help", no_argument, NULL, ARG_HELP },
4626 { NULL, 0, NULL, 0 }
4627 };
4628
4629 int c;
4630
4631 assert(argc >= 0);
4632 assert(argv);
4633
4634 while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
4635 switch (c) {
4636
4637 case ARG_HELP:
4638 runlevel_help();
4639 return 0;
4640
4641 case '?':
4642 return -EINVAL;
4643
4644 default:
b0193f1c 4645 log_error("Unknown option code '%c'.", c);
e4b61340
LP
4646 return -EINVAL;
4647 }
4648 }
4649
4650 if (optind < argc) {
4651 log_error("Too many arguments.");
4652 return -EINVAL;
4653 }
4654
4655 return 1;
4656}
4657
4658static int parse_argv(int argc, char *argv[]) {
4659 assert(argc >= 0);
4660 assert(argv);
4661
4662 if (program_invocation_short_name) {
4663
4664 if (strstr(program_invocation_short_name, "halt")) {
4665 arg_action = ACTION_HALT;
4666 return halt_parse_argv(argc, argv);
4667 } else if (strstr(program_invocation_short_name, "poweroff")) {
4668 arg_action = ACTION_POWEROFF;
4669 return halt_parse_argv(argc, argv);
4670 } else if (strstr(program_invocation_short_name, "reboot")) {
5622dde3
KS
4671 if (kexec_loaded())
4672 arg_action = ACTION_KEXEC;
4673 else
4674 arg_action = ACTION_REBOOT;
e4b61340
LP
4675 return halt_parse_argv(argc, argv);
4676 } else if (strstr(program_invocation_short_name, "shutdown")) {
4677 arg_action = ACTION_POWEROFF;
4678 return shutdown_parse_argv(argc, argv);
4679 } else if (strstr(program_invocation_short_name, "init")) {
d5ca5f11
LP
4680
4681 if (sd_booted() > 0) {
4682 arg_action = ACTION_INVALID;
4683 return telinit_parse_argv(argc, argv);
4684 } else {
4685 /* Hmm, so some other init system is
4686 * running, we need to forward this
4687 * request to it. For now we simply
4688 * guess that it is Upstart. */
4689
4690 execv("/lib/upstart/telinit", argv);
4691
4692 log_error("Couldn't find an alternative telinit implementation to spawn.");
4693 return -EIO;
4694 }
4695
e4b61340
LP
4696 } else if (strstr(program_invocation_short_name, "runlevel")) {
4697 arg_action = ACTION_RUNLEVEL;
4698 return runlevel_parse_argv(argc, argv);
4699 }
4700 }
4701
4702 arg_action = ACTION_SYSTEMCTL;
4703 return systemctl_parse_argv(argc, argv);
4704}
4705
d55ae9e6 4706static int action_to_runlevel(void) {
eb22ac37
LP
4707
4708 static const char table[_ACTION_MAX] = {
4709 [ACTION_HALT] = '0',
4710 [ACTION_POWEROFF] = '0',
4711 [ACTION_REBOOT] = '6',
4712 [ACTION_RUNLEVEL2] = '2',
4713 [ACTION_RUNLEVEL3] = '3',
4714 [ACTION_RUNLEVEL4] = '4',
4715 [ACTION_RUNLEVEL5] = '5',
4716 [ACTION_RESCUE] = '1'
4717 };
4718
d55ae9e6
LP
4719 assert(arg_action < _ACTION_MAX);
4720
4721 return table[arg_action];
4722}
4723
f1c5860b 4724static int talk_upstart(void) {
d55ae9e6
LP
4725 DBusMessage *m = NULL, *reply = NULL;
4726 DBusError error;
4727 int previous, rl, r;
4728 char
4729 env1_buf[] = "RUNLEVEL=X",
4730 env2_buf[] = "PREVLEVEL=X";
4731 char *env1 = env1_buf, *env2 = env2_buf;
4732 const char *emit = "runlevel";
4733 dbus_bool_t b_false = FALSE;
4734 DBusMessageIter iter, sub;
f1c5860b 4735 DBusConnection *bus;
d55ae9e6
LP
4736
4737 dbus_error_init(&error);
4738
4739 if (!(rl = action_to_runlevel()))
4740 return 0;
4741
4742 if (utmp_get_runlevel(&previous, NULL) < 0)
4743 previous = 'N';
4744
b574246b 4745 if (!(bus = dbus_connection_open_private("unix:abstract=/com/ubuntu/upstart", &error))) {
f1c5860b
LP
4746 if (dbus_error_has_name(&error, DBUS_ERROR_NO_SERVER)) {
4747 r = 0;
4748 goto finish;
4749 }
4750
4cf5d675 4751 log_error("Failed to connect to Upstart bus: %s", bus_error_message(&error));
f1c5860b
LP
4752 r = -EIO;
4753 goto finish;
4754 }
4755
4756 if ((r = bus_check_peercred(bus)) < 0) {
4757 log_error("Failed to verify owner of bus.");
4758 goto finish;
4759 }
4760
d55ae9e6
LP
4761 if (!(m = dbus_message_new_method_call(
4762 "com.ubuntu.Upstart",
4763 "/com/ubuntu/Upstart",
4764 "com.ubuntu.Upstart0_6",
4765 "EmitEvent"))) {
4766
4767 log_error("Could not allocate message.");
f1c5860b
LP
4768 r = -ENOMEM;
4769 goto finish;
d55ae9e6
LP
4770 }
4771
4772 dbus_message_iter_init_append(m, &iter);
4773
4774 env1_buf[sizeof(env1_buf)-2] = rl;
4775 env2_buf[sizeof(env2_buf)-2] = previous;
4776
4777 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &emit) ||
4778 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub) ||
4779 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env1) ||
4780 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env2) ||
4781 !dbus_message_iter_close_container(&iter, &sub) ||
4782 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b_false)) {
4783 log_error("Could not append arguments to message.");
4784 r = -ENOMEM;
4785 goto finish;
4786 }
4787
4788 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4789
c67de56f 4790 if (bus_error_is_no_service(&error)) {
aabd9b11 4791 r = -EADDRNOTAVAIL;
d55ae9e6
LP
4792 goto finish;
4793 }
4794
4cf5d675 4795 log_error("Failed to issue method call: %s", bus_error_message(&error));
d55ae9e6
LP
4796 r = -EIO;
4797 goto finish;
4798 }
4799
0a55b298 4800 r = 1;
d55ae9e6
LP
4801
4802finish:
4803 if (m)
4804 dbus_message_unref(m);
4805
4806 if (reply)
4807 dbus_message_unref(reply);
4808
b574246b 4809 if (bus) {
5d452f9c 4810 dbus_connection_flush(bus);
b574246b 4811 dbus_connection_close(bus);
f1c5860b 4812 dbus_connection_unref(bus);
b574246b 4813 }
f1c5860b 4814
d55ae9e6
LP
4815 dbus_error_free(&error);
4816
4817 return r;
4818}
4819
4820static int talk_initctl(void) {
eb22ac37
LP
4821 struct init_request request;
4822 int r, fd;
d55ae9e6 4823 char rl;
eb22ac37 4824
d55ae9e6 4825 if (!(rl = action_to_runlevel()))
eb22ac37
LP
4826 return 0;
4827
4828 zero(request);
4829 request.magic = INIT_MAGIC;
4830 request.sleeptime = 0;
4831 request.cmd = INIT_CMD_RUNLVL;
d55ae9e6
LP
4832 request.runlevel = rl;
4833
4834 if ((fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY)) < 0) {
4835
4836 if (errno == ENOENT)
4837 return 0;
eb22ac37 4838
d55ae9e6 4839 log_error("Failed to open "INIT_FIFO": %m");
eb22ac37 4840 return -errno;
d55ae9e6 4841 }
eb22ac37 4842
d55ae9e6 4843 errno = 0;
eb22ac37
LP
4844 r = loop_write(fd, &request, sizeof(request), false) != sizeof(request);
4845 close_nointr_nofail(fd);
4846
d55ae9e6
LP
4847 if (r < 0) {
4848 log_error("Failed to write to "INIT_FIFO": %m");
eb22ac37 4849 return errno ? -errno : -EIO;
d55ae9e6 4850 }
eb22ac37
LP
4851
4852 return 1;
e4b61340
LP
4853}
4854
ee5762e3 4855static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
7e4249b9 4856
7e4249b9
LP
4857 static const struct {
4858 const char* verb;
4859 const enum {
4860 MORE,
4861 LESS,
4862 EQUAL
4863 } argc_cmp;
4864 const int argc;
729e3769 4865 int (* const dispatch)(DBusConnection *bus, char **args);
7e4249b9 4866 } verbs[] = {
ee5762e3 4867 { "list-units", LESS, 1, list_units },
729e3769 4868 { "list-unit-files", EQUAL, 1, list_unit_files },
ee5762e3
LP
4869 { "list-jobs", EQUAL, 1, list_jobs },
4870 { "clear-jobs", EQUAL, 1, daemon_reload },
4871 { "load", MORE, 2, load_unit },
4872 { "cancel", MORE, 2, cancel_job },
4873 { "start", MORE, 2, start_unit },
4874 { "stop", MORE, 2, start_unit },
a76f7be2 4875 { "condstop", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
ee5762e3
LP
4876 { "reload", MORE, 2, start_unit },
4877 { "restart", MORE, 2, start_unit },
4878 { "try-restart", MORE, 2, start_unit },
4879 { "reload-or-restart", MORE, 2, start_unit },
4880 { "reload-or-try-restart", MORE, 2, start_unit },
4881 { "force-reload", MORE, 2, start_unit }, /* For compatibility with SysV */
64e5f1b7 4882 { "condreload", MORE, 2, start_unit }, /* For compatibility with ALTLinux */
ee5762e3
LP
4883 { "condrestart", MORE, 2, start_unit }, /* For compatibility with RH */
4884 { "isolate", EQUAL, 2, start_unit },
8a0867d6 4885 { "kill", MORE, 2, kill_unit },
ee5762e3
LP
4886 { "is-active", MORE, 2, check_unit },
4887 { "check", MORE, 2, check_unit },
4888 { "show", MORE, 1, show },
4889 { "status", MORE, 2, show },
b43f208f 4890 { "help", MORE, 2, show },
ee5762e3
LP
4891 { "dump", EQUAL, 1, dump },
4892 { "dot", EQUAL, 1, dot },
4893 { "snapshot", LESS, 2, snapshot },
4894 { "delete", MORE, 2, delete_snapshot },
4895 { "daemon-reload", EQUAL, 1, daemon_reload },
4896 { "daemon-reexec", EQUAL, 1, daemon_reload },
ee5762e3
LP
4897 { "show-environment", EQUAL, 1, show_enviroment },
4898 { "set-environment", MORE, 2, set_environment },
4899 { "unset-environment", MORE, 2, set_environment },
4900 { "halt", EQUAL, 1, start_special },
4901 { "poweroff", EQUAL, 1, start_special },
4902 { "reboot", EQUAL, 1, start_special },
20b09ca7 4903 { "kexec", EQUAL, 1, start_special },
6edd7d0a
LP
4904 { "suspend", EQUAL, 1, start_special },
4905 { "hibernate", EQUAL, 1, start_special },
ee5762e3
LP
4906 { "default", EQUAL, 1, start_special },
4907 { "rescue", EQUAL, 1, start_special },
4908 { "emergency", EQUAL, 1, start_special },
20b09ca7 4909 { "exit", EQUAL, 1, start_special },
fdf20a31 4910 { "reset-failed", MORE, 1, reset_failed },
ee5762e3
LP
4911 { "enable", MORE, 2, enable_unit },
4912 { "disable", MORE, 2, enable_unit },
729e3769
LP
4913 { "is-enabled", MORE, 2, unit_is_enabled },
4914 { "reenable", MORE, 2, enable_unit },
4915 { "preset", MORE, 2, enable_unit },
4916 { "mask", MORE, 2, enable_unit },
4917 { "unmask", MORE, 2, enable_unit },
957eb8ca
LP
4918 { "link", MORE, 2, enable_unit },
4919 { "switch-root", MORE, 2, switch_root },
7e4249b9
LP
4920 };
4921
e4b61340 4922 int left;
7e4249b9 4923 unsigned i;
7e4249b9 4924
e4b61340
LP
4925 assert(argc >= 0);
4926 assert(argv);
ee5762e3 4927 assert(error);
7e4249b9
LP
4928
4929 left = argc - optind;
4930
4931 if (left <= 0)
4932 /* Special rule: no arguments means "list-units" */
4933 i = 0;
4934 else {
b43f208f
KS
4935 if (streq(argv[optind], "help") && !argv[optind+1]) {
4936 log_error("This command expects one or more "
4937 "unit names. Did you mean --help?");
4938 return -EINVAL;
0183528f
LP
4939 }
4940
7e4249b9
LP
4941 for (i = 0; i < ELEMENTSOF(verbs); i++)
4942 if (streq(argv[optind], verbs[i].verb))
4943 break;
4944
4945 if (i >= ELEMENTSOF(verbs)) {
b0193f1c 4946 log_error("Unknown operation '%s'.", argv[optind]);
e4b61340 4947 return -EINVAL;
7e4249b9
LP
4948 }
4949 }
4950
4951 switch (verbs[i].argc_cmp) {
4952
4953 case EQUAL:
4954 if (left != verbs[i].argc) {
4955 log_error("Invalid number of arguments.");
e4b61340 4956 return -EINVAL;
7e4249b9
LP
4957 }
4958
4959 break;
4960
4961 case MORE:
4962 if (left < verbs[i].argc) {
4963 log_error("Too few arguments.");
e4b61340 4964 return -EINVAL;
7e4249b9
LP
4965 }
4966
4967 break;
4968
4969 case LESS:
4970 if (left > verbs[i].argc) {
4971 log_error("Too many arguments.");
e4b61340 4972 return -EINVAL;
7e4249b9
LP
4973 }
4974
4975 break;
4976
4977 default:
4978 assert_not_reached("Unknown comparison operator.");
4979 }
4980
ee5762e3
LP
4981 /* Require a bus connection for all operations but
4982 * enable/disable */
729e3769
LP
4983 if (!streq(verbs[i].verb, "enable") &&
4984 !streq(verbs[i].verb, "disable") &&
c971700e 4985 !streq(verbs[i].verb, "is-enabled") &&
d380a3bc 4986 !streq(verbs[i].verb, "list-unit-files") &&
729e3769
LP
4987 !streq(verbs[i].verb, "reenable") &&
4988 !streq(verbs[i].verb, "preset") &&
4989 !streq(verbs[i].verb, "mask") &&
4990 !streq(verbs[i].verb, "unmask") &&
4991 !streq(verbs[i].verb, "link")) {
82e23ddd
LP
4992
4993 if (running_in_chroot() > 0) {
4994 log_info("Running in chroot, ignoring request.");
4995 return 0;
4996 }
4997
3beddc78 4998 if (((!streq(verbs[i].verb, "reboot") &&
59ddae9f
LP
4999 !streq(verbs[i].verb, "halt") &&
5000 !streq(verbs[i].verb, "poweroff")) || arg_force <= 0) && !bus) {
f176b5c2
LP
5001 log_error("Failed to get D-Bus connection: %s",
5002 dbus_error_is_set(error) ? error->message : "No connection to service manager.");
8185a509
LP
5003 return -EIO;
5004 }
5005
5006 } else {
5007
729e3769 5008 if (!bus && !avoid_bus()) {
f176b5c2
LP
5009 log_error("Failed to get D-Bus connection: %s",
5010 dbus_error_is_set(error) ? error->message : "No connection to service manager.");
82e23ddd
LP
5011 return -EIO;
5012 }
ee5762e3
LP
5013 }
5014
729e3769 5015 return verbs[i].dispatch(bus, argv + optind);
e4b61340
LP
5016}
5017
52c00215 5018static int send_shutdownd(usec_t t, char mode, bool dry_run, bool warn, const char *message) {
04ebb595 5019 int fd;
f6144808 5020 struct msghdr msghdr;
04ebb595 5021 struct iovec iovec[2];
f6144808 5022 union sockaddr_union sockaddr;
04ebb595
LP
5023 struct sd_shutdown_command c;
5024
5025 fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0);
5026 if (fd < 0)
5027 return -errno;
f6144808
LP
5028
5029 zero(c);
04ebb595 5030 c.usec = t;
f6144808 5031 c.mode = mode;
52c00215 5032 c.dry_run = dry_run;
9be9828c
LP
5033 c.warn_wall = warn;
5034
f6144808
LP
5035 zero(sockaddr);
5036 sockaddr.sa.sa_family = AF_UNIX;
2b583ce6 5037 strncpy(sockaddr.un.sun_path, "/run/systemd/shutdownd", sizeof(sockaddr.un.sun_path));
f6144808 5038
f6144808
LP
5039 zero(msghdr);
5040 msghdr.msg_name = &sockaddr;
2b583ce6 5041 msghdr.msg_namelen = offsetof(struct sockaddr_un, sun_path) + sizeof("/run/systemd/shutdownd") - 1;
f6144808 5042
04ebb595
LP
5043 zero(iovec);
5044 iovec[0].iov_base = (char*) &c;
5045 iovec[0].iov_len = offsetof(struct sd_shutdown_command, wall_message);
5046
5047 if (isempty(message))
5048 msghdr.msg_iovlen = 1;
5049 else {
5050 iovec[1].iov_base = (char*) message;
5051 iovec[1].iov_len = strlen(message);
5052 msghdr.msg_iovlen = 2;
5053 }
5054 msghdr.msg_iov = iovec;
f6144808
LP
5055
5056 if (sendmsg(fd, &msghdr, MSG_NOSIGNAL) < 0) {
5057 close_nointr_nofail(fd);
5058 return -errno;
5059 }
5060
5061 close_nointr_nofail(fd);
5062 return 0;
5063}
5064
e4b61340 5065static int reload_with_fallback(DBusConnection *bus) {
e4b61340
LP
5066
5067 if (bus) {
5068 /* First, try systemd via D-Bus. */
d76702a7 5069 if (daemon_reload(bus, NULL) >= 0)
e4b61340
LP
5070 return 0;
5071 }
5072
5073 /* Nothing else worked, so let's try signals */
5074 assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC);
5075
5076 if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) {
5077 log_error("kill() failed: %m");
5078 return -errno;
5079 }
5080
5081 return 0;
5082}
5083
5084static int start_with_fallback(DBusConnection *bus) {
e4b61340
LP
5085
5086 if (bus) {
5087 /* First, try systemd via D-Bus. */
729e3769 5088 if (start_unit(bus, NULL) >= 0)
983d9c90 5089 goto done;
e4b61340
LP
5090 }
5091
ec7f7f20
LP
5092 /* Hmm, talking to systemd via D-Bus didn't work. Then
5093 * let's try to talk to Upstart via D-Bus. */
e364ad06 5094 if (talk_upstart() > 0)
ec7f7f20
LP
5095 goto done;
5096
e4b61340
LP
5097 /* Nothing else worked, so let's try
5098 * /dev/initctl */
fbc43921 5099 if (talk_initctl() > 0)
983d9c90 5100 goto done;
d55ae9e6
LP
5101
5102 log_error("Failed to talk to init daemon.");
5103 return -EIO;
983d9c90
LP
5104
5105done:
5106 warn_wall(arg_action);
5107 return 0;
e4b61340
LP
5108}
5109
d91b8841 5110static _noreturn_ void halt_now(enum action a) {
e606bb61
LP
5111
5112 /* Make sure C-A-D is handled by the kernel from this
5113 * point on... */
5114 reboot(RB_ENABLE_CAD);
5115
4c80c73c 5116 switch (a) {
e606bb61
LP
5117
5118 case ACTION_HALT:
5119 log_info("Halting.");
5120 reboot(RB_HALT_SYSTEM);
5121 break;
5122
5123 case ACTION_POWEROFF:
5124 log_info("Powering off.");
5125 reboot(RB_POWER_OFF);
5126 break;
5127
5128 case ACTION_REBOOT:
5129 log_info("Rebooting.");
5130 reboot(RB_AUTOBOOT);
5131 break;
5132
5133 default:
5134 assert_not_reached("Unknown halt action.");
5135 }
5136
5137 assert_not_reached("Uh? This shouldn't happen.");
5138}
5139
e4b61340
LP
5140static int halt_main(DBusConnection *bus) {
5141 int r;
5142
bc8c2f5c 5143 if (geteuid() != 0) {
7e59bfcb
LP
5144 /* Try logind if we are a normal user and no special
5145 * mode applies. Maybe PolicyKit allows us to shutdown
5146 * the machine. */
5147
5148 if (arg_when <= 0 &&
5149 !arg_dry &&
65491fd8 5150 !arg_force &&
7e59bfcb
LP
5151 (arg_action == ACTION_POWEROFF ||
5152 arg_action == ACTION_REBOOT)) {
4c80c73c
KS
5153 r = reboot_with_logind(bus, arg_action);
5154 if (r >= 0)
5155 return r;
5156 }
5157
cc8a7a61 5158 log_error("Must be root.");
bc8c2f5c
LP
5159 return -EPERM;
5160 }
5161
f6144808 5162 if (arg_when > 0) {
9be9828c
LP
5163 char *m;
5164
5165 m = strv_join(arg_wall, " ");
5166 r = send_shutdownd(arg_when,
5167 arg_action == ACTION_HALT ? 'H' :
5168 arg_action == ACTION_POWEROFF ? 'P' :
04ebb595 5169 arg_action == ACTION_KEXEC ? 'K' :
9be9828c 5170 'r',
52c00215 5171 arg_dry,
9be9828c
LP
5172 !arg_no_wall,
5173 m);
5174 free(m);
5175
5176 if (r < 0)
f6144808 5177 log_warning("Failed to talk to shutdownd, proceeding with immediate shutdown: %s", strerror(-r));
08e4b1c5 5178 else {
7e59bfcb
LP
5179 char date[FORMAT_TIMESTAMP_MAX];
5180
08e4b1c5
LP
5181 log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.",
5182 format_timestamp(date, sizeof(date), arg_when));
f6144808 5183 return 0;
08e4b1c5 5184 }
f6144808
LP
5185 }
5186
65491fd8 5187 if (!arg_dry && !arg_force)
e4b61340
LP
5188 return start_with_fallback(bus);
5189
d90e1a30
LP
5190 if (!arg_no_wtmp) {
5191 if (sd_booted() > 0)
5192 log_debug("Not writing utmp record, assuming that systemd-update-utmp is used.");
7e59bfcb
LP
5193 else {
5194 r = utmp_put_shutdown();
5195 if (r < 0)
5196 log_warning("Failed to write utmp record: %s", strerror(-r));
5197 }
d90e1a30 5198 }
e4b61340 5199
e4b61340
LP
5200 if (arg_dry)
5201 return 0;
5202
e606bb61 5203 halt_now(arg_action);
e4b61340
LP
5204 /* We should never reach this. */
5205 return -ENOSYS;
5206}
5207
5208static int runlevel_main(void) {
5209 int r, runlevel, previous;
5210
729e3769
LP
5211 r = utmp_get_runlevel(&runlevel, &previous);
5212 if (r < 0) {
5213 puts("unknown");
e4b61340
LP
5214 return r;
5215 }
5216
5217 printf("%c %c\n",
5218 previous <= 0 ? 'N' : previous,
5219 runlevel <= 0 ? 'N' : runlevel);
5220
5221 return 0;
5222}
5223
5224int main(int argc, char*argv[]) {
22f4096c 5225 int r, retval = EXIT_FAILURE;
e4b61340
LP
5226 DBusConnection *bus = NULL;
5227 DBusError error;
5228
5229 dbus_error_init(&error);
5230
5231 log_parse_environment();
2396fb04 5232 log_open();
e4b61340 5233
04ebb595
LP
5234 r = parse_argv(argc, argv);
5235 if (r < 0)
e4b61340
LP
5236 goto finish;
5237 else if (r == 0) {
22f4096c 5238 retval = EXIT_SUCCESS;
7e4249b9
LP
5239 goto finish;
5240 }
5241
e4b61340
LP
5242 /* /sbin/runlevel doesn't need to communicate via D-Bus, so
5243 * let's shortcut this */
5244 if (arg_action == ACTION_RUNLEVEL) {
22f4096c
LP
5245 r = runlevel_main();
5246 retval = r < 0 ? EXIT_FAILURE : r;
e4b61340
LP
5247 goto finish;
5248 }
5249
82e23ddd
LP
5250 if (running_in_chroot() > 0 && arg_action != ACTION_SYSTEMCTL) {
5251 log_info("Running in chroot, ignoring request.");
5252 retval = 0;
5253 goto finish;
5254 }
5255
729e3769
LP
5256 if (!avoid_bus()) {
5257 if (arg_transport == TRANSPORT_NORMAL)
5258 bus_connect(arg_scope == UNIT_FILE_SYSTEM ? DBUS_BUS_SYSTEM : DBUS_BUS_SESSION, &bus, &private_bus, &error);
5259 else if (arg_transport == TRANSPORT_POLKIT) {
5260 bus_connect_system_polkit(&bus, &error);
5261 private_bus = false;
5262 } else if (arg_transport == TRANSPORT_SSH) {
5263 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
5264 private_bus = false;
5265 } else
5266 assert_not_reached("Uh, invalid transport...");
5267 }
e4b61340
LP
5268
5269 switch (arg_action) {
5270
22f4096c
LP
5271 case ACTION_SYSTEMCTL:
5272 r = systemctl_main(bus, argc, argv, &error);
e4b61340 5273 break;
e4b61340
LP
5274
5275 case ACTION_HALT:
5276 case ACTION_POWEROFF:
5277 case ACTION_REBOOT:
5622dde3 5278 case ACTION_KEXEC:
22f4096c 5279 r = halt_main(bus);
e4b61340
LP
5280 break;
5281
e4b61340
LP
5282 case ACTION_RUNLEVEL2:
5283 case ACTION_RUNLEVEL3:
5284 case ACTION_RUNLEVEL4:
5285 case ACTION_RUNLEVEL5:
5286 case ACTION_RESCUE:
514f4ef5 5287 case ACTION_EMERGENCY:
eb22ac37 5288 case ACTION_DEFAULT:
22f4096c 5289 r = start_with_fallback(bus);
e4b61340 5290 break;
7e4249b9 5291
e4b61340
LP
5292 case ACTION_RELOAD:
5293 case ACTION_REEXEC:
22f4096c 5294 r = reload_with_fallback(bus);
e4b61340
LP
5295 break;
5296
dfcc5c33
MS
5297 case ACTION_CANCEL_SHUTDOWN: {
5298 char *m = NULL;
5299
5300 if (arg_wall) {
5301 m = strv_join(arg_wall, " ");
5302 if (!m) {
5303 retval = EXIT_FAILURE;
5304 goto finish;
5305 }
5306 }
5307 r = send_shutdownd(arg_when, SD_SHUTDOWN_NONE, false, !arg_no_wall, m);
5308 if (r < 0)
5309 log_warning("Failed to talk to shutdownd, shutdown hasn't been cancelled: %s", strerror(-r));
5310 free(m);
f6144808 5311 break;
dfcc5c33 5312 }
f6144808 5313
eb22ac37
LP
5314 case ACTION_INVALID:
5315 case ACTION_RUNLEVEL:
e4b61340
LP
5316 default:
5317 assert_not_reached("Unknown action");
5318 }
7e4249b9 5319
22f4096c
LP
5320 retval = r < 0 ? EXIT_FAILURE : r;
5321
7e4249b9 5322finish:
b574246b 5323 if (bus) {
5d452f9c 5324 dbus_connection_flush(bus);
b574246b 5325 dbus_connection_close(bus);
7e4249b9 5326 dbus_connection_unref(bus);
b574246b 5327 }
7e4249b9 5328
e4b61340
LP
5329 dbus_error_free(&error);
5330
7e4249b9
LP
5331 dbus_shutdown();
5332
ea4a240d
LP
5333 strv_free(arg_property);
5334
1888c907 5335 pager_close();
6bb92a16
LP
5336 ask_password_agent_close();
5337 polkit_agent_close();
1888c907 5338
7e4249b9
LP
5339 return retval;
5340}