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