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>
33 #include "dbus-common.h"
36 #include "cgroup-show.h"
37 #include "sysfs-show.h"
38 #include "spawn-polkit-agent.h"
40 static char **arg_property = NULL;
41 static bool arg_all = false;
42 static bool arg_no_pager = false;
43 static const char *arg_kill_who = NULL;
44 static int arg_signal = SIGTERM;
45 static enum transport {
49 } arg_transport = TRANSPORT_NORMAL;
50 static bool arg_ask_password = true;
51 static const char *arg_host = NULL;
53 static bool on_tty(void) {
56 /* Note that this is invoked relatively early, before we start
57 * the pager. That means the value we return reflects whether
58 * we originally were started on a tty, not if we currently
59 * are. But this is intended, since we want colour and so on
60 * when run in our own pager. */
62 if (_unlikely_(t < 0))
63 t = isatty(STDOUT_FILENO) > 0;
68 static void pager_open_if_enabled(void) {
70 /* Cache result before we open the pager */
79 static void polkit_agent_open_if_enabled(void) {
81 /* Open the polkit agent as a child process if necessary */
83 if (!arg_ask_password)
89 static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
90 DBusMessage *reply = NULL;
92 DBusMessageIter iter, sub, sub2;
95 pager_open_if_enabled();
97 r = bus_method_call_with_reply (
99 "org.freedesktop.login1",
100 "/org/freedesktop/login1",
101 "org.freedesktop.login1.Manager",
109 if (!dbus_message_iter_init(reply, &iter) ||
110 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
111 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
112 log_error("Failed to parse reply.");
117 dbus_message_iter_recurse(&iter, &sub);
120 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
122 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
123 const char *id, *user, *seat, *object;
126 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
127 log_error("Failed to parse reply.");
132 dbus_message_iter_recurse(&sub, &sub2);
134 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
135 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
136 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
137 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
138 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
139 log_error("Failed to parse reply.");
144 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
148 dbus_message_iter_next(&sub);
152 printf("\n%u sessions listed.\n", k);
158 dbus_message_unref(reply);
163 static int list_users(DBusConnection *bus, char **args, unsigned n) {
164 DBusMessage *reply = NULL;
166 DBusMessageIter iter, sub, sub2;
169 pager_open_if_enabled();
171 r = bus_method_call_with_reply (
173 "org.freedesktop.login1",
174 "/org/freedesktop/login1",
175 "org.freedesktop.login1.Manager",
183 if (!dbus_message_iter_init(reply, &iter) ||
184 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
185 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
186 log_error("Failed to parse reply.");
191 dbus_message_iter_recurse(&iter, &sub);
194 printf("%10s %-16s\n", "UID", "USER");
196 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
197 const char *user, *object;
200 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
201 log_error("Failed to parse reply.");
206 dbus_message_iter_recurse(&sub, &sub2);
208 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
209 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
210 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
211 log_error("Failed to parse reply.");
216 printf("%10u %-16s\n", (unsigned) uid, user);
220 dbus_message_iter_next(&sub);
224 printf("\n%u users listed.\n", k);
230 dbus_message_unref(reply);
235 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
236 DBusMessage *reply = NULL;
238 DBusMessageIter iter, sub, sub2;
241 pager_open_if_enabled();
243 r = bus_method_call_with_reply (
245 "org.freedesktop.login1",
246 "/org/freedesktop/login1",
247 "org.freedesktop.login1.Manager",
255 if (!dbus_message_iter_init(reply, &iter) ||
256 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
257 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
258 log_error("Failed to parse reply.");
263 dbus_message_iter_recurse(&iter, &sub);
266 printf("%-16s\n", "SEAT");
268 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
269 const char *seat, *object;
271 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
272 log_error("Failed to parse reply.");
277 dbus_message_iter_recurse(&sub, &sub2);
279 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
280 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
281 log_error("Failed to parse reply.");
286 printf("%-16s\n", seat);
290 dbus_message_iter_next(&sub);
294 printf("\n%u seats listed.\n", k);
300 dbus_message_unref(reply);
305 typedef struct SessionStatusInfo {
310 const char *default_control_group;
316 const char *remote_host;
317 const char *remote_user;
325 typedef struct UserStatusInfo {
329 const char *default_control_group;
335 typedef struct SeatStatusInfo {
337 const char *active_session;
341 static void print_session_status_info(SessionStatusInfo *i) {
342 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
343 char since2[FORMAT_TIMESTAMP_MAX], *s2;
346 printf("%s - ", strna(i->id));
349 printf("%s (%u)\n", i->name, (unsigned) i->uid);
351 printf("%u\n", (unsigned) i->uid);
353 s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
354 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
357 printf("\t Since: %s; %s\n", s2, s1);
359 printf("\t Since: %s\n", s2);
364 printf("\t Leader: %u", (unsigned) i->leader);
366 get_process_comm(i->leader, &t);
376 printf("\t Seat: %s", i->seat);
379 printf("; vc%i", i->vtnr);
385 printf("\t TTY: %s\n", i->tty);
387 printf("\t Display: %s\n", i->display);
389 if (i->remote_host && i->remote_user)
390 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
391 else if (i->remote_host)
392 printf("\t Remote: %s\n", i->remote_host);
393 else if (i->remote_user)
394 printf("\t Remote: user %s\n", i->remote_user);
396 printf("\t Remote: Yes\n");
399 printf("\t Service: %s", i->service);
402 printf("; type %s", i->type);
405 printf("; class %s", i->class);
408 } else if (i->type) {
409 printf("\t Type: %s\n", i->type);
412 printf("; class %s", i->class);
414 printf("\t Class: %s\n", i->class);
417 printf("\t State: %s\n", i->state);
419 if (i->default_control_group) {
422 printf("\t CGroup: %s\n", i->default_control_group);
424 if (arg_transport != TRANSPORT_SSH) {
431 show_cgroup_and_extra_by_spec(i->default_control_group, "\t\t ", c, false, arg_all, &i->leader, i->leader > 0 ? 1 : 0);
436 static void print_user_status_info(UserStatusInfo *i) {
437 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
438 char since2[FORMAT_TIMESTAMP_MAX], *s2;
442 printf("%s (%u)\n", i->name, (unsigned) i->uid);
444 printf("%u\n", (unsigned) i->uid);
446 s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
447 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
450 printf("\t Since: %s; %s\n", s2, s1);
452 printf("\t Since: %s\n", s2);
454 if (!isempty(i->state))
455 printf("\t State: %s\n", i->state);
457 if (!strv_isempty(i->sessions)) {
459 printf("\tSessions:");
461 STRV_FOREACH(l, i->sessions) {
462 if (streq_ptr(*l, i->display))
471 if (i->default_control_group) {
474 printf("\t CGroup: %s\n", i->default_control_group);
476 if (arg_transport != TRANSPORT_SSH) {
483 show_cgroup_by_path(i->default_control_group, "\t\t ", c, false, arg_all);
488 static void print_seat_status_info(SeatStatusInfo *i) {
491 printf("%s\n", strna(i->id));
493 if (!strv_isempty(i->sessions)) {
495 printf("\tSessions:");
497 STRV_FOREACH(l, i->sessions) {
498 if (streq_ptr(*l, i->active_session))
507 if (arg_transport != TRANSPORT_SSH) {
516 printf("\t Devices:\n");
518 show_sysfs(i->id, "\t\t ", c);
522 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
527 switch (dbus_message_iter_get_arg_type(iter)) {
529 case DBUS_TYPE_STRING: {
532 dbus_message_iter_get_basic(iter, &s);
535 if (streq(name, "Id"))
537 else if (streq(name, "Name"))
539 else if (streq(name, "DefaultControlGroup"))
540 i->default_control_group = s;
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, "State"))
561 case DBUS_TYPE_UINT32: {
564 dbus_message_iter_get_basic(iter, &u);
566 if (streq(name, "VTNr"))
568 else if (streq(name, "Leader"))
569 i->leader = (pid_t) u;
574 case DBUS_TYPE_BOOLEAN: {
577 dbus_message_iter_get_basic(iter, &b);
579 if (streq(name, "Remote"))
585 case DBUS_TYPE_UINT64: {
588 dbus_message_iter_get_basic(iter, &u);
590 if (streq(name, "Timestamp"))
591 i->timestamp = (usec_t) u;
596 case DBUS_TYPE_STRUCT: {
599 dbus_message_iter_recurse(iter, &sub);
601 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
604 dbus_message_iter_get_basic(&sub, &u);
607 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
610 dbus_message_iter_get_basic(&sub, &s);
623 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
628 switch (dbus_message_iter_get_arg_type(iter)) {
630 case DBUS_TYPE_STRING: {
633 dbus_message_iter_get_basic(iter, &s);
636 if (streq(name, "Name"))
638 else if (streq(name, "DefaultControlGroup"))
639 i->default_control_group = s;
640 else if (streq(name, "State"))
646 case DBUS_TYPE_UINT32: {
649 dbus_message_iter_get_basic(iter, &u);
651 if (streq(name, "UID"))
657 case DBUS_TYPE_UINT64: {
660 dbus_message_iter_get_basic(iter, &u);
662 if (streq(name, "Timestamp"))
663 i->timestamp = (usec_t) u;
668 case DBUS_TYPE_STRUCT: {
671 dbus_message_iter_recurse(iter, &sub);
673 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
676 dbus_message_iter_get_basic(&sub, &s);
685 case DBUS_TYPE_ARRAY: {
687 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
688 DBusMessageIter sub, sub2;
690 dbus_message_iter_recurse(iter, &sub);
691 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
695 dbus_message_iter_recurse(&sub, &sub2);
697 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
698 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
701 l = strv_append(i->sessions, id);
705 strv_free(i->sessions);
709 dbus_message_iter_next(&sub);
720 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
725 switch (dbus_message_iter_get_arg_type(iter)) {
727 case DBUS_TYPE_STRING: {
730 dbus_message_iter_get_basic(iter, &s);
733 if (streq(name, "Id"))
739 case DBUS_TYPE_STRUCT: {
742 dbus_message_iter_recurse(iter, &sub);
744 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
747 dbus_message_iter_get_basic(&sub, &s);
750 i->active_session = s;
756 case DBUS_TYPE_ARRAY: {
758 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
759 DBusMessageIter sub, sub2;
761 dbus_message_iter_recurse(iter, &sub);
762 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
766 dbus_message_iter_recurse(&sub, &sub2);
768 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
769 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
772 l = strv_append(i->sessions, id);
776 strv_free(i->sessions);
780 dbus_message_iter_next(&sub);
791 static int print_property(const char *name, DBusMessageIter *iter) {
795 if (arg_property && !strv_find(arg_property, name))
798 switch (dbus_message_iter_get_arg_type(iter)) {
800 case DBUS_TYPE_STRUCT: {
803 dbus_message_iter_recurse(iter, &sub);
805 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
806 (streq(name, "Display") || streq(name, "ActiveSession"))) {
809 dbus_message_iter_get_basic(&sub, &s);
811 if (arg_all || !isempty(s))
812 printf("%s=%s\n", name, s);
818 case DBUS_TYPE_ARRAY:
820 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
821 DBusMessageIter sub, sub2;
824 dbus_message_iter_recurse(iter, &sub);
825 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
829 dbus_message_iter_recurse(&sub, &sub2);
831 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
832 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
836 printf("%s=%s", name, id);
841 dbus_message_iter_next(&sub);
844 if (!found && arg_all)
845 printf("%s=\n", name);
855 if (generic_print_property(name, iter, arg_all) > 0)
859 printf("%s=[unprintable]\n", name);
864 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
865 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
866 const char *interface = "";
868 DBusMessageIter iter, sub, sub2, sub3;
869 SessionStatusInfo session_info;
870 UserStatusInfo user_info;
871 SeatStatusInfo seat_info;
880 r = bus_method_call_with_reply(
882 "org.freedesktop.login1",
884 "org.freedesktop.DBus.Properties",
888 DBUS_TYPE_STRING, &interface,
893 if (!dbus_message_iter_init(reply, &iter) ||
894 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
895 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
896 log_error("Failed to parse reply.");
901 dbus_message_iter_recurse(&iter, &sub);
908 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
911 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
912 log_error("Failed to parse reply.");
917 dbus_message_iter_recurse(&sub, &sub2);
919 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
920 log_error("Failed to parse reply.");
925 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
926 log_error("Failed to parse reply.");
931 dbus_message_iter_recurse(&sub2, &sub3);
934 r = print_property(name, &sub3);
935 else if (strstr(verb, "session"))
936 r = status_property_session(name, &sub3, &session_info);
937 else if (strstr(verb, "user"))
938 r = status_property_user(name, &sub3, &user_info);
940 r = status_property_seat(name, &sub3, &seat_info);
943 log_error("Failed to parse reply.");
947 dbus_message_iter_next(&sub);
950 if (!show_properties) {
951 if (strstr(verb, "session"))
952 print_session_status_info(&session_info);
953 else if (strstr(verb, "user"))
954 print_user_status_info(&user_info);
956 print_seat_status_info(&seat_info);
962 strv_free(seat_info.sessions);
963 strv_free(user_info.sessions);
968 static int show(DBusConnection *bus, char **args, unsigned n) {
969 DBusMessage *reply = NULL;
973 bool show_properties, new_line = false;
978 dbus_error_init(&error);
980 show_properties = !strstr(args[0], "status");
983 pager_open_if_enabled();
985 if (show_properties && n <= 1) {
986 /* If not argument is specified inspect the manager
989 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
993 for (i = 1; i < n; i++) {
994 const char *path = NULL;
996 if (strstr(args[0], "session")) {
998 ret = bus_method_call_with_reply (
1000 "org.freedesktop.login1",
1001 "/org/freedesktop/login1",
1002 "org.freedesktop.login1.Manager",
1006 DBUS_TYPE_STRING, &args[i],
1009 } else if (strstr(args[0], "user")) {
1013 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1015 log_error("User %s unknown.", args[i]);
1020 ret = bus_method_call_with_reply (
1022 "org.freedesktop.login1",
1023 "/org/freedesktop/login1",
1024 "org.freedesktop.login1.Manager",
1028 DBUS_TYPE_UINT32, &u,
1032 ret = bus_method_call_with_reply (
1034 "org.freedesktop.login1",
1035 "/org/freedesktop/login1",
1036 "org.freedesktop.login1.Manager",
1040 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);
1058 dbus_message_unref(reply);
1064 dbus_message_unref(reply);
1066 dbus_error_free(&error);
1071 static int activate(DBusConnection *bus, char **args, unsigned n) {
1077 for (i = 1; i < n; i++) {
1079 ret = bus_method_call_with_reply (
1081 "org.freedesktop.login1",
1082 "/org/freedesktop/login1",
1083 "org.freedesktop.login1.Manager",
1084 streq(args[0], "lock-session") ? "LockSession" :
1085 streq(args[0], "unlock-session") ? "UnlockSession" :
1086 streq(args[0], "terminate-session") ? "TerminateSession" :
1090 DBUS_TYPE_STRING, &args[i],
1100 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1107 arg_kill_who = "all";
1109 for (i = 1; i < n; i++) {
1110 ret = bus_method_call_with_reply (
1112 "org.freedesktop.login1",
1113 "/org/freedesktop/login1",
1114 "org.freedesktop.login1.Manager",
1118 DBUS_TYPE_STRING, &args[i],
1119 DBUS_TYPE_STRING, &arg_kill_who,
1120 DBUS_TYPE_INT32, &arg_signal,
1130 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1133 dbus_bool_t b, interactive = true;
1137 polkit_agent_open_if_enabled();
1139 b = streq(args[0], "enable-linger");
1141 for (i = 1; i < n; i++) {
1145 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1147 log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1152 ret = bus_method_call_with_reply (
1154 "org.freedesktop.login1",
1155 "/org/freedesktop/login1",
1156 "org.freedesktop.login1.Manager",
1160 DBUS_TYPE_UINT32, &u,
1161 DBUS_TYPE_BOOLEAN, &b,
1162 DBUS_TYPE_BOOLEAN, &interactive,
1172 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1178 for (i = 1; i < n; i++) {
1182 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1184 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1189 ret = bus_method_call_with_reply (
1191 "org.freedesktop.login1",
1192 "/org/freedesktop/login1",
1193 "org.freedesktop.login1.Manager",
1197 DBUS_TYPE_UINT32, &u,
1207 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1214 arg_kill_who = "all";
1216 for (i = 1; i < n; i++) {
1220 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1222 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1227 ret = bus_method_call_with_reply (
1229 "org.freedesktop.login1",
1230 "/org/freedesktop/login1",
1231 "org.freedesktop.login1.Manager",
1235 DBUS_TYPE_UINT32, &u,
1236 DBUS_TYPE_INT32, &arg_signal,
1246 static int attach(DBusConnection *bus, char **args, unsigned n) {
1249 dbus_bool_t interactive = true;
1253 polkit_agent_open_if_enabled();
1255 for (i = 2; i < n; i++) {
1256 ret = bus_method_call_with_reply (
1258 "org.freedesktop.login1",
1259 "/org/freedesktop/login1",
1260 "org.freedesktop.login1.Manager",
1264 DBUS_TYPE_STRING, &args[1],
1265 DBUS_TYPE_STRING, &args[i],
1266 DBUS_TYPE_BOOLEAN, &interactive,
1276 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1277 dbus_bool_t interactive = true;
1281 polkit_agent_open_if_enabled();
1283 return bus_method_call_with_reply (
1285 "org.freedesktop.login1",
1286 "/org/freedesktop/login1",
1287 "org.freedesktop.login1.Manager",
1291 DBUS_TYPE_BOOLEAN, &interactive,
1295 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1296 polkit_agent_open_if_enabled();
1298 return bus_method_call_with_reply (
1300 "org.freedesktop.login1",
1301 "/org/freedesktop/login1",
1302 "org.freedesktop.login1.Manager",
1309 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1315 for (i = 1; i < n; i++) {
1316 ret = bus_method_call_with_reply (
1318 "org.freedesktop.login1",
1319 "/org/freedesktop/login1",
1320 "org.freedesktop.login1.Manager",
1324 DBUS_TYPE_STRING, &args[i],
1334 static int help(void) {
1336 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1337 "Send control commands to or query the login manager.\n\n"
1338 " -h --help Show this help\n"
1339 " --version Show package version\n"
1340 " -p --property=NAME Show only properties by this name\n"
1341 " -a --all Show all properties, including empty ones\n"
1342 " --kill-who=WHO Who to send signal to\n"
1343 " -s --signal=SIGNAL Which signal to send\n"
1344 " --no-ask-password Don't prompt for password\n"
1345 " -H --host=[USER@]HOST Show information for remote host\n"
1346 " -P --privileged Acquire privileges before execution\n"
1347 " --no-pager Do not pipe output into a pager\n\n"
1349 " list-sessions List sessions\n"
1350 " session-status [ID...] Show session status\n"
1351 " show-session [ID...] Show properties of one or more sessions\n"
1352 " activate [ID] Activate a session\n"
1353 " lock-session [ID...] Screen lock one or more sessions\n"
1354 " unlock-session [ID...] Screen unlock one or more sessions\n"
1355 " lock-sessions Screen lock all current sessions\n"
1356 " terminate-session [ID...] Terminate one or more sessions\n"
1357 " kill-session [ID...] Send signal to processes of a session\n"
1358 " list-users List users\n"
1359 " user-status [USER...] Show user status\n"
1360 " show-user [USER...] Show properties of one or more users\n"
1361 " enable-linger [USER...] Enable linger state of one or more users\n"
1362 " disable-linger [USER...] Disable linger state of one or more users\n"
1363 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1364 " kill-user [USER...] Send signal to processes of a user\n"
1365 " list-seats List seats\n"
1366 " seat-status [NAME...] Show seat status\n"
1367 " show-seat [NAME...] Show properties of one or more seats\n"
1368 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1369 " flush-devices Flush all device associations\n"
1370 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1371 program_invocation_short_name);
1376 static int parse_argv(int argc, char *argv[]) {
1379 ARG_VERSION = 0x100,
1385 static const struct option options[] = {
1386 { "help", no_argument, NULL, 'h' },
1387 { "version", no_argument, NULL, ARG_VERSION },
1388 { "property", required_argument, NULL, 'p' },
1389 { "all", no_argument, NULL, 'a' },
1390 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1391 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1392 { "signal", required_argument, NULL, 's' },
1393 { "host", required_argument, NULL, 'H' },
1394 { "privileged", no_argument, NULL, 'P' },
1395 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1396 { NULL, 0, NULL, 0 }
1404 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1413 puts(PACKAGE_STRING);
1415 puts(SYSTEMD_FEATURES);
1421 l = strv_append(arg_property, optarg);
1425 strv_free(arg_property);
1428 /* If the user asked for a particular
1429 * property, show it to him, even if it is
1440 arg_no_pager = true;
1443 case ARG_NO_ASK_PASSWORD:
1444 arg_ask_password = false;
1448 arg_kill_who = optarg;
1452 arg_signal = signal_from_string_try_harder(optarg);
1453 if (arg_signal < 0) {
1454 log_error("Failed to parse signal string %s.", optarg);
1460 arg_transport = TRANSPORT_POLKIT;
1464 arg_transport = TRANSPORT_SSH;
1472 log_error("Unknown option code %c", c);
1480 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1482 static const struct {
1490 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1492 { "list-sessions", LESS, 1, list_sessions },
1493 { "session-status", MORE, 2, show },
1494 { "show-session", MORE, 1, show },
1495 { "activate", EQUAL, 2, activate },
1496 { "lock-session", MORE, 2, activate },
1497 { "unlock-session", MORE, 2, activate },
1498 { "lock-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 log_parse_environment();
1592 r = parse_argv(argc, argv);
1596 retval = EXIT_SUCCESS;
1600 if (arg_transport == TRANSPORT_NORMAL)
1601 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1602 else if (arg_transport == TRANSPORT_POLKIT)
1603 bus_connect_system_polkit(&bus, &error);
1604 else if (arg_transport == TRANSPORT_SSH)
1605 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1607 assert_not_reached("Uh, invalid transport...");
1609 r = loginctl_main(bus, argc, argv, &error);
1610 retval = r < 0 ? EXIT_FAILURE : r;
1614 dbus_connection_flush(bus);
1615 dbus_connection_close(bus);
1616 dbus_connection_unref(bus);
1619 dbus_error_free(&error);
1622 strv_free(arg_property);