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