chiark / gitweb /
execute: check tcpwrap for sockets passed via new-style socket passing as well.
[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 delete_snapshot(DBusConnection *bus, char **args, unsigned n) {
1367         DBusMessage *m = NULL, *reply = NULL;
1368         int r;
1369         DBusError error;
1370         unsigned i;
1371
1372         assert(bus);
1373         assert(args);
1374
1375         dbus_error_init(&error);
1376
1377         for (i = 1; i < n; i++) {
1378                 const char *path = NULL;
1379
1380                 if (!(m = dbus_message_new_method_call(
1381                                       "org.freedesktop.systemd1",
1382                                       "/org/freedesktop/systemd1",
1383                                       "org.freedesktop.systemd1.Manager",
1384                                       "GetUnit"))) {
1385                         log_error("Could not allocate message.");
1386                         r = -ENOMEM;
1387                         goto finish;
1388                 }
1389
1390                 if (!dbus_message_append_args(m,
1391                                               DBUS_TYPE_STRING, &args[i],
1392                                               DBUS_TYPE_INVALID)) {
1393                         log_error("Could not append arguments to message.");
1394                         r = -ENOMEM;
1395                         goto finish;
1396                 }
1397
1398                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1399                         log_error("Failed to issue method call: %s", error.message);
1400                         r = -EIO;
1401                         goto finish;
1402                 }
1403
1404                 if (!dbus_message_get_args(reply, &error,
1405                                            DBUS_TYPE_OBJECT_PATH, &path,
1406                                            DBUS_TYPE_INVALID)) {
1407                         log_error("Failed to parse reply: %s", error.message);
1408                         r = -EIO;
1409                         goto finish;
1410                 }
1411
1412                 dbus_message_unref(m);
1413                 if (!(m = dbus_message_new_method_call(
1414                                       "org.freedesktop.systemd1",
1415                                       path,
1416                                       "org.freedesktop.systemd1.Snapshot",
1417                                       "Remove"))) {
1418                         log_error("Could not allocate message.");
1419                         r = -ENOMEM;
1420                         goto finish;
1421                 }
1422
1423                 dbus_message_unref(reply);
1424                 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1425                         log_error("Failed to issue method call: %s", error.message);
1426                         r = -EIO;
1427                         goto finish;
1428                 }
1429
1430                 dbus_message_unref(m);
1431                 dbus_message_unref(reply);
1432                 m = reply = NULL;
1433         }
1434
1435         r = 0;
1436
1437 finish:
1438         if (m)
1439                 dbus_message_unref(m);
1440
1441         if (reply)
1442                 dbus_message_unref(reply);
1443
1444         dbus_error_free(&error);
1445
1446         return r;
1447 }
1448
1449 static int clear_jobs(DBusConnection *bus, char **args, unsigned n) {
1450         DBusMessage *m = NULL, *reply = NULL;
1451         DBusError error;
1452         int r;
1453         const char *method;
1454
1455         dbus_error_init(&error);
1456
1457         if (arg_action == ACTION_RELOAD)
1458                 method = "Reload";
1459         else if (arg_action == ACTION_REEXEC)
1460                 method = "Reexecute";
1461         else {
1462                 assert(arg_action == ACTION_SYSTEMCTL);
1463
1464                 method =
1465                         streq(args[0], "clear-jobs")    ? "ClearJobs" :
1466                         streq(args[0], "daemon-reload") ? "Reload" :
1467                         streq(args[0], "daemon-reexec") ? "Reexecute" :
1468                                                           "Exit";
1469         }
1470
1471         if (!(m = dbus_message_new_method_call(
1472                               "org.freedesktop.systemd1",
1473                               "/org/freedesktop/systemd1",
1474                               "org.freedesktop.systemd1.Manager",
1475                               method))) {
1476                 log_error("Could not allocate message.");
1477                 return -ENOMEM;
1478         }
1479
1480         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1481
1482                 if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) {
1483                         /* There's always a fallback possible for
1484                          * legacy actions. */
1485                         r = 0;
1486                         goto finish;
1487                 }
1488
1489                 log_error("Failed to issue method call: %s", error.message);
1490                 r = -EIO;
1491                 goto finish;
1492         }
1493
1494         r = 1;
1495
1496 finish:
1497         if (m)
1498                 dbus_message_unref(m);
1499
1500         if (reply)
1501                 dbus_message_unref(reply);
1502
1503         dbus_error_free(&error);
1504
1505         return r;
1506 }
1507
1508 static int show_enviroment(DBusConnection *bus, char **args, unsigned n) {
1509         DBusMessage *m = NULL, *reply = NULL;
1510         DBusError error;
1511         DBusMessageIter iter, sub, sub2;
1512         int r;
1513         const char
1514                 *interface = "org.freedesktop.systemd1.Manager",
1515                 *property = "Environment";
1516
1517         dbus_error_init(&error);
1518
1519         if (!(m = dbus_message_new_method_call(
1520                               "org.freedesktop.systemd1",
1521                               "/org/freedesktop/systemd1",
1522                               "org.freedesktop.DBus.Properties",
1523                               "Get"))) {
1524                 log_error("Could not allocate message.");
1525                 return -ENOMEM;
1526         }
1527
1528         if (!dbus_message_append_args(m,
1529                                       DBUS_TYPE_STRING, &interface,
1530                                       DBUS_TYPE_STRING, &property,
1531                                       DBUS_TYPE_INVALID)) {
1532                 log_error("Could not append arguments to message.");
1533                 r = -ENOMEM;
1534                 goto finish;
1535         }
1536
1537         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1538                 log_error("Failed to issue method call: %s", error.message);
1539                 r = -EIO;
1540                 goto finish;
1541         }
1542
1543         if (!dbus_message_iter_init(reply, &iter) ||
1544             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)  {
1545                 log_error("Failed to parse reply.");
1546                 r = -EIO;
1547                 goto finish;
1548         }
1549
1550         dbus_message_iter_recurse(&iter, &sub);
1551
1552         if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY ||
1553             dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_STRING)  {
1554                 log_error("Failed to parse reply.");
1555                 r = -EIO;
1556                 goto finish;
1557         }
1558
1559         dbus_message_iter_recurse(&sub, &sub2);
1560
1561         while (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_INVALID) {
1562                 const char *text;
1563
1564                 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) {
1565                         log_error("Failed to parse reply.");
1566                         r = -EIO;
1567                         goto finish;
1568                 }
1569
1570                 dbus_message_iter_get_basic(&sub2, &text);
1571                 printf("%s\n", text);
1572
1573                 dbus_message_iter_next(&sub2);
1574         }
1575
1576         r = 0;
1577
1578 finish:
1579         if (m)
1580                 dbus_message_unref(m);
1581
1582         if (reply)
1583                 dbus_message_unref(reply);
1584
1585         dbus_error_free(&error);
1586
1587         return r;
1588 }
1589
1590 static int set_environment(DBusConnection *bus, char **args, unsigned n) {
1591         DBusMessage *m = NULL, *reply = NULL;
1592         DBusError error;
1593         int r;
1594         const char *method;
1595         DBusMessageIter iter, sub;
1596         unsigned i;
1597
1598         dbus_error_init(&error);
1599
1600         method = streq(args[0], "set-environment")
1601                 ? "SetEnvironment"
1602                 : "UnsetEnvironment";
1603
1604         if (!(m = dbus_message_new_method_call(
1605                               "org.freedesktop.systemd1",
1606                               "/org/freedesktop/systemd1",
1607                               "org.freedesktop.systemd1.Manager",
1608                               method))) {
1609
1610                 log_error("Could not allocate message.");
1611                 return -ENOMEM;
1612         }
1613
1614         dbus_message_iter_init_append(m, &iter);
1615
1616         if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
1617                 log_error("Could not append arguments to message.");
1618                 r = -ENOMEM;
1619                 goto finish;
1620         }
1621
1622         for (i = 1; i < n; i++)
1623                 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &args[i])) {
1624                         log_error("Could not append arguments to message.");
1625                         r = -ENOMEM;
1626                         goto finish;
1627                 }
1628
1629         if (!dbus_message_iter_close_container(&iter, &sub)) {
1630                 log_error("Could not append arguments to message.");
1631                 r = -ENOMEM;
1632                 goto finish;
1633         }
1634
1635         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1636                 log_error("Failed to issue method call: %s", error.message);
1637                 r = -EIO;
1638                 goto finish;
1639         }
1640
1641         r = 0;
1642
1643 finish:
1644         if (m)
1645                 dbus_message_unref(m);
1646
1647         if (reply)
1648                 dbus_message_unref(reply);
1649
1650         dbus_error_free(&error);
1651
1652         return r;
1653 }
1654
1655 static int systemctl_help(void) {
1656
1657         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1658                "Send control commands to the systemd manager.\n\n"
1659                "  -h --help      Show this help\n"
1660                "  -t --type=TYPE List only units of a particular type\n"
1661                "  -a --all       Show all units, including dead ones\n"
1662                "     --replace   When installing a new job, replace existing conflicting ones\n"
1663                "     --system    Connect to system bus\n"
1664                "     --session   Connect to session bus\n"
1665                "  -q --quiet     Suppress output\n"
1666                "     --no-block  Do not wait until operation finished\n"
1667                "     --no-wall   Don't send wall message before halt/power-off/reboot\n\n"
1668                "Commands:\n"
1669                "  list-units                      List units\n"
1670                "  list-jobs                       List jobs\n"
1671                "  clear-jobs                      Cancel all jobs\n"
1672                "  load [NAME...]                  Load one or more units\n"
1673                "  cancel [JOB...]                 Cancel one or more jobs\n"
1674                "  start [NAME...]                 Start one or more units\n"
1675                "  stop [NAME...]                  Stop one or more units\n"
1676                "  restart [NAME...]               Restart one or more units\n"
1677                "  reload [NAME...]                Reload one or more units\n"
1678                "  isolate [NAME]                  Start one unit and stop all others\n"
1679                "  check [NAME...]                 Check whether any of the passed units are active\n"
1680                "  monitor                         Monitor unit/job changes\n"
1681                "  dump                            Dump server status\n"
1682                "  snapshot [NAME]                 Create a snapshot\n"
1683                "  delete [NAME...]                Remove one or more snapshots\n"
1684                "  daemon-reload                   Reload systemd manager configuration\n"
1685                "  daemon-reexec                   Reexecute systemd manager\n"
1686                "  daemon-exit                     Ask the systemd manager to quit\n"
1687                "  show-environment                Dump environment\n"
1688                "  set-environment [NAME=VALUE...] Set one or more environment variables\n"
1689                "  unset-environment [NAME...]     Unset one or more environment variables\n"
1690                "  halt                            Shut down and halt the system\n"
1691                "  poweroff                        Shut down and power-off the system\n"
1692                "  reboot                          Shut down and reboot the system\n"
1693                "  default                         Enter default mode\n"
1694                "  rescue                          Enter rescue mode\n"
1695                "  emergency                       Enter emergency mode\n",
1696                program_invocation_short_name);
1697
1698         return 0;
1699 }
1700
1701 static int halt_help(void) {
1702
1703         printf("%s [OPTIONS...]\n\n"
1704                "%s the system.\n\n"
1705                "     --help      Show this help\n"
1706                "     --halt      Halt the machine\n"
1707                "  -p --poweroff  Switch off the machine\n"
1708                "     --reboot    Reboot the machine\n"
1709                "  -f --force     Force immediate halt/power-off/reboot\n"
1710                "  -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
1711                "  -d --no-wtmp   Don't write wtmp record\n"
1712                "  -n --no-sync   Don't sync before halt/power-off/reboot\n"
1713                "     --no-wall   Don't send wall message before halt/power-off/reboot\n",
1714                program_invocation_short_name,
1715                arg_action == ACTION_REBOOT   ? "Reboot" :
1716                arg_action == ACTION_POWEROFF ? "Power off" :
1717                                                "Halt");
1718
1719         return 0;
1720 }
1721
1722 static int shutdown_help(void) {
1723
1724         printf("%s [OPTIONS...] [now] [WALL...]\n\n"
1725                "Shut down the system.\n\n"
1726                "     --help      Show this help\n"
1727                "  -H --halt      Halt the machine\n"
1728                "  -P --poweroff  Power-off the machine\n"
1729                "  -r --reboot    Reboot the machine\n"
1730                "  -h             Equivalent to --poweroff, overriden by --halt\n"
1731                "  -k             Don't halt/power-off/reboot, just send warnings\n"
1732                "     --no-wall   Don't send wall message before halt/power-off/reboot\n",
1733                program_invocation_short_name);
1734
1735         return 0;
1736 }
1737
1738 static int telinit_help(void) {
1739
1740         printf("%s [OPTIONS...] {COMMAND}\n\n"
1741                "Send control commands to the init daemon.\n\n"
1742                "     --help      Show this help\n"
1743                "     --no-wall   Don't send wall message before halt/power-off/reboot\n\n"
1744                "Commands:\n"
1745                "  0              Power-off the machine\n"
1746                "  6              Reboot the machine\n"
1747                "  2, 3, 4, 5     Start runlevelX.target unit\n"
1748                "  1, s, S        Enter rescue mode\n"
1749                "  q, Q           Reload init daemon configuration\n"
1750                "  u, U           Reexecute init daemon\n",
1751                program_invocation_short_name);
1752
1753         return 0;
1754 }
1755
1756 static int runlevel_help(void) {
1757
1758         printf("%s [OPTIONS...]\n\n"
1759                "Prints the previous and current runlevel of the init system.\n\n"
1760                "     --help      Show this help\n",
1761                program_invocation_short_name);
1762
1763         return 0;
1764 }
1765
1766 static int systemctl_parse_argv(int argc, char *argv[]) {
1767
1768         enum {
1769                 ARG_REPLACE = 0x100,
1770                 ARG_SESSION,
1771                 ARG_SYSTEM,
1772                 ARG_NO_BLOCK,
1773                 ARG_NO_WALL
1774         };
1775
1776         static const struct option options[] = {
1777                 { "help",      no_argument,       NULL, 'h'          },
1778                 { "type",      required_argument, NULL, 't'          },
1779                 { "all",       no_argument,       NULL, 'a'          },
1780                 { "replace",   no_argument,       NULL, ARG_REPLACE  },
1781                 { "session",   no_argument,       NULL, ARG_SESSION  },
1782                 { "system",    no_argument,       NULL, ARG_SYSTEM   },
1783                 { "no-block",  no_argument,       NULL, ARG_NO_BLOCK },
1784                 { "no-wall",   no_argument,       NULL, ARG_NO_WALL  },
1785                 { "quiet",     no_argument,       NULL, 'q'          },
1786                 { NULL,        0,                 NULL, 0            }
1787         };
1788
1789         int c;
1790
1791         assert(argc >= 0);
1792         assert(argv);
1793
1794         while ((c = getopt_long(argc, argv, "htaq", options, NULL)) >= 0) {
1795
1796                 switch (c) {
1797
1798                 case 'h':
1799                         systemctl_help();
1800                         return 0;
1801
1802                 case 't':
1803                         arg_type = optarg;
1804                         break;
1805
1806                 case 'a':
1807                         arg_all = true;
1808                         break;
1809
1810                 case ARG_REPLACE:
1811                         arg_replace = true;
1812                         break;
1813
1814                 case ARG_SESSION:
1815                         arg_session = true;
1816                         break;
1817
1818                 case ARG_SYSTEM:
1819                         arg_session = false;
1820                         break;
1821
1822                 case ARG_NO_BLOCK:
1823                         arg_no_block = true;
1824                         break;
1825
1826                 case ARG_NO_WALL:
1827                         arg_no_wall = true;
1828                         break;
1829
1830                 case 'q':
1831                         arg_quiet = true;
1832                         break;
1833
1834                 case '?':
1835                         return -EINVAL;
1836
1837                 default:
1838                         log_error("Unknown option code %c", c);
1839                         return -EINVAL;
1840                 }
1841         }
1842
1843         return 1;
1844 }
1845
1846 static int halt_parse_argv(int argc, char *argv[]) {
1847
1848         enum {
1849                 ARG_HELP = 0x100,
1850                 ARG_HALT,
1851                 ARG_REBOOT,
1852                 ARG_NO_WALL
1853         };
1854
1855         static const struct option options[] = {
1856                 { "help",      no_argument,       NULL, ARG_HELP    },
1857                 { "halt",      no_argument,       NULL, ARG_HALT    },
1858                 { "poweroff",  no_argument,       NULL, 'p'         },
1859                 { "reboot",    no_argument,       NULL, ARG_REBOOT  },
1860                 { "force",     no_argument,       NULL, 'f'         },
1861                 { "wtmp-only", no_argument,       NULL, 'w'         },
1862                 { "no-wtmp",   no_argument,       NULL, 'd'         },
1863                 { "no-sync",   no_argument,       NULL, 'n'         },
1864                 { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
1865                 { NULL,        0,                 NULL, 0           }
1866         };
1867
1868         int c, runlevel;
1869
1870         assert(argc >= 0);
1871         assert(argv);
1872
1873         if (utmp_get_runlevel(&runlevel, NULL) >= 0)
1874                 if (runlevel == '0' || runlevel == '6')
1875                         arg_immediate = true;
1876
1877         while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) {
1878                 switch (c) {
1879
1880                 case ARG_HELP:
1881                         halt_help();
1882                         return 0;
1883
1884                 case ARG_HALT:
1885                         arg_action = ACTION_HALT;
1886                         break;
1887
1888                 case 'p':
1889                         arg_action = ACTION_POWEROFF;
1890                         break;
1891
1892                 case ARG_REBOOT:
1893                         arg_action = ACTION_REBOOT;
1894                         break;
1895
1896                 case 'f':
1897                         arg_immediate = true;
1898                         break;
1899
1900                 case 'w':
1901                         arg_dry = true;
1902                         break;
1903
1904                 case 'd':
1905                         arg_no_wtmp = true;
1906                         break;
1907
1908                 case 'n':
1909                         arg_no_sync = true;
1910                         break;
1911
1912                 case ARG_NO_WALL:
1913                         arg_no_wall = true;
1914                         break;
1915
1916                 case 'i':
1917                 case 'h':
1918                         /* Compatibility nops */
1919                         break;
1920
1921                 case '?':
1922                         return -EINVAL;
1923
1924                 default:
1925                         log_error("Unknown option code %c", c);
1926                         return -EINVAL;
1927                 }
1928         }
1929
1930         if (optind < argc) {
1931                 log_error("Too many arguments.");
1932                 return -EINVAL;
1933         }
1934
1935         return 1;
1936 }
1937
1938 static int shutdown_parse_argv(int argc, char *argv[]) {
1939
1940         enum {
1941                 ARG_HELP = 0x100,
1942                 ARG_NO_WALL
1943         };
1944
1945         static const struct option options[] = {
1946                 { "help",      no_argument,       NULL, ARG_HELP    },
1947                 { "halt",      no_argument,       NULL, 'H'         },
1948                 { "poweroff",  no_argument,       NULL, 'P'         },
1949                 { "reboot",    no_argument,       NULL, 'r'         },
1950                 { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
1951                 { NULL,        0,                 NULL, 0           }
1952         };
1953
1954         int c;
1955
1956         assert(argc >= 0);
1957         assert(argv);
1958
1959         while ((c = getopt_long(argc, argv, "HPrhkt:a", options, NULL)) >= 0) {
1960                 switch (c) {
1961
1962                 case ARG_HELP:
1963                         shutdown_help();
1964                         return 0;
1965
1966                 case 'H':
1967                         arg_action = ACTION_HALT;
1968                         break;
1969
1970                 case 'P':
1971                         arg_action = ACTION_POWEROFF;
1972                         break;
1973
1974                 case 'r':
1975                         arg_action = ACTION_REBOOT;
1976                         break;
1977
1978                 case 'h':
1979                         if (arg_action != ACTION_HALT)
1980                                 arg_action = ACTION_POWEROFF;
1981                         break;
1982
1983                 case 'k':
1984                         arg_dry = true;
1985                         break;
1986
1987                 case ARG_NO_WALL:
1988                         arg_no_wall = true;
1989                         break;
1990
1991                 case 't':
1992                 case 'a':
1993                         /* Compatibility nops */
1994                         break;
1995
1996                 case '?':
1997                         return -EINVAL;
1998
1999                 default:
2000                         log_error("Unknown option code %c", c);
2001                         return -EINVAL;
2002                 }
2003         }
2004
2005         if (argc > optind && !streq(argv[optind], "now"))
2006                 log_warning("First argument '%s' isn't 'now'. Ignoring.", argv[optind]);
2007
2008         /* We ignore the time argument */
2009         if (argc > optind + 1)
2010                 arg_wall = argv + optind + 1;
2011
2012         optind = argc;
2013
2014         return 1;
2015 }
2016
2017 static int telinit_parse_argv(int argc, char *argv[]) {
2018
2019         enum {
2020                 ARG_HELP = 0x100,
2021                 ARG_NO_WALL
2022         };
2023
2024         static const struct option options[] = {
2025                 { "help",      no_argument,       NULL, ARG_HELP    },
2026                 { "no-wall",   no_argument,       NULL, ARG_NO_WALL },
2027                 { NULL,        0,                 NULL, 0           }
2028         };
2029
2030         static const struct {
2031                 char from;
2032                 enum action to;
2033         } table[] = {
2034                 { '0', ACTION_POWEROFF },
2035                 { '6', ACTION_REBOOT },
2036                 { '1', ACTION_RESCUE },
2037                 { '2', ACTION_RUNLEVEL2 },
2038                 { '3', ACTION_RUNLEVEL3 },
2039                 { '4', ACTION_RUNLEVEL4 },
2040                 { '5', ACTION_RUNLEVEL5 },
2041                 { 's', ACTION_RESCUE },
2042                 { 'S', ACTION_RESCUE },
2043                 { 'q', ACTION_RELOAD },
2044                 { 'Q', ACTION_RELOAD },
2045                 { 'u', ACTION_REEXEC },
2046                 { 'U', ACTION_REEXEC }
2047         };
2048
2049         unsigned i;
2050         int c;
2051
2052         assert(argc >= 0);
2053         assert(argv);
2054
2055         while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
2056                 switch (c) {
2057
2058                 case ARG_HELP:
2059                         telinit_help();
2060                         return 0;
2061
2062                 case ARG_NO_WALL:
2063                         arg_no_wall = true;
2064                         break;
2065
2066                 case '?':
2067                         return -EINVAL;
2068
2069                 default:
2070                         log_error("Unknown option code %c", c);
2071                         return -EINVAL;
2072                 }
2073         }
2074
2075         if (optind >= argc) {
2076                 telinit_help();
2077                 return -EINVAL;
2078         }
2079
2080         if (optind + 1 < argc) {
2081                 log_error("Too many arguments.");
2082                 return -EINVAL;
2083         }
2084
2085         if (strlen(argv[optind]) != 1) {
2086                 log_error("Expected single character argument.");
2087                 return -EINVAL;
2088         }
2089
2090         for (i = 0; i < ELEMENTSOF(table); i++)
2091                 if (table[i].from == argv[optind][0])
2092                         break;
2093
2094         if (i >= ELEMENTSOF(table)) {
2095                 log_error("Unknown command %s.", argv[optind]);
2096                 return -EINVAL;
2097         }
2098
2099         arg_action = table[i].to;
2100
2101         optind ++;
2102
2103         return 1;
2104 }
2105
2106 static int runlevel_parse_argv(int argc, char *argv[]) {
2107
2108         enum {
2109                 ARG_HELP = 0x100,
2110         };
2111
2112         static const struct option options[] = {
2113                 { "help",      no_argument,       NULL, ARG_HELP    },
2114                 { NULL,        0,                 NULL, 0           }
2115         };
2116
2117         int c;
2118
2119         assert(argc >= 0);
2120         assert(argv);
2121
2122         while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
2123                 switch (c) {
2124
2125                 case ARG_HELP:
2126                         runlevel_help();
2127                         return 0;
2128
2129                 case '?':
2130                         return -EINVAL;
2131
2132                 default:
2133                         log_error("Unknown option code %c", c);
2134                         return -EINVAL;
2135                 }
2136         }
2137
2138         if (optind < argc) {
2139                 log_error("Too many arguments.");
2140                 return -EINVAL;
2141         }
2142
2143         return 1;
2144 }
2145
2146 static int parse_argv(int argc, char *argv[]) {
2147         assert(argc >= 0);
2148         assert(argv);
2149
2150         if (program_invocation_short_name) {
2151
2152                 if (strstr(program_invocation_short_name, "halt")) {
2153                         arg_action = ACTION_HALT;
2154                         return halt_parse_argv(argc, argv);
2155                 } else if (strstr(program_invocation_short_name, "poweroff")) {
2156                         arg_action = ACTION_POWEROFF;
2157                         return halt_parse_argv(argc, argv);
2158                 } else if (strstr(program_invocation_short_name, "reboot")) {
2159                         arg_action = ACTION_REBOOT;
2160                         return halt_parse_argv(argc, argv);
2161                 } else if (strstr(program_invocation_short_name, "shutdown")) {
2162                         arg_action = ACTION_POWEROFF;
2163                         return shutdown_parse_argv(argc, argv);
2164                 } else if (strstr(program_invocation_short_name, "init")) {
2165                         arg_action = ACTION_INVALID;
2166                         return telinit_parse_argv(argc, argv);
2167                 } else if (strstr(program_invocation_short_name, "runlevel")) {
2168                         arg_action = ACTION_RUNLEVEL;
2169                         return runlevel_parse_argv(argc, argv);
2170                 }
2171         }
2172
2173         arg_action = ACTION_SYSTEMCTL;
2174         return systemctl_parse_argv(argc, argv);
2175 }
2176
2177 static int action_to_runlevel(void) {
2178
2179         static const char table[_ACTION_MAX] = {
2180                 [ACTION_HALT] =      '0',
2181                 [ACTION_POWEROFF] =  '0',
2182                 [ACTION_REBOOT] =    '6',
2183                 [ACTION_RUNLEVEL2] = '2',
2184                 [ACTION_RUNLEVEL3] = '3',
2185                 [ACTION_RUNLEVEL4] = '4',
2186                 [ACTION_RUNLEVEL5] = '5',
2187                 [ACTION_RESCUE] =    '1'
2188         };
2189
2190         assert(arg_action < _ACTION_MAX);
2191
2192         return table[arg_action];
2193 }
2194
2195 static int talk_upstart(void) {
2196         DBusMessage *m = NULL, *reply = NULL;
2197         DBusError error;
2198         int previous, rl, r;
2199         char
2200                 env1_buf[] = "RUNLEVEL=X",
2201                 env2_buf[] = "PREVLEVEL=X";
2202         char *env1 = env1_buf, *env2 = env2_buf;
2203         const char *emit = "runlevel";
2204         dbus_bool_t b_false = FALSE;
2205         DBusMessageIter iter, sub;
2206         DBusConnection *bus;
2207
2208         dbus_error_init(&error);
2209
2210         if (!(rl = action_to_runlevel()))
2211                 return 0;
2212
2213         if (utmp_get_runlevel(&previous, NULL) < 0)
2214                 previous = 'N';
2215
2216         if (!(bus = dbus_connection_open("unix:abstract=/com/ubuntu/upstart", &error))) {
2217                 if (dbus_error_has_name(&error, DBUS_ERROR_NO_SERVER)) {
2218                         r = 0;
2219                         goto finish;
2220                 }
2221
2222                 log_error("Failed to connect to Upstart bus: %s", error.message);
2223                 r = -EIO;
2224                 goto finish;
2225         }
2226
2227         if ((r = bus_check_peercred(bus)) < 0) {
2228                 log_error("Failed to verify owner of bus.");
2229                 goto finish;
2230         }
2231
2232         if (!(m = dbus_message_new_method_call(
2233                               "com.ubuntu.Upstart",
2234                               "/com/ubuntu/Upstart",
2235                               "com.ubuntu.Upstart0_6",
2236                               "EmitEvent"))) {
2237
2238                 log_error("Could not allocate message.");
2239                 r = -ENOMEM;
2240                 goto finish;
2241         }
2242
2243         dbus_message_iter_init_append(m, &iter);
2244
2245         env1_buf[sizeof(env1_buf)-2] = rl;
2246         env2_buf[sizeof(env2_buf)-2] = previous;
2247
2248         if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &emit) ||
2249             !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub) ||
2250             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env1) ||
2251             !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env2) ||
2252             !dbus_message_iter_close_container(&iter, &sub) ||
2253             !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b_false)) {
2254                 log_error("Could not append arguments to message.");
2255                 r = -ENOMEM;
2256                 goto finish;
2257         }
2258
2259         if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2260
2261                 if (error_is_no_service(&error)) {
2262                         r = 0;
2263                         goto finish;
2264                 }
2265
2266                 log_error("Failed to issue method call: %s", error.message);
2267                 r = -EIO;
2268                 goto finish;
2269         }
2270
2271         r = 1;
2272
2273 finish:
2274         if (m)
2275                 dbus_message_unref(m);
2276
2277         if (reply)
2278                 dbus_message_unref(reply);
2279
2280         if (bus)
2281                 dbus_connection_unref(bus);
2282
2283         dbus_error_free(&error);
2284
2285         return r;
2286 }
2287
2288 static int talk_initctl(void) {
2289         struct init_request request;
2290         int r, fd;
2291         char rl;
2292
2293         if (!(rl = action_to_runlevel()))
2294                 return 0;
2295
2296         zero(request);
2297         request.magic = INIT_MAGIC;
2298         request.sleeptime = 0;
2299         request.cmd = INIT_CMD_RUNLVL;
2300         request.runlevel = rl;
2301
2302         if ((fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY)) < 0) {
2303
2304                 if (errno == ENOENT)
2305                         return 0;
2306
2307                 log_error("Failed to open "INIT_FIFO": %m");
2308                 return -errno;
2309         }
2310
2311         errno = 0;
2312         r = loop_write(fd, &request, sizeof(request), false) != sizeof(request);
2313         close_nointr_nofail(fd);
2314
2315         if (r < 0) {
2316                 log_error("Failed to write to "INIT_FIFO": %m");
2317                 return errno ? -errno : -EIO;
2318         }
2319
2320         return 1;
2321 }
2322
2323 static int systemctl_main(DBusConnection *bus, int argc, char *argv[]) {
2324
2325         static const struct {
2326                 const char* verb;
2327                 const enum {
2328                         MORE,
2329                         LESS,
2330                         EQUAL
2331                 } argc_cmp;
2332                 const int argc;
2333                 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
2334         } verbs[] = {
2335                 { "list-units",        LESS,  1, list_units      },
2336                 { "list-jobs",         EQUAL, 1, list_jobs       },
2337                 { "clear-jobs",        EQUAL, 1, clear_jobs      },
2338                 { "load",              MORE,  2, load_unit       },
2339                 { "cancel",            MORE,  2, cancel_job      },
2340                 { "start",             MORE,  2, start_unit      },
2341                 { "stop",              MORE,  2, start_unit      },
2342                 { "reload",            MORE,  2, start_unit      },
2343                 { "restart",           MORE,  2, start_unit      },
2344                 { "isolate",           EQUAL, 2, start_unit      },
2345                 { "check",             MORE,  2, check_unit      },
2346                 { "monitor",           EQUAL, 1, monitor         },
2347                 { "dump",              EQUAL, 1, dump            },
2348                 { "snapshot",          LESS,  2, snapshot        },
2349                 { "delete",            MORE,  2, delete_snapshot },
2350                 { "daemon-reload",     EQUAL, 1, clear_jobs      },
2351                 { "daemon-reexec",     EQUAL, 1, clear_jobs      },
2352                 { "daemon-exit",       EQUAL, 1, clear_jobs      },
2353                 { "show-environment",  EQUAL, 1, show_enviroment },
2354                 { "set-environment",   MORE,  2, set_environment },
2355                 { "unset-environment", MORE,  2, set_environment },
2356                 { "halt",              EQUAL, 1, start_special   },
2357                 { "poweroff",          EQUAL, 1, start_special   },
2358                 { "reboot",            EQUAL, 1, start_special   },
2359                 { "default",           EQUAL, 1, start_special   },
2360                 { "rescue",            EQUAL, 1, start_special   },
2361                 { "emergency",         EQUAL, 1, start_special   },
2362         };
2363
2364         int left;
2365         unsigned i;
2366
2367         assert(bus);
2368         assert(argc >= 0);
2369         assert(argv);
2370
2371         left = argc - optind;
2372
2373         if (left <= 0)
2374                 /* Special rule: no arguments means "list-units" */
2375                 i = 0;
2376         else {
2377                 if (streq(argv[optind], "help")) {
2378                         systemctl_help();
2379                         return 0;
2380                 }
2381
2382                 for (i = 0; i < ELEMENTSOF(verbs); i++)
2383                         if (streq(argv[optind], verbs[i].verb))
2384                                 break;
2385
2386                 if (i >= ELEMENTSOF(verbs)) {
2387                         log_error("Unknown operation %s", argv[optind]);
2388                         return -EINVAL;
2389                 }
2390         }
2391
2392         switch (verbs[i].argc_cmp) {
2393
2394         case EQUAL:
2395                 if (left != verbs[i].argc) {
2396                         log_error("Invalid number of arguments.");
2397                         return -EINVAL;
2398                 }
2399
2400                 break;
2401
2402         case MORE:
2403                 if (left < verbs[i].argc) {
2404                         log_error("Too few arguments.");
2405                         return -EINVAL;
2406                 }
2407
2408                 break;
2409
2410         case LESS:
2411                 if (left > verbs[i].argc) {
2412                         log_error("Too many arguments.");
2413                         return -EINVAL;
2414                 }
2415
2416                 break;
2417
2418         default:
2419                 assert_not_reached("Unknown comparison operator.");
2420         }
2421
2422         return verbs[i].dispatch(bus, argv + optind, left);
2423 }
2424
2425 static int reload_with_fallback(DBusConnection *bus) {
2426         int r;
2427
2428         if (bus) {
2429                 /* First, try systemd via D-Bus. */
2430                 if ((r = clear_jobs(bus, NULL, 0)) > 0)
2431                         return 0;
2432         }
2433
2434         /* Nothing else worked, so let's try signals */
2435         assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC);
2436
2437         if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) {
2438                 log_error("kill() failed: %m");
2439                 return -errno;
2440         }
2441
2442         return 0;
2443 }
2444
2445 static int start_with_fallback(DBusConnection *bus) {
2446         int r;
2447
2448         warn_wall(arg_action);
2449
2450         if (bus) {
2451                 /* First, try systemd via D-Bus. */
2452                 if ((r = start_unit(bus, NULL, 0)) > 0)
2453                         return 0;
2454
2455                 /* Hmm, talking to systemd via D-Bus didn't work. Then
2456                  * let's try to talk to Upstart via D-Bus. */
2457                 if ((r = talk_upstart()) > 0)
2458                         return 0;
2459         }
2460
2461         /* Nothing else worked, so let's try
2462          * /dev/initctl */
2463         if ((r = talk_initctl()) != 0)
2464                 return 0;
2465
2466         log_error("Failed to talk to init daemon.");
2467         return -EIO;
2468 }
2469
2470 static int halt_main(DBusConnection *bus) {
2471         int r;
2472
2473         if (!arg_immediate)
2474                 return start_with_fallback(bus);
2475
2476         if (!arg_no_wtmp)
2477                 if ((r = utmp_put_shutdown(0)) < 0)
2478                         log_warning("Failed to write utmp record: %s", strerror(-r));
2479
2480         if (!arg_no_sync)
2481                 sync();
2482
2483         if (arg_dry)
2484                 return 0;
2485
2486         /* Make sure C-A-D is handled by the kernel from this
2487          * point on... */
2488         reboot(RB_ENABLE_CAD);
2489
2490         switch (arg_action) {
2491
2492         case ACTION_HALT:
2493                 log_info("Halting");
2494                 reboot(RB_HALT_SYSTEM);
2495                 break;
2496
2497         case ACTION_POWEROFF:
2498                 log_info("Powering off");
2499                 reboot(RB_POWER_OFF);
2500                 break;
2501
2502         case ACTION_REBOOT:
2503                 log_info("Rebooting");
2504                 reboot(RB_AUTOBOOT);
2505                 break;
2506
2507         default:
2508                 assert_not_reached("Unknown halt action.");
2509         }
2510
2511         /* We should never reach this. */
2512         return -ENOSYS;
2513 }
2514
2515 static int runlevel_main(void) {
2516         int r, runlevel, previous;
2517
2518         if ((r = utmp_get_runlevel(&runlevel, &previous)) < 0) {
2519                 printf("unknown");
2520                 return r;
2521         }
2522
2523         printf("%c %c\n",
2524                previous <= 0 ? 'N' : previous,
2525                runlevel <= 0 ? 'N' : runlevel);
2526
2527         return 0;
2528 }
2529
2530 int main(int argc, char*argv[]) {
2531         int r, retval = 1;
2532         DBusConnection *bus = NULL;
2533         DBusError error;
2534
2535         dbus_error_init(&error);
2536
2537         log_parse_environment();
2538
2539         if ((r = parse_argv(argc, argv)) < 0)
2540                 goto finish;
2541         else if (r == 0) {
2542                 retval = 0;
2543                 goto finish;
2544         }
2545
2546         /* /sbin/runlevel doesn't need to communicate via D-Bus, so
2547          * let's shortcut this */
2548         if (arg_action == ACTION_RUNLEVEL) {
2549                 retval = runlevel_main() < 0;
2550                 goto finish;
2551         }
2552
2553         /* If we are root, then let's not go via the bus */
2554         if (geteuid() == 0 && !arg_session) {
2555                 bus = dbus_connection_open("unix:abstract=/org/freedesktop/systemd1/private", &error);
2556
2557                 if (bus && bus_check_peercred(bus) < 0) {
2558                         log_error("Failed to verify owner of bus.");
2559                         goto finish;
2560                 }
2561         } else
2562                 bus = dbus_bus_get(arg_session ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error);
2563
2564         if (bus)
2565                 dbus_connection_set_exit_on_disconnect(bus, FALSE);
2566
2567         switch (arg_action) {
2568
2569         case ACTION_SYSTEMCTL: {
2570
2571                 if (!bus) {
2572                         log_error("Failed to get D-Bus connection: %s", error.message);
2573                         goto finish;
2574                 }
2575
2576                 retval = systemctl_main(bus, argc, argv) < 0;
2577                 break;
2578         }
2579
2580         case ACTION_HALT:
2581         case ACTION_POWEROFF:
2582         case ACTION_REBOOT:
2583                 retval = halt_main(bus) < 0;
2584                 break;
2585
2586         case ACTION_RUNLEVEL2:
2587         case ACTION_RUNLEVEL3:
2588         case ACTION_RUNLEVEL4:
2589         case ACTION_RUNLEVEL5:
2590         case ACTION_RESCUE:
2591         case ACTION_EMERGENCY:
2592         case ACTION_DEFAULT:
2593                 retval = start_with_fallback(bus) < 0;
2594                 break;
2595
2596         case ACTION_RELOAD:
2597         case ACTION_REEXEC:
2598                 retval = reload_with_fallback(bus) < 0;
2599                 break;
2600
2601         case ACTION_INVALID:
2602         case ACTION_RUNLEVEL:
2603         default:
2604                 assert_not_reached("Unknown action");
2605         }
2606
2607 finish:
2608
2609         if (bus)
2610                 dbus_connection_unref(bus);
2611
2612         dbus_error_free(&error);
2613
2614         dbus_shutdown();
2615
2616         return retval;
2617 }