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