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