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 void pager_open_if_enabled(void) {
55 /* Cache result before we open the pager */
62 static void polkit_agent_open_if_enabled(void) {
64 /* Open the polkit agent as a child process if necessary */
66 if (!arg_ask_password)
72 static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
73 DBusMessage *reply = NULL;
75 DBusMessageIter iter, sub, sub2;
78 pager_open_if_enabled();
80 r = bus_method_call_with_reply (
82 "org.freedesktop.login1",
83 "/org/freedesktop/login1",
84 "org.freedesktop.login1.Manager",
92 if (!dbus_message_iter_init(reply, &iter) ||
93 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
94 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
95 log_error("Failed to parse reply.");
100 dbus_message_iter_recurse(&iter, &sub);
103 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
105 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
106 const char *id, *user, *seat, *object;
109 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
110 log_error("Failed to parse reply.");
115 dbus_message_iter_recurse(&sub, &sub2);
117 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
118 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
119 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
120 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
121 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
122 log_error("Failed to parse reply.");
127 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
131 dbus_message_iter_next(&sub);
135 printf("\n%u sessions listed.\n", k);
141 dbus_message_unref(reply);
146 static int list_users(DBusConnection *bus, char **args, unsigned n) {
147 DBusMessage *reply = NULL;
149 DBusMessageIter iter, sub, sub2;
152 pager_open_if_enabled();
154 r = bus_method_call_with_reply (
156 "org.freedesktop.login1",
157 "/org/freedesktop/login1",
158 "org.freedesktop.login1.Manager",
166 if (!dbus_message_iter_init(reply, &iter) ||
167 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
168 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
169 log_error("Failed to parse reply.");
174 dbus_message_iter_recurse(&iter, &sub);
177 printf("%10s %-16s\n", "UID", "USER");
179 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
180 const char *user, *object;
183 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
184 log_error("Failed to parse reply.");
189 dbus_message_iter_recurse(&sub, &sub2);
191 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
192 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
193 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
194 log_error("Failed to parse reply.");
199 printf("%10u %-16s\n", (unsigned) uid, user);
203 dbus_message_iter_next(&sub);
207 printf("\n%u users listed.\n", k);
213 dbus_message_unref(reply);
218 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
219 DBusMessage *reply = NULL;
221 DBusMessageIter iter, sub, sub2;
224 pager_open_if_enabled();
226 r = bus_method_call_with_reply (
228 "org.freedesktop.login1",
229 "/org/freedesktop/login1",
230 "org.freedesktop.login1.Manager",
238 if (!dbus_message_iter_init(reply, &iter) ||
239 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
240 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
241 log_error("Failed to parse reply.");
246 dbus_message_iter_recurse(&iter, &sub);
249 printf("%-16s\n", "SEAT");
251 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
252 const char *seat, *object;
254 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
255 log_error("Failed to parse reply.");
260 dbus_message_iter_recurse(&sub, &sub2);
262 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
263 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
264 log_error("Failed to parse reply.");
269 printf("%-16s\n", seat);
273 dbus_message_iter_next(&sub);
277 printf("\n%u seats listed.\n", k);
283 dbus_message_unref(reply);
288 typedef struct SessionStatusInfo {
293 const char *default_control_group;
299 const char *remote_host;
300 const char *remote_user;
308 typedef struct UserStatusInfo {
312 const char *default_control_group;
318 typedef struct SeatStatusInfo {
320 const char *active_session;
324 static void print_session_status_info(SessionStatusInfo *i) {
325 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
326 char since2[FORMAT_TIMESTAMP_MAX], *s2;
329 printf("%s - ", strna(i->id));
332 printf("%s (%u)\n", i->name, (unsigned) i->uid);
334 printf("%u\n", (unsigned) i->uid);
336 s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
337 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
340 printf("\t Since: %s; %s\n", s2, s1);
342 printf("\t Since: %s\n", s2);
347 printf("\t Leader: %u", (unsigned) i->leader);
349 get_process_comm(i->leader, &t);
359 printf("\t Seat: %s", i->seat);
362 printf("; vc%i", i->vtnr);
368 printf("\t TTY: %s\n", i->tty);
370 printf("\t Display: %s\n", i->display);
372 if (i->remote_host && i->remote_user)
373 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
374 else if (i->remote_host)
375 printf("\t Remote: %s\n", i->remote_host);
376 else if (i->remote_user)
377 printf("\t Remote: user %s\n", i->remote_user);
379 printf("\t Remote: Yes\n");
382 printf("\t Service: %s", i->service);
385 printf("; type %s", i->type);
388 printf("; class %s", i->class);
391 } else if (i->type) {
392 printf("\t Type: %s\n", i->type);
395 printf("; class %s", i->class);
397 printf("\t Class: %s\n", i->class);
400 printf("\t State: %s\n", i->state);
402 if (i->default_control_group) {
405 printf("\t CGroup: %s\n", i->default_control_group);
407 if (arg_transport != TRANSPORT_SSH) {
414 show_cgroup_and_extra_by_spec(i->default_control_group, "\t\t ", c, false, arg_all, &i->leader, i->leader > 0 ? 1 : 0);
419 static void print_user_status_info(UserStatusInfo *i) {
420 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
421 char since2[FORMAT_TIMESTAMP_MAX], *s2;
425 printf("%s (%u)\n", i->name, (unsigned) i->uid);
427 printf("%u\n", (unsigned) i->uid);
429 s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
430 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
433 printf("\t Since: %s; %s\n", s2, s1);
435 printf("\t Since: %s\n", s2);
437 if (!isempty(i->state))
438 printf("\t State: %s\n", i->state);
440 if (!strv_isempty(i->sessions)) {
442 printf("\tSessions:");
444 STRV_FOREACH(l, i->sessions) {
445 if (streq_ptr(*l, i->display))
454 if (i->default_control_group) {
457 printf("\t CGroup: %s\n", i->default_control_group);
459 if (arg_transport != TRANSPORT_SSH) {
466 show_cgroup_by_path(i->default_control_group, "\t\t ", c, false, arg_all);
471 static void print_seat_status_info(SeatStatusInfo *i) {
474 printf("%s\n", strna(i->id));
476 if (!strv_isempty(i->sessions)) {
478 printf("\tSessions:");
480 STRV_FOREACH(l, i->sessions) {
481 if (streq_ptr(*l, i->active_session))
490 if (arg_transport != TRANSPORT_SSH) {
499 printf("\t Devices:\n");
501 show_sysfs(i->id, "\t\t ", c);
505 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
510 switch (dbus_message_iter_get_arg_type(iter)) {
512 case DBUS_TYPE_STRING: {
515 dbus_message_iter_get_basic(iter, &s);
518 if (streq(name, "Id"))
520 else if (streq(name, "Name"))
522 else if (streq(name, "DefaultControlGroup"))
523 i->default_control_group = s;
524 else if (streq(name, "TTY"))
526 else if (streq(name, "Display"))
528 else if (streq(name, "RemoteHost"))
530 else if (streq(name, "RemoteUser"))
532 else if (streq(name, "Service"))
534 else if (streq(name, "Type"))
536 else if (streq(name, "Class"))
538 else if (streq(name, "State"))
544 case DBUS_TYPE_UINT32: {
547 dbus_message_iter_get_basic(iter, &u);
549 if (streq(name, "VTNr"))
551 else if (streq(name, "Leader"))
552 i->leader = (pid_t) u;
557 case DBUS_TYPE_BOOLEAN: {
560 dbus_message_iter_get_basic(iter, &b);
562 if (streq(name, "Remote"))
568 case DBUS_TYPE_UINT64: {
571 dbus_message_iter_get_basic(iter, &u);
573 if (streq(name, "Timestamp"))
574 i->timestamp = (usec_t) u;
579 case DBUS_TYPE_STRUCT: {
582 dbus_message_iter_recurse(iter, &sub);
584 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
587 dbus_message_iter_get_basic(&sub, &u);
590 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
593 dbus_message_iter_get_basic(&sub, &s);
606 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
611 switch (dbus_message_iter_get_arg_type(iter)) {
613 case DBUS_TYPE_STRING: {
616 dbus_message_iter_get_basic(iter, &s);
619 if (streq(name, "Name"))
621 else if (streq(name, "DefaultControlGroup"))
622 i->default_control_group = s;
623 else if (streq(name, "State"))
629 case DBUS_TYPE_UINT32: {
632 dbus_message_iter_get_basic(iter, &u);
634 if (streq(name, "UID"))
640 case DBUS_TYPE_UINT64: {
643 dbus_message_iter_get_basic(iter, &u);
645 if (streq(name, "Timestamp"))
646 i->timestamp = (usec_t) u;
651 case DBUS_TYPE_STRUCT: {
654 dbus_message_iter_recurse(iter, &sub);
656 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
659 dbus_message_iter_get_basic(&sub, &s);
668 case DBUS_TYPE_ARRAY: {
670 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
671 DBusMessageIter sub, sub2;
673 dbus_message_iter_recurse(iter, &sub);
674 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
678 dbus_message_iter_recurse(&sub, &sub2);
680 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
681 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
684 l = strv_append(i->sessions, id);
688 strv_free(i->sessions);
692 dbus_message_iter_next(&sub);
703 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
708 switch (dbus_message_iter_get_arg_type(iter)) {
710 case DBUS_TYPE_STRING: {
713 dbus_message_iter_get_basic(iter, &s);
716 if (streq(name, "Id"))
722 case DBUS_TYPE_STRUCT: {
725 dbus_message_iter_recurse(iter, &sub);
727 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
730 dbus_message_iter_get_basic(&sub, &s);
733 i->active_session = s;
739 case DBUS_TYPE_ARRAY: {
741 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
742 DBusMessageIter sub, sub2;
744 dbus_message_iter_recurse(iter, &sub);
745 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
749 dbus_message_iter_recurse(&sub, &sub2);
751 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
752 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
755 l = strv_append(i->sessions, id);
759 strv_free(i->sessions);
763 dbus_message_iter_next(&sub);
774 static int print_property(const char *name, DBusMessageIter *iter) {
778 if (arg_property && !strv_find(arg_property, name))
781 switch (dbus_message_iter_get_arg_type(iter)) {
783 case DBUS_TYPE_STRUCT: {
786 dbus_message_iter_recurse(iter, &sub);
788 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
789 (streq(name, "Display") || streq(name, "ActiveSession"))) {
792 dbus_message_iter_get_basic(&sub, &s);
794 if (arg_all || !isempty(s))
795 printf("%s=%s\n", name, s);
801 case DBUS_TYPE_ARRAY:
803 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
804 DBusMessageIter sub, sub2;
807 dbus_message_iter_recurse(iter, &sub);
808 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
812 dbus_message_iter_recurse(&sub, &sub2);
814 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
815 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
819 printf("%s=%s", name, id);
824 dbus_message_iter_next(&sub);
827 if (!found && arg_all)
828 printf("%s=\n", name);
838 if (generic_print_property(name, iter, arg_all) > 0)
842 printf("%s=[unprintable]\n", name);
847 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
848 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
849 const char *interface = "";
851 DBusMessageIter iter, sub, sub2, sub3;
852 SessionStatusInfo session_info;
853 UserStatusInfo user_info;
854 SeatStatusInfo seat_info;
863 r = bus_method_call_with_reply(
865 "org.freedesktop.login1",
867 "org.freedesktop.DBus.Properties",
871 DBUS_TYPE_STRING, &interface,
876 if (!dbus_message_iter_init(reply, &iter) ||
877 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
878 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
879 log_error("Failed to parse reply.");
884 dbus_message_iter_recurse(&iter, &sub);
891 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
894 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
895 log_error("Failed to parse reply.");
900 dbus_message_iter_recurse(&sub, &sub2);
902 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
903 log_error("Failed to parse reply.");
908 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
909 log_error("Failed to parse reply.");
914 dbus_message_iter_recurse(&sub2, &sub3);
917 r = print_property(name, &sub3);
918 else if (strstr(verb, "session"))
919 r = status_property_session(name, &sub3, &session_info);
920 else if (strstr(verb, "user"))
921 r = status_property_user(name, &sub3, &user_info);
923 r = status_property_seat(name, &sub3, &seat_info);
926 log_error("Failed to parse reply.");
930 dbus_message_iter_next(&sub);
933 if (!show_properties) {
934 if (strstr(verb, "session"))
935 print_session_status_info(&session_info);
936 else if (strstr(verb, "user"))
937 print_user_status_info(&user_info);
939 print_seat_status_info(&seat_info);
945 strv_free(seat_info.sessions);
946 strv_free(user_info.sessions);
951 static int show(DBusConnection *bus, char **args, unsigned n) {
952 DBusMessage *reply = NULL;
956 bool show_properties, new_line = false;
961 dbus_error_init(&error);
963 show_properties = !strstr(args[0], "status");
965 pager_open_if_enabled();
967 if (show_properties && n <= 1) {
968 /* If not argument is specified inspect the manager
971 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
975 for (i = 1; i < n; i++) {
976 const char *path = NULL;
978 if (strstr(args[0], "session")) {
980 ret = bus_method_call_with_reply (
982 "org.freedesktop.login1",
983 "/org/freedesktop/login1",
984 "org.freedesktop.login1.Manager",
988 DBUS_TYPE_STRING, &args[i],
991 } else if (strstr(args[0], "user")) {
995 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
997 log_error("User %s unknown.", args[i]);
1002 ret = bus_method_call_with_reply (
1004 "org.freedesktop.login1",
1005 "/org/freedesktop/login1",
1006 "org.freedesktop.login1.Manager",
1010 DBUS_TYPE_UINT32, &u,
1014 ret = bus_method_call_with_reply (
1016 "org.freedesktop.login1",
1017 "/org/freedesktop/login1",
1018 "org.freedesktop.login1.Manager",
1022 DBUS_TYPE_STRING, &args[i],
1028 if (!dbus_message_get_args(reply, &error,
1029 DBUS_TYPE_OBJECT_PATH, &path,
1030 DBUS_TYPE_INVALID)) {
1031 log_error("Failed to parse reply: %s", bus_error_message(&error));
1036 r = show_one(args[0], bus, path, show_properties, &new_line);
1040 dbus_message_unref(reply);
1046 dbus_message_unref(reply);
1048 dbus_error_free(&error);
1053 static int activate(DBusConnection *bus, char **args, unsigned n) {
1059 for (i = 1; i < n; i++) {
1061 ret = bus_method_call_with_reply (
1063 "org.freedesktop.login1",
1064 "/org/freedesktop/login1",
1065 "org.freedesktop.login1.Manager",
1066 streq(args[0], "lock-session") ? "LockSession" :
1067 streq(args[0], "unlock-session") ? "UnlockSession" :
1068 streq(args[0], "terminate-session") ? "TerminateSession" :
1072 DBUS_TYPE_STRING, &args[i],
1082 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1089 arg_kill_who = "all";
1091 for (i = 1; i < n; i++) {
1092 ret = bus_method_call_with_reply (
1094 "org.freedesktop.login1",
1095 "/org/freedesktop/login1",
1096 "org.freedesktop.login1.Manager",
1100 DBUS_TYPE_STRING, &args[i],
1101 DBUS_TYPE_STRING, &arg_kill_who,
1102 DBUS_TYPE_INT32, &arg_signal,
1112 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1115 dbus_bool_t b, interactive = true;
1119 polkit_agent_open_if_enabled();
1121 b = streq(args[0], "enable-linger");
1123 for (i = 1; i < n; i++) {
1127 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1129 log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1134 ret = bus_method_call_with_reply (
1136 "org.freedesktop.login1",
1137 "/org/freedesktop/login1",
1138 "org.freedesktop.login1.Manager",
1142 DBUS_TYPE_UINT32, &u,
1143 DBUS_TYPE_BOOLEAN, &b,
1144 DBUS_TYPE_BOOLEAN, &interactive,
1154 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1160 for (i = 1; i < n; i++) {
1164 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1166 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1171 ret = bus_method_call_with_reply (
1173 "org.freedesktop.login1",
1174 "/org/freedesktop/login1",
1175 "org.freedesktop.login1.Manager",
1179 DBUS_TYPE_UINT32, &u,
1189 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1196 arg_kill_who = "all";
1198 for (i = 1; i < n; i++) {
1202 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1204 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1209 ret = bus_method_call_with_reply (
1211 "org.freedesktop.login1",
1212 "/org/freedesktop/login1",
1213 "org.freedesktop.login1.Manager",
1217 DBUS_TYPE_UINT32, &u,
1218 DBUS_TYPE_INT32, &arg_signal,
1228 static int attach(DBusConnection *bus, char **args, unsigned n) {
1231 dbus_bool_t interactive = true;
1235 polkit_agent_open_if_enabled();
1237 for (i = 2; i < n; i++) {
1238 ret = bus_method_call_with_reply (
1240 "org.freedesktop.login1",
1241 "/org/freedesktop/login1",
1242 "org.freedesktop.login1.Manager",
1246 DBUS_TYPE_STRING, &args[1],
1247 DBUS_TYPE_STRING, &args[i],
1248 DBUS_TYPE_BOOLEAN, &interactive,
1258 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1259 dbus_bool_t interactive = true;
1263 polkit_agent_open_if_enabled();
1265 return bus_method_call_with_reply (
1267 "org.freedesktop.login1",
1268 "/org/freedesktop/login1",
1269 "org.freedesktop.login1.Manager",
1273 DBUS_TYPE_BOOLEAN, &interactive,
1277 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1278 polkit_agent_open_if_enabled();
1280 return bus_method_call_with_reply (
1282 "org.freedesktop.login1",
1283 "/org/freedesktop/login1",
1284 "org.freedesktop.login1.Manager",
1291 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1297 for (i = 1; i < n; i++) {
1298 ret = bus_method_call_with_reply (
1300 "org.freedesktop.login1",
1301 "/org/freedesktop/login1",
1302 "org.freedesktop.login1.Manager",
1306 DBUS_TYPE_STRING, &args[i],
1316 static int help(void) {
1318 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1319 "Send control commands to or query the login manager.\n\n"
1320 " -h --help Show this help\n"
1321 " --version Show package version\n"
1322 " -p --property=NAME Show only properties by this name\n"
1323 " -a --all Show all properties, including empty ones\n"
1324 " --kill-who=WHO Who to send signal to\n"
1325 " -s --signal=SIGNAL Which signal to send\n"
1326 " --no-ask-password Don't prompt for password\n"
1327 " -H --host=[USER@]HOST Show information for remote host\n"
1328 " -P --privileged Acquire privileges before execution\n"
1329 " --no-pager Do not pipe output into a pager\n\n"
1331 " list-sessions List sessions\n"
1332 " session-status [ID...] Show session status\n"
1333 " show-session [ID...] Show properties of one or more sessions\n"
1334 " activate [ID] Activate a session\n"
1335 " lock-session [ID...] Screen lock one or more sessions\n"
1336 " unlock-session [ID...] Screen unlock one or more sessions\n"
1337 " lock-sessions Screen lock all current sessions\n"
1338 " terminate-session [ID...] Terminate one or more sessions\n"
1339 " kill-session [ID...] Send signal to processes of a session\n"
1340 " list-users List users\n"
1341 " user-status [USER...] Show user status\n"
1342 " show-user [USER...] Show properties of one or more users\n"
1343 " enable-linger [USER...] Enable linger state of one or more users\n"
1344 " disable-linger [USER...] Disable linger state of one or more users\n"
1345 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1346 " kill-user [USER...] Send signal to processes of a user\n"
1347 " list-seats List seats\n"
1348 " seat-status [NAME...] Show seat status\n"
1349 " show-seat [NAME...] Show properties of one or more seats\n"
1350 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1351 " flush-devices Flush all device associations\n"
1352 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1353 program_invocation_short_name);
1358 static int parse_argv(int argc, char *argv[]) {
1361 ARG_VERSION = 0x100,
1367 static const struct option options[] = {
1368 { "help", no_argument, NULL, 'h' },
1369 { "version", no_argument, NULL, ARG_VERSION },
1370 { "property", required_argument, NULL, 'p' },
1371 { "all", no_argument, NULL, 'a' },
1372 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1373 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1374 { "signal", required_argument, NULL, 's' },
1375 { "host", required_argument, NULL, 'H' },
1376 { "privileged", no_argument, NULL, 'P' },
1377 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1378 { NULL, 0, NULL, 0 }
1386 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1395 puts(PACKAGE_STRING);
1397 puts(SYSTEMD_FEATURES);
1403 l = strv_append(arg_property, optarg);
1407 strv_free(arg_property);
1410 /* If the user asked for a particular
1411 * property, show it to him, even if it is
1422 arg_no_pager = true;
1425 case ARG_NO_ASK_PASSWORD:
1426 arg_ask_password = false;
1430 arg_kill_who = optarg;
1434 arg_signal = signal_from_string_try_harder(optarg);
1435 if (arg_signal < 0) {
1436 log_error("Failed to parse signal string %s.", optarg);
1442 arg_transport = TRANSPORT_POLKIT;
1446 arg_transport = TRANSPORT_SSH;
1454 log_error("Unknown option code %c", c);
1462 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1464 static const struct {
1472 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1474 { "list-sessions", LESS, 1, list_sessions },
1475 { "session-status", MORE, 2, show },
1476 { "show-session", MORE, 1, show },
1477 { "activate", EQUAL, 2, activate },
1478 { "lock-session", MORE, 2, activate },
1479 { "unlock-session", MORE, 2, activate },
1480 { "lock-sessions", EQUAL, 1, lock_sessions },
1481 { "terminate-session", MORE, 2, activate },
1482 { "kill-session", MORE, 2, kill_session },
1483 { "list-users", EQUAL, 1, list_users },
1484 { "user-status", MORE, 2, show },
1485 { "show-user", MORE, 1, show },
1486 { "enable-linger", MORE, 2, enable_linger },
1487 { "disable-linger", MORE, 2, enable_linger },
1488 { "terminate-user", MORE, 2, terminate_user },
1489 { "kill-user", MORE, 2, kill_user },
1490 { "list-seats", EQUAL, 1, list_seats },
1491 { "seat-status", MORE, 2, show },
1492 { "show-seat", MORE, 1, show },
1493 { "attach", MORE, 3, attach },
1494 { "flush-devices", EQUAL, 1, flush_devices },
1495 { "terminate-seat", MORE, 2, terminate_seat },
1505 left = argc - optind;
1508 /* Special rule: no arguments means "list-sessions" */
1511 if (streq(argv[optind], "help")) {
1516 for (i = 0; i < ELEMENTSOF(verbs); i++)
1517 if (streq(argv[optind], verbs[i].verb))
1520 if (i >= ELEMENTSOF(verbs)) {
1521 log_error("Unknown operation %s", argv[optind]);
1526 switch (verbs[i].argc_cmp) {
1529 if (left != verbs[i].argc) {
1530 log_error("Invalid number of arguments.");
1537 if (left < verbs[i].argc) {
1538 log_error("Too few arguments.");
1545 if (left > verbs[i].argc) {
1546 log_error("Too many arguments.");
1553 assert_not_reached("Unknown comparison operator.");
1557 log_error("Failed to get D-Bus connection: %s", error->message);
1561 return verbs[i].dispatch(bus, argv + optind, left);
1564 int main(int argc, char*argv[]) {
1565 int r, retval = EXIT_FAILURE;
1566 DBusConnection *bus = NULL;
1569 dbus_error_init(&error);
1571 log_parse_environment();
1574 r = parse_argv(argc, argv);
1578 retval = EXIT_SUCCESS;
1582 if (arg_transport == TRANSPORT_NORMAL)
1583 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1584 else if (arg_transport == TRANSPORT_POLKIT)
1585 bus_connect_system_polkit(&bus, &error);
1586 else if (arg_transport == TRANSPORT_SSH)
1587 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1589 assert_not_reached("Uh, invalid transport...");
1591 r = loginctl_main(bus, argc, argv, &error);
1592 retval = r < 0 ? EXIT_FAILURE : r;
1596 dbus_connection_flush(bus);
1597 dbus_connection_close(bus);
1598 dbus_connection_unref(bus);
1601 dbus_error_free(&error);
1604 strv_free(arg_property);