chiark / gitweb /
systemctl: add 'check' call
[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
45 static const char *arg_type = NULL;
46 static bool arg_all = false;
47 static bool arg_replace = false;
48 static bool arg_session = false;
49 static bool arg_no_block = false;
50 static bool arg_immediate = false;
51 static bool arg_no_wtmp = false;
52 static bool arg_no_sync = false;
53 static bool arg_no_wall = false;
54 static bool arg_dry = false;
55 static bool arg_quiet = false;
56 static char **arg_wall = NULL;
57 enum action {
58         ACTION_INVALID,
59         ACTION_SYSTEMCTL,
60         ACTION_HALT,
61         ACTION_POWEROFF,
62         ACTION_REBOOT,
63         ACTION_RUNLEVEL2,
64         ACTION_RUNLEVEL3,
65         ACTION_RUNLEVEL4,
66         ACTION_RUNLEVEL5,
67         ACTION_RESCUE,
68         ACTION_EMERGENCY,
69         ACTION_DEFAULT,
70         ACTION_RELOAD,
71         ACTION_REEXEC,
72         ACTION_RUNLEVEL,
73         _ACTION_MAX
74 } arg_action = ACTION_SYSTEMCTL;
75
76 static bool error_is_no_service(DBusError *error) {
77
78         assert(error);
79
80         if (!dbus_error_is_set(error))
81                 return false;
82
83         if (dbus_error_has_name(error, DBUS_ERROR_NAME_HAS_NO_OWNER))
84                 return true;
85
86         if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN))
87                 return true;
88
89         return startswith(error->name, "org.freedesktop.DBus.Error.Spawn.");
90 }
91
92 static int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) {
93
94         assert(iter);
95         assert(data);
96
97         if (dbus_message_iter_get_arg_type(iter) != type)
98                 return -EIO;
99
100         dbus_message_iter_get_basic(iter, data);
101
102         if (!dbus_message_iter_next(iter) != !next)
103                 return -EIO;
104
105         return 0;
106 }
107
108 static int bus_check_peercred(DBusConnection *c) {
109         int fd;
110         struct ucred ucred;
111         socklen_t l;
112
113         assert(c);
114
115         assert_se(dbus_connection_get_unix_fd(c, &fd));
116
117         l = sizeof(struct ucred);
118         if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &l) < 0) {
119                 log_error("SO_PEERCRED failed: %m");
120                 return -errno;
121         }
122
123         if (l != sizeof(struct ucred)) {
124                 log_error("SO_PEERCRED returned wrong size.");
125                 return -E2BIG;
126         }
127
128         if (ucred.uid != 0)
129                 return -EPERM;
130
131         return 1;
132 }
133
134 static int columns(void) {
135         static int parsed_columns = 0;
136         const char *e;
137
138         if (parsed_columns > 0)
139                 return parsed_columns;
140
141         if ((e = getenv("COLUMNS")))
142                 parsed_columns = atoi(e);
143
144         if (parsed_columns <= 0) {
145                 struct winsize ws;
146                 zero(ws);
147
148                 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
149                         parsed_columns = ws.ws_col;
150         }
151
152         if (parsed_columns <= 0)
153                 parsed_columns = 80;
154
155         return parsed_columns;
156
157 }
158
159 static void warn_wall(enum action action) {
160         static const char *table[_ACTION_MAX] = {
161                 [ACTION_HALT]      = "The system is going down for system halt NOW!",
162                 [ACTION_REBOOT]    = "The system is going down for reboot NOW!",
163                 [ACTION_POWEROFF]  = "The system is going down for power-off NOW!",
164                 [ACTION_RESCUE]    = "The system is going down to rescue mode NOW!",
165                 [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!"
166         };
167
168         if (arg_no_wall)
169                 return;
170
171         if (arg_wall) {
172                 char *p;
173
174                 if (!(p = strv_join(arg_wall, " "))) {
175                         log_error("Failed to join strings.");
176                         return;
177                 }
178
179                 if (*p) {
180                         utmp_wall(p);
181                         free(p);
182                         return;
183                 }
184
185                 free(p);
186         }
187
188         if (!table[action])
189                 return;
190
191         utmp_wall(table[action]);
192 }
193
194 static int list_units(DBusConnection *bus, char **args, unsigned n) {
195         DBusMessage *m = NULL, *reply = NULL;
196         DBusError error;
197         int r;
198         DBusMessageIter iter, sub, sub2;
199         unsigned k = 0;
200
201         dbus_error_init(&error);
202
203         assert(bus);
204
205         if (!(m = dbus_message_new_method_call(
206                               "org.freedesktop.systemd1",
207                               "/org/freedesktop/systemd1",
208                               "org.freedesktop.systemd1.Manager",
209                               "ListUnits"))) {
210                 log_error("Could not allocate message.");
211                 return -ENOMEM;
212         }
213
214         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
215                 log_error("Failed to issue method call: %s", error.message);
216                 r = -EIO;
217                 goto finish;
218         }
219
220         if (!dbus_message_iter_init(reply, &iter) ||
221             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
222             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
223                 log_error("Failed to parse reply.");
224                 r = -EIO;
225                 goto finish;
226         }
227
228         dbus_message_iter_recurse(&iter, &sub);
229
230         printf("%-45s %-6s %-12s %-12s %-15s %s\n", "UNIT", "LOAD", "ACTIVE", "SUB", "JOB", "DESCRIPTION");
231
232         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
233                 const char *id, *description, *load_state, *active_state, *sub_state, *unit_state, *job_type, *job_path, *dot;
234                 uint32_t job_id;
235
236                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
237                         log_error("Failed to parse reply.");
238                         r = -EIO;
239                         goto finish;
240                 }
241
242                 dbus_message_iter_recurse(&sub, &sub2);
243
244                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
245                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &description, true) < 0 ||
246                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 ||
247                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 ||
248                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 ||
249                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_state, true) < 0 ||
250                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &job_id, true) < 0 ||
251                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &job_type, true) < 0 ||
252                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, false) < 0) {
253                         log_error("Failed to parse reply.");
254                         r = -EIO;
255                         goto finish;
256                 }
257
258                 if ((!arg_type || ((dot = strrchr(id, '.')) &&
259                                    streq(dot+1, arg_type))) &&
260                     (arg_all || !streq(active_state, "inactive"))) {
261
262                         int a = 0, b = 0;
263
264                         printf("%-45s %-6s %-12s %-12s%n", id, load_state, active_state, sub_state, &a);
265
266                         if (job_id != 0)
267                                 printf(" %-15s%n", job_type, &b);
268                         else
269                                 b = 1 + 15;
270
271                         if (a + b + 2 < columns()) {
272                                 if (job_id == 0)
273                                         printf("                ");
274
275                                 printf("%.*s", columns() - a - b - 2, description);
276                         }
277
278                         fputs("\n", stdout);
279                         k++;
280                 }
281
282                 dbus_message_iter_next(&sub);
283         }
284
285         if (arg_all)
286                 printf("\n%u units listed.\n", k);
287         else
288                 printf("\n%u live units listed. Pass --all to see dead units, too.\n", k);
289
290         r = 0;
291
292 finish:
293         if (m)
294                 dbus_message_unref(m);
295
296         if (reply)
297                 dbus_message_unref(reply);
298
299         dbus_error_free(&error);
300
301         return r;
302 }
303
304 static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
305         DBusMessage *m = NULL, *reply = NULL;
306         DBusError error;
307         int r;
308         DBusMessageIter iter, sub, sub2;
309         unsigned k = 0;
310
311         dbus_error_init(&error);
312
313         assert(bus);
314
315         if (!(m = dbus_message_new_method_call(
316                               "org.freedesktop.systemd1",
317                               "/org/freedesktop/systemd1",
318                               "org.freedesktop.systemd1.Manager",
319                               "ListJobs"))) {
320                 log_error("Could not allocate message.");
321                 return -ENOMEM;
322         }
323
324         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
325                 log_error("Failed to issue method call: %s", error.message);
326                 r = -EIO;
327                 goto finish;
328         }
329
330         if (!dbus_message_iter_init(reply, &iter) ||
331             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
332             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
333                 log_error("Failed to parse reply.");
334                 r = -EIO;
335                 goto finish;
336         }
337
338         dbus_message_iter_recurse(&iter, &sub);
339
340         printf("%4s %-45s %-17s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
341
342         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
343                 const char *name, *type, *state, *job_path, *unit_path;
344                 uint32_t id;
345
346                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
347                         log_error("Failed to parse reply.");
348                         r = -EIO;
349                         goto finish;
350                 }
351
352                 dbus_message_iter_recurse(&sub, &sub2);
353
354                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &id, true) < 0 ||
355                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
356                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
357                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, true) < 0 ||
358                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, true) < 0 ||
359                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, false) < 0) {
360                         log_error("Failed to parse reply.");
361                         r = -EIO;
362                         goto finish;
363                 }
364
365                 printf("%4u %-45s %-17s %-7s\n", id, name, type, state);
366                 k++;
367
368                 dbus_message_iter_next(&sub);
369         }
370
371         printf("\n%u jobs listed.\n", k);
372         r = 0;
373
374 finish:
375         if (m)
376                 dbus_message_unref(m);
377
378         if (reply)
379                 dbus_message_unref(reply);
380
381         dbus_error_free(&error);
382
383         return r;
384 }
385
386 static int load_unit(DBusConnection *bus, char **args, unsigned n) {
387         DBusMessage *m = NULL, *reply = NULL;
388         DBusError error;
389         int r;
390         unsigned i;
391
392         dbus_error_init(&error);
393
394         assert(bus);
395         assert(args);
396
397         for (i = 1; i < n; i++) {
398
399                 if (!(m = dbus_message_new_method_call(
400                                       "org.freedesktop.systemd1",
401                                       "/org/freedesktop/systemd1",
402                                       "org.freedesktop.systemd1.Manager",
403                                       "LoadUnit"))) {
404                         log_error("Could not allocate message.");
405                         r = -ENOMEM;
406                         goto finish;
407                 }
408
409                 if (!dbus_message_append_args(m,
410                                               DBUS_TYPE_STRING, &args[i],
411                                               DBUS_TYPE_INVALID)) {
412                         log_error("Could not append arguments to message.");
413                         r = -ENOMEM;
414                         goto finish;
415                 }
416
417                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
418                         log_error("Failed to issue method call: %s", error.message);
419                         r = -EIO;
420                         goto finish;
421                 }
422
423                 dbus_message_unref(m);
424                 dbus_message_unref(reply);
425
426                 m = reply = NULL;
427         }
428
429         r = 0;
430
431 finish:
432         if (m)
433                 dbus_message_unref(m);
434
435         if (reply)
436                 dbus_message_unref(reply);
437
438         dbus_error_free(&error);
439
440         return r;
441 }
442
443 static int cancel_job(DBusConnection *bus, char **args, unsigned n) {
444         DBusMessage *m = NULL, *reply = NULL;
445         DBusError error;
446         int r;
447         unsigned i;
448
449         dbus_error_init(&error);
450
451         assert(bus);
452         assert(args);
453
454         for (i = 1; i < n; i++) {
455                 unsigned id;
456                 const char *path;
457
458                 if (!(m = dbus_message_new_method_call(
459                                       "org.freedesktop.systemd1",
460                                       "/org/freedesktop/systemd1",
461                                       "org.freedesktop.systemd1.Manager",
462                                       "GetJob"))) {
463                         log_error("Could not allocate message.");
464                         r = -ENOMEM;
465                         goto finish;
466                 }
467
468                 if ((r = safe_atou(args[i], &id)) < 0) {
469                         log_error("Failed to parse job id: %s", strerror(-r));
470                         goto finish;
471                 }
472
473                 assert_cc(sizeof(uint32_t) == sizeof(id));
474                 if (!dbus_message_append_args(m,
475                                               DBUS_TYPE_UINT32, &id,
476                                               DBUS_TYPE_INVALID)) {
477                         log_error("Could not append arguments to message.");
478                         r = -ENOMEM;
479                         goto finish;
480                 }
481
482                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
483                         log_error("Failed to issue method call: %s", error.message);
484                         r = -EIO;
485                         goto finish;
486                 }
487
488                 if (!dbus_message_get_args(reply, &error,
489                                            DBUS_TYPE_OBJECT_PATH, &path,
490                                            DBUS_TYPE_INVALID)) {
491                         log_error("Failed to parse reply: %s", error.message);
492                         r = -EIO;
493                         goto finish;
494                 }
495
496                 dbus_message_unref(m);
497                 if (!(m = dbus_message_new_method_call(
498                                       "org.freedesktop.systemd1",
499                                       path,
500                                       "org.freedesktop.systemd1.Job",
501                                       "Cancel"))) {
502                         log_error("Could not allocate message.");
503                         r = -ENOMEM;
504                         goto finish;
505                 }
506
507                 dbus_message_unref(reply);
508                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
509                         log_error("Failed to issue method call: %s", error.message);
510                         r = -EIO;
511                         goto finish;
512                 }
513
514                 dbus_message_unref(m);
515                 dbus_message_unref(reply);
516                 m = reply = NULL;
517         }
518
519         r = 0;
520
521 finish:
522         if (m)
523                 dbus_message_unref(m);
524
525         if (reply)
526                 dbus_message_unref(reply);
527
528         dbus_error_free(&error);
529
530         return r;
531 }
532
533 static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) {
534         DBusError error;
535         Set *s = data;
536
537         assert(connection);
538         assert(message);
539         assert(s);
540
541         dbus_error_init(&error);
542
543         /* log_debug("Got D-Bus request: %s.%s() on %s", */
544         /*           dbus_message_get_interface(message), */
545         /*           dbus_message_get_member(message), */
546         /*           dbus_message_get_path(message)); */
547
548         if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
549                 log_error("Warning! D-Bus connection terminated.");
550                 dbus_connection_close(connection);
551
552         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
553                 uint32_t id;
554                 const char *path;
555
556                 if (!dbus_message_get_args(message, &error,
557                                            DBUS_TYPE_UINT32, &id,
558                                            DBUS_TYPE_OBJECT_PATH, &path,
559                                            DBUS_TYPE_INVALID))
560                         log_error("Failed to parse message: %s", error.message);
561                 else {
562                         char *p;
563
564                         if ((p = set_remove(s, (char*) path)))
565                                 free(p);
566                 }
567         }
568
569         dbus_error_free(&error);
570         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
571 }
572
573 static int enable_wait_for_jobs(DBusConnection *bus) {
574         DBusError error;
575         DBusMessage *m = NULL, *reply = NULL;
576         int r;
577
578         assert(bus);
579
580         dbus_error_init(&error);
581
582         dbus_bus_add_match(bus,
583                            "type='signal',"
584                            "sender='org.freedesktop.systemd1',"
585                            "interface='org.freedesktop.systemd1.Manager',"
586                            "member='JobRemoved',"
587                            "path='/org/freedesktop/systemd1'",
588                            &error);
589
590         if (dbus_error_is_set(&error)) {
591                 log_error("Failed to add match: %s", error.message);
592                 r = -EIO;
593                 goto finish;
594         }
595
596         if (!(m = dbus_message_new_method_call(
597                               "org.freedesktop.systemd1",
598                               "/org/freedesktop/systemd1",
599                               "org.freedesktop.systemd1.Manager",
600                               "Subscribe"))) {
601                 log_error("Could not allocate message.");
602                 r = -ENOMEM;
603                 goto finish;
604         }
605
606         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
607                 log_error("Failed to issue method call: %s", error.message);
608                 r = -EIO;
609                 goto finish;
610         }
611
612         r = 0;
613
614 finish:
615         /* This is slightly dirty, since we don't undo the match registrations. */
616
617         if (m)
618                 dbus_message_unref(m);
619
620         if (reply)
621                 dbus_message_unref(reply);
622
623         dbus_error_free(&error);
624
625         return r;
626 }
627
628 static int wait_for_jobs(DBusConnection *bus, Set *s) {
629         int r;
630
631         assert(bus);
632         assert(s);
633
634         if (!dbus_connection_add_filter(bus, wait_filter, s, NULL)) {
635                 log_error("Failed to add filter.");
636                 r = -ENOMEM;
637                 goto finish;
638         }
639
640         while (!set_isempty(s) &&
641                dbus_connection_read_write_dispatch(bus, -1))
642                 ;
643
644         r = 0;
645
646 finish:
647         /* This is slightly dirty, since we don't undo the filter registration. */
648
649         return r;
650 }
651
652 static int start_unit_one(
653                 DBusConnection *bus,
654                 const char *method,
655                 const char *name,
656                 const char *mode,
657                 Set *s) {
658
659         DBusMessage *m = NULL, *reply = NULL;
660         DBusError error;
661         int r;
662
663         assert(bus);
664         assert(method);
665         assert(name);
666         assert(mode);
667         assert(arg_no_block || s);
668
669         dbus_error_init(&error);
670
671         if (!(m = dbus_message_new_method_call(
672                               "org.freedesktop.systemd1",
673                               "/org/freedesktop/systemd1",
674                               "org.freedesktop.systemd1.Manager",
675                               method))) {
676                 log_error("Could not allocate message.");
677                 r = -ENOMEM;
678                 goto finish;
679         }
680
681         if (!dbus_message_append_args(m,
682                                       DBUS_TYPE_STRING, &name,
683                                       DBUS_TYPE_STRING, &mode,
684                                       DBUS_TYPE_INVALID)) {
685                 log_error("Could not append arguments to message.");
686                 r = -ENOMEM;
687                 goto finish;
688         }
689
690         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
691
692                 if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) {
693                         /* There's always a fallback possible for
694                          * legacy actions. */
695                         r = 0;
696                         goto finish;
697                 }
698
699                 log_error("Failed to issue method call: %s", error.message);
700                 r = -EIO;
701                 goto finish;
702         }
703
704         if (!arg_no_block) {
705                 const char *path;
706                 char *p;
707
708                 if (!dbus_message_get_args(reply, &error,
709                                            DBUS_TYPE_OBJECT_PATH, &path,
710                                            DBUS_TYPE_INVALID)) {
711                         log_error("Failed to parse reply: %s", error.message);
712                         r = -EIO;
713                         goto finish;
714                 }
715
716                 if (!(p = strdup(path))) {
717                         log_error("Failed to duplicate path.");
718                         r = -ENOMEM;
719                         goto finish;
720                 }
721
722                 if ((r = set_put(s, p)) < 0) {
723                         free(p);
724                         log_error("Failed to add path to set.");
725                         goto finish;
726                 }
727         }
728
729         r = 1;
730
731 finish:
732         if (m)
733                 dbus_message_unref(m);
734
735         if (reply)
736                 dbus_message_unref(reply);
737
738         dbus_error_free(&error);
739
740         return r;
741 }
742
743 static enum action verb_to_action(const char *verb) {
744         if (streq(verb, "halt"))
745                 return ACTION_HALT;
746         else if (streq(verb, "poweroff"))
747                 return ACTION_POWEROFF;
748         else if (streq(verb, "reboot"))
749                 return ACTION_REBOOT;
750         else if (streq(verb, "rescue"))
751                 return ACTION_RESCUE;
752         else if (streq(verb, "emergency"))
753                 return ACTION_EMERGENCY;
754         else if (streq(verb, "default"))
755                 return ACTION_DEFAULT;
756         else
757                 return ACTION_INVALID;
758 }
759
760 static int start_unit(DBusConnection *bus, char **args, unsigned n) {
761
762         static const char * const table[_ACTION_MAX] = {
763                 [ACTION_HALT] = SPECIAL_HALT_TARGET,
764                 [ACTION_POWEROFF] = SPECIAL_POWEROFF_TARGET,
765                 [ACTION_REBOOT] = SPECIAL_REBOOT_TARGET,
766                 [ACTION_RUNLEVEL2] = SPECIAL_RUNLEVEL2_TARGET,
767                 [ACTION_RUNLEVEL3] = SPECIAL_RUNLEVEL3_TARGET,
768                 [ACTION_RUNLEVEL4] = SPECIAL_RUNLEVEL4_TARGET,
769                 [ACTION_RUNLEVEL5] = SPECIAL_RUNLEVEL5_TARGET,
770                 [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET,
771                 [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_SERVICE,
772                 [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET
773         };
774
775         int r;
776         unsigned i;
777         const char *method, *mode, *one_name;
778         Set *s = NULL;
779
780         assert(bus);
781
782         if (arg_action == ACTION_SYSTEMCTL) {
783                 method =
784                         streq(args[0], "stop")    ? "StopUnit" :
785                         streq(args[0], "reload")  ? "ReloadUnit" :
786                         streq(args[0], "restart") ? "RestartUnit" :
787                                                     "StartUnit";
788
789                 mode =
790                         (streq(args[0], "isolate") ||
791                          streq(args[0], "rescue")  ||
792                          streq(args[0], "emergency")) ? "isolate" :
793                                           arg_replace ? "replace" :
794                                                         "fail";
795
796                 one_name = table[verb_to_action(args[0])];
797
798         } else {
799                 assert(arg_action < ELEMENTSOF(table));
800                 assert(table[arg_action]);
801
802                 method = "StartUnit";
803
804                 mode = (arg_action == ACTION_EMERGENCY ||
805                         arg_action == ACTION_RESCUE) ? "isolate" : "replace";
806
807                 one_name = table[arg_action];
808         }
809
810         if (!arg_no_block) {
811                 if ((r = enable_wait_for_jobs(bus)) < 0) {
812                         log_error("Could not watch jobs: %s", strerror(-r));
813                         goto finish;
814                 }
815
816                 if (!(s = set_new(string_hash_func, string_compare_func))) {
817                         log_error("Failed to allocate set.");
818                         r = -ENOMEM;
819                         goto finish;
820                 }
821         }
822
823         r = 0;
824
825         if (one_name) {
826                 if ((r = start_unit_one(bus, method, one_name, mode, s)) <= 0)
827                         goto finish;
828         } else {
829                 for (i = 1; i < n; i++)
830                         if ((r = start_unit_one(bus, method, args[i], mode, s)) < 0)
831                                 goto finish;
832         }
833
834         if (!arg_no_block)
835                 r = wait_for_jobs(bus, s);
836
837 finish:
838         if (s)
839                 set_free_free(s);
840
841         return r;
842 }
843
844 static int start_special(DBusConnection *bus, char **args, unsigned n) {
845         assert(bus);
846         assert(args);
847
848         warn_wall(verb_to_action(args[0]));
849
850         return start_unit(bus, args, n);
851 }
852
853 static int check_unit(DBusConnection *bus, char **args, unsigned n) {
854         DBusMessage *m = NULL, *reply = NULL;
855         const char
856                 *interface = "org.freedesktop.systemd1.Unit",
857                 *property = "ActiveState";
858         int r = -EADDRNOTAVAIL;
859         DBusError error;
860         unsigned i;
861
862         assert(bus);
863         assert(args);
864
865         dbus_error_init(&error);
866
867         for (i = 1; i < n; i++) {
868                 const char *path = NULL;
869                 const char *state;
870                 DBusMessageIter iter, sub;
871
872                 if (!(m = dbus_message_new_method_call(
873                                       "org.freedesktop.systemd1",
874                                       "/org/freedesktop/systemd1",
875                                       "org.freedesktop.systemd1.Manager",
876                                       "GetUnit"))) {
877                         log_error("Could not allocate message.");
878                         r = -ENOMEM;
879                         goto finish;
880                 }
881
882                 if (!dbus_message_append_args(m,
883                                               DBUS_TYPE_STRING, &args[i],
884                                               DBUS_TYPE_INVALID)) {
885                         log_error("Could not append arguments to message.");
886                         r = -ENOMEM;
887                         goto finish;
888                 }
889
890                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
891
892                         /* Hmm, cannot figure out anything about this unit... */
893                         if (!arg_quiet)
894                                 puts("unknown");
895
896                         continue;
897                 }
898
899                 if (!dbus_message_get_args(reply, &error,
900                                            DBUS_TYPE_OBJECT_PATH, &path,
901                                            DBUS_TYPE_INVALID)) {
902                         log_error("Failed to parse reply: %s", error.message);
903                         r = -EIO;
904                         goto finish;
905                 }
906
907                 dbus_message_unref(m);
908                 if (!(m = dbus_message_new_method_call(
909                                       "org.freedesktop.systemd1",
910                                       path,
911                                       "org.freedesktop.DBus.Properties",
912                                       "Get"))) {
913                         log_error("Could not allocate message.");
914                         r = -ENOMEM;
915                         goto finish;
916                 }
917
918                 if (!dbus_message_append_args(m,
919                                               DBUS_TYPE_STRING, &interface,
920                                               DBUS_TYPE_STRING, &property,
921                                               DBUS_TYPE_INVALID)) {
922                         log_error("Could not append arguments to message.");
923                         r = -ENOMEM;
924                         goto finish;
925                 }
926
927                 dbus_message_unref(reply);
928                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
929                         log_error("Failed to issue method call: %s", error.message);
930                         r = -EIO;
931                         goto finish;
932                 }
933
934                 if (!dbus_message_iter_init(reply, &iter) ||
935                     dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
936                         log_error("Failed to parse reply.");
937                         r = -EIO;
938                         goto finish;
939                 }
940
941                 dbus_message_iter_recurse(&iter, &sub);
942
943                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
944                         log_error("Failed to parse reply.");
945                         r = -EIO;
946                         goto finish;
947                 }
948
949                 dbus_message_iter_get_basic(&sub, &state);
950
951                 if (!arg_quiet)
952                         puts(state);
953
954                 if (streq(state, "active") || startswith(state, "active-"))
955                         r = 0;
956
957                 dbus_message_unref(m);
958                 dbus_message_unref(reply);
959                 m = reply = NULL;
960         }
961
962 finish:
963         if (m)
964                 dbus_message_unref(m);
965
966         if (reply)
967                 dbus_message_unref(reply);
968
969         dbus_error_free(&error);
970
971         return r;
972
973 }
974
975 static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage *message, void *data) {
976         DBusError error;
977         DBusMessage *m = NULL, *reply = NULL;
978
979         assert(connection);
980         assert(message);
981
982         dbus_error_init(&error);
983
984         /* log_debug("Got D-Bus request: %s.%s() on %s", */
985         /*           dbus_message_get_interface(message), */
986         /*           dbus_message_get_member(message), */
987         /*           dbus_message_get_path(message)); */
988
989         if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
990                 log_error("Warning! D-Bus connection terminated.");
991                 dbus_connection_close(connection);
992
993         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitNew") ||
994                    dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
995                 const char *id, *path;
996
997                 if (!dbus_message_get_args(message, &error,
998                                            DBUS_TYPE_STRING, &id,
999                                            DBUS_TYPE_OBJECT_PATH, &path,
1000                                            DBUS_TYPE_INVALID))
1001                         log_error("Failed to parse message: %s", error.message);
1002                 else if (streq(dbus_message_get_member(message), "UnitNew"))
1003                         printf("Unit %s added.\n", id);
1004                 else
1005                         printf("Unit %s removed.\n", id);
1006
1007         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobNew") ||
1008                    dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
1009                 uint32_t id;
1010                 const char *path;
1011
1012                 if (!dbus_message_get_args(message, &error,
1013                                            DBUS_TYPE_UINT32, &id,
1014                                            DBUS_TYPE_OBJECT_PATH, &path,
1015                                            DBUS_TYPE_INVALID))
1016                         log_error("Failed to parse message: %s", error.message);
1017                 else if (streq(dbus_message_get_member(message), "JobNew"))
1018                         printf("Job %u added.\n", id);
1019                 else
1020                         printf("Job %u removed.\n", id);
1021
1022
1023         } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Unit", "Changed") ||
1024                    dbus_message_is_signal(message, "org.freedesktop.systemd1.Job", "Changed")) {
1025
1026                 const char *path, *interface, *property = "Id";
1027                 DBusMessageIter iter, sub;
1028
1029                 path = dbus_message_get_path(message);
1030                 interface = dbus_message_get_interface(message);
1031
1032                 if (!(m = dbus_message_new_method_call(
1033                               "org.freedesktop.systemd1",
1034                               path,
1035                               "org.freedesktop.DBus.Properties",
1036                               "Get"))) {
1037                         log_error("Could not allocate message.");
1038                         goto oom;
1039                 }
1040
1041                 if (!dbus_message_append_args(m,
1042                                               DBUS_TYPE_STRING, &interface,
1043                                               DBUS_TYPE_STRING, &property,
1044                                               DBUS_TYPE_INVALID)) {
1045                         log_error("Could not append arguments to message.");
1046                         goto finish;
1047                 }
1048
1049                 if (!(reply = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
1050                         log_error("Failed to issue method call: %s", error.message);
1051                         goto finish;
1052                 }
1053
1054                 if (!dbus_message_iter_init(reply, &iter) ||
1055                     dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
1056                         log_error("Failed to parse reply.");
1057                         goto finish;
1058                 }
1059
1060                 dbus_message_iter_recurse(&iter, &sub);
1061
1062                 if (streq(interface, "org.freedesktop.systemd1.Unit")) {
1063                         const char *id;
1064
1065                         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
1066                                 log_error("Failed to parse reply.");
1067                                 goto finish;
1068                         }
1069
1070                         dbus_message_iter_get_basic(&sub, &id);
1071                         printf("Unit %s changed.\n", id);
1072                 } else {
1073                         uint32_t id;
1074
1075                         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT32)  {
1076                                 log_error("Failed to parse reply.");
1077                                 goto finish;
1078                         }
1079
1080                         dbus_message_iter_get_basic(&sub, &id);
1081                         printf("Job %u changed.\n", id);
1082                 }
1083         }
1084
1085 finish:
1086         if (m)
1087                 dbus_message_unref(m);
1088
1089         if (reply)
1090                 dbus_message_unref(reply);
1091
1092         dbus_error_free(&error);
1093         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1094
1095 oom:
1096         if (m)
1097                 dbus_message_unref(m);
1098
1099         if (reply)
1100                 dbus_message_unref(reply);
1101
1102         dbus_error_free(&error);
1103         return DBUS_HANDLER_RESULT_NEED_MEMORY;
1104 }
1105
1106 static int monitor(DBusConnection *bus, char **args, unsigned n) {
1107         DBusMessage *m = NULL, *reply = NULL;
1108         DBusError error;
1109         int r;
1110
1111         dbus_error_init(&error);
1112
1113         dbus_bus_add_match(bus,
1114                            "type='signal',"
1115                            "sender='org.freedesktop.systemd1',"
1116                            "interface='org.freedesktop.systemd1.Manager',"
1117                            "path='/org/freedesktop/systemd1'",
1118                            &error);
1119
1120         if (dbus_error_is_set(&error)) {
1121                 log_error("Failed to add match: %s", error.message);
1122                 r = -EIO;
1123                 goto finish;
1124         }
1125
1126         dbus_bus_add_match(bus,
1127                            "type='signal',"
1128                            "sender='org.freedesktop.systemd1',"
1129                            "interface='org.freedesktop.systemd1.Unit',"
1130                            "member='Changed'",
1131                            &error);
1132
1133         if (dbus_error_is_set(&error)) {
1134                 log_error("Failed to add match: %s", error.message);
1135                 r = -EIO;
1136                 goto finish;
1137         }
1138
1139         dbus_bus_add_match(bus,
1140                            "type='signal',"
1141                            "sender='org.freedesktop.systemd1',"
1142                            "interface='org.freedesktop.systemd1.Job',"
1143                            "member='Changed'",
1144                            &error);
1145
1146         if (dbus_error_is_set(&error)) {
1147                 log_error("Failed to add match: %s", error.message);
1148                 r = -EIO;
1149                 goto finish;
1150         }
1151
1152         if (!dbus_connection_add_filter(bus, monitor_filter, NULL, NULL)) {
1153                 log_error("Failed to add filter.");
1154                 r = -ENOMEM;
1155                 goto finish;
1156         }
1157
1158         if (!(m = dbus_message_new_method_call(
1159                               "org.freedesktop.systemd1",
1160                               "/org/freedesktop/systemd1",
1161                               "org.freedesktop.systemd1.Manager",
1162                               "Subscribe"))) {
1163                 log_error("Could not allocate message.");
1164                 r = -ENOMEM;
1165                 goto finish;
1166         }
1167
1168         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1169                 log_error("Failed to issue method call: %s", error.message);
1170                 r = -EIO;
1171                 goto finish;
1172         }
1173
1174         while (dbus_connection_read_write_dispatch(bus, -1))
1175                 ;
1176
1177         r = 0;
1178
1179 finish:
1180
1181         /* This is slightly dirty, since we don't undo the filter or the matches. */
1182
1183         if (m)
1184                 dbus_message_unref(m);
1185
1186         if (reply)
1187                 dbus_message_unref(reply);
1188
1189         dbus_error_free(&error);
1190
1191         return r;
1192 }
1193
1194 static int dump(DBusConnection *bus, char **args, unsigned n) {
1195         DBusMessage *m = NULL, *reply = NULL;
1196         DBusError error;
1197         int r;
1198         const char *text;
1199
1200         dbus_error_init(&error);
1201
1202         if (!(m = dbus_message_new_method_call(
1203                               "org.freedesktop.systemd1",
1204                               "/org/freedesktop/systemd1",
1205                               "org.freedesktop.systemd1.Manager",
1206                               "Dump"))) {
1207                 log_error("Could not allocate message.");
1208                 return -ENOMEM;
1209         }
1210
1211         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1212                 log_error("Failed to issue method call: %s", error.message);
1213                 r = -EIO;
1214                 goto finish;
1215         }
1216
1217         if (!dbus_message_get_args(reply, &error,
1218                                    DBUS_TYPE_STRING, &text,
1219                                    DBUS_TYPE_INVALID)) {
1220                 log_error("Failed to parse reply: %s", error.message);
1221                 r = -EIO;
1222                 goto finish;
1223         }
1224
1225         fputs(text, stdout);
1226
1227         r = 0;
1228
1229 finish:
1230         if (m)
1231                 dbus_message_unref(m);
1232
1233         if (reply)
1234                 dbus_message_unref(reply);
1235
1236         dbus_error_free(&error);
1237
1238         return r;
1239 }
1240
1241 static int snapshot(DBusConnection *bus, char **args, unsigned n) {
1242         DBusMessage *m = NULL, *reply = NULL;
1243         DBusError error;
1244         int r;
1245         const char *name = "", *path, *id;
1246         dbus_bool_t cleanup = FALSE;
1247         DBusMessageIter iter, sub;
1248         const char
1249                 *interface = "org.freedesktop.systemd1.Unit",
1250                 *property = "Id";
1251
1252         dbus_error_init(&error);
1253
1254         if (!(m = dbus_message_new_method_call(
1255                               "org.freedesktop.systemd1",
1256                               "/org/freedesktop/systemd1",
1257                               "org.freedesktop.systemd1.Manager",
1258                               "CreateSnapshot"))) {
1259                 log_error("Could not allocate message.");
1260                 return -ENOMEM;
1261         }
1262
1263         if (n > 1)
1264                 name = args[1];
1265
1266         if (!dbus_message_append_args(m,
1267                                       DBUS_TYPE_STRING, &name,
1268                                       DBUS_TYPE_BOOLEAN, &cleanup,
1269                                       DBUS_TYPE_INVALID)) {
1270                 log_error("Could not append arguments to message.");
1271                 r = -ENOMEM;
1272                 goto finish;
1273         }
1274
1275         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1276                 log_error("Failed to issue method call: %s", error.message);
1277                 r = -EIO;
1278                 goto finish;
1279         }
1280
1281         if (!dbus_message_get_args(reply, &error,
1282                                    DBUS_TYPE_OBJECT_PATH, &path,
1283                                    DBUS_TYPE_INVALID)) {
1284                 log_error("Failed to parse reply: %s", error.message);
1285                 r = -EIO;
1286                 goto finish;
1287         }
1288
1289         dbus_message_unref(m);
1290         if (!(m = dbus_message_new_method_call(
1291                               "org.freedesktop.systemd1",
1292                               path,
1293                               "org.freedesktop.DBus.Properties",
1294                               "Get"))) {
1295                 log_error("Could not allocate message.");
1296                 return -ENOMEM;
1297         }
1298
1299         if (!dbus_message_append_args(m,
1300                                       DBUS_TYPE_STRING, &interface,
1301                                       DBUS_TYPE_STRING, &property,
1302                                       DBUS_TYPE_INVALID)) {
1303                 log_error("Could not append arguments to message.");
1304                 r = -ENOMEM;
1305                 goto finish;
1306         }
1307
1308         dbus_message_unref(reply);
1309         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1310                 log_error("Failed to issue method call: %s", error.message);
1311                 r = -EIO;
1312                 goto finish;
1313         }
1314
1315         if (!dbus_message_iter_init(reply, &iter) ||
1316             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
1317                 log_error("Failed to parse reply.");
1318                 r = -EIO;
1319                 goto finish;
1320         }
1321
1322         dbus_message_iter_recurse(&iter, &sub);
1323
1324         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)  {
1325                 log_error("Failed to parse reply.");
1326                 r = -EIO;
1327                 goto finish;
1328         }
1329
1330         dbus_message_iter_get_basic(&sub, &id);
1331
1332         if (!arg_quiet)
1333                 puts(id);
1334         r = 0;
1335
1336 finish:
1337         if (m)
1338                 dbus_message_unref(m);
1339
1340         if (reply)
1341                 dbus_message_unref(reply);
1342
1343         dbus_error_free(&error);
1344
1345         return r;
1346 }
1347
1348 static int clear_jobs(DBusConnection *bus, char **args, unsigned n) {
1349         DBusMessage *m = NULL, *reply = NULL;
1350         DBusError error;
1351         int r;
1352         const char *method;
1353
1354         dbus_error_init(&error);
1355
1356         if (arg_action == ACTION_RELOAD)
1357                 method = "Reload";
1358         else if (arg_action == ACTION_REEXEC)
1359                 method = "Reexecute";
1360         else {
1361                 assert(arg_action == ACTION_SYSTEMCTL);
1362
1363                 method =
1364                         streq(args[0], "clear-jobs")    ? "ClearJobs" :
1365                         streq(args[0], "daemon-reload") ? "Reload" :
1366                         streq(args[0], "daemon-reexec") ? "Reexecute" :
1367                                                           "Exit";
1368         }
1369
1370         if (!(m = dbus_message_new_method_call(
1371                               "org.freedesktop.systemd1",
1372                               "/org/freedesktop/systemd1",
1373                               "org.freedesktop.systemd1.Manager",
1374                               method))) {
1375                 log_error("Could not allocate message.");
1376                 return -ENOMEM;
1377         }
1378
1379         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1380
1381                 if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) {
1382                         /* There's always a fallback possible for
1383                          * legacy actions. */
1384                         r = 0;
1385                         goto finish;
1386                 }
1387
1388                 log_error("Failed to issue method call: %s", error.message);
1389                 r = -EIO;
1390                 goto finish;
1391         }
1392
1393         r = 1;
1394
1395 finish:
1396         if (m)
1397                 dbus_message_unref(m);
1398
1399         if (reply)
1400                 dbus_message_unref(reply);
1401
1402         dbus_error_free(&error);
1403
1404         return r;
1405 }
1406
1407 static int show_enviroment(DBusConnection *bus, char **args, unsigned n) {
1408         DBusMessage *m = NULL, *reply = NULL;
1409         DBusError error;
1410         DBusMessageIter iter, sub, sub2;
1411         int r;
1412         const char
1413                 *interface = "org.freedesktop.systemd1.Manager",
1414                 *property = "Environment";
1415
1416         dbus_error_init(&error);
1417
1418         if (!(m = dbus_message_new_method_call(
1419                               "org.freedesktop.systemd1",
1420                               "/org/freedesktop/systemd1",
1421                               "org.freedesktop.DBus.Properties",
1422                               "Get"))) {
1423                 log_error("Could not allocate message.");
1424                 return -ENOMEM;
1425         }
1426
1427         if (!dbus_message_append_args(m,
1428                                       DBUS_TYPE_STRING, &interface,
1429                                       DBUS_TYPE_STRING, &property,
1430                                       DBUS_TYPE_INVALID)) {
1431                 log_error("Could not append arguments to message.");
1432                 r = -ENOMEM;
1433                 goto finish;
1434         }
1435
1436         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1437                 log_error("Failed to issue method call: %s", error.message);
1438                 r = -EIO;
1439                 goto finish;
1440         }
1441
1442         if (!dbus_message_iter_init(reply, &iter) ||
1443             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
1444                 log_error("Failed to parse reply.");
1445                 r = -EIO;
1446                 goto finish;
1447         }
1448
1449         dbus_message_iter_recurse(&iter, &sub);
1450
1451         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY ||
1452             dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_STRING)  {
1453                 log_error("Failed to parse reply.");
1454                 r = -EIO;
1455                 goto finish;
1456         }
1457
1458         dbus_message_iter_recurse(&sub, &sub2);
1459
1460         while (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_INVALID) {
1461                 const char *text;
1462
1463                 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) {
1464                         log_error("Failed to parse reply.");
1465                         r = -EIO;
1466                         goto finish;
1467                 }
1468
1469                 dbus_message_iter_get_basic(&sub2, &text);
1470                 printf("%s\n", text);
1471
1472                 dbus_message_iter_next(&sub2);
1473         }
1474
1475         r = 0;
1476
1477 finish:
1478         if (m)
1479                 dbus_message_unref(m);
1480
1481         if (reply)
1482                 dbus_message_unref(reply);
1483
1484         dbus_error_free(&error);
1485
1486         return r;
1487 }
1488
1489 static int set_environment(DBusConnection *bus, char **args, unsigned n) {
1490         DBusMessage *m = NULL, *reply = NULL;
1491         DBusError error;
1492         int r;
1493         const char *method;
1494         DBusMessageIter iter, sub;
1495         unsigned i;
1496
1497         dbus_error_init(&error);
1498
1499         method = streq(args[0], "set-environment")
1500                 ? "SetEnvironment"
1501                 : "UnsetEnvironment";
1502
1503         if (!(m = dbus_message_new_method_call(
1504                               "org.freedesktop.systemd1",
1505                               "/org/freedesktop/systemd1",
1506                               "org.freedesktop.systemd1.Manager",
1507                               method))) {
1508
1509                 log_error("Could not allocate message.");
1510                 return -ENOMEM;
1511         }
1512
1513         dbus_message_iter_init_append(m, &iter);
1514
1515         if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
1516                 log_error("Could not append arguments to message.");
1517                 r = -ENOMEM;
1518                 goto finish;
1519         }
1520
1521         for (i = 1; i < n; i++)
1522                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &args[i])) {
1523                         log_error("Could not append arguments to message.");
1524                         r = -ENOMEM;
1525                         goto finish;
1526                 }
1527
1528         if (!dbus_message_iter_close_container(&iter, &sub)) {
1529                 log_error("Could not append arguments to message.");
1530                 r = -ENOMEM;
1531                 goto finish;
1532         }
1533
1534         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1535                 log_error("Failed to issue method call: %s", error.message);
1536                 r = -EIO;
1537                 goto finish;
1538         }
1539
1540         r = 0;
1541
1542 finish:
1543         if (m)
1544                 dbus_message_unref(m);
1545
1546         if (reply)
1547                 dbus_message_unref(reply);
1548
1549         dbus_error_free(&error);
1550
1551         return r;
1552 }
1553
1554 static int systemctl_help(void) {
1555
1556         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1557                "Send control commands to the systemd manager.\n\n"
1558                "  -h --help      Show this help\n"
1559                "  -t --type=TYPE List only units of a particular type\n"
1560                "  -a --all       Show all units, including dead ones\n"
1561                "     --replace   When installing a new job, replace existing conflicting ones\n"
1562                "     --system    Connect to system bus\n"
1563                "     --session   Connect to session bus\n"
1564                "  -q --quiet     Suppress output\n"
1565                "     --no-block  Do not wait until operation finished\n"
1566                "     --no-wall   Don't send wall message before halt/power-off/reboot\n\n"
1567                "Commands:\n"
1568                "  list-units                      List units\n"
1569                "  list-jobs                       List jobs\n"
1570                "  clear-jobs                      Cancel all jobs\n"
1571                "  load [NAME...]                  Load one or more units\n"
1572                "  cancel [JOB...]                 Cancel one or more jobs\n"
1573                "  start [NAME...]                 Start one or more units\n"
1574                "  stop [NAME...]                  Stop one or more units\n"
1575                "  restart [NAME...]               Restart one or more units\n"
1576                "  reload [NAME...]                Reload one or more units\n"
1577                "  isolate [NAME]                  Start one unit and stop all others\n"
1578                "  check [NAME...]                 Check whether any of the passed units are active\n"
1579                "  monitor                         Monitor unit/job changes\n"
1580                "  dump                            Dump server status\n"
1581                "  snapshot [NAME]                 Create a snapshot\n"
1582                "  daemon-reload                   Reload systemd manager configuration\n"
1583                "  daemon-reexec                   Reexecute systemd manager\n"
1584                "  daemon-exit                     Ask the systemd manager to quit\n"
1585                "  show-environment                Dump environment\n"
1586                "  set-environment [NAME=VALUE...] Set one or more environment variables\n"
1587                "  unset-environment [NAME...]     Unset one or more environment variables\n"
1588                "  halt                            Shut down and halt the system\n"
1589                "  poweroff                        Shut down and power-off the system\n"
1590                "  reboot                          Shut down and reboot the system\n"
1591                "  default                         Enter default mode\n"
1592                "  rescue                          Enter rescue mode\n"
1593                "  emergency                       Enter emergency mode\n",
1594                program_invocation_short_name);
1595
1596         return 0;
1597 }
1598
1599 static int halt_help(void) {
1600
1601         printf("%s [OPTIONS...]\n\n"
1602                "%s the system.\n\n"
1603                "     --help      Show this help\n"
1604                "     --halt      Halt the machine\n"
1605                "  -p --poweroff  Switch off the machine\n"
1606                "     --reboot    Reboot the machine\n"
1607                "  -f --force     Force immediate halt/power-off/reboot\n"
1608                "  -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
1609                "  -d --no-wtmp   Don't write wtmp record\n"
1610                "  -n --no-sync   Don't sync before halt/power-off/reboot\n"
1611                "     --no-wall   Don't send wall message before halt/power-off/reboot\n",
1612                program_invocation_short_name,
1613                arg_action == ACTION_REBOOT   ? "Reboot" :
1614                arg_action == ACTION_POWEROFF ? "Power off" :
1615                                                "Halt");
1616
1617         return 0;
1618 }
1619
1620 static int shutdown_help(void) {
1621
1622         printf("%s [OPTIONS...] [now] [WALL...]\n\n"
1623                "Shut down the system.\n\n"
1624                "     --help      Show this help\n"
1625                "  -H --halt      Halt the machine\n"
1626                "  -P --poweroff  Power-off the machine\n"
1627                "  -r --reboot    Reboot the machine\n"
1628                "  -h             Equivalent to --poweroff, overriden by --halt\n"
1629                "  -k             Don't halt/power-off/reboot, just send warnings\n"
1630                "     --no-wall   Don't send wall message before halt/power-off/reboot\n",
1631                program_invocation_short_name);
1632
1633         return 0;
1634 }
1635
1636 static int telinit_help(void) {
1637
1638         printf("%s [OPTIONS...] {COMMAND}\n\n"
1639                "Send control commands to the init daemon.\n\n"
1640                "     --help      Show this help\n"
1641                "     --no-wall   Don't send wall message before halt/power-off/reboot\n\n"
1642                "Commands:\n"
1643                "  0              Power-off the machine\n"
1644                "  6              Reboot the machine\n"
1645                "  2, 3, 4, 5     Start runlevelX.target unit\n"
1646                "  1, s, S        Enter rescue mode\n"
1647                "  q, Q           Reload init daemon configuration\n"
1648                "  u, U           Reexecute init daemon\n",
1649                program_invocation_short_name);
1650
1651         return 0;
1652 }
1653
1654 static int runlevel_help(void) {
1655
1656         printf("%s [OPTIONS...]\n\n"
1657                "Prints the previous and current runlevel of the init system.\n\n"
1658                "     --help      Show this help\n",
1659                program_invocation_short_name);
1660
1661         return 0;
1662 }
1663
1664 static int systemctl_parse_argv(int argc, char *argv[]) {
1665
1666         enum {
1667                 ARG_REPLACE = 0x100,
1668                 ARG_SESSION,
1669                 ARG_SYSTEM,
1670                 ARG_NO_BLOCK,
1671                 ARG_NO_WALL
1672         };
1673
1674         static const struct option options[] = {
1675                 { "help",      no_argument,       NULL, 'h'          },
1676                 { "type",      required_argument, NULL, 't'          },
1677                 { "all",       no_argument,       NULL, 'a'          },
1678                 { "replace",   no_argument,       NULL, ARG_REPLACE  },
1679                 { "session",   no_argument,       NULL, ARG_SESSION  },
1680                 { "system",    no_argument,       NULL, ARG_SYSTEM   },
1681                 { "no-block",  no_argument,       NULL, ARG_NO_BLOCK },
1682                 { "no-wall",   no_argument,       NULL, ARG_NO_WALL  },
1683                 { "quiet",     no_argument,       NULL, 'q'          },
1684                 { NULL,        0,                 NULL, 0            }
1685         };
1686
1687         int c;
1688
1689         assert(argc >= 0);
1690         assert(argv);
1691
1692         while ((c = getopt_long(argc, argv, "htaq", options, NULL)) >= 0) {
1693
1694                 switch (c) {
1695
1696                 case 'h':
1697                         systemctl_help();
1698                         return 0;
1699
1700                 case 't':
1701                         arg_type = optarg;
1702                         break;
1703
1704                 case 'a':
1705                         arg_all = true;
1706                         break;
1707
1708                 case ARG_REPLACE:
1709                         arg_replace = true;
1710                         break;
1711
1712                 case ARG_SESSION:
1713                         arg_session = true;
1714                         break;
1715
1716                 case ARG_SYSTEM:
1717                         arg_session = false;
1718                         break;
1719
1720                 case ARG_NO_BLOCK:
1721                         arg_no_block = true;
1722                         break;
1723
1724                 case ARG_NO_WALL:
1725                         arg_no_wall = true;
1726                         break;
1727
1728                 case 'q':
1729                         arg_quiet = true;
1730                         break;
1731
1732                 case '?':
1733                         return -EINVAL;
1734
1735                 default:
1736                         log_error("Unknown option code %c", c);
1737                         return -EINVAL;
1738                 }
1739         }
1740
1741         return 1;
1742 }
1743
1744 static int halt_parse_argv(int argc, char *argv[]) {
1745
1746         enum {
1747                 ARG_HELP = 0x100,
1748                 ARG_HALT,
1749                 ARG_REBOOT,
1750                 ARG_NO_WALL
1751         };
1752
1753         static const struct option options[] = {
1754                 { "help",      no_argument,       NULL, ARG_HELP    },
1755                 { "halt",      no_argument,       NULL, ARG_HALT    },
1756                 { "poweroff",  no_argument,       NULL, 'p'         },
1757                 { "reboot",    no_argument,       NULL, ARG_REBOOT  },
1758                 { "force",     no_argument,       NULL, 'f'         },
1759                 { "wtmp-only", no_argument,       NULL, 'w'         },
1760                 { "no-wtmp",   no_argument,       NULL, 'd'         },
1761                 { "no-sync",   no_argument,       NULL, 'n'         },
1762                 { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
1763                 { NULL,        0,                 NULL, 0           }
1764         };
1765
1766         int c, runlevel;
1767
1768         assert(argc >= 0);
1769         assert(argv);
1770
1771         if (utmp_get_runlevel(&runlevel, NULL) >= 0)
1772                 if (runlevel == '0' || runlevel == '6')
1773                         arg_immediate = true;
1774
1775         while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) {
1776                 switch (c) {
1777
1778                 case ARG_HELP:
1779                         halt_help();
1780                         return 0;
1781
1782                 case ARG_HALT:
1783                         arg_action = ACTION_HALT;
1784                         break;
1785
1786                 case 'p':
1787                         arg_action = ACTION_POWEROFF;
1788                         break;
1789
1790                 case ARG_REBOOT:
1791                         arg_action = ACTION_REBOOT;
1792                         break;
1793
1794                 case 'f':
1795                         arg_immediate = true;
1796                         break;
1797
1798                 case 'w':
1799                         arg_dry = true;
1800                         break;
1801
1802                 case 'd':
1803                         arg_no_wtmp = true;
1804                         break;
1805
1806                 case 'n':
1807                         arg_no_sync = true;
1808                         break;
1809
1810                 case ARG_NO_WALL:
1811                         arg_no_wall = true;
1812                         break;
1813
1814                 case 'i':
1815                 case 'h':
1816                         /* Compatibility nops */
1817                         break;
1818
1819                 case '?':
1820                         return -EINVAL;
1821
1822                 default:
1823                         log_error("Unknown option code %c", c);
1824                         return -EINVAL;
1825                 }
1826         }
1827
1828         if (optind < argc) {
1829                 log_error("Too many arguments.");
1830                 return -EINVAL;
1831         }
1832
1833         return 1;
1834 }
1835
1836 static int shutdown_parse_argv(int argc, char *argv[]) {
1837
1838         enum {
1839                 ARG_HELP = 0x100,
1840                 ARG_NO_WALL
1841         };
1842
1843         static const struct option options[] = {
1844                 { "help",      no_argument,       NULL, ARG_HELP    },
1845                 { "halt",      no_argument,       NULL, 'H'         },
1846                 { "poweroff",  no_argument,       NULL, 'P'         },
1847                 { "reboot",    no_argument,       NULL, 'r'         },
1848                 { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
1849                 { NULL,        0,                 NULL, 0           }
1850         };
1851
1852         int c;
1853
1854         assert(argc >= 0);
1855         assert(argv);
1856
1857         while ((c = getopt_long(argc, argv, "HPrhkt:a", options, NULL)) >= 0) {
1858                 switch (c) {
1859
1860                 case ARG_HELP:
1861                         shutdown_help();
1862                         return 0;
1863
1864                 case 'H':
1865                         arg_action = ACTION_HALT;
1866                         break;
1867
1868                 case 'P':
1869                         arg_action = ACTION_POWEROFF;
1870                         break;
1871
1872                 case 'r':
1873                         arg_action = ACTION_REBOOT;
1874                         break;
1875
1876                 case 'h':
1877                         if (arg_action != ACTION_HALT)
1878                                 arg_action = ACTION_POWEROFF;
1879                         break;
1880
1881                 case 'k':
1882                         arg_dry = true;
1883                         break;
1884
1885                 case ARG_NO_WALL:
1886                         arg_no_wall = true;
1887                         break;
1888
1889                 case 't':
1890                 case 'a':
1891                         /* Compatibility nops */
1892                         break;
1893
1894                 case '?':
1895                         return -EINVAL;
1896
1897                 default:
1898                         log_error("Unknown option code %c", c);
1899                         return -EINVAL;
1900                 }
1901         }
1902
1903         if (argc > optind && !streq(argv[optind], "now"))
1904                 log_warning("First argument '%s' isn't 'now'. Ignoring.", argv[optind]);
1905
1906         /* We ignore the time argument */
1907         if (argc > optind + 1)
1908                 arg_wall = argv + optind + 1;
1909
1910         optind = argc;
1911
1912         return 1;
1913 }
1914
1915 static int telinit_parse_argv(int argc, char *argv[]) {
1916
1917         enum {
1918                 ARG_HELP = 0x100,
1919                 ARG_NO_WALL
1920         };
1921
1922         static const struct option options[] = {
1923                 { "help",      no_argument,       NULL, ARG_HELP    },
1924                 { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
1925                 { NULL,        0,                 NULL, 0           }
1926         };
1927
1928         static const struct {
1929                 char from;
1930                 enum action to;
1931         } table[] = {
1932                 { '0', ACTION_POWEROFF },
1933                 { '6', ACTION_REBOOT },
1934                 { '1', ACTION_RESCUE },
1935                 { '2', ACTION_RUNLEVEL2 },
1936                 { '3', ACTION_RUNLEVEL3 },
1937                 { '4', ACTION_RUNLEVEL4 },
1938                 { '5', ACTION_RUNLEVEL5 },
1939                 { 's', ACTION_RESCUE },
1940                 { 'S', ACTION_RESCUE },
1941                 { 'q', ACTION_RELOAD },
1942                 { 'Q', ACTION_RELOAD },
1943                 { 'u', ACTION_REEXEC },
1944                 { 'U', ACTION_REEXEC }
1945         };
1946
1947         unsigned i;
1948         int c;
1949
1950         assert(argc >= 0);
1951         assert(argv);
1952
1953         while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
1954                 switch (c) {
1955
1956                 case ARG_HELP:
1957                         telinit_help();
1958                         return 0;
1959
1960                 case ARG_NO_WALL:
1961                         arg_no_wall = true;
1962                         break;
1963
1964                 case '?':
1965                         return -EINVAL;
1966
1967                 default:
1968                         log_error("Unknown option code %c", c);
1969                         return -EINVAL;
1970                 }
1971         }
1972
1973         if (optind >= argc) {
1974                 telinit_help();
1975                 return -EINVAL;
1976         }
1977
1978         if (optind + 1 < argc) {
1979                 log_error("Too many arguments.");
1980                 return -EINVAL;
1981         }
1982
1983         if (strlen(argv[optind]) != 1) {
1984                 log_error("Expected single character argument.");
1985                 return -EINVAL;
1986         }
1987
1988         for (i = 0; i < ELEMENTSOF(table); i++)
1989                 if (table[i].from == argv[optind][0])
1990                         break;
1991
1992         if (i >= ELEMENTSOF(table)) {
1993                 log_error("Unknown command %s.", argv[optind]);
1994                 return -EINVAL;
1995         }
1996
1997         arg_action = table[i].to;
1998
1999         optind ++;
2000
2001         return 1;
2002 }
2003
2004 static int runlevel_parse_argv(int argc, char *argv[]) {
2005
2006         enum {
2007                 ARG_HELP = 0x100,
2008         };
2009
2010         static const struct option options[] = {
2011                 { "help",      no_argument,       NULL, ARG_HELP    },
2012                 { NULL,        0,                 NULL, 0           }
2013         };
2014
2015         int c;
2016
2017         assert(argc >= 0);
2018         assert(argv);
2019
2020         while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
2021                 switch (c) {
2022
2023                 case ARG_HELP:
2024                         runlevel_help();
2025                         return 0;
2026
2027                 case '?':
2028                         return -EINVAL;
2029
2030                 default:
2031                         log_error("Unknown option code %c", c);
2032                         return -EINVAL;
2033                 }
2034         }
2035
2036         if (optind < argc) {
2037                 log_error("Too many arguments.");
2038                 return -EINVAL;
2039         }
2040
2041         return 1;
2042 }
2043
2044 static int parse_argv(int argc, char *argv[]) {
2045         assert(argc >= 0);
2046         assert(argv);
2047
2048         if (program_invocation_short_name) {
2049
2050                 if (strstr(program_invocation_short_name, "halt")) {
2051                         arg_action = ACTION_HALT;
2052                         return halt_parse_argv(argc, argv);
2053                 } else if (strstr(program_invocation_short_name, "poweroff")) {
2054                         arg_action = ACTION_POWEROFF;
2055                         return halt_parse_argv(argc, argv);
2056                 } else if (strstr(program_invocation_short_name, "reboot")) {
2057                         arg_action = ACTION_REBOOT;
2058                         return halt_parse_argv(argc, argv);
2059                 } else if (strstr(program_invocation_short_name, "shutdown")) {
2060                         arg_action = ACTION_POWEROFF;
2061                         return shutdown_parse_argv(argc, argv);
2062                 } else if (strstr(program_invocation_short_name, "init")) {
2063                         arg_action = ACTION_INVALID;
2064                         return telinit_parse_argv(argc, argv);
2065                 } else if (strstr(program_invocation_short_name, "runlevel")) {
2066                         arg_action = ACTION_RUNLEVEL;
2067                         return runlevel_parse_argv(argc, argv);
2068                 }
2069         }
2070
2071         arg_action = ACTION_SYSTEMCTL;
2072         return systemctl_parse_argv(argc, argv);
2073 }
2074
2075 static int action_to_runlevel(void) {
2076
2077         static const char table[_ACTION_MAX] = {
2078                 [ACTION_HALT] =      '0',
2079                 [ACTION_POWEROFF] =  '0',
2080                 [ACTION_REBOOT] =    '6',
2081                 [ACTION_RUNLEVEL2] = '2',
2082                 [ACTION_RUNLEVEL3] = '3',
2083                 [ACTION_RUNLEVEL4] = '4',
2084                 [ACTION_RUNLEVEL5] = '5',
2085                 [ACTION_RESCUE] =    '1'
2086         };
2087
2088         assert(arg_action < _ACTION_MAX);
2089
2090         return table[arg_action];
2091 }
2092
2093 static int talk_upstart(void) {
2094         DBusMessage *m = NULL, *reply = NULL;
2095         DBusError error;
2096         int previous, rl, r;
2097         char
2098                 env1_buf[] = "RUNLEVEL=X",
2099                 env2_buf[] = "PREVLEVEL=X";
2100         char *env1 = env1_buf, *env2 = env2_buf;
2101         const char *emit = "runlevel";
2102         dbus_bool_t b_false = FALSE;
2103         DBusMessageIter iter, sub;
2104         DBusConnection *bus;
2105
2106         dbus_error_init(&error);
2107
2108         if (!(rl = action_to_runlevel()))
2109                 return 0;
2110
2111         if (utmp_get_runlevel(&previous, NULL) < 0)
2112                 previous = 'N';
2113
2114         if (!(bus = dbus_connection_open("unix:abstract=/com/ubuntu/upstart", &error))) {
2115                 if (dbus_error_has_name(&error, DBUS_ERROR_NO_SERVER)) {
2116                         r = 0;
2117                         goto finish;
2118                 }
2119
2120                 log_error("Failed to connect to Upstart bus: %s", error.message);
2121                 r = -EIO;
2122                 goto finish;
2123         }
2124
2125         if ((r = bus_check_peercred(bus)) < 0) {
2126                 log_error("Failed to verify owner of bus.");
2127                 goto finish;
2128         }
2129
2130         if (!(m = dbus_message_new_method_call(
2131                               "com.ubuntu.Upstart",
2132                               "/com/ubuntu/Upstart",
2133                               "com.ubuntu.Upstart0_6",
2134                               "EmitEvent"))) {
2135
2136                 log_error("Could not allocate message.");
2137                 r = -ENOMEM;
2138                 goto finish;
2139         }
2140
2141         dbus_message_iter_init_append(m, &iter);
2142
2143         env1_buf[sizeof(env1_buf)-2] = rl;
2144         env2_buf[sizeof(env2_buf)-2] = previous;
2145
2146         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &emit) ||
2147             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub) ||
2148             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env1) ||
2149             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env2) ||
2150             !dbus_message_iter_close_container(&iter, &sub) ||
2151             !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b_false)) {
2152                 log_error("Could not append arguments to message.");
2153                 r = -ENOMEM;
2154                 goto finish;
2155         }
2156
2157         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2158
2159                 if (error_is_no_service(&error)) {
2160                         r = 0;
2161                         goto finish;
2162                 }
2163
2164                 log_error("Failed to issue method call: %s", error.message);
2165                 r = -EIO;
2166                 goto finish;
2167         }
2168
2169         r = 1;
2170
2171 finish:
2172         if (m)
2173                 dbus_message_unref(m);
2174
2175         if (reply)
2176                 dbus_message_unref(reply);
2177
2178         if (bus)
2179                 dbus_connection_unref(bus);
2180
2181         dbus_error_free(&error);
2182
2183         return r;
2184 }
2185
2186 static int talk_initctl(void) {
2187         struct init_request request;
2188         int r, fd;
2189         char rl;
2190
2191         if (!(rl = action_to_runlevel()))
2192                 return 0;
2193
2194         zero(request);
2195         request.magic = INIT_MAGIC;
2196         request.sleeptime = 0;
2197         request.cmd = INIT_CMD_RUNLVL;
2198         request.runlevel = rl;
2199
2200         if ((fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY)) < 0) {
2201
2202                 if (errno == ENOENT)
2203                         return 0;
2204
2205                 log_error("Failed to open "INIT_FIFO": %m");
2206                 return -errno;
2207         }
2208
2209         errno = 0;
2210         r = loop_write(fd, &request, sizeof(request), false) != sizeof(request);
2211         close_nointr_nofail(fd);
2212
2213         if (r < 0) {
2214                 log_error("Failed to write to "INIT_FIFO": %m");
2215                 return errno ? -errno : -EIO;
2216         }
2217
2218         return 1;
2219 }
2220
2221 static int systemctl_main(DBusConnection *bus, int argc, char *argv[]) {
2222
2223         static const struct {
2224                 const char* verb;
2225                 const enum {
2226                         MORE,
2227                         LESS,
2228                         EQUAL
2229                 } argc_cmp;
2230                 const int argc;
2231                 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
2232         } verbs[] = {
2233                 { "list-units",        LESS,  1, list_units      },
2234                 { "list-jobs",         EQUAL, 1, list_jobs       },
2235                 { "clear-jobs",        EQUAL, 1, clear_jobs      },
2236                 { "load",              MORE,  2, load_unit       },
2237                 { "cancel",            MORE,  2, cancel_job      },
2238                 { "start",             MORE,  2, start_unit      },
2239                 { "stop",              MORE,  2, start_unit      },
2240                 { "reload",            MORE,  2, start_unit      },
2241                 { "restart",           MORE,  2, start_unit      },
2242                 { "isolate",           EQUAL, 2, start_unit      },
2243                 { "check",             MORE,  2, check_unit      },
2244                 { "monitor",           EQUAL, 1, monitor         },
2245                 { "dump",              EQUAL, 1, dump            },
2246                 { "snapshot",          LESS,  2, snapshot        },
2247                 { "daemon-reload",     EQUAL, 1, clear_jobs      },
2248                 { "daemon-reexec",     EQUAL, 1, clear_jobs      },
2249                 { "daemon-exit",       EQUAL, 1, clear_jobs      },
2250                 { "show-environment",  EQUAL, 1, show_enviroment },
2251                 { "set-environment",   MORE,  2, set_environment },
2252                 { "unset-environment", MORE,  2, set_environment },
2253                 { "halt",              EQUAL, 1, start_special   },
2254                 { "poweroff",          EQUAL, 1, start_special   },
2255                 { "reboot",            EQUAL, 1, start_special   },
2256                 { "default",           EQUAL, 1, start_special   },
2257                 { "rescue",            EQUAL, 1, start_special   },
2258                 { "emergency",         EQUAL, 1, start_special   },
2259         };
2260
2261         int left;
2262         unsigned i;
2263
2264         assert(bus);
2265         assert(argc >= 0);
2266         assert(argv);
2267
2268         left = argc - optind;
2269
2270         if (left <= 0)
2271                 /* Special rule: no arguments means "list-units" */
2272                 i = 0;
2273         else {
2274                 if (streq(argv[optind], "help")) {
2275                         systemctl_help();
2276                         return 0;
2277                 }
2278
2279                 for (i = 0; i < ELEMENTSOF(verbs); i++)
2280                         if (streq(argv[optind], verbs[i].verb))
2281                                 break;
2282
2283                 if (i >= ELEMENTSOF(verbs)) {
2284                         log_error("Unknown operation %s", argv[optind]);
2285                         return -EINVAL;
2286                 }
2287         }
2288
2289         switch (verbs[i].argc_cmp) {
2290
2291         case EQUAL:
2292                 if (left != verbs[i].argc) {
2293                         log_error("Invalid number of arguments.");
2294                         return -EINVAL;
2295                 }
2296
2297                 break;
2298
2299         case MORE:
2300                 if (left < verbs[i].argc) {
2301                         log_error("Too few arguments.");
2302                         return -EINVAL;
2303                 }
2304
2305                 break;
2306
2307         case LESS:
2308                 if (left > verbs[i].argc) {
2309                         log_error("Too many arguments.");
2310                         return -EINVAL;
2311                 }
2312
2313                 break;
2314
2315         default:
2316                 assert_not_reached("Unknown comparison operator.");
2317         }
2318
2319         return verbs[i].dispatch(bus, argv + optind, left);
2320 }
2321
2322 static int reload_with_fallback(DBusConnection *bus) {
2323         int r;
2324
2325         if (bus) {
2326                 /* First, try systemd via D-Bus. */
2327                 if ((r = clear_jobs(bus, NULL, 0)) > 0)
2328                         return 0;
2329         }
2330
2331         /* Nothing else worked, so let's try signals */
2332         assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC);
2333
2334         if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) {
2335                 log_error("kill() failed: %m");
2336                 return -errno;
2337         }
2338
2339         return 0;
2340 }
2341
2342 static int start_with_fallback(DBusConnection *bus) {
2343         int r;
2344
2345         warn_wall(arg_action);
2346
2347         if (bus) {
2348                 /* First, try systemd via D-Bus. */
2349                 if ((r = start_unit(bus, NULL, 0)) > 0)
2350                         return 0;
2351
2352                 /* Hmm, talking to systemd via D-Bus didn't work. Then
2353                  * let's try to talk to Upstart via D-Bus. */
2354                 if ((r = talk_upstart()) > 0)
2355                         return 0;
2356         }
2357
2358         /* Nothing else worked, so let's try
2359          * /dev/initctl */
2360         if ((r = talk_initctl()) != 0)
2361                 return 0;
2362
2363         log_error("Failed to talk to init daemon.");
2364         return -EIO;
2365 }
2366
2367 static int halt_main(DBusConnection *bus) {
2368         int r;
2369
2370         if (!arg_immediate)
2371                 return start_with_fallback(bus);
2372
2373         if (!arg_no_wtmp)
2374                 if ((r = utmp_put_shutdown(0)) < 0)
2375                         log_warning("Failed to write utmp record: %s", strerror(-r));
2376
2377         if (!arg_no_sync)
2378                 sync();
2379
2380         if (arg_dry)
2381                 return 0;
2382
2383         /* Make sure C-A-D is handled by the kernel from this
2384          * point on... */
2385         reboot(RB_ENABLE_CAD);
2386
2387         switch (arg_action) {
2388
2389         case ACTION_HALT:
2390                 log_info("Halting");
2391                 reboot(RB_HALT_SYSTEM);
2392                 break;
2393
2394         case ACTION_POWEROFF:
2395                 log_info("Powering off");
2396                 reboot(RB_POWER_OFF);
2397                 break;
2398
2399         case ACTION_REBOOT:
2400                 log_info("Rebooting");
2401                 reboot(RB_AUTOBOOT);
2402                 break;
2403
2404         default:
2405                 assert_not_reached("Unknown halt action.");
2406         }
2407
2408         /* We should never reach this. */
2409         return -ENOSYS;
2410 }
2411
2412 static int runlevel_main(void) {
2413         int r, runlevel, previous;
2414
2415         if ((r = utmp_get_runlevel(&runlevel, &previous)) < 0) {
2416                 printf("unknown");
2417                 return r;
2418         }
2419
2420         printf("%c %c\n",
2421                previous <= 0 ? 'N' : previous,
2422                runlevel <= 0 ? 'N' : runlevel);
2423
2424         return 0;
2425 }
2426
2427 int main(int argc, char*argv[]) {
2428         int r, retval = 1;
2429         DBusConnection *bus = NULL;
2430         DBusError error;
2431
2432         dbus_error_init(&error);
2433
2434         log_parse_environment();
2435
2436         if ((r = parse_argv(argc, argv)) < 0)
2437                 goto finish;
2438         else if (r == 0) {
2439                 retval = 0;
2440                 goto finish;
2441         }
2442
2443         /* /sbin/runlevel doesn't need to communicate via D-Bus, so
2444          * let's shortcut this */
2445         if (arg_action == ACTION_RUNLEVEL) {
2446                 retval = runlevel_main() < 0;
2447                 goto finish;
2448         }
2449
2450         /* If we are root, then let's not go via the bus */
2451         if (geteuid() == 0 && !arg_session) {
2452                 bus = dbus_connection_open("unix:abstract=/org/freedesktop/systemd1/private", &error);
2453
2454                 if (bus && bus_check_peercred(bus) < 0) {
2455                         log_error("Failed to verify owner of bus.");
2456                         goto finish;
2457                 }
2458         } else
2459                 bus = dbus_bus_get(arg_session ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error);
2460
2461         if (bus)
2462                 dbus_connection_set_exit_on_disconnect(bus, FALSE);
2463
2464         switch (arg_action) {
2465
2466         case ACTION_SYSTEMCTL: {
2467
2468                 if (!bus) {
2469                         log_error("Failed to get D-Bus connection: %s", error.message);
2470                         goto finish;
2471                 }
2472
2473                 retval = systemctl_main(bus, argc, argv) < 0;
2474                 break;
2475         }
2476
2477         case ACTION_HALT:
2478         case ACTION_POWEROFF:
2479         case ACTION_REBOOT:
2480                 retval = halt_main(bus) < 0;
2481                 break;
2482
2483         case ACTION_RUNLEVEL2:
2484         case ACTION_RUNLEVEL3:
2485         case ACTION_RUNLEVEL4:
2486         case ACTION_RUNLEVEL5:
2487         case ACTION_RESCUE:
2488         case ACTION_EMERGENCY:
2489         case ACTION_DEFAULT:
2490                 retval = start_with_fallback(bus) < 0;
2491                 break;
2492
2493         case ACTION_RELOAD:
2494         case ACTION_REEXEC:
2495                 retval = reload_with_fallback(bus) < 0;
2496                 break;
2497
2498         case ACTION_INVALID:
2499         case ACTION_RUNLEVEL:
2500         default:
2501                 assert_not_reached("Unknown action");
2502         }
2503
2504 finish:
2505
2506         if (bus)
2507                 dbus_connection_unref(bus);
2508
2509         dbus_error_free(&error);
2510
2511         dbus_shutdown();
2512
2513         return retval;
2514 }