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