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