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