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