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