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