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