1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
22 #include <dbus/dbus.h>
34 #include "dbus-common.h"
37 #include "unit-name.h"
38 #include "cgroup-show.h"
39 #include "sysfs-show.h"
40 #include "spawn-polkit-agent.h"
42 static char **arg_property = NULL;
43 static bool arg_all = false;
44 static bool arg_full = false;
45 static bool arg_no_pager = false;
46 static const char *arg_kill_who = NULL;
47 static int arg_signal = SIGTERM;
48 static enum transport {
52 } arg_transport = TRANSPORT_NORMAL;
53 static bool arg_ask_password = true;
54 static char *arg_host = NULL;
55 static char *arg_user = NULL;
57 static void pager_open_if_enabled(void) {
59 /* Cache result before we open the pager */
66 static void polkit_agent_open_if_enabled(void) {
68 /* Open the polkit agent as a child process if necessary */
70 if (!arg_ask_password)
76 static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
77 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
79 DBusMessageIter iter, sub, sub2;
82 pager_open_if_enabled();
84 r = bus_method_call_with_reply (
86 "org.freedesktop.login1",
87 "/org/freedesktop/login1",
88 "org.freedesktop.login1.Manager",
96 if (!dbus_message_iter_init(reply, &iter) ||
97 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
98 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
99 log_error("Failed to parse reply.");
103 dbus_message_iter_recurse(&iter, &sub);
106 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
108 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
109 const char *id, *user, *seat, *object;
112 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
113 log_error("Failed to parse reply.");
117 dbus_message_iter_recurse(&sub, &sub2);
119 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
120 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
121 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
122 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
123 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
124 log_error("Failed to parse reply.");
128 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
132 dbus_message_iter_next(&sub);
136 printf("\n%u sessions listed.\n", k);
141 static int list_users(DBusConnection *bus, char **args, unsigned n) {
142 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
144 DBusMessageIter iter, sub, sub2;
147 pager_open_if_enabled();
149 r = bus_method_call_with_reply (
151 "org.freedesktop.login1",
152 "/org/freedesktop/login1",
153 "org.freedesktop.login1.Manager",
161 if (!dbus_message_iter_init(reply, &iter) ||
162 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
163 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
164 log_error("Failed to parse reply.");
168 dbus_message_iter_recurse(&iter, &sub);
171 printf("%10s %-16s\n", "UID", "USER");
173 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
174 const char *user, *object;
177 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
178 log_error("Failed to parse reply.");
182 dbus_message_iter_recurse(&sub, &sub2);
184 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
185 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
186 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
187 log_error("Failed to parse reply.");
191 printf("%10u %-16s\n", (unsigned) uid, user);
195 dbus_message_iter_next(&sub);
199 printf("\n%u users listed.\n", k);
204 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
205 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
207 DBusMessageIter iter, sub, sub2;
210 pager_open_if_enabled();
212 r = bus_method_call_with_reply (
214 "org.freedesktop.login1",
215 "/org/freedesktop/login1",
216 "org.freedesktop.login1.Manager",
224 if (!dbus_message_iter_init(reply, &iter) ||
225 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
226 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
227 log_error("Failed to parse reply.");
231 dbus_message_iter_recurse(&iter, &sub);
234 printf("%-16s\n", "SEAT");
236 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
237 const char *seat, *object;
239 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
240 log_error("Failed to parse reply.");
244 dbus_message_iter_recurse(&sub, &sub2);
246 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
247 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
248 log_error("Failed to parse reply.");
252 printf("%-16s\n", seat);
256 dbus_message_iter_next(&sub);
260 printf("\n%u seats listed.\n", k);
265 static int show_unit_cgroup(DBusConnection *bus, const char *interface, const char *unit) {
266 const char *property = "ControlGroup";
267 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
268 _cleanup_free_ char *path = NULL;
269 DBusMessageIter iter, sub;
278 if (arg_transport == TRANSPORT_SSH)
281 path = unit_dbus_path_from_name(unit);
285 r = bus_method_call_with_reply(
287 "org.freedesktop.systemd1",
289 "org.freedesktop.DBus.Properties",
293 DBUS_TYPE_STRING, &interface,
294 DBUS_TYPE_STRING, &property,
297 log_error("Failed to query ControlGroup: %s", bus_error(&error, r));
298 dbus_error_free(&error);
302 if (!dbus_message_iter_init(reply, &iter) ||
303 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
304 log_error("Failed to parse reply.");
308 dbus_message_iter_recurse(&iter, &sub);
309 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
310 log_error("Failed to parse reply.");
314 dbus_message_iter_get_basic(&sub, &cgroup);
317 arg_all * OUTPUT_SHOW_ALL |
318 arg_full * OUTPUT_FULL_WIDTH;
326 show_cgroup_by_path(cgroup, "\t\t ", c, false, output_flags);
330 typedef struct SessionStatusInfo {
340 const char *remote_host;
341 const char *remote_user;
350 typedef struct UserStatusInfo {
360 typedef struct SeatStatusInfo {
362 const char *active_session;
366 static void print_session_status_info(DBusConnection *bus, SessionStatusInfo *i) {
367 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
368 char since2[FORMAT_TIMESTAMP_MAX], *s2;
371 printf("%s - ", strna(i->id));
374 printf("%s (%u)\n", i->name, (unsigned) i->uid);
376 printf("%u\n", (unsigned) i->uid);
378 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
379 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
382 printf("\t Since: %s; %s\n", s2, s1);
384 printf("\t Since: %s\n", s2);
387 _cleanup_free_ char *t = NULL;
389 printf("\t Leader: %u", (unsigned) i->leader);
391 get_process_comm(i->leader, &t);
399 printf("\t Seat: %s", i->seat);
402 printf("; vc%i", i->vtnr);
408 printf("\t TTY: %s\n", i->tty);
410 printf("\t Display: %s\n", i->display);
412 if (i->remote_host && i->remote_user)
413 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
414 else if (i->remote_host)
415 printf("\t Remote: %s\n", i->remote_host);
416 else if (i->remote_user)
417 printf("\t Remote: user %s\n", i->remote_user);
419 printf("\t Remote: Yes\n");
422 printf("\t Service: %s", i->service);
425 printf("; type %s", i->type);
428 printf("; class %s", i->class);
431 } else if (i->type) {
432 printf("\t Type: %s\n", i->type);
435 printf("; class %s", i->class);
437 printf("\t Class: %s\n", i->class);
440 printf("\t State: %s\n", i->state);
443 printf("\t Unit: %s\n", i->scope);
444 show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i->scope);
448 static void print_user_status_info(DBusConnection *bus, UserStatusInfo *i) {
449 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
450 char since2[FORMAT_TIMESTAMP_MAX], *s2;
454 printf("%s (%u)\n", i->name, (unsigned) i->uid);
456 printf("%u\n", (unsigned) i->uid);
458 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
459 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
462 printf("\t Since: %s; %s\n", s2, s1);
464 printf("\t Since: %s\n", s2);
466 if (!isempty(i->state))
467 printf("\t State: %s\n", i->state);
470 if (!strv_isempty(i->sessions)) {
472 printf("\tSessions:");
474 STRV_FOREACH(l, i->sessions) {
475 if (streq_ptr(*l, i->display))
485 printf("\t Unit: %s\n", i->slice);
486 show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i->slice);
490 static void print_seat_status_info(SeatStatusInfo *i) {
493 printf("%s\n", strna(i->id));
495 if (!strv_isempty(i->sessions)) {
497 printf("\tSessions:");
499 STRV_FOREACH(l, i->sessions) {
500 if (streq_ptr(*l, i->active_session))
509 if (arg_transport != TRANSPORT_SSH) {
518 printf("\t Devices:\n");
520 show_sysfs(i->id, "\t\t ", c);
524 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
529 switch (dbus_message_iter_get_arg_type(iter)) {
531 case DBUS_TYPE_STRING: {
534 dbus_message_iter_get_basic(iter, &s);
537 if (streq(name, "Id"))
539 else if (streq(name, "Name"))
541 else if (streq(name, "TTY"))
543 else if (streq(name, "Display"))
545 else if (streq(name, "RemoteHost"))
547 else if (streq(name, "RemoteUser"))
549 else if (streq(name, "Service"))
551 else if (streq(name, "Type"))
553 else if (streq(name, "Class"))
555 else if (streq(name, "Scope"))
557 else if (streq(name, "State"))
563 case DBUS_TYPE_UINT32: {
566 dbus_message_iter_get_basic(iter, &u);
568 if (streq(name, "VTNr"))
570 else if (streq(name, "Leader"))
571 i->leader = (pid_t) u;
576 case DBUS_TYPE_BOOLEAN: {
579 dbus_message_iter_get_basic(iter, &b);
581 if (streq(name, "Remote"))
587 case DBUS_TYPE_UINT64: {
590 dbus_message_iter_get_basic(iter, &u);
592 if (streq(name, "Timestamp"))
593 i->timestamp = (usec_t) u;
598 case DBUS_TYPE_STRUCT: {
601 dbus_message_iter_recurse(iter, &sub);
603 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
606 dbus_message_iter_get_basic(&sub, &u);
609 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
612 dbus_message_iter_get_basic(&sub, &s);
625 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
630 switch (dbus_message_iter_get_arg_type(iter)) {
632 case DBUS_TYPE_STRING: {
635 dbus_message_iter_get_basic(iter, &s);
638 if (streq(name, "Name"))
640 else if (streq(name, "Slice"))
642 else if (streq(name, "State"))
648 case DBUS_TYPE_UINT32: {
651 dbus_message_iter_get_basic(iter, &u);
653 if (streq(name, "UID"))
659 case DBUS_TYPE_UINT64: {
662 dbus_message_iter_get_basic(iter, &u);
664 if (streq(name, "Timestamp"))
665 i->timestamp = (usec_t) u;
670 case DBUS_TYPE_STRUCT: {
673 dbus_message_iter_recurse(iter, &sub);
675 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
678 dbus_message_iter_get_basic(&sub, &s);
687 case DBUS_TYPE_ARRAY: {
689 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
690 DBusMessageIter sub, sub2;
692 dbus_message_iter_recurse(iter, &sub);
693 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
697 dbus_message_iter_recurse(&sub, &sub2);
699 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
700 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
703 l = strv_append(i->sessions, id);
707 strv_free(i->sessions);
711 dbus_message_iter_next(&sub);
722 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
727 switch (dbus_message_iter_get_arg_type(iter)) {
729 case DBUS_TYPE_STRING: {
732 dbus_message_iter_get_basic(iter, &s);
735 if (streq(name, "Id"))
741 case DBUS_TYPE_STRUCT: {
744 dbus_message_iter_recurse(iter, &sub);
746 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
749 dbus_message_iter_get_basic(&sub, &s);
752 i->active_session = s;
758 case DBUS_TYPE_ARRAY: {
760 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
761 DBusMessageIter sub, sub2;
763 dbus_message_iter_recurse(iter, &sub);
764 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
768 dbus_message_iter_recurse(&sub, &sub2);
770 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
771 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
774 l = strv_append(i->sessions, id);
778 strv_free(i->sessions);
782 dbus_message_iter_next(&sub);
793 static int print_property(const char *name, DBusMessageIter *iter) {
797 if (arg_property && !strv_find(arg_property, name))
800 switch (dbus_message_iter_get_arg_type(iter)) {
802 case DBUS_TYPE_STRUCT: {
805 dbus_message_iter_recurse(iter, &sub);
807 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
808 (streq(name, "Display") || streq(name, "ActiveSession"))) {
811 dbus_message_iter_get_basic(&sub, &s);
813 if (arg_all || !isempty(s))
814 printf("%s=%s\n", name, s);
820 case DBUS_TYPE_ARRAY:
822 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
823 DBusMessageIter sub, sub2;
826 dbus_message_iter_recurse(iter, &sub);
827 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
831 dbus_message_iter_recurse(&sub, &sub2);
833 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
834 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
838 printf("%s=%s", name, id);
843 dbus_message_iter_next(&sub);
846 if (!found && arg_all)
847 printf("%s=\n", name);
857 if (generic_print_property(name, iter, arg_all) > 0)
861 printf("%s=[unprintable]\n", name);
866 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
867 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
868 const char *interface = "";
870 DBusMessageIter iter, sub, sub2, sub3;
871 SessionStatusInfo session_info = {};
872 UserStatusInfo user_info = {};
873 SeatStatusInfo seat_info = {};
878 r = bus_method_call_with_reply(
880 "org.freedesktop.login1",
882 "org.freedesktop.DBus.Properties",
886 DBUS_TYPE_STRING, &interface,
891 if (!dbus_message_iter_init(reply, &iter) ||
892 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
893 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
894 log_error("Failed to parse reply.");
899 dbus_message_iter_recurse(&iter, &sub);
906 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
909 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
910 log_error("Failed to parse reply.");
915 dbus_message_iter_recurse(&sub, &sub2);
917 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
918 log_error("Failed to parse reply.");
923 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
924 log_error("Failed to parse reply.");
929 dbus_message_iter_recurse(&sub2, &sub3);
932 r = print_property(name, &sub3);
933 else if (strstr(verb, "session"))
934 r = status_property_session(name, &sub3, &session_info);
935 else if (strstr(verb, "user"))
936 r = status_property_user(name, &sub3, &user_info);
938 r = status_property_seat(name, &sub3, &seat_info);
941 log_error("Failed to parse reply.");
945 dbus_message_iter_next(&sub);
948 if (!show_properties) {
949 if (strstr(verb, "session"))
950 print_session_status_info(bus, &session_info);
951 else if (strstr(verb, "user"))
952 print_user_status_info(bus, &user_info);
954 print_seat_status_info(&seat_info);
960 strv_free(seat_info.sessions);
961 strv_free(user_info.sessions);
966 static int show(DBusConnection *bus, char **args, unsigned n) {
967 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
971 bool show_properties, new_line = false;
976 dbus_error_init(&error);
978 show_properties = !strstr(args[0], "status");
980 pager_open_if_enabled();
982 if (show_properties && n <= 1) {
983 /* If not argument is specified inspect the manager
986 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
990 for (i = 1; i < n; i++) {
991 const char *path = NULL;
993 if (strstr(args[0], "session")) {
995 ret = bus_method_call_with_reply (
997 "org.freedesktop.login1",
998 "/org/freedesktop/login1",
999 "org.freedesktop.login1.Manager",
1003 DBUS_TYPE_STRING, &args[i],
1006 } else if (strstr(args[0], "user")) {
1010 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1012 log_error("User %s unknown.", args[i]);
1017 ret = bus_method_call_with_reply(
1019 "org.freedesktop.login1",
1020 "/org/freedesktop/login1",
1021 "org.freedesktop.login1.Manager",
1025 DBUS_TYPE_UINT32, &u,
1030 ret = bus_method_call_with_reply(
1032 "org.freedesktop.login1",
1033 "/org/freedesktop/login1",
1034 "org.freedesktop.login1.Manager",
1038 DBUS_TYPE_STRING, &args[i],
1046 if (!dbus_message_get_args(reply, &error,
1047 DBUS_TYPE_OBJECT_PATH, &path,
1048 DBUS_TYPE_INVALID)) {
1049 log_error("Failed to parse reply: %s", bus_error_message(&error));
1054 r = show_one(args[0], bus, path, show_properties, &new_line);
1060 dbus_error_free(&error);
1065 static int activate(DBusConnection *bus, char **args, unsigned n) {
1071 for (i = 1; i < n; i++) {
1073 ret = bus_method_call_with_reply (
1075 "org.freedesktop.login1",
1076 "/org/freedesktop/login1",
1077 "org.freedesktop.login1.Manager",
1078 streq(args[0], "lock-session") ? "LockSession" :
1079 streq(args[0], "unlock-session") ? "UnlockSession" :
1080 streq(args[0], "terminate-session") ? "TerminateSession" :
1084 DBUS_TYPE_STRING, &args[i],
1094 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1100 arg_kill_who = "all";
1102 for (i = 1; i < n; i++) {
1105 r = bus_method_call_with_reply (
1107 "org.freedesktop.login1",
1108 "/org/freedesktop/login1",
1109 "org.freedesktop.login1.Manager",
1113 DBUS_TYPE_STRING, &args[i],
1114 DBUS_TYPE_STRING, &arg_kill_who,
1115 DBUS_TYPE_INT32, &arg_signal,
1124 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1126 dbus_bool_t b, interactive = true;
1130 polkit_agent_open_if_enabled();
1132 b = streq(args[0], "enable-linger");
1134 for (i = 1; i < n; i++) {
1139 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1141 log_error("Failed to resolve user %s: %s", args[i], strerror(-r));
1146 r = bus_method_call_with_reply (
1148 "org.freedesktop.login1",
1149 "/org/freedesktop/login1",
1150 "org.freedesktop.login1.Manager",
1154 DBUS_TYPE_UINT32, &u,
1155 DBUS_TYPE_BOOLEAN, &b,
1156 DBUS_TYPE_BOOLEAN, &interactive,
1165 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1170 for (i = 1; i < n; i++) {
1175 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1177 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1182 r = bus_method_call_with_reply (
1184 "org.freedesktop.login1",
1185 "/org/freedesktop/login1",
1186 "org.freedesktop.login1.Manager",
1190 DBUS_TYPE_UINT32, &u,
1199 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1205 arg_kill_who = "all";
1207 for (i = 1; i < n; i++) {
1212 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1214 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1219 r = bus_method_call_with_reply (
1221 "org.freedesktop.login1",
1222 "/org/freedesktop/login1",
1223 "org.freedesktop.login1.Manager",
1227 DBUS_TYPE_UINT32, &u,
1228 DBUS_TYPE_INT32, &arg_signal,
1237 static int attach(DBusConnection *bus, char **args, unsigned n) {
1239 dbus_bool_t interactive = true;
1243 polkit_agent_open_if_enabled();
1245 for (i = 2; i < n; i++) {
1248 r = bus_method_call_with_reply (
1250 "org.freedesktop.login1",
1251 "/org/freedesktop/login1",
1252 "org.freedesktop.login1.Manager",
1256 DBUS_TYPE_STRING, &args[1],
1257 DBUS_TYPE_STRING, &args[i],
1258 DBUS_TYPE_BOOLEAN, &interactive,
1267 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1268 dbus_bool_t interactive = true;
1272 polkit_agent_open_if_enabled();
1274 return bus_method_call_with_reply (
1276 "org.freedesktop.login1",
1277 "/org/freedesktop/login1",
1278 "org.freedesktop.login1.Manager",
1282 DBUS_TYPE_BOOLEAN, &interactive,
1286 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1289 polkit_agent_open_if_enabled();
1291 return bus_method_call_with_reply (
1293 "org.freedesktop.login1",
1294 "/org/freedesktop/login1",
1295 "org.freedesktop.login1.Manager",
1296 streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1302 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1307 for (i = 1; i < n; i++) {
1310 r = bus_method_call_with_reply (
1312 "org.freedesktop.login1",
1313 "/org/freedesktop/login1",
1314 "org.freedesktop.login1.Manager",
1318 DBUS_TYPE_STRING, &args[i],
1327 static int help(void) {
1329 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1330 "Send control commands to or query the login manager.\n\n"
1331 " -h --help Show this help\n"
1332 " --version Show package version\n"
1333 " -p --property=NAME Show only properties by this name\n"
1334 " -a --all Show all properties, including empty ones\n"
1335 " --kill-who=WHO Who to send signal to\n"
1336 " -l --full Do not ellipsize output\n"
1337 " -s --signal=SIGNAL Which signal to send\n"
1338 " --no-ask-password Don't prompt for password\n"
1339 " -H --host=[USER@]HOST Show information for remote host\n"
1340 " -P --privileged Acquire privileges before execution\n"
1341 " --no-pager Do not pipe output into a pager\n\n"
1343 " list-sessions List sessions\n"
1344 " session-status [ID...] Show session status\n"
1345 " show-session [ID...] Show properties of one or more sessions\n"
1346 " activate [ID] Activate a session\n"
1347 " lock-session [ID...] Screen lock one or more sessions\n"
1348 " unlock-session [ID...] Screen unlock one or more sessions\n"
1349 " lock-sessions Screen lock all current sessions\n"
1350 " unlock-sessions Screen unlock all current sessions\n"
1351 " terminate-session [ID...] Terminate one or more sessions\n"
1352 " kill-session [ID...] Send signal to processes of a session\n"
1353 " list-users List users\n"
1354 " user-status [USER...] Show user status\n"
1355 " show-user [USER...] Show properties of one or more users\n"
1356 " enable-linger [USER...] Enable linger state of one or more users\n"
1357 " disable-linger [USER...] Disable linger state of one or more users\n"
1358 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1359 " kill-user [USER...] Send signal to processes of a user\n"
1360 " list-seats List seats\n"
1361 " seat-status [NAME...] Show seat status\n"
1362 " show-seat [NAME...] Show properties of one or more seats\n"
1363 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1364 " flush-devices Flush all device associations\n"
1365 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1366 program_invocation_short_name);
1371 static int parse_argv(int argc, char *argv[]) {
1374 ARG_VERSION = 0x100,
1377 ARG_NO_ASK_PASSWORD,
1380 static const struct option options[] = {
1381 { "help", no_argument, NULL, 'h' },
1382 { "version", no_argument, NULL, ARG_VERSION },
1383 { "property", required_argument, NULL, 'p' },
1384 { "all", no_argument, NULL, 'a' },
1385 { "full", no_argument, NULL, 'l' },
1386 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1387 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1388 { "signal", required_argument, NULL, 's' },
1389 { "host", required_argument, NULL, 'H' },
1390 { "privileged", no_argument, NULL, 'P' },
1391 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1392 { NULL, 0, NULL, 0 }
1400 while ((c = getopt_long(argc, argv, "hp:als:H:P", options, NULL)) >= 0) {
1409 puts(PACKAGE_STRING);
1410 puts(SYSTEMD_FEATURES);
1416 l = strv_append(arg_property, optarg);
1420 strv_free(arg_property);
1423 /* If the user asked for a particular
1424 * property, show it to him, even if it is
1439 arg_no_pager = true;
1442 case ARG_NO_ASK_PASSWORD:
1443 arg_ask_password = false;
1447 arg_kill_who = optarg;
1451 arg_signal = signal_from_string_try_harder(optarg);
1452 if (arg_signal < 0) {
1453 log_error("Failed to parse signal string %s.", optarg);
1459 arg_transport = TRANSPORT_POLKIT;
1463 arg_transport = TRANSPORT_SSH;
1464 parse_user_at_host(optarg, &arg_user, &arg_host);
1471 log_error("Unknown option code %c", c);
1479 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1481 static const struct {
1489 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1491 { "list-sessions", LESS, 1, list_sessions },
1492 { "session-status", MORE, 2, show },
1493 { "show-session", MORE, 1, show },
1494 { "activate", EQUAL, 2, activate },
1495 { "lock-session", MORE, 2, activate },
1496 { "unlock-session", MORE, 2, activate },
1497 { "lock-sessions", EQUAL, 1, lock_sessions },
1498 { "unlock-sessions", EQUAL, 1, lock_sessions },
1499 { "terminate-session", MORE, 2, activate },
1500 { "kill-session", MORE, 2, kill_session },
1501 { "list-users", EQUAL, 1, list_users },
1502 { "user-status", MORE, 2, show },
1503 { "show-user", MORE, 1, show },
1504 { "enable-linger", MORE, 2, enable_linger },
1505 { "disable-linger", MORE, 2, enable_linger },
1506 { "terminate-user", MORE, 2, terminate_user },
1507 { "kill-user", MORE, 2, kill_user },
1508 { "list-seats", EQUAL, 1, list_seats },
1509 { "seat-status", MORE, 2, show },
1510 { "show-seat", MORE, 1, show },
1511 { "attach", MORE, 3, attach },
1512 { "flush-devices", EQUAL, 1, flush_devices },
1513 { "terminate-seat", MORE, 2, terminate_seat },
1523 left = argc - optind;
1526 /* Special rule: no arguments means "list-sessions" */
1529 if (streq(argv[optind], "help")) {
1534 for (i = 0; i < ELEMENTSOF(verbs); i++)
1535 if (streq(argv[optind], verbs[i].verb))
1538 if (i >= ELEMENTSOF(verbs)) {
1539 log_error("Unknown operation %s", argv[optind]);
1544 switch (verbs[i].argc_cmp) {
1547 if (left != verbs[i].argc) {
1548 log_error("Invalid number of arguments.");
1555 if (left < verbs[i].argc) {
1556 log_error("Too few arguments.");
1563 if (left > verbs[i].argc) {
1564 log_error("Too many arguments.");
1571 assert_not_reached("Unknown comparison operator.");
1575 log_error("Failed to get D-Bus connection: %s", error->message);
1579 return verbs[i].dispatch(bus, argv + optind, left);
1582 int main(int argc, char*argv[]) {
1583 int r, retval = EXIT_FAILURE;
1584 DBusConnection *bus = NULL;
1587 dbus_error_init(&error);
1589 setlocale(LC_ALL, "");
1590 log_parse_environment();
1593 r = parse_argv(argc, argv);
1597 retval = EXIT_SUCCESS;
1601 if (arg_transport == TRANSPORT_NORMAL)
1602 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1603 else if (arg_transport == TRANSPORT_POLKIT)
1604 bus_connect_system_polkit(&bus, &error);
1605 else if (arg_transport == TRANSPORT_SSH)
1606 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1608 assert_not_reached("Uh, invalid transport...");
1610 r = loginctl_main(bus, argc, argv, &error);
1611 retval = r < 0 ? EXIT_FAILURE : r;
1615 dbus_connection_flush(bus);
1616 dbus_connection_close(bus);
1617 dbus_connection_unref(bus);
1620 dbus_error_free(&error);
1623 strv_free(arg_property);