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