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