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