]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/loginctl.c
loginctl: fix output of type with class
[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 } SessionStatusInfo;
275
276 typedef struct UserStatusInfo {
277 uid_t uid;
278 const char *name;
279 usec_t timestamp;
280 const char *state;
281 char **sessions;
282 const char *display;
283 const char *slice;
284 } UserStatusInfo;
285
286 typedef struct SeatStatusInfo {
287 const char *id;
288 const char *active_session;
289 char **sessions;
290 } SeatStatusInfo;
291
292 static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
293 const char *contents;
294 int r;
295
296 r = sd_bus_message_peek_type(m, NULL, &contents);
297 if (r < 0)
298 return r;
299
300 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, contents);
301 if (r < 0)
302 return r;
303
304 if (contents[0] == 's' || contents[0] == 'o') {
305 const char *s;
306 char **p = (char **) userdata;
307
308 r = sd_bus_message_read_basic(m, contents[0], &s);
309 if (r < 0)
310 return r;
311
312 free(*p);
313 *p = strdup(s);
314
315 if (!*p)
316 return -ENOMEM;
317 } else {
318 r = sd_bus_message_read_basic(m, contents[0], userdata);
319 if (r < 0)
320 return r;
321 }
322
323 r = sd_bus_message_skip(m, contents+1);
324 if (r < 0)
325 return r;
326
327 r = sd_bus_message_exit_container(m);
328 if (r < 0)
329 return r;
330
331 return 0;
332 }
333
334 static int prop_map_sessions_strv(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
335 const char *name;
336 int r;
337
338 assert(bus);
339 assert(m);
340
341 r = sd_bus_message_enter_container(m, 'a', "(so)");
342 if (r < 0)
343 return r;
344
345 while ((r = sd_bus_message_read(m, "(so)", &name, NULL)) > 0) {
346 r = strv_extend(userdata, name);
347 if (r < 0)
348 return r;
349 }
350 if (r < 0)
351 return r;
352
353 return sd_bus_message_exit_container(m);
354 }
355
356 static int print_session_status_info(sd_bus *bus, const char *path, bool *new_line) {
357
358 static const struct bus_properties_map map[] = {
359 { "Id", "s", NULL, offsetof(SessionStatusInfo, id) },
360 { "Name", "s", NULL, offsetof(SessionStatusInfo, name) },
361 { "TTY", "s", NULL, offsetof(SessionStatusInfo, tty) },
362 { "Display", "s", NULL, offsetof(SessionStatusInfo, display) },
363 { "RemoteHost", "s", NULL, offsetof(SessionStatusInfo, remote_host) },
364 { "RemoteUser", "s", NULL, offsetof(SessionStatusInfo, remote_user) },
365 { "Service", "s", NULL, offsetof(SessionStatusInfo, service) },
366 { "Type", "s", NULL, offsetof(SessionStatusInfo, type) },
367 { "Class", "s", NULL, offsetof(SessionStatusInfo, class) },
368 { "Scope", "s", NULL, offsetof(SessionStatusInfo, scope) },
369 { "State", "s", NULL, offsetof(SessionStatusInfo, state) },
370 { "VTNr", "u", NULL, offsetof(SessionStatusInfo, vtnr) },
371 { "Leader", "u", NULL, offsetof(SessionStatusInfo, leader) },
372 { "Remote", "b", NULL, offsetof(SessionStatusInfo, remote) },
373 { "Timestamp", "t", NULL, offsetof(SessionStatusInfo, timestamp) },
374 { "User", "(uo)", prop_map_first_of_struct, offsetof(SessionStatusInfo, uid) },
375 { "Seat", "(so)", prop_map_first_of_struct, offsetof(SessionStatusInfo, seat) },
376 {}
377 };
378
379 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
380 char since2[FORMAT_TIMESTAMP_MAX], *s2;
381 SessionStatusInfo i = {};
382 int r;
383
384 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
385 if (r < 0) {
386 log_error("Could not get properties: %s", strerror(-r));
387 return r;
388 }
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%i", 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 (i.state)
466 printf("\t State: %s\n", i.state);
467
468 if (i.scope) {
469 printf("\t Unit: %s\n", i.scope);
470 show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader);
471 }
472
473 return 0;
474 }
475
476 static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) {
477
478 static const struct bus_properties_map map[] = {
479 { "Name", "s", NULL, offsetof(UserStatusInfo, name) },
480 { "Slice", "s", NULL, offsetof(UserStatusInfo, slice) },
481 { "State", "s", NULL, offsetof(UserStatusInfo, state) },
482 { "UID", "u", NULL, offsetof(UserStatusInfo, uid) },
483 { "Timestamp", "t", NULL, offsetof(UserStatusInfo, timestamp) },
484 { "Display", "(so)", prop_map_first_of_struct, offsetof(UserStatusInfo, display) },
485 { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(UserStatusInfo, sessions) },
486 {}
487 };
488
489 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
490 char since2[FORMAT_TIMESTAMP_MAX], *s2;
491 UserStatusInfo i = {};
492 int r;
493
494 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
495 if (r < 0) {
496 log_error("Could not get properties: %s", strerror(-r));
497 goto finish;
498 }
499
500 if (*new_line)
501 printf("\n");
502
503 *new_line = true;
504
505 if (i.name)
506 printf("%s (%u)\n", i.name, (unsigned) i.uid);
507 else
508 printf("%u\n", (unsigned) i.uid);
509
510 s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp);
511 s2 = format_timestamp(since2, sizeof(since2), i.timestamp);
512
513 if (s1)
514 printf("\t Since: %s; %s\n", s2, s1);
515 else if (s2)
516 printf("\t Since: %s\n", s2);
517
518 if (!isempty(i.state))
519 printf("\t State: %s\n", i.state);
520
521 if (!strv_isempty(i.sessions)) {
522 char **l;
523 printf("\tSessions:");
524
525 STRV_FOREACH(l, i.sessions) {
526 if (streq_ptr(*l, i.display))
527 printf(" *%s", *l);
528 else
529 printf(" %s", *l);
530 }
531
532 printf("\n");
533 }
534
535 if (i.slice) {
536 printf("\t Unit: %s\n", i.slice);
537 show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0);
538 }
539
540 finish:
541 strv_free(i.sessions);
542
543 return r;
544 }
545
546 static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) {
547
548 static const struct bus_properties_map map[] = {
549 { "Id", "s", NULL, offsetof(SeatStatusInfo, id) },
550 { "ActiveSession", "(so)", prop_map_first_of_struct, offsetof(SeatStatusInfo, active_session) },
551 { "Sessions", "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) },
552 {}
553 };
554
555 SeatStatusInfo i = {};
556 int r;
557
558 r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
559 if (r < 0) {
560 log_error("Could not get properties: %s", strerror(-r));
561 goto finish;
562 }
563
564 if (*new_line)
565 printf("\n");
566
567 *new_line = true;
568
569 printf("%s\n", strna(i.id));
570
571 if (!strv_isempty(i.sessions)) {
572 char **l;
573 printf("\tSessions:");
574
575 STRV_FOREACH(l, i.sessions) {
576 if (streq_ptr(*l, i.active_session))
577 printf(" *%s", *l);
578 else
579 printf(" %s", *l);
580 }
581
582 printf("\n");
583 }
584
585 if (arg_transport == BUS_TRANSPORT_LOCAL) {
586 unsigned c;
587
588 c = columns();
589 if (c > 21)
590 c -= 21;
591 else
592 c = 0;
593
594 printf("\t Devices:\n");
595
596 show_sysfs(i.id, "\t\t ", c);
597 }
598
599 finish:
600 strv_free(i.sessions);
601
602 return r;
603 }
604
605 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
606 int r;
607
608 if (*new_line)
609 printf("\n");
610
611 *new_line = true;
612
613 r = bus_print_all_properties(bus, "org.freedesktop.login1", path, arg_property, arg_all);
614 if (r < 0)
615 log_error("Could not get properties: %s", strerror(-r));
616
617 return r;
618 }
619
620 static int show_session(sd_bus *bus, char **args, unsigned n) {
621 bool properties, new_line = false;
622 unsigned i;
623 int r;
624
625 assert(bus);
626 assert(args);
627
628 properties = !strstr(args[0], "status");
629
630 pager_open_if_enabled();
631
632 if (properties && n <= 1) {
633 /* If not argument is specified inspect the manager
634 * itself */
635 return show_properties(bus, "/org/freedesktop/login1", &new_line);
636 }
637
638 for (i = 1; i < n; i++) {
639 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
640 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
641 const char *path = NULL;
642
643 r = sd_bus_call_method(
644 bus,
645 "org.freedesktop.login1",
646 "/org/freedesktop/login1",
647 "org.freedesktop.login1.Manager",
648 "GetSession",
649 &error, &reply,
650 "s", args[i]);
651 if (r < 0) {
652 log_error("Failed to get session: %s", bus_error_message(&error, r));
653 return r;
654 }
655
656 r = sd_bus_message_read(reply, "o", &path);
657 if (r < 0)
658 return bus_log_parse_error(r);
659
660 if (properties)
661 r = show_properties(bus, path, &new_line);
662 else
663 r = print_session_status_info(bus, path, &new_line);
664
665 if (r < 0)
666 return r;
667 }
668
669 return 0;
670 }
671
672 static int show_user(sd_bus *bus, char **args, unsigned n) {
673 bool properties, new_line = false;
674 unsigned i;
675 int r;
676
677 assert(bus);
678 assert(args);
679
680 properties = !strstr(args[0], "status");
681
682 pager_open_if_enabled();
683
684 if (properties && n <= 1) {
685 /* If not argument is specified inspect the manager
686 * itself */
687 return show_properties(bus, "/org/freedesktop/login1", &new_line);
688 }
689
690 for (i = 1; i < n; i++) {
691 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
692 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
693 const char *path = NULL;
694 uid_t uid;
695
696 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
697 if (r < 0) {
698 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
699 return r;
700 }
701
702 r = sd_bus_call_method(
703 bus,
704 "org.freedesktop.login1",
705 "/org/freedesktop/login1",
706 "org.freedesktop.login1.Manager",
707 "GetUser",
708 &error, &reply,
709 "u", (uint32_t) uid);
710 if (r < 0) {
711 log_error("Failed to get user: %s", bus_error_message(&error, r));
712 return r;
713 }
714
715 r = sd_bus_message_read(reply, "o", &path);
716 if (r < 0)
717 return bus_log_parse_error(r);
718
719 if (properties)
720 r = show_properties(bus, path, &new_line);
721 else
722 r = print_user_status_info(bus, path, &new_line);
723
724 if (r < 0)
725 return r;
726 }
727
728 return 0;
729 }
730
731 static int show_seat(sd_bus *bus, char **args, unsigned n) {
732 bool properties, new_line = false;
733 unsigned i;
734 int r;
735
736 assert(bus);
737 assert(args);
738
739 properties = !strstr(args[0], "status");
740
741 pager_open_if_enabled();
742
743 if (properties && n <= 1) {
744 /* If not argument is specified inspect the manager
745 * itself */
746 return show_properties(bus, "/org/freedesktop/login1", &new_line);
747 }
748
749 for (i = 1; i < n; i++) {
750 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
751 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
752 const char *path = NULL;
753
754 r = sd_bus_call_method(
755 bus,
756 "org.freedesktop.login1",
757 "/org/freedesktop/login1",
758 "org.freedesktop.login1.Manager",
759 "GetSeat",
760 &error, &reply,
761 "s", args[i]);
762 if (r < 0) {
763 log_error("Failed to get seat: %s", bus_error_message(&error, r));
764 return r;
765 }
766
767 r = sd_bus_message_read(reply, "o", &path);
768 if (r < 0)
769 return bus_log_parse_error(r);
770
771 if (properties)
772 r = show_properties(bus, path, &new_line);
773 else
774 r = print_seat_status_info(bus, path, &new_line);
775
776 if (r < 0)
777 return r;
778 }
779
780 return 0;
781 }
782
783 static int activate(sd_bus *bus, char **args, unsigned n) {
784 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
785 unsigned i;
786 int r;
787
788 assert(args);
789
790 for (i = 1; i < n; i++) {
791
792 r = sd_bus_call_method (
793 bus,
794 "org.freedesktop.login1",
795 "/org/freedesktop/login1",
796 "org.freedesktop.login1.Manager",
797 streq(args[0], "lock-session") ? "LockSession" :
798 streq(args[0], "unlock-session") ? "UnlockSession" :
799 streq(args[0], "terminate-session") ? "TerminateSession" :
800 "ActivateSession",
801 &error, NULL,
802 "s", args[i]);
803 if (r < 0) {
804 log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
805 return r;
806 }
807 }
808
809 return 0;
810 }
811
812 static int kill_session(sd_bus *bus, char **args, unsigned n) {
813 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
814 unsigned i;
815 int r;
816
817 assert(args);
818
819 if (!arg_kill_who)
820 arg_kill_who = "all";
821
822 for (i = 1; i < n; i++) {
823
824 r = sd_bus_call_method (
825 bus,
826 "org.freedesktop.login1",
827 "/org/freedesktop/login1",
828 "org.freedesktop.login1.Manager",
829 "KillSession",
830 &error, NULL,
831 "ssi", args[i], arg_kill_who, arg_signal);
832 if (r < 0) {
833 log_error("Could not kill session: %s", bus_error_message(&error, -r));
834 return r;
835 }
836 }
837
838 return 0;
839 }
840
841 static int enable_linger(sd_bus *bus, char **args, unsigned n) {
842 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
843 unsigned i;
844 bool b;
845 int r;
846
847 assert(args);
848
849 polkit_agent_open_if_enabled();
850
851 b = streq(args[0], "enable-linger");
852
853 for (i = 1; i < n; i++) {
854 uid_t uid;
855
856 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
857 if (r < 0) {
858 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
859 return r;
860 }
861
862 r = sd_bus_call_method (
863 bus,
864 "org.freedesktop.login1",
865 "/org/freedesktop/login1",
866 "org.freedesktop.login1.Manager",
867 "SetUserLinger",
868 &error, NULL,
869 "ubb", (uint32_t) uid, b, true);
870 if (r < 0) {
871 log_error("Could not enable linger: %s", bus_error_message(&error, -r));
872 return r;
873 }
874 }
875
876 return 0;
877 }
878
879 static int terminate_user(sd_bus *bus, char **args, unsigned n) {
880 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
881 unsigned i;
882 int r;
883
884 assert(args);
885
886 for (i = 1; i < n; i++) {
887 uid_t uid;
888
889 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
890 if (r < 0) {
891 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
892 return r;
893 }
894
895 r = sd_bus_call_method (
896 bus,
897 "org.freedesktop.login1",
898 "/org/freedesktop/login1",
899 "org.freedesktop.login1.Manager",
900 "TerminateUser",
901 &error, NULL,
902 "u", (uint32_t) uid);
903 if (r < 0) {
904 log_error("Could not terminate user: %s", bus_error_message(&error, -r));
905 return r;
906 }
907 }
908
909 return 0;
910 }
911
912 static int kill_user(sd_bus *bus, char **args, unsigned n) {
913 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
914 unsigned i;
915 int r;
916
917 assert(args);
918
919 if (!arg_kill_who)
920 arg_kill_who = "all";
921
922 for (i = 1; i < n; i++) {
923 uid_t uid;
924
925 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
926 if (r < 0) {
927 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
928 return r;
929 }
930
931 r = sd_bus_call_method (
932 bus,
933 "org.freedesktop.login1",
934 "/org/freedesktop/login1",
935 "org.freedesktop.login1.Manager",
936 "KillUser",
937 &error, NULL,
938 "ui", (uint32_t) uid, arg_signal);
939 if (r < 0) {
940 log_error("Could not kill user: %s", bus_error_message(&error, -r));
941 return r;
942 }
943 }
944
945 return 0;
946 }
947
948 static int attach(sd_bus *bus, char **args, unsigned n) {
949 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
950 unsigned i;
951 int r;
952
953 assert(args);
954
955 polkit_agent_open_if_enabled();
956
957 for (i = 2; i < n; i++) {
958
959 r = sd_bus_call_method (
960 bus,
961 "org.freedesktop.login1",
962 "/org/freedesktop/login1",
963 "org.freedesktop.login1.Manager",
964 "AttachDevice",
965 &error, NULL,
966 "ssb", args[1], args[i], true);
967
968 if (r < 0) {
969 log_error("Could not attach device: %s", bus_error_message(&error, -r));
970 return r;
971 }
972 }
973
974 return 0;
975 }
976
977 static int flush_devices(sd_bus *bus, char **args, unsigned n) {
978 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
979 int r;
980
981 assert(args);
982
983 polkit_agent_open_if_enabled();
984
985 r = sd_bus_call_method (
986 bus,
987 "org.freedesktop.login1",
988 "/org/freedesktop/login1",
989 "org.freedesktop.login1.Manager",
990 "FlushDevices",
991 &error, NULL,
992 "b", true);
993 if (r < 0)
994 log_error("Could not flush devices: %s", bus_error_message(&error, -r));
995
996 return r;
997 }
998
999 static int lock_sessions(sd_bus *bus, char **args, unsigned n) {
1000 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1001 int r;
1002
1003 assert(args);
1004
1005 r = sd_bus_call_method (
1006 bus,
1007 "org.freedesktop.login1",
1008 "/org/freedesktop/login1",
1009 "org.freedesktop.login1.Manager",
1010 streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1011 &error, NULL,
1012 NULL);
1013 if (r < 0)
1014 log_error("Could not lock sessions: %s", bus_error_message(&error, -r));
1015
1016 return r;
1017 }
1018
1019 static int terminate_seat(sd_bus *bus, char **args, unsigned n) {
1020 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1021 unsigned i;
1022 int r;
1023
1024 assert(args);
1025
1026 for (i = 1; i < n; i++) {
1027
1028 r = sd_bus_call_method (
1029 bus,
1030 "org.freedesktop.login1",
1031 "/org/freedesktop/login1",
1032 "org.freedesktop.login1.Manager",
1033 "TerminateSeat",
1034 &error, NULL,
1035 "s", args[i]);
1036 if (r < 0) {
1037 log_error("Could not terminate seat: %s", bus_error_message(&error, -r));
1038 return r;
1039 }
1040 }
1041
1042 return 0;
1043 }
1044
1045 static int help(void) {
1046
1047 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1048 "Send control commands to or query the login manager.\n\n"
1049 " -h --help Show this help\n"
1050 " --version Show package version\n"
1051 " --no-pager Do not pipe output into a pager\n"
1052 " --no-legend Do not show the headers and footers\n"
1053 " --no-ask-password Don't prompt for password\n"
1054 " -H --host=[USER@]HOST Operate on remote host\n"
1055 " -M --machine=CONTAINER Operate on local container\n"
1056 " -p --property=NAME Show only properties by this name\n"
1057 " -a --all Show all properties, including empty ones\n"
1058 " -l --full Do not ellipsize output\n"
1059 " --kill-who=WHO Who to send signal to\n"
1060 " -s --signal=SIGNAL Which signal to send\n\n"
1061 "Commands:\n"
1062 " list-sessions List sessions\n"
1063 " session-status ID... Show session status\n"
1064 " show-session [ID...] Show properties of sessions or the manager\n"
1065 " activate ID Activate a session\n"
1066 " lock-session ID... Screen lock one or more sessions\n"
1067 " unlock-session ID... Screen unlock one or more sessions\n"
1068 " lock-sessions Screen lock all current sessions\n"
1069 " unlock-sessions Screen unlock all current sessions\n"
1070 " terminate-session ID... Terminate one or more sessions\n"
1071 " kill-session ID... Send signal to processes of a session\n"
1072 " list-users List users\n"
1073 " user-status USER... Show user status\n"
1074 " show-user [USER...] Show properties of users or the manager\n"
1075 " enable-linger USER... Enable linger state of one or more users\n"
1076 " disable-linger USER... Disable linger state of one or more users\n"
1077 " terminate-user USER... Terminate all sessions of one or more users\n"
1078 " kill-user USER... Send signal to processes of a user\n"
1079 " list-seats List seats\n"
1080 " seat-status NAME... Show seat status\n"
1081 " show-seat NAME... Show properties of one or more seats\n"
1082 " attach NAME DEVICE... Attach one or more devices to a seat\n"
1083 " flush-devices Flush all device associations\n"
1084 " terminate-seat NAME... Terminate all sessions on one or more seats\n",
1085 program_invocation_short_name);
1086
1087 return 0;
1088 }
1089
1090 static int parse_argv(int argc, char *argv[]) {
1091
1092 enum {
1093 ARG_VERSION = 0x100,
1094 ARG_NO_PAGER,
1095 ARG_NO_LEGEND,
1096 ARG_KILL_WHO,
1097 ARG_NO_ASK_PASSWORD,
1098 };
1099
1100 static const struct option options[] = {
1101 { "help", no_argument, NULL, 'h' },
1102 { "version", no_argument, NULL, ARG_VERSION },
1103 { "property", required_argument, NULL, 'p' },
1104 { "all", no_argument, NULL, 'a' },
1105 { "full", no_argument, NULL, 'l' },
1106 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1107 { "no-legend", no_argument, NULL, ARG_NO_LEGEND },
1108 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1109 { "signal", required_argument, NULL, 's' },
1110 { "host", required_argument, NULL, 'H' },
1111 { "machine", required_argument, NULL, 'M' },
1112 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1113 {}
1114 };
1115
1116 int c, r;
1117
1118 assert(argc >= 0);
1119 assert(argv);
1120
1121 while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0) {
1122
1123 switch (c) {
1124
1125 case 'h':
1126 return help();
1127
1128 case ARG_VERSION:
1129 puts(PACKAGE_STRING);
1130 puts(SYSTEMD_FEATURES);
1131 return 0;
1132
1133 case 'p': {
1134 r = strv_extend(&arg_property, optarg);
1135 if (r < 0)
1136 return log_oom();
1137
1138 /* If the user asked for a particular
1139 * property, show it to him, even if it is
1140 * empty. */
1141 arg_all = true;
1142 break;
1143 }
1144
1145 case 'a':
1146 arg_all = true;
1147 break;
1148
1149 case 'l':
1150 arg_full = true;
1151 break;
1152
1153 case ARG_NO_PAGER:
1154 arg_no_pager = true;
1155 break;
1156
1157 case ARG_NO_LEGEND:
1158 arg_legend = false;
1159 break;
1160
1161 case ARG_NO_ASK_PASSWORD:
1162 arg_ask_password = false;
1163 break;
1164
1165 case ARG_KILL_WHO:
1166 arg_kill_who = optarg;
1167 break;
1168
1169 case 's':
1170 arg_signal = signal_from_string_try_harder(optarg);
1171 if (arg_signal < 0) {
1172 log_error("Failed to parse signal string %s.", optarg);
1173 return -EINVAL;
1174 }
1175 break;
1176
1177 case 'H':
1178 arg_transport = BUS_TRANSPORT_REMOTE;
1179 arg_host = optarg;
1180 break;
1181
1182 case 'M':
1183 arg_transport = BUS_TRANSPORT_CONTAINER;
1184 arg_host = optarg;
1185 break;
1186
1187 case '?':
1188 return -EINVAL;
1189
1190 default:
1191 assert_not_reached("Unhandled option");
1192 }
1193 }
1194
1195 return 1;
1196 }
1197
1198 static int loginctl_main(sd_bus *bus, int argc, char *argv[]) {
1199
1200 static const struct {
1201 const char* verb;
1202 const enum {
1203 MORE,
1204 LESS,
1205 EQUAL
1206 } argc_cmp;
1207 const int argc;
1208 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
1209 } verbs[] = {
1210 { "list-sessions", LESS, 1, list_sessions },
1211 { "session-status", MORE, 2, show_session },
1212 { "show-session", MORE, 1, show_session },
1213 { "activate", EQUAL, 2, activate },
1214 { "lock-session", MORE, 2, activate },
1215 { "unlock-session", MORE, 2, activate },
1216 { "lock-sessions", EQUAL, 1, lock_sessions },
1217 { "unlock-sessions", EQUAL, 1, lock_sessions },
1218 { "terminate-session", MORE, 2, activate },
1219 { "kill-session", MORE, 2, kill_session },
1220 { "list-users", EQUAL, 1, list_users },
1221 { "user-status", MORE, 2, show_user },
1222 { "show-user", MORE, 1, show_user },
1223 { "enable-linger", MORE, 2, enable_linger },
1224 { "disable-linger", MORE, 2, enable_linger },
1225 { "terminate-user", MORE, 2, terminate_user },
1226 { "kill-user", MORE, 2, kill_user },
1227 { "list-seats", EQUAL, 1, list_seats },
1228 { "seat-status", MORE, 2, show_seat },
1229 { "show-seat", MORE, 1, show_seat },
1230 { "attach", MORE, 3, attach },
1231 { "flush-devices", EQUAL, 1, flush_devices },
1232 { "terminate-seat", MORE, 2, terminate_seat },
1233 };
1234
1235 int left;
1236 unsigned i;
1237
1238 assert(argc >= 0);
1239 assert(argv);
1240
1241 left = argc - optind;
1242
1243 if (left <= 0)
1244 /* Special rule: no arguments means "list-sessions" */
1245 i = 0;
1246 else {
1247 if (streq(argv[optind], "help")) {
1248 help();
1249 return 0;
1250 }
1251
1252 for (i = 0; i < ELEMENTSOF(verbs); i++)
1253 if (streq(argv[optind], verbs[i].verb))
1254 break;
1255
1256 if (i >= ELEMENTSOF(verbs)) {
1257 log_error("Unknown operation %s", argv[optind]);
1258 return -EINVAL;
1259 }
1260 }
1261
1262 switch (verbs[i].argc_cmp) {
1263
1264 case EQUAL:
1265 if (left != verbs[i].argc) {
1266 log_error("Invalid number of arguments.");
1267 return -EINVAL;
1268 }
1269
1270 break;
1271
1272 case MORE:
1273 if (left < verbs[i].argc) {
1274 log_error("Too few arguments.");
1275 return -EINVAL;
1276 }
1277
1278 break;
1279
1280 case LESS:
1281 if (left > verbs[i].argc) {
1282 log_error("Too many arguments.");
1283 return -EINVAL;
1284 }
1285
1286 break;
1287
1288 default:
1289 assert_not_reached("Unknown comparison operator.");
1290 }
1291
1292 return verbs[i].dispatch(bus, argv + optind, left);
1293 }
1294
1295 int main(int argc, char *argv[]) {
1296 _cleanup_bus_unref_ sd_bus *bus = NULL;
1297 int r;
1298
1299 setlocale(LC_ALL, "");
1300 log_parse_environment();
1301 log_open();
1302
1303 r = parse_argv(argc, argv);
1304 if (r <= 0)
1305 goto finish;
1306
1307 r = bus_open_transport(arg_transport, arg_host, false, &bus);
1308 if (r < 0) {
1309 log_error("Failed to create bus connection: %s", strerror(-r));
1310 goto finish;
1311 }
1312
1313 r = loginctl_main(bus, argc, argv);
1314
1315 finish:
1316 pager_close();
1317
1318 strv_free(arg_property);
1319
1320 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1321 }