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