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