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 "cgroup-show.h"
38 #include "sysfs-show.h"
39 #include "spawn-polkit-agent.h"
41 static char **arg_property = NULL;
42 static bool arg_all = false;
43 static bool arg_full = false;
44 static bool arg_no_pager = false;
45 static const char *arg_kill_who = NULL;
46 static int arg_signal = SIGTERM;
47 static enum transport {
51 } arg_transport = TRANSPORT_NORMAL;
52 static bool arg_ask_password = true;
53 static const char *arg_host = NULL;
55 static void pager_open_if_enabled(void) {
57 /* Cache result before we open the pager */
64 static void polkit_agent_open_if_enabled(void) {
66 /* Open the polkit agent as a child process if necessary */
68 if (!arg_ask_password)
74 static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
75 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
77 DBusMessageIter iter, sub, sub2;
80 pager_open_if_enabled();
82 r = bus_method_call_with_reply (
84 "org.freedesktop.login1",
85 "/org/freedesktop/login1",
86 "org.freedesktop.login1.Manager",
94 if (!dbus_message_iter_init(reply, &iter) ||
95 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
96 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
97 log_error("Failed to parse reply.");
102 dbus_message_iter_recurse(&iter, &sub);
105 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
107 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
108 const char *id, *user, *seat, *object;
111 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
112 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.");
129 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
133 dbus_message_iter_next(&sub);
137 printf("\n%u sessions listed.\n", k);
145 static int list_users(DBusConnection *bus, char **args, unsigned n) {
146 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
148 DBusMessageIter iter, sub, sub2;
151 pager_open_if_enabled();
153 r = bus_method_call_with_reply (
155 "org.freedesktop.login1",
156 "/org/freedesktop/login1",
157 "org.freedesktop.login1.Manager",
165 if (!dbus_message_iter_init(reply, &iter) ||
166 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
167 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
168 log_error("Failed to parse reply.");
173 dbus_message_iter_recurse(&iter, &sub);
176 printf("%10s %-16s\n", "UID", "USER");
178 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
179 const char *user, *object;
182 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
183 log_error("Failed to parse reply.");
188 dbus_message_iter_recurse(&sub, &sub2);
190 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
191 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
192 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
193 log_error("Failed to parse reply.");
198 printf("%10u %-16s\n", (unsigned) uid, user);
202 dbus_message_iter_next(&sub);
206 printf("\n%u users listed.\n", k);
214 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
215 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
217 DBusMessageIter iter, sub, sub2;
220 pager_open_if_enabled();
222 r = bus_method_call_with_reply (
224 "org.freedesktop.login1",
225 "/org/freedesktop/login1",
226 "org.freedesktop.login1.Manager",
234 if (!dbus_message_iter_init(reply, &iter) ||
235 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
236 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
237 log_error("Failed to parse reply.");
242 dbus_message_iter_recurse(&iter, &sub);
245 printf("%-16s\n", "SEAT");
247 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
248 const char *seat, *object;
250 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
251 log_error("Failed to parse reply.");
256 dbus_message_iter_recurse(&sub, &sub2);
258 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
259 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
260 log_error("Failed to parse reply.");
265 printf("%-16s\n", seat);
269 dbus_message_iter_next(&sub);
273 printf("\n%u seats listed.\n", k);
281 typedef struct SessionStatusInfo {
286 const char *default_control_group;
292 const char *remote_host;
293 const char *remote_user;
301 typedef struct UserStatusInfo {
305 const char *default_control_group;
311 typedef struct SeatStatusInfo {
313 const char *active_session;
317 static void print_session_status_info(SessionStatusInfo *i) {
318 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
319 char since2[FORMAT_TIMESTAMP_MAX], *s2;
322 printf("%s - ", strna(i->id));
325 printf("%s (%u)\n", i->name, (unsigned) i->uid);
327 printf("%u\n", (unsigned) i->uid);
329 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
330 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
333 printf("\t Since: %s; %s\n", s2, s1);
335 printf("\t Since: %s\n", s2);
340 printf("\t Leader: %u", (unsigned) i->leader);
342 get_process_comm(i->leader, &t);
352 printf("\t Seat: %s", i->seat);
355 printf("; vc%i", i->vtnr);
361 printf("\t TTY: %s\n", i->tty);
363 printf("\t Display: %s\n", i->display);
365 if (i->remote_host && i->remote_user)
366 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
367 else if (i->remote_host)
368 printf("\t Remote: %s\n", i->remote_host);
369 else if (i->remote_user)
370 printf("\t Remote: user %s\n", i->remote_user);
372 printf("\t Remote: Yes\n");
375 printf("\t Service: %s", i->service);
378 printf("; type %s", i->type);
381 printf("; class %s", i->class);
384 } else if (i->type) {
385 printf("\t Type: %s\n", i->type);
388 printf("; class %s", i->class);
390 printf("\t Class: %s\n", i->class);
393 printf("\t State: %s\n", i->state);
395 if (i->default_control_group) {
398 arg_all * OUTPUT_SHOW_ALL |
399 arg_full * OUTPUT_FULL_WIDTH;
401 printf("\t CGroup: %s\n", i->default_control_group);
403 if (arg_transport != TRANSPORT_SSH) {
410 show_cgroup_and_extra_by_spec(i->default_control_group,
411 "\t\t ", c, false, &i->leader,
412 i->leader > 0 ? 1 : 0,
418 static void print_user_status_info(UserStatusInfo *i) {
419 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
420 char since2[FORMAT_TIMESTAMP_MAX], *s2;
424 printf("%s (%u)\n", i->name, (unsigned) i->uid);
426 printf("%u\n", (unsigned) i->uid);
428 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
429 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
432 printf("\t Since: %s; %s\n", s2, s1);
434 printf("\t Since: %s\n", s2);
436 if (!isempty(i->state))
437 printf("\t State: %s\n", i->state);
439 if (!strv_isempty(i->sessions)) {
441 printf("\tSessions:");
443 STRV_FOREACH(l, i->sessions) {
444 if (streq_ptr(*l, i->display))
453 if (i->default_control_group) {
456 arg_all * OUTPUT_SHOW_ALL |
457 arg_full * OUTPUT_FULL_WIDTH;
459 printf("\t CGroup: %s\n", i->default_control_group);
461 if (arg_transport != TRANSPORT_SSH) {
468 show_cgroup_by_path(i->default_control_group, "\t\t ",
469 c, false, output_flags);
474 static void print_seat_status_info(SeatStatusInfo *i) {
477 printf("%s\n", strna(i->id));
479 if (!strv_isempty(i->sessions)) {
481 printf("\tSessions:");
483 STRV_FOREACH(l, i->sessions) {
484 if (streq_ptr(*l, i->active_session))
493 if (arg_transport != TRANSPORT_SSH) {
502 printf("\t Devices:\n");
504 show_sysfs(i->id, "\t\t ", c);
508 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
513 switch (dbus_message_iter_get_arg_type(iter)) {
515 case DBUS_TYPE_STRING: {
518 dbus_message_iter_get_basic(iter, &s);
521 if (streq(name, "Id"))
523 else if (streq(name, "Name"))
525 else if (streq(name, "DefaultControlGroup"))
526 i->default_control_group = s;
527 else if (streq(name, "TTY"))
529 else if (streq(name, "Display"))
531 else if (streq(name, "RemoteHost"))
533 else if (streq(name, "RemoteUser"))
535 else if (streq(name, "Service"))
537 else if (streq(name, "Type"))
539 else if (streq(name, "Class"))
541 else if (streq(name, "State"))
547 case DBUS_TYPE_UINT32: {
550 dbus_message_iter_get_basic(iter, &u);
552 if (streq(name, "VTNr"))
554 else if (streq(name, "Leader"))
555 i->leader = (pid_t) u;
560 case DBUS_TYPE_BOOLEAN: {
563 dbus_message_iter_get_basic(iter, &b);
565 if (streq(name, "Remote"))
571 case DBUS_TYPE_UINT64: {
574 dbus_message_iter_get_basic(iter, &u);
576 if (streq(name, "Timestamp"))
577 i->timestamp = (usec_t) u;
582 case DBUS_TYPE_STRUCT: {
585 dbus_message_iter_recurse(iter, &sub);
587 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
590 dbus_message_iter_get_basic(&sub, &u);
593 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
596 dbus_message_iter_get_basic(&sub, &s);
609 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
614 switch (dbus_message_iter_get_arg_type(iter)) {
616 case DBUS_TYPE_STRING: {
619 dbus_message_iter_get_basic(iter, &s);
622 if (streq(name, "Name"))
624 else if (streq(name, "DefaultControlGroup"))
625 i->default_control_group = s;
626 else if (streq(name, "State"))
632 case DBUS_TYPE_UINT32: {
635 dbus_message_iter_get_basic(iter, &u);
637 if (streq(name, "UID"))
643 case DBUS_TYPE_UINT64: {
646 dbus_message_iter_get_basic(iter, &u);
648 if (streq(name, "Timestamp"))
649 i->timestamp = (usec_t) u;
654 case DBUS_TYPE_STRUCT: {
657 dbus_message_iter_recurse(iter, &sub);
659 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
662 dbus_message_iter_get_basic(&sub, &s);
671 case DBUS_TYPE_ARRAY: {
673 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
674 DBusMessageIter sub, sub2;
676 dbus_message_iter_recurse(iter, &sub);
677 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
681 dbus_message_iter_recurse(&sub, &sub2);
683 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
684 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
687 l = strv_append(i->sessions, id);
691 strv_free(i->sessions);
695 dbus_message_iter_next(&sub);
706 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
711 switch (dbus_message_iter_get_arg_type(iter)) {
713 case DBUS_TYPE_STRING: {
716 dbus_message_iter_get_basic(iter, &s);
719 if (streq(name, "Id"))
725 case DBUS_TYPE_STRUCT: {
728 dbus_message_iter_recurse(iter, &sub);
730 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
733 dbus_message_iter_get_basic(&sub, &s);
736 i->active_session = s;
742 case DBUS_TYPE_ARRAY: {
744 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
745 DBusMessageIter sub, sub2;
747 dbus_message_iter_recurse(iter, &sub);
748 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
752 dbus_message_iter_recurse(&sub, &sub2);
754 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
755 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
758 l = strv_append(i->sessions, id);
762 strv_free(i->sessions);
766 dbus_message_iter_next(&sub);
777 static int print_property(const char *name, DBusMessageIter *iter) {
781 if (arg_property && !strv_find(arg_property, name))
784 switch (dbus_message_iter_get_arg_type(iter)) {
786 case DBUS_TYPE_STRUCT: {
789 dbus_message_iter_recurse(iter, &sub);
791 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
792 (streq(name, "Display") || streq(name, "ActiveSession"))) {
795 dbus_message_iter_get_basic(&sub, &s);
797 if (arg_all || !isempty(s))
798 printf("%s=%s\n", name, s);
804 case DBUS_TYPE_ARRAY:
806 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
807 DBusMessageIter sub, sub2;
810 dbus_message_iter_recurse(iter, &sub);
811 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
815 dbus_message_iter_recurse(&sub, &sub2);
817 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
818 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
822 printf("%s=%s", name, id);
827 dbus_message_iter_next(&sub);
830 if (!found && arg_all)
831 printf("%s=\n", name);
841 if (generic_print_property(name, iter, arg_all) > 0)
845 printf("%s=[unprintable]\n", name);
850 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
851 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
852 const char *interface = "";
854 DBusMessageIter iter, sub, sub2, sub3;
855 SessionStatusInfo session_info;
856 UserStatusInfo user_info;
857 SeatStatusInfo seat_info;
866 r = bus_method_call_with_reply(
868 "org.freedesktop.login1",
870 "org.freedesktop.DBus.Properties",
874 DBUS_TYPE_STRING, &interface,
879 if (!dbus_message_iter_init(reply, &iter) ||
880 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
881 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
882 log_error("Failed to parse reply.");
887 dbus_message_iter_recurse(&iter, &sub);
894 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
897 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
898 log_error("Failed to parse reply.");
903 dbus_message_iter_recurse(&sub, &sub2);
905 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
906 log_error("Failed to parse reply.");
911 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
912 log_error("Failed to parse reply.");
917 dbus_message_iter_recurse(&sub2, &sub3);
920 r = print_property(name, &sub3);
921 else if (strstr(verb, "session"))
922 r = status_property_session(name, &sub3, &session_info);
923 else if (strstr(verb, "user"))
924 r = status_property_user(name, &sub3, &user_info);
926 r = status_property_seat(name, &sub3, &seat_info);
929 log_error("Failed to parse reply.");
933 dbus_message_iter_next(&sub);
936 if (!show_properties) {
937 if (strstr(verb, "session"))
938 print_session_status_info(&session_info);
939 else if (strstr(verb, "user"))
940 print_user_status_info(&user_info);
942 print_seat_status_info(&seat_info);
948 strv_free(seat_info.sessions);
949 strv_free(user_info.sessions);
954 static int show(DBusConnection *bus, char **args, unsigned n) {
955 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
959 bool show_properties, new_line = false;
964 dbus_error_init(&error);
966 show_properties = !strstr(args[0], "status");
968 pager_open_if_enabled();
970 if (show_properties && n <= 1) {
971 /* If not argument is specified inspect the manager
974 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
978 for (i = 1; i < n; i++) {
979 const char *path = NULL;
981 if (strstr(args[0], "session")) {
983 ret = bus_method_call_with_reply (
985 "org.freedesktop.login1",
986 "/org/freedesktop/login1",
987 "org.freedesktop.login1.Manager",
991 DBUS_TYPE_STRING, &args[i],
994 } else if (strstr(args[0], "user")) {
998 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1000 log_error("User %s unknown.", args[i]);
1005 ret = bus_method_call_with_reply (
1007 "org.freedesktop.login1",
1008 "/org/freedesktop/login1",
1009 "org.freedesktop.login1.Manager",
1013 DBUS_TYPE_UINT32, &u,
1017 ret = bus_method_call_with_reply (
1019 "org.freedesktop.login1",
1020 "/org/freedesktop/login1",
1021 "org.freedesktop.login1.Manager",
1025 DBUS_TYPE_STRING, &args[i],
1031 if (!dbus_message_get_args(reply, &error,
1032 DBUS_TYPE_OBJECT_PATH, &path,
1033 DBUS_TYPE_INVALID)) {
1034 log_error("Failed to parse reply: %s", bus_error_message(&error));
1039 r = show_one(args[0], bus, path, show_properties, &new_line);
1045 dbus_error_free(&error);
1050 static int activate(DBusConnection *bus, char **args, unsigned n) {
1056 for (i = 1; i < n; i++) {
1058 ret = bus_method_call_with_reply (
1060 "org.freedesktop.login1",
1061 "/org/freedesktop/login1",
1062 "org.freedesktop.login1.Manager",
1063 streq(args[0], "lock-session") ? "LockSession" :
1064 streq(args[0], "unlock-session") ? "UnlockSession" :
1065 streq(args[0], "terminate-session") ? "TerminateSession" :
1069 DBUS_TYPE_STRING, &args[i],
1079 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1086 arg_kill_who = "all";
1088 for (i = 1; i < n; i++) {
1089 ret = bus_method_call_with_reply (
1091 "org.freedesktop.login1",
1092 "/org/freedesktop/login1",
1093 "org.freedesktop.login1.Manager",
1097 DBUS_TYPE_STRING, &args[i],
1098 DBUS_TYPE_STRING, &arg_kill_who,
1099 DBUS_TYPE_INT32, &arg_signal,
1109 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1112 dbus_bool_t b, interactive = true;
1116 polkit_agent_open_if_enabled();
1118 b = streq(args[0], "enable-linger");
1120 for (i = 1; i < n; i++) {
1124 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1126 log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1131 ret = bus_method_call_with_reply (
1133 "org.freedesktop.login1",
1134 "/org/freedesktop/login1",
1135 "org.freedesktop.login1.Manager",
1139 DBUS_TYPE_UINT32, &u,
1140 DBUS_TYPE_BOOLEAN, &b,
1141 DBUS_TYPE_BOOLEAN, &interactive,
1151 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1157 for (i = 1; i < n; i++) {
1161 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1163 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1168 ret = bus_method_call_with_reply (
1170 "org.freedesktop.login1",
1171 "/org/freedesktop/login1",
1172 "org.freedesktop.login1.Manager",
1176 DBUS_TYPE_UINT32, &u,
1186 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1193 arg_kill_who = "all";
1195 for (i = 1; i < n; i++) {
1199 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1201 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1206 ret = bus_method_call_with_reply (
1208 "org.freedesktop.login1",
1209 "/org/freedesktop/login1",
1210 "org.freedesktop.login1.Manager",
1214 DBUS_TYPE_UINT32, &u,
1215 DBUS_TYPE_INT32, &arg_signal,
1225 static int attach(DBusConnection *bus, char **args, unsigned n) {
1228 dbus_bool_t interactive = true;
1232 polkit_agent_open_if_enabled();
1234 for (i = 2; i < n; i++) {
1235 ret = bus_method_call_with_reply (
1237 "org.freedesktop.login1",
1238 "/org/freedesktop/login1",
1239 "org.freedesktop.login1.Manager",
1243 DBUS_TYPE_STRING, &args[1],
1244 DBUS_TYPE_STRING, &args[i],
1245 DBUS_TYPE_BOOLEAN, &interactive,
1255 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1256 dbus_bool_t interactive = true;
1260 polkit_agent_open_if_enabled();
1262 return bus_method_call_with_reply (
1264 "org.freedesktop.login1",
1265 "/org/freedesktop/login1",
1266 "org.freedesktop.login1.Manager",
1270 DBUS_TYPE_BOOLEAN, &interactive,
1274 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1277 polkit_agent_open_if_enabled();
1279 return bus_method_call_with_reply (
1281 "org.freedesktop.login1",
1282 "/org/freedesktop/login1",
1283 "org.freedesktop.login1.Manager",
1284 streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1290 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1296 for (i = 1; i < n; i++) {
1297 ret = bus_method_call_with_reply (
1299 "org.freedesktop.login1",
1300 "/org/freedesktop/login1",
1301 "org.freedesktop.login1.Manager",
1305 DBUS_TYPE_STRING, &args[i],
1315 static int help(void) {
1317 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1318 "Send control commands to or query the login manager.\n\n"
1319 " -h --help Show this help\n"
1320 " --version Show package version\n"
1321 " -p --property=NAME Show only properties by this name\n"
1322 " -a --all Show all properties, including empty ones\n"
1323 " --kill-who=WHO Who to send signal to\n"
1324 " --full Do not ellipsize output\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 " unlock-sessions Screen unlock all current sessions\n"
1339 " terminate-session [ID...] Terminate one or more sessions\n"
1340 " kill-session [ID...] Send signal to processes of a session\n"
1341 " list-users List users\n"
1342 " user-status [USER...] Show user status\n"
1343 " show-user [USER...] Show properties of one or more users\n"
1344 " enable-linger [USER...] Enable linger state of one or more users\n"
1345 " disable-linger [USER...] Disable linger state of one or more users\n"
1346 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1347 " kill-user [USER...] Send signal to processes of a user\n"
1348 " list-seats List seats\n"
1349 " seat-status [NAME...] Show seat status\n"
1350 " show-seat [NAME...] Show properties of one or more seats\n"
1351 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1352 " flush-devices Flush all device associations\n"
1353 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1354 program_invocation_short_name);
1359 static int parse_argv(int argc, char *argv[]) {
1362 ARG_VERSION = 0x100,
1365 ARG_NO_ASK_PASSWORD,
1369 static const struct option options[] = {
1370 { "help", no_argument, NULL, 'h' },
1371 { "version", no_argument, NULL, ARG_VERSION },
1372 { "property", required_argument, NULL, 'p' },
1373 { "all", no_argument, NULL, 'a' },
1374 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1375 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1376 { "signal", required_argument, NULL, 's' },
1377 { "host", required_argument, NULL, 'H' },
1378 { "privileged", no_argument, NULL, 'P' },
1379 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1380 { "full", no_argument, NULL, ARG_FULL },
1381 { NULL, 0, NULL, 0 }
1389 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1398 puts(PACKAGE_STRING);
1399 puts(SYSTEMD_FEATURES);
1405 l = strv_append(arg_property, optarg);
1409 strv_free(arg_property);
1412 /* If the user asked for a particular
1413 * property, show it to him, even if it is
1424 arg_no_pager = true;
1427 case ARG_NO_ASK_PASSWORD:
1428 arg_ask_password = false;
1432 arg_kill_who = optarg;
1436 arg_signal = signal_from_string_try_harder(optarg);
1437 if (arg_signal < 0) {
1438 log_error("Failed to parse signal string %s.", optarg);
1444 arg_transport = TRANSPORT_POLKIT;
1448 arg_transport = TRANSPORT_SSH;
1460 log_error("Unknown option code %c", c);
1468 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1470 static const struct {
1478 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1480 { "list-sessions", LESS, 1, list_sessions },
1481 { "session-status", MORE, 2, show },
1482 { "show-session", MORE, 1, show },
1483 { "activate", EQUAL, 2, activate },
1484 { "lock-session", MORE, 2, activate },
1485 { "unlock-session", MORE, 2, activate },
1486 { "lock-sessions", EQUAL, 1, lock_sessions },
1487 { "unlock-sessions", EQUAL, 1, lock_sessions },
1488 { "terminate-session", MORE, 2, activate },
1489 { "kill-session", MORE, 2, kill_session },
1490 { "list-users", EQUAL, 1, list_users },
1491 { "user-status", MORE, 2, show },
1492 { "show-user", MORE, 1, show },
1493 { "enable-linger", MORE, 2, enable_linger },
1494 { "disable-linger", MORE, 2, enable_linger },
1495 { "terminate-user", MORE, 2, terminate_user },
1496 { "kill-user", MORE, 2, kill_user },
1497 { "list-seats", EQUAL, 1, list_seats },
1498 { "seat-status", MORE, 2, show },
1499 { "show-seat", MORE, 1, show },
1500 { "attach", MORE, 3, attach },
1501 { "flush-devices", EQUAL, 1, flush_devices },
1502 { "terminate-seat", MORE, 2, terminate_seat },
1512 left = argc - optind;
1515 /* Special rule: no arguments means "list-sessions" */
1518 if (streq(argv[optind], "help")) {
1523 for (i = 0; i < ELEMENTSOF(verbs); i++)
1524 if (streq(argv[optind], verbs[i].verb))
1527 if (i >= ELEMENTSOF(verbs)) {
1528 log_error("Unknown operation %s", argv[optind]);
1533 switch (verbs[i].argc_cmp) {
1536 if (left != verbs[i].argc) {
1537 log_error("Invalid number of arguments.");
1544 if (left < verbs[i].argc) {
1545 log_error("Too few arguments.");
1552 if (left > verbs[i].argc) {
1553 log_error("Too many arguments.");
1560 assert_not_reached("Unknown comparison operator.");
1564 log_error("Failed to get D-Bus connection: %s", error->message);
1568 return verbs[i].dispatch(bus, argv + optind, left);
1571 int main(int argc, char*argv[]) {
1572 int r, retval = EXIT_FAILURE;
1573 DBusConnection *bus = NULL;
1576 dbus_error_init(&error);
1578 setlocale(LC_ALL, "");
1579 log_parse_environment();
1582 r = parse_argv(argc, argv);
1586 retval = EXIT_SUCCESS;
1590 if (arg_transport == TRANSPORT_NORMAL)
1591 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1592 else if (arg_transport == TRANSPORT_POLKIT)
1593 bus_connect_system_polkit(&bus, &error);
1594 else if (arg_transport == TRANSPORT_SSH)
1595 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1597 assert_not_reached("Uh, invalid transport...");
1599 r = loginctl_main(bus, argc, argv, &error);
1600 retval = r < 0 ? EXIT_FAILURE : r;
1604 dbus_connection_flush(bus);
1605 dbus_connection_close(bus);
1606 dbus_connection_unref(bus);
1609 dbus_error_free(&error);
1612 strv_free(arg_property);