]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/loginctl.c
core: modernize execution code a bit
[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 (properties && argc <= 1) {
688 /* If not argument is specified inspect the manager
689 * itself */
690 return show_properties(bus, "/org/freedesktop/login1", &new_line);
691 }
692
693 for (i = 1; i < argc; i++) {
694 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
695 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
696 const char *path = NULL;
697
698 r = sd_bus_call_method(
699 bus,
700 "org.freedesktop.login1",
701 "/org/freedesktop/login1",
702 "org.freedesktop.login1.Manager",
703 "GetSession",
704 &error, &reply,
705 "s", argv[i]);
706 if (r < 0) {
707 log_error("Failed to get session: %s", bus_error_message(&error, r));
708 return r;
709 }
710
711 r = sd_bus_message_read(reply, "o", &path);
712 if (r < 0)
713 return bus_log_parse_error(r);
714
715 if (properties)
716 r = show_properties(bus, path, &new_line);
717 else
718 r = print_session_status_info(bus, path, &new_line);
719
720 if (r < 0)
721 return r;
722 }
723
724 return 0;
725 }
726
727 static int show_user(int argc, char *argv[], void *userdata) {
728 bool properties, new_line = false;
729 sd_bus *bus = userdata;
730 int r, i;
731
732 assert(bus);
733 assert(argv);
734
735 properties = !strstr(argv[0], "status");
736
737 pager_open_if_enabled();
738
739 if (properties && argc <= 1) {
740 /* If not argument is specified inspect the manager
741 * itself */
742 return show_properties(bus, "/org/freedesktop/login1", &new_line);
743 }
744
745 for (i = 1; i < argc; i++) {
746 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
747 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
748 const char *path = NULL;
749 uid_t uid;
750
751 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
752 if (r < 0)
753 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
754
755 r = sd_bus_call_method(
756 bus,
757 "org.freedesktop.login1",
758 "/org/freedesktop/login1",
759 "org.freedesktop.login1.Manager",
760 "GetUser",
761 &error, &reply,
762 "u", (uint32_t) uid);
763 if (r < 0) {
764 log_error("Failed to get user: %s", bus_error_message(&error, r));
765 return r;
766 }
767
768 r = sd_bus_message_read(reply, "o", &path);
769 if (r < 0)
770 return bus_log_parse_error(r);
771
772 if (properties)
773 r = show_properties(bus, path, &new_line);
774 else
775 r = print_user_status_info(bus, path, &new_line);
776
777 if (r < 0)
778 return r;
779 }
780
781 return 0;
782 }
783
784 static int show_seat(int argc, char *argv[], void *userdata) {
785 bool properties, new_line = false;
786 sd_bus *bus = userdata;
787 int r, i;
788
789 assert(bus);
790 assert(argv);
791
792 properties = !strstr(argv[0], "status");
793
794 pager_open_if_enabled();
795
796 if (properties && argc <= 1) {
797 /* If not argument is specified inspect the manager
798 * itself */
799 return show_properties(bus, "/org/freedesktop/login1", &new_line);
800 }
801
802 for (i = 1; i < argc; i++) {
803 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
804 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
805 const char *path = NULL;
806
807 r = sd_bus_call_method(
808 bus,
809 "org.freedesktop.login1",
810 "/org/freedesktop/login1",
811 "org.freedesktop.login1.Manager",
812 "GetSeat",
813 &error, &reply,
814 "s", argv[i]);
815 if (r < 0) {
816 log_error("Failed to get seat: %s", bus_error_message(&error, r));
817 return r;
818 }
819
820 r = sd_bus_message_read(reply, "o", &path);
821 if (r < 0)
822 return bus_log_parse_error(r);
823
824 if (properties)
825 r = show_properties(bus, path, &new_line);
826 else
827 r = print_seat_status_info(bus, path, &new_line);
828
829 if (r < 0)
830 return r;
831 }
832
833 return 0;
834 }
835
836 static int activate(int argc, char *argv[], void *userdata) {
837 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
838 sd_bus *bus = userdata;
839 int r, i;
840
841 assert(bus);
842 assert(argv);
843
844 polkit_agent_open_if_enabled();
845
846 for (i = 1; i < argc; i++) {
847
848 r = sd_bus_call_method (
849 bus,
850 "org.freedesktop.login1",
851 "/org/freedesktop/login1",
852 "org.freedesktop.login1.Manager",
853 streq(argv[0], "lock-session") ? "LockSession" :
854 streq(argv[0], "unlock-session") ? "UnlockSession" :
855 streq(argv[0], "terminate-session") ? "TerminateSession" :
856 "ActivateSession",
857 &error, NULL,
858 "s", argv[i]);
859 if (r < 0) {
860 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
861 return r;
862 }
863 }
864
865 return 0;
866 }
867
868 static int kill_session(int argc, char *argv[], void *userdata) {
869 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
870 sd_bus *bus = userdata;
871 int r, i;
872
873 assert(bus);
874 assert(argv);
875
876 polkit_agent_open_if_enabled();
877
878 if (!arg_kill_who)
879 arg_kill_who = "all";
880
881 for (i = 1; i < argc; i++) {
882
883 r = sd_bus_call_method (
884 bus,
885 "org.freedesktop.login1",
886 "/org/freedesktop/login1",
887 "org.freedesktop.login1.Manager",
888 "KillSession",
889 &error, NULL,
890 "ssi", argv[i], arg_kill_who, arg_signal);
891 if (r < 0) {
892 log_error("Could not kill session: %s", bus_error_message(&error, -r));
893 return r;
894 }
895 }
896
897 return 0;
898 }
899
900 static int enable_linger(int argc, char *argv[], void *userdata) {
901 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
902 sd_bus *bus = userdata;
903 bool b;
904 int r, i;
905
906 assert(bus);
907 assert(argv);
908
909 polkit_agent_open_if_enabled();
910
911 b = streq(argv[0], "enable-linger");
912
913 for (i = 1; i < argc; i++) {
914 uid_t uid;
915
916 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
917 if (r < 0)
918 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
919
920 r = sd_bus_call_method (
921 bus,
922 "org.freedesktop.login1",
923 "/org/freedesktop/login1",
924 "org.freedesktop.login1.Manager",
925 "SetUserLinger",
926 &error, NULL,
927 "ubb", (uint32_t) uid, b, true);
928 if (r < 0) {
929 log_error("Could not enable linger: %s", bus_error_message(&error, -r));
930 return r;
931 }
932 }
933
934 return 0;
935 }
936
937 static int terminate_user(int argc, char *argv[], void *userdata) {
938 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
939 sd_bus *bus = userdata;
940 int r, i;
941
942 assert(bus);
943 assert(argv);
944
945 polkit_agent_open_if_enabled();
946
947 for (i = 1; i < argc; i++) {
948 uid_t uid;
949
950 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
951 if (r < 0)
952 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
953
954 r = sd_bus_call_method (
955 bus,
956 "org.freedesktop.login1",
957 "/org/freedesktop/login1",
958 "org.freedesktop.login1.Manager",
959 "TerminateUser",
960 &error, NULL,
961 "u", (uint32_t) uid);
962 if (r < 0) {
963 log_error("Could not terminate user: %s", bus_error_message(&error, -r));
964 return r;
965 }
966 }
967
968 return 0;
969 }
970
971 static int kill_user(int argc, char *argv[], void *userdata) {
972 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
973 sd_bus *bus = userdata;
974 int r, i;
975
976 assert(bus);
977 assert(argv);
978
979 polkit_agent_open_if_enabled();
980
981 if (!arg_kill_who)
982 arg_kill_who = "all";
983
984 for (i = 1; i < argc; i++) {
985 uid_t uid;
986
987 r = get_user_creds((const char**) (argv+i), &uid, NULL, NULL, NULL);
988 if (r < 0)
989 return log_error_errno(r, "Failed to look up user %s: %m", argv[i]);
990
991 r = sd_bus_call_method (
992 bus,
993 "org.freedesktop.login1",
994 "/org/freedesktop/login1",
995 "org.freedesktop.login1.Manager",
996 "KillUser",
997 &error, NULL,
998 "ui", (uint32_t) uid, arg_signal);
999 if (r < 0) {
1000 log_error("Could not kill user: %s", bus_error_message(&error, -r));
1001 return r;
1002 }
1003 }
1004
1005 return 0;
1006 }
1007
1008 static int attach(int argc, char *argv[], void *userdata) {
1009 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1010 sd_bus *bus = userdata;
1011 int r, i;
1012
1013 assert(bus);
1014 assert(argv);
1015
1016 polkit_agent_open_if_enabled();
1017
1018 for (i = 2; i < argc; i++) {
1019
1020 r = sd_bus_call_method (
1021 bus,
1022 "org.freedesktop.login1",
1023 "/org/freedesktop/login1",
1024 "org.freedesktop.login1.Manager",
1025 "AttachDevice",
1026 &error, NULL,
1027 "ssb", argv[1], argv[i], true);
1028
1029 if (r < 0) {
1030 log_error("Could not attach device: %s", bus_error_message(&error, -r));
1031 return r;
1032 }
1033 }
1034
1035 return 0;
1036 }
1037
1038 static int flush_devices(int argc, char *argv[], void *userdata) {
1039 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1040 sd_bus *bus = userdata;
1041 int r;
1042
1043 assert(bus);
1044 assert(argv);
1045
1046 polkit_agent_open_if_enabled();
1047
1048 r = sd_bus_call_method (
1049 bus,
1050 "org.freedesktop.login1",
1051 "/org/freedesktop/login1",
1052 "org.freedesktop.login1.Manager",
1053 "FlushDevices",
1054 &error, NULL,
1055 "b", true);
1056 if (r < 0)
1057 log_error("Could not flush devices: %s", bus_error_message(&error, -r));
1058
1059 return r;
1060 }
1061
1062 static int lock_sessions(int argc, char *argv[], void *userdata) {
1063 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1064 sd_bus *bus = userdata;
1065 int r;
1066
1067 assert(bus);
1068 assert(argv);
1069
1070 polkit_agent_open_if_enabled();
1071
1072 r = sd_bus_call_method(
1073 bus,
1074 "org.freedesktop.login1",
1075 "/org/freedesktop/login1",
1076 "org.freedesktop.login1.Manager",
1077 streq(argv[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1078 &error, NULL,
1079 NULL);
1080 if (r < 0)
1081 log_error("Could not lock sessions: %s", bus_error_message(&error, -r));
1082
1083 return r;
1084 }
1085
1086 static int terminate_seat(int argc, char *argv[], void *userdata) {
1087 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1088 sd_bus *bus = userdata;
1089 int r, i;
1090
1091 assert(bus);
1092 assert(argv);
1093
1094 polkit_agent_open_if_enabled();
1095
1096 for (i = 1; i < argc; i++) {
1097
1098 r = sd_bus_call_method(
1099 bus,
1100 "org.freedesktop.login1",
1101 "/org/freedesktop/login1",
1102 "org.freedesktop.login1.Manager",
1103 "TerminateSeat",
1104 &error, NULL,
1105 "s", argv[i]);
1106 if (r < 0) {
1107 log_error("Could not terminate seat: %s", bus_error_message(&error, -r));
1108 return r;
1109 }
1110 }
1111
1112 return 0;
1113 }
1114
1115 static int help(int argc, char *argv[], void *userdata) {
1116
1117 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1118 "Send control commands to or query the login manager.\n\n"
1119 " -h --help Show this help\n"
1120 " --version Show package version\n"
1121 " --no-pager Do not pipe output into a pager\n"
1122 " --no-legend Do not show the headers and footers\n"
1123 " --no-ask-password Don't prompt for password\n"
1124 " -H --host=[USER@]HOST Operate on remote host\n"
1125 " -M --machine=CONTAINER Operate on local container\n"
1126 " -p --property=NAME Show only properties by this name\n"
1127 " -a --all Show all properties, including empty ones\n"
1128 " -l --full Do not ellipsize output\n"
1129 " --kill-who=WHO Who to send signal to\n"
1130 " -s --signal=SIGNAL Which signal to send\n"
1131 " -n --lines=INTEGER Number of journal entries to show\n"
1132 " -o --output=STRING Change journal output mode (short, short-monotonic,\n"
1133 " verbose, export, json, json-pretty, json-sse, cat)\n\n"
1134 "Session Commands:\n"
1135 " list-sessions List sessions\n"
1136 " session-status ID... Show session status\n"
1137 " show-session [ID...] Show properties of sessions or the manager\n"
1138 " activate ID Activate a session\n"
1139 " lock-session ID... Screen lock one or more sessions\n"
1140 " unlock-session ID... Screen unlock one or more sessions\n"
1141 " lock-sessions Screen lock all current sessions\n"
1142 " unlock-sessions Screen unlock all current sessions\n"
1143 " terminate-session ID... Terminate one or more sessions\n"
1144 " kill-session ID... Send signal to processes of a session\n\n"
1145 "User Commands:\n"
1146 " list-users List users\n"
1147 " user-status USER... Show user status\n"
1148 " show-user [USER...] Show properties of users or the manager\n"
1149 " enable-linger USER... Enable linger state of one or more users\n"
1150 " disable-linger USER... Disable linger state of one or more users\n"
1151 " terminate-user USER... Terminate all sessions of one or more users\n"
1152 " kill-user USER... Send signal to processes of a user\n\n"
1153 "Seat Commands:\n"
1154 " list-seats List seats\n"
1155 " seat-status NAME... Show seat status\n"
1156 " show-seat NAME... Show properties of one or more seats\n"
1157 " attach NAME DEVICE... Attach one or more devices to a seat\n"
1158 " flush-devices Flush all device associations\n"
1159 " terminate-seat NAME... Terminate all sessions on one or more seats\n"
1160 , program_invocation_short_name);
1161
1162 return 0;
1163 }
1164
1165 static int parse_argv(int argc, char *argv[]) {
1166
1167 enum {
1168 ARG_VERSION = 0x100,
1169 ARG_NO_PAGER,
1170 ARG_NO_LEGEND,
1171 ARG_KILL_WHO,
1172 ARG_NO_ASK_PASSWORD,
1173 };
1174
1175 static const struct option options[] = {
1176 { "help", no_argument, NULL, 'h' },
1177 { "version", no_argument, NULL, ARG_VERSION },
1178 { "property", required_argument, NULL, 'p' },
1179 { "all", no_argument, NULL, 'a' },
1180 { "full", no_argument, NULL, 'l' },
1181 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1182 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1183 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1184 { "signal", required_argument, NULL, 's' },
1185 { "host", required_argument, NULL, 'H' },
1186 { "machine", required_argument, NULL, 'M' },
1187 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1188 { "lines", required_argument, NULL, 'n' },
1189 { "output", required_argument, NULL, 'o' },
1190 {}
1191 };
1192
1193 int c, r;
1194
1195 assert(argc >= 0);
1196 assert(argv);
1197
1198 while ((c = getopt_long(argc, argv, "hp:als:H:M:n:o:", options, NULL)) >= 0)
1199
1200 switch (c) {
1201
1202 case 'h':
1203 help(0, NULL, NULL);
1204 return 0;
1205
1206 case ARG_VERSION:
1207 puts(PACKAGE_STRING);
1208 puts(SYSTEMD_FEATURES);
1209 return 0;
1210
1211 case 'p': {
1212 r = strv_extend(&arg_property, optarg);
1213 if (r < 0)
1214 return log_oom();
1215
1216 /* If the user asked for a particular
1217 * property, show it to him, even if it is
1218 * empty. */
1219 arg_all = true;
1220 break;
1221 }
1222
1223 case 'a':
1224 arg_all = true;
1225 break;
1226
1227 case 'l':
1228 arg_full = true;
1229 break;
1230
1231 case 'n':
1232 if (safe_atou(optarg, &arg_lines) < 0) {
1233 log_error("Failed to parse lines '%s'", optarg);
1234 return -EINVAL;
1235 }
1236 break;
1237
1238 case 'o':
1239 arg_output = output_mode_from_string(optarg);
1240 if (arg_output < 0) {
1241 log_error("Unknown output '%s'.", optarg);
1242 return -EINVAL;
1243 }
1244 break;
1245
1246 case ARG_NO_PAGER:
1247 arg_no_pager = true;
1248 break;
1249
1250 case ARG_NO_LEGEND:
1251 arg_legend = false;
1252 break;
1253
1254 case ARG_NO_ASK_PASSWORD:
1255 arg_ask_password = false;
1256 break;
1257
1258 case ARG_KILL_WHO:
1259 arg_kill_who = optarg;
1260 break;
1261
1262 case 's':
1263 arg_signal = signal_from_string_try_harder(optarg);
1264 if (arg_signal < 0) {
1265 log_error("Failed to parse signal string %s.", optarg);
1266 return -EINVAL;
1267 }
1268 break;
1269
1270 case 'H':
1271 arg_transport = BUS_TRANSPORT_REMOTE;
1272 arg_host = optarg;
1273 break;
1274
1275 case 'M':
1276 arg_transport = BUS_TRANSPORT_MACHINE;
1277 arg_host = optarg;
1278 break;
1279
1280 case '?':
1281 return -EINVAL;
1282
1283 default:
1284 assert_not_reached("Unhandled option");
1285 }
1286
1287 return 1;
1288 }
1289
1290 static int loginctl_main(int argc, char *argv[], sd_bus *bus) {
1291
1292 static const Verb verbs[] = {
1293 { "help", VERB_ANY, VERB_ANY, 0, help },
1294 { "list-sessions", VERB_ANY, 1, VERB_DEFAULT, list_sessions },
1295 { "session-status", 2, VERB_ANY, 0, show_session },
1296 { "show-session", VERB_ANY, VERB_ANY, 0, show_session },
1297 { "activate", 2, 2, 0, activate },
1298 { "lock-session", 2, VERB_ANY, 0, activate },
1299 { "unlock-session", 2, VERB_ANY, 0, activate },
1300 { "lock-sessions", VERB_ANY, 1, 0, lock_sessions },
1301 { "unlock-sessions", VERB_ANY, 1, 0, lock_sessions },
1302 { "terminate-session", 2, VERB_ANY, 0, activate },
1303 { "kill-session", 2, VERB_ANY, 0, kill_session },
1304 { "list-users", VERB_ANY, 1, 0, list_users },
1305 { "user-status", 2, VERB_ANY, 0, show_user },
1306 { "show-user", VERB_ANY, VERB_ANY, 0, show_user },
1307 { "enable-linger", 2, VERB_ANY, 0, enable_linger },
1308 { "disable-linger", 2, VERB_ANY, 0, enable_linger },
1309 { "terminate-user", 2, VERB_ANY, 0, terminate_user },
1310 { "kill-user", 2, VERB_ANY, 0, kill_user },
1311 { "list-seats", VERB_ANY, 1, 0, list_seats },
1312 { "seat-status", 2, VERB_ANY, 0, show_seat },
1313 { "show-seat", VERB_ANY, 1, 0, show_seat },
1314 { "attach", 3, VERB_ANY, 0, attach },
1315 { "flush-devices", VERB_ANY, 1, 0, flush_devices },
1316 { "terminate-seat", 2, VERB_ANY, 0, terminate_seat },
1317 {}
1318 };
1319
1320 return dispatch_verb(argc, argv, verbs, bus);
1321 }
1322
1323 int main(int argc, char *argv[]) {
1324 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1325 int r;
1326
1327 setlocale(LC_ALL, "");
1328 log_parse_environment();
1329 log_open();
1330
1331 r = parse_argv(argc, argv);
1332 if (r <= 0)
1333 goto finish;
1334
1335 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1336 if (r < 0) {
1337 log_error_errno(r, "Failed to create bus connection: %m");
1338 goto finish;
1339 }
1340
1341 r = loginctl_main(argc, argv, bus);
1342
1343 finish:
1344 pager_close();
1345 polkit_agent_close();
1346
1347 strv_free(arg_property);
1348
1349 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1350 }