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