]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/systemctl.c
service: rework PID parsing logic everywhere
[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
22#include <stdio.h>
23#include <getopt.h>
24#include <stdbool.h>
25#include <string.h>
26#include <errno.h>
27#include <sys/ioctl.h>
28#include <termios.h>
29#include <unistd.h>
30
31#include <dbus/dbus.h>
32
33#include "log.h"
34#include "util.h"
35#include "macro.h"
36#include "set.h"
37
38static const char *arg_type = NULL;
39static bool arg_all = false;
40static bool arg_replace = false;
41static bool arg_session = false;
42static bool arg_block = false;
43
44static int bus_iter_get_basic_and_next(DBusMessageIter *iter, int type, void *data, bool next) {
45
46 if (dbus_message_iter_get_arg_type(iter) != type)
47 return -EIO;
48
49 dbus_message_iter_get_basic(iter, data);
50
51 if (!dbus_message_iter_next(iter) != !next)
52 return -EIO;
53
54 return 0;
55}
56
57static int columns(void) {
58 static int parsed_columns = 0;
59 const char *e;
60
61 if (parsed_columns > 0)
62 return parsed_columns;
63
64 if ((e = getenv("COLUMNS")))
65 parsed_columns = atoi(e);
66
67 if (parsed_columns <= 0) {
68 struct winsize ws;
69 zero(ws);
70
71 if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
72 parsed_columns = ws.ws_col;
73 }
74
75 if (parsed_columns <= 0)
76 parsed_columns = 80;
77
78 return parsed_columns;
79}
80
81static int list_units(DBusConnection *bus, char **args, unsigned n) {
82 DBusMessage *m = NULL, *reply = NULL;
83 DBusError error;
84 int r;
85 DBusMessageIter iter, sub, sub2;
86 unsigned k = 0;
87
88 dbus_error_init(&error);
89
90 if (!(m = dbus_message_new_method_call(
91 "org.freedesktop.systemd1",
92 "/org/freedesktop/systemd1",
93 "org.freedesktop.systemd1.Manager",
94 "ListUnits"))) {
95 log_error("Could not allocate message.");
96 return -ENOMEM;
97 }
98
99 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
100 log_error("Failed to issue method call: %s", error.message);
101 r = -EIO;
102 goto finish;
103 }
104
105 if (!dbus_message_iter_init(reply, &iter) ||
106 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
107 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
108 log_error("Failed to parse reply.");
109 r = -EIO;
110 goto finish;
111 }
112
113 dbus_message_iter_recurse(&iter, &sub);
114
115 printf("%-45s %-6s %-12s %-12s %-15s %s\n", "UNIT", "LOAD", "ACTIVE", "SUB", "JOB", "DESCRIPTION");
116
117 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
118 const char *id, *description, *load_state, *active_state, *sub_state, *unit_state, *job_type, *job_path, *dot;
119 uint32_t job_id;
120
121 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
122 log_error("Failed to parse reply.");
123 r = -EIO;
124 goto finish;
125 }
126
127 dbus_message_iter_recurse(&sub, &sub2);
128
129 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
130 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &description, true) < 0 ||
131 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &load_state, true) < 0 ||
132 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &active_state, true) < 0 ||
133 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &sub_state, true) < 0 ||
134 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_state, true) < 0 ||
135 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &job_id, true) < 0 ||
136 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &job_type, true) < 0 ||
137 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, false) < 0) {
138 log_error("Failed to parse reply.");
139 r = -EIO;
140 goto finish;
141 }
142
143 if ((!arg_type || ((dot = strrchr(id, '.')) &&
144 streq(dot+1, arg_type))) &&
145 (arg_all || !streq(active_state, "inactive"))) {
146
147 int a = 0, b = 0;
148
149 printf("%-45s %-6s %-12s %-12s%n", id, load_state, active_state, sub_state, &a);
150
151 if (job_id != 0)
152 printf(" %-15s%n", job_type, &b);
153 else
154 b = 1 + 15;
155
156 if (a + b + 2 < columns()) {
157 if (job_id == 0)
158 printf(" ");
159
160 printf("%.*s", columns() - a - b - 2, description);
161 }
162
163 fputs("\n", stdout);
164 k++;
165 }
166
167 dbus_message_iter_next(&sub);
168 }
169
170 if (arg_all)
171 printf("\n%u units listed.\n", k);
172 else
173 printf("\n%u live units listed. Pass --all to see dead units, too.\n", k);
174
175 r = 0;
176
177finish:
178 if (m)
179 dbus_message_unref(m);
180
181 if (reply)
182 dbus_message_unref(reply);
183
184 dbus_error_free(&error);
185
186 return r;
187}
188
189static int list_jobs(DBusConnection *bus, char **args, unsigned n) {
190 DBusMessage *m = NULL, *reply = NULL;
191 DBusError error;
192 int r;
193 DBusMessageIter iter, sub, sub2;
194 unsigned k = 0;
195
196 dbus_error_init(&error);
197
198 if (!(m = dbus_message_new_method_call(
199 "org.freedesktop.systemd1",
200 "/org/freedesktop/systemd1",
201 "org.freedesktop.systemd1.Manager",
202 "ListJobs"))) {
203 log_error("Could not allocate message.");
204 return -ENOMEM;
205 }
206
207 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
208 log_error("Failed to issue method call: %s", error.message);
209 r = -EIO;
210 goto finish;
211 }
212
213 if (!dbus_message_iter_init(reply, &iter) ||
214 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
215 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
216 log_error("Failed to parse reply.");
217 r = -EIO;
218 goto finish;
219 }
220
221 dbus_message_iter_recurse(&iter, &sub);
222
223 printf("%4s %-45s %-17s %-7s\n", "JOB", "UNIT", "TYPE", "STATE");
224
225 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
226 const char *name, *type, *state, *job_path, *unit_path;
227 uint32_t id;
228
229 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
230 log_error("Failed to parse reply.");
231 r = -EIO;
232 goto finish;
233 }
234
235 dbus_message_iter_recurse(&sub, &sub2);
236
237 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &id, true) < 0 ||
238 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
239 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &type, true) < 0 ||
240 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &state, true) < 0 ||
241 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &job_path, true) < 0 ||
242 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &unit_path, false) < 0) {
243 log_error("Failed to parse reply.");
244 r = -EIO;
245 goto finish;
246 }
247
248 printf("%4u %-45s %-17s %-7s\n", id, name, type, state);
249 k++;
250
251 dbus_message_iter_next(&sub);
252 }
253
254 printf("\n%u jobs listed.\n", k);
255 r = 0;
256
257finish:
258 if (m)
259 dbus_message_unref(m);
260
261 if (reply)
262 dbus_message_unref(reply);
263
264 dbus_error_free(&error);
265
266 return r;
267}
268
269static int load_unit(DBusConnection *bus, char **args, unsigned n) {
270 DBusMessage *m = NULL, *reply = NULL;
271 DBusError error;
272 int r;
273 unsigned i;
274
275 dbus_error_init(&error);
276
277 for (i = 1; i < n; i++) {
278
279 if (!(m = dbus_message_new_method_call(
280 "org.freedesktop.systemd1",
281 "/org/freedesktop/systemd1",
282 "org.freedesktop.systemd1.Manager",
283 "LoadUnit"))) {
284 log_error("Could not allocate message.");
285 r = -ENOMEM;
286 goto finish;
287 }
288
289 if (!dbus_message_append_args(m,
290 DBUS_TYPE_STRING, &args[i],
291 DBUS_TYPE_INVALID)) {
292 log_error("Could not append arguments to message.");
293 r = -ENOMEM;
294 goto finish;
295 }
296
297 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
298 log_error("Failed to issue method call: %s", error.message);
299 r = -EIO;
300 goto finish;
301 }
302
303 dbus_message_unref(m);
304 dbus_message_unref(reply);
305
306 m = reply = NULL;
307 }
308
309 r = 0;
310
311finish:
312 if (m)
313 dbus_message_unref(m);
314
315 if (reply)
316 dbus_message_unref(reply);
317
318 dbus_error_free(&error);
319
320 return r;
321}
322
323static int cancel_job(DBusConnection *bus, char **args, unsigned n) {
324 DBusMessage *m = NULL, *reply = NULL;
325 DBusError error;
326 int r;
327 unsigned i;
328
329 dbus_error_init(&error);
330
331 for (i = 1; i < n; i++) {
332 unsigned id;
333 const char *path;
334
335 if (!(m = dbus_message_new_method_call(
336 "org.freedesktop.systemd1",
337 "/org/freedesktop/systemd1",
338 "org.freedesktop.systemd1.Manager",
339 "GetJob"))) {
340 log_error("Could not allocate message.");
341 r = -ENOMEM;
342 goto finish;
343 }
344
345 if ((r = safe_atou(args[i], &id)) < 0) {
346 log_error("Failed to parse job id: %s", strerror(-r));
347 goto finish;
348 }
349
350 assert_cc(sizeof(uint32_t) == sizeof(id));
351 if (!dbus_message_append_args(m,
352 DBUS_TYPE_UINT32, &id,
353 DBUS_TYPE_INVALID)) {
354 log_error("Could not append arguments to message.");
355 r = -ENOMEM;
356 goto finish;
357 }
358
359 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
360 log_error("Failed to issue method call: %s", error.message);
361 r = -EIO;
362 goto finish;
363 }
364
365 if (!dbus_message_get_args(reply, &error,
366 DBUS_TYPE_OBJECT_PATH, &path,
367 DBUS_TYPE_INVALID)) {
368 log_error("Failed to parse reply: %s", error.message);
369 r = -EIO;
370 goto finish;
371 }
372
373 dbus_message_unref(m);
374 if (!(m = dbus_message_new_method_call(
375 "org.freedesktop.systemd1",
376 path,
377 "org.freedesktop.systemd1.Job",
378 "Cancel"))) {
379 log_error("Could not allocate message.");
380 r = -ENOMEM;
381 goto finish;
382 }
383
384 dbus_message_unref(reply);
385 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
386 log_error("Failed to issue method call: %s", error.message);
387 r = -EIO;
388 goto finish;
389 }
390
391 dbus_message_unref(m);
392 dbus_message_unref(reply);
393 m = reply = NULL;
394 }
395
396 r = 0;
397
398finish:
399 if (m)
400 dbus_message_unref(m);
401
402 if (reply)
403 dbus_message_unref(reply);
404
405 dbus_error_free(&error);
406
407 return r;
408}
409
410static DBusHandlerResult wait_filter(DBusConnection *connection, DBusMessage *message, void *data) {
411 DBusError error;
412 Set *s = data;
413
414 assert(connection);
415 assert(message);
416 assert(s);
417
418 dbus_error_init(&error);
419
420 /* log_debug("Got D-Bus request: %s.%s() on %s", */
421 /* dbus_message_get_interface(message), */
422 /* dbus_message_get_member(message), */
423 /* dbus_message_get_path(message)); */
424
425 if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
426 log_error("Warning! D-Bus connection terminated.");
427 dbus_connection_close(connection);
428
429 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
430 uint32_t id;
431 const char *path;
432
433 if (!dbus_message_get_args(message, &error,
434 DBUS_TYPE_UINT32, &id,
435 DBUS_TYPE_OBJECT_PATH, &path,
436 DBUS_TYPE_INVALID))
437 log_error("Failed to parse message: %s", error.message);
438 else {
439 char *p;
440
441 if ((p = set_remove(s, (char*) path)))
442 free(p);
443 }
444 }
445
446 dbus_error_free(&error);
447 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
448}
449
479ef5d3 450static int enable_wait_for_jobs(DBusConnection *bus) {
7e4249b9
LP
451 DBusError error;
452 DBusMessage *m = NULL, *reply = NULL;
453 int r;
454
455 assert(bus);
7e4249b9
LP
456
457 dbus_error_init(&error);
458
459 dbus_bus_add_match(bus,
460 "type='signal',"
461 "sender='org.freedesktop.systemd1',"
462 "interface='org.freedesktop.systemd1.Manager',"
463 "member='JobRemoved',"
464 "path='/org/freedesktop/systemd1'",
465 &error);
466
467 if (dbus_error_is_set(&error)) {
468 log_error("Failed to add match: %s", error.message);
469 r = -EIO;
470 goto finish;
471 }
472
7e4249b9
LP
473 if (!(m = dbus_message_new_method_call(
474 "org.freedesktop.systemd1",
475 "/org/freedesktop/systemd1",
476 "org.freedesktop.systemd1.Manager",
477 "Subscribe"))) {
478 log_error("Could not allocate message.");
479 r = -ENOMEM;
480 goto finish;
481 }
482
483 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
484 log_error("Failed to issue method call: %s", error.message);
485 r = -EIO;
486 goto finish;
487 }
488
7e4249b9
LP
489 r = 0;
490
491finish:
479ef5d3 492 /* This is slightly dirty, since we don't undo the match registrations. */
7e4249b9
LP
493
494 if (m)
495 dbus_message_unref(m);
496
497 if (reply)
498 dbus_message_unref(reply);
499
500 dbus_error_free(&error);
501
502 return r;
503}
504
479ef5d3
LP
505static int wait_for_jobs(DBusConnection *bus, Set *s) {
506 int r;
507
508 assert(bus);
509 assert(s);
510
511 if (!dbus_connection_add_filter(bus, wait_filter, s, NULL)) {
512 log_error("Failed to add filter.");
513 r = -ENOMEM;
514 goto finish;
515 }
516
517 while (!set_isempty(s) &&
518 dbus_connection_read_write_dispatch(bus, -1))
519 ;
520
521 r = 0;
522
523finish:
524 /* This is slightly dirty, since we don't undo the filter registration. */
525
526 return r;
527}
528
7e4249b9
LP
529static int start_unit(DBusConnection *bus, char **args, unsigned n) {
530 DBusMessage *m = NULL, *reply = NULL;
531 DBusError error;
532 int r;
533 unsigned i;
534 const char *method, *mode;
535 char *p = NULL;
536 Set *s = NULL;
537
538 dbus_error_init(&error);
539
540 method =
541 streq(args[0], "start") ? "StartUnit" :
542 streq(args[0], "stop") ? "StopUnit" :
543 streq(args[0], "reload") ? "ReloadUnit" :
544 "RestartUnit";
545
546 mode = arg_replace ? "replace" : "fail";
547
479ef5d3
LP
548 if (arg_block) {
549 if ((r = enable_wait_for_jobs(bus)) < 0) {
550 log_error("Could not watch jobs: %s", strerror(-r));
551 goto finish;
552 }
553 }
554
7e4249b9
LP
555 for (i = 1; i < n; i++) {
556
557 if (!(m = dbus_message_new_method_call(
558 "org.freedesktop.systemd1",
559 "/org/freedesktop/systemd1",
560 "org.freedesktop.systemd1.Manager",
561 method))) {
562 log_error("Could not allocate message.");
563 r = -ENOMEM;
564 goto finish;
565 }
566
567 if (!dbus_message_append_args(m,
568 DBUS_TYPE_STRING, &args[i],
569 DBUS_TYPE_STRING, &mode,
570 DBUS_TYPE_INVALID)) {
571 log_error("Could not append arguments to message.");
572 r = -ENOMEM;
573 goto finish;
574 }
575
576 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
577 log_error("Failed to issue method call: %s", error.message);
578 r = -EIO;
579 goto finish;
580 }
581
582 if (arg_block) {
583 const char *path;
584
585 if (!dbus_message_get_args(reply, &error,
586 DBUS_TYPE_OBJECT_PATH, &path,
587 DBUS_TYPE_INVALID)) {
588 log_error("Failed to parse reply: %s", error.message);
589 r = -EIO;
590 goto finish;
591 }
592
593 if (!s)
594 if (!(s = set_new(string_hash_func, string_compare_func))) {
595 log_error("Failed to allocate set.");
596 r = -ENOMEM;
597 goto finish;
598 }
599
600 if (!(p = strdup(path))) {
601 log_error("Failed to duplicate path.");
602 r = -ENOMEM;
603 goto finish;
604 }
605
606 if ((r = set_put(s, p)) < 0) {
607 log_error("Failed to add path to set.");
608 goto finish;
609 }
610 p = NULL;
611 }
612
613 dbus_message_unref(m);
614 dbus_message_unref(reply);
615
616 m = reply = NULL;
617 }
618
619 if (arg_block)
620 r = wait_for_jobs(bus, s);
621 else
622 r = 0;
623
624finish:
625 free(p);
626
627 if (s)
628 set_free_free(s);
629
630 if (m)
631 dbus_message_unref(m);
632
633 if (reply)
634 dbus_message_unref(reply);
635
636 dbus_error_free(&error);
637
638 return r;
639}
640
641static int isolate_unit(DBusConnection *bus, char **args, unsigned n) {
642 DBusMessage *m = NULL, *reply = NULL;
643 DBusError error;
644 int r;
645 const char *mode = "isolate";
646 char *p = NULL;
647 Set *s = NULL;
648
649 dbus_error_init(&error);
650
479ef5d3
LP
651 if (arg_block) {
652 if ((r = enable_wait_for_jobs(bus)) < 0) {
653 log_error("Could not watch jobs: %s", strerror(-r));
654 goto finish;
655 }
656 }
657
7e4249b9
LP
658 if (!(m = dbus_message_new_method_call(
659 "org.freedesktop.systemd1",
660 "/org/freedesktop/systemd1",
661 "org.freedesktop.systemd1.Manager",
662 "StartUnit"))) {
663 log_error("Could not allocate message.");
664 r = -ENOMEM;
665 goto finish;
666 }
667
668 if (!dbus_message_append_args(m,
669 DBUS_TYPE_STRING, &args[1],
670 DBUS_TYPE_STRING, &mode,
671 DBUS_TYPE_INVALID)) {
672 log_error("Could not append arguments to message.");
673 r = -ENOMEM;
674 goto finish;
675 }
676
677 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
678 log_error("Failed to issue method call: %s", error.message);
679 r = -EIO;
680 goto finish;
681 }
682
683 if (arg_block) {
684 const char *path;
685
686 if (!dbus_message_get_args(reply, &error,
687 DBUS_TYPE_OBJECT_PATH, &path,
688 DBUS_TYPE_INVALID)) {
689 log_error("Failed to parse reply: %s", error.message);
690 r = -EIO;
691 goto finish;
692 }
693
694 if (!(s = set_new(string_hash_func, string_compare_func))) {
695 log_error("Failed to allocate set.");
696 r = -ENOMEM;
697 goto finish;
698 }
699
700 if (!(p = strdup(path))) {
701 log_error("Failed to duplicate path.");
702 r = -ENOMEM;
703 goto finish;
704 }
705
706 if ((r = set_put(s, p)) < 0) {
707 log_error("Failed to add path to set.");
708 goto finish;
709 }
710 p = NULL;
711
712 r = wait_for_jobs(bus, s);
713
714 } else
715 r = 0;
716
717finish:
718 free(p);
719
720 if (s)
721 set_free_free(s);
722
723 if (m)
724 dbus_message_unref(m);
725
726 if (reply)
727 dbus_message_unref(reply);
728
729 dbus_error_free(&error);
730
731 return r;
732}
733
734static DBusHandlerResult monitor_filter(DBusConnection *connection, DBusMessage *message, void *data) {
735 DBusError error;
736 DBusMessage *m = NULL, *reply = NULL;
737
738 assert(connection);
739 assert(message);
740
741 dbus_error_init(&error);
742
743 /* log_debug("Got D-Bus request: %s.%s() on %s", */
744 /* dbus_message_get_interface(message), */
745 /* dbus_message_get_member(message), */
746 /* dbus_message_get_path(message)); */
747
748 if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
749 log_error("Warning! D-Bus connection terminated.");
750 dbus_connection_close(connection);
751
752 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitNew") ||
753 dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
754 const char *id, *path;
755
756 if (!dbus_message_get_args(message, &error,
757 DBUS_TYPE_STRING, &id,
758 DBUS_TYPE_OBJECT_PATH, &path,
759 DBUS_TYPE_INVALID))
760 log_error("Failed to parse message: %s", error.message);
761 else if (streq(dbus_message_get_member(message), "UnitNew"))
762 printf("Unit %s added.\n", id);
763 else
764 printf("Unit %s removed.\n", id);
765
766 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobNew") ||
767 dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
768 uint32_t id;
769 const char *path;
770
771 if (!dbus_message_get_args(message, &error,
772 DBUS_TYPE_UINT32, &id,
773 DBUS_TYPE_OBJECT_PATH, &path,
774 DBUS_TYPE_INVALID))
775 log_error("Failed to parse message: %s", error.message);
776 else if (streq(dbus_message_get_member(message), "JobNew"))
777 printf("Job %u added.\n", id);
778 else
779 printf("Job %u removed.\n", id);
780
781
782 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Unit", "Changed") ||
783 dbus_message_is_signal(message, "org.freedesktop.systemd1.Job", "Changed")) {
784
785 const char *path, *interface, *property = "Id";
786 DBusMessageIter iter, sub;
787
788 path = dbus_message_get_path(message);
789 interface = dbus_message_get_interface(message);
790
791 if (!(m = dbus_message_new_method_call(
792 "org.freedesktop.systemd1",
793 path,
794 "org.freedesktop.DBus.Properties",
795 "Get"))) {
796 log_error("Could not allocate message.");
797 goto oom;
798 }
799
800 if (!dbus_message_append_args(m,
801 DBUS_TYPE_STRING, &interface,
802 DBUS_TYPE_STRING, &property,
803 DBUS_TYPE_INVALID)) {
804 log_error("Could not append arguments to message.");
805 goto finish;
806 }
807
808 if (!(reply = dbus_connection_send_with_reply_and_block(connection, m, -1, &error))) {
809 log_error("Failed to issue method call: %s", error.message);
810 goto finish;
811 }
812
813 if (!dbus_message_iter_init(reply, &iter) ||
814 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
815 log_error("Failed to parse reply.");
816 goto finish;
817 }
818
819 dbus_message_iter_recurse(&iter, &sub);
820
821 if (streq(interface, "org.freedesktop.systemd1.Unit")) {
822 const char *id;
823
824 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
825 log_error("Failed to parse reply.");
826 goto finish;
827 }
828
829 dbus_message_iter_get_basic(&sub, &id);
830 printf("Unit %s changed.\n", id);
831 } else {
832 uint32_t id;
833
834 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_UINT32) {
835 log_error("Failed to parse reply.");
836 goto finish;
837 }
838
839 dbus_message_iter_get_basic(&sub, &id);
840 printf("Job %u changed.\n", id);
841 }
842 }
843
844finish:
845 if (m)
846 dbus_message_unref(m);
847
848 if (reply)
849 dbus_message_unref(reply);
850
851 dbus_error_free(&error);
852 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
853
854oom:
855 if (m)
856 dbus_message_unref(m);
857
858 if (reply)
859 dbus_message_unref(reply);
860
861 dbus_error_free(&error);
862 return DBUS_HANDLER_RESULT_NEED_MEMORY;
863}
864
865static int monitor(DBusConnection *bus, char **args, unsigned n) {
866 DBusMessage *m = NULL, *reply = NULL;
867 DBusError error;
868 int r;
869
870 dbus_error_init(&error);
871
872 dbus_bus_add_match(bus,
873 "type='signal',"
874 "sender='org.freedesktop.systemd1',"
875 "interface='org.freedesktop.systemd1.Manager',"
876 "path='/org/freedesktop/systemd1'",
877 &error);
878
879 if (dbus_error_is_set(&error)) {
880 log_error("Failed to add match: %s", error.message);
881 r = -EIO;
882 goto finish;
883 }
884
885 dbus_bus_add_match(bus,
886 "type='signal',"
887 "sender='org.freedesktop.systemd1',"
888 "interface='org.freedesktop.systemd1.Unit',"
889 "member='Changed'",
890 &error);
891
892 if (dbus_error_is_set(&error)) {
893 log_error("Failed to add match: %s", error.message);
894 r = -EIO;
895 goto finish;
896 }
897
898 dbus_bus_add_match(bus,
899 "type='signal',"
900 "sender='org.freedesktop.systemd1',"
901 "interface='org.freedesktop.systemd1.Job',"
902 "member='Changed'",
903 &error);
904
905 if (dbus_error_is_set(&error)) {
906 log_error("Failed to add match: %s", error.message);
907 r = -EIO;
908 goto finish;
909 }
910
911 if (!dbus_connection_add_filter(bus, monitor_filter, NULL, NULL)) {
912 log_error("Failed to add filter.");
913 r = -ENOMEM;
914 goto finish;
915 }
916
917 if (!(m = dbus_message_new_method_call(
918 "org.freedesktop.systemd1",
919 "/org/freedesktop/systemd1",
920 "org.freedesktop.systemd1.Manager",
921 "Subscribe"))) {
922 log_error("Could not allocate message.");
923 r = -ENOMEM;
924 goto finish;
925 }
926
927 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
928 log_error("Failed to issue method call: %s", error.message);
929 r = -EIO;
930 goto finish;
931 }
932
933 while (dbus_connection_read_write_dispatch(bus, -1))
934 ;
935
936 r = 0;
937
938finish:
939
940 /* This is slightly dirty, since we don't undo the filter or the matches. */
941
942 if (m)
943 dbus_message_unref(m);
944
945 if (reply)
946 dbus_message_unref(reply);
947
948 dbus_error_free(&error);
949
950 return r;
951}
952
953static int dump(DBusConnection *bus, char **args, unsigned n) {
954 DBusMessage *m = NULL, *reply = NULL;
955 DBusError error;
956 int r;
957 const char *text;
958
959 dbus_error_init(&error);
960
961 if (!(m = dbus_message_new_method_call(
962 "org.freedesktop.systemd1",
963 "/org/freedesktop/systemd1",
964 "org.freedesktop.systemd1.Manager",
965 "Dump"))) {
966 log_error("Could not allocate message.");
967 return -ENOMEM;
968 }
969
970 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
971 log_error("Failed to issue method call: %s", error.message);
972 r = -EIO;
973 goto finish;
974 }
975
976 if (!dbus_message_get_args(reply, &error,
977 DBUS_TYPE_STRING, &text,
978 DBUS_TYPE_INVALID)) {
979 log_error("Failed to parse reply: %s", error.message);
980 r = -EIO;
981 goto finish;
982 }
983
984 fputs(text, stdout);
985
986 r = 0;
987
988finish:
989 if (m)
990 dbus_message_unref(m);
991
992 if (reply)
993 dbus_message_unref(reply);
994
995 dbus_error_free(&error);
996
997 return r;
998}
999
1000static int snapshot(DBusConnection *bus, char **args, unsigned n) {
1001 DBusMessage *m = NULL, *reply = NULL;
1002 DBusError error;
1003 int r;
1004 const char *name = "", *path, *id;
1005 dbus_bool_t cleanup = FALSE;
1006 DBusMessageIter iter, sub;
1007 const char
1008 *interface = "org.freedesktop.systemd1.Unit",
1009 *property = "Id";
1010
1011 dbus_error_init(&error);
1012
1013 if (!(m = dbus_message_new_method_call(
1014 "org.freedesktop.systemd1",
1015 "/org/freedesktop/systemd1",
1016 "org.freedesktop.systemd1.Manager",
1017 "CreateSnapshot"))) {
1018 log_error("Could not allocate message.");
1019 return -ENOMEM;
1020 }
1021
1022 if (n > 1)
1023 name = args[1];
1024
1025 if (!dbus_message_append_args(m,
1026 DBUS_TYPE_STRING, &name,
1027 DBUS_TYPE_BOOLEAN, &cleanup,
1028 DBUS_TYPE_INVALID)) {
1029 log_error("Could not append arguments to message.");
1030 r = -ENOMEM;
1031 goto finish;
1032 }
1033
1034 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1035 log_error("Failed to issue method call: %s", error.message);
1036 r = -EIO;
1037 goto finish;
1038 }
1039
1040 if (!dbus_message_get_args(reply, &error,
1041 DBUS_TYPE_OBJECT_PATH, &path,
1042 DBUS_TYPE_INVALID)) {
1043 log_error("Failed to parse reply: %s", error.message);
1044 r = -EIO;
1045 goto finish;
1046 }
1047
1048 dbus_message_unref(m);
1049 if (!(m = dbus_message_new_method_call(
1050 "org.freedesktop.systemd1",
1051 path,
1052 "org.freedesktop.DBus.Properties",
1053 "Get"))) {
1054 log_error("Could not allocate message.");
1055 return -ENOMEM;
1056 }
1057
1058 if (!dbus_message_append_args(m,
1059 DBUS_TYPE_STRING, &interface,
1060 DBUS_TYPE_STRING, &property,
1061 DBUS_TYPE_INVALID)) {
1062 log_error("Could not append arguments to message.");
1063 r = -ENOMEM;
1064 goto finish;
1065 }
1066
1067 dbus_message_unref(reply);
1068 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1069 log_error("Failed to issue method call: %s", error.message);
1070 r = -EIO;
1071 goto finish;
1072 }
1073
1074 if (!dbus_message_iter_init(reply, &iter) ||
1075 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
1076 log_error("Failed to parse reply.");
1077 r = -EIO;
1078 goto finish;
1079 }
1080
1081 dbus_message_iter_recurse(&iter, &sub);
1082
1083 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
1084 log_error("Failed to parse reply.");
1085 r = -EIO;
1086 goto finish;
1087 }
1088
1089 dbus_message_iter_get_basic(&sub, &id);
1090 puts(id);
1091 r = 0;
1092
1093finish:
1094 if (m)
1095 dbus_message_unref(m);
1096
1097 if (reply)
1098 dbus_message_unref(reply);
1099
1100 dbus_error_free(&error);
1101
1102 return r;
1103}
1104
1105static int clear_jobs(DBusConnection *bus, char **args, unsigned n) {
1106 DBusMessage *m = NULL, *reply = NULL;
1107 DBusError error;
1108 int r;
1109 const char *method;
1110
1111 dbus_error_init(&error);
1112
1113 method =
1114 streq(args[0], "clear-jobs") ? "ClearJobs" :
1115 streq(args[0], "daemon-reload") ? "Reload" :
1116 streq(args[0], "daemon-reexec") ? "Reexecute" :
1117 "Exit";
1118
1119 if (!(m = dbus_message_new_method_call(
1120 "org.freedesktop.systemd1",
1121 "/org/freedesktop/systemd1",
1122 "org.freedesktop.systemd1.Manager",
1123 method))) {
1124 log_error("Could not allocate message.");
1125 return -ENOMEM;
1126 }
1127
1128 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1129 log_error("Failed to issue method call: %s", error.message);
1130 r = -EIO;
1131 goto finish;
1132 }
1133
1134 r = 0;
1135
1136finish:
1137 if (m)
1138 dbus_message_unref(m);
1139
1140 if (reply)
1141 dbus_message_unref(reply);
1142
1143 dbus_error_free(&error);
1144
1145 return r;
1146}
1147
1148static int show_enviroment(DBusConnection *bus, char **args, unsigned n) {
1149 DBusMessage *m = NULL, *reply = NULL;
1150 DBusError error;
1151 DBusMessageIter iter, sub, sub2;
1152 int r;
1153 const char
1154 *interface = "org.freedesktop.systemd1.Manager",
1155 *property = "Environment";
1156
1157 dbus_error_init(&error);
1158
1159 if (!(m = dbus_message_new_method_call(
1160 "org.freedesktop.systemd1",
1161 "/org/freedesktop/systemd1",
1162 "org.freedesktop.DBus.Properties",
1163 "Get"))) {
1164 log_error("Could not allocate message.");
1165 return -ENOMEM;
1166 }
1167
1168 if (!dbus_message_append_args(m,
1169 DBUS_TYPE_STRING, &interface,
1170 DBUS_TYPE_STRING, &property,
1171 DBUS_TYPE_INVALID)) {
1172 log_error("Could not append arguments to message.");
1173 r = -ENOMEM;
1174 goto finish;
1175 }
1176
1177 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1178 log_error("Failed to issue method call: %s", error.message);
1179 r = -EIO;
1180 goto finish;
1181 }
1182
1183 if (!dbus_message_iter_init(reply, &iter) ||
1184 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
1185 log_error("Failed to parse reply.");
1186 r = -EIO;
1187 goto finish;
1188 }
1189
1190 dbus_message_iter_recurse(&iter, &sub);
1191
1192 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_ARRAY ||
1193 dbus_message_iter_get_element_type(&sub) != DBUS_TYPE_STRING) {
1194 log_error("Failed to parse reply.");
1195 r = -EIO;
1196 goto finish;
1197 }
1198
1199 dbus_message_iter_recurse(&sub, &sub2);
1200
1201 while (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_INVALID) {
1202 const char *text;
1203
1204 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) {
1205 log_error("Failed to parse reply.");
1206 r = -EIO;
1207 goto finish;
1208 }
1209
1210 dbus_message_iter_get_basic(&sub2, &text);
1211 printf("%s\n", text);
1212
1213 dbus_message_iter_next(&sub2);
1214 }
1215
1216 r = 0;
1217
1218finish:
1219 if (m)
1220 dbus_message_unref(m);
1221
1222 if (reply)
1223 dbus_message_unref(reply);
1224
1225 dbus_error_free(&error);
1226
1227 return r;
1228}
1229
1230static int set_environment(DBusConnection *bus, char **args, unsigned n) {
1231 DBusMessage *m = NULL, *reply = NULL;
1232 DBusError error;
1233 int r;
1234 const char *method;
1235 DBusMessageIter iter, sub;
1236 unsigned i;
1237
1238 dbus_error_init(&error);
1239
1240 method = streq(args[0], "set-environment")
1241 ? "SetEnvironment"
1242 : "UnsetEnvironment";
1243
1244 if (!(m = dbus_message_new_method_call(
1245 "org.freedesktop.systemd1",
1246 "/org/freedesktop/systemd1",
1247 "org.freedesktop.systemd1.Manager",
1248 method))) {
1249
1250 log_error("Could not allocate message.");
1251 return -ENOMEM;
1252 }
1253
1254 dbus_message_iter_init_append(m, &iter);
1255
1256 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) {
1257 log_error("Could not append arguments to message.");
1258 r = -ENOMEM;
1259 goto finish;
1260 }
1261
1262 for (i = 1; i < n; i++)
1263 if (!dbus_message_iter_append_basic(&sub, DBUS_TYPE_STRING, &args[i])) {
1264 log_error("Could not append arguments to message.");
1265 r = -ENOMEM;
1266 goto finish;
1267 }
1268
1269 if (!dbus_message_iter_close_container(&iter, &sub)) {
1270 log_error("Could not append arguments to message.");
1271 r = -ENOMEM;
1272 goto finish;
1273 }
1274
1275 if (!(reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error))) {
1276 log_error("Failed to issue method call: %s", error.message);
1277 r = -EIO;
1278 goto finish;
1279 }
1280
1281 r = 0;
1282
1283finish:
1284 if (m)
1285 dbus_message_unref(m);
1286
1287 if (reply)
1288 dbus_message_unref(reply);
1289
1290 dbus_error_free(&error);
1291
1292 return r;
1293}
1294
1295static int help(void) {
1296
1297 printf("%s [options]\n\n"
1298 " -h --help Show this help\n"
1299 " -t --type=TYPE List only units of a particular type\n"
1300 " -a --all Show all units, including dead ones\n"
1301 " --replace When installing a new job, replace existing conflicting ones\n"
1302 " --system Connect to system bus\n"
1303 " --session Connect to session bus\n"
1304 " --block Wait until operation finished\n\n"
1305 "Commands:\n"
1306 " list-units List units\n"
1307 " list-jobs List jobs\n"
1308 " clear-jobs Cancel all jobs\n"
1309 " load [NAME...] Load one or more units\n"
1310 " cancel [JOB...] Cancel one or more jobs\n"
1311 " start [NAME...] Start one or more units\n"
1312 " stop [NAME...] Stop one or more units\n"
1313 " restart [NAME...] Restart one or more units\n"
1314 " reload [NAME...] Reload one or more units\n"
1315 " isolate [NAME] Start one unit and stop all others\n"
1316 " monitor Monitor unit/job changes\n"
1317 " dump Dump server status\n"
1318 " snapshot [NAME] Create a snapshot\n"
1319 " daemon-reload Reload daemon configuration\n"
1320 " daemon-reexecute Reexecute daemon\n"
1321 " daemon-exit Ask the daemon to quit\n"
1322 " show-environment Dump environment\n"
1323 " set-environment [NAME=VALUE...] Set one or more environment variables\n"
1324 " unset-environment [NAME...] Unset one or more environment variables\n",
5b6319dc 1325 program_invocation_short_name);
7e4249b9
LP
1326
1327 return 0;
1328}
1329
1330static int parse_argv(int argc, char *argv[]) {
1331
1332 enum {
1333 ARG_REPLACE = 0x100,
1334 ARG_SESSION,
1335 ARG_SYSTEM,
1336 ARG_BLOCK,
1337 };
1338
1339 static const struct option options[] = {
1340 { "help", no_argument, NULL, 'h' },
1341 { "type", required_argument, NULL, 't' },
1342 { "all", no_argument, NULL, 'a' },
1343 { "replace", no_argument, NULL, ARG_REPLACE },
1344 { "session", no_argument, NULL, ARG_SESSION },
1345 { "system", no_argument, NULL, ARG_SYSTEM },
b08a3550
LP
1346 { "block", no_argument, NULL, ARG_BLOCK },
1347 { NULL, 0, NULL, 0 }
7e4249b9
LP
1348 };
1349
1350 int c;
1351
1352 assert(argc >= 1);
1353 assert(argv);
1354
1355 while ((c = getopt_long(argc, argv, "hta", options, NULL)) >= 0) {
1356
1357 switch (c) {
1358
1359 case 'h':
1360 help();
1361 return 0;
1362
1363 case 't':
1364 arg_type = optarg;
1365 break;
1366
1367 case 'a':
1368 arg_all = true;
1369 break;
1370
1371 case ARG_REPLACE:
1372 arg_replace = true;
1373 break;
1374
1375 case ARG_SESSION:
1376 arg_session = true;
1377 break;
1378
1379 case ARG_SYSTEM:
1380 arg_session = false;
1381 break;
1382
1383 case ARG_BLOCK:
1384 arg_block = true;
1385 break;
1386
1387 case '?':
1388 return -EINVAL;
1389
1390 default:
1391 log_error("Unknown option code %c", c);
1392 return -EINVAL;
1393 }
1394 }
1395
1396 return 1;
1397}
1398
1399int main(int argc, char*argv[]) {
1400
7e4249b9
LP
1401 static const struct {
1402 const char* verb;
1403 const enum {
1404 MORE,
1405 LESS,
1406 EQUAL
1407 } argc_cmp;
1408 const int argc;
1409 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1410 } verbs[] = {
1411 { "list-units", LESS, 1, list_units },
1412 { "list-jobs", EQUAL, 1, list_jobs },
1413 { "clear-jobs", EQUAL, 1, clear_jobs },
1414 { "load", MORE, 2, load_unit },
1415 { "cancel", MORE, 2, cancel_job },
1416 { "start", MORE, 2, start_unit },
1417 { "stop", MORE, 2, start_unit },
1418 { "reload", MORE, 2, start_unit },
1419 { "restart", MORE, 2, start_unit },
1420 { "isolate", EQUAL, 2, isolate_unit },
1421 { "monitor", EQUAL, 1, monitor },
1422 { "dump", EQUAL, 1, dump },
1423 { "snapshot", LESS, 2, snapshot },
1424 { "daemon-reload", EQUAL, 1, clear_jobs },
1425 { "daemon-reexec", EQUAL, 1, clear_jobs },
1426 { "daemon-exit", EQUAL, 1, clear_jobs },
1427 { "show-environment", EQUAL, 1, show_enviroment },
1428 { "set-environment", MORE, 2, set_environment },
1429 { "unset-environment", MORE, 2, set_environment },
1430 };
1431
1432 int r, retval = 1, left;
1433 unsigned i;
1434 DBusConnection *bus = NULL;
1435 DBusError error;
1436
1437 dbus_error_init(&error);
1438
1439 log_set_target(LOG_TARGET_CONSOLE);
10e87ee7 1440 log_set_max_level(LOG_INFO);
7e4249b9
LP
1441 log_parse_environment();
1442
1443 if ((r = parse_argv(argc, argv)) < 0)
1444 goto finish;
1445 else if (r == 0) {
1446 retval = 0;
1447 goto finish;
1448 }
1449
1450 left = argc - optind;
1451
1452 if (left <= 0)
1453 /* Special rule: no arguments means "list-units" */
1454 i = 0;
1455 else {
1456 for (i = 0; i < ELEMENTSOF(verbs); i++)
1457 if (streq(argv[optind], verbs[i].verb))
1458 break;
1459
1460 if (i >= ELEMENTSOF(verbs)) {
1461 log_error("Unknown operation %s", argv[optind]);
1462 goto finish;
1463 }
1464 }
1465
1466 switch (verbs[i].argc_cmp) {
1467
1468 case EQUAL:
1469 if (left != verbs[i].argc) {
1470 log_error("Invalid number of arguments.");
1471 goto finish;
1472 }
1473
1474 break;
1475
1476 case MORE:
1477 if (left < verbs[i].argc) {
1478 log_error("Too few arguments.");
1479 goto finish;
1480 }
1481
1482 break;
1483
1484 case LESS:
1485 if (left > verbs[i].argc) {
1486 log_error("Too many arguments.");
1487 goto finish;
1488 }
1489
1490 break;
1491
1492 default:
1493 assert_not_reached("Unknown comparison operator.");
1494 }
1495
1496 if (!(bus = dbus_bus_get(arg_session ? DBUS_BUS_SESSION : DBUS_BUS_SYSTEM, &error))) {
1497 log_error("Failed to get D-Bus connection: %s", error.message);
1498 goto finish;
1499 }
1500
1501 dbus_connection_set_exit_on_disconnect(bus, FALSE);
1502
1503 retval = verbs[i].dispatch(bus, argv + optind, left) < 0;
1504
1505finish:
1506
1507 if (bus)
1508 dbus_connection_unref(bus);
1509
1510 dbus_shutdown();
1511
1512 return retval;
1513}