]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/systemctl.c
update fixme
[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>
eb22ac37 31#include <fcntl.h>
f1c5860b 32#include <sys/socket.h>
ee5762e3 33#include <sys/stat.h>
7e4249b9
LP
34
35#include <dbus/dbus.h>
36
37#include "log.h"
38#include "util.h"
39#include "macro.h"
40#include "set.h"
e4b61340 41#include "utmp-wtmp.h"
514f4ef5 42#include "special.h"
eb22ac37 43#include "initreq.h"
e4a9373f 44#include "strv.h"
9a1ac7b9 45#include "dbus-common.h"
ab35fb1b 46#include "cgroup-show.h"
c6c18be3 47#include "cgroup-util.h"
582a507f 48#include "list.h"
ee5762e3
LP
49#include "path-lookup.h"
50#include "conf-parser.h"
51#include "sd-daemon.h"
7e4249b9
LP
52
53static const char *arg_type = NULL;
ea4a240d 54static char **arg_property = NULL;
7e4249b9 55static bool arg_all = false;
90d473a1 56static bool arg_fail = false;
7e4249b9 57static bool arg_session = false;
ee5762e3 58static bool arg_global = false;
e4b61340 59static bool arg_immediate = false;
ee5762e3 60static bool arg_no_block = false;
e4b61340
LP
61static bool arg_no_wtmp = false;
62static bool arg_no_sync = false;
514f4ef5 63static bool arg_no_wall = false;
ee5762e3 64static bool arg_no_reload = false;
e4b61340 65static bool arg_dry = false;
0183528f 66static bool arg_quiet = false;
ee5762e3
LP
67static bool arg_full = false;
68static bool arg_force = false;
69static bool arg_defaults = false;
e4b61340 70static char **arg_wall = NULL;
4445a875 71static enum action {
e4b61340
LP
72 ACTION_INVALID,
73 ACTION_SYSTEMCTL,
74 ACTION_HALT,
75 ACTION_POWEROFF,
76 ACTION_REBOOT,
e4b61340
LP
77 ACTION_RUNLEVEL2,
78 ACTION_RUNLEVEL3,
79 ACTION_RUNLEVEL4,
80 ACTION_RUNLEVEL5,
81 ACTION_RESCUE,
514f4ef5
LP
82 ACTION_EMERGENCY,
83 ACTION_DEFAULT,
e4b61340
LP
84 ACTION_RELOAD,
85 ACTION_REEXEC,
86 ACTION_RUNLEVEL,
87 _ACTION_MAX
88} arg_action = ACTION_SYSTEMCTL;
4445a875
LP
89static enum dot {
90 DOT_ALL,
91 DOT_ORDER,
92 DOT_REQUIRE
93} arg_dot = DOT_ALL;
e4b61340 94
f4579ce7
LP
95static bool private_bus = false;
96
ee5762e3
LP
97static int daemon_reload(DBusConnection *bus, char **args, unsigned n);
98
2cc59dbf
LP
99static const char *ansi_highlight(bool b) {
100 static int t = -1;
101
102 if (_unlikely_(t < 0))
103 t = isatty(STDOUT_FILENO) > 0;
104
105 if (!t)
106 return "";
107
108 return b ? ANSI_HIGHLIGHT_ON : ANSI_HIGHLIGHT_OFF;
109}
110
e4b61340
LP
111static bool error_is_no_service(DBusError *error) {
112
514f4ef5
LP
113 assert(error);
114
e4b61340
LP
115 if (!dbus_error_is_set(error))
116 return false;
117
118 if (dbus_error_has_name(error, DBUS_ERROR_NAME_HAS_NO_OWNER))
119 return true;
120
121 if (dbus_error_has_name(error, DBUS_ERROR_SERVICE_UNKNOWN))
122 return true;
123
124 return startswith(error->name, "org.freedesktop.DBus.Error.Spawn.");
125}
7e4249b9
LP
126
127static int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) {
128
514f4ef5
LP
129 assert(iter);
130 assert(data);
131
7e4249b9
LP
132 if (dbus_message_iter_get_arg_type(iter) != type)
133 return -EIO;
134
135 dbus_message_iter_get_basic(iter, data);
136
137 if (!dbus_message_iter_next(iter) != !next)
138 return -EIO;
139
140 return 0;
141}
142
514f4ef5 143static void warn_wall(enum action action) {
ef2f1067 144 static const char *table[_ACTION_MAX] = {
514f4ef5
LP
145 [ACTION_HALT] = "The system is going down for system halt NOW!",
146 [ACTION_REBOOT] = "The system is going down for reboot NOW!",
147 [ACTION_POWEROFF] = "The system is going down for power-off NOW!",
148 [ACTION_RESCUE] = "The system is going down to rescue mode NOW!",
149 [ACTION_EMERGENCY] = "The system is going down to emergency mode NOW!"
ef2f1067
LP
150 };
151
514f4ef5
LP
152 if (arg_no_wall)
153 return;
154
e4a9373f
LP
155 if (arg_wall) {
156 char *p;
157
158 if (!(p = strv_join(arg_wall, " "))) {
159 log_error("Failed to join strings.");
160 return;
161 }
162
163 if (*p) {
164 utmp_wall(p);
165 free(p);
166 return;
167 }
168
169 free(p);
170 }
171
514f4ef5 172 if (!table[action])
ef2f1067
LP
173 return;
174
514f4ef5 175 utmp_wall(table[action]);
ef2f1067
LP
176}
177
7e4249b9
LP
178static int list_units(DBusConnection *bus, char **args, unsigned n) {
179 DBusMessage *m = NULL, *reply = NULL;
180 DBusError error;
181 int r;
182 DBusMessageIter iter, sub, sub2;
183 unsigned k = 0;
184
185 dbus_error_init(&error);
186
514f4ef5
LP
187 assert(bus);
188
7e4249b9
LP
189 if (!(m = dbus_message_new_method_call(
190 "org.freedesktop.systemd1",
191 "/org/freedesktop/systemd1",
192 "org.freedesktop.systemd1.Manager",
193 "ListUnits"))) {
194 log_error("Could not allocate message.");
195 return -ENOMEM;
196 }
197
198 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
199 log_error("Failed to issue method call: %s", error.message);
200 r = -EIO;
201 goto finish;
202 }
203
204 if (!dbus_message_iter_init(reply, &iter) ||
205 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
206 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
207 log_error("Failed to parse reply.");
208 r = -EIO;
209 goto finish;
210 }
211
212 dbus_message_iter_recurse(&iter, &sub);
213
d0642824
LP
214 if (isatty(STDOUT_FILENO))
215 printf("%-45s %-6s %-12s %-12s %-15s %s\n", "UNIT", "LOAD", "ACTIVE", "SUB", "JOB", "DESCRIPTION");
7e4249b9
LP
216
217 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
8fe914ec 218 const char *id, *description, *load_state, *active_state, *sub_state, *following, *unit_path, *job_type, *job_path, *dot;
7e4249b9
LP
219 uint32_t job_id;
220
221 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
222 log_error("Failed to parse reply.");
223 r = -EIO;
224 goto finish;
225 }
226
227 dbus_message_iter_recurse(&sub, &sub2);
228
229 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
230 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &description, true) < 0 ||
231 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 ||
232 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 ||
233 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 ||
8fe914ec 234 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &following, true) < 0 ||
4445a875 235 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, true) < 0 ||
7e4249b9
LP
236 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &job_id, true) < 0 ||
237 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &job_type, true) < 0 ||
238 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, false) < 0) {
239 log_error("Failed to parse reply.");
240 r = -EIO;
241 goto finish;
242 }
243
244 if ((!arg_type || ((dot = strrchr(id, '.')) &&
245 streq(dot+1, arg_type))) &&
8fe914ec
LP
246 (arg_all || !(streq(active_state, "inactive") || following[0]) || job_id > 0)) {
247 char *e;
7e4249b9
LP
248 int a = 0, b = 0;
249
e0376b17 250 if (streq(active_state, "maintenance"))
2cc59dbf 251 fputs(ansi_highlight(true), stdout);
e0376b17 252
8fe914ec
LP
253 e = arg_full ? NULL : ellipsize(id, 45, 33);
254 printf("%-45s %-6s %-12s %-12s%n", e ? e : id, load_state, active_state, sub_state, &a);
255 free(e);
7e4249b9
LP
256
257 if (job_id != 0)
0ff3dea7 258 printf(" => %-12s%n", job_type, &b);
7e4249b9 259 else
e0376b17 260 b = 1 + 15;
7e4249b9
LP
261
262 if (a + b + 2 < columns()) {
263 if (job_id == 0)
264 printf(" ");
265
e0376b17 266 printf(" %.*s", columns() - a - b - 2, description);
7e4249b9
LP
267 }
268
e0376b17 269 if (streq(active_state, "maintenance"))
2cc59dbf 270 fputs(ansi_highlight(false), stdout);
e0376b17 271
7e4249b9
LP
272 fputs("\n", stdout);
273 k++;
274 }
275
276 dbus_message_iter_next(&sub);
277 }
278
d0642824 279 if (isatty(STDOUT_FILENO)) {
0ff3dea7 280
d0642824
LP
281 printf("\nLOAD = Load State, reflects whether the unit configuration was properly loaded.\n"
282 "ACTIVE = Active State, the high-level unit activation state, i.e. generalization of the substate.\n"
283 "SUB = Substate, the low-level unit activation state, possible values depend on unit type.\n"
284 "JOB = Job, shows pending jobs for the unit.\n");
285
286 if (arg_all)
287 printf("\n%u units listed.\n", k);
288 else
289 printf("\n%u units listed. Pass --all to see inactive units, too.\n", k);
290 }
4445a875
LP
291
292 r = 0;
293
294finish:
295 if (m)
296 dbus_message_unref(m);
297
298 if (reply)
299 dbus_message_unref(reply);
300
301 dbus_error_free(&error);
302
303 return r;
304}
305
306static int dot_one_property(const char *name, const char *prop, DBusMessageIter *iter) {
307 static const char * const colors[] = {
308 "Requires", "[color=\"black\"]",
309 "RequiresOverridable", "[color=\"black\"]",
310 "Requisite", "[color=\"darkblue\"]",
311 "RequisiteOverridable", "[color=\"darkblue\"]",
312 "Wants", "[color=\"darkgrey\"]",
313 "Conflicts", "[color=\"red\"]",
314 "After", "[color=\"green\"]"
315 };
316
317 const char *c = NULL;
318 unsigned i;
319
320 assert(name);
321 assert(prop);
322 assert(iter);
323
324 for (i = 0; i < ELEMENTSOF(colors); i += 2)
325 if (streq(colors[i], prop)) {
326 c = colors[i+1];
327 break;
328 }
329
330 if (!c)
331 return 0;
332
333 if (arg_dot != DOT_ALL)
334 if ((arg_dot == DOT_ORDER) != streq(prop, "After"))
335 return 0;
336
337 switch (dbus_message_iter_get_arg_type(iter)) {
338
339 case DBUS_TYPE_ARRAY:
340
341 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
342 DBusMessageIter sub;
343
344 dbus_message_iter_recurse(iter, &sub);
345
346 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
347 const char *s;
348
349 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
350 dbus_message_iter_get_basic(&sub, &s);
351 printf("\t\"%s\"->\"%s\" %s;\n", name, s, c);
352
353 dbus_message_iter_next(&sub);
354 }
355
356 return 0;
357 }
358 }
359
360 return 0;
361}
362
363static int dot_one(DBusConnection *bus, const char *name, const char *path) {
364 DBusMessage *m = NULL, *reply = NULL;
365 const char *interface = "org.freedesktop.systemd1.Unit";
366 int r;
367 DBusError error;
368 DBusMessageIter iter, sub, sub2, sub3;
369
370 assert(bus);
371 assert(path);
372
373 dbus_error_init(&error);
374
375 if (!(m = dbus_message_new_method_call(
376 "org.freedesktop.systemd1",
377 path,
378 "org.freedesktop.DBus.Properties",
379 "GetAll"))) {
380 log_error("Could not allocate message.");
381 r = -ENOMEM;
382 goto finish;
383 }
384
385 if (!dbus_message_append_args(m,
386 DBUS_TYPE_STRING, &interface,
387 DBUS_TYPE_INVALID)) {
388 log_error("Could not append arguments to message.");
389 r = -ENOMEM;
390 goto finish;
391 }
392
393 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
394 log_error("Failed to issue method call: %s", error.message);
395 r = -EIO;
396 goto finish;
397 }
398
399 if (!dbus_message_iter_init(reply, &iter) ||
400 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
401 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
402 log_error("Failed to parse reply.");
403 r = -EIO;
404 goto finish;
405 }
406
407 dbus_message_iter_recurse(&iter, &sub);
408
409 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
410 const char *prop;
411
412 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
413 log_error("Failed to parse reply.");
414 r = -EIO;
415 goto finish;
416 }
417
418 dbus_message_iter_recurse(&sub, &sub2);
419
420 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &prop, true) < 0) {
421 log_error("Failed to parse reply.");
422 r = -EIO;
423 goto finish;
424 }
425
426 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
427 log_error("Failed to parse reply.");
428 r = -EIO;
429 goto finish;
430 }
431
432 dbus_message_iter_recurse(&sub2, &sub3);
433
434 if (dot_one_property(name, prop, &sub3)) {
435 log_error("Failed to parse reply.");
436 r = -EIO;
437 goto finish;
438 }
439
440 dbus_message_iter_next(&sub);
441 }
442
443finish:
444 if (m)
445 dbus_message_unref(m);
446
447 if (reply)
448 dbus_message_unref(reply);
449
450 dbus_error_free(&error);
451
452 return r;
453}
454
455static int dot(DBusConnection *bus, char **args, unsigned n) {
456 DBusMessage *m = NULL, *reply = NULL;
457 DBusError error;
458 int r;
459 DBusMessageIter iter, sub, sub2;
460
461 dbus_error_init(&error);
462
463 assert(bus);
464
465 if (!(m = dbus_message_new_method_call(
466 "org.freedesktop.systemd1",
467 "/org/freedesktop/systemd1",
468 "org.freedesktop.systemd1.Manager",
469 "ListUnits"))) {
470 log_error("Could not allocate message.");
471 return -ENOMEM;
472 }
473
474 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
475 log_error("Failed to issue method call: %s", error.message);
476 r = -EIO;
477 goto finish;
478 }
479
480 if (!dbus_message_iter_init(reply, &iter) ||
481 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
482 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
483 log_error("Failed to parse reply.");
484 r = -EIO;
485 goto finish;
486 }
487
488 printf("digraph systemd {\n");
489
490 dbus_message_iter_recurse(&iter, &sub);
491 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
4a4d6b4b 492 const char *id, *description, *load_state, *active_state, *sub_state, *following, *unit_path;
4445a875
LP
493
494 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
495 log_error("Failed to parse reply.");
496 r = -EIO;
497 goto finish;
498 }
499
500 dbus_message_iter_recurse(&sub, &sub2);
501
502 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
503 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &description, true) < 0 ||
504 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 ||
505 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 ||
506 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 ||
4a4d6b4b 507 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &following, true) < 0 ||
4445a875
LP
508 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, true) < 0) {
509 log_error("Failed to parse reply.");
510 r = -EIO;
511 goto finish;
512 }
513
514 if ((r = dot_one(bus, id, unit_path)) < 0)
515 goto finish;
516
517 /* printf("\t\"%s\";\n", id); */
518 dbus_message_iter_next(&sub);
519 }
520
521 printf("}\n");
522
523 log_info(" Color legend: black = Requires\n"
524 " dark blue = Requisite\n"
525 " dark grey = Wants\n"
526 " red = Conflicts\n"
527 " green = After\n");
528
529 if (isatty(fileno(stdout)))
530 log_notice("-- You probably want to process this output with graphviz' dot tool.\n"
531 "-- Try a shell pipeline like 'systemctl dot | dot -Tsvg > systemd.svg'!\n");
7e4249b9
LP
532
533 r = 0;
534
535finish:
536 if (m)
537 dbus_message_unref(m);
538
539 if (reply)
540 dbus_message_unref(reply);
541
542 dbus_error_free(&error);
543
544 return r;
545}
546
547static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
548 DBusMessage *m = NULL, *reply = NULL;
549 DBusError error;
550 int r;
551 DBusMessageIter iter, sub, sub2;
552 unsigned k = 0;
553
554 dbus_error_init(&error);
555
514f4ef5
LP
556 assert(bus);
557
7e4249b9
LP
558 if (!(m = dbus_message_new_method_call(
559 "org.freedesktop.systemd1",
560 "/org/freedesktop/systemd1",
561 "org.freedesktop.systemd1.Manager",
562 "ListJobs"))) {
563 log_error("Could not allocate message.");
564 return -ENOMEM;
565 }
566
567 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
568 log_error("Failed to issue method call: %s", error.message);
569 r = -EIO;
570 goto finish;
571 }
572
573 if (!dbus_message_iter_init(reply, &iter) ||
574 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
575 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
576 log_error("Failed to parse reply.");
577 r = -EIO;
578 goto finish;
579 }
580
581 dbus_message_iter_recurse(&iter, &sub);
582
583 printf("%4s %-45s %-17s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
584
585 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
586 const char *name, *type, *state, *job_path, *unit_path;
587 uint32_t id;
8fe914ec 588 char *e;
7e4249b9
LP
589
590 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
591 log_error("Failed to parse reply.");
592 r = -EIO;
593 goto finish;
594 }
595
596 dbus_message_iter_recurse(&sub, &sub2);
597
598 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &id, true) < 0 ||
599 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
600 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
601 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, true) < 0 ||
602 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, true) < 0 ||
603 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, false) < 0) {
604 log_error("Failed to parse reply.");
605 r = -EIO;
606 goto finish;
607 }
608
8fe914ec
LP
609 e = arg_full ? NULL : ellipsize(name, 45, 33);
610 printf("%4u %-45s %-17s %-7s\n", id, e ? e : name, type, state);
611 free(e);
612
7e4249b9
LP
613 k++;
614
615 dbus_message_iter_next(&sub);
616 }
617
618 printf("\n%u jobs listed.\n", k);
619 r = 0;
620
621finish:
622 if (m)
623 dbus_message_unref(m);
624
625 if (reply)
626 dbus_message_unref(reply);
627
628 dbus_error_free(&error);
629
630 return r;
631}
632
633static int load_unit(DBusConnection *bus, char **args, unsigned n) {
634 DBusMessage *m = NULL, *reply = NULL;
635 DBusError error;
636 int r;
637 unsigned i;
638
639 dbus_error_init(&error);
640
514f4ef5
LP
641 assert(bus);
642 assert(args);
643
7e4249b9
LP
644 for (i = 1; i < n; i++) {
645
646 if (!(m = dbus_message_new_method_call(
647 "org.freedesktop.systemd1",
648 "/org/freedesktop/systemd1",
649 "org.freedesktop.systemd1.Manager",
650 "LoadUnit"))) {
651 log_error("Could not allocate message.");
652 r = -ENOMEM;
653 goto finish;
654 }
655
656 if (!dbus_message_append_args(m,
657 DBUS_TYPE_STRING, &args[i],
658 DBUS_TYPE_INVALID)) {
659 log_error("Could not append arguments to message.");
660 r = -ENOMEM;
661 goto finish;
662 }
663
664 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
665 log_error("Failed to issue method call: %s", error.message);
666 r = -EIO;
667 goto finish;
668 }
669
670 dbus_message_unref(m);
671 dbus_message_unref(reply);
672
673 m = reply = NULL;
674 }
675
676 r = 0;
677
678finish:
679 if (m)
680 dbus_message_unref(m);
681
682 if (reply)
683 dbus_message_unref(reply);
684
685 dbus_error_free(&error);
686
687 return r;
688}
689
690static int cancel_job(DBusConnection *bus, char **args, unsigned n) {
691 DBusMessage *m = NULL, *reply = NULL;
692 DBusError error;
693 int r;
694 unsigned i;
695
696 dbus_error_init(&error);
697
514f4ef5
LP
698 assert(bus);
699 assert(args);
700
ee5762e3
LP
701 if (n <= 1)
702 return daemon_reload(bus, args, n);
703
7e4249b9
LP
704 for (i = 1; i < n; i++) {
705 unsigned id;
706 const char *path;
707
708 if (!(m = dbus_message_new_method_call(
709 "org.freedesktop.systemd1",
710 "/org/freedesktop/systemd1",
711 "org.freedesktop.systemd1.Manager",
712 "GetJob"))) {
713 log_error("Could not allocate message.");
714 r = -ENOMEM;
715 goto finish;
716 }
717
718 if ((r = safe_atou(args[i], &id)) < 0) {
719 log_error("Failed to parse job id: %s", strerror(-r));
720 goto finish;
721 }
722
723 assert_cc(sizeof(uint32_t) == sizeof(id));
724 if (!dbus_message_append_args(m,
725 DBUS_TYPE_UINT32, &id,
726 DBUS_TYPE_INVALID)) {
727 log_error("Could not append arguments to message.");
728 r = -ENOMEM;
729 goto finish;
730 }
731
732 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
733 log_error("Failed to issue method call: %s", error.message);
734 r = -EIO;
735 goto finish;
736 }
737
738 if (!dbus_message_get_args(reply, &error,
739 DBUS_TYPE_OBJECT_PATH, &path,
740 DBUS_TYPE_INVALID)) {
741 log_error("Failed to parse reply: %s", error.message);
742 r = -EIO;
743 goto finish;
744 }
745
746 dbus_message_unref(m);
747 if (!(m = dbus_message_new_method_call(
748 "org.freedesktop.systemd1",
749 path,
750 "org.freedesktop.systemd1.Job",
751 "Cancel"))) {
752 log_error("Could not allocate message.");
753 r = -ENOMEM;
754 goto finish;
755 }
756
757 dbus_message_unref(reply);
758 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
759 log_error("Failed to issue method call: %s", error.message);
760 r = -EIO;
761 goto finish;
762 }
763
764 dbus_message_unref(m);
765 dbus_message_unref(reply);
766 m = reply = NULL;
767 }
768
769 r = 0;
770
771finish:
772 if (m)
773 dbus_message_unref(m);
774
775 if (reply)
776 dbus_message_unref(reply);
777
778 dbus_error_free(&error);
779
780 return r;
781}
782
ee5762e3 783static bool need_daemon_reload(DBusConnection *bus, const char *unit) {
45fb0699
LP
784 DBusMessage *m, *reply;
785 dbus_bool_t b = FALSE;
786 DBusMessageIter iter, sub;
787 const char
788 *interface = "org.freedesktop.systemd1.Unit",
789 *property = "NeedDaemonReload",
790 *path;
791
792 /* We ignore all errors here, since this is used to show a warning only */
793
794 if (!(m = dbus_message_new_method_call(
795 "org.freedesktop.systemd1",
796 "/org/freedesktop/systemd1",
797 "org.freedesktop.systemd1.Manager",
798 "GetUnit")))
799 goto finish;
800
801 if (!dbus_message_append_args(m,
802 DBUS_TYPE_STRING, &unit,
803 DBUS_TYPE_INVALID))
804 goto finish;
805
806 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL)))
807 goto finish;
808
809 if (!dbus_message_get_args(reply, NULL,
810 DBUS_TYPE_OBJECT_PATH, &path,
811 DBUS_TYPE_INVALID))
812 goto finish;
813
814 dbus_message_unref(m);
815 if (!(m = dbus_message_new_method_call(
816 "org.freedesktop.systemd1",
817 path,
818 "org.freedesktop.DBus.Properties",
819 "Get")))
820 goto finish;
821
822 if (!dbus_message_append_args(m,
823 DBUS_TYPE_STRING, &interface,
824 DBUS_TYPE_STRING, &property,
825 DBUS_TYPE_INVALID)) {
826 goto finish;
827 }
828
829 dbus_message_unref(reply);
830 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, NULL)))
831 goto finish;
832
833 if (!dbus_message_iter_init(reply, &iter) ||
834 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
835 goto finish;
836
837 dbus_message_iter_recurse(&iter, &sub);
838
839 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_BOOLEAN)
840 goto finish;
841
842 dbus_message_iter_get_basic(&sub, &b);
843
844finish:
845 if (m)
846 dbus_message_unref(m);
847
848 if (reply)
849 dbus_message_unref(reply);
850
851 return b;
852}
853
5e374895
LP
854typedef struct WaitData {
855 Set *set;
856 bool failed;
857} WaitData;
858
7e4249b9
LP
859static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) {
860 DBusError error;
5e374895 861 WaitData *d = data;
7e4249b9
LP
862
863 assert(connection);
864 assert(message);
5e374895 865 assert(d);
7e4249b9
LP
866
867 dbus_error_init(&error);
868
54165a39
LP
869 log_debug("Got D-Bus request: %s.%s() on %s",
870 dbus_message_get_interface(message),
871 dbus_message_get_member(message),
872 dbus_message_get_path(message));
7e4249b9
LP
873
874 if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
875 log_error("Warning! D-Bus connection terminated.");
876 dbus_connection_close(connection);
877
878 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
879 uint32_t id;
880 const char *path;
5e374895 881 dbus_bool_t success = true;
7e4249b9
LP
882
883 if (!dbus_message_get_args(message, &error,
884 DBUS_TYPE_UINT32, &id,
885 DBUS_TYPE_OBJECT_PATH, &path,
5e374895 886 DBUS_TYPE_BOOLEAN, &success,
7e4249b9
LP
887 DBUS_TYPE_INVALID))
888 log_error("Failed to parse message: %s", error.message);
889 else {
890 char *p;
891
5e374895 892 if ((p = set_remove(d->set, (char*) path)))
7e4249b9 893 free(p);
5e374895
LP
894
895 if (!success)
896 d->failed = true;
7e4249b9
LP
897 }
898 }
899
900 dbus_error_free(&error);
901 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
902}
903
479ef5d3 904static int enable_wait_for_jobs(DBusConnection *bus) {
7e4249b9 905 DBusError error;
7e4249b9
LP
906
907 assert(bus);
7e4249b9 908
f4579ce7
LP
909 if (private_bus)
910 return 0;
911
7e4249b9 912 dbus_error_init(&error);
7e4249b9
LP
913 dbus_bus_add_match(bus,
914 "type='signal',"
915 "sender='org.freedesktop.systemd1',"
916 "interface='org.freedesktop.systemd1.Manager',"
917 "member='JobRemoved',"
918 "path='/org/freedesktop/systemd1'",
919 &error);
920
921 if (dbus_error_is_set(&error)) {
922 log_error("Failed to add match: %s", error.message);
a567261a
LP
923 dbus_error_free(&error);
924 return -EIO;
7e4249b9
LP
925 }
926
479ef5d3 927 /* This is slightly dirty, since we don't undo the match registrations. */
a567261a 928 return 0;
7e4249b9
LP
929}
930
479ef5d3
LP
931static int wait_for_jobs(DBusConnection *bus, Set *s) {
932 int r;
5e374895 933 WaitData d;
479ef5d3
LP
934
935 assert(bus);
936 assert(s);
937
5e374895
LP
938 zero(d);
939 d.set = s;
940 d.failed = false;
941
942 if (!dbus_connection_add_filter(bus, wait_filter, &d, NULL)) {
479ef5d3
LP
943 log_error("Failed to add filter.");
944 r = -ENOMEM;
945 goto finish;
946 }
947
948 while (!set_isempty(s) &&
949 dbus_connection_read_write_dispatch(bus, -1))
950 ;
951
5e374895
LP
952 if (!arg_quiet && d.failed)
953 log_error("Job failed, see logs for details.");
954
955 r = d.failed ? -EIO : 0;
479ef5d3
LP
956
957finish:
958 /* This is slightly dirty, since we don't undo the filter registration. */
959
960 return r;
961}
962
e4b61340
LP
963static int start_unit_one(
964 DBusConnection *bus,
965 const char *method,
966 const char *name,
967 const char *mode,
968 Set *s) {
7e4249b9 969
7e4249b9
LP
970 DBusMessage *m = NULL, *reply = NULL;
971 DBusError error;
45fb0699 972 const char *path;
7e4249b9 973 int r;
7e4249b9 974
e4b61340
LP
975 assert(bus);
976 assert(method);
977 assert(name);
978 assert(mode);
6e905d93 979 assert(arg_no_block || s);
7e4249b9 980
e4b61340 981 dbus_error_init(&error);
479ef5d3 982
7e4249b9
LP
983 if (!(m = dbus_message_new_method_call(
984 "org.freedesktop.systemd1",
985 "/org/freedesktop/systemd1",
986 "org.freedesktop.systemd1.Manager",
e4b61340 987 method))) {
7e4249b9
LP
988 log_error("Could not allocate message.");
989 r = -ENOMEM;
990 goto finish;
991 }
992
993 if (!dbus_message_append_args(m,
e4b61340 994 DBUS_TYPE_STRING, &name,
7e4249b9
LP
995 DBUS_TYPE_STRING, &mode,
996 DBUS_TYPE_INVALID)) {
997 log_error("Could not append arguments to message.");
998 r = -ENOMEM;
999 goto finish;
1000 }
1001
1002 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
e4b61340
LP
1003
1004 if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) {
1005 /* There's always a fallback possible for
1006 * legacy actions. */
1007 r = 0;
1008 goto finish;
1009 }
1010
7e4249b9
LP
1011 log_error("Failed to issue method call: %s", error.message);
1012 r = -EIO;
1013 goto finish;
1014 }
1015
45fb0699
LP
1016 if (!dbus_message_get_args(reply, &error,
1017 DBUS_TYPE_OBJECT_PATH, &path,
1018 DBUS_TYPE_INVALID)) {
1019 log_error("Failed to parse reply: %s", error.message);
1020 r = -EIO;
1021 goto finish;
1022 }
1023
ee5762e3 1024 if (need_daemon_reload(bus, name))
45fb0699
LP
1025 log_warning("Unit file of created job changed on disk, 'systemctl %s daemon-reload' recommended.",
1026 arg_session ? "--session" : "--system");
1027
6e905d93 1028 if (!arg_no_block) {
e4b61340 1029 char *p;
7e4249b9 1030
7e4249b9
LP
1031 if (!(p = strdup(path))) {
1032 log_error("Failed to duplicate path.");
1033 r = -ENOMEM;
1034 goto finish;
1035 }
1036
1037 if ((r = set_put(s, p)) < 0) {
e4b61340 1038 free(p);
7e4249b9
LP
1039 log_error("Failed to add path to set.");
1040 goto finish;
1041 }
e4b61340 1042 }
7e4249b9 1043
e4b61340 1044 r = 1;
7e4249b9
LP
1045
1046finish:
7e4249b9
LP
1047 if (m)
1048 dbus_message_unref(m);
1049
1050 if (reply)
1051 dbus_message_unref(reply);
1052
1053 dbus_error_free(&error);
1054
1055 return r;
1056}
1057
514f4ef5
LP
1058static enum action verb_to_action(const char *verb) {
1059 if (streq(verb, "halt"))
1060 return ACTION_HALT;
1061 else if (streq(verb, "poweroff"))
1062 return ACTION_POWEROFF;
1063 else if (streq(verb, "reboot"))
1064 return ACTION_REBOOT;
1065 else if (streq(verb, "rescue"))
1066 return ACTION_RESCUE;
1067 else if (streq(verb, "emergency"))
1068 return ACTION_EMERGENCY;
1069 else if (streq(verb, "default"))
1070 return ACTION_DEFAULT;
1071 else
1072 return ACTION_INVALID;
1073}
1074
e4b61340
LP
1075static int start_unit(DBusConnection *bus, char **args, unsigned n) {
1076
1077 static const char * const table[_ACTION_MAX] = {
514f4ef5
LP
1078 [ACTION_HALT] = SPECIAL_HALT_TARGET,
1079 [ACTION_POWEROFF] = SPECIAL_POWEROFF_TARGET,
1080 [ACTION_REBOOT] = SPECIAL_REBOOT_TARGET,
1081 [ACTION_RUNLEVEL2] = SPECIAL_RUNLEVEL2_TARGET,
1082 [ACTION_RUNLEVEL3] = SPECIAL_RUNLEVEL3_TARGET,
1083 [ACTION_RUNLEVEL4] = SPECIAL_RUNLEVEL4_TARGET,
1084 [ACTION_RUNLEVEL5] = SPECIAL_RUNLEVEL5_TARGET,
1085 [ACTION_RESCUE] = SPECIAL_RESCUE_TARGET,
f057408c 1086 [ACTION_EMERGENCY] = SPECIAL_EMERGENCY_TARGET,
514f4ef5 1087 [ACTION_DEFAULT] = SPECIAL_DEFAULT_TARGET
e4b61340
LP
1088 };
1089
1090 int r;
1091 unsigned i;
514f4ef5 1092 const char *method, *mode, *one_name;
e4b61340
LP
1093 Set *s = NULL;
1094
514f4ef5
LP
1095 assert(bus);
1096
e4b61340
LP
1097 if (arg_action == ACTION_SYSTEMCTL) {
1098 method =
6f28c033
LP
1099 streq(args[0], "stop") ? "StopUnit" :
1100 streq(args[0], "reload") ? "ReloadUnit" :
1101 streq(args[0], "restart") ? "RestartUnit" :
1102 streq(args[0], "try-restart") ? "TryRestartUnit" :
1103 streq(args[0], "reload-or-restart") ? "ReloadOrRestartUnit" :
9d8a57ff
LP
1104 streq(args[0], "reload-or-try-restart") ||
1105 streq(args[0], "force-reload") ||
1106 streq(args[0], "condrestart") ? "ReloadOrTryRestartUnit" :
6f28c033 1107 "StartUnit";
e4b61340
LP
1108
1109 mode =
514f4ef5
LP
1110 (streq(args[0], "isolate") ||
1111 streq(args[0], "rescue") ||
1112 streq(args[0], "emergency")) ? "isolate" :
90d473a1
LP
1113 arg_fail ? "fail" :
1114 "replace";
e4b61340 1115
514f4ef5 1116 one_name = table[verb_to_action(args[0])];
e4b61340 1117
e4b61340
LP
1118 } else {
1119 assert(arg_action < ELEMENTSOF(table));
1120 assert(table[arg_action]);
1121
1122 method = "StartUnit";
514f4ef5
LP
1123
1124 mode = (arg_action == ACTION_EMERGENCY ||
1125 arg_action == ACTION_RESCUE) ? "isolate" : "replace";
1126
1127 one_name = table[arg_action];
1128 }
1129
6e905d93 1130 if (!arg_no_block) {
514f4ef5
LP
1131 if ((r = enable_wait_for_jobs(bus)) < 0) {
1132 log_error("Could not watch jobs: %s", strerror(-r));
1133 goto finish;
1134 }
1135
1136 if (!(s = set_new(string_hash_func, string_compare_func))) {
1137 log_error("Failed to allocate set.");
1138 r = -ENOMEM;
1139 goto finish;
1140 }
e4b61340
LP
1141 }
1142
1143 r = 0;
1144
514f4ef5
LP
1145 if (one_name) {
1146 if ((r = start_unit_one(bus, method, one_name, mode, s)) <= 0)
1147 goto finish;
1148 } else {
e4b61340
LP
1149 for (i = 1; i < n; i++)
1150 if ((r = start_unit_one(bus, method, args[i], mode, s)) < 0)
1151 goto finish;
e4b61340
LP
1152 }
1153
6e905d93 1154 if (!arg_no_block)
514f4ef5
LP
1155 r = wait_for_jobs(bus, s);
1156
e4b61340
LP
1157finish:
1158 if (s)
1159 set_free_free(s);
1160
1161 return r;
1162}
1163
514f4ef5 1164static int start_special(DBusConnection *bus, char **args, unsigned n) {
983d9c90
LP
1165 int r;
1166
514f4ef5
LP
1167 assert(bus);
1168 assert(args);
1169
983d9c90
LP
1170 r = start_unit(bus, args, n);
1171
1172 if (r >= 0)
1173 warn_wall(verb_to_action(args[0]));
514f4ef5 1174
983d9c90 1175 return r;
514f4ef5
LP
1176}
1177
0183528f
LP
1178static int check_unit(DBusConnection *bus, char **args, unsigned n) {
1179 DBusMessage *m = NULL, *reply = NULL;
1180 const char
1181 *interface = "org.freedesktop.systemd1.Unit",
1182 *property = "ActiveState";
1183 int r = -EADDRNOTAVAIL;
1184 DBusError error;
1185 unsigned i;
1186
1187 assert(bus);
1188 assert(args);
1189
1190 dbus_error_init(&error);
1191
1192 for (i = 1; i < n; i++) {
1193 const char *path = NULL;
1194 const char *state;
1195 DBusMessageIter iter, sub;
1196
1197 if (!(m = dbus_message_new_method_call(
1198 "org.freedesktop.systemd1",
1199 "/org/freedesktop/systemd1",
1200 "org.freedesktop.systemd1.Manager",
1201 "GetUnit"))) {
1202 log_error("Could not allocate message.");
1203 r = -ENOMEM;
1204 goto finish;
1205 }
1206
1207 if (!dbus_message_append_args(m,
1208 DBUS_TYPE_STRING, &args[i],
1209 DBUS_TYPE_INVALID)) {
1210 log_error("Could not append arguments to message.");
1211 r = -ENOMEM;
1212 goto finish;
1213 }
1214
1215 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1216
1217 /* Hmm, cannot figure out anything about this unit... */
1218 if (!arg_quiet)
1219 puts("unknown");
1220
ed2d7a44 1221 dbus_error_free(&error);
0183528f
LP
1222 continue;
1223 }
1224
1225 if (!dbus_message_get_args(reply, &error,
1226 DBUS_TYPE_OBJECT_PATH, &path,
1227 DBUS_TYPE_INVALID)) {
1228 log_error("Failed to parse reply: %s", error.message);
1229 r = -EIO;
1230 goto finish;
1231 }
1232
1233 dbus_message_unref(m);
1234 if (!(m = dbus_message_new_method_call(
1235 "org.freedesktop.systemd1",
1236 path,
1237 "org.freedesktop.DBus.Properties",
1238 "Get"))) {
1239 log_error("Could not allocate message.");
1240 r = -ENOMEM;
1241 goto finish;
1242 }
1243
1244 if (!dbus_message_append_args(m,
1245 DBUS_TYPE_STRING, &interface,
1246 DBUS_TYPE_STRING, &property,
1247 DBUS_TYPE_INVALID)) {
1248 log_error("Could not append arguments to message.");
1249 r = -ENOMEM;
1250 goto finish;
1251 }
1252
1253 dbus_message_unref(reply);
1254 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1255 log_error("Failed to issue method call: %s", error.message);
1256 r = -EIO;
1257 goto finish;
1258 }
1259
1260 if (!dbus_message_iter_init(reply, &iter) ||
1261 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
1262 log_error("Failed to parse reply.");
1263 r = -EIO;
1264 goto finish;
1265 }
1266
1267 dbus_message_iter_recurse(&iter, &sub);
1268
1269 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
1270 log_error("Failed to parse reply.");
1271 r = -EIO;
1272 goto finish;
1273 }
1274
1275 dbus_message_iter_get_basic(&sub, &state);
1276
1277 if (!arg_quiet)
1278 puts(state);
1279
01b1b079 1280 if (streq(state, "active") || startswith(state, "reloading"))
0183528f
LP
1281 r = 0;
1282
1283 dbus_message_unref(m);
1284 dbus_message_unref(reply);
1285 m = reply = NULL;
1286 }
1287
1288finish:
1289 if (m)
1290 dbus_message_unref(m);
1291
1292 if (reply)
1293 dbus_message_unref(reply);
1294
1295 dbus_error_free(&error);
1296
1297 return r;
48220598
LP
1298}
1299
582a507f
LP
1300typedef struct ExecStatusInfo {
1301 char *path;
1302 char **argv;
1303
b708e7ce
LP
1304 bool ignore;
1305
582a507f
LP
1306 usec_t start_timestamp;
1307 usec_t exit_timestamp;
1308 pid_t pid;
1309 int code;
1310 int status;
1311
1312 LIST_FIELDS(struct ExecStatusInfo, exec);
1313} ExecStatusInfo;
1314
1315static void exec_status_info_free(ExecStatusInfo *i) {
1316 assert(i);
1317
1318 free(i->path);
1319 strv_free(i->argv);
1320 free(i);
1321}
1322
1323static int exec_status_info_deserialize(DBusMessageIter *sub, ExecStatusInfo *i) {
1324 uint64_t start_timestamp, exit_timestamp;
1325 DBusMessageIter sub2, sub3;
1326 const char*path;
1327 unsigned n;
1328 uint32_t pid;
1329 int32_t code, status;
b708e7ce 1330 dbus_bool_t ignore;
582a507f
LP
1331
1332 assert(i);
1333 assert(i);
1334
1335 if (dbus_message_iter_get_arg_type(sub) != DBUS_TYPE_STRUCT)
1336 return -EIO;
1337
1338 dbus_message_iter_recurse(sub, &sub2);
1339
1340 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, true) < 0)
1341 return -EIO;
1342
1343 if (!(i->path = strdup(path)))
1344 return -ENOMEM;
1345
1346 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_ARRAY ||
1347 dbus_message_iter_get_element_type(&sub2) != DBUS_TYPE_STRING)
1348 return -EIO;
1349
1350 n = 0;
1351 dbus_message_iter_recurse(&sub2, &sub3);
1352 while (dbus_message_iter_get_arg_type(&sub3) != DBUS_TYPE_INVALID) {
1353 assert(dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_STRING);
1354 dbus_message_iter_next(&sub3);
1355 n++;
1356 }
1357
1358
1359 if (!(i->argv = new0(char*, n+1)))
1360 return -ENOMEM;
1361
1362 n = 0;
1363 dbus_message_iter_recurse(&sub2, &sub3);
1364 while (dbus_message_iter_get_arg_type(&sub3) != DBUS_TYPE_INVALID) {
1365 const char *s;
1366
1367 assert(dbus_message_iter_get_arg_type(&sub3) == DBUS_TYPE_STRING);
1368 dbus_message_iter_get_basic(&sub3, &s);
1369 dbus_message_iter_next(&sub3);
1370
1371 if (!(i->argv[n++] = strdup(s)))
1372 return -ENOMEM;
1373 }
1374
1375 if (!dbus_message_iter_next(&sub2) ||
b708e7ce 1376 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_BOOLEAN, &ignore, true) < 0 ||
582a507f
LP
1377 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &start_timestamp, true) < 0 ||
1378 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &exit_timestamp, true) < 0 ||
1379 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &pid, true) < 0 ||
1380 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &code, true) < 0 ||
1381 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_INT32, &status, false) < 0)
1382 return -EIO;
1383
b708e7ce 1384 i->ignore = ignore;
582a507f
LP
1385 i->start_timestamp = (usec_t) start_timestamp;
1386 i->exit_timestamp = (usec_t) exit_timestamp;
1387 i->pid = (pid_t) pid;
1388 i->code = code;
1389 i->status = status;
1390
1391 return 0;
1392}
1393
61cbdc4b
LP
1394typedef struct UnitStatusInfo {
1395 const char *id;
1396 const char *load_state;
1397 const char *active_state;
1398 const char *sub_state;
1399
1400 const char *description;
1401
1402 const char *fragment_path;
1403 const char *default_control_group;
1404
45fb0699
LP
1405 bool need_daemon_reload;
1406
61cbdc4b
LP
1407 /* Service */
1408 pid_t main_pid;
1409 pid_t control_pid;
1410 const char *status_text;
1411 bool running;
1412
1413 usec_t start_timestamp;
1414 usec_t exit_timestamp;
1415
1416 int exit_code, exit_status;
1417
1418 /* Socket */
1419 unsigned n_accepted;
1420 unsigned n_connections;
b8131a87 1421 bool accept;
61cbdc4b
LP
1422
1423 /* Device */
1424 const char *sysfs_path;
1425
1426 /* Mount, Automount */
1427 const char *where;
1428
1429 /* Swap */
1430 const char *what;
582a507f
LP
1431
1432 LIST_HEAD(ExecStatusInfo, exec);
61cbdc4b
LP
1433} UnitStatusInfo;
1434
1435static void print_status_info(UnitStatusInfo *i) {
582a507f
LP
1436 ExecStatusInfo *p;
1437
61cbdc4b
LP
1438 assert(i);
1439
1440 /* This shows pretty information about a unit. See
1441 * print_property() for a low-level property printer */
1442
1443 printf("%s", strna(i->id));
1444
1445 if (i->description && !streq_ptr(i->id, i->description))
1446 printf(" - %s", i->description);
1447
1448 printf("\n");
1449
1450 if (i->fragment_path)
1451 printf("\t Loaded: %s (%s)\n", strna(i->load_state), i->fragment_path);
1452 else if (streq_ptr(i->load_state, "failed"))
2cc59dbf
LP
1453 printf("\t Loaded: %s%s%s\n",
1454 ansi_highlight(true),
1455 strna(i->load_state),
1456 ansi_highlight(false));
61cbdc4b
LP
1457 else
1458 printf("\t Loaded: %s\n", strna(i->load_state));
1459
582a507f
LP
1460 if (streq_ptr(i->active_state, "maintenance")) {
1461 if (streq_ptr(i->active_state, i->sub_state))
2cc59dbf
LP
1462 printf("\t Active: %s%s%s\n",
1463 ansi_highlight(true),
1464 strna(i->active_state),
1465 ansi_highlight(false));
582a507f 1466 else
2cc59dbf
LP
1467 printf("\t Active: %s%s (%s)%s\n",
1468 ansi_highlight(true),
582a507f 1469 strna(i->active_state),
2cc59dbf
LP
1470 strna(i->sub_state),
1471 ansi_highlight(false));
582a507f
LP
1472 } else {
1473 if (streq_ptr(i->active_state, i->sub_state))
1474 printf("\t Active: %s\n",
1475 strna(i->active_state));
1476 else
1477 printf("\t Active: %s (%s)\n",
1478 strna(i->active_state),
1479 strna(i->sub_state));
1480 }
61cbdc4b
LP
1481
1482 if (i->sysfs_path)
1483 printf("\t Device: %s\n", i->sysfs_path);
1484 else if (i->where)
1485 printf("\t Where: %s\n", i->where);
1486 else if (i->what)
1487 printf("\t What: %s\n", i->what);
1488
b8131a87 1489 if (i->accept)
61cbdc4b
LP
1490 printf("\tAccepted: %u; Connected: %u\n", i->n_accepted, i->n_connections);
1491
582a507f
LP
1492 LIST_FOREACH(exec, p, i->exec) {
1493 char *t;
1494
1495 /* Only show exited processes here */
1496 if (p->code == 0)
1497 continue;
1498
1499 t = strv_join(p->argv, " ");
1500 printf("\t Exited: %u (%s, code=%s, ", p->pid, strna(t), sigchld_code_to_string(p->code));
1501 free(t);
1502
1503 if (p->code == CLD_EXITED)
1504 printf("status=%i", p->status);
1505 else
1506 printf("signal=%s", signal_to_string(p->status));
1507 printf(")\n");
1508
1509 if (i->main_pid == p->pid &&
1510 i->start_timestamp == p->start_timestamp &&
1511 i->exit_timestamp == p->start_timestamp)
1512 /* Let's not show this twice */
1513 i->main_pid = 0;
1514
1515 if (p->pid == i->control_pid)
1516 i->control_pid = 0;
1517 }
1518
61cbdc4b
LP
1519 if (i->main_pid > 0 || i->control_pid > 0) {
1520 printf("\t");
1521
1522 if (i->main_pid > 0) {
582a507f 1523 printf(" Main: %u", (unsigned) i->main_pid);
61cbdc4b
LP
1524
1525 if (i->running) {
1526 char *t = NULL;
1527 get_process_name(i->main_pid, &t);
1528 if (t) {
1529 printf(" (%s)", t);
1530 free(t);
1531 }
1532 } else {
1533 printf(" (code=%s, ", sigchld_code_to_string(i->exit_code));
1534
1535 if (i->exit_code == CLD_EXITED)
1536 printf("status=%i", i->exit_status);
1537 else
582a507f 1538 printf("signal=%s", signal_to_string(i->exit_status));
2cc59dbf 1539 printf(")"); }
61cbdc4b
LP
1540 }
1541
1542 if (i->main_pid > 0 && i->control_pid > 0)
1543 printf(";");
1544
1545 if (i->control_pid > 0) {
1546 char *t = NULL;
1547
1548 printf(" Control: %u", (unsigned) i->control_pid);
1549
1550 get_process_name(i->control_pid, &t);
1551 if (t) {
1552 printf(" (%s)", t);
1553 free(t);
1554 }
1555 }
1556
1557 printf("\n");
1558 }
1559
17bb7382
LP
1560 if (i->status_text)
1561 printf("\t Status: \"%s\"\n", i->status_text);
1562
c59760ee 1563 if (i->default_control_group) {
ab35fb1b
LP
1564 unsigned c;
1565
61cbdc4b 1566 printf("\t CGroup: %s\n", i->default_control_group);
ab35fb1b
LP
1567
1568 if ((c = columns()) > 18)
1569 c -= 18;
1570 else
1571 c = 0;
1572
35d2e7ec 1573 show_cgroup_by_path(i->default_control_group, "\t\t ", c);
c59760ee 1574 }
45fb0699
LP
1575
1576 if (i->need_daemon_reload)
2cc59dbf
LP
1577 printf("\n%sWarning:%s Unit file changed on disk, 'systemctl %s daemon-reload' recommended.\n",
1578 ansi_highlight(true),
1579 ansi_highlight(false),
45fb0699 1580 arg_session ? "--session" : "--system");
61cbdc4b
LP
1581}
1582
1583static int status_property(const char *name, DBusMessageIter *iter, UnitStatusInfo *i) {
1584
1585 switch (dbus_message_iter_get_arg_type(iter)) {
1586
1587 case DBUS_TYPE_STRING: {
1588 const char *s;
1589
1590 dbus_message_iter_get_basic(iter, &s);
1591
1592 if (s[0]) {
1593 if (streq(name, "Id"))
1594 i->id = s;
1595 else if (streq(name, "LoadState"))
1596 i->load_state = s;
1597 else if (streq(name, "ActiveState"))
1598 i->active_state = s;
1599 else if (streq(name, "SubState"))
1600 i->sub_state = s;
1601 else if (streq(name, "Description"))
1602 i->description = s;
1603 else if (streq(name, "FragmentPath"))
1604 i->fragment_path = s;
1605 else if (streq(name, "DefaultControlGroup"))
1606 i->default_control_group = s;
1607 else if (streq(name, "StatusText"))
1608 i->status_text = s;
1609 else if (streq(name, "SysFSPath"))
1610 i->sysfs_path = s;
1611 else if (streq(name, "Where"))
1612 i->where = s;
1613 else if (streq(name, "What"))
1614 i->what = s;
1615 }
1616
1617 break;
1618 }
1619
b8131a87
LP
1620 case DBUS_TYPE_BOOLEAN: {
1621 dbus_bool_t b;
1622
1623 dbus_message_iter_get_basic(iter, &b);
1624
1625 if (streq(name, "Accept"))
1626 i->accept = b;
45fb0699
LP
1627 else if (streq(name, "NeedDaemonReload"))
1628 i->need_daemon_reload = b;
b8131a87
LP
1629
1630 break;
1631 }
1632
61cbdc4b
LP
1633 case DBUS_TYPE_UINT32: {
1634 uint32_t u;
1635
1636 dbus_message_iter_get_basic(iter, &u);
1637
1638 if (streq(name, "MainPID")) {
1639 if (u > 0) {
1640 i->main_pid = (pid_t) u;
1641 i->running = true;
1642 }
1643 } else if (streq(name, "ControlPID"))
1644 i->control_pid = (pid_t) u;
1645 else if (streq(name, "ExecMainPID")) {
1646 if (u > 0)
1647 i->main_pid = (pid_t) u;
1648 } else if (streq(name, "NAccepted"))
1649 i->n_accepted = u;
1650 else if (streq(name, "NConnections"))
1651 i->n_connections = u;
1652
1653 break;
1654 }
1655
1656 case DBUS_TYPE_INT32: {
1657 int32_t j;
1658
1659 dbus_message_iter_get_basic(iter, &j);
1660
1661 if (streq(name, "ExecMainCode"))
1662 i->exit_code = (int) j;
1663 else if (streq(name, "ExecMainStatus"))
1664 i->exit_status = (int) j;
1665
1666 break;
1667 }
1668
1669 case DBUS_TYPE_UINT64: {
1670 uint64_t u;
1671
1672 dbus_message_iter_get_basic(iter, &u);
1673
1674 if (streq(name, "ExecMainStartTimestamp"))
1675 i->start_timestamp = (usec_t) u;
1676 else if (streq(name, "ExecMainExitTimestamp"))
1677 i->exit_timestamp = (usec_t) u;
1678
1679 break;
1680 }
582a507f
LP
1681
1682 case DBUS_TYPE_ARRAY: {
1683
1684 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT &&
1685 startswith(name, "Exec")) {
1686 DBusMessageIter sub;
1687
1688 dbus_message_iter_recurse(iter, &sub);
1689 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
1690 ExecStatusInfo *info;
1691 int r;
1692
1693 if (!(info = new0(ExecStatusInfo, 1)))
1694 return -ENOMEM;
1695
1696 if ((r = exec_status_info_deserialize(&sub, info)) < 0) {
1697 free(info);
1698 return r;
1699 }
1700
1701 LIST_PREPEND(ExecStatusInfo, exec, i->exec, info);
1702
1703 dbus_message_iter_next(&sub);
1704 }
1705 }
1706
1707 break;
1708 }
61cbdc4b
LP
1709 }
1710
1711 return 0;
1712}
1713
48220598
LP
1714static int print_property(const char *name, DBusMessageIter *iter) {
1715 assert(name);
1716 assert(iter);
1717
61cbdc4b
LP
1718 /* This is a low-level property printer, see
1719 * print_status_info() for the nicer output */
1720
ea4a240d 1721 if (arg_property && !strv_find(arg_property, name))
48220598
LP
1722 return 0;
1723
1724 switch (dbus_message_iter_get_arg_type(iter)) {
1725
1726 case DBUS_TYPE_STRING: {
1727 const char *s;
1728 dbus_message_iter_get_basic(iter, &s);
1729
1730 if (arg_all || s[0])
1731 printf("%s=%s\n", name, s);
1732
1733 return 0;
1734 }
1735
1736 case DBUS_TYPE_BOOLEAN: {
1737 dbus_bool_t b;
1738 dbus_message_iter_get_basic(iter, &b);
1739 printf("%s=%s\n", name, yes_no(b));
1740
1741 return 0;
1742 }
1743
1744 case DBUS_TYPE_UINT64: {
1745 uint64_t u;
1746 dbus_message_iter_get_basic(iter, &u);
1747
1748 /* Yes, heuristics! But we can change this check
1749 * should it turn out to not be sufficient */
1750
14ad1d14 1751 if (strstr(name, "Timestamp")) {
48220598
LP
1752 char timestamp[FORMAT_TIMESTAMP_MAX], *t;
1753
1754 if ((t = format_timestamp(timestamp, sizeof(timestamp), u)) || arg_all)
1755 printf("%s=%s\n", name, strempty(t));
552e4331
LP
1756 } else if (strstr(name, "USec")) {
1757 char timespan[FORMAT_TIMESPAN_MAX];
1758
1759 printf("%s=%s\n", name, format_timespan(timespan, sizeof(timespan), u));
48220598
LP
1760 } else
1761 printf("%s=%llu\n", name, (unsigned long long) u);
1762
1763 return 0;
1764 }
1765
1766 case DBUS_TYPE_UINT32: {
1767 uint32_t u;
1768 dbus_message_iter_get_basic(iter, &u);
1769
5bd07073 1770 if (strstr(name, "UMask") || strstr(name, "Mode"))
48220598
LP
1771 printf("%s=%04o\n", name, u);
1772 else
1773 printf("%s=%u\n", name, (unsigned) u);
1774
1775 return 0;
1776 }
1777
1778 case DBUS_TYPE_INT32: {
1779 int32_t i;
1780 dbus_message_iter_get_basic(iter, &i);
1781
1782 printf("%s=%i\n", name, (int) i);
1783 return 0;
1784 }
1785
1786 case DBUS_TYPE_STRUCT: {
1787 DBusMessageIter sub;
1788 dbus_message_iter_recurse(iter, &sub);
1789
ebf57b80 1790 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "Job")) {
48220598
LP
1791 uint32_t u;
1792
1793 dbus_message_iter_get_basic(&sub, &u);
1794
1795 if (u)
1796 printf("%s=%u\n", name, (unsigned) u);
1797 else if (arg_all)
1798 printf("%s=\n", name);
1799
1800 return 0;
ebf57b80 1801 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Unit")) {
48220598
LP
1802 const char *s;
1803
1804 dbus_message_iter_get_basic(&sub, &s);
1805
1806 if (arg_all || s[0])
1807 printf("%s=%s\n", name, s);
1808
1809 return 0;
1810 }
1811
1812 break;
1813 }
1814
1815 case DBUS_TYPE_ARRAY:
1816
1817 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRING) {
1818 DBusMessageIter sub;
1819 bool space = false;
1820
1821 dbus_message_iter_recurse(iter, &sub);
48220598
LP
1822 if (arg_all ||
1823 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1824 printf("%s=", name);
1825
1826 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1827 const char *s;
1828
1829 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING);
1830 dbus_message_iter_get_basic(&sub, &s);
1831 printf("%s%s", space ? " " : "", s);
1832
1833 space = true;
1834 dbus_message_iter_next(&sub);
1835 }
1836
1837 puts("");
1838 }
1839
ebf57b80 1840 return 0;
582a507f 1841
82c121a4
LP
1842 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_BYTE) {
1843 DBusMessageIter sub;
1844
1845 dbus_message_iter_recurse(iter, &sub);
82c121a4
LP
1846 if (arg_all ||
1847 dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1848 printf("%s=", name);
1849
1850 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1851 uint8_t u;
ebf57b80 1852
82c121a4
LP
1853 assert(dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE);
1854 dbus_message_iter_get_basic(&sub, &u);
1855 printf("%02x", u);
1856
1857 dbus_message_iter_next(&sub);
1858 }
1859
1860 puts("");
1861 }
1862
1863 return 0;
582a507f 1864
ebf57b80
LP
1865 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Paths")) {
1866 DBusMessageIter sub, sub2;
1867
1868 dbus_message_iter_recurse(iter, &sub);
ebf57b80
LP
1869 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
1870 const char *type, *path;
1871
1872 dbus_message_iter_recurse(&sub, &sub2);
1873
1874 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) >= 0 &&
1875 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &path, false) >= 0)
1876 printf("%s=%s\n", type, path);
1877
1878 dbus_message_iter_next(&sub);
1879 }
1880
707e5e52 1881 return 0;
582a507f 1882
707e5e52
LP
1883 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Timers")) {
1884 DBusMessageIter sub, sub2;
1885
1886 dbus_message_iter_recurse(iter, &sub);
707e5e52
LP
1887 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
1888 const char *base;
1889 uint64_t value, next_elapse;
1890
1891 dbus_message_iter_recurse(&sub, &sub2);
1892
1893 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &base, true) >= 0 &&
1894 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &value, true) >= 0 &&
552e4331
LP
1895 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT64, &next_elapse, false) >= 0) {
1896 char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX];
1897
1898 printf("%s={ value=%s ; next_elapse=%s }\n",
fe68089d 1899 base,
552e4331
LP
1900 format_timespan(timespan1, sizeof(timespan1), value),
1901 format_timespan(timespan2, sizeof(timespan2), next_elapse));
1902 }
fe68089d
LP
1903
1904 dbus_message_iter_next(&sub);
1905 }
1906
1907 return 0;
fe68089d 1908
582a507f
LP
1909 } else if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && startswith(name, "Exec")) {
1910 DBusMessageIter sub;
fe68089d
LP
1911
1912 dbus_message_iter_recurse(iter, &sub);
fe68089d 1913 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
582a507f 1914 ExecStatusInfo info;
fe68089d 1915
582a507f
LP
1916 zero(info);
1917 if (exec_status_info_deserialize(&sub, &info) >= 0) {
fe68089d 1918 char timestamp1[FORMAT_TIMESTAMP_MAX], timestamp2[FORMAT_TIMESTAMP_MAX];
582a507f
LP
1919 char *t;
1920
1921 t = strv_join(info.argv, " ");
1922
b708e7ce 1923 printf("%s={ path=%s ; argv[]=%s ; ignore=%s ; start_time=[%s] ; stop_time=[%s] ; pid=%u ; code=%s ; status=%i%s%s }\n",
582a507f
LP
1924 name,
1925 strna(info.path),
1926 strna(t),
b708e7ce 1927 yes_no(info.ignore),
582a507f
LP
1928 strna(format_timestamp(timestamp1, sizeof(timestamp1), info.start_timestamp)),
1929 strna(format_timestamp(timestamp2, sizeof(timestamp2), info.exit_timestamp)),
1930 (unsigned) info. pid,
1931 sigchld_code_to_string(info.code),
1932 info.status,
1933 info.code == CLD_EXITED ? "" : "/",
1934 strempty(info.code == CLD_EXITED ? NULL : signal_to_string(info.status)));
fe68089d 1935
582a507f 1936 free(t);
fe68089d
LP
1937 }
1938
582a507f
LP
1939 free(info.path);
1940 strv_free(info.argv);
707e5e52
LP
1941
1942 dbus_message_iter_next(&sub);
1943 }
1944
48220598
LP
1945 return 0;
1946 }
1947
1948 break;
1949 }
1950
1951 if (arg_all)
1952 printf("%s=[unprintable]\n", name);
1953
1954 return 0;
1955}
1956
61cbdc4b 1957static int show_one(DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
48220598
LP
1958 DBusMessage *m = NULL, *reply = NULL;
1959 const char *interface = "";
1960 int r;
1961 DBusError error;
1962 DBusMessageIter iter, sub, sub2, sub3;
61cbdc4b 1963 UnitStatusInfo info;
582a507f 1964 ExecStatusInfo *p;
48220598
LP
1965
1966 assert(bus);
1967 assert(path);
61cbdc4b 1968 assert(new_line);
48220598 1969
61cbdc4b 1970 zero(info);
48220598
LP
1971 dbus_error_init(&error);
1972
1973 if (!(m = dbus_message_new_method_call(
1974 "org.freedesktop.systemd1",
1975 path,
1976 "org.freedesktop.DBus.Properties",
1977 "GetAll"))) {
1978 log_error("Could not allocate message.");
1979 r = -ENOMEM;
1980 goto finish;
1981 }
1982
1983 if (!dbus_message_append_args(m,
1984 DBUS_TYPE_STRING, &interface,
1985 DBUS_TYPE_INVALID)) {
1986 log_error("Could not append arguments to message.");
1987 r = -ENOMEM;
1988 goto finish;
1989 }
1990
1991 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1992 log_error("Failed to issue method call: %s", error.message);
1993 r = -EIO;
1994 goto finish;
1995 }
1996
1997 if (!dbus_message_iter_init(reply, &iter) ||
1998 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
1999 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
2000 log_error("Failed to parse reply.");
2001 r = -EIO;
2002 goto finish;
2003 }
2004
2005 dbus_message_iter_recurse(&iter, &sub);
2006
61cbdc4b
LP
2007 if (*new_line)
2008 printf("\n");
2009
2010 *new_line = true;
2011
48220598
LP
2012 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
2013 const char *name;
2014
2015 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
2016 log_error("Failed to parse reply.");
2017 r = -EIO;
2018 goto finish;
2019 }
2020
2021 dbus_message_iter_recurse(&sub, &sub2);
0183528f 2022
48220598
LP
2023 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
2024 log_error("Failed to parse reply.");
2025 r = -EIO;
2026 goto finish;
2027 }
2028
2029 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
2030 log_error("Failed to parse reply.");
2031 r = -EIO;
2032 goto finish;
2033 }
2034
2035 dbus_message_iter_recurse(&sub2, &sub3);
2036
61cbdc4b
LP
2037 if (show_properties)
2038 r = print_property(name, &sub3);
2039 else
2040 r = status_property(name, &sub3, &info);
2041
2042 if (r < 0) {
48220598
LP
2043 log_error("Failed to parse reply.");
2044 r = -EIO;
2045 goto finish;
2046 }
2047
2048 dbus_message_iter_next(&sub);
2049 }
2050
61cbdc4b
LP
2051 if (!show_properties)
2052 print_status_info(&info);
2053
582a507f
LP
2054 while ((p = info.exec)) {
2055 LIST_REMOVE(ExecStatusInfo, exec, info.exec, p);
2056 exec_status_info_free(p);
2057 }
2058
48220598
LP
2059 r = 0;
2060
2061finish:
2062 if (m)
2063 dbus_message_unref(m);
2064
2065 if (reply)
2066 dbus_message_unref(reply);
2067
2068 dbus_error_free(&error);
2069
2070 return r;
2071}
2072
2073static int show(DBusConnection *bus, char **args, unsigned n) {
2074 DBusMessage *m = NULL, *reply = NULL;
2075 int r;
2076 DBusError error;
2077 unsigned i;
61cbdc4b 2078 bool show_properties, new_line = false;
48220598
LP
2079
2080 assert(bus);
2081 assert(args);
2082
2083 dbus_error_init(&error);
2084
61cbdc4b
LP
2085 show_properties = !streq(args[0], "status");
2086
2087 if (show_properties && n <= 1) {
48220598
LP
2088 /* If not argument is specified inspect the manager
2089 * itself */
2090
61cbdc4b 2091 r = show_one(bus, "/org/freedesktop/systemd1", show_properties, &new_line);
48220598
LP
2092 goto finish;
2093 }
2094
2095 for (i = 1; i < n; i++) {
2096 const char *path = NULL;
2097 uint32_t id;
2098
61cbdc4b 2099 if (!show_properties || safe_atou32(args[i], &id) < 0) {
48220598
LP
2100
2101 if (!(m = dbus_message_new_method_call(
2102 "org.freedesktop.systemd1",
2103 "/org/freedesktop/systemd1",
2104 "org.freedesktop.systemd1.Manager",
e87d1818 2105 "LoadUnit"))) {
48220598
LP
2106 log_error("Could not allocate message.");
2107 r = -ENOMEM;
2108 goto finish;
2109 }
2110
2111 if (!dbus_message_append_args(m,
2112 DBUS_TYPE_STRING, &args[i],
2113 DBUS_TYPE_INVALID)) {
2114 log_error("Could not append arguments to message.");
2115 r = -ENOMEM;
2116 goto finish;
2117 }
2118
ed2d7a44
LP
2119 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2120
2121 if (!dbus_error_has_name(&error, DBUS_ERROR_ACCESS_DENIED)) {
2122 log_error("Failed to issue method call: %s", error.message);
2123 r = -EIO;
2124 goto finish;
2125 }
2126
2127 dbus_error_free(&error);
2128
2129 dbus_message_unref(m);
2130 if (!(m = dbus_message_new_method_call(
2131 "org.freedesktop.systemd1",
2132 "/org/freedesktop/systemd1",
2133 "org.freedesktop.systemd1.Manager",
2134 "GetUnit"))) {
2135 log_error("Could not allocate message.");
2136 r = -ENOMEM;
2137 goto finish;
2138 }
2139
2140 if (!dbus_message_append_args(m,
2141 DBUS_TYPE_STRING, &args[i],
2142 DBUS_TYPE_INVALID)) {
2143 log_error("Could not append arguments to message.");
2144 r = -ENOMEM;
2145 goto finish;
2146 }
2147
2148 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2149 log_error("Failed to issue method call: %s", error.message);
2150 r = -EIO;
2151 goto finish;
2152 }
2153 }
2154
48220598
LP
2155 } else {
2156
2157 if (!(m = dbus_message_new_method_call(
2158 "org.freedesktop.systemd1",
2159 "/org/freedesktop/systemd1",
2160 "org.freedesktop.systemd1.Manager",
2161 "GetJob"))) {
2162 log_error("Could not allocate message.");
2163 r = -ENOMEM;
2164 goto finish;
2165 }
2166
2167 if (!dbus_message_append_args(m,
2168 DBUS_TYPE_UINT32, &id,
2169 DBUS_TYPE_INVALID)) {
2170 log_error("Could not append arguments to message.");
2171 r = -ENOMEM;
2172 goto finish;
2173 }
48220598 2174
ed2d7a44
LP
2175 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2176 log_error("Failed to issue method call: %s", error.message);
2177 r = -EIO;
2178 goto finish;
2179 }
48220598
LP
2180 }
2181
2182 if (!dbus_message_get_args(reply, &error,
2183 DBUS_TYPE_OBJECT_PATH, &path,
2184 DBUS_TYPE_INVALID)) {
2185 log_error("Failed to parse reply: %s", error.message);
2186 r = -EIO;
2187 goto finish;
2188 }
2189
61cbdc4b 2190 if ((r = show_one(bus, path, show_properties, &new_line)) < 0)
48220598
LP
2191 goto finish;
2192
2193 dbus_message_unref(m);
2194 dbus_message_unref(reply);
2195 m = reply = NULL;
2196 }
2197
2198 r = 0;
2199
2200finish:
2201 if (m)
2202 dbus_message_unref(m);
2203
2204 if (reply)
2205 dbus_message_unref(reply);
2206
2207 dbus_error_free(&error);
2208
2209 return r;
0183528f
LP
2210}
2211
7e4249b9
LP
2212static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage *message, void *data) {
2213 DBusError error;
2214 DBusMessage *m = NULL, *reply = NULL;
2215
2216 assert(connection);
2217 assert(message);
2218
2219 dbus_error_init(&error);
2220
54165a39
LP
2221 log_debug("Got D-Bus request: %s.%s() on %s",
2222 dbus_message_get_interface(message),
2223 dbus_message_get_member(message),
2224 dbus_message_get_path(message));
7e4249b9
LP
2225
2226 if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
2227 log_error("Warning! D-Bus connection terminated.");
2228 dbus_connection_close(connection);
2229
2230 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitNew") ||
2231 dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
2232 const char *id, *path;
2233
2234 if (!dbus_message_get_args(message, &error,
2235 DBUS_TYPE_STRING, &id,
2236 DBUS_TYPE_OBJECT_PATH, &path,
2237 DBUS_TYPE_INVALID))
2238 log_error("Failed to parse message: %s", error.message);
2239 else if (streq(dbus_message_get_member(message), "UnitNew"))
2240 printf("Unit %s added.\n", id);
2241 else
2242 printf("Unit %s removed.\n", id);
2243
2244 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobNew") ||
2245 dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
2246 uint32_t id;
2247 const char *path;
2248
2249 if (!dbus_message_get_args(message, &error,
2250 DBUS_TYPE_UINT32, &id,
2251 DBUS_TYPE_OBJECT_PATH, &path,
2252 DBUS_TYPE_INVALID))
2253 log_error("Failed to parse message: %s", error.message);
2254 else if (streq(dbus_message_get_member(message), "JobNew"))
2255 printf("Job %u added.\n", id);
2256 else
2257 printf("Job %u removed.\n", id);
2258
2259
2260 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Unit", "Changed") ||
2261 dbus_message_is_signal(message, "org.freedesktop.systemd1.Job", "Changed")) {
2262
2263 const char *path, *interface, *property = "Id";
2264 DBusMessageIter iter, sub;
2265
2266 path = dbus_message_get_path(message);
2267 interface = dbus_message_get_interface(message);
2268
2269 if (!(m = dbus_message_new_method_call(
2270 "org.freedesktop.systemd1",
2271 path,
2272 "org.freedesktop.DBus.Properties",
2273 "Get"))) {
2274 log_error("Could not allocate message.");
2275 goto oom;
2276 }
2277
2278 if (!dbus_message_append_args(m,
2279 DBUS_TYPE_STRING, &interface,
2280 DBUS_TYPE_STRING, &property,
2281 DBUS_TYPE_INVALID)) {
2282 log_error("Could not append arguments to message.");
2283 goto finish;
2284 }
2285
2286 if (!(reply = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
2287 log_error("Failed to issue method call: %s", error.message);
2288 goto finish;
2289 }
2290
2291 if (!dbus_message_iter_init(reply, &iter) ||
2292 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
2293 log_error("Failed to parse reply.");
2294 goto finish;
2295 }
2296
2297 dbus_message_iter_recurse(&iter, &sub);
2298
2299 if (streq(interface, "org.freedesktop.systemd1.Unit")) {
2300 const char *id;
2301
2302 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
2303 log_error("Failed to parse reply.");
2304 goto finish;
2305 }
2306
2307 dbus_message_iter_get_basic(&sub, &id);
2308 printf("Unit %s changed.\n", id);
2309 } else {
2310 uint32_t id;
2311
2312 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT32) {
2313 log_error("Failed to parse reply.");
2314 goto finish;
2315 }
2316
2317 dbus_message_iter_get_basic(&sub, &id);
2318 printf("Job %u changed.\n", id);
2319 }
2320 }
2321
2322finish:
2323 if (m)
2324 dbus_message_unref(m);
2325
2326 if (reply)
2327 dbus_message_unref(reply);
2328
2329 dbus_error_free(&error);
2330 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2331
2332oom:
2333 if (m)
2334 dbus_message_unref(m);
2335
2336 if (reply)
2337 dbus_message_unref(reply);
2338
2339 dbus_error_free(&error);
2340 return DBUS_HANDLER_RESULT_NEED_MEMORY;
2341}
2342
2343static int monitor(DBusConnection *bus, char **args, unsigned n) {
2344 DBusMessage *m = NULL, *reply = NULL;
2345 DBusError error;
2346 int r;
2347
2348 dbus_error_init(&error);
2349
f4579ce7
LP
2350 if (!private_bus) {
2351 dbus_bus_add_match(bus,
2352 "type='signal',"
2353 "sender='org.freedesktop.systemd1',"
2354 "interface='org.freedesktop.systemd1.Manager',"
2355 "path='/org/freedesktop/systemd1'",
2356 &error);
7e4249b9 2357
f4579ce7
LP
2358 if (dbus_error_is_set(&error)) {
2359 log_error("Failed to add match: %s", error.message);
2360 r = -EIO;
2361 goto finish;
2362 }
7e4249b9 2363
f4579ce7
LP
2364 dbus_bus_add_match(bus,
2365 "type='signal',"
2366 "sender='org.freedesktop.systemd1',"
2367 "interface='org.freedesktop.systemd1.Unit',"
2368 "member='Changed'",
2369 &error);
7e4249b9 2370
f4579ce7
LP
2371 if (dbus_error_is_set(&error)) {
2372 log_error("Failed to add match: %s", error.message);
2373 r = -EIO;
2374 goto finish;
2375 }
7e4249b9 2376
f4579ce7
LP
2377 dbus_bus_add_match(bus,
2378 "type='signal',"
2379 "sender='org.freedesktop.systemd1',"
2380 "interface='org.freedesktop.systemd1.Job',"
2381 "member='Changed'",
2382 &error);
7e4249b9 2383
f4579ce7
LP
2384 if (dbus_error_is_set(&error)) {
2385 log_error("Failed to add match: %s", error.message);
2386 r = -EIO;
2387 goto finish;
2388 }
7e4249b9
LP
2389 }
2390
2391 if (!dbus_connection_add_filter(bus, monitor_filter, NULL, NULL)) {
2392 log_error("Failed to add filter.");
2393 r = -ENOMEM;
2394 goto finish;
2395 }
2396
2397 if (!(m = dbus_message_new_method_call(
2398 "org.freedesktop.systemd1",
2399 "/org/freedesktop/systemd1",
2400 "org.freedesktop.systemd1.Manager",
2401 "Subscribe"))) {
2402 log_error("Could not allocate message.");
2403 r = -ENOMEM;
2404 goto finish;
2405 }
2406
2407 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2408 log_error("Failed to issue method call: %s", error.message);
2409 r = -EIO;
2410 goto finish;
2411 }
2412
2413 while (dbus_connection_read_write_dispatch(bus, -1))
2414 ;
2415
2416 r = 0;
2417
2418finish:
2419
2420 /* This is slightly dirty, since we don't undo the filter or the matches. */
2421
2422 if (m)
2423 dbus_message_unref(m);
2424
2425 if (reply)
2426 dbus_message_unref(reply);
2427
2428 dbus_error_free(&error);
2429
2430 return r;
2431}
2432
2433static int dump(DBusConnection *bus, char **args, unsigned n) {
2434 DBusMessage *m = NULL, *reply = NULL;
2435 DBusError error;
2436 int r;
2437 const char *text;
2438
2439 dbus_error_init(&error);
2440
2441 if (!(m = dbus_message_new_method_call(
2442 "org.freedesktop.systemd1",
2443 "/org/freedesktop/systemd1",
2444 "org.freedesktop.systemd1.Manager",
2445 "Dump"))) {
2446 log_error("Could not allocate message.");
2447 return -ENOMEM;
2448 }
2449
2450 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2451 log_error("Failed to issue method call: %s", error.message);
2452 r = -EIO;
2453 goto finish;
2454 }
2455
2456 if (!dbus_message_get_args(reply, &error,
2457 DBUS_TYPE_STRING, &text,
2458 DBUS_TYPE_INVALID)) {
2459 log_error("Failed to parse reply: %s", error.message);
2460 r = -EIO;
2461 goto finish;
2462 }
2463
2464 fputs(text, stdout);
2465
2466 r = 0;
2467
2468finish:
2469 if (m)
2470 dbus_message_unref(m);
2471
2472 if (reply)
2473 dbus_message_unref(reply);
2474
2475 dbus_error_free(&error);
2476
2477 return r;
2478}
2479
2480static int snapshot(DBusConnection *bus, char **args, unsigned n) {
2481 DBusMessage *m = NULL, *reply = NULL;
2482 DBusError error;
2483 int r;
2484 const char *name = "", *path, *id;
2485 dbus_bool_t cleanup = FALSE;
2486 DBusMessageIter iter, sub;
2487 const char
2488 *interface = "org.freedesktop.systemd1.Unit",
2489 *property = "Id";
2490
2491 dbus_error_init(&error);
2492
2493 if (!(m = dbus_message_new_method_call(
2494 "org.freedesktop.systemd1",
2495 "/org/freedesktop/systemd1",
2496 "org.freedesktop.systemd1.Manager",
2497 "CreateSnapshot"))) {
2498 log_error("Could not allocate message.");
2499 return -ENOMEM;
2500 }
2501
2502 if (n > 1)
2503 name = args[1];
2504
2505 if (!dbus_message_append_args(m,
2506 DBUS_TYPE_STRING, &name,
2507 DBUS_TYPE_BOOLEAN, &cleanup,
2508 DBUS_TYPE_INVALID)) {
2509 log_error("Could not append arguments to message.");
2510 r = -ENOMEM;
2511 goto finish;
2512 }
2513
2514 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2515 log_error("Failed to issue method call: %s", error.message);
2516 r = -EIO;
2517 goto finish;
2518 }
2519
2520 if (!dbus_message_get_args(reply, &error,
2521 DBUS_TYPE_OBJECT_PATH, &path,
2522 DBUS_TYPE_INVALID)) {
2523 log_error("Failed to parse reply: %s", error.message);
2524 r = -EIO;
2525 goto finish;
2526 }
2527
2528 dbus_message_unref(m);
2529 if (!(m = dbus_message_new_method_call(
2530 "org.freedesktop.systemd1",
2531 path,
2532 "org.freedesktop.DBus.Properties",
2533 "Get"))) {
2534 log_error("Could not allocate message.");
2535 return -ENOMEM;
2536 }
2537
2538 if (!dbus_message_append_args(m,
2539 DBUS_TYPE_STRING, &interface,
2540 DBUS_TYPE_STRING, &property,
2541 DBUS_TYPE_INVALID)) {
2542 log_error("Could not append arguments to message.");
2543 r = -ENOMEM;
2544 goto finish;
2545 }
2546
2547 dbus_message_unref(reply);
2548 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2549 log_error("Failed to issue method call: %s", error.message);
2550 r = -EIO;
2551 goto finish;
2552 }
2553
2554 if (!dbus_message_iter_init(reply, &iter) ||
2555 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
2556 log_error("Failed to parse reply.");
2557 r = -EIO;
2558 goto finish;
2559 }
2560
2561 dbus_message_iter_recurse(&iter, &sub);
2562
2563 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
2564 log_error("Failed to parse reply.");
2565 r = -EIO;
2566 goto finish;
2567 }
2568
2569 dbus_message_iter_get_basic(&sub, &id);
0183528f
LP
2570
2571 if (!arg_quiet)
2572 puts(id);
7e4249b9
LP
2573 r = 0;
2574
2575finish:
2576 if (m)
2577 dbus_message_unref(m);
2578
2579 if (reply)
2580 dbus_message_unref(reply);
2581
2582 dbus_error_free(&error);
2583
2584 return r;
2585}
2586
6759e7a7
LP
2587static int delete_snapshot(DBusConnection *bus, char **args, unsigned n) {
2588 DBusMessage *m = NULL, *reply = NULL;
2589 int r;
2590 DBusError error;
2591 unsigned i;
2592
2593 assert(bus);
2594 assert(args);
2595
2596 dbus_error_init(&error);
2597
2598 for (i = 1; i < n; i++) {
2599 const char *path = NULL;
2600
2601 if (!(m = dbus_message_new_method_call(
2602 "org.freedesktop.systemd1",
2603 "/org/freedesktop/systemd1",
2604 "org.freedesktop.systemd1.Manager",
2605 "GetUnit"))) {
2606 log_error("Could not allocate message.");
2607 r = -ENOMEM;
2608 goto finish;
2609 }
2610
2611 if (!dbus_message_append_args(m,
2612 DBUS_TYPE_STRING, &args[i],
2613 DBUS_TYPE_INVALID)) {
2614 log_error("Could not append arguments to message.");
2615 r = -ENOMEM;
2616 goto finish;
2617 }
2618
2619 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2620 log_error("Failed to issue method call: %s", error.message);
2621 r = -EIO;
2622 goto finish;
2623 }
2624
2625 if (!dbus_message_get_args(reply, &error,
2626 DBUS_TYPE_OBJECT_PATH, &path,
2627 DBUS_TYPE_INVALID)) {
2628 log_error("Failed to parse reply: %s", error.message);
2629 r = -EIO;
2630 goto finish;
2631 }
2632
2633 dbus_message_unref(m);
2634 if (!(m = dbus_message_new_method_call(
2635 "org.freedesktop.systemd1",
2636 path,
2637 "org.freedesktop.systemd1.Snapshot",
2638 "Remove"))) {
2639 log_error("Could not allocate message.");
2640 r = -ENOMEM;
2641 goto finish;
2642 }
2643
2644 dbus_message_unref(reply);
2645 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2646 log_error("Failed to issue method call: %s", error.message);
2647 r = -EIO;
2648 goto finish;
2649 }
2650
2651 dbus_message_unref(m);
2652 dbus_message_unref(reply);
2653 m = reply = NULL;
2654 }
2655
2656 r = 0;
2657
2658finish:
2659 if (m)
2660 dbus_message_unref(m);
2661
2662 if (reply)
2663 dbus_message_unref(reply);
2664
2665 dbus_error_free(&error);
2666
2667 return r;
2668}
2669
ee5762e3 2670static int daemon_reload(DBusConnection *bus, char **args, unsigned n) {
7e4249b9
LP
2671 DBusMessage *m = NULL, *reply = NULL;
2672 DBusError error;
2673 int r;
2674 const char *method;
2675
2676 dbus_error_init(&error);
2677
e4b61340
LP
2678 if (arg_action == ACTION_RELOAD)
2679 method = "Reload";
2680 else if (arg_action == ACTION_REEXEC)
2681 method = "Reexecute";
2682 else {
2683 assert(arg_action == ACTION_SYSTEMCTL);
2684
2685 method =
ee5762e3
LP
2686 streq(args[0], "clear-jobs") ||
2687 streq(args[0], "cancel") ? "ClearJobs" :
5632e374
LP
2688 streq(args[0], "daemon-reexec") ? "Reexecute" :
2689 streq(args[0], "reset-maintenance") ? "ResetMaintenance" :
ee5762e3
LP
2690 streq(args[0], "daemon-exit") ? "Exit" :
2691 "Reload";
e4b61340 2692 }
7e4249b9
LP
2693
2694 if (!(m = dbus_message_new_method_call(
2695 "org.freedesktop.systemd1",
2696 "/org/freedesktop/systemd1",
2697 "org.freedesktop.systemd1.Manager",
2698 method))) {
2699 log_error("Could not allocate message.");
2700 return -ENOMEM;
2701 }
2702
2703 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
e4b61340
LP
2704
2705 if (arg_action != ACTION_SYSTEMCTL && error_is_no_service(&error)) {
2706 /* There's always a fallback possible for
2707 * legacy actions. */
2708 r = 0;
2709 goto finish;
2710 }
2711
7e4249b9
LP
2712 log_error("Failed to issue method call: %s", error.message);
2713 r = -EIO;
2714 goto finish;
2715 }
2716
e4b61340 2717 r = 1;
7e4249b9
LP
2718
2719finish:
2720 if (m)
2721 dbus_message_unref(m);
2722
2723 if (reply)
2724 dbus_message_unref(reply);
2725
2726 dbus_error_free(&error);
2727
2728 return r;
2729}
2730
5632e374
LP
2731static int reset_maintenance(DBusConnection *bus, char **args, unsigned n) {
2732 DBusMessage *m = NULL, *reply = NULL;
2733 unsigned i;
2734 int r;
2735 DBusError error;
2736
2737 assert(bus);
2738 dbus_error_init(&error);
2739
2740 if (n <= 1)
ee5762e3 2741 return daemon_reload(bus, args, n);
5632e374
LP
2742
2743 for (i = 1; i < n; i++) {
2744
2745 if (!(m = dbus_message_new_method_call(
2746 "org.freedesktop.systemd1",
2747 "/org/freedesktop/systemd1",
2748 "org.freedesktop.systemd1.Manager",
2749 "ResetMaintenanceUnit"))) {
2750 log_error("Could not allocate message.");
2751 r = -ENOMEM;
2752 goto finish;
2753 }
2754
2755 if (!dbus_message_append_args(m,
2756 DBUS_TYPE_STRING, args + i,
2757 DBUS_TYPE_INVALID)) {
2758 log_error("Could not append arguments to message.");
2759 r = -ENOMEM;
2760 goto finish;
2761 }
2762
2763 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2764 log_error("Failed to issue method call: %s", error.message);
2765 r = -EIO;
2766 goto finish;
2767 }
2768
2769 dbus_message_unref(m);
2770 dbus_message_unref(reply);
2771 m = reply = NULL;
2772 }
2773
2774 r = 0;
2775
2776finish:
2777 if (m)
2778 dbus_message_unref(m);
2779
2780 if (reply)
2781 dbus_message_unref(reply);
2782
2783 dbus_error_free(&error);
2784
2785 return r;
2786}
2787
7e4249b9
LP
2788static int show_enviroment(DBusConnection *bus, char **args, unsigned n) {
2789 DBusMessage *m = NULL, *reply = NULL;
2790 DBusError error;
2791 DBusMessageIter iter, sub, sub2;
2792 int r;
2793 const char
2794 *interface = "org.freedesktop.systemd1.Manager",
2795 *property = "Environment";
2796
2797 dbus_error_init(&error);
2798
2799 if (!(m = dbus_message_new_method_call(
2800 "org.freedesktop.systemd1",
2801 "/org/freedesktop/systemd1",
2802 "org.freedesktop.DBus.Properties",
2803 "Get"))) {
2804 log_error("Could not allocate message.");
2805 return -ENOMEM;
2806 }
2807
2808 if (!dbus_message_append_args(m,
2809 DBUS_TYPE_STRING, &interface,
2810 DBUS_TYPE_STRING, &property,
2811 DBUS_TYPE_INVALID)) {
2812 log_error("Could not append arguments to message.");
2813 r = -ENOMEM;
2814 goto finish;
2815 }
2816
2817 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2818 log_error("Failed to issue method call: %s", error.message);
2819 r = -EIO;
2820 goto finish;
2821 }
2822
2823 if (!dbus_message_iter_init(reply, &iter) ||
2824 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
2825 log_error("Failed to parse reply.");
2826 r = -EIO;
2827 goto finish;
2828 }
2829
2830 dbus_message_iter_recurse(&iter, &sub);
2831
2832 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY ||
2833 dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_STRING) {
2834 log_error("Failed to parse reply.");
2835 r = -EIO;
2836 goto finish;
2837 }
2838
2839 dbus_message_iter_recurse(&sub, &sub2);
2840
2841 while (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_INVALID) {
2842 const char *text;
2843
2844 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) {
2845 log_error("Failed to parse reply.");
2846 r = -EIO;
2847 goto finish;
2848 }
2849
2850 dbus_message_iter_get_basic(&sub2, &text);
2851 printf("%s\n", text);
2852
2853 dbus_message_iter_next(&sub2);
2854 }
2855
2856 r = 0;
2857
2858finish:
2859 if (m)
2860 dbus_message_unref(m);
2861
2862 if (reply)
2863 dbus_message_unref(reply);
2864
2865 dbus_error_free(&error);
2866
2867 return r;
2868}
2869
2870static int set_environment(DBusConnection *bus, char **args, unsigned n) {
2871 DBusMessage *m = NULL, *reply = NULL;
2872 DBusError error;
2873 int r;
2874 const char *method;
2875 DBusMessageIter iter, sub;
2876 unsigned i;
2877
2878 dbus_error_init(&error);
2879
2880 method = streq(args[0], "set-environment")
2881 ? "SetEnvironment"
2882 : "UnsetEnvironment";
2883
2884 if (!(m = dbus_message_new_method_call(
2885 "org.freedesktop.systemd1",
2886 "/org/freedesktop/systemd1",
2887 "org.freedesktop.systemd1.Manager",
2888 method))) {
2889
2890 log_error("Could not allocate message.");
2891 return -ENOMEM;
2892 }
2893
2894 dbus_message_iter_init_append(m, &iter);
2895
2896 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
2897 log_error("Could not append arguments to message.");
2898 r = -ENOMEM;
2899 goto finish;
2900 }
2901
2902 for (i = 1; i < n; i++)
2903 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &args[i])) {
2904 log_error("Could not append arguments to message.");
2905 r = -ENOMEM;
2906 goto finish;
2907 }
2908
2909 if (!dbus_message_iter_close_container(&iter, &sub)) {
2910 log_error("Could not append arguments to message.");
2911 r = -ENOMEM;
2912 goto finish;
2913 }
2914
2915 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
2916 log_error("Failed to issue method call: %s", error.message);
2917 r = -EIO;
2918 goto finish;
2919 }
2920
2921 r = 0;
2922
2923finish:
2924 if (m)
2925 dbus_message_unref(m);
2926
2927 if (reply)
2928 dbus_message_unref(reply);
2929
2930 dbus_error_free(&error);
2931
2932 return r;
2933}
2934
ee5762e3
LP
2935typedef struct {
2936 char *name;
2937 char *path;
2938
2939 char **aliases;
2940 char **wanted_by;
2941} InstallInfo;
2942
2943static Hashmap *will_install = NULL, *have_installed = NULL;
2944static Set *remove_symlinks_to = NULL;
2945
2946static void install_info_free(InstallInfo *i) {
2947 assert(i);
2948
2949 free(i->name);
2950 free(i->path);
2951 strv_free(i->aliases);
2952 strv_free(i->wanted_by);
2953 free(i);
2954}
2955
2956static void install_info_hashmap_free(Hashmap *m) {
2957 InstallInfo *i;
2958
2959 while ((i = hashmap_steal_first(m)))
2960 install_info_free(i);
2961
2962 hashmap_free(m);
2963}
2964
2965static bool unit_name_valid(const char *name) {
2966
2967 /* This is a minimal version of unit_name_valid() from
2968 * unit-name.c */
2969
2970 if (!*name)
2971 return false;
2972
2973 if (ignore_file(name))
2974 return false;
2975
2976 return true;
2977}
2978
2979static int install_info_add(const char *name) {
2980 InstallInfo *i;
2981 int r;
2982
2983 assert(will_install);
2984
2985 if (!unit_name_valid(name))
2986 return -EINVAL;
2987
2988 if (hashmap_get(have_installed, name) ||
2989 hashmap_get(will_install, name))
2990 return 0;
2991
2992 if (!(i = new0(InstallInfo, 1))) {
2993 r = -ENOMEM;
2994 goto fail;
2995 }
2996
2997 if (!(i->name = strdup(name))) {
2998 r = -ENOMEM;
2999 goto fail;
3000 }
3001
3002 if ((r = hashmap_put(will_install, i->name, i)) < 0)
3003 goto fail;
3004
3005 return 0;
3006
3007fail:
3008 if (i)
3009 install_info_free(i);
3010
3011 return r;
3012}
3013
3014static int config_parse_also(
3015 const char *filename,
3016 unsigned line,
3017 const char *section,
3018 const char *lvalue,
3019 const char *rvalue,
3020 void *data,
3021 void *userdata) {
3022
3023 char *w;
3024 size_t l;
3025 char *state;
3026
3027 assert(filename);
3028 assert(lvalue);
3029 assert(rvalue);
3030
3031 FOREACH_WORD_QUOTED(w, l, rvalue, state) {
3032 char *n;
3033 int r;
3034
3035 if (!(n = strndup(w, l)))
3036 return -ENOMEM;
3037
3038 r = install_info_add(n);
3039 free(n);
3040
3041 if (r < 0)
3042 return r;
3043 }
3044
3045 return 0;
3046}
3047
3048static int mark_symlink_for_removal(const char *p) {
3049 char *n;
3050 int r;
3051
3052 assert(p);
3053 assert(path_is_absolute(p));
3054
3055 if (!remove_symlinks_to)
3056 return 0;
3057
3058 if (!(n = strdup(p)))
3059 return -ENOMEM;
3060
3061 path_kill_slashes(n);
3062
3063 if ((r = set_put(remove_symlinks_to, n)) < 0) {
3064 free(n);
3065 return r == -EEXIST ? 0 : r;
3066 }
3067
3068 return 0;
3069}
3070
3071static int remove_marked_symlinks_fd(int fd, const char *config_path, const char *root, bool *deleted) {
3072 int r = 0;
3073 DIR *d;
3074 struct dirent *de;
3075
3076 assert(fd >= 0);
3077 assert(root);
3078 assert(deleted);
3079
3080 if (!(d = fdopendir(fd))) {
3081 close_nointr_nofail(fd);
3082 return -errno;
3083 }
3084
3085 rewinddir(d);
3086
3087 while ((de = readdir(d))) {
3088 bool is_dir = false, is_link = false;
3089
3090 if (ignore_file(de->d_name))
3091 continue;
3092
3093 if (de->d_type == DT_LNK)
3094 is_link = true;
3095 else if (de->d_type == DT_DIR)
3096 is_dir = true;
3097 else if (de->d_type == DT_UNKNOWN) {
3098 struct stat st;
3099
3100 if (fstatat(fd, de->d_name, &st, AT_SYMLINK_NOFOLLOW) < 0) {
3101 log_error("Failed to stat %s/%s: %m", root, de->d_name);
3102
3103 if (r == 0)
3104 r = -errno;
3105 continue;
3106 }
3107
3108 is_link = S_ISLNK(st.st_mode);
3109 is_dir = S_ISDIR(st.st_mode);
3110 } else
3111 continue;
3112
3113 if (is_dir) {
3114 int nfd, q;
3115 char *p;
3116
3117 if ((nfd = openat(fd, de->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW)) < 0) {
3118 log_error("Failed to open %s/%s: %m", root, de->d_name);
3119
3120 if (r == 0)
3121 r = -errno;
3122 continue;
3123 }
3124
3125 if (asprintf(&p, "%s/%s", root, de->d_name) < 0) {
3126 log_error("Failed to allocate directory string.");
3127 close_nointr_nofail(nfd);
3128 r = -ENOMEM;
3129 break;
3130 }
3131
3132 /* This will close nfd, regardless whether it succeeds or not */
3133 q = remove_marked_symlinks_fd(nfd, config_path, p, deleted);
3134 free(p);
3135
3136 if (r == 0)
3137 q = r;
3138
3139 } else if (is_link) {
3140 char *p, *dest, *c;
3141 int q;
3142
3143 if (asprintf(&p, "%s/%s", root, de->d_name) < 0) {
3144 log_error("Failed to allocate symlink string.");
3145 r = -ENOMEM;
3146 break;
3147 }
3148
3149 if ((q = readlink_and_make_absolute(p, &dest)) < 0) {
3150 log_error("Cannot read symlink %s: %s", p, strerror(-q));
3151 free(p);
3152
3153 if (r == 0)
3154 r = q;
3155 continue;
3156 }
3157
3158 if ((c = canonicalize_file_name(dest))) {
3159 /* This might fail if the destination
3160 * is already removed */
3161
3162 free(dest);
3163 dest = c;
3164 }
3165
3166 path_kill_slashes(dest);
3167 if (set_get(remove_symlinks_to, dest)) {
3168
3169 if (!arg_quiet)
3170 log_info("rm '%s'", p);
3171
3172 if (unlink(p) < 0) {
3173 log_error("Cannot unlink symlink %s: %m", p);
3174
3175 if (r == 0)
3176 r = -errno;
3177 } else {
3178 rmdir_parents(p, config_path);
3179 path_kill_slashes(p);
3180
3181 if (!set_get(remove_symlinks_to, p)) {
3182
3183 if ((r = mark_symlink_for_removal(p)) < 0) {
3184 if (r == 0)
3185 r = q;
3186 } else
3187 *deleted = true;
3188 }
3189 }
3190 }
3191
3192 free(p);
3193 free(dest);
3194 }
3195 }
3196
3197 closedir(d);
3198
3199 return r;
3200}
3201
3202static int remove_marked_symlinks(const char *config_path) {
3203 int fd, r = 0;
3204 bool deleted;
3205
3206 assert(config_path);
3207
3208 if (set_size(remove_symlinks_to) <= 0)
3209 return 0;
3210
3211 if ((fd = open(config_path, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW)) < 0)
3212 return -errno;
3213
3214 do {
3215 int q, cfd;
3216 deleted = false;
3217
3218 if ((cfd = dup(fd)) < 0) {
3219 r = -errno;
3220 break;
3221 }
3222
3223 /* This takes possession of cfd and closes it */
3224 if ((q = remove_marked_symlinks_fd(cfd, config_path, config_path, &deleted)) < 0) {
3225 if (r == 0)
3226 r = q;
3227 }
3228 } while (deleted);
3229
3230 close_nointr_nofail(fd);
3231
3232 return r;
3233}
3234
3235static int create_symlink(const char *verb, const char *old_path, const char *new_path) {
3236 int r;
3237
3238 assert(old_path);
3239 assert(new_path);
3240 assert(verb);
3241
3242 if (streq(verb, "enable")) {
3243 char *dest;
3244
3245 mkdir_parents(new_path, 0755);
3246
3247 if (symlink(old_path, new_path) >= 0) {
3248
3249 if (!arg_quiet)
3250 log_info("ln -s '%s' '%s'", old_path, new_path);
3251
3252 return 0;
3253 }
3254
3255 if (errno != EEXIST) {
3256 log_error("Cannot link %s to %s: %m", old_path, new_path);
3257 return -errno;
3258 }
3259
3260 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
3261
3262 if (errno == EINVAL) {
3263 log_error("Cannot link %s to %s, file exists already and is not a symlink.", old_path, new_path);
3264 return -EEXIST;
3265 }
3266
3267 log_error("readlink() failed: %s", strerror(-r));
3268 return r;
3269 }
3270
3271 if (streq(dest, old_path)) {
3272 free(dest);
3273 return 0;
3274 }
3275
3276 if (!arg_force) {
3277 log_error("Cannot link %s to %s, symlink exists already and points to %s.", old_path, new_path, dest);
3278 free(dest);
3279 return -EEXIST;
3280 }
3281
3282 free(dest);
3283 unlink(new_path);
3284
3285 if (!arg_quiet)
3286 log_info("ln -s '%s' '%s'", old_path, new_path);
3287
3288 if (symlink(old_path, new_path) >= 0)
3289 return 0;
3290
3291 log_error("Cannot link %s to %s: %m", old_path, new_path);
3292 return -errno;
3293
3294 } else if (streq(verb, "disable")) {
3295 char *dest;
3296
3297 if ((r = mark_symlink_for_removal(old_path)) < 0)
3298 return r;
3299
3300 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
3301 if (errno == ENOENT)
3302 return 0;
3303
3304 if (errno == EINVAL) {
3305 log_warning("File %s not a symlink, ignoring.", old_path);
3306 return 0;
3307 }
3308
3309 log_error("readlink() failed: %s", strerror(-r));
3310 return r;
3311 }
3312
3313 if (!streq(dest, old_path)) {
3314 log_warning("File %s not a symlink to %s but points to %s, ignoring.", new_path, old_path, dest);
3315 free(dest);
3316 return 0;
3317 }
3318
3319 free(dest);
3320
3321 if ((r = mark_symlink_for_removal(new_path)) < 0)
3322 return r;
3323
3324 if (!arg_quiet)
3325 log_info("rm '%s'", new_path);
3326
3327 if (unlink(new_path) >= 0)
3328 return 0;
3329
3330 log_error("Cannot unlink %s: %m", new_path);
3331 return -errno;
3332
3333 } else if (streq(verb, "is-enabled")) {
3334 char *dest;
3335
3336 if ((r = readlink_and_make_absolute(new_path, &dest)) < 0) {
3337
3338 if (errno == ENOENT || errno == EINVAL)
3339 return 0;
3340
3341 log_error("readlink() failed: %s", strerror(-r));
3342 return r;
3343 }
3344
3345 if (streq(dest, old_path)) {
3346 free(dest);
3347 return 1;
3348 }
3349
3350 return 0;
3351 }
3352
3353 assert_not_reached("Unknown action.");
3354}
3355
3356static int install_info_symlink_alias(const char *verb, InstallInfo *i, const char *config_path) {
3357 char **s;
3358 char *alias_path = NULL;
3359 int r;
3360
3361 assert(verb);
3362 assert(i);
3363 assert(config_path);
3364
3365 STRV_FOREACH(s, i->aliases) {
3366
3367 if (!unit_name_valid(*s)) {
3368 log_error("Invalid name %s.", *s);
3369 r = -EINVAL;
3370 goto finish;
3371 }
3372
3373 free(alias_path);
3374 if (!(alias_path = path_make_absolute(*s, config_path))) {
3375 log_error("Out of memory");
3376 r = -ENOMEM;
3377 goto finish;
3378 }
3379
3380 if ((r = create_symlink(verb, i->path, alias_path)) != 0)
3381 goto finish;
3382
3383 if (streq(verb, "disable"))
3384 rmdir_parents(alias_path, config_path);
3385 }
3386
3387 r = 0;
3388
3389finish:
3390 free(alias_path);
3391
3392 return r;
3393}
3394
3395static int install_info_symlink_wants(const char *verb, InstallInfo *i, const char *config_path) {
3396 char **s;
3397 char *alias_path = NULL;
3398 int r;
3399
3400 assert(verb);
3401 assert(i);
3402 assert(config_path);
3403
3404 STRV_FOREACH(s, i->wanted_by) {
3405 if (!unit_name_valid(*s)) {
3406 log_error("Invalid name %s.", *s);
3407 r = -EINVAL;
3408 goto finish;
3409 }
3410
3411 free(alias_path);
3412 alias_path = NULL;
3413
3414 if (asprintf(&alias_path, "%s/%s.wants/%s", config_path, *s, i->name) < 0) {
3415 log_error("Out of memory");
3416 r = -ENOMEM;
3417 goto finish;
3418 }
3419
3420 if ((r = create_symlink(verb, i->path, alias_path)) != 0)
3421 goto finish;
3422
3423 if (streq(verb, "disable"))
3424 rmdir_parents(alias_path, config_path);
3425 }
3426
3427 r = 0;
3428
3429finish:
3430 free(alias_path);
3431
3432 return r;
3433}
3434
3435static int install_info_apply(const char *verb, LookupPaths *paths, InstallInfo *i, const char *config_path) {
3436
3437 const ConfigItem items[] = {
3438 { "Alias", config_parse_strv, &i->aliases, "Install" },
3439 { "WantedBy", config_parse_strv, &i->wanted_by, "Install" },
3440 { "Also", config_parse_also, NULL, "Install" },
3441
3442 { NULL, NULL, NULL, NULL }
3443 };
3444
3445 char **p;
3446 char *filename = NULL;
3447 FILE *f = NULL;
3448 int r;
3449
3450 assert(paths);
3451 assert(i);
3452
3453 STRV_FOREACH(p, paths->unit_path) {
3454 int fd;
3455
3456 if (!(filename = path_make_absolute(i->name, *p))) {
3457 log_error("Out of memory");
3458 return -ENOMEM;
3459 }
3460
3461 /* Ensure that we don't follow symlinks */
3462 if ((fd = open(filename, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NOCTTY)) >= 0)
3463 if ((f = fdopen(fd, "re")))
3464 break;
3465
3466 if (errno == ELOOP) {
3467 log_error("Refusing to operate on symlinks, please pass unit names or absolute paths to unit files.");
3468 free(filename);
3469 return -errno;
3470 }
3471
3472 if (errno != ENOENT) {
3473 log_error("Failed to open %s: %m", filename);
3474 free(filename);
3475 return -errno;
3476 }
3477
3478 free(filename);
3479 filename = NULL;
3480 }
3481
3482 if (!f) {
3483 log_error("Couldn't find %s.", i->name);
3484 return -ENOENT;
3485 }
3486
3487 i->path = filename;
3488
3489 if ((r = config_parse(filename, f, NULL, items, true, i)) < 0) {
3490 fclose(f);
3491 return r;
3492 }
3493
3494 fclose(f);
3495
3496 if ((r = install_info_symlink_alias(verb, i, config_path)) != 0)
3497 return r;
3498
3499 if ((r = install_info_symlink_wants(verb, i, config_path)) != 0)
3500 return r;
3501
3502 if ((r = mark_symlink_for_removal(filename)) < 0)
3503 return r;
3504
3505 if ((r = remove_marked_symlinks(config_path)) < 0)
3506 return r;
3507
3508 return 0;
3509}
3510
3511static char *get_config_path(void) {
3512
3513 if (arg_session && arg_global)
3514 return strdup(SESSION_CONFIG_UNIT_PATH);
3515
3516 if (arg_session) {
3517 char *p;
3518
3519 if (session_config_home(&p) < 0)
3520 return NULL;
3521
3522 return p;
3523 }
3524
3525 return strdup(SYSTEM_CONFIG_UNIT_PATH);
3526}
3527
3528static int enable_unit(DBusConnection *bus, char **args, unsigned n) {
3529 DBusError error;
3530 int r;
3531 LookupPaths paths;
3532 char *config_path = NULL;
3533 unsigned j;
3534 InstallInfo *i;
3535 const char *verb = args[0];
3536
3537 dbus_error_init(&error);
3538
3539 zero(paths);
3540 if ((r = lookup_paths_init(&paths, arg_session ? MANAGER_SESSION : MANAGER_SYSTEM)) < 0) {
3541 log_error("Failed to determine lookup paths: %s", strerror(-r));
3542 goto finish;
3543 }
3544
3545 if (!(config_path = get_config_path())) {
3546 log_error("Failed to determine config path");
3547 r = -ENOMEM;
3548 goto finish;
3549 }
3550
3551 will_install = hashmap_new(string_hash_func, string_compare_func);
3552 have_installed = hashmap_new(string_hash_func, string_compare_func);
3553
3554 if (!will_install || !have_installed) {
3555 log_error("Failed to allocate unit sets.");
3556 r = -ENOMEM;
3557 goto finish;
3558 }
3559
3560 if (!arg_defaults && streq(verb, "disable"))
3561 if (!(remove_symlinks_to = set_new(string_hash_func, string_compare_func))) {
3562 log_error("Failed to allocate symlink sets.");
3563 r = -ENOMEM;
3564 goto finish;
3565 }
3566
3567 for (j = 1; j < n; j++)
3568 if ((r = install_info_add(args[j])) < 0)
3569 goto finish;
3570
3571 while ((i = hashmap_first(will_install))) {
3572 int q;
3573
3574 assert_se(hashmap_move_one(have_installed, will_install, i->name) == 0);
3575
3576 if ((q = install_info_apply(verb, &paths, i, config_path)) != 0) {
3577
3578 if (q < 0) {
3579 if (r == 0)
3580 r = q;
3581 goto finish;
3582 }
3583
3584 /* In test mode and found something */
3585 r = 1;
3586 break;
3587 }
3588 }
3589
3590 if (streq(verb, "is-enabled"))
3591 r = r > 0 ? 0 : -ENOENT;
3592 else if (bus &&
3593 /* Don't try to reload anything if the user asked us to not do this */
3594 !arg_no_reload &&
3595 /* Don't try to reload anything when updating a unit globally */
3596 !arg_global &&
3597 /* Don't try to reload anything if we are called for system changes but the system wasn't booted with systemd */
3598 (arg_session || sd_booted() > 0) &&
3599 /* Don't try to reload anything if we are running in a chroot environment */
3600 (arg_session || running_in_chroot() <= 0) ) {
3601 int q;
3602
3603 if ((q = daemon_reload(bus, args, n)) < 0)
3604 r = q;
3605 }
3606
3607finish:
3608 install_info_hashmap_free(will_install);
3609 install_info_hashmap_free(have_installed);
3610
3611 set_free_free(remove_symlinks_to);
3612
3613 lookup_paths_free(&paths);
3614
3615 free(config_path);
3616
3617 return r;
3618}
3619
e4b61340 3620static int systemctl_help(void) {
7e4249b9 3621
2e33c433 3622 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
5632e374 3623 "Send control commands to or query the systemd manager.\n\n"
48220598
LP
3624 " -h --help Show this help\n"
3625 " -t --type=TYPE List only units of a particular type\n"
393a2f9b 3626 " -p --property=NAME Show only properties by this name\n"
48220598 3627 " -a --all Show all units/properties, including dead/empty ones\n"
ee5762e3
LP
3628 " --full Don't ellipsize unit names on output\n"
3629 " --fail When queueing a new job, fail if conflicting jobs are\n"
6f28c033 3630 " pending\n"
ee5762e3
LP
3631 " -q --quiet Suppress output\n"
3632 " --no-block Do not wait until operation finished\n"
48220598
LP
3633 " --system Connect to system bus\n"
3634 " --session Connect to session bus\n"
4445a875 3635 " --order When generating graph for dot, show only order\n"
ee5762e3
LP
3636 " --require When generating graph for dot, show only requirement\n"
3637 " --no-wall Don't send wall message before halt/power-off/reboot\n"
3638 " --global Enable/disable unit files globally\n"
3639 " --no-reload When enabling/disabling unit files, don't reload daemon\n"
3640 " configuration\n"
3641 " --force When enabling unit files, override existing symlinks\n"
3642 " --defaults When disabling unit files, remove default symlinks only\n\n"
7e4249b9
LP
3643 "Commands:\n"
3644 " list-units List units\n"
ee5762e3
LP
3645 " start [NAME...] Start (activate) one or more units\n"
3646 " stop [NAME...] Stop (deactivate) one or more units\n"
7e4249b9 3647 " reload [NAME...] Reload one or more units\n"
6f28c033
LP
3648 " restart [NAME...] Start or restart one or more units\n"
3649 " try-restart [NAME...] Restart one or more units if active\n"
3650 " reload-or-restart [NAME...] Reload one or more units is possible,\n"
3651 " otherwise start or restart\n"
3652 " reload-or-try-restart [NAME...] Reload one or more units is possible,\n"
3653 " otherwise restart if active\n"
7e4249b9 3654 " isolate [NAME] Start one unit and stop all others\n"
ee5762e3
LP
3655 " is-active [NAME...] Check whether units are active\n"
3656 " status [NAME...] Show runtime status of one or more units\n"
6f28c033 3657 " show [NAME...|JOB...] Show properties of one or more\n"
ee5762e3
LP
3658 " units/jobs or the manager\n"
3659 " reset-maintenance [NAME...] Reset maintenance state for all, one,\n"
5632e374 3660 " or more units\n"
ee5762e3
LP
3661 " enable [NAME...] Enable one or more unit files\n"
3662 " disable [NAME...] Disable one or more unit files\n"
3663 " is-enabled [NAME...] Check whether unit files are enabled\n"
48220598
LP
3664 " load [NAME...] Load one or more units\n"
3665 " list-jobs List jobs\n"
ee5762e3 3666 " cancel [JOB...] Cancel all, one, or more jobs\n"
7e4249b9
LP
3667 " monitor Monitor unit/job changes\n"
3668 " dump Dump server status\n"
4445a875 3669 " dot Dump dependency graph for dot(1)\n"
7e4249b9 3670 " snapshot [NAME] Create a snapshot\n"
6759e7a7 3671 " delete [NAME...] Remove one or more snapshots\n"
5ec7ed4e
LP
3672 " daemon-reload Reload systemd manager configuration\n"
3673 " daemon-reexec Reexecute systemd manager\n"
3674 " daemon-exit Ask the systemd manager to quit\n"
7e4249b9
LP
3675 " show-environment Dump environment\n"
3676 " set-environment [NAME=VALUE...] Set one or more environment variables\n"
514f4ef5
LP
3677 " unset-environment [NAME...] Unset one or more environment variables\n"
3678 " halt Shut down and halt the system\n"
2e33c433 3679 " poweroff Shut down and power-off the system\n"
514f4ef5 3680 " reboot Shut down and reboot the system\n"
6f28c033
LP
3681 " rescue Enter system rescue mode\n"
3682 " emergency Enter system emergency mode\n"
3683 " default Enter system default mode\n",
5b6319dc 3684 program_invocation_short_name);
7e4249b9
LP
3685
3686 return 0;
3687}
3688
e4b61340
LP
3689static int halt_help(void) {
3690
2e33c433 3691 printf("%s [OPTIONS...]\n\n"
e4b61340
LP
3692 "%s the system.\n\n"
3693 " --help Show this help\n"
3694 " --halt Halt the machine\n"
3695 " -p --poweroff Switch off the machine\n"
3696 " --reboot Reboot the machine\n"
2e33c433
LP
3697 " -f --force Force immediate halt/power-off/reboot\n"
3698 " -w --wtmp-only Don't halt/power-off/reboot, just write wtmp record\n"
e4b61340 3699 " -d --no-wtmp Don't write wtmp record\n"
2e33c433
LP
3700 " -n --no-sync Don't sync before halt/power-off/reboot\n"
3701 " --no-wall Don't send wall message before halt/power-off/reboot\n",
e4b61340
LP
3702 program_invocation_short_name,
3703 arg_action == ACTION_REBOOT ? "Reboot" :
3704 arg_action == ACTION_POWEROFF ? "Power off" :
3705 "Halt");
3706
3707 return 0;
3708}
3709
3710static int shutdown_help(void) {
3711
2e33c433 3712 printf("%s [OPTIONS...] [now] [WALL...]\n\n"
e4b61340
LP
3713 "Shut down the system.\n\n"
3714 " --help Show this help\n"
3715 " -H --halt Halt the machine\n"
3716 " -P --poweroff Power-off the machine\n"
3717 " -r --reboot Reboot the machine\n"
3718 " -h Equivalent to --poweroff, overriden by --halt\n"
2e33c433
LP
3719 " -k Don't halt/power-off/reboot, just send warnings\n"
3720 " --no-wall Don't send wall message before halt/power-off/reboot\n",
e4b61340
LP
3721 program_invocation_short_name);
3722
3723 return 0;
3724}
3725
3726static int telinit_help(void) {
3727
2e33c433 3728 printf("%s [OPTIONS...] {COMMAND}\n\n"
514f4ef5
LP
3729 "Send control commands to the init daemon.\n\n"
3730 " --help Show this help\n"
2e33c433 3731 " --no-wall Don't send wall message before halt/power-off/reboot\n\n"
e4b61340
LP
3732 "Commands:\n"
3733 " 0 Power-off the machine\n"
3734 " 6 Reboot the machine\n"
514f4ef5
LP
3735 " 2, 3, 4, 5 Start runlevelX.target unit\n"
3736 " 1, s, S Enter rescue mode\n"
3737 " q, Q Reload init daemon configuration\n"
3738 " u, U Reexecute init daemon\n",
e4b61340
LP
3739 program_invocation_short_name);
3740
3741 return 0;
3742}
3743
3744static int runlevel_help(void) {
3745
2e33c433 3746 printf("%s [OPTIONS...]\n\n"
e4b61340
LP
3747 "Prints the previous and current runlevel of the init system.\n\n"
3748 " --help Show this help\n",
3749 program_invocation_short_name);
3750
3751 return 0;
3752}
3753
3754static int systemctl_parse_argv(int argc, char *argv[]) {
7e4249b9
LP
3755
3756 enum {
90d473a1 3757 ARG_FAIL = 0x100,
7e4249b9
LP
3758 ARG_SESSION,
3759 ARG_SYSTEM,
ee5762e3 3760 ARG_GLOBAL,
6e905d93 3761 ARG_NO_BLOCK,
4445a875
LP
3762 ARG_NO_WALL,
3763 ARG_ORDER,
8fe914ec 3764 ARG_REQUIRE,
ee5762e3
LP
3765 ARG_FULL,
3766 ARG_FORCE,
3767 ARG_NO_RELOAD,
3768 ARG_DEFAULTS
7e4249b9
LP
3769 };
3770
3771 static const struct option options[] = {
ee5762e3
LP
3772 { "help", no_argument, NULL, 'h' },
3773 { "type", required_argument, NULL, 't' },
3774 { "property", required_argument, NULL, 'p' },
3775 { "all", no_argument, NULL, 'a' },
3776 { "full", no_argument, NULL, ARG_FULL },
3777 { "fail", no_argument, NULL, ARG_FAIL },
3778 { "session", no_argument, NULL, ARG_SESSION },
3779 { "system", no_argument, NULL, ARG_SYSTEM },
3780 { "global", no_argument, NULL, ARG_GLOBAL },
3781 { "no-block", no_argument, NULL, ARG_NO_BLOCK },
3782 { "no-wall", no_argument, NULL, ARG_NO_WALL },
3783 { "quiet", no_argument, NULL, 'q' },
3784 { "order", no_argument, NULL, ARG_ORDER },
3785 { "require", no_argument, NULL, ARG_REQUIRE },
3786 { "force", no_argument, NULL, ARG_FORCE },
3787 { "no-reload", no_argument, NULL, ARG_NO_RELOAD },
3788 { "defaults", no_argument, NULL, ARG_DEFAULTS },
3789 { NULL, 0, NULL, 0 }
7e4249b9
LP
3790 };
3791
3792 int c;
3793
e4b61340 3794 assert(argc >= 0);
7e4249b9
LP
3795 assert(argv);
3796
48220598 3797 while ((c = getopt_long(argc, argv, "ht:p:aq", options, NULL)) >= 0) {
7e4249b9
LP
3798
3799 switch (c) {
3800
3801 case 'h':
e4b61340 3802 systemctl_help();
7e4249b9
LP
3803 return 0;
3804
3805 case 't':
3806 arg_type = optarg;
3807 break;
3808
ea4a240d
LP
3809 case 'p': {
3810 char **l;
3811
3812 if (!(l = strv_append(arg_property, optarg)))
3813 return -ENOMEM;
3814
3815 strv_free(arg_property);
3816 arg_property = l;
48220598
LP
3817
3818 /* If the user asked for a particular
3819 * property, show it to him, even if it is
3820 * empty. */
3821 arg_all = true;
3822 break;
ea4a240d 3823 }
48220598 3824
7e4249b9
LP
3825 case 'a':
3826 arg_all = true;
3827 break;
3828
90d473a1
LP
3829 case ARG_FAIL:
3830 arg_fail = true;
7e4249b9
LP
3831 break;
3832
3833 case ARG_SESSION:
3834 arg_session = true;
3835 break;
3836
3837 case ARG_SYSTEM:
3838 arg_session = false;
3839 break;
3840
6e905d93
LP
3841 case ARG_NO_BLOCK:
3842 arg_no_block = true;
7e4249b9
LP
3843 break;
3844
514f4ef5
LP
3845 case ARG_NO_WALL:
3846 arg_no_wall = true;
3847 break;
3848
4445a875
LP
3849 case ARG_ORDER:
3850 arg_dot = DOT_ORDER;
3851 break;
3852
3853 case ARG_REQUIRE:
3854 arg_dot = DOT_REQUIRE;
3855 break;
3856
8fe914ec
LP
3857 case ARG_FULL:
3858 arg_full = true;
3859 break;
3860
0183528f
LP
3861 case 'q':
3862 arg_quiet = true;
3863 break;
3864
ee5762e3
LP
3865 case ARG_FORCE:
3866 arg_force = true;
3867 break;
3868
3869 case ARG_NO_RELOAD:
3870 arg_no_reload = true;
3871 break;
3872
3873 case ARG_GLOBAL:
3874 arg_global = true;
3875 arg_session = true;
3876 break;
3877
3878 case ARG_DEFAULTS:
3879 arg_defaults = true;
3880 break;
3881
7e4249b9
LP
3882 case '?':
3883 return -EINVAL;
3884
3885 default:
3886 log_error("Unknown option code %c", c);
3887 return -EINVAL;
3888 }
3889 }
3890
3891 return 1;
3892}
3893
e4b61340
LP
3894static int halt_parse_argv(int argc, char *argv[]) {
3895
3896 enum {
3897 ARG_HELP = 0x100,
3898 ARG_HALT,
514f4ef5
LP
3899 ARG_REBOOT,
3900 ARG_NO_WALL
e4b61340
LP
3901 };
3902
3903 static const struct option options[] = {
3904 { "help", no_argument, NULL, ARG_HELP },
3905 { "halt", no_argument, NULL, ARG_HALT },
3906 { "poweroff", no_argument, NULL, 'p' },
3907 { "reboot", no_argument, NULL, ARG_REBOOT },
3908 { "force", no_argument, NULL, 'f' },
3909 { "wtmp-only", no_argument, NULL, 'w' },
3910 { "no-wtmp", no_argument, NULL, 'd' },
3911 { "no-sync", no_argument, NULL, 'n' },
514f4ef5 3912 { "no-wall", no_argument, NULL, ARG_NO_WALL },
e4b61340
LP
3913 { NULL, 0, NULL, 0 }
3914 };
3915
3916 int c, runlevel;
3917
3918 assert(argc >= 0);
3919 assert(argv);
3920
3921 if (utmp_get_runlevel(&runlevel, NULL) >= 0)
3922 if (runlevel == '0' || runlevel == '6')
3923 arg_immediate = true;
3924
3925 while ((c = getopt_long(argc, argv, "pfwdnih", options, NULL)) >= 0) {
3926 switch (c) {
3927
3928 case ARG_HELP:
3929 halt_help();
3930 return 0;
3931
3932 case ARG_HALT:
3933 arg_action = ACTION_HALT;
3934 break;
3935
3936 case 'p':
a042efad
MS
3937 if (arg_action != ACTION_REBOOT)
3938 arg_action = ACTION_POWEROFF;
e4b61340
LP
3939 break;
3940
3941 case ARG_REBOOT:
3942 arg_action = ACTION_REBOOT;
3943 break;
3944
3945 case 'f':
3946 arg_immediate = true;
3947 break;
3948
3949 case 'w':
3950 arg_dry = true;
3951 break;
3952
3953 case 'd':
3954 arg_no_wtmp = true;
3955 break;
3956
3957 case 'n':
3958 arg_no_sync = true;
3959 break;
3960
514f4ef5
LP
3961 case ARG_NO_WALL:
3962 arg_no_wall = true;
3963 break;
3964
e4b61340
LP
3965 case 'i':
3966 case 'h':
3967 /* Compatibility nops */
3968 break;
3969
3970 case '?':
3971 return -EINVAL;
3972
3973 default:
3974 log_error("Unknown option code %c", c);
3975 return -EINVAL;
3976 }
3977 }
3978
3979 if (optind < argc) {
3980 log_error("Too many arguments.");
3981 return -EINVAL;
3982 }
3983
3984 return 1;
3985}
3986
3987static int shutdown_parse_argv(int argc, char *argv[]) {
3988
3989 enum {
3990 ARG_HELP = 0x100,
514f4ef5 3991 ARG_NO_WALL
e4b61340
LP
3992 };
3993
3994 static const struct option options[] = {
3995 { "help", no_argument, NULL, ARG_HELP },
3996 { "halt", no_argument, NULL, 'H' },
3997 { "poweroff", no_argument, NULL, 'P' },
3998 { "reboot", no_argument, NULL, 'r' },
514f4ef5 3999 { "no-wall", no_argument, NULL, ARG_NO_WALL },
e4b61340
LP
4000 { NULL, 0, NULL, 0 }
4001 };
4002
4003 int c;
4004
4005 assert(argc >= 0);
4006 assert(argv);
4007
4008 while ((c = getopt_long(argc, argv, "HPrhkt:a", options, NULL)) >= 0) {
4009 switch (c) {
4010
4011 case ARG_HELP:
4012 shutdown_help();
4013 return 0;
4014
4015 case 'H':
4016 arg_action = ACTION_HALT;
4017 break;
4018
4019 case 'P':
4020 arg_action = ACTION_POWEROFF;
4021 break;
4022
4023 case 'r':
4024 arg_action = ACTION_REBOOT;
4025 break;
4026
4027 case 'h':
4028 if (arg_action != ACTION_HALT)
4029 arg_action = ACTION_POWEROFF;
4030 break;
4031
4032 case 'k':
4033 arg_dry = true;
4034 break;
4035
514f4ef5
LP
4036 case ARG_NO_WALL:
4037 arg_no_wall = true;
4038 break;
4039
e4b61340
LP
4040 case 't':
4041 case 'a':
4042 /* Compatibility nops */
4043 break;
4044
4045 case '?':
4046 return -EINVAL;
4047
4048 default:
4049 log_error("Unknown option code %c", c);
4050 return -EINVAL;
4051 }
4052 }
4053
4545812f
LP
4054 if (argc > optind && !streq(argv[optind], "now"))
4055 log_warning("First argument '%s' isn't 'now'. Ignoring.", argv[optind]);
442b9094 4056
e4b61340
LP
4057 /* We ignore the time argument */
4058 if (argc > optind + 1)
4059 arg_wall = argv + optind + 1;
4060
4061 optind = argc;
4062
4063 return 1;
e4b61340
LP
4064}
4065
4066static int telinit_parse_argv(int argc, char *argv[]) {
4067
4068 enum {
4069 ARG_HELP = 0x100,
514f4ef5 4070 ARG_NO_WALL
e4b61340
LP
4071 };
4072
4073 static const struct option options[] = {
4074 { "help", no_argument, NULL, ARG_HELP },
514f4ef5 4075 { "no-wall", no_argument, NULL, ARG_NO_WALL },
e4b61340
LP
4076 { NULL, 0, NULL, 0 }
4077 };
4078
4079 static const struct {
4080 char from;
4081 enum action to;
4082 } table[] = {
4083 { '0', ACTION_POWEROFF },
4084 { '6', ACTION_REBOOT },
ef2f1067 4085 { '1', ACTION_RESCUE },
e4b61340
LP
4086 { '2', ACTION_RUNLEVEL2 },
4087 { '3', ACTION_RUNLEVEL3 },
4088 { '4', ACTION_RUNLEVEL4 },
4089 { '5', ACTION_RUNLEVEL5 },
4090 { 's', ACTION_RESCUE },
4091 { 'S', ACTION_RESCUE },
4092 { 'q', ACTION_RELOAD },
4093 { 'Q', ACTION_RELOAD },
4094 { 'u', ACTION_REEXEC },
4095 { 'U', ACTION_REEXEC }
4096 };
4097
4098 unsigned i;
4099 int c;
4100
4101 assert(argc >= 0);
4102 assert(argv);
4103
4104 while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
4105 switch (c) {
4106
4107 case ARG_HELP:
4108 telinit_help();
4109 return 0;
4110
514f4ef5
LP
4111 case ARG_NO_WALL:
4112 arg_no_wall = true;
4113 break;
4114
e4b61340
LP
4115 case '?':
4116 return -EINVAL;
4117
4118 default:
4119 log_error("Unknown option code %c", c);
4120 return -EINVAL;
4121 }
4122 }
4123
4124 if (optind >= argc) {
2f02ce40 4125 telinit_help();
e4b61340
LP
4126 return -EINVAL;
4127 }
4128
4129 if (optind + 1 < argc) {
4130 log_error("Too many arguments.");
4131 return -EINVAL;
4132 }
4133
4134 if (strlen(argv[optind]) != 1) {
4135 log_error("Expected single character argument.");
4136 return -EINVAL;
4137 }
4138
4139 for (i = 0; i < ELEMENTSOF(table); i++)
4140 if (table[i].from == argv[optind][0])
4141 break;
4142
4143 if (i >= ELEMENTSOF(table)) {
4144 log_error("Unknown command %s.", argv[optind]);
4145 return -EINVAL;
4146 }
4147
4148 arg_action = table[i].to;
4149
4150 optind ++;
4151
4152 return 1;
4153}
4154
4155static int runlevel_parse_argv(int argc, char *argv[]) {
4156
4157 enum {
4158 ARG_HELP = 0x100,
4159 };
4160
4161 static const struct option options[] = {
4162 { "help", no_argument, NULL, ARG_HELP },
4163 { NULL, 0, NULL, 0 }
4164 };
4165
4166 int c;
4167
4168 assert(argc >= 0);
4169 assert(argv);
4170
4171 while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0) {
4172 switch (c) {
4173
4174 case ARG_HELP:
4175 runlevel_help();
4176 return 0;
4177
4178 case '?':
4179 return -EINVAL;
4180
4181 default:
4182 log_error("Unknown option code %c", c);
4183 return -EINVAL;
4184 }
4185 }
4186
4187 if (optind < argc) {
4188 log_error("Too many arguments.");
4189 return -EINVAL;
4190 }
4191
4192 return 1;
4193}
4194
4195static int parse_argv(int argc, char *argv[]) {
4196 assert(argc >= 0);
4197 assert(argv);
4198
4199 if (program_invocation_short_name) {
4200
4201 if (strstr(program_invocation_short_name, "halt")) {
4202 arg_action = ACTION_HALT;
4203 return halt_parse_argv(argc, argv);
4204 } else if (strstr(program_invocation_short_name, "poweroff")) {
4205 arg_action = ACTION_POWEROFF;
4206 return halt_parse_argv(argc, argv);
4207 } else if (strstr(program_invocation_short_name, "reboot")) {
4208 arg_action = ACTION_REBOOT;
4209 return halt_parse_argv(argc, argv);
4210 } else if (strstr(program_invocation_short_name, "shutdown")) {
4211 arg_action = ACTION_POWEROFF;
4212 return shutdown_parse_argv(argc, argv);
4213 } else if (strstr(program_invocation_short_name, "init")) {
d5ca5f11
LP
4214
4215 if (sd_booted() > 0) {
4216 arg_action = ACTION_INVALID;
4217 return telinit_parse_argv(argc, argv);
4218 } else {
4219 /* Hmm, so some other init system is
4220 * running, we need to forward this
4221 * request to it. For now we simply
4222 * guess that it is Upstart. */
4223
4224 execv("/lib/upstart/telinit", argv);
4225
4226 log_error("Couldn't find an alternative telinit implementation to spawn.");
4227 return -EIO;
4228 }
4229
e4b61340
LP
4230 } else if (strstr(program_invocation_short_name, "runlevel")) {
4231 arg_action = ACTION_RUNLEVEL;
4232 return runlevel_parse_argv(argc, argv);
4233 }
4234 }
4235
4236 arg_action = ACTION_SYSTEMCTL;
4237 return systemctl_parse_argv(argc, argv);
4238}
4239
d55ae9e6 4240static int action_to_runlevel(void) {
eb22ac37
LP
4241
4242 static const char table[_ACTION_MAX] = {
4243 [ACTION_HALT] = '0',
4244 [ACTION_POWEROFF] = '0',
4245 [ACTION_REBOOT] = '6',
4246 [ACTION_RUNLEVEL2] = '2',
4247 [ACTION_RUNLEVEL3] = '3',
4248 [ACTION_RUNLEVEL4] = '4',
4249 [ACTION_RUNLEVEL5] = '5',
4250 [ACTION_RESCUE] = '1'
4251 };
4252
d55ae9e6
LP
4253 assert(arg_action < _ACTION_MAX);
4254
4255 return table[arg_action];
4256}
4257
f1c5860b 4258static int talk_upstart(void) {
d55ae9e6
LP
4259 DBusMessage *m = NULL, *reply = NULL;
4260 DBusError error;
4261 int previous, rl, r;
4262 char
4263 env1_buf[] = "RUNLEVEL=X",
4264 env2_buf[] = "PREVLEVEL=X";
4265 char *env1 = env1_buf, *env2 = env2_buf;
4266 const char *emit = "runlevel";
4267 dbus_bool_t b_false = FALSE;
4268 DBusMessageIter iter, sub;
f1c5860b 4269 DBusConnection *bus;
d55ae9e6
LP
4270
4271 dbus_error_init(&error);
4272
4273 if (!(rl = action_to_runlevel()))
4274 return 0;
4275
4276 if (utmp_get_runlevel(&previous, NULL) < 0)
4277 previous = 'N';
4278
b574246b 4279 if (!(bus = dbus_connection_open_private("unix:abstract=/com/ubuntu/upstart", &error))) {
f1c5860b
LP
4280 if (dbus_error_has_name(&error, DBUS_ERROR_NO_SERVER)) {
4281 r = 0;
4282 goto finish;
4283 }
4284
4285 log_error("Failed to connect to Upstart bus: %s", error.message);
4286 r = -EIO;
4287 goto finish;
4288 }
4289
4290 if ((r = bus_check_peercred(bus)) < 0) {
4291 log_error("Failed to verify owner of bus.");
4292 goto finish;
4293 }
4294
d55ae9e6
LP
4295 if (!(m = dbus_message_new_method_call(
4296 "com.ubuntu.Upstart",
4297 "/com/ubuntu/Upstart",
4298 "com.ubuntu.Upstart0_6",
4299 "EmitEvent"))) {
4300
4301 log_error("Could not allocate message.");
f1c5860b
LP
4302 r = -ENOMEM;
4303 goto finish;
d55ae9e6
LP
4304 }
4305
4306 dbus_message_iter_init_append(m, &iter);
4307
4308 env1_buf[sizeof(env1_buf)-2] = rl;
4309 env2_buf[sizeof(env2_buf)-2] = previous;
4310
4311 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &emit) ||
4312 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub) ||
4313 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env1) ||
4314 !dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &env2) ||
4315 !dbus_message_iter_close_container(&iter, &sub) ||
4316 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_BOOLEAN, &b_false)) {
4317 log_error("Could not append arguments to message.");
4318 r = -ENOMEM;
4319 goto finish;
4320 }
4321
4322 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
4323
4324 if (error_is_no_service(&error)) {
4325 r = 0;
4326 goto finish;
4327 }
4328
4329 log_error("Failed to issue method call: %s", error.message);
4330 r = -EIO;
4331 goto finish;
4332 }
4333
4334 r = 1;
4335
4336finish:
4337 if (m)
4338 dbus_message_unref(m);
4339
4340 if (reply)
4341 dbus_message_unref(reply);
4342
b574246b
LP
4343 if (bus) {
4344 dbus_connection_close(bus);
f1c5860b 4345 dbus_connection_unref(bus);
b574246b 4346 }
f1c5860b 4347
d55ae9e6
LP
4348 dbus_error_free(&error);
4349
4350 return r;
4351}
4352
4353static int talk_initctl(void) {
eb22ac37
LP
4354 struct init_request request;
4355 int r, fd;
d55ae9e6 4356 char rl;
eb22ac37 4357
d55ae9e6 4358 if (!(rl = action_to_runlevel()))
eb22ac37
LP
4359 return 0;
4360
4361 zero(request);
4362 request.magic = INIT_MAGIC;
4363 request.sleeptime = 0;
4364 request.cmd = INIT_CMD_RUNLVL;
d55ae9e6
LP
4365 request.runlevel = rl;
4366
4367 if ((fd = open(INIT_FIFO, O_WRONLY|O_NDELAY|O_CLOEXEC|O_NOCTTY)) < 0) {
4368
4369 if (errno == ENOENT)
4370 return 0;
eb22ac37 4371
d55ae9e6 4372 log_error("Failed to open "INIT_FIFO": %m");
eb22ac37 4373 return -errno;
d55ae9e6 4374 }
eb22ac37 4375
d55ae9e6 4376 errno = 0;
eb22ac37
LP
4377 r = loop_write(fd, &request, sizeof(request), false) != sizeof(request);
4378 close_nointr_nofail(fd);
4379
d55ae9e6
LP
4380 if (r < 0) {
4381 log_error("Failed to write to "INIT_FIFO": %m");
eb22ac37 4382 return errno ? -errno : -EIO;
d55ae9e6 4383 }
eb22ac37
LP
4384
4385 return 1;
e4b61340
LP
4386}
4387
ee5762e3 4388static int systemctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
7e4249b9 4389
7e4249b9
LP
4390 static const struct {
4391 const char* verb;
4392 const enum {
4393 MORE,
4394 LESS,
4395 EQUAL
4396 } argc_cmp;
4397 const int argc;
4398 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
4399 } verbs[] = {
ee5762e3
LP
4400 { "list-units", LESS, 1, list_units },
4401 { "list-jobs", EQUAL, 1, list_jobs },
4402 { "clear-jobs", EQUAL, 1, daemon_reload },
4403 { "load", MORE, 2, load_unit },
4404 { "cancel", MORE, 2, cancel_job },
4405 { "start", MORE, 2, start_unit },
4406 { "stop", MORE, 2, start_unit },
4407 { "reload", MORE, 2, start_unit },
4408 { "restart", MORE, 2, start_unit },
4409 { "try-restart", MORE, 2, start_unit },
4410 { "reload-or-restart", MORE, 2, start_unit },
4411 { "reload-or-try-restart", MORE, 2, start_unit },
4412 { "force-reload", MORE, 2, start_unit }, /* For compatibility with SysV */
4413 { "condrestart", MORE, 2, start_unit }, /* For compatibility with RH */
4414 { "isolate", EQUAL, 2, start_unit },
4415 { "is-active", MORE, 2, check_unit },
4416 { "check", MORE, 2, check_unit },
4417 { "show", MORE, 1, show },
4418 { "status", MORE, 2, show },
4419 { "monitor", EQUAL, 1, monitor },
4420 { "dump", EQUAL, 1, dump },
4421 { "dot", EQUAL, 1, dot },
4422 { "snapshot", LESS, 2, snapshot },
4423 { "delete", MORE, 2, delete_snapshot },
4424 { "daemon-reload", EQUAL, 1, daemon_reload },
4425 { "daemon-reexec", EQUAL, 1, daemon_reload },
4426 { "daemon-exit", EQUAL, 1, daemon_reload },
4427 { "show-environment", EQUAL, 1, show_enviroment },
4428 { "set-environment", MORE, 2, set_environment },
4429 { "unset-environment", MORE, 2, set_environment },
4430 { "halt", EQUAL, 1, start_special },
4431 { "poweroff", EQUAL, 1, start_special },
4432 { "reboot", EQUAL, 1, start_special },
4433 { "default", EQUAL, 1, start_special },
4434 { "rescue", EQUAL, 1, start_special },
4435 { "emergency", EQUAL, 1, start_special },
4436 { "reset-maintenance", MORE, 1, reset_maintenance },
4437 { "enable", MORE, 2, enable_unit },
4438 { "disable", MORE, 2, enable_unit },
4439 { "is-enabled", MORE, 2, enable_unit }
7e4249b9
LP
4440 };
4441
e4b61340 4442 int left;
7e4249b9 4443 unsigned i;
7e4249b9 4444
e4b61340
LP
4445 assert(argc >= 0);
4446 assert(argv);
ee5762e3 4447 assert(error);
7e4249b9
LP
4448
4449 left = argc - optind;
4450
4451 if (left <= 0)
4452 /* Special rule: no arguments means "list-units" */
4453 i = 0;
4454 else {
0183528f
LP
4455 if (streq(argv[optind], "help")) {
4456 systemctl_help();
4457 return 0;
4458 }
4459
7e4249b9
LP
4460 for (i = 0; i < ELEMENTSOF(verbs); i++)
4461 if (streq(argv[optind], verbs[i].verb))
4462 break;
4463
4464 if (i >= ELEMENTSOF(verbs)) {
4465 log_error("Unknown operation %s", argv[optind]);
e4b61340 4466 return -EINVAL;
7e4249b9
LP
4467 }
4468 }
4469
4470 switch (verbs[i].argc_cmp) {
4471
4472 case EQUAL:
4473 if (left != verbs[i].argc) {
4474 log_error("Invalid number of arguments.");
e4b61340 4475 return -EINVAL;
7e4249b9
LP
4476 }
4477
4478 break;
4479
4480 case MORE:
4481 if (left < verbs[i].argc) {
4482 log_error("Too few arguments.");
e4b61340 4483 return -EINVAL;
7e4249b9
LP
4484 }
4485
4486 break;
4487
4488 case LESS:
4489 if (left > verbs[i].argc) {
4490 log_error("Too many arguments.");
e4b61340 4491 return -EINVAL;
7e4249b9
LP
4492 }
4493
4494 break;
4495
4496 default:
4497 assert_not_reached("Unknown comparison operator.");
4498 }
4499
ee5762e3
LP
4500 /* Require a bus connection for all operations but
4501 * enable/disable */
4502 if (!streq(verbs[i].verb, "enable") &&
4503 !streq(verbs[i].verb, "disable") &&
4504 !bus) {
4505 log_error("Failed to get D-Bus connection: %s", error->message);
4506 return -EIO;
4507 }
4508
e4b61340
LP
4509 return verbs[i].dispatch(bus, argv + optind, left);
4510}
4511
4512static int reload_with_fallback(DBusConnection *bus) {
4513 int r;
4514
4515 if (bus) {
4516 /* First, try systemd via D-Bus. */
ee5762e3 4517 if ((r = daemon_reload(bus, NULL, 0)) > 0)
e4b61340
LP
4518 return 0;
4519 }
4520
4521 /* Nothing else worked, so let's try signals */
4522 assert(arg_action == ACTION_RELOAD || arg_action == ACTION_REEXEC);
4523
4524 if (kill(1, arg_action == ACTION_RELOAD ? SIGHUP : SIGTERM) < 0) {
4525 log_error("kill() failed: %m");
4526 return -errno;
4527 }
4528
4529 return 0;
4530}
4531
4532static int start_with_fallback(DBusConnection *bus) {
4533 int r;
4534
4535 if (bus) {
4536 /* First, try systemd via D-Bus. */
4537 if ((r = start_unit(bus, NULL, 0)) > 0)
983d9c90 4538 goto done;
e4b61340
LP
4539 }
4540
ec7f7f20
LP
4541 /* Hmm, talking to systemd via D-Bus didn't work. Then
4542 * let's try to talk to Upstart via D-Bus. */
4543 if ((r = talk_upstart()) > 0)
4544 goto done;
4545
e4b61340
LP
4546 /* Nothing else worked, so let's try
4547 * /dev/initctl */
d55ae9e6 4548 if ((r = talk_initctl()) != 0)
983d9c90 4549 goto done;
d55ae9e6
LP
4550
4551 log_error("Failed to talk to init daemon.");
4552 return -EIO;
983d9c90
LP
4553
4554done:
4555 warn_wall(arg_action);
4556 return 0;
e4b61340
LP
4557}
4558
4559static int halt_main(DBusConnection *bus) {
4560 int r;
4561
bc8c2f5c
LP
4562 if (geteuid() != 0) {
4563 log_error("Must to be root.");
4564 return -EPERM;
4565 }
4566
d47b555b 4567 if (!arg_dry && !arg_immediate)
e4b61340
LP
4568 return start_with_fallback(bus);
4569
4570 if (!arg_no_wtmp)
4571 if ((r = utmp_put_shutdown(0)) < 0)
4572 log_warning("Failed to write utmp record: %s", strerror(-r));
4573
4574 if (!arg_no_sync)
4575 sync();
4576
4577 if (arg_dry)
4578 return 0;
4579
4580 /* Make sure C-A-D is handled by the kernel from this
4581 * point on... */
4582 reboot(RB_ENABLE_CAD);
4583
4584 switch (arg_action) {
4585
4586 case ACTION_HALT:
4587 log_info("Halting");
4588 reboot(RB_HALT_SYSTEM);
4589 break;
4590
4591 case ACTION_POWEROFF:
4592 log_info("Powering off");
4593 reboot(RB_POWER_OFF);
4594 break;
4595
4596 case ACTION_REBOOT:
4597 log_info("Rebooting");
4598 reboot(RB_AUTOBOOT);
4599 break;
4600
4601 default:
4602 assert_not_reached("Unknown halt action.");
4603 }
4604
4605 /* We should never reach this. */
4606 return -ENOSYS;
4607}
4608
4609static int runlevel_main(void) {
4610 int r, runlevel, previous;
4611
4612 if ((r = utmp_get_runlevel(&runlevel, &previous)) < 0) {
4613 printf("unknown");
4614 return r;
4615 }
4616
4617 printf("%c %c\n",
4618 previous <= 0 ? 'N' : previous,
4619 runlevel <= 0 ? 'N' : runlevel);
4620
4621 return 0;
4622}
4623
4624int main(int argc, char*argv[]) {
4625 int r, retval = 1;
4626 DBusConnection *bus = NULL;
4627 DBusError error;
4628
4629 dbus_error_init(&error);
4630
4631 log_parse_environment();
4632
4633 if ((r = parse_argv(argc, argv)) < 0)
4634 goto finish;
4635 else if (r == 0) {
4636 retval = 0;
7e4249b9
LP
4637 goto finish;
4638 }
4639
e4b61340
LP
4640 /* /sbin/runlevel doesn't need to communicate via D-Bus, so
4641 * let's shortcut this */
4642 if (arg_action == ACTION_RUNLEVEL) {
4643 retval = runlevel_main() < 0;
4644 goto finish;
4645 }
4646
f4579ce7 4647 bus_connect(arg_session ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &bus, &private_bus, &error);
e4b61340
LP
4648
4649 switch (arg_action) {
4650
4651 case ACTION_SYSTEMCTL: {
ee5762e3 4652 retval = systemctl_main(bus, argc, argv, &error) < 0;
e4b61340
LP
4653 break;
4654 }
4655
4656 case ACTION_HALT:
4657 case ACTION_POWEROFF:
4658 case ACTION_REBOOT:
4659 retval = halt_main(bus) < 0;
4660 break;
4661
e4b61340
LP
4662 case ACTION_RUNLEVEL2:
4663 case ACTION_RUNLEVEL3:
4664 case ACTION_RUNLEVEL4:
4665 case ACTION_RUNLEVEL5:
4666 case ACTION_RESCUE:
514f4ef5 4667 case ACTION_EMERGENCY:
eb22ac37 4668 case ACTION_DEFAULT:
e4b61340
LP
4669 retval = start_with_fallback(bus) < 0;
4670 break;
7e4249b9 4671
e4b61340
LP
4672 case ACTION_RELOAD:
4673 case ACTION_REEXEC:
4674 retval = reload_with_fallback(bus) < 0;
4675 break;
4676
eb22ac37
LP
4677 case ACTION_INVALID:
4678 case ACTION_RUNLEVEL:
e4b61340
LP
4679 default:
4680 assert_not_reached("Unknown action");
4681 }
7e4249b9
LP
4682
4683finish:
4684
b574246b
LP
4685 if (bus) {
4686 dbus_connection_close(bus);
7e4249b9 4687 dbus_connection_unref(bus);
b574246b 4688 }
7e4249b9 4689
e4b61340
LP
4690 dbus_error_free(&error);
4691
7e4249b9
LP
4692 dbus_shutdown();
4693
ea4a240d
LP
4694 strv_free(arg_property);
4695
7e4249b9
LP
4696 return retval;
4697}