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