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