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