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