]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/systemctl.c
man: minor edits to daemon, sd_listen_fds, sd_notify, systemctl, systemd.exec, system...
[thirdparty/systemd.git] / src / systemctl.c
CommitLineData
7e4249b9
LP
1/*-*- Mode: C; c-basic-offset: 8 -*-*/
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
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
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
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
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>
7e4249b9
LP
33
34#include <dbus/dbus.h>
35
36#include "log.h"
37#include "util.h"
38#include "macro.h"
39#include "set.h"
e4b61340 40#include "utmp-wtmp.h"
514f4ef5 41#include "special.h"
eb22ac37 42#include "initreq.h"
e4a9373f 43#include "strv.h"
9a1ac7b9 44#include "dbus-common.h"
7e4249b9
LP
45
46static const char *arg_type = NULL;
48220598 47static const char *arg_property = NULL;
7e4249b9
LP
48static bool arg_all = false;
49static bool arg_replace = false;
50static bool arg_session = false;
6e905d93 51static bool arg_no_block = false;
e4b61340
LP
52static bool arg_immediate = false;
53static bool arg_no_wtmp = false;
54static bool arg_no_sync = false;
514f4ef5 55static bool arg_no_wall = false;
e4b61340 56static bool arg_dry = false;
0183528f 57static bool arg_quiet = false;
e4b61340
LP
58static char **arg_wall = NULL;
59enum action {
60 ACTION_INVALID,
61 ACTION_SYSTEMCTL,
62 ACTION_HALT,
63 ACTION_POWEROFF,
64 ACTION_REBOOT,
e4b61340
LP
65 ACTION_RUNLEVEL2,
66 ACTION_RUNLEVEL3,
67 ACTION_RUNLEVEL4,
68 ACTION_RUNLEVEL5,
69 ACTION_RESCUE,
514f4ef5
LP
70 ACTION_EMERGENCY,
71 ACTION_DEFAULT,
e4b61340
LP
72 ACTION_RELOAD,
73 ACTION_REEXEC,
74 ACTION_RUNLEVEL,
75 _ACTION_MAX
76} arg_action = ACTION_SYSTEMCTL;
77
78static bool error_is_no_service(DBusError *error) {
79
514f4ef5
LP
80 assert(error);
81
e4b61340
LP
82 if (!dbus_error_is_set(error))
83 return false;
84
85 if (dbus_error_has_name(error, DBUS_ERROR_NAME_HAS_NO_OWNER))
86 return true;
87
88 if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN))
89 return true;
90
91 return startswith(error->name, "org.freedesktop.DBus.Error.Spawn.");
92}
7e4249b9
LP
93
94static int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) {
95
514f4ef5
LP
96 assert(iter);
97 assert(data);
98
7e4249b9
LP
99 if (dbus_message_iter_get_arg_type(iter) != type)
100 return -EIO;
101
102 dbus_message_iter_get_basic(iter, data);
103
104 if (!dbus_message_iter_next(iter) != !next)
105 return -EIO;
106
107 return 0;
108}
109
110static int columns(void) {
111 static int parsed_columns = 0;
112 const char *e;
113
114 if (parsed_columns > 0)
115 return parsed_columns;
116
117 if ((e = getenv("COLUMNS")))
118 parsed_columns = atoi(e);
119
120 if (parsed_columns <= 0) {
121 struct winsize ws;
122 zero(ws);
123
124 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
125 parsed_columns = ws.ws_col;
126 }
127
128 if (parsed_columns <= 0)
129 parsed_columns = 80;
130
131 return parsed_columns;
e4b61340 132
7e4249b9
LP
133}
134
514f4ef5 135static void warn_wall(enum action action) {
ef2f1067 136 static const char *table[_ACTION_MAX] = {
514f4ef5
LP
137 [ACTION_HALT] = "The system is going down for system halt NOW!",
138 [ACTION_REBOOT] = "The system is going down for reboot NOW!",
139 [ACTION_POWEROFF] = "The system is going down for power-off NOW!",
140 [ACTION_RESCUE] = "The system is going down to rescue mode NOW!",
141 [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!"
ef2f1067
LP
142 };
143
514f4ef5
LP
144 if (arg_no_wall)
145 return;
146
e4a9373f
LP
147 if (arg_wall) {
148 char *p;
149
150 if (!(p = strv_join(arg_wall, " "))) {
151 log_error("Failed to join strings.");
152 return;
153 }
154
155 if (*p) {
156 utmp_wall(p);
157 free(p);
158 return;
159 }
160
161 free(p);
162 }
163
514f4ef5 164 if (!table[action])
ef2f1067
LP
165 return;
166
514f4ef5 167 utmp_wall(table[action]);
ef2f1067
LP
168}
169
7e4249b9
LP
170static int list_units(DBusConnection *bus, char **args, unsigned n) {
171 DBusMessage *m = NULL, *reply = NULL;
172 DBusError error;
173 int r;
174 DBusMessageIter iter, sub, sub2;
175 unsigned k = 0;
176
177 dbus_error_init(&error);
178
514f4ef5
LP
179 assert(bus);
180
7e4249b9
LP
181 if (!(m = dbus_message_new_method_call(
182 "org.freedesktop.systemd1",
183 "/org/freedesktop/systemd1",
184 "org.freedesktop.systemd1.Manager",
185 "ListUnits"))) {
186 log_error("Could not allocate message.");
187 return -ENOMEM;
188 }
189
190 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
191 log_error("Failed to issue method call: %s", error.message);
192 r = -EIO;
193 goto finish;
194 }
195
196 if (!dbus_message_iter_init(reply, &iter) ||
197 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
198 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
199 log_error("Failed to parse reply.");
200 r = -EIO;
201 goto finish;
202 }
203
204 dbus_message_iter_recurse(&iter, &sub);
205
206 printf("%-45s %-6s %-12s %-12s %-15s %s\n", "UNIT", "LOAD", "ACTIVE", "SUB", "JOB", "DESCRIPTION");
207
208 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
209 const char *id, *description, *load_state, *active_state, *sub_state, *unit_state, *job_type, *job_path, *dot;
210 uint32_t job_id;
211
212 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
213 log_error("Failed to parse reply.");
214 r = -EIO;
215 goto finish;
216 }
217
218 dbus_message_iter_recurse(&sub, &sub2);
219
220 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
221 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &description, true) < 0 ||
222 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 ||
223 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 ||
224 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 ||
225 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_state, true) < 0 ||
226 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &job_id, true) < 0 ||
227 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &job_type, true) < 0 ||
228 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, false) < 0) {
229 log_error("Failed to parse reply.");
230 r = -EIO;
231 goto finish;
232 }
233
234 if ((!arg_type || ((dot = strrchr(id, '.')) &&
235 streq(dot+1, arg_type))) &&
236 (arg_all || !streq(active_state, "inactive"))) {
237
238 int a = 0, b = 0;
239
240 printf("%-45s %-6s %-12s %-12s%n", id, load_state, active_state, sub_state, &a);
241
242 if (job_id != 0)
243 printf(" %-15s%n", job_type, &b);
244 else
7e61bf20 245 b = 1 + 16;
7e4249b9
LP
246
247 if (a + b + 2 < columns()) {
248 if (job_id == 0)
249 printf(" ");
250
251 printf("%.*s", columns() - a - b - 2, description);
252 }
253
254 fputs("\n", stdout);
255 k++;
256 }
257
258 dbus_message_iter_next(&sub);
259 }
260
261 if (arg_all)
262 printf("\n%u units listed.\n", k);
263 else
264 printf("\n%u live units listed. Pass --all to see dead units, too.\n", k);
265
266 r = 0;
267
268finish:
269 if (m)
270 dbus_message_unref(m);
271
272 if (reply)
273 dbus_message_unref(reply);
274
275 dbus_error_free(&error);
276
277 return r;
278}
279
280static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
281 DBusMessage *m = NULL, *reply = NULL;
282 DBusError error;
283 int r;
284 DBusMessageIter iter, sub, sub2;
285 unsigned k = 0;
286
287 dbus_error_init(&error);
288
514f4ef5
LP
289 assert(bus);
290
7e4249b9
LP
291 if (!(m = dbus_message_new_method_call(
292 "org.freedesktop.systemd1",
293 "/org/freedesktop/systemd1",
294 "org.freedesktop.systemd1.Manager",
295 "ListJobs"))) {
296 log_error("Could not allocate message.");
297 return -ENOMEM;
298 }
299
300 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
301 log_error("Failed to issue method call: %s", error.message);
302 r = -EIO;
303 goto finish;
304 }
305
306 if (!dbus_message_iter_init(reply, &iter) ||
307 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
308 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
309 log_error("Failed to parse reply.");
310 r = -EIO;
311 goto finish;
312 }
313
314 dbus_message_iter_recurse(&iter, &sub);
315
316 printf("%4s %-45s %-17s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
317
318 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
319 const char *name, *type, *state, *job_path, *unit_path;
320 uint32_t id;
321
322 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
323 log_error("Failed to parse reply.");
324 r = -EIO;
325 goto finish;
326 }
327
328 dbus_message_iter_recurse(&sub, &sub2);
329
330 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &id, true) < 0 ||
331 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
332 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
333 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, true) < 0 ||
334 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, true) < 0 ||
335 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, false) < 0) {
336 log_error("Failed to parse reply.");
337 r = -EIO;
338 goto finish;
339 }
340
341 printf("%4u %-45s %-17s %-7s\n", id, name, type, state);
342 k++;
343
344 dbus_message_iter_next(&sub);
345 }
346
347 printf("\n%u jobs listed.\n", k);
348 r = 0;
349
350finish:
351 if (m)
352 dbus_message_unref(m);
353
354 if (reply)
355 dbus_message_unref(reply);
356
357 dbus_error_free(&error);
358
359 return r;
360}
361
362static int load_unit(DBusConnection *bus, char **args, unsigned n) {
363 DBusMessage *m = NULL, *reply = NULL;
364 DBusError error;
365 int r;
366 unsigned i;
367
368 dbus_error_init(&error);
369
514f4ef5
LP
370 assert(bus);
371 assert(args);
372
7e4249b9
LP
373 for (i = 1; i < n; i++) {
374
375 if (!(m = dbus_message_new_method_call(
376 "org.freedesktop.systemd1",
377 "/org/freedesktop/systemd1",
378 "org.freedesktop.systemd1.Manager",
379 "LoadUnit"))) {
380 log_error("Could not allocate message.");
381 r = -ENOMEM;
382 goto finish;
383 }
384
385 if (!dbus_message_append_args(m,
386 DBUS_TYPE_STRING, &args[i],
387 DBUS_TYPE_INVALID)) {
388 log_error("Could not append arguments to message.");
389 r = -ENOMEM;
390 goto finish;
391 }
392
393 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
394 log_error("Failed to issue method call: %s", error.message);
395 r = -EIO;
396 goto finish;
397 }
398
399 dbus_message_unref(m);
400 dbus_message_unref(reply);
401
402 m = reply = NULL;
403 }
404
405 r = 0;
406
407finish:
408 if (m)
409 dbus_message_unref(m);
410
411 if (reply)
412 dbus_message_unref(reply);
413
414 dbus_error_free(&error);
415
416 return r;
417}
418
419static int cancel_job(DBusConnection *bus, char **args, unsigned n) {
420 DBusMessage *m = NULL, *reply = NULL;
421 DBusError error;
422 int r;
423 unsigned i;
424
425 dbus_error_init(&error);
426
514f4ef5
LP
427 assert(bus);
428 assert(args);
429
7e4249b9
LP
430 for (i = 1; i < n; i++) {
431 unsigned id;
432 const char *path;
433
434 if (!(m = dbus_message_new_method_call(
435 "org.freedesktop.systemd1",
436 "/org/freedesktop/systemd1",
437 "org.freedesktop.systemd1.Manager",
438 "GetJob"))) {
439 log_error("Could not allocate message.");
440 r = -ENOMEM;
441 goto finish;
442 }
443
444 if ((r = safe_atou(args[i], &id)) < 0) {
445 log_error("Failed to parse job id: %s", strerror(-r));
446 goto finish;
447 }
448
449 assert_cc(sizeof(uint32_t) == sizeof(id));
450 if (!dbus_message_append_args(m,
451 DBUS_TYPE_UINT32, &id,
452 DBUS_TYPE_INVALID)) {
453 log_error("Could not append arguments to message.");
454 r = -ENOMEM;
455 goto finish;
456 }
457
458 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
459 log_error("Failed to issue method call: %s", error.message);
460 r = -EIO;
461 goto finish;
462 }
463
464 if (!dbus_message_get_args(reply, &error,
465 DBUS_TYPE_OBJECT_PATH, &path,
466 DBUS_TYPE_INVALID)) {
467 log_error("Failed to parse reply: %s", error.message);
468 r = -EIO;
469 goto finish;
470 }
471
472 dbus_message_unref(m);
473 if (!(m = dbus_message_new_method_call(
474 "org.freedesktop.systemd1",
475 path,
476 "org.freedesktop.systemd1.Job",
477 "Cancel"))) {
478 log_error("Could not allocate message.");
479 r = -ENOMEM;
480 goto finish;
481 }
482
483 dbus_message_unref(reply);
484 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
485 log_error("Failed to issue method call: %s", error.message);
486 r = -EIO;
487 goto finish;
488 }
489
490 dbus_message_unref(m);
491 dbus_message_unref(reply);
492 m = reply = NULL;
493 }
494
495 r = 0;
496
497finish:
498 if (m)
499 dbus_message_unref(m);
500
501 if (reply)
502 dbus_message_unref(reply);
503
504 dbus_error_free(&error);
505
506 return r;
507}
508
5e374895
LP
509typedef struct WaitData {
510 Set *set;
511 bool failed;
512} WaitData;
513
7e4249b9
LP
514static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) {
515 DBusError error;
5e374895 516 WaitData *d = data;
7e4249b9
LP
517
518 assert(connection);
519 assert(message);
5e374895 520 assert(d);
7e4249b9
LP
521
522 dbus_error_init(&error);
523
524 /* log_debug("Got D-Bus request: %s.%s() on %s", */
525 /* dbus_message_get_interface(message), */
526 /* dbus_message_get_member(message), */
527 /* dbus_message_get_path(message)); */
528
529 if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
530 log_error("Warning! D-Bus connection terminated.");
531 dbus_connection_close(connection);
532
533 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
534 uint32_t id;
535 const char *path;
5e374895 536 dbus_bool_t success = true;
7e4249b9
LP
537
538 if (!dbus_message_get_args(message, &error,
539 DBUS_TYPE_UINT32, &id,
540 DBUS_TYPE_OBJECT_PATH, &path,
5e374895 541 DBUS_TYPE_BOOLEAN, &success,
7e4249b9
LP
542 DBUS_TYPE_INVALID))
543 log_error("Failed to parse message: %s", error.message);
544 else {
545 char *p;
546
5e374895 547 if ((p = set_remove(d->set, (char*) path)))
7e4249b9 548 free(p);
5e374895
LP
549
550 if (!success)
551 d->failed = true;
7e4249b9
LP
552 }
553 }
554
555 dbus_error_free(&error);
556 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
557}
558
479ef5d3 559static int enable_wait_for_jobs(DBusConnection *bus) {
7e4249b9 560 DBusError error;
7e4249b9
LP
561
562 assert(bus);
7e4249b9
LP
563
564 dbus_error_init(&error);
7e4249b9
LP
565 dbus_bus_add_match(bus,
566 "type='signal',"
567 "sender='org.freedesktop.systemd1',"
568 "interface='org.freedesktop.systemd1.Manager',"
569 "member='JobRemoved',"
570 "path='/org/freedesktop/systemd1'",
571 &error);
572
573 if (dbus_error_is_set(&error)) {
574 log_error("Failed to add match: %s", error.message);
a567261a
LP
575 dbus_error_free(&error);
576 return -EIO;
7e4249b9
LP
577 }
578
479ef5d3 579 /* This is slightly dirty, since we don't undo the match registrations. */
a567261a 580 return 0;
7e4249b9
LP
581}
582
479ef5d3
LP
583static int wait_for_jobs(DBusConnection *bus, Set *s) {
584 int r;
5e374895 585 WaitData d;
479ef5d3
LP
586
587 assert(bus);
588 assert(s);
589
5e374895
LP
590 zero(d);
591 d.set = s;
592 d.failed = false;
593
594 if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL)) {
479ef5d3
LP
595 log_error("Failed to add filter.");
596 r = -ENOMEM;
597 goto finish;
598 }
599
600 while (!set_isempty(s) &&
601 dbus_connection_read_write_dispatch(bus, -1))
602 ;
603
5e374895
LP
604 if (!arg_quiet && d.failed)
605 log_error("Job failed, see logs for details.");
606
607 r = d.failed ? -EIO : 0;
479ef5d3
LP
608
609finish:
610 /* This is slightly dirty, since we don't undo the filter registration. */
611
612 return r;
613}
614
e4b61340
LP
615static int start_unit_one(
616 DBusConnection *bus,
617 const char *method,
618 const char *name,
619 const char *mode,
620 Set *s) {
7e4249b9 621
7e4249b9
LP
622 DBusMessage *m = NULL, *reply = NULL;
623 DBusError error;
624 int r;
7e4249b9 625
e4b61340
LP
626 assert(bus);
627 assert(method);
628 assert(name);
629 assert(mode);
6e905d93 630 assert(arg_no_block || s);
7e4249b9 631
e4b61340 632 dbus_error_init(&error);
479ef5d3 633
7e4249b9
LP
634 if (!(m = dbus_message_new_method_call(
635 "org.freedesktop.systemd1",
636 "/org/freedesktop/systemd1",
637 "org.freedesktop.systemd1.Manager",
e4b61340 638 method))) {
7e4249b9
LP
639 log_error("Could not allocate message.");
640 r = -ENOMEM;
641 goto finish;
642 }
643
644 if (!dbus_message_append_args(m,
e4b61340 645 DBUS_TYPE_STRING, &name,
7e4249b9
LP
646 DBUS_TYPE_STRING, &mode,
647 DBUS_TYPE_INVALID)) {
648 log_error("Could not append arguments to message.");
649 r = -ENOMEM;
650 goto finish;
651 }
652
653 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
e4b61340
LP
654
655 if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) {
656 /* There's always a fallback possible for
657 * legacy actions. */
658 r = 0;
659 goto finish;
660 }
661
7e4249b9
LP
662 log_error("Failed to issue method call: %s", error.message);
663 r = -EIO;
664 goto finish;
665 }
666
6e905d93 667 if (!arg_no_block) {
7e4249b9 668 const char *path;
e4b61340 669 char *p;
7e4249b9
LP
670
671 if (!dbus_message_get_args(reply, &error,
672 DBUS_TYPE_OBJECT_PATH, &path,
673 DBUS_TYPE_INVALID)) {
674 log_error("Failed to parse reply: %s", error.message);
675 r = -EIO;
676 goto finish;
677 }
678
7e4249b9
LP
679 if (!(p = strdup(path))) {
680 log_error("Failed to duplicate path.");
681 r = -ENOMEM;
682 goto finish;
683 }
684
685 if ((r = set_put(s, p)) < 0) {
e4b61340 686 free(p);
7e4249b9
LP
687 log_error("Failed to add path to set.");
688 goto finish;
689 }
e4b61340 690 }
7e4249b9 691
e4b61340 692 r = 1;
7e4249b9
LP
693
694finish:
7e4249b9
LP
695 if (m)
696 dbus_message_unref(m);
697
698 if (reply)
699 dbus_message_unref(reply);
700
701 dbus_error_free(&error);
702
703 return r;
704}
705
514f4ef5
LP
706static enum action verb_to_action(const char *verb) {
707 if (streq(verb, "halt"))
708 return ACTION_HALT;
709 else if (streq(verb, "poweroff"))
710 return ACTION_POWEROFF;
711 else if (streq(verb, "reboot"))
712 return ACTION_REBOOT;
713 else if (streq(verb, "rescue"))
714 return ACTION_RESCUE;
715 else if (streq(verb, "emergency"))
716 return ACTION_EMERGENCY;
717 else if (streq(verb, "default"))
718 return ACTION_DEFAULT;
719 else
720 return ACTION_INVALID;
721}
722
e4b61340
LP
723static int start_unit(DBusConnection *bus, char **args, unsigned n) {
724
725 static const char * const table[_ACTION_MAX] = {
514f4ef5
LP
726 [ACTION_HALT] = SPECIAL_HALT_TARGET,
727 [ACTION_POWEROFF] = SPECIAL_POWEROFF_TARGET,
728 [ACTION_REBOOT] = SPECIAL_REBOOT_TARGET,
729 [ACTION_RUNLEVEL2] = SPECIAL_RUNLEVEL2_TARGET,
730 [ACTION_RUNLEVEL3] = SPECIAL_RUNLEVEL3_TARGET,
731 [ACTION_RUNLEVEL4] = SPECIAL_RUNLEVEL4_TARGET,
732 [ACTION_RUNLEVEL5] = SPECIAL_RUNLEVEL5_TARGET,
733 [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET,
734 [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_SERVICE,
735 [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET
e4b61340
LP
736 };
737
738 int r;
739 unsigned i;
514f4ef5 740 const char *method, *mode, *one_name;
e4b61340
LP
741 Set *s = NULL;
742
514f4ef5
LP
743 assert(bus);
744
e4b61340
LP
745 if (arg_action == ACTION_SYSTEMCTL) {
746 method =
e4b61340
LP
747 streq(args[0], "stop") ? "StopUnit" :
748 streq(args[0], "reload") ? "ReloadUnit" :
749 streq(args[0], "restart") ? "RestartUnit" :
514f4ef5 750 "StartUnit";
e4b61340
LP
751
752 mode =
514f4ef5
LP
753 (streq(args[0], "isolate") ||
754 streq(args[0], "rescue") ||
755 streq(args[0], "emergency")) ? "isolate" :
756 arg_replace ? "replace" :
757 "fail";
e4b61340 758
514f4ef5 759 one_name = table[verb_to_action(args[0])];
e4b61340 760
e4b61340
LP
761 } else {
762 assert(arg_action < ELEMENTSOF(table));
763 assert(table[arg_action]);
764
765 method = "StartUnit";
514f4ef5
LP
766
767 mode = (arg_action == ACTION_EMERGENCY ||
768 arg_action == ACTION_RESCUE) ? "isolate" : "replace";
769
770 one_name = table[arg_action];
771 }
772
6e905d93 773 if (!arg_no_block) {
514f4ef5
LP
774 if ((r = enable_wait_for_jobs(bus)) < 0) {
775 log_error("Could not watch jobs: %s", strerror(-r));
776 goto finish;
777 }
778
779 if (!(s = set_new(string_hash_func, string_compare_func))) {
780 log_error("Failed to allocate set.");
781 r = -ENOMEM;
782 goto finish;
783 }
e4b61340
LP
784 }
785
786 r = 0;
787
514f4ef5
LP
788 if (one_name) {
789 if ((r = start_unit_one(bus, method, one_name, mode, s)) <= 0)
790 goto finish;
791 } else {
e4b61340
LP
792 for (i = 1; i < n; i++)
793 if ((r = start_unit_one(bus, method, args[i], mode, s)) < 0)
794 goto finish;
e4b61340
LP
795 }
796
6e905d93 797 if (!arg_no_block)
514f4ef5
LP
798 r = wait_for_jobs(bus, s);
799
e4b61340
LP
800finish:
801 if (s)
802 set_free_free(s);
803
804 return r;
805}
806
514f4ef5
LP
807static int start_special(DBusConnection *bus, char **args, unsigned n) {
808 assert(bus);
809 assert(args);
810
811 warn_wall(verb_to_action(args[0]));
812
813 return start_unit(bus, args, n);
814}
815
0183528f
LP
816static int check_unit(DBusConnection *bus, char **args, unsigned n) {
817 DBusMessage *m = NULL, *reply = NULL;
818 const char
819 *interface = "org.freedesktop.systemd1.Unit",
820 *property = "ActiveState";
821 int r = -EADDRNOTAVAIL;
822 DBusError error;
823 unsigned i;
824
825 assert(bus);
826 assert(args);
827
828 dbus_error_init(&error);
829
830 for (i = 1; i < n; i++) {
831 const char *path = NULL;
832 const char *state;
833 DBusMessageIter iter, sub;
834
835 if (!(m = dbus_message_new_method_call(
836 "org.freedesktop.systemd1",
837 "/org/freedesktop/systemd1",
838 "org.freedesktop.systemd1.Manager",
839 "GetUnit"))) {
840 log_error("Could not allocate message.");
841 r = -ENOMEM;
842 goto finish;
843 }
844
845 if (!dbus_message_append_args(m,
846 DBUS_TYPE_STRING, &args[i],
847 DBUS_TYPE_INVALID)) {
848 log_error("Could not append arguments to message.");
849 r = -ENOMEM;
850 goto finish;
851 }
852
853 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
854
855 /* Hmm, cannot figure out anything about this unit... */
856 if (!arg_quiet)
857 puts("unknown");
858
ed2d7a44 859 dbus_error_free(&error);
0183528f
LP
860 continue;
861 }
862
863 if (!dbus_message_get_args(reply, &error,
864 DBUS_TYPE_OBJECT_PATH, &path,
865 DBUS_TYPE_INVALID)) {
866 log_error("Failed to parse reply: %s", error.message);
867 r = -EIO;
868 goto finish;
869 }
870
871 dbus_message_unref(m);
872 if (!(m = dbus_message_new_method_call(
873 "org.freedesktop.systemd1",
874 path,
875 "org.freedesktop.DBus.Properties",
876 "Get"))) {
877 log_error("Could not allocate message.");
878 r = -ENOMEM;
879 goto finish;
880 }
881
882 if (!dbus_message_append_args(m,
883 DBUS_TYPE_STRING, &interface,
884 DBUS_TYPE_STRING, &property,
885 DBUS_TYPE_INVALID)) {
886 log_error("Could not append arguments to message.");
887 r = -ENOMEM;
888 goto finish;
889 }
890
891 dbus_message_unref(reply);
892 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
893 log_error("Failed to issue method call: %s", error.message);
894 r = -EIO;
895 goto finish;
896 }
897
898 if (!dbus_message_iter_init(reply, &iter) ||
899 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
900 log_error("Failed to parse reply.");
901 r = -EIO;
902 goto finish;
903 }
904
905 dbus_message_iter_recurse(&iter, &sub);
906
907 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
908 log_error("Failed to parse reply.");
909 r = -EIO;
910 goto finish;
911 }
912
913 dbus_message_iter_get_basic(&sub, &state);
914
915 if (!arg_quiet)
916 puts(state);
917
01b1b079 918 if (streq(state, "active") || startswith(state, "reloading"))
0183528f
LP
919 r = 0;
920
921 dbus_message_unref(m);
922 dbus_message_unref(reply);
923 m = reply = NULL;
924 }
925
926finish:
927 if (m)
928 dbus_message_unref(m);
929
930 if (reply)
931 dbus_message_unref(reply);
932
933 dbus_error_free(&error);
934
935 return r;
48220598
LP
936}
937
c59760ee 938static void show_cgroup(const char *name) {
5f750597
LP
939 char *fn;
940 FILE *f;
941 pid_t last = 0;
c59760ee
LP
942
943 if (!startswith(name, "name=systemd:"))
944 return;
945
946 if (asprintf(&fn, "/cgroup/systemd/%s/tasks", name + 13) < 0)
947 return;
948
5f750597 949 f = fopen(fn, "r");
c59760ee
LP
950 free(fn);
951
5f750597 952 if (!f)
c59760ee
LP
953 return;
954
5f750597
LP
955 while (!feof(f)) {
956 unsigned long ul;
c59760ee 957
5f750597 958 if (fscanf(f, "%lu", &ul) != 1)
c59760ee
LP
959 break;
960
5f750597
LP
961 if (ul <= 0)
962 continue;
963
964 if (last > 0) {
965 char *t = NULL;
966 get_process_cmdline(last, 60, &t);
fc6071bf 967 printf("\t\t \342\224\234 %lu %s\n", (unsigned long) last, strna(t));
5f750597 968 free(t);
7e61bf20
LP
969 } else
970 printf("\t\t \342\224\202\n");
5f750597
LP
971
972 last = (pid_t) ul;
973 }
974
975 if (last > 0) {
976 char *t = NULL;
977 get_process_cmdline(last, 60, &t);
fc6071bf 978 printf("\t\t \342\224\224 %lu %s\n", (unsigned long) last, strna(t));
c59760ee
LP
979 free(t);
980 }
981
5f750597 982 fclose(f);
c59760ee
LP
983}
984
61cbdc4b
LP
985typedef struct UnitStatusInfo {
986 const char *id;
987 const char *load_state;
988 const char *active_state;
989 const char *sub_state;
990
991 const char *description;
992
993 const char *fragment_path;
994 const char *default_control_group;
995
996 /* Service */
997 pid_t main_pid;
998 pid_t control_pid;
999 const char *status_text;
1000 bool running;
1001
1002 usec_t start_timestamp;
1003 usec_t exit_timestamp;
1004
1005 int exit_code, exit_status;
1006
1007 /* Socket */
1008 unsigned n_accepted;
1009 unsigned n_connections;
1010
1011 /* Device */
1012 const char *sysfs_path;
1013
1014 /* Mount, Automount */
1015 const char *where;
1016
1017 /* Swap */
1018 const char *what;
1019} UnitStatusInfo;
1020
1021static void print_status_info(UnitStatusInfo *i) {
1022 assert(i);
1023
1024 /* This shows pretty information about a unit. See
1025 * print_property() for a low-level property printer */
1026
1027 printf("%s", strna(i->id));
1028
1029 if (i->description && !streq_ptr(i->id, i->description))
1030 printf(" - %s", i->description);
1031
1032 printf("\n");
1033
1034 if (i->fragment_path)
1035 printf("\t Loaded: %s (%s)\n", strna(i->load_state), i->fragment_path);
1036 else if (streq_ptr(i->load_state, "failed"))
1037 printf("\t Loaded: " ANSI_HIGHLIGHT_ON "%s" ANSI_HIGHLIGHT_OFF "\n", strna(i->load_state));
1038 else
1039 printf("\t Loaded: %s\n", strna(i->load_state));
1040
1041 if (streq_ptr(i->active_state, "maintenance"))
1042 printf("\t Active: " ANSI_HIGHLIGHT_ON "%s (%s)" ANSI_HIGHLIGHT_OFF "\n",
1043 strna(i->active_state),
1044 strna(i->sub_state));
1045 else
1046 printf("\t Active: %s (%s)\n",
1047 strna(i->active_state),
1048 strna(i->sub_state));
1049
1050 if (i->sysfs_path)
1051 printf("\t Device: %s\n", i->sysfs_path);
1052 else if (i->where)
1053 printf("\t Where: %s\n", i->where);
1054 else if (i->what)
1055 printf("\t What: %s\n", i->what);
1056
1057 if (i->status_text)
1058 printf("\t Status: \"%s\"\n", i->status_text);
1059
1060 if (i->id && endswith(i->id, ".socket"))
1061 printf("\tAccepted: %u; Connected: %u\n", i->n_accepted, i->n_connections);
1062
1063 if (i->main_pid > 0 || i->control_pid > 0) {
1064 printf("\t");
1065
1066 if (i->main_pid > 0) {
1067 printf(" Process: %u", (unsigned) i->main_pid);
1068
1069 if (i->running) {
1070 char *t = NULL;
1071 get_process_name(i->main_pid, &t);
1072 if (t) {
1073 printf(" (%s)", t);
1074 free(t);
1075 }
1076 } else {
1077 printf(" (code=%s, ", sigchld_code_to_string(i->exit_code));
1078
1079 if (i->exit_code == CLD_EXITED)
1080 printf("status=%i", i->exit_status);
1081 else
1082 printf("signal=%s", strsignal(i->exit_status));
1083 printf(")");
1084 }
1085 }
1086
1087 if (i->main_pid > 0 && i->control_pid > 0)
1088 printf(";");
1089
1090 if (i->control_pid > 0) {
1091 char *t = NULL;
1092
1093 printf(" Control: %u", (unsigned) i->control_pid);
1094
1095 get_process_name(i->control_pid, &t);
1096 if (t) {
1097 printf(" (%s)", t);
1098 free(t);
1099 }
1100 }
1101
1102 printf("\n");
1103 }
1104
c59760ee 1105 if (i->default_control_group) {
61cbdc4b 1106 printf("\t CGroup: %s\n", i->default_control_group);
c59760ee
LP
1107 show_cgroup(i->default_control_group);
1108 }
61cbdc4b
LP
1109}
1110
1111static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) {
1112
1113 switch (dbus_message_iter_get_arg_type(iter)) {
1114
1115 case DBUS_TYPE_STRING: {
1116 const char *s;
1117
1118 dbus_message_iter_get_basic(iter, &s);
1119
1120 if (s[0]) {
1121 if (streq(name, "Id"))
1122 i->id = s;
1123 else if (streq(name, "LoadState"))
1124 i->load_state = s;
1125 else if (streq(name, "ActiveState"))
1126 i->active_state = s;
1127 else if (streq(name, "SubState"))
1128 i->sub_state = s;
1129 else if (streq(name, "Description"))
1130 i->description = s;
1131 else if (streq(name, "FragmentPath"))
1132 i->fragment_path = s;
1133 else if (streq(name, "DefaultControlGroup"))
1134 i->default_control_group = s;
1135 else if (streq(name, "StatusText"))
1136 i->status_text = s;
1137 else if (streq(name, "SysFSPath"))
1138 i->sysfs_path = s;
1139 else if (streq(name, "Where"))
1140 i->where = s;
1141 else if (streq(name, "What"))
1142 i->what = s;
1143 }
1144
1145 break;
1146 }
1147
1148 case DBUS_TYPE_UINT32: {
1149 uint32_t u;
1150
1151 dbus_message_iter_get_basic(iter, &u);
1152
1153 if (streq(name, "MainPID")) {
1154 if (u > 0) {
1155 i->main_pid = (pid_t) u;
1156 i->running = true;
1157 }
1158 } else if (streq(name, "ControlPID"))
1159 i->control_pid = (pid_t) u;
1160 else if (streq(name, "ExecMainPID")) {
1161 if (u > 0)
1162 i->main_pid = (pid_t) u;
1163 } else if (streq(name, "NAccepted"))
1164 i->n_accepted = u;
1165 else if (streq(name, "NConnections"))
1166 i->n_connections = u;
1167
1168 break;
1169 }
1170
1171 case DBUS_TYPE_INT32: {
1172 int32_t j;
1173
1174 dbus_message_iter_get_basic(iter, &j);
1175
1176 if (streq(name, "ExecMainCode"))
1177 i->exit_code = (int) j;
1178 else if (streq(name, "ExecMainStatus"))
1179 i->exit_status = (int) j;
1180
1181 break;
1182 }
1183
1184 case DBUS_TYPE_UINT64: {
1185 uint64_t u;
1186
1187 dbus_message_iter_get_basic(iter, &u);
1188
1189 if (streq(name, "ExecMainStartTimestamp"))
1190 i->start_timestamp = (usec_t) u;
1191 else if (streq(name, "ExecMainExitTimestamp"))
1192 i->exit_timestamp = (usec_t) u;
1193
1194 break;
1195 }
1196 }
1197
1198 return 0;
1199}
1200
48220598
LP
1201static int print_property(const char *name, DBusMessageIter *iter) {
1202 assert(name);
1203 assert(iter);
1204
61cbdc4b
LP
1205 /* This is a low-level property printer, see
1206 * print_status_info() for the nicer output */
1207
48220598
LP
1208 if (arg_property && !streq(name, arg_property))
1209 return 0;
1210
1211 switch (dbus_message_iter_get_arg_type(iter)) {
1212
1213 case DBUS_TYPE_STRING: {
1214 const char *s;
1215 dbus_message_iter_get_basic(iter, &s);
1216
1217 if (arg_all || s[0])
1218 printf("%s=%s\n", name, s);
1219
1220 return 0;
1221 }
1222
1223 case DBUS_TYPE_BOOLEAN: {
1224 dbus_bool_t b;
1225 dbus_message_iter_get_basic(iter, &b);
1226 printf("%s=%s\n", name, yes_no(b));
1227
1228 return 0;
1229 }
1230
1231 case DBUS_TYPE_UINT64: {
1232 uint64_t u;
1233 dbus_message_iter_get_basic(iter, &u);
1234
1235 /* Yes, heuristics! But we can change this check
1236 * should it turn out to not be sufficient */
1237
14ad1d14 1238 if (strstr(name, "Timestamp")) {
48220598
LP
1239 char timestamp[FORMAT_TIMESTAMP_MAX], *t;
1240
1241 if ((t = format_timestamp(timestamp, sizeof(timestamp), u)) || arg_all)
1242 printf("%s=%s\n", name, strempty(t));
552e4331
LP
1243 } else if (strstr(name, "USec")) {
1244 char timespan[FORMAT_TIMESPAN_MAX];
1245
1246 printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u));
48220598
LP
1247 } else
1248 printf("%s=%llu\n", name, (unsigned long long) u);
1249
1250 return 0;
1251 }
1252
1253 case DBUS_TYPE_UINT32: {
1254 uint32_t u;
1255 dbus_message_iter_get_basic(iter, &u);
1256
5bd07073 1257 if (strstr(name, "UMask") || strstr(name, "Mode"))
48220598
LP
1258 printf("%s=%04o\n", name, u);
1259 else
1260 printf("%s=%u\n", name, (unsigned) u);
1261
1262 return 0;
1263 }
1264
1265 case DBUS_TYPE_INT32: {
1266 int32_t i;
1267 dbus_message_iter_get_basic(iter, &i);
1268
1269 printf("%s=%i\n", name, (int) i);
1270 return 0;
1271 }
1272
1273 case DBUS_TYPE_STRUCT: {
1274 DBusMessageIter sub;
1275 dbus_message_iter_recurse(iter, &sub);
1276
ebf57b80 1277 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "Job")) {
48220598
LP
1278 uint32_t u;
1279
1280 dbus_message_iter_get_basic(&sub, &u);
1281
1282 if (u)
1283 printf("%s=%u\n", name, (unsigned) u);
1284 else if (arg_all)
1285 printf("%s=\n", name);
1286
1287 return 0;
ebf57b80 1288 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Unit")) {
48220598
LP
1289 const char *s;
1290
1291 dbus_message_iter_get_basic(&sub, &s);
1292
1293 if (arg_all || s[0])
1294 printf("%s=%s\n", name, s);
1295
1296 return 0;
1297 }
1298
1299 break;
1300 }
1301
1302 case DBUS_TYPE_ARRAY:
1303
1304 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
1305 DBusMessageIter sub;
1306 bool space = false;
1307
1308 dbus_message_iter_recurse(iter, &sub);
1309
1310 if (arg_all ||
1311 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1312 printf("%s=", name);
1313
1314 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1315 const char *s;
1316
1317 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
1318 dbus_message_iter_get_basic(&sub, &s);
1319 printf("%s%s", space ? " " : "", s);
1320
1321 space = true;
1322 dbus_message_iter_next(&sub);
1323 }
1324
1325 puts("");
1326 }
1327
ebf57b80 1328 return 0;
82c121a4
LP
1329 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) {
1330 DBusMessageIter sub;
1331
1332 dbus_message_iter_recurse(iter, &sub);
1333
1334 if (arg_all ||
1335 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1336 printf("%s=", name);
1337
1338 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1339 uint8_t u;
ebf57b80 1340
82c121a4
LP
1341 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE);
1342 dbus_message_iter_get_basic(&sub, &u);
1343 printf("%02x", u);
1344
1345 dbus_message_iter_next(&sub);
1346 }
1347
1348 puts("");
1349 }
1350
1351 return 0;
ebf57b80
LP
1352 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Paths")) {
1353 DBusMessageIter sub, sub2;
1354
1355 dbus_message_iter_recurse(iter, &sub);
1356
1357 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
1358 const char *type, *path;
1359
1360 dbus_message_iter_recurse(&sub, &sub2);
1361
1362 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) >= 0 &&
1363 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, false) >= 0)
1364 printf("%s=%s\n", type, path);
1365
1366 dbus_message_iter_next(&sub);
1367 }
1368
707e5e52
LP
1369 return 0;
1370 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Timers")) {
1371 DBusMessageIter sub, sub2;
1372
1373 dbus_message_iter_recurse(iter, &sub);
1374
1375 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
1376 const char *base;
1377 uint64_t value, next_elapse;
1378
1379 dbus_message_iter_recurse(&sub, &sub2);
1380
1381 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &base, true) >= 0 &&
1382 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &value, true) >= 0 &&
552e4331
LP
1383 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &next_elapse, false) >= 0) {
1384 char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX];
1385
1386 printf("%s={ value=%s ; next_elapse=%s }\n",
fe68089d 1387 base,
552e4331
LP
1388 format_timespan(timespan1, sizeof(timespan1), value),
1389 format_timespan(timespan2, sizeof(timespan2), next_elapse));
1390 }
fe68089d
LP
1391
1392 dbus_message_iter_next(&sub);
1393 }
1394
1395 return 0;
1396 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && startswith(name, "Exec")) {
1397
1398 DBusMessageIter sub, sub2, sub3;
1399
1400 dbus_message_iter_recurse(iter, &sub);
1401
1402 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
1403 const char *path;
1404 uint64_t start_time, exit_time;
1405 uint32_t pid;
1406 int32_t code, status;
1407
1408 dbus_message_iter_recurse(&sub, &sub2);
1409
1410 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0)
1411 continue;
1412
1413 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_ARRAY ||
1414 dbus_message_iter_get_element_type(&sub2) != DBUS_TYPE_STRING)
1415 continue;
1416
1417 printf("%s={ path=%s ; argv[]=", name, path);
1418
1419 dbus_message_iter_recurse(&sub2, &sub3);
1420
1421 while (dbus_message_iter_get_arg_type(&sub3) != DBUS_TYPE_INVALID) {
1422 const char *s;
1423
1424 assert(dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_STRING);
1425 dbus_message_iter_get_basic(&sub3, &s);
1426 printf("%s ", s);
1427 dbus_message_iter_next(&sub3);
1428 }
1429
1430 if (dbus_message_iter_next(&sub2) &&
1431 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &start_time, true) >= 0 &&
1432 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &exit_time, true) >= 0 &&
1433 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, true) >= 0 &&
1434 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &code, true) >= 0 &&
1435 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &status, false) >= 0) {
1436
1437 char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
1438
1439 printf("; start=%s ; stop=%s ; pid=%u ; code=%s ; status=%i/%s",
1440 strna(format_timestamp(timestamp1, sizeof(timestamp1), start_time)),
1441 strna(format_timestamp(timestamp2, sizeof(timestamp2), exit_time)),
1442 (unsigned) pid,
1443 sigchld_code_to_string(code),
1444 status,
61cbdc4b 1445 strempty(code == CLD_EXITED ? NULL : strsignal(status)));
fe68089d
LP
1446 }
1447
1448 printf(" }\n");
707e5e52
LP
1449
1450 dbus_message_iter_next(&sub);
1451 }
1452
48220598
LP
1453 return 0;
1454 }
1455
1456 break;
1457 }
1458
1459 if (arg_all)
1460 printf("%s=[unprintable]\n", name);
1461
1462 return 0;
1463}
1464
61cbdc4b 1465static int show_one(DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
48220598
LP
1466 DBusMessage *m = NULL, *reply = NULL;
1467 const char *interface = "";
1468 int r;
1469 DBusError error;
1470 DBusMessageIter iter, sub, sub2, sub3;
61cbdc4b 1471 UnitStatusInfo info;
48220598
LP
1472
1473 assert(bus);
1474 assert(path);
61cbdc4b 1475 assert(new_line);
48220598 1476
61cbdc4b 1477 zero(info);
48220598
LP
1478 dbus_error_init(&error);
1479
1480 if (!(m = dbus_message_new_method_call(
1481 "org.freedesktop.systemd1",
1482 path,
1483 "org.freedesktop.DBus.Properties",
1484 "GetAll"))) {
1485 log_error("Could not allocate message.");
1486 r = -ENOMEM;
1487 goto finish;
1488 }
1489
1490 if (!dbus_message_append_args(m,
1491 DBUS_TYPE_STRING, &interface,
1492 DBUS_TYPE_INVALID)) {
1493 log_error("Could not append arguments to message.");
1494 r = -ENOMEM;
1495 goto finish;
1496 }
1497
1498 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1499 log_error("Failed to issue method call: %s", error.message);
1500 r = -EIO;
1501 goto finish;
1502 }
1503
1504 if (!dbus_message_iter_init(reply, &iter) ||
1505 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
1506 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
1507 log_error("Failed to parse reply.");
1508 r = -EIO;
1509 goto finish;
1510 }
1511
1512 dbus_message_iter_recurse(&iter, &sub);
1513
61cbdc4b
LP
1514 if (*new_line)
1515 printf("\n");
1516
1517 *new_line = true;
1518
48220598
LP
1519 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1520 const char *name;
1521
1522 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
1523 log_error("Failed to parse reply.");
1524 r = -EIO;
1525 goto finish;
1526 }
1527
1528 dbus_message_iter_recurse(&sub, &sub2);
0183528f 1529
48220598
LP
1530 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
1531 log_error("Failed to parse reply.");
1532 r = -EIO;
1533 goto finish;
1534 }
1535
1536 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
1537 log_error("Failed to parse reply.");
1538 r = -EIO;
1539 goto finish;
1540 }
1541
1542 dbus_message_iter_recurse(&sub2, &sub3);
1543
61cbdc4b
LP
1544 if (show_properties)
1545 r = print_property(name, &sub3);
1546 else
1547 r = status_property(name, &sub3, &info);
1548
1549 if (r < 0) {
48220598
LP
1550 log_error("Failed to parse reply.");
1551 r = -EIO;
1552 goto finish;
1553 }
1554
1555 dbus_message_iter_next(&sub);
1556 }
1557
61cbdc4b
LP
1558 if (!show_properties)
1559 print_status_info(&info);
1560
48220598
LP
1561 r = 0;
1562
1563finish:
1564 if (m)
1565 dbus_message_unref(m);
1566
1567 if (reply)
1568 dbus_message_unref(reply);
1569
1570 dbus_error_free(&error);
1571
1572 return r;
1573}
1574
1575static int show(DBusConnection *bus, char **args, unsigned n) {
1576 DBusMessage *m = NULL, *reply = NULL;
1577 int r;
1578 DBusError error;
1579 unsigned i;
61cbdc4b 1580 bool show_properties, new_line = false;
48220598
LP
1581
1582 assert(bus);
1583 assert(args);
1584
1585 dbus_error_init(&error);
1586
61cbdc4b
LP
1587 show_properties = !streq(args[0], "status");
1588
1589 if (show_properties && n <= 1) {
48220598
LP
1590 /* If not argument is specified inspect the manager
1591 * itself */
1592
61cbdc4b 1593 r = show_one(bus, "/org/freedesktop/systemd1", show_properties, &new_line);
48220598
LP
1594 goto finish;
1595 }
1596
1597 for (i = 1; i < n; i++) {
1598 const char *path = NULL;
1599 uint32_t id;
1600
61cbdc4b 1601 if (!show_properties || safe_atou32(args[i], &id) < 0) {
48220598
LP
1602
1603 if (!(m = dbus_message_new_method_call(
1604 "org.freedesktop.systemd1",
1605 "/org/freedesktop/systemd1",
1606 "org.freedesktop.systemd1.Manager",
e87d1818 1607 "LoadUnit"))) {
48220598
LP
1608 log_error("Could not allocate message.");
1609 r = -ENOMEM;
1610 goto finish;
1611 }
1612
1613 if (!dbus_message_append_args(m,
1614 DBUS_TYPE_STRING, &args[i],
1615 DBUS_TYPE_INVALID)) {
1616 log_error("Could not append arguments to message.");
1617 r = -ENOMEM;
1618 goto finish;
1619 }
1620
ed2d7a44
LP
1621 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1622
1623 if (!dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) {
1624 log_error("Failed to issue method call: %s", error.message);
1625 r = -EIO;
1626 goto finish;
1627 }
1628
1629 dbus_error_free(&error);
1630
1631 dbus_message_unref(m);
1632 if (!(m = dbus_message_new_method_call(
1633 "org.freedesktop.systemd1",
1634 "/org/freedesktop/systemd1",
1635 "org.freedesktop.systemd1.Manager",
1636 "GetUnit"))) {
1637 log_error("Could not allocate message.");
1638 r = -ENOMEM;
1639 goto finish;
1640 }
1641
1642 if (!dbus_message_append_args(m,
1643 DBUS_TYPE_STRING, &args[i],
1644 DBUS_TYPE_INVALID)) {
1645 log_error("Could not append arguments to message.");
1646 r = -ENOMEM;
1647 goto finish;
1648 }
1649
1650 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1651 log_error("Failed to issue method call: %s", error.message);
1652 r = -EIO;
1653 goto finish;
1654 }
1655 }
1656
48220598
LP
1657 } else {
1658
1659 if (!(m = dbus_message_new_method_call(
1660 "org.freedesktop.systemd1",
1661 "/org/freedesktop/systemd1",
1662 "org.freedesktop.systemd1.Manager",
1663 "GetJob"))) {
1664 log_error("Could not allocate message.");
1665 r = -ENOMEM;
1666 goto finish;
1667 }
1668
1669 if (!dbus_message_append_args(m,
1670 DBUS_TYPE_UINT32, &id,
1671 DBUS_TYPE_INVALID)) {
1672 log_error("Could not append arguments to message.");
1673 r = -ENOMEM;
1674 goto finish;
1675 }
48220598 1676
ed2d7a44
LP
1677 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1678 log_error("Failed to issue method call: %s", error.message);
1679 r = -EIO;
1680 goto finish;
1681 }
48220598
LP
1682 }
1683
1684 if (!dbus_message_get_args(reply, &error,
1685 DBUS_TYPE_OBJECT_PATH, &path,
1686 DBUS_TYPE_INVALID)) {
1687 log_error("Failed to parse reply: %s", error.message);
1688 r = -EIO;
1689 goto finish;
1690 }
1691
61cbdc4b 1692 if ((r = show_one(bus, path, show_properties, &new_line)) < 0)
48220598
LP
1693 goto finish;
1694
1695 dbus_message_unref(m);
1696 dbus_message_unref(reply);
1697 m = reply = NULL;
1698 }
1699
1700 r = 0;
1701
1702finish:
1703 if (m)
1704 dbus_message_unref(m);
1705
1706 if (reply)
1707 dbus_message_unref(reply);
1708
1709 dbus_error_free(&error);
1710
1711 return r;
0183528f
LP
1712}
1713
7e4249b9
LP
1714static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage *message, void *data) {
1715 DBusError error;
1716 DBusMessage *m = NULL, *reply = NULL;
1717
1718 assert(connection);
1719 assert(message);
1720
1721 dbus_error_init(&error);
1722
1723 /* log_debug("Got D-Bus request: %s.%s() on %s", */
1724 /* dbus_message_get_interface(message), */
1725 /* dbus_message_get_member(message), */
1726 /* dbus_message_get_path(message)); */
1727
1728 if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
1729 log_error("Warning! D-Bus connection terminated.");
1730 dbus_connection_close(connection);
1731
1732 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitNew") ||
1733 dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
1734 const char *id, *path;
1735
1736 if (!dbus_message_get_args(message, &error,
1737 DBUS_TYPE_STRING, &id,
1738 DBUS_TYPE_OBJECT_PATH, &path,
1739 DBUS_TYPE_INVALID))
1740 log_error("Failed to parse message: %s", error.message);
1741 else if (streq(dbus_message_get_member(message), "UnitNew"))
1742 printf("Unit %s added.\n", id);
1743 else
1744 printf("Unit %s removed.\n", id);
1745
1746 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobNew") ||
1747 dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
1748 uint32_t id;
1749 const char *path;
1750
1751 if (!dbus_message_get_args(message, &error,
1752 DBUS_TYPE_UINT32, &id,
1753 DBUS_TYPE_OBJECT_PATH, &path,
1754 DBUS_TYPE_INVALID))
1755 log_error("Failed to parse message: %s", error.message);
1756 else if (streq(dbus_message_get_member(message), "JobNew"))
1757 printf("Job %u added.\n", id);
1758 else
1759 printf("Job %u removed.\n", id);
1760
1761
1762 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Unit", "Changed") ||
1763 dbus_message_is_signal(message, "org.freedesktop.systemd1.Job", "Changed")) {
1764
1765 const char *path, *interface, *property = "Id";
1766 DBusMessageIter iter, sub;
1767
1768 path = dbus_message_get_path(message);
1769 interface = dbus_message_get_interface(message);
1770
1771 if (!(m = dbus_message_new_method_call(
1772 "org.freedesktop.systemd1",
1773 path,
1774 "org.freedesktop.DBus.Properties",
1775 "Get"))) {
1776 log_error("Could not allocate message.");
1777 goto oom;
1778 }
1779
1780 if (!dbus_message_append_args(m,
1781 DBUS_TYPE_STRING, &interface,
1782 DBUS_TYPE_STRING, &property,
1783 DBUS_TYPE_INVALID)) {
1784 log_error("Could not append arguments to message.");
1785 goto finish;
1786 }
1787
1788 if (!(reply = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
1789 log_error("Failed to issue method call: %s", error.message);
1790 goto finish;
1791 }
1792
1793 if (!dbus_message_iter_init(reply, &iter) ||
1794 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
1795 log_error("Failed to parse reply.");
1796 goto finish;
1797 }
1798
1799 dbus_message_iter_recurse(&iter, &sub);
1800
1801 if (streq(interface, "org.freedesktop.systemd1.Unit")) {
1802 const char *id;
1803
1804 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
1805 log_error("Failed to parse reply.");
1806 goto finish;
1807 }
1808
1809 dbus_message_iter_get_basic(&sub, &id);
1810 printf("Unit %s changed.\n", id);
1811 } else {
1812 uint32_t id;
1813
1814 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT32) {
1815 log_error("Failed to parse reply.");
1816 goto finish;
1817 }
1818
1819 dbus_message_iter_get_basic(&sub, &id);
1820 printf("Job %u changed.\n", id);
1821 }
1822 }
1823
1824finish:
1825 if (m)
1826 dbus_message_unref(m);
1827
1828 if (reply)
1829 dbus_message_unref(reply);
1830
1831 dbus_error_free(&error);
1832 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1833
1834oom:
1835 if (m)
1836 dbus_message_unref(m);
1837
1838 if (reply)
1839 dbus_message_unref(reply);
1840
1841 dbus_error_free(&error);
1842 return DBUS_HANDLER_RESULT_NEED_MEMORY;
1843}
1844
1845static int monitor(DBusConnection *bus, char **args, unsigned n) {
1846 DBusMessage *m = NULL, *reply = NULL;
1847 DBusError error;
1848 int r;
1849
1850 dbus_error_init(&error);
1851
1852 dbus_bus_add_match(bus,
1853 "type='signal',"
1854 "sender='org.freedesktop.systemd1',"
1855 "interface='org.freedesktop.systemd1.Manager',"
1856 "path='/org/freedesktop/systemd1'",
1857 &error);
1858
1859 if (dbus_error_is_set(&error)) {
1860 log_error("Failed to add match: %s", error.message);
1861 r = -EIO;
1862 goto finish;
1863 }
1864
1865 dbus_bus_add_match(bus,
1866 "type='signal',"
1867 "sender='org.freedesktop.systemd1',"
1868 "interface='org.freedesktop.systemd1.Unit',"
1869 "member='Changed'",
1870 &error);
1871
1872 if (dbus_error_is_set(&error)) {
1873 log_error("Failed to add match: %s", error.message);
1874 r = -EIO;
1875 goto finish;
1876 }
1877
1878 dbus_bus_add_match(bus,
1879 "type='signal',"
1880 "sender='org.freedesktop.systemd1',"
1881 "interface='org.freedesktop.systemd1.Job',"
1882 "member='Changed'",
1883 &error);
1884
1885 if (dbus_error_is_set(&error)) {
1886 log_error("Failed to add match: %s", error.message);
1887 r = -EIO;
1888 goto finish;
1889 }
1890
1891 if (!dbus_connection_add_filter(bus, monitor_filter, NULL, NULL)) {
1892 log_error("Failed to add filter.");
1893 r = -ENOMEM;
1894 goto finish;
1895 }
1896
1897 if (!(m = dbus_message_new_method_call(
1898 "org.freedesktop.systemd1",
1899 "/org/freedesktop/systemd1",
1900 "org.freedesktop.systemd1.Manager",
1901 "Subscribe"))) {
1902 log_error("Could not allocate message.");
1903 r = -ENOMEM;
1904 goto finish;
1905 }
1906
1907 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1908 log_error("Failed to issue method call: %s", error.message);
1909 r = -EIO;
1910 goto finish;
1911 }
1912
1913 while (dbus_connection_read_write_dispatch(bus, -1))
1914 ;
1915
1916 r = 0;
1917
1918finish:
1919
1920 /* This is slightly dirty, since we don't undo the filter or the matches. */
1921
1922 if (m)
1923 dbus_message_unref(m);
1924
1925 if (reply)
1926 dbus_message_unref(reply);
1927
1928 dbus_error_free(&error);
1929
1930 return r;
1931}
1932
1933static int dump(DBusConnection *bus, char **args, unsigned n) {
1934 DBusMessage *m = NULL, *reply = NULL;
1935 DBusError error;
1936 int r;
1937 const char *text;
1938
1939 dbus_error_init(&error);
1940
1941 if (!(m = dbus_message_new_method_call(
1942 "org.freedesktop.systemd1",
1943 "/org/freedesktop/systemd1",
1944 "org.freedesktop.systemd1.Manager",
1945 "Dump"))) {
1946 log_error("Could not allocate message.");
1947 return -ENOMEM;
1948 }
1949
1950 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1951 log_error("Failed to issue method call: %s", error.message);
1952 r = -EIO;
1953 goto finish;
1954 }
1955
1956 if (!dbus_message_get_args(reply, &error,
1957 DBUS_TYPE_STRING, &text,
1958 DBUS_TYPE_INVALID)) {
1959 log_error("Failed to parse reply: %s", error.message);
1960 r = -EIO;
1961 goto finish;
1962 }
1963
1964 fputs(text, stdout);
1965
1966 r = 0;
1967
1968finish:
1969 if (m)
1970 dbus_message_unref(m);
1971
1972 if (reply)
1973 dbus_message_unref(reply);
1974
1975 dbus_error_free(&error);
1976
1977 return r;
1978}
1979
1980static int snapshot(DBusConnection *bus, char **args, unsigned n) {
1981 DBusMessage *m = NULL, *reply = NULL;
1982 DBusError error;
1983 int r;
1984 const char *name = "", *path, *id;
1985 dbus_bool_t cleanup = FALSE;
1986 DBusMessageIter iter, sub;
1987 const char
1988 *interface = "org.freedesktop.systemd1.Unit",
1989 *property = "Id";
1990
1991 dbus_error_init(&error);
1992
1993 if (!(m = dbus_message_new_method_call(
1994 "org.freedesktop.systemd1",
1995 "/org/freedesktop/systemd1",
1996 "org.freedesktop.systemd1.Manager",
1997 "CreateSnapshot"))) {
1998 log_error("Could not allocate message.");
1999 return -ENOMEM;
2000 }
2001
2002 if (n > 1)
2003 name = args[1];
2004
2005 if (!dbus_message_append_args(m,
2006 DBUS_TYPE_STRING, &name,
2007 DBUS_TYPE_BOOLEAN, &cleanup,
2008 DBUS_TYPE_INVALID)) {
2009 log_error("Could not append arguments to message.");
2010 r = -ENOMEM;
2011 goto finish;
2012 }
2013
2014 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2015 log_error("Failed to issue method call: %s", error.message);
2016 r = -EIO;
2017 goto finish;
2018 }
2019
2020 if (!dbus_message_get_args(reply, &error,
2021 DBUS_TYPE_OBJECT_PATH, &path,
2022 DBUS_TYPE_INVALID)) {
2023 log_error("Failed to parse reply: %s", error.message);
2024 r = -EIO;
2025 goto finish;
2026 }
2027
2028 dbus_message_unref(m);
2029 if (!(m = dbus_message_new_method_call(
2030 "org.freedesktop.systemd1",
2031 path,
2032 "org.freedesktop.DBus.Properties",
2033 "Get"))) {
2034 log_error("Could not allocate message.");
2035 return -ENOMEM;
2036 }
2037
2038 if (!dbus_message_append_args(m,
2039 DBUS_TYPE_STRING, &interface,
2040 DBUS_TYPE_STRING, &property,
2041 DBUS_TYPE_INVALID)) {
2042 log_error("Could not append arguments to message.");
2043 r = -ENOMEM;
2044 goto finish;
2045 }
2046
2047 dbus_message_unref(reply);
2048 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2049 log_error("Failed to issue method call: %s", error.message);
2050 r = -EIO;
2051 goto finish;
2052 }
2053
2054 if (!dbus_message_iter_init(reply, &iter) ||
2055 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
2056 log_error("Failed to parse reply.");
2057 r = -EIO;
2058 goto finish;
2059 }
2060
2061 dbus_message_iter_recurse(&iter, &sub);
2062
2063 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
2064 log_error("Failed to parse reply.");
2065 r = -EIO;
2066 goto finish;
2067 }
2068
2069 dbus_message_iter_get_basic(&sub, &id);
0183528f
LP
2070
2071 if (!arg_quiet)
2072 puts(id);
7e4249b9
LP
2073 r = 0;
2074
2075finish:
2076 if (m)
2077 dbus_message_unref(m);
2078
2079 if (reply)
2080 dbus_message_unref(reply);
2081
2082 dbus_error_free(&error);
2083
2084 return r;
2085}
2086
6759e7a7
LP
2087static int delete_snapshot(DBusConnection *bus, char **args, unsigned n) {
2088 DBusMessage *m = NULL, *reply = NULL;
2089 int r;
2090 DBusError error;
2091 unsigned i;
2092
2093 assert(bus);
2094 assert(args);
2095
2096 dbus_error_init(&error);
2097
2098 for (i = 1; i < n; i++) {
2099 const char *path = NULL;
2100
2101 if (!(m = dbus_message_new_method_call(
2102 "org.freedesktop.systemd1",
2103 "/org/freedesktop/systemd1",
2104 "org.freedesktop.systemd1.Manager",
2105 "GetUnit"))) {
2106 log_error("Could not allocate message.");
2107 r = -ENOMEM;
2108 goto finish;
2109 }
2110
2111 if (!dbus_message_append_args(m,
2112 DBUS_TYPE_STRING, &args[i],
2113 DBUS_TYPE_INVALID)) {
2114 log_error("Could not append arguments to message.");
2115 r = -ENOMEM;
2116 goto finish;
2117 }
2118
2119 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2120 log_error("Failed to issue method call: %s", error.message);
2121 r = -EIO;
2122 goto finish;
2123 }
2124
2125 if (!dbus_message_get_args(reply, &error,
2126 DBUS_TYPE_OBJECT_PATH, &path,
2127 DBUS_TYPE_INVALID)) {
2128 log_error("Failed to parse reply: %s", error.message);
2129 r = -EIO;
2130 goto finish;
2131 }
2132
2133 dbus_message_unref(m);
2134 if (!(m = dbus_message_new_method_call(
2135 "org.freedesktop.systemd1",
2136 path,
2137 "org.freedesktop.systemd1.Snapshot",
2138 "Remove"))) {
2139 log_error("Could not allocate message.");
2140 r = -ENOMEM;
2141 goto finish;
2142 }
2143
2144 dbus_message_unref(reply);
2145 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2146 log_error("Failed to issue method call: %s", error.message);
2147 r = -EIO;
2148 goto finish;
2149 }
2150
2151 dbus_message_unref(m);
2152 dbus_message_unref(reply);
2153 m = reply = NULL;
2154 }
2155
2156 r = 0;
2157
2158finish:
2159 if (m)
2160 dbus_message_unref(m);
2161
2162 if (reply)
2163 dbus_message_unref(reply);
2164
2165 dbus_error_free(&error);
2166
2167 return r;
2168}
2169
7e4249b9
LP
2170static int clear_jobs(DBusConnection *bus, char **args, unsigned n) {
2171 DBusMessage *m = NULL, *reply = NULL;
2172 DBusError error;
2173 int r;
2174 const char *method;
2175
2176 dbus_error_init(&error);
2177
e4b61340
LP
2178 if (arg_action == ACTION_RELOAD)
2179 method = "Reload";
2180 else if (arg_action == ACTION_REEXEC)
2181 method = "Reexecute";
2182 else {
2183 assert(arg_action == ACTION_SYSTEMCTL);
2184
2185 method =
2186 streq(args[0], "clear-jobs") ? "ClearJobs" :
2187 streq(args[0], "daemon-reload") ? "Reload" :
2188 streq(args[0], "daemon-reexec") ? "Reexecute" :
2189 "Exit";
2190 }
7e4249b9
LP
2191
2192 if (!(m = dbus_message_new_method_call(
2193 "org.freedesktop.systemd1",
2194 "/org/freedesktop/systemd1",
2195 "org.freedesktop.systemd1.Manager",
2196 method))) {
2197 log_error("Could not allocate message.");
2198 return -ENOMEM;
2199 }
2200
2201 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
e4b61340
LP
2202
2203 if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) {
2204 /* There's always a fallback possible for
2205 * legacy actions. */
2206 r = 0;
2207 goto finish;
2208 }
2209
7e4249b9
LP
2210 log_error("Failed to issue method call: %s", error.message);
2211 r = -EIO;
2212 goto finish;
2213 }
2214
e4b61340 2215 r = 1;
7e4249b9
LP
2216
2217finish:
2218 if (m)
2219 dbus_message_unref(m);
2220
2221 if (reply)
2222 dbus_message_unref(reply);
2223
2224 dbus_error_free(&error);
2225
2226 return r;
2227}
2228
2229static int show_enviroment(DBusConnection *bus, char **args, unsigned n) {
2230 DBusMessage *m = NULL, *reply = NULL;
2231 DBusError error;
2232 DBusMessageIter iter, sub, sub2;
2233 int r;
2234 const char
2235 *interface = "org.freedesktop.systemd1.Manager",
2236 *property = "Environment";
2237
2238 dbus_error_init(&error);
2239
2240 if (!(m = dbus_message_new_method_call(
2241 "org.freedesktop.systemd1",
2242 "/org/freedesktop/systemd1",
2243 "org.freedesktop.DBus.Properties",
2244 "Get"))) {
2245 log_error("Could not allocate message.");
2246 return -ENOMEM;
2247 }
2248
2249 if (!dbus_message_append_args(m,
2250 DBUS_TYPE_STRING, &interface,
2251 DBUS_TYPE_STRING, &property,
2252 DBUS_TYPE_INVALID)) {
2253 log_error("Could not append arguments to message.");
2254 r = -ENOMEM;
2255 goto finish;
2256 }
2257
2258 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2259 log_error("Failed to issue method call: %s", error.message);
2260 r = -EIO;
2261 goto finish;
2262 }
2263
2264 if (!dbus_message_iter_init(reply, &iter) ||
2265 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
2266 log_error("Failed to parse reply.");
2267 r = -EIO;
2268 goto finish;
2269 }
2270
2271 dbus_message_iter_recurse(&iter, &sub);
2272
2273 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY ||
2274 dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_STRING) {
2275 log_error("Failed to parse reply.");
2276 r = -EIO;
2277 goto finish;
2278 }
2279
2280 dbus_message_iter_recurse(&sub, &sub2);
2281
2282 while (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_INVALID) {
2283 const char *text;
2284
2285 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) {
2286 log_error("Failed to parse reply.");
2287 r = -EIO;
2288 goto finish;
2289 }
2290
2291 dbus_message_iter_get_basic(&sub2, &text);
2292 printf("%s\n", text);
2293
2294 dbus_message_iter_next(&sub2);
2295 }
2296
2297 r = 0;
2298
2299finish:
2300 if (m)
2301 dbus_message_unref(m);
2302
2303 if (reply)
2304 dbus_message_unref(reply);
2305
2306 dbus_error_free(&error);
2307
2308 return r;
2309}
2310
2311static int set_environment(DBusConnection *bus, char **args, unsigned n) {
2312 DBusMessage *m = NULL, *reply = NULL;
2313 DBusError error;
2314 int r;
2315 const char *method;
2316 DBusMessageIter iter, sub;
2317 unsigned i;
2318
2319 dbus_error_init(&error);
2320
2321 method = streq(args[0], "set-environment")
2322 ? "SetEnvironment"
2323 : "UnsetEnvironment";
2324
2325 if (!(m = dbus_message_new_method_call(
2326 "org.freedesktop.systemd1",
2327 "/org/freedesktop/systemd1",
2328 "org.freedesktop.systemd1.Manager",
2329 method))) {
2330
2331 log_error("Could not allocate message.");
2332 return -ENOMEM;
2333 }
2334
2335 dbus_message_iter_init_append(m, &iter);
2336
2337 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
2338 log_error("Could not append arguments to message.");
2339 r = -ENOMEM;
2340 goto finish;
2341 }
2342
2343 for (i = 1; i < n; i++)
2344 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &args[i])) {
2345 log_error("Could not append arguments to message.");
2346 r = -ENOMEM;
2347 goto finish;
2348 }
2349
2350 if (!dbus_message_iter_close_container(&iter, &sub)) {
2351 log_error("Could not append arguments to message.");
2352 r = -ENOMEM;
2353 goto finish;
2354 }
2355
2356 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2357 log_error("Failed to issue method call: %s", error.message);
2358 r = -EIO;
2359 goto finish;
2360 }
2361
2362 r = 0;
2363
2364finish:
2365 if (m)
2366 dbus_message_unref(m);
2367
2368 if (reply)
2369 dbus_message_unref(reply);
2370
2371 dbus_error_free(&error);
2372
2373 return r;
2374}
2375
e4b61340 2376static int systemctl_help(void) {
7e4249b9 2377
2e33c433 2378 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
5ec7ed4e 2379 "Send control commands to the systemd manager.\n\n"
48220598
LP
2380 " -h --help Show this help\n"
2381 " -t --type=TYPE List only units of a particular type\n"
393a2f9b 2382 " -p --property=NAME Show only properties by this name\n"
48220598
LP
2383 " -a --all Show all units/properties, including dead/empty ones\n"
2384 " --replace When installing a new job, replace existing conflicting ones\n"
2385 " --system Connect to system bus\n"
2386 " --session Connect to session bus\n"
2387 " -q --quiet Suppress output\n"
2388 " --no-block Do not wait until operation finished\n"
2389 " --no-wall Don't send wall message before halt/power-off/reboot\n\n"
7e4249b9
LP
2390 "Commands:\n"
2391 " list-units List units\n"
7e4249b9
LP
2392 " start [NAME...] Start one or more units\n"
2393 " stop [NAME...] Stop one or more units\n"
2394 " restart [NAME...] Restart one or more units\n"
2395 " reload [NAME...] Reload one or more units\n"
2396 " isolate [NAME] Start one unit and stop all others\n"
0183528f 2397 " check [NAME...] Check whether any of the passed units are active\n"
61cbdc4b
LP
2398 " status [NAME...] Show status of one or more units\n"
2399 " show [NAME...|JOB...] Show properties of one or more units/jobs/manager\n"
48220598
LP
2400 " load [NAME...] Load one or more units\n"
2401 " list-jobs List jobs\n"
2402 " cancel [JOB...] Cancel one or more jobs\n"
2403 " clear-jobs Cancel all jobs\n"
7e4249b9
LP
2404 " monitor Monitor unit/job changes\n"
2405 " dump Dump server status\n"
2406 " snapshot [NAME] Create a snapshot\n"
6759e7a7 2407 " delete [NAME...] Remove one or more snapshots\n"
5ec7ed4e
LP
2408 " daemon-reload Reload systemd manager configuration\n"
2409 " daemon-reexec Reexecute systemd manager\n"
2410 " daemon-exit Ask the systemd manager to quit\n"
7e4249b9
LP
2411 " show-environment Dump environment\n"
2412 " set-environment [NAME=VALUE...] Set one or more environment variables\n"
514f4ef5
LP
2413 " unset-environment [NAME...] Unset one or more environment variables\n"
2414 " halt Shut down and halt the system\n"
2e33c433 2415 " poweroff Shut down and power-off the system\n"
514f4ef5 2416 " reboot Shut down and reboot the system\n"
514f4ef5
LP
2417 " default Enter default mode\n"
2418 " rescue Enter rescue mode\n"
2419 " emergency Enter emergency mode\n",
5b6319dc 2420 program_invocation_short_name);
7e4249b9
LP
2421
2422 return 0;
2423}
2424
e4b61340
LP
2425static int halt_help(void) {
2426
2e33c433 2427 printf("%s [OPTIONS...]\n\n"
e4b61340
LP
2428 "%s the system.\n\n"
2429 " --help Show this help\n"
2430 " --halt Halt the machine\n"
2431 " -p --poweroff Switch off the machine\n"
2432 " --reboot Reboot the machine\n"
2e33c433
LP
2433 " -f --force Force immediate halt/power-off/reboot\n"
2434 " -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
e4b61340 2435 " -d --no-wtmp Don't write wtmp record\n"
2e33c433
LP
2436 " -n --no-sync Don't sync before halt/power-off/reboot\n"
2437 " --no-wall Don't send wall message before halt/power-off/reboot\n",
e4b61340
LP
2438 program_invocation_short_name,
2439 arg_action == ACTION_REBOOT ? "Reboot" :
2440 arg_action == ACTION_POWEROFF ? "Power off" :
2441 "Halt");
2442
2443 return 0;
2444}
2445
2446static int shutdown_help(void) {
2447
2e33c433 2448 printf("%s [OPTIONS...] [now] [WALL...]\n\n"
e4b61340
LP
2449 "Shut down the system.\n\n"
2450 " --help Show this help\n"
2451 " -H --halt Halt the machine\n"
2452 " -P --poweroff Power-off the machine\n"
2453 " -r --reboot Reboot the machine\n"
2454 " -h Equivalent to --poweroff, overriden by --halt\n"
2e33c433
LP
2455 " -k Don't halt/power-off/reboot, just send warnings\n"
2456 " --no-wall Don't send wall message before halt/power-off/reboot\n",
e4b61340
LP
2457 program_invocation_short_name);
2458
2459 return 0;
2460}
2461
2462static int telinit_help(void) {
2463
2e33c433 2464 printf("%s [OPTIONS...] {COMMAND}\n\n"
514f4ef5
LP
2465 "Send control commands to the init daemon.\n\n"
2466 " --help Show this help\n"
2e33c433 2467 " --no-wall Don't send wall message before halt/power-off/reboot\n\n"
e4b61340
LP
2468 "Commands:\n"
2469 " 0 Power-off the machine\n"
2470 " 6 Reboot the machine\n"
514f4ef5
LP
2471 " 2, 3, 4, 5 Start runlevelX.target unit\n"
2472 " 1, s, S Enter rescue mode\n"
2473 " q, Q Reload init daemon configuration\n"
2474 " u, U Reexecute init daemon\n",
e4b61340
LP
2475 program_invocation_short_name);
2476
2477 return 0;
2478}
2479
2480static int runlevel_help(void) {
2481
2e33c433 2482 printf("%s [OPTIONS...]\n\n"
e4b61340
LP
2483 "Prints the previous and current runlevel of the init system.\n\n"
2484 " --help Show this help\n",
2485 program_invocation_short_name);
2486
2487 return 0;
2488}
2489
2490static int systemctl_parse_argv(int argc, char *argv[]) {
7e4249b9
LP
2491
2492 enum {
2493 ARG_REPLACE = 0x100,
2494 ARG_SESSION,
2495 ARG_SYSTEM,
6e905d93 2496 ARG_NO_BLOCK,
514f4ef5 2497 ARG_NO_WALL
7e4249b9
LP
2498 };
2499
2500 static const struct option options[] = {
6e905d93
LP
2501 { "help", no_argument, NULL, 'h' },
2502 { "type", required_argument, NULL, 't' },
48220598 2503 { "property", required_argument, NULL, 'p' },
6e905d93
LP
2504 { "all", no_argument, NULL, 'a' },
2505 { "replace", no_argument, NULL, ARG_REPLACE },
2506 { "session", no_argument, NULL, ARG_SESSION },
2507 { "system", no_argument, NULL, ARG_SYSTEM },
2508 { "no-block", no_argument, NULL, ARG_NO_BLOCK },
2509 { "no-wall", no_argument, NULL, ARG_NO_WALL },
0183528f 2510 { "quiet", no_argument, NULL, 'q' },
6e905d93 2511 { NULL, 0, NULL, 0 }
7e4249b9
LP
2512 };
2513
2514 int c;
2515
e4b61340 2516 assert(argc >= 0);
7e4249b9
LP
2517 assert(argv);
2518
48220598 2519 while ((c = getopt_long(argc, argv, "ht:p:aq", options, NULL)) >= 0) {
7e4249b9
LP
2520
2521 switch (c) {
2522
2523 case 'h':
e4b61340 2524 systemctl_help();
7e4249b9
LP
2525 return 0;
2526
2527 case 't':
2528 arg_type = optarg;
2529 break;
2530
48220598
LP
2531 case 'p':
2532 arg_property = optarg;
2533
2534 /* If the user asked for a particular
2535 * property, show it to him, even if it is
2536 * empty. */
2537 arg_all = true;
2538 break;
2539
7e4249b9
LP
2540 case 'a':
2541 arg_all = true;
2542 break;
2543
2544 case ARG_REPLACE:
2545 arg_replace = true;
2546 break;
2547
2548 case ARG_SESSION:
2549 arg_session = true;
2550 break;
2551
2552 case ARG_SYSTEM:
2553 arg_session = false;
2554 break;
2555
6e905d93
LP
2556 case ARG_NO_BLOCK:
2557 arg_no_block = true;
7e4249b9
LP
2558 break;
2559
514f4ef5
LP
2560 case ARG_NO_WALL:
2561 arg_no_wall = true;
2562 break;
2563
0183528f
LP
2564 case 'q':
2565 arg_quiet = true;
2566 break;
2567
7e4249b9
LP
2568 case '?':
2569 return -EINVAL;
2570
2571 default:
2572 log_error("Unknown option code %c", c);
2573 return -EINVAL;
2574 }
2575 }
2576
2577 return 1;
2578}
2579
e4b61340
LP
2580static int halt_parse_argv(int argc, char *argv[]) {
2581
2582 enum {
2583 ARG_HELP = 0x100,
2584 ARG_HALT,
514f4ef5
LP
2585 ARG_REBOOT,
2586 ARG_NO_WALL
e4b61340
LP
2587 };
2588
2589 static const struct option options[] = {
2590 { "help", no_argument, NULL, ARG_HELP },
2591 { "halt", no_argument, NULL, ARG_HALT },
2592 { "poweroff", no_argument, NULL, 'p' },
2593 { "reboot", no_argument, NULL, ARG_REBOOT },
2594 { "force", no_argument, NULL, 'f' },
2595 { "wtmp-only", no_argument, NULL, 'w' },
2596 { "no-wtmp", no_argument, NULL, 'd' },
2597 { "no-sync", no_argument, NULL, 'n' },
514f4ef5 2598 { "no-wall", no_argument, NULL, ARG_NO_WALL },
e4b61340
LP
2599 { NULL, 0, NULL, 0 }
2600 };
2601
2602 int c, runlevel;
2603
2604 assert(argc >= 0);
2605 assert(argv);
2606
2607 if (utmp_get_runlevel(&runlevel, NULL) >= 0)
2608 if (runlevel == '0' || runlevel == '6')
2609 arg_immediate = true;
2610
2611 while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) {
2612 switch (c) {
2613
2614 case ARG_HELP:
2615 halt_help();
2616 return 0;
2617
2618 case ARG_HALT:
2619 arg_action = ACTION_HALT;
2620 break;
2621
2622 case 'p':
2623 arg_action = ACTION_POWEROFF;
2624 break;
2625
2626 case ARG_REBOOT:
2627 arg_action = ACTION_REBOOT;
2628 break;
2629
2630 case 'f':
2631 arg_immediate = true;
2632 break;
2633
2634 case 'w':
2635 arg_dry = true;
2636 break;
2637
2638 case 'd':
2639 arg_no_wtmp = true;
2640 break;
2641
2642 case 'n':
2643 arg_no_sync = true;
2644 break;
2645
514f4ef5
LP
2646 case ARG_NO_WALL:
2647 arg_no_wall = true;
2648 break;
2649
e4b61340
LP
2650 case 'i':
2651 case 'h':
2652 /* Compatibility nops */
2653 break;
2654
2655 case '?':
2656 return -EINVAL;
2657
2658 default:
2659 log_error("Unknown option code %c", c);
2660 return -EINVAL;
2661 }
2662 }
2663
2664 if (optind < argc) {
2665 log_error("Too many arguments.");
2666 return -EINVAL;
2667 }
2668
2669 return 1;
2670}
2671
2672static int shutdown_parse_argv(int argc, char *argv[]) {
2673
2674 enum {
2675 ARG_HELP = 0x100,
514f4ef5 2676 ARG_NO_WALL
e4b61340
LP
2677 };
2678
2679 static const struct option options[] = {
2680 { "help", no_argument, NULL, ARG_HELP },
2681 { "halt", no_argument, NULL, 'H' },
2682 { "poweroff", no_argument, NULL, 'P' },
2683 { "reboot", no_argument, NULL, 'r' },
514f4ef5 2684 { "no-wall", no_argument, NULL, ARG_NO_WALL },
e4b61340
LP
2685 { NULL, 0, NULL, 0 }
2686 };
2687
2688 int c;
2689
2690 assert(argc >= 0);
2691 assert(argv);
2692
2693 while ((c = getopt_long(argc, argv, "HPrhkt:a", options, NULL)) >= 0) {
2694 switch (c) {
2695
2696 case ARG_HELP:
2697 shutdown_help();
2698 return 0;
2699
2700 case 'H':
2701 arg_action = ACTION_HALT;
2702 break;
2703
2704 case 'P':
2705 arg_action = ACTION_POWEROFF;
2706 break;
2707
2708 case 'r':
2709 arg_action = ACTION_REBOOT;
2710 break;
2711
2712 case 'h':
2713 if (arg_action != ACTION_HALT)
2714 arg_action = ACTION_POWEROFF;
2715 break;
2716
2717 case 'k':
2718 arg_dry = true;
2719 break;
2720
514f4ef5
LP
2721 case ARG_NO_WALL:
2722 arg_no_wall = true;
2723 break;
2724
e4b61340
LP
2725 case 't':
2726 case 'a':
2727 /* Compatibility nops */
2728 break;
2729
2730 case '?':
2731 return -EINVAL;
2732
2733 default:
2734 log_error("Unknown option code %c", c);
2735 return -EINVAL;
2736 }
2737 }
2738
4545812f
LP
2739 if (argc > optind && !streq(argv[optind], "now"))
2740 log_warning("First argument '%s' isn't 'now'. Ignoring.", argv[optind]);
442b9094 2741
e4b61340
LP
2742 /* We ignore the time argument */
2743 if (argc > optind + 1)
2744 arg_wall = argv + optind + 1;
2745
2746 optind = argc;
2747
2748 return 1;
e4b61340
LP
2749}
2750
2751static int telinit_parse_argv(int argc, char *argv[]) {
2752
2753 enum {
2754 ARG_HELP = 0x100,
514f4ef5 2755 ARG_NO_WALL
e4b61340
LP
2756 };
2757
2758 static const struct option options[] = {
2759 { "help", no_argument, NULL, ARG_HELP },
514f4ef5 2760 { "no-wall", no_argument, NULL, ARG_NO_WALL },
e4b61340
LP
2761 { NULL, 0, NULL, 0 }
2762 };
2763
2764 static const struct {
2765 char from;
2766 enum action to;
2767 } table[] = {
2768 { '0', ACTION_POWEROFF },
2769 { '6', ACTION_REBOOT },
ef2f1067 2770 { '1', ACTION_RESCUE },
e4b61340
LP
2771 { '2', ACTION_RUNLEVEL2 },
2772 { '3', ACTION_RUNLEVEL3 },
2773 { '4', ACTION_RUNLEVEL4 },
2774 { '5', ACTION_RUNLEVEL5 },
2775 { 's', ACTION_RESCUE },
2776 { 'S', ACTION_RESCUE },
2777 { 'q', ACTION_RELOAD },
2778 { 'Q', ACTION_RELOAD },
2779 { 'u', ACTION_REEXEC },
2780 { 'U', ACTION_REEXEC }
2781 };
2782
2783 unsigned i;
2784 int c;
2785
2786 assert(argc >= 0);
2787 assert(argv);
2788
2789 while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
2790 switch (c) {
2791
2792 case ARG_HELP:
2793 telinit_help();
2794 return 0;
2795
514f4ef5
LP
2796 case ARG_NO_WALL:
2797 arg_no_wall = true;
2798 break;
2799
e4b61340
LP
2800 case '?':
2801 return -EINVAL;
2802
2803 default:
2804 log_error("Unknown option code %c", c);
2805 return -EINVAL;
2806 }
2807 }
2808
2809 if (optind >= argc) {
2f02ce40 2810 telinit_help();
e4b61340
LP
2811 return -EINVAL;
2812 }
2813
2814 if (optind + 1 < argc) {
2815 log_error("Too many arguments.");
2816 return -EINVAL;
2817 }
2818
2819 if (strlen(argv[optind]) != 1) {
2820 log_error("Expected single character argument.");
2821 return -EINVAL;
2822 }
2823
2824 for (i = 0; i < ELEMENTSOF(table); i++)
2825 if (table[i].from == argv[optind][0])
2826 break;
2827
2828 if (i >= ELEMENTSOF(table)) {
2829 log_error("Unknown command %s.", argv[optind]);
2830 return -EINVAL;
2831 }
2832
2833 arg_action = table[i].to;
2834
2835 optind ++;
2836
2837 return 1;
2838}
2839
2840static int runlevel_parse_argv(int argc, char *argv[]) {
2841
2842 enum {
2843 ARG_HELP = 0x100,
2844 };
2845
2846 static const struct option options[] = {
2847 { "help", no_argument, NULL, ARG_HELP },
2848 { NULL, 0, NULL, 0 }
2849 };
2850
2851 int c;
2852
2853 assert(argc >= 0);
2854 assert(argv);
2855
2856 while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
2857 switch (c) {
2858
2859 case ARG_HELP:
2860 runlevel_help();
2861 return 0;
2862
2863 case '?':
2864 return -EINVAL;
2865
2866 default:
2867 log_error("Unknown option code %c", c);
2868 return -EINVAL;
2869 }
2870 }
2871
2872 if (optind < argc) {
2873 log_error("Too many arguments.");
2874 return -EINVAL;
2875 }
2876
2877 return 1;
2878}
2879
2880static int parse_argv(int argc, char *argv[]) {
2881 assert(argc >= 0);
2882 assert(argv);
2883
2884 if (program_invocation_short_name) {
2885
2886 if (strstr(program_invocation_short_name, "halt")) {
2887 arg_action = ACTION_HALT;
2888 return halt_parse_argv(argc, argv);
2889 } else if (strstr(program_invocation_short_name, "poweroff")) {
2890 arg_action = ACTION_POWEROFF;
2891 return halt_parse_argv(argc, argv);
2892 } else if (strstr(program_invocation_short_name, "reboot")) {
2893 arg_action = ACTION_REBOOT;
2894 return halt_parse_argv(argc, argv);
2895 } else if (strstr(program_invocation_short_name, "shutdown")) {
2896 arg_action = ACTION_POWEROFF;
2897 return shutdown_parse_argv(argc, argv);
2898 } else if (strstr(program_invocation_short_name, "init")) {
2899 arg_action = ACTION_INVALID;
2900 return telinit_parse_argv(argc, argv);
2901 } else if (strstr(program_invocation_short_name, "runlevel")) {
2902 arg_action = ACTION_RUNLEVEL;
2903 return runlevel_parse_argv(argc, argv);
2904 }
2905 }
2906
2907 arg_action = ACTION_SYSTEMCTL;
2908 return systemctl_parse_argv(argc, argv);
2909}
2910
d55ae9e6 2911static int action_to_runlevel(void) {
eb22ac37
LP
2912
2913 static const char table[_ACTION_MAX] = {
2914 [ACTION_HALT] = '0',
2915 [ACTION_POWEROFF] = '0',
2916 [ACTION_REBOOT] = '6',
2917 [ACTION_RUNLEVEL2] = '2',
2918 [ACTION_RUNLEVEL3] = '3',
2919 [ACTION_RUNLEVEL4] = '4',
2920 [ACTION_RUNLEVEL5] = '5',
2921 [ACTION_RESCUE] = '1'
2922 };
2923
d55ae9e6
LP
2924 assert(arg_action < _ACTION_MAX);
2925
2926 return table[arg_action];
2927}
2928
f1c5860b 2929static int talk_upstart(void) {
d55ae9e6
LP
2930 DBusMessage *m = NULL, *reply = NULL;
2931 DBusError error;
2932 int previous, rl, r;
2933 char
2934 env1_buf[] = "RUNLEVEL=X",
2935 env2_buf[] = "PREVLEVEL=X";
2936 char *env1 = env1_buf, *env2 = env2_buf;
2937 const char *emit = "runlevel";
2938 dbus_bool_t b_false = FALSE;
2939 DBusMessageIter iter, sub;
f1c5860b 2940 DBusConnection *bus;
d55ae9e6
LP
2941
2942 dbus_error_init(&error);
2943
2944 if (!(rl = action_to_runlevel()))
2945 return 0;
2946
2947 if (utmp_get_runlevel(&previous, NULL) < 0)
2948 previous = 'N';
2949
f1c5860b
LP
2950 if (!(bus = dbus_connection_open("unix:abstract=/com/ubuntu/upstart", &error))) {
2951 if (dbus_error_has_name(&error, DBUS_ERROR_NO_SERVER)) {
2952 r = 0;
2953 goto finish;
2954 }
2955
2956 log_error("Failed to connect to Upstart bus: %s", error.message);
2957 r = -EIO;
2958 goto finish;
2959 }
2960
2961 if ((r = bus_check_peercred(bus)) < 0) {
2962 log_error("Failed to verify owner of bus.");
2963 goto finish;
2964 }
2965
d55ae9e6
LP
2966 if (!(m = dbus_message_new_method_call(
2967 "com.ubuntu.Upstart",
2968 "/com/ubuntu/Upstart",
2969 "com.ubuntu.Upstart0_6",
2970 "EmitEvent"))) {
2971
2972 log_error("Could not allocate message.");
f1c5860b
LP
2973 r = -ENOMEM;
2974 goto finish;
d55ae9e6
LP
2975 }
2976
2977 dbus_message_iter_init_append(m, &iter);
2978
2979 env1_buf[sizeof(env1_buf)-2] = rl;
2980 env2_buf[sizeof(env2_buf)-2] = previous;
2981
2982 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &emit) ||
2983 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub) ||
2984 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env1) ||
2985 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env2) ||
2986 !dbus_message_iter_close_container(&iter, &sub) ||
2987 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b_false)) {
2988 log_error("Could not append arguments to message.");
2989 r = -ENOMEM;
2990 goto finish;
2991 }
2992
2993 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2994
2995 if (error_is_no_service(&error)) {
2996 r = 0;
2997 goto finish;
2998 }
2999
3000 log_error("Failed to issue method call: %s", error.message);
3001 r = -EIO;
3002 goto finish;
3003 }
3004
3005 r = 1;
3006
3007finish:
3008 if (m)
3009 dbus_message_unref(m);
3010
3011 if (reply)
3012 dbus_message_unref(reply);
3013
f1c5860b
LP
3014 if (bus)
3015 dbus_connection_unref(bus);
3016
d55ae9e6
LP
3017 dbus_error_free(&error);
3018
3019 return r;
3020}
3021
3022static int talk_initctl(void) {
eb22ac37
LP
3023 struct init_request request;
3024 int r, fd;
d55ae9e6 3025 char rl;
eb22ac37 3026
d55ae9e6 3027 if (!(rl = action_to_runlevel()))
eb22ac37
LP
3028 return 0;
3029
3030 zero(request);
3031 request.magic = INIT_MAGIC;
3032 request.sleeptime = 0;
3033 request.cmd = INIT_CMD_RUNLVL;
d55ae9e6
LP
3034 request.runlevel = rl;
3035
3036 if ((fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY)) < 0) {
3037
3038 if (errno == ENOENT)
3039 return 0;
eb22ac37 3040
d55ae9e6 3041 log_error("Failed to open "INIT_FIFO": %m");
eb22ac37 3042 return -errno;
d55ae9e6 3043 }
eb22ac37 3044
d55ae9e6 3045 errno = 0;
eb22ac37
LP
3046 r = loop_write(fd, &request, sizeof(request), false) != sizeof(request);
3047 close_nointr_nofail(fd);
3048
d55ae9e6
LP
3049 if (r < 0) {
3050 log_error("Failed to write to "INIT_FIFO": %m");
eb22ac37 3051 return errno ? -errno : -EIO;
d55ae9e6 3052 }
eb22ac37
LP
3053
3054 return 1;
e4b61340
LP
3055}
3056
3057static int systemctl_main(DBusConnection *bus, int argc, char *argv[]) {
7e4249b9 3058
7e4249b9
LP
3059 static const struct {
3060 const char* verb;
3061 const enum {
3062 MORE,
3063 LESS,
3064 EQUAL
3065 } argc_cmp;
3066 const int argc;
3067 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
3068 } verbs[] = {
3069 { "list-units", LESS, 1, list_units },
3070 { "list-jobs", EQUAL, 1, list_jobs },
3071 { "clear-jobs", EQUAL, 1, clear_jobs },
3072 { "load", MORE, 2, load_unit },
3073 { "cancel", MORE, 2, cancel_job },
3074 { "start", MORE, 2, start_unit },
3075 { "stop", MORE, 2, start_unit },
3076 { "reload", MORE, 2, start_unit },
3077 { "restart", MORE, 2, start_unit },
e4b61340 3078 { "isolate", EQUAL, 2, start_unit },
0183528f 3079 { "check", MORE, 2, check_unit },
48220598 3080 { "show", MORE, 1, show },
61cbdc4b 3081 { "status", MORE, 2, show },
7e4249b9
LP
3082 { "monitor", EQUAL, 1, monitor },
3083 { "dump", EQUAL, 1, dump },
3084 { "snapshot", LESS, 2, snapshot },
6759e7a7 3085 { "delete", MORE, 2, delete_snapshot },
7e4249b9
LP
3086 { "daemon-reload", EQUAL, 1, clear_jobs },
3087 { "daemon-reexec", EQUAL, 1, clear_jobs },
3088 { "daemon-exit", EQUAL, 1, clear_jobs },
3089 { "show-environment", EQUAL, 1, show_enviroment },
3090 { "set-environment", MORE, 2, set_environment },
3091 { "unset-environment", MORE, 2, set_environment },
514f4ef5
LP
3092 { "halt", EQUAL, 1, start_special },
3093 { "poweroff", EQUAL, 1, start_special },
3094 { "reboot", EQUAL, 1, start_special },
3095 { "default", EQUAL, 1, start_special },
3096 { "rescue", EQUAL, 1, start_special },
3097 { "emergency", EQUAL, 1, start_special },
7e4249b9
LP
3098 };
3099
e4b61340 3100 int left;
7e4249b9 3101 unsigned i;
7e4249b9 3102
e4b61340
LP
3103 assert(bus);
3104 assert(argc >= 0);
3105 assert(argv);
7e4249b9
LP
3106
3107 left = argc - optind;
3108
3109 if (left <= 0)
3110 /* Special rule: no arguments means "list-units" */
3111 i = 0;
3112 else {
0183528f
LP
3113 if (streq(argv[optind], "help")) {
3114 systemctl_help();
3115 return 0;
3116 }
3117
7e4249b9
LP
3118 for (i = 0; i < ELEMENTSOF(verbs); i++)
3119 if (streq(argv[optind], verbs[i].verb))
3120 break;
3121
3122 if (i >= ELEMENTSOF(verbs)) {
3123 log_error("Unknown operation %s", argv[optind]);
e4b61340 3124 return -EINVAL;
7e4249b9
LP
3125 }
3126 }
3127
3128 switch (verbs[i].argc_cmp) {
3129
3130 case EQUAL:
3131 if (left != verbs[i].argc) {
3132 log_error("Invalid number of arguments.");
e4b61340 3133 return -EINVAL;
7e4249b9
LP
3134 }
3135
3136 break;
3137
3138 case MORE:
3139 if (left < verbs[i].argc) {
3140 log_error("Too few arguments.");
e4b61340 3141 return -EINVAL;
7e4249b9
LP
3142 }
3143
3144 break;
3145
3146 case LESS:
3147 if (left > verbs[i].argc) {
3148 log_error("Too many arguments.");
e4b61340 3149 return -EINVAL;
7e4249b9
LP
3150 }
3151
3152 break;
3153
3154 default:
3155 assert_not_reached("Unknown comparison operator.");
3156 }
3157
e4b61340
LP
3158 return verbs[i].dispatch(bus, argv + optind, left);
3159}
3160
3161static int reload_with_fallback(DBusConnection *bus) {
3162 int r;
3163
3164 if (bus) {
3165 /* First, try systemd via D-Bus. */
3166 if ((r = clear_jobs(bus, NULL, 0)) > 0)
3167 return 0;
3168 }
3169
3170 /* Nothing else worked, so let's try signals */
3171 assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC);
3172
3173 if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) {
3174 log_error("kill() failed: %m");
3175 return -errno;
3176 }
3177
3178 return 0;
3179}
3180
3181static int start_with_fallback(DBusConnection *bus) {
3182 int r;
3183
514f4ef5 3184 warn_wall(arg_action);
ef2f1067 3185
e4b61340
LP
3186 if (bus) {
3187 /* First, try systemd via D-Bus. */
3188 if ((r = start_unit(bus, NULL, 0)) > 0)
3189 return 0;
3190
3191 /* Hmm, talking to systemd via D-Bus didn't work. Then
3192 * let's try to talk to Upstart via D-Bus. */
f1c5860b 3193 if ((r = talk_upstart()) > 0)
e4b61340
LP
3194 return 0;
3195 }
3196
3197 /* Nothing else worked, so let's try
3198 * /dev/initctl */
d55ae9e6
LP
3199 if ((r = talk_initctl()) != 0)
3200 return 0;
3201
3202 log_error("Failed to talk to init daemon.");
3203 return -EIO;
e4b61340
LP
3204}
3205
3206static int halt_main(DBusConnection *bus) {
3207 int r;
3208
3209 if (!arg_immediate)
3210 return start_with_fallback(bus);
3211
3212 if (!arg_no_wtmp)
3213 if ((r = utmp_put_shutdown(0)) < 0)
3214 log_warning("Failed to write utmp record: %s", strerror(-r));
3215
3216 if (!arg_no_sync)
3217 sync();
3218
3219 if (arg_dry)
3220 return 0;
3221
3222 /* Make sure C-A-D is handled by the kernel from this
3223 * point on... */
3224 reboot(RB_ENABLE_CAD);
3225
3226 switch (arg_action) {
3227
3228 case ACTION_HALT:
3229 log_info("Halting");
3230 reboot(RB_HALT_SYSTEM);
3231 break;
3232
3233 case ACTION_POWEROFF:
3234 log_info("Powering off");
3235 reboot(RB_POWER_OFF);
3236 break;
3237
3238 case ACTION_REBOOT:
3239 log_info("Rebooting");
3240 reboot(RB_AUTOBOOT);
3241 break;
3242
3243 default:
3244 assert_not_reached("Unknown halt action.");
3245 }
3246
3247 /* We should never reach this. */
3248 return -ENOSYS;
3249}
3250
3251static int runlevel_main(void) {
3252 int r, runlevel, previous;
3253
3254 if ((r = utmp_get_runlevel(&runlevel, &previous)) < 0) {
3255 printf("unknown");
3256 return r;
3257 }
3258
3259 printf("%c %c\n",
3260 previous <= 0 ? 'N' : previous,
3261 runlevel <= 0 ? 'N' : runlevel);
3262
3263 return 0;
3264}
3265
3266int main(int argc, char*argv[]) {
3267 int r, retval = 1;
3268 DBusConnection *bus = NULL;
3269 DBusError error;
3270
3271 dbus_error_init(&error);
3272
3273 log_parse_environment();
3274
3275 if ((r = parse_argv(argc, argv)) < 0)
3276 goto finish;
3277 else if (r == 0) {
3278 retval = 0;
7e4249b9
LP
3279 goto finish;
3280 }
3281
e4b61340
LP
3282 /* /sbin/runlevel doesn't need to communicate via D-Bus, so
3283 * let's shortcut this */
3284 if (arg_action == ACTION_RUNLEVEL) {
3285 retval = runlevel_main() < 0;
3286 goto finish;
3287 }
3288
9a1ac7b9 3289 bus_connect(arg_session ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, &error);
e4b61340
LP
3290
3291 switch (arg_action) {
3292
3293 case ACTION_SYSTEMCTL: {
3294
3295 if (!bus) {
3296 log_error("Failed to get D-Bus connection: %s", error.message);
3297 goto finish;
3298 }
3299
3300 retval = systemctl_main(bus, argc, argv) < 0;
3301 break;
3302 }
3303
3304 case ACTION_HALT:
3305 case ACTION_POWEROFF:
3306 case ACTION_REBOOT:
3307 retval = halt_main(bus) < 0;
3308 break;
3309
e4b61340
LP
3310 case ACTION_RUNLEVEL2:
3311 case ACTION_RUNLEVEL3:
3312 case ACTION_RUNLEVEL4:
3313 case ACTION_RUNLEVEL5:
3314 case ACTION_RESCUE:
514f4ef5 3315 case ACTION_EMERGENCY:
eb22ac37 3316 case ACTION_DEFAULT:
e4b61340
LP
3317 retval = start_with_fallback(bus) < 0;
3318 break;
7e4249b9 3319
e4b61340
LP
3320 case ACTION_RELOAD:
3321 case ACTION_REEXEC:
3322 retval = reload_with_fallback(bus) < 0;
3323 break;
3324
eb22ac37
LP
3325 case ACTION_INVALID:
3326 case ACTION_RUNLEVEL:
e4b61340
LP
3327 default:
3328 assert_not_reached("Unknown action");
3329 }
7e4249b9
LP
3330
3331finish:
3332
3333 if (bus)
3334 dbus_connection_unref(bus);
3335
e4b61340
LP
3336 dbus_error_free(&error);
3337
7e4249b9
LP
3338 dbus_shutdown();
3339
3340 return retval;
3341}