]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/loginctl.c
pager: introduce "jump to end" option
[thirdparty/systemd.git] / src / login / loginctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
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 Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <dbus/dbus.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <getopt.h>
27 #include <pwd.h>
28 #include <locale.h>
29
30 #include "log.h"
31 #include "util.h"
32 #include "macro.h"
33 #include "pager.h"
34 #include "dbus-common.h"
35 #include "build.h"
36 #include "strv.h"
37 #include "cgroup-show.h"
38 #include "sysfs-show.h"
39 #include "spawn-polkit-agent.h"
40
41 static char **arg_property = NULL;
42 static bool arg_all = false;
43 static bool arg_full = false;
44 static bool arg_no_pager = false;
45 static const char *arg_kill_who = NULL;
46 static int arg_signal = SIGTERM;
47 static enum transport {
48 TRANSPORT_NORMAL,
49 TRANSPORT_SSH,
50 TRANSPORT_POLKIT
51 } arg_transport = TRANSPORT_NORMAL;
52 static bool arg_ask_password = true;
53 static const char *arg_host = NULL;
54
55 static void pager_open_if_enabled(void) {
56
57 /* Cache result before we open the pager */
58 if (arg_no_pager)
59 return;
60
61 pager_open(false);
62 }
63
64 static void polkit_agent_open_if_enabled(void) {
65
66 /* Open the polkit agent as a child process if necessary */
67
68 if (!arg_ask_password)
69 return;
70
71 polkit_agent_open();
72 }
73
74 static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
75 DBusMessage *reply = NULL;
76 int r;
77 DBusMessageIter iter, sub, sub2;
78 unsigned k = 0;
79
80 pager_open_if_enabled();
81
82 r = bus_method_call_with_reply (
83 bus,
84 "org.freedesktop.login1",
85 "/org/freedesktop/login1",
86 "org.freedesktop.login1.Manager",
87 "ListSessions",
88 &reply,
89 NULL,
90 DBUS_TYPE_INVALID);
91 if (r)
92 goto finish;
93
94 if (!dbus_message_iter_init(reply, &iter) ||
95 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
96 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
97 log_error("Failed to parse reply.");
98 r = -EIO;
99 goto finish;
100 }
101
102 dbus_message_iter_recurse(&iter, &sub);
103
104 if (on_tty())
105 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
106
107 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
108 const char *id, *user, *seat, *object;
109 uint32_t uid;
110
111 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
112 log_error("Failed to parse reply.");
113 r = -EIO;
114 goto finish;
115 }
116
117 dbus_message_iter_recurse(&sub, &sub2);
118
119 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
120 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
121 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
122 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
123 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
124 log_error("Failed to parse reply.");
125 r = -EIO;
126 goto finish;
127 }
128
129 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
130
131 k++;
132
133 dbus_message_iter_next(&sub);
134 }
135
136 if (on_tty())
137 printf("\n%u sessions listed.\n", k);
138
139 r = 0;
140
141 finish:
142 if (reply)
143 dbus_message_unref(reply);
144
145 return r;
146 }
147
148 static int list_users(DBusConnection *bus, char **args, unsigned n) {
149 DBusMessage *reply = NULL;
150 int r;
151 DBusMessageIter iter, sub, sub2;
152 unsigned k = 0;
153
154 pager_open_if_enabled();
155
156 r = bus_method_call_with_reply (
157 bus,
158 "org.freedesktop.login1",
159 "/org/freedesktop/login1",
160 "org.freedesktop.login1.Manager",
161 "ListUsers",
162 &reply,
163 NULL,
164 DBUS_TYPE_INVALID);
165 if (r)
166 goto finish;
167
168 if (!dbus_message_iter_init(reply, &iter) ||
169 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
170 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
171 log_error("Failed to parse reply.");
172 r = -EIO;
173 goto finish;
174 }
175
176 dbus_message_iter_recurse(&iter, &sub);
177
178 if (on_tty())
179 printf("%10s %-16s\n", "UID", "USER");
180
181 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
182 const char *user, *object;
183 uint32_t uid;
184
185 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
186 log_error("Failed to parse reply.");
187 r = -EIO;
188 goto finish;
189 }
190
191 dbus_message_iter_recurse(&sub, &sub2);
192
193 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
194 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
195 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
196 log_error("Failed to parse reply.");
197 r = -EIO;
198 goto finish;
199 }
200
201 printf("%10u %-16s\n", (unsigned) uid, user);
202
203 k++;
204
205 dbus_message_iter_next(&sub);
206 }
207
208 if (on_tty())
209 printf("\n%u users listed.\n", k);
210
211 r = 0;
212
213 finish:
214 if (reply)
215 dbus_message_unref(reply);
216
217 return r;
218 }
219
220 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
221 DBusMessage *reply = NULL;
222 int r;
223 DBusMessageIter iter, sub, sub2;
224 unsigned k = 0;
225
226 pager_open_if_enabled();
227
228 r = bus_method_call_with_reply (
229 bus,
230 "org.freedesktop.login1",
231 "/org/freedesktop/login1",
232 "org.freedesktop.login1.Manager",
233 "ListSeats",
234 &reply,
235 NULL,
236 DBUS_TYPE_INVALID);
237 if (r)
238 goto finish;
239
240 if (!dbus_message_iter_init(reply, &iter) ||
241 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
242 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
243 log_error("Failed to parse reply.");
244 r = -EIO;
245 goto finish;
246 }
247
248 dbus_message_iter_recurse(&iter, &sub);
249
250 if (on_tty())
251 printf("%-16s\n", "SEAT");
252
253 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
254 const char *seat, *object;
255
256 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
257 log_error("Failed to parse reply.");
258 r = -EIO;
259 goto finish;
260 }
261
262 dbus_message_iter_recurse(&sub, &sub2);
263
264 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
265 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
266 log_error("Failed to parse reply.");
267 r = -EIO;
268 goto finish;
269 }
270
271 printf("%-16s\n", seat);
272
273 k++;
274
275 dbus_message_iter_next(&sub);
276 }
277
278 if (on_tty())
279 printf("\n%u seats listed.\n", k);
280
281 r = 0;
282
283 finish:
284 if (reply)
285 dbus_message_unref(reply);
286
287 return r;
288 }
289
290 typedef struct SessionStatusInfo {
291 const char *id;
292 uid_t uid;
293 const char *name;
294 usec_t timestamp;
295 const char *default_control_group;
296 int vtnr;
297 const char *seat;
298 const char *tty;
299 const char *display;
300 bool remote;
301 const char *remote_host;
302 const char *remote_user;
303 const char *service;
304 pid_t leader;
305 const char *type;
306 const char *class;
307 const char *state;
308 } SessionStatusInfo;
309
310 typedef struct UserStatusInfo {
311 uid_t uid;
312 const char *name;
313 usec_t timestamp;
314 const char *default_control_group;
315 const char *state;
316 char **sessions;
317 const char *display;
318 } UserStatusInfo;
319
320 typedef struct SeatStatusInfo {
321 const char *id;
322 const char *active_session;
323 char **sessions;
324 } SeatStatusInfo;
325
326 static void print_session_status_info(SessionStatusInfo *i) {
327 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
328 char since2[FORMAT_TIMESTAMP_MAX], *s2;
329 assert(i);
330
331 printf("%s - ", strna(i->id));
332
333 if (i->name)
334 printf("%s (%u)\n", i->name, (unsigned) i->uid);
335 else
336 printf("%u\n", (unsigned) i->uid);
337
338 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
339 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
340
341 if (s1)
342 printf("\t Since: %s; %s\n", s2, s1);
343 else if (s2)
344 printf("\t Since: %s\n", s2);
345
346 if (i->leader > 0) {
347 char *t = NULL;
348
349 printf("\t Leader: %u", (unsigned) i->leader);
350
351 get_process_comm(i->leader, &t);
352 if (t) {
353 printf(" (%s)", t);
354 free(t);
355 }
356
357 printf("\n");
358 }
359
360 if (i->seat) {
361 printf("\t Seat: %s", i->seat);
362
363 if (i->vtnr > 0)
364 printf("; vc%i", i->vtnr);
365
366 printf("\n");
367 }
368
369 if (i->tty)
370 printf("\t TTY: %s\n", i->tty);
371 else if (i->display)
372 printf("\t Display: %s\n", i->display);
373
374 if (i->remote_host && i->remote_user)
375 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
376 else if (i->remote_host)
377 printf("\t Remote: %s\n", i->remote_host);
378 else if (i->remote_user)
379 printf("\t Remote: user %s\n", i->remote_user);
380 else if (i->remote)
381 printf("\t Remote: Yes\n");
382
383 if (i->service) {
384 printf("\t Service: %s", i->service);
385
386 if (i->type)
387 printf("; type %s", i->type);
388
389 if (i->class)
390 printf("; class %s", i->class);
391
392 printf("\n");
393 } else if (i->type) {
394 printf("\t Type: %s\n", i->type);
395
396 if (i->class)
397 printf("; class %s", i->class);
398 } else if (i->class)
399 printf("\t Class: %s\n", i->class);
400
401 if (i->state)
402 printf("\t State: %s\n", i->state);
403
404 if (i->default_control_group) {
405 unsigned c;
406 int output_flags =
407 arg_all * OUTPUT_SHOW_ALL |
408 arg_full * OUTPUT_FULL_WIDTH;
409
410 printf("\t CGroup: %s\n", i->default_control_group);
411
412 if (arg_transport != TRANSPORT_SSH) {
413 c = columns();
414 if (c > 18)
415 c -= 18;
416 else
417 c = 0;
418
419 show_cgroup_and_extra_by_spec(i->default_control_group,
420 "\t\t ", c, false, &i->leader,
421 i->leader > 0 ? 1 : 0,
422 output_flags);
423 }
424 }
425 }
426
427 static void print_user_status_info(UserStatusInfo *i) {
428 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
429 char since2[FORMAT_TIMESTAMP_MAX], *s2;
430 assert(i);
431
432 if (i->name)
433 printf("%s (%u)\n", i->name, (unsigned) i->uid);
434 else
435 printf("%u\n", (unsigned) i->uid);
436
437 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
438 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
439
440 if (s1)
441 printf("\t Since: %s; %s\n", s2, s1);
442 else if (s2)
443 printf("\t Since: %s\n", s2);
444
445 if (!isempty(i->state))
446 printf("\t State: %s\n", i->state);
447
448 if (!strv_isempty(i->sessions)) {
449 char **l;
450 printf("\tSessions:");
451
452 STRV_FOREACH(l, i->sessions) {
453 if (streq_ptr(*l, i->display))
454 printf(" *%s", *l);
455 else
456 printf(" %s", *l);
457 }
458
459 printf("\n");
460 }
461
462 if (i->default_control_group) {
463 unsigned c;
464 int output_flags =
465 arg_all * OUTPUT_SHOW_ALL |
466 arg_full * OUTPUT_FULL_WIDTH;
467
468 printf("\t CGroup: %s\n", i->default_control_group);
469
470 if (arg_transport != TRANSPORT_SSH) {
471 c = columns();
472 if (c > 18)
473 c -= 18;
474 else
475 c = 0;
476
477 show_cgroup_by_path(i->default_control_group, "\t\t ",
478 c, false, output_flags);
479 }
480 }
481 }
482
483 static void print_seat_status_info(SeatStatusInfo *i) {
484 assert(i);
485
486 printf("%s\n", strna(i->id));
487
488 if (!strv_isempty(i->sessions)) {
489 char **l;
490 printf("\tSessions:");
491
492 STRV_FOREACH(l, i->sessions) {
493 if (streq_ptr(*l, i->active_session))
494 printf(" *%s", *l);
495 else
496 printf(" %s", *l);
497 }
498
499 printf("\n");
500 }
501
502 if (arg_transport != TRANSPORT_SSH) {
503 unsigned c;
504
505 c = columns();
506 if (c > 21)
507 c -= 21;
508 else
509 c = 0;
510
511 printf("\t Devices:\n");
512
513 show_sysfs(i->id, "\t\t ", c);
514 }
515 }
516
517 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
518 assert(name);
519 assert(iter);
520 assert(i);
521
522 switch (dbus_message_iter_get_arg_type(iter)) {
523
524 case DBUS_TYPE_STRING: {
525 const char *s;
526
527 dbus_message_iter_get_basic(iter, &s);
528
529 if (!isempty(s)) {
530 if (streq(name, "Id"))
531 i->id = s;
532 else if (streq(name, "Name"))
533 i->name = s;
534 else if (streq(name, "DefaultControlGroup"))
535 i->default_control_group = s;
536 else if (streq(name, "TTY"))
537 i->tty = s;
538 else if (streq(name, "Display"))
539 i->display = s;
540 else if (streq(name, "RemoteHost"))
541 i->remote_host = s;
542 else if (streq(name, "RemoteUser"))
543 i->remote_user = s;
544 else if (streq(name, "Service"))
545 i->service = s;
546 else if (streq(name, "Type"))
547 i->type = s;
548 else if (streq(name, "Class"))
549 i->class = s;
550 else if (streq(name, "State"))
551 i->state = s;
552 }
553 break;
554 }
555
556 case DBUS_TYPE_UINT32: {
557 uint32_t u;
558
559 dbus_message_iter_get_basic(iter, &u);
560
561 if (streq(name, "VTNr"))
562 i->vtnr = (int) u;
563 else if (streq(name, "Leader"))
564 i->leader = (pid_t) u;
565
566 break;
567 }
568
569 case DBUS_TYPE_BOOLEAN: {
570 dbus_bool_t b;
571
572 dbus_message_iter_get_basic(iter, &b);
573
574 if (streq(name, "Remote"))
575 i->remote = b;
576
577 break;
578 }
579
580 case DBUS_TYPE_UINT64: {
581 uint64_t u;
582
583 dbus_message_iter_get_basic(iter, &u);
584
585 if (streq(name, "Timestamp"))
586 i->timestamp = (usec_t) u;
587
588 break;
589 }
590
591 case DBUS_TYPE_STRUCT: {
592 DBusMessageIter sub;
593
594 dbus_message_iter_recurse(iter, &sub);
595
596 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
597 uint32_t u;
598
599 dbus_message_iter_get_basic(&sub, &u);
600 i->uid = (uid_t) u;
601
602 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
603 const char *s;
604
605 dbus_message_iter_get_basic(&sub, &s);
606
607 if (!isempty(s))
608 i->seat = s;
609 }
610
611 break;
612 }
613 }
614
615 return 0;
616 }
617
618 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
619 assert(name);
620 assert(iter);
621 assert(i);
622
623 switch (dbus_message_iter_get_arg_type(iter)) {
624
625 case DBUS_TYPE_STRING: {
626 const char *s;
627
628 dbus_message_iter_get_basic(iter, &s);
629
630 if (!isempty(s)) {
631 if (streq(name, "Name"))
632 i->name = s;
633 else if (streq(name, "DefaultControlGroup"))
634 i->default_control_group = s;
635 else if (streq(name, "State"))
636 i->state = s;
637 }
638 break;
639 }
640
641 case DBUS_TYPE_UINT32: {
642 uint32_t u;
643
644 dbus_message_iter_get_basic(iter, &u);
645
646 if (streq(name, "UID"))
647 i->uid = (uid_t) u;
648
649 break;
650 }
651
652 case DBUS_TYPE_UINT64: {
653 uint64_t u;
654
655 dbus_message_iter_get_basic(iter, &u);
656
657 if (streq(name, "Timestamp"))
658 i->timestamp = (usec_t) u;
659
660 break;
661 }
662
663 case DBUS_TYPE_STRUCT: {
664 DBusMessageIter sub;
665
666 dbus_message_iter_recurse(iter, &sub);
667
668 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
669 const char *s;
670
671 dbus_message_iter_get_basic(&sub, &s);
672
673 if (!isempty(s))
674 i->display = s;
675 }
676
677 break;
678 }
679
680 case DBUS_TYPE_ARRAY: {
681
682 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
683 DBusMessageIter sub, sub2;
684
685 dbus_message_iter_recurse(iter, &sub);
686 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
687 const char *id;
688 const char *path;
689
690 dbus_message_iter_recurse(&sub, &sub2);
691
692 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
693 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
694 char **l;
695
696 l = strv_append(i->sessions, id);
697 if (!l)
698 return -ENOMEM;
699
700 strv_free(i->sessions);
701 i->sessions = l;
702 }
703
704 dbus_message_iter_next(&sub);
705 }
706
707 return 0;
708 }
709 }
710 }
711
712 return 0;
713 }
714
715 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
716 assert(name);
717 assert(iter);
718 assert(i);
719
720 switch (dbus_message_iter_get_arg_type(iter)) {
721
722 case DBUS_TYPE_STRING: {
723 const char *s;
724
725 dbus_message_iter_get_basic(iter, &s);
726
727 if (!isempty(s)) {
728 if (streq(name, "Id"))
729 i->id = s;
730 }
731 break;
732 }
733
734 case DBUS_TYPE_STRUCT: {
735 DBusMessageIter sub;
736
737 dbus_message_iter_recurse(iter, &sub);
738
739 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
740 const char *s;
741
742 dbus_message_iter_get_basic(&sub, &s);
743
744 if (!isempty(s))
745 i->active_session = s;
746 }
747
748 break;
749 }
750
751 case DBUS_TYPE_ARRAY: {
752
753 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
754 DBusMessageIter sub, sub2;
755
756 dbus_message_iter_recurse(iter, &sub);
757 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
758 const char *id;
759 const char *path;
760
761 dbus_message_iter_recurse(&sub, &sub2);
762
763 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
764 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
765 char **l;
766
767 l = strv_append(i->sessions, id);
768 if (!l)
769 return -ENOMEM;
770
771 strv_free(i->sessions);
772 i->sessions = l;
773 }
774
775 dbus_message_iter_next(&sub);
776 }
777
778 return 0;
779 }
780 }
781 }
782
783 return 0;
784 }
785
786 static int print_property(const char *name, DBusMessageIter *iter) {
787 assert(name);
788 assert(iter);
789
790 if (arg_property && !strv_find(arg_property, name))
791 return 0;
792
793 switch (dbus_message_iter_get_arg_type(iter)) {
794
795 case DBUS_TYPE_STRUCT: {
796 DBusMessageIter sub;
797
798 dbus_message_iter_recurse(iter, &sub);
799
800 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
801 (streq(name, "Display") || streq(name, "ActiveSession"))) {
802 const char *s;
803
804 dbus_message_iter_get_basic(&sub, &s);
805
806 if (arg_all || !isempty(s))
807 printf("%s=%s\n", name, s);
808 return 0;
809 }
810 break;
811 }
812
813 case DBUS_TYPE_ARRAY:
814
815 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
816 DBusMessageIter sub, sub2;
817 bool found = false;
818
819 dbus_message_iter_recurse(iter, &sub);
820 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
821 const char *id;
822 const char *path;
823
824 dbus_message_iter_recurse(&sub, &sub2);
825
826 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
827 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
828 if (found)
829 printf(" %s", id);
830 else {
831 printf("%s=%s", name, id);
832 found = true;
833 }
834 }
835
836 dbus_message_iter_next(&sub);
837 }
838
839 if (!found && arg_all)
840 printf("%s=\n", name);
841 else if (found)
842 printf("\n");
843
844 return 0;
845 }
846
847 break;
848 }
849
850 if (generic_print_property(name, iter, arg_all) > 0)
851 return 0;
852
853 if (arg_all)
854 printf("%s=[unprintable]\n", name);
855
856 return 0;
857 }
858
859 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
860 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
861 const char *interface = "";
862 int r;
863 DBusMessageIter iter, sub, sub2, sub3;
864 SessionStatusInfo session_info;
865 UserStatusInfo user_info;
866 SeatStatusInfo seat_info;
867
868 assert(path);
869 assert(new_line);
870
871 zero(session_info);
872 zero(user_info);
873 zero(seat_info);
874
875 r = bus_method_call_with_reply(
876 bus,
877 "org.freedesktop.login1",
878 path,
879 "org.freedesktop.DBus.Properties",
880 "GetAll",
881 &reply,
882 NULL,
883 DBUS_TYPE_STRING, &interface,
884 DBUS_TYPE_INVALID);
885 if (r < 0)
886 goto finish;
887
888 if (!dbus_message_iter_init(reply, &iter) ||
889 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
890 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
891 log_error("Failed to parse reply.");
892 r = -EIO;
893 goto finish;
894 }
895
896 dbus_message_iter_recurse(&iter, &sub);
897
898 if (*new_line)
899 printf("\n");
900
901 *new_line = true;
902
903 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
904 const char *name;
905
906 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
907 log_error("Failed to parse reply.");
908 r = -EIO;
909 goto finish;
910 }
911
912 dbus_message_iter_recurse(&sub, &sub2);
913
914 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
915 log_error("Failed to parse reply.");
916 r = -EIO;
917 goto finish;
918 }
919
920 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
921 log_error("Failed to parse reply.");
922 r = -EIO;
923 goto finish;
924 }
925
926 dbus_message_iter_recurse(&sub2, &sub3);
927
928 if (show_properties)
929 r = print_property(name, &sub3);
930 else if (strstr(verb, "session"))
931 r = status_property_session(name, &sub3, &session_info);
932 else if (strstr(verb, "user"))
933 r = status_property_user(name, &sub3, &user_info);
934 else
935 r = status_property_seat(name, &sub3, &seat_info);
936
937 if (r < 0) {
938 log_error("Failed to parse reply.");
939 goto finish;
940 }
941
942 dbus_message_iter_next(&sub);
943 }
944
945 if (!show_properties) {
946 if (strstr(verb, "session"))
947 print_session_status_info(&session_info);
948 else if (strstr(verb, "user"))
949 print_user_status_info(&user_info);
950 else
951 print_seat_status_info(&seat_info);
952 }
953
954 r = 0;
955
956 finish:
957 strv_free(seat_info.sessions);
958 strv_free(user_info.sessions);
959
960 return r;
961 }
962
963 static int show(DBusConnection *bus, char **args, unsigned n) {
964 DBusMessage *reply = NULL;
965 int r, ret = 0;
966 DBusError error;
967 unsigned i;
968 bool show_properties, new_line = false;
969
970 assert(bus);
971 assert(args);
972
973 dbus_error_init(&error);
974
975 show_properties = !strstr(args[0], "status");
976
977 pager_open_if_enabled();
978
979 if (show_properties && n <= 1) {
980 /* If not argument is specified inspect the manager
981 * itself */
982
983 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
984 goto finish;
985 }
986
987 for (i = 1; i < n; i++) {
988 const char *path = NULL;
989
990 if (strstr(args[0], "session")) {
991
992 ret = bus_method_call_with_reply (
993 bus,
994 "org.freedesktop.login1",
995 "/org/freedesktop/login1",
996 "org.freedesktop.login1.Manager",
997 "GetSession",
998 &reply,
999 NULL,
1000 DBUS_TYPE_STRING, &args[i],
1001 DBUS_TYPE_INVALID);
1002
1003 } else if (strstr(args[0], "user")) {
1004 uid_t uid;
1005 uint32_t u;
1006
1007 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1008 if (ret < 0) {
1009 log_error("User %s unknown.", args[i]);
1010 goto finish;
1011 }
1012
1013 u = (uint32_t) uid;
1014 ret = bus_method_call_with_reply (
1015 bus,
1016 "org.freedesktop.login1",
1017 "/org/freedesktop/login1",
1018 "org.freedesktop.login1.Manager",
1019 "GetUser",
1020 &reply,
1021 NULL,
1022 DBUS_TYPE_UINT32, &u,
1023 DBUS_TYPE_INVALID);
1024 } else {
1025
1026 ret = bus_method_call_with_reply (
1027 bus,
1028 "org.freedesktop.login1",
1029 "/org/freedesktop/login1",
1030 "org.freedesktop.login1.Manager",
1031 "GetSeat",
1032 &reply,
1033 NULL,
1034 DBUS_TYPE_STRING, &args[i],
1035 DBUS_TYPE_INVALID);
1036 }
1037 if (ret)
1038 goto finish;
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", bus_error_message(&error));
1044 ret = -EIO;
1045 goto finish;
1046 }
1047
1048 r = show_one(args[0], bus, path, show_properties, &new_line);
1049 if (r != 0)
1050 ret = r;
1051
1052 dbus_message_unref(reply);
1053 reply = NULL;
1054 }
1055
1056 finish:
1057 if (reply)
1058 dbus_message_unref(reply);
1059
1060 dbus_error_free(&error);
1061
1062 return ret;
1063 }
1064
1065 static int activate(DBusConnection *bus, char **args, unsigned n) {
1066 int ret = 0;
1067 unsigned i;
1068
1069 assert(args);
1070
1071 for (i = 1; i < n; i++) {
1072
1073 ret = bus_method_call_with_reply (
1074 bus,
1075 "org.freedesktop.login1",
1076 "/org/freedesktop/login1",
1077 "org.freedesktop.login1.Manager",
1078 streq(args[0], "lock-session") ? "LockSession" :
1079 streq(args[0], "unlock-session") ? "UnlockSession" :
1080 streq(args[0], "terminate-session") ? "TerminateSession" :
1081 "ActivateSession",
1082 NULL,
1083 NULL,
1084 DBUS_TYPE_STRING, &args[i],
1085 DBUS_TYPE_INVALID);
1086 if (ret)
1087 goto finish;
1088 }
1089
1090 finish:
1091 return ret;
1092 }
1093
1094 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1095 int ret = 0;
1096 unsigned i;
1097
1098 assert(args);
1099
1100 if (!arg_kill_who)
1101 arg_kill_who = "all";
1102
1103 for (i = 1; i < n; i++) {
1104 ret = bus_method_call_with_reply (
1105 bus,
1106 "org.freedesktop.login1",
1107 "/org/freedesktop/login1",
1108 "org.freedesktop.login1.Manager",
1109 "KillSession",
1110 NULL,
1111 NULL,
1112 DBUS_TYPE_STRING, &args[i],
1113 DBUS_TYPE_STRING, &arg_kill_who,
1114 DBUS_TYPE_INT32, &arg_signal,
1115 DBUS_TYPE_INVALID);
1116 if (ret)
1117 goto finish;
1118 }
1119
1120 finish:
1121 return ret;
1122 }
1123
1124 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1125 int ret = 0;
1126 unsigned i;
1127 dbus_bool_t b, interactive = true;
1128
1129 assert(args);
1130
1131 polkit_agent_open_if_enabled();
1132
1133 b = streq(args[0], "enable-linger");
1134
1135 for (i = 1; i < n; i++) {
1136 uint32_t u;
1137 uid_t uid;
1138
1139 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1140 if (ret < 0) {
1141 log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1142 goto finish;
1143 }
1144
1145 u = (uint32_t) uid;
1146 ret = bus_method_call_with_reply (
1147 bus,
1148 "org.freedesktop.login1",
1149 "/org/freedesktop/login1",
1150 "org.freedesktop.login1.Manager",
1151 "SetUserLinger",
1152 NULL,
1153 NULL,
1154 DBUS_TYPE_UINT32, &u,
1155 DBUS_TYPE_BOOLEAN, &b,
1156 DBUS_TYPE_BOOLEAN, &interactive,
1157 DBUS_TYPE_INVALID);
1158 if (ret)
1159 goto finish;
1160 }
1161
1162 finish:
1163 return ret;
1164 }
1165
1166 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1167 int ret = 0;
1168 unsigned i;
1169
1170 assert(args);
1171
1172 for (i = 1; i < n; i++) {
1173 uint32_t u;
1174 uid_t uid;
1175
1176 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1177 if (ret < 0) {
1178 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1179 goto finish;
1180 }
1181
1182 u = (uint32_t) uid;
1183 ret = bus_method_call_with_reply (
1184 bus,
1185 "org.freedesktop.login1",
1186 "/org/freedesktop/login1",
1187 "org.freedesktop.login1.Manager",
1188 "TerminateUser",
1189 NULL,
1190 NULL,
1191 DBUS_TYPE_UINT32, &u,
1192 DBUS_TYPE_INVALID);
1193 if (ret)
1194 goto finish;
1195 }
1196
1197 finish:
1198 return ret;
1199 }
1200
1201 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1202 int ret = 0;
1203 unsigned i;
1204
1205 assert(args);
1206
1207 if (!arg_kill_who)
1208 arg_kill_who = "all";
1209
1210 for (i = 1; i < n; i++) {
1211 uid_t uid;
1212 uint32_t u;
1213
1214 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1215 if (ret < 0) {
1216 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1217 goto finish;
1218 }
1219
1220 u = (uint32_t) uid;
1221 ret = bus_method_call_with_reply (
1222 bus,
1223 "org.freedesktop.login1",
1224 "/org/freedesktop/login1",
1225 "org.freedesktop.login1.Manager",
1226 "KillUser",
1227 NULL,
1228 NULL,
1229 DBUS_TYPE_UINT32, &u,
1230 DBUS_TYPE_INT32, &arg_signal,
1231 DBUS_TYPE_INVALID);
1232 if (ret)
1233 goto finish;
1234 }
1235
1236 finish:
1237 return ret;
1238 }
1239
1240 static int attach(DBusConnection *bus, char **args, unsigned n) {
1241 int ret = 0;
1242 unsigned i;
1243 dbus_bool_t interactive = true;
1244
1245 assert(args);
1246
1247 polkit_agent_open_if_enabled();
1248
1249 for (i = 2; i < n; i++) {
1250 ret = bus_method_call_with_reply (
1251 bus,
1252 "org.freedesktop.login1",
1253 "/org/freedesktop/login1",
1254 "org.freedesktop.login1.Manager",
1255 "AttachDevice",
1256 NULL,
1257 NULL,
1258 DBUS_TYPE_STRING, &args[1],
1259 DBUS_TYPE_STRING, &args[i],
1260 DBUS_TYPE_BOOLEAN, &interactive,
1261 DBUS_TYPE_INVALID);
1262 if (ret)
1263 goto finish;
1264 }
1265
1266 finish:
1267 return ret;
1268 }
1269
1270 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1271 dbus_bool_t interactive = true;
1272
1273 assert(args);
1274
1275 polkit_agent_open_if_enabled();
1276
1277 return bus_method_call_with_reply (
1278 bus,
1279 "org.freedesktop.login1",
1280 "/org/freedesktop/login1",
1281 "org.freedesktop.login1.Manager",
1282 "FlushDevices",
1283 NULL,
1284 NULL,
1285 DBUS_TYPE_BOOLEAN, &interactive,
1286 DBUS_TYPE_INVALID);
1287 }
1288
1289 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1290 assert(args);
1291
1292 polkit_agent_open_if_enabled();
1293
1294 return bus_method_call_with_reply (
1295 bus,
1296 "org.freedesktop.login1",
1297 "/org/freedesktop/login1",
1298 "org.freedesktop.login1.Manager",
1299 streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1300 NULL,
1301 NULL,
1302 DBUS_TYPE_INVALID);
1303 }
1304
1305 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1306 int ret = 0;
1307 unsigned i;
1308
1309 assert(args);
1310
1311 for (i = 1; i < n; i++) {
1312 ret = bus_method_call_with_reply (
1313 bus,
1314 "org.freedesktop.login1",
1315 "/org/freedesktop/login1",
1316 "org.freedesktop.login1.Manager",
1317 "TerminateSeat",
1318 NULL,
1319 NULL,
1320 DBUS_TYPE_STRING, &args[i],
1321 DBUS_TYPE_INVALID);
1322 if (ret)
1323 goto finish;
1324 }
1325
1326 finish:
1327 return ret;
1328 }
1329
1330 static int help(void) {
1331
1332 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1333 "Send control commands to or query the login manager.\n\n"
1334 " -h --help Show this help\n"
1335 " --version Show package version\n"
1336 " -p --property=NAME Show only properties by this name\n"
1337 " -a --all Show all properties, including empty ones\n"
1338 " --kill-who=WHO Who to send signal to\n"
1339 " --full Do not ellipsize output\n"
1340 " -s --signal=SIGNAL Which signal to send\n"
1341 " --no-ask-password Don't prompt for password\n"
1342 " -H --host=[USER@]HOST Show information for remote host\n"
1343 " -P --privileged Acquire privileges before execution\n"
1344 " --no-pager Do not pipe output into a pager\n\n"
1345 "Commands:\n"
1346 " list-sessions List sessions\n"
1347 " session-status [ID...] Show session status\n"
1348 " show-session [ID...] Show properties of one or more sessions\n"
1349 " activate [ID] Activate a session\n"
1350 " lock-session [ID...] Screen lock one or more sessions\n"
1351 " unlock-session [ID...] Screen unlock one or more sessions\n"
1352 " lock-sessions Screen lock all current sessions\n"
1353 " unlock-sessions Screen unlock all current sessions\n"
1354 " terminate-session [ID...] Terminate one or more sessions\n"
1355 " kill-session [ID...] Send signal to processes of a session\n"
1356 " list-users List users\n"
1357 " user-status [USER...] Show user status\n"
1358 " show-user [USER...] Show properties of one or more users\n"
1359 " enable-linger [USER...] Enable linger state of one or more users\n"
1360 " disable-linger [USER...] Disable linger state of one or more users\n"
1361 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1362 " kill-user [USER...] Send signal to processes of a user\n"
1363 " list-seats List seats\n"
1364 " seat-status [NAME...] Show seat status\n"
1365 " show-seat [NAME...] Show properties of one or more seats\n"
1366 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1367 " flush-devices Flush all device associations\n"
1368 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1369 program_invocation_short_name);
1370
1371 return 0;
1372 }
1373
1374 static int parse_argv(int argc, char *argv[]) {
1375
1376 enum {
1377 ARG_VERSION = 0x100,
1378 ARG_NO_PAGER,
1379 ARG_KILL_WHO,
1380 ARG_NO_ASK_PASSWORD,
1381 ARG_FULL,
1382 };
1383
1384 static const struct option options[] = {
1385 { "help", no_argument, NULL, 'h' },
1386 { "version", no_argument, NULL, ARG_VERSION },
1387 { "property", required_argument, NULL, 'p' },
1388 { "all", no_argument, NULL, 'a' },
1389 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1390 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1391 { "signal", required_argument, NULL, 's' },
1392 { "host", required_argument, NULL, 'H' },
1393 { "privileged", no_argument, NULL, 'P' },
1394 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1395 { "full", no_argument, NULL, ARG_FULL },
1396 { NULL, 0, NULL, 0 }
1397 };
1398
1399 int c;
1400
1401 assert(argc >= 0);
1402 assert(argv);
1403
1404 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1405
1406 switch (c) {
1407
1408 case 'h':
1409 help();
1410 return 0;
1411
1412 case ARG_VERSION:
1413 puts(PACKAGE_STRING);
1414 puts(SYSTEMD_FEATURES);
1415 return 0;
1416
1417 case 'p': {
1418 char **l;
1419
1420 l = strv_append(arg_property, optarg);
1421 if (!l)
1422 return -ENOMEM;
1423
1424 strv_free(arg_property);
1425 arg_property = l;
1426
1427 /* If the user asked for a particular
1428 * property, show it to him, even if it is
1429 * empty. */
1430 arg_all = true;
1431 break;
1432 }
1433
1434 case 'a':
1435 arg_all = true;
1436 break;
1437
1438 case ARG_NO_PAGER:
1439 arg_no_pager = true;
1440 break;
1441
1442 case ARG_NO_ASK_PASSWORD:
1443 arg_ask_password = false;
1444 break;
1445
1446 case ARG_KILL_WHO:
1447 arg_kill_who = optarg;
1448 break;
1449
1450 case 's':
1451 arg_signal = signal_from_string_try_harder(optarg);
1452 if (arg_signal < 0) {
1453 log_error("Failed to parse signal string %s.", optarg);
1454 return -EINVAL;
1455 }
1456 break;
1457
1458 case 'P':
1459 arg_transport = TRANSPORT_POLKIT;
1460 break;
1461
1462 case 'H':
1463 arg_transport = TRANSPORT_SSH;
1464 arg_host = optarg;
1465 break;
1466
1467 case ARG_FULL:
1468 arg_full = true;
1469 break;
1470
1471 case '?':
1472 return -EINVAL;
1473
1474 default:
1475 log_error("Unknown option code %c", c);
1476 return -EINVAL;
1477 }
1478 }
1479
1480 return 1;
1481 }
1482
1483 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1484
1485 static const struct {
1486 const char* verb;
1487 const enum {
1488 MORE,
1489 LESS,
1490 EQUAL
1491 } argc_cmp;
1492 const int argc;
1493 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1494 } verbs[] = {
1495 { "list-sessions", LESS, 1, list_sessions },
1496 { "session-status", MORE, 2, show },
1497 { "show-session", MORE, 1, show },
1498 { "activate", EQUAL, 2, activate },
1499 { "lock-session", MORE, 2, activate },
1500 { "unlock-session", MORE, 2, activate },
1501 { "lock-sessions", EQUAL, 1, lock_sessions },
1502 { "unlock-sessions", EQUAL, 1, lock_sessions },
1503 { "terminate-session", MORE, 2, activate },
1504 { "kill-session", MORE, 2, kill_session },
1505 { "list-users", EQUAL, 1, list_users },
1506 { "user-status", MORE, 2, show },
1507 { "show-user", MORE, 1, show },
1508 { "enable-linger", MORE, 2, enable_linger },
1509 { "disable-linger", MORE, 2, enable_linger },
1510 { "terminate-user", MORE, 2, terminate_user },
1511 { "kill-user", MORE, 2, kill_user },
1512 { "list-seats", EQUAL, 1, list_seats },
1513 { "seat-status", MORE, 2, show },
1514 { "show-seat", MORE, 1, show },
1515 { "attach", MORE, 3, attach },
1516 { "flush-devices", EQUAL, 1, flush_devices },
1517 { "terminate-seat", MORE, 2, terminate_seat },
1518 };
1519
1520 int left;
1521 unsigned i;
1522
1523 assert(argc >= 0);
1524 assert(argv);
1525 assert(error);
1526
1527 left = argc - optind;
1528
1529 if (left <= 0)
1530 /* Special rule: no arguments means "list-sessions" */
1531 i = 0;
1532 else {
1533 if (streq(argv[optind], "help")) {
1534 help();
1535 return 0;
1536 }
1537
1538 for (i = 0; i < ELEMENTSOF(verbs); i++)
1539 if (streq(argv[optind], verbs[i].verb))
1540 break;
1541
1542 if (i >= ELEMENTSOF(verbs)) {
1543 log_error("Unknown operation %s", argv[optind]);
1544 return -EINVAL;
1545 }
1546 }
1547
1548 switch (verbs[i].argc_cmp) {
1549
1550 case EQUAL:
1551 if (left != verbs[i].argc) {
1552 log_error("Invalid number of arguments.");
1553 return -EINVAL;
1554 }
1555
1556 break;
1557
1558 case MORE:
1559 if (left < verbs[i].argc) {
1560 log_error("Too few arguments.");
1561 return -EINVAL;
1562 }
1563
1564 break;
1565
1566 case LESS:
1567 if (left > verbs[i].argc) {
1568 log_error("Too many arguments.");
1569 return -EINVAL;
1570 }
1571
1572 break;
1573
1574 default:
1575 assert_not_reached("Unknown comparison operator.");
1576 }
1577
1578 if (!bus) {
1579 log_error("Failed to get D-Bus connection: %s", error->message);
1580 return -EIO;
1581 }
1582
1583 return verbs[i].dispatch(bus, argv + optind, left);
1584 }
1585
1586 int main(int argc, char*argv[]) {
1587 int r, retval = EXIT_FAILURE;
1588 DBusConnection *bus = NULL;
1589 DBusError error;
1590
1591 dbus_error_init(&error);
1592
1593 setlocale(LC_ALL, "");
1594 log_parse_environment();
1595 log_open();
1596
1597 r = parse_argv(argc, argv);
1598 if (r < 0)
1599 goto finish;
1600 else if (r == 0) {
1601 retval = EXIT_SUCCESS;
1602 goto finish;
1603 }
1604
1605 if (arg_transport == TRANSPORT_NORMAL)
1606 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1607 else if (arg_transport == TRANSPORT_POLKIT)
1608 bus_connect_system_polkit(&bus, &error);
1609 else if (arg_transport == TRANSPORT_SSH)
1610 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1611 else
1612 assert_not_reached("Uh, invalid transport...");
1613
1614 r = loginctl_main(bus, argc, argv, &error);
1615 retval = r < 0 ? EXIT_FAILURE : r;
1616
1617 finish:
1618 if (bus) {
1619 dbus_connection_flush(bus);
1620 dbus_connection_close(bus);
1621 dbus_connection_unref(bus);
1622 }
1623
1624 dbus_error_free(&error);
1625 dbus_shutdown();
1626
1627 strv_free(arg_property);
1628
1629 pager_close();
1630
1631 return retval;
1632 }