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