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