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