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