]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/loginctl.c
sd-bus: rename sd_bus_open_system_container() to sd_bus_open_system_machine()
[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 "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"
1066 " list-users List users\n"
1067 " user-status USER... Show user status\n"
1068 " show-user [USER...] Show properties of users or the manager\n"
1069 " enable-linger USER... Enable linger state of one or more users\n"
1070 " disable-linger USER... Disable linger state of one or more users\n"
1071 " terminate-user USER... Terminate all sessions of one or more users\n"
1072 " kill-user USER... Send signal to processes of a user\n"
1073 " list-seats List seats\n"
1074 " seat-status NAME... Show seat status\n"
1075 " show-seat NAME... Show properties of one or more seats\n"
1076 " attach NAME DEVICE... Attach one or more devices to a seat\n"
1077 " flush-devices Flush all device associations\n"
1078 " terminate-seat NAME... Terminate all sessions on one or more seats\n"
1079 , program_invocation_short_name);
1080 }
1081
1082 static int parse_argv(int argc, char *argv[]) {
1083
1084 enum {
1085 ARG_VERSION = 0x100,
1086 ARG_NO_PAGER,
1087 ARG_NO_LEGEND,
1088 ARG_KILL_WHO,
1089 ARG_NO_ASK_PASSWORD,
1090 };
1091
1092 static const struct option options[] = {
1093 { "help", no_argument, NULL, 'h' },
1094 { "version", no_argument, NULL, ARG_VERSION },
1095 { "property", required_argument, NULL, 'p' },
1096 { "all", no_argument, NULL, 'a' },
1097 { "full", no_argument, NULL, 'l' },
1098 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1099 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1100 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1101 { "signal", required_argument, NULL, 's' },
1102 { "host", required_argument, NULL, 'H' },
1103 { "machine", required_argument, NULL, 'M' },
1104 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1105 {}
1106 };
1107
1108 int c, r;
1109
1110 assert(argc >= 0);
1111 assert(argv);
1112
1113 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
1114
1115 switch (c) {
1116
1117 case 'h':
1118 help();
1119 return 0;
1120
1121 case ARG_VERSION:
1122 puts(PACKAGE_STRING);
1123 puts(SYSTEMD_FEATURES);
1124 return 0;
1125
1126 case 'p': {
1127 r = strv_extend(&arg_property, optarg);
1128 if (r < 0)
1129 return log_oom();
1130
1131 /* If the user asked for a particular
1132 * property, show it to him, even if it is
1133 * empty. */
1134 arg_all = true;
1135 break;
1136 }
1137
1138 case 'a':
1139 arg_all = true;
1140 break;
1141
1142 case 'l':
1143 arg_full = true;
1144 break;
1145
1146 case ARG_NO_PAGER:
1147 arg_no_pager = true;
1148 break;
1149
1150 case ARG_NO_LEGEND:
1151 arg_legend = false;
1152 break;
1153
1154 case ARG_NO_ASK_PASSWORD:
1155 arg_ask_password = false;
1156 break;
1157
1158 case ARG_KILL_WHO:
1159 arg_kill_who = optarg;
1160 break;
1161
1162 case 's':
1163 arg_signal = signal_from_string_try_harder(optarg);
1164 if (arg_signal < 0) {
1165 log_error("Failed to parse signal string %s.", optarg);
1166 return -EINVAL;
1167 }
1168 break;
1169
1170 case 'H':
1171 arg_transport = BUS_TRANSPORT_REMOTE;
1172 arg_host = optarg;
1173 break;
1174
1175 case 'M':
1176 arg_transport = BUS_TRANSPORT_MACHINE;
1177 arg_host = optarg;
1178 break;
1179
1180 case '?':
1181 return -EINVAL;
1182
1183 default:
1184 assert_not_reached("Unhandled option");
1185 }
1186
1187 return 1;
1188 }
1189
1190 static int loginctl_main(sd_bus *bus, int argc, char *argv[]) {
1191
1192 static const struct {
1193 const char* verb;
1194 const enum {
1195 MORE,
1196 LESS,
1197 EQUAL
1198 } argc_cmp;
1199 const int argc;
1200 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
1201 } verbs[] = {
1202 { "list-sessions", LESS, 1, list_sessions },
1203 { "session-status", MORE, 2, show_session },
1204 { "show-session", MORE, 1, show_session },
1205 { "activate", EQUAL, 2, activate },
1206 { "lock-session", MORE, 2, activate },
1207 { "unlock-session", MORE, 2, activate },
1208 { "lock-sessions", EQUAL, 1, lock_sessions },
1209 { "unlock-sessions", EQUAL, 1, lock_sessions },
1210 { "terminate-session", MORE, 2, activate },
1211 { "kill-session", MORE, 2, kill_session },
1212 { "list-users", EQUAL, 1, list_users },
1213 { "user-status", MORE, 2, show_user },
1214 { "show-user", MORE, 1, show_user },
1215 { "enable-linger", MORE, 2, enable_linger },
1216 { "disable-linger", MORE, 2, enable_linger },
1217 { "terminate-user", MORE, 2, terminate_user },
1218 { "kill-user", MORE, 2, kill_user },
1219 { "list-seats", EQUAL, 1, list_seats },
1220 { "seat-status", MORE, 2, show_seat },
1221 { "show-seat", MORE, 1, show_seat },
1222 { "attach", MORE, 3, attach },
1223 { "flush-devices", EQUAL, 1, flush_devices },
1224 { "terminate-seat", MORE, 2, terminate_seat },
1225 };
1226
1227 int left;
1228 unsigned i;
1229
1230 assert(argc >= 0);
1231 assert(argv);
1232
1233 left = argc - optind;
1234
1235 if (left <= 0)
1236 /* Special rule: no arguments means "list-sessions" */
1237 i = 0;
1238 else {
1239 if (streq(argv[optind], "help")) {
1240 help();
1241 return 0;
1242 }
1243
1244 for (i = 0; i < ELEMENTSOF(verbs); i++)
1245 if (streq(argv[optind], verbs[i].verb))
1246 break;
1247
1248 if (i >= ELEMENTSOF(verbs)) {
1249 log_error("Unknown operation %s", argv[optind]);
1250 return -EINVAL;
1251 }
1252 }
1253
1254 switch (verbs[i].argc_cmp) {
1255
1256 case EQUAL:
1257 if (left != verbs[i].argc) {
1258 log_error("Invalid number of arguments.");
1259 return -EINVAL;
1260 }
1261
1262 break;
1263
1264 case MORE:
1265 if (left < verbs[i].argc) {
1266 log_error("Too few arguments.");
1267 return -EINVAL;
1268 }
1269
1270 break;
1271
1272 case LESS:
1273 if (left > verbs[i].argc) {
1274 log_error("Too many arguments.");
1275 return -EINVAL;
1276 }
1277
1278 break;
1279
1280 default:
1281 assert_not_reached("Unknown comparison operator.");
1282 }
1283
1284 return verbs[i].dispatch(bus, argv + optind, left);
1285 }
1286
1287 int main(int argc, char *argv[]) {
1288 _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1289 int r;
1290
1291 setlocale(LC_ALL, "");
1292 log_parse_environment();
1293 log_open();
1294
1295 r = parse_argv(argc, argv);
1296 if (r <= 0)
1297 goto finish;
1298
1299 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1300 if (r < 0) {
1301 log_error_errno(r, "Failed to create bus connection: %m");
1302 goto finish;
1303 }
1304
1305 r = loginctl_main(bus, argc, argv);
1306
1307 finish:
1308 pager_close();
1309
1310 strv_free(arg_property);
1311
1312 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1313 }