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.");
101 dbus_message_iter_recurse(&iter, &sub);
104 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
106 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
107 const char *id, *user, *seat, *object;
110 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
111 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.");
126 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
130 dbus_message_iter_next(&sub);
134 printf("\n%u sessions listed.\n", k);
139 static int list_users(DBusConnection *bus, char **args, unsigned n) {
140 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
142 DBusMessageIter iter, sub, sub2;
145 pager_open_if_enabled();
147 r = bus_method_call_with_reply (
149 "org.freedesktop.login1",
150 "/org/freedesktop/login1",
151 "org.freedesktop.login1.Manager",
159 if (!dbus_message_iter_init(reply, &iter) ||
160 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
161 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
162 log_error("Failed to parse reply.");
166 dbus_message_iter_recurse(&iter, &sub);
169 printf("%10s %-16s\n", "UID", "USER");
171 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
172 const char *user, *object;
175 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
176 log_error("Failed to parse reply.");
180 dbus_message_iter_recurse(&sub, &sub2);
182 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
183 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
184 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
185 log_error("Failed to parse reply.");
189 printf("%10u %-16s\n", (unsigned) uid, user);
193 dbus_message_iter_next(&sub);
197 printf("\n%u users listed.\n", k);
202 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
203 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
205 DBusMessageIter iter, sub, sub2;
208 pager_open_if_enabled();
210 r = bus_method_call_with_reply (
212 "org.freedesktop.login1",
213 "/org/freedesktop/login1",
214 "org.freedesktop.login1.Manager",
222 if (!dbus_message_iter_init(reply, &iter) ||
223 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
224 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
225 log_error("Failed to parse reply.");
229 dbus_message_iter_recurse(&iter, &sub);
232 printf("%-16s\n", "SEAT");
234 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
235 const char *seat, *object;
237 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
238 log_error("Failed to parse reply.");
242 dbus_message_iter_recurse(&sub, &sub2);
244 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
245 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
246 log_error("Failed to parse reply.");
250 printf("%-16s\n", seat);
254 dbus_message_iter_next(&sub);
258 printf("\n%u seats listed.\n", k);
263 typedef struct SessionStatusInfo {
268 const char *default_control_group;
274 const char *remote_host;
275 const char *remote_user;
283 typedef struct UserStatusInfo {
287 const char *default_control_group;
293 typedef struct SeatStatusInfo {
295 const char *active_session;
299 static void print_session_status_info(SessionStatusInfo *i) {
300 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
301 char since2[FORMAT_TIMESTAMP_MAX], *s2;
304 printf("%s - ", strna(i->id));
307 printf("%s (%u)\n", i->name, (unsigned) i->uid);
309 printf("%u\n", (unsigned) i->uid);
311 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
312 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
315 printf("\t Since: %s; %s\n", s2, s1);
317 printf("\t Since: %s\n", s2);
322 printf("\t Leader: %u", (unsigned) i->leader);
324 get_process_comm(i->leader, &t);
334 printf("\t Seat: %s", i->seat);
337 printf("; vc%i", i->vtnr);
343 printf("\t TTY: %s\n", i->tty);
345 printf("\t Display: %s\n", i->display);
347 if (i->remote_host && i->remote_user)
348 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
349 else if (i->remote_host)
350 printf("\t Remote: %s\n", i->remote_host);
351 else if (i->remote_user)
352 printf("\t Remote: user %s\n", i->remote_user);
354 printf("\t Remote: Yes\n");
357 printf("\t Service: %s", i->service);
360 printf("; type %s", i->type);
363 printf("; class %s", i->class);
366 } else if (i->type) {
367 printf("\t Type: %s\n", i->type);
370 printf("; class %s", i->class);
372 printf("\t Class: %s\n", i->class);
375 printf("\t State: %s\n", i->state);
377 if (i->default_control_group) {
380 arg_all * OUTPUT_SHOW_ALL |
381 arg_full * OUTPUT_FULL_WIDTH;
383 printf("\t CGroup: %s\n", i->default_control_group);
385 if (arg_transport != TRANSPORT_SSH) {
392 show_cgroup_and_extra_by_spec(i->default_control_group,
393 "\t\t ", c, false, &i->leader,
394 i->leader > 0 ? 1 : 0,
400 static void print_user_status_info(UserStatusInfo *i) {
401 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
402 char since2[FORMAT_TIMESTAMP_MAX], *s2;
406 printf("%s (%u)\n", i->name, (unsigned) i->uid);
408 printf("%u\n", (unsigned) i->uid);
410 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
411 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
414 printf("\t Since: %s; %s\n", s2, s1);
416 printf("\t Since: %s\n", s2);
418 if (!isempty(i->state))
419 printf("\t State: %s\n", i->state);
421 if (!strv_isempty(i->sessions)) {
423 printf("\tSessions:");
425 STRV_FOREACH(l, i->sessions) {
426 if (streq_ptr(*l, i->display))
435 if (i->default_control_group) {
438 arg_all * OUTPUT_SHOW_ALL |
439 arg_full * OUTPUT_FULL_WIDTH;
441 printf("\t CGroup: %s\n", i->default_control_group);
443 if (arg_transport != TRANSPORT_SSH) {
450 show_cgroup_by_path(i->default_control_group, "\t\t ",
451 c, false, output_flags);
456 static void print_seat_status_info(SeatStatusInfo *i) {
459 printf("%s\n", strna(i->id));
461 if (!strv_isempty(i->sessions)) {
463 printf("\tSessions:");
465 STRV_FOREACH(l, i->sessions) {
466 if (streq_ptr(*l, i->active_session))
475 if (arg_transport != TRANSPORT_SSH) {
484 printf("\t Devices:\n");
486 show_sysfs(i->id, "\t\t ", c);
490 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
495 switch (dbus_message_iter_get_arg_type(iter)) {
497 case DBUS_TYPE_STRING: {
500 dbus_message_iter_get_basic(iter, &s);
503 if (streq(name, "Id"))
505 else if (streq(name, "Name"))
507 else if (streq(name, "DefaultControlGroup"))
508 i->default_control_group = s;
509 else if (streq(name, "TTY"))
511 else if (streq(name, "Display"))
513 else if (streq(name, "RemoteHost"))
515 else if (streq(name, "RemoteUser"))
517 else if (streq(name, "Service"))
519 else if (streq(name, "Type"))
521 else if (streq(name, "Class"))
523 else if (streq(name, "State"))
529 case DBUS_TYPE_UINT32: {
532 dbus_message_iter_get_basic(iter, &u);
534 if (streq(name, "VTNr"))
536 else if (streq(name, "Leader"))
537 i->leader = (pid_t) u;
542 case DBUS_TYPE_BOOLEAN: {
545 dbus_message_iter_get_basic(iter, &b);
547 if (streq(name, "Remote"))
553 case DBUS_TYPE_UINT64: {
556 dbus_message_iter_get_basic(iter, &u);
558 if (streq(name, "Timestamp"))
559 i->timestamp = (usec_t) u;
564 case DBUS_TYPE_STRUCT: {
567 dbus_message_iter_recurse(iter, &sub);
569 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
572 dbus_message_iter_get_basic(&sub, &u);
575 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
578 dbus_message_iter_get_basic(&sub, &s);
591 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
596 switch (dbus_message_iter_get_arg_type(iter)) {
598 case DBUS_TYPE_STRING: {
601 dbus_message_iter_get_basic(iter, &s);
604 if (streq(name, "Name"))
606 else if (streq(name, "DefaultControlGroup"))
607 i->default_control_group = s;
608 else if (streq(name, "State"))
614 case DBUS_TYPE_UINT32: {
617 dbus_message_iter_get_basic(iter, &u);
619 if (streq(name, "UID"))
625 case DBUS_TYPE_UINT64: {
628 dbus_message_iter_get_basic(iter, &u);
630 if (streq(name, "Timestamp"))
631 i->timestamp = (usec_t) u;
636 case DBUS_TYPE_STRUCT: {
639 dbus_message_iter_recurse(iter, &sub);
641 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
644 dbus_message_iter_get_basic(&sub, &s);
653 case DBUS_TYPE_ARRAY: {
655 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
656 DBusMessageIter sub, sub2;
658 dbus_message_iter_recurse(iter, &sub);
659 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
663 dbus_message_iter_recurse(&sub, &sub2);
665 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
666 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
669 l = strv_append(i->sessions, id);
673 strv_free(i->sessions);
677 dbus_message_iter_next(&sub);
688 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
693 switch (dbus_message_iter_get_arg_type(iter)) {
695 case DBUS_TYPE_STRING: {
698 dbus_message_iter_get_basic(iter, &s);
701 if (streq(name, "Id"))
707 case DBUS_TYPE_STRUCT: {
710 dbus_message_iter_recurse(iter, &sub);
712 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
715 dbus_message_iter_get_basic(&sub, &s);
718 i->active_session = s;
724 case DBUS_TYPE_ARRAY: {
726 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
727 DBusMessageIter sub, sub2;
729 dbus_message_iter_recurse(iter, &sub);
730 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
734 dbus_message_iter_recurse(&sub, &sub2);
736 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
737 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
740 l = strv_append(i->sessions, id);
744 strv_free(i->sessions);
748 dbus_message_iter_next(&sub);
759 static int print_property(const char *name, DBusMessageIter *iter) {
763 if (arg_property && !strv_find(arg_property, name))
766 switch (dbus_message_iter_get_arg_type(iter)) {
768 case DBUS_TYPE_STRUCT: {
771 dbus_message_iter_recurse(iter, &sub);
773 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
774 (streq(name, "Display") || streq(name, "ActiveSession"))) {
777 dbus_message_iter_get_basic(&sub, &s);
779 if (arg_all || !isempty(s))
780 printf("%s=%s\n", name, s);
786 case DBUS_TYPE_ARRAY:
788 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
789 DBusMessageIter sub, sub2;
792 dbus_message_iter_recurse(iter, &sub);
793 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
797 dbus_message_iter_recurse(&sub, &sub2);
799 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
800 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
804 printf("%s=%s", name, id);
809 dbus_message_iter_next(&sub);
812 if (!found && arg_all)
813 printf("%s=\n", name);
823 if (generic_print_property(name, iter, arg_all) > 0)
827 printf("%s=[unprintable]\n", name);
832 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
833 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
834 const char *interface = "";
836 DBusMessageIter iter, sub, sub2, sub3;
837 SessionStatusInfo session_info = {};
838 UserStatusInfo user_info = {};
839 SeatStatusInfo seat_info = {};
844 r = bus_method_call_with_reply(
846 "org.freedesktop.login1",
848 "org.freedesktop.DBus.Properties",
852 DBUS_TYPE_STRING, &interface,
857 if (!dbus_message_iter_init(reply, &iter) ||
858 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
859 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
860 log_error("Failed to parse reply.");
865 dbus_message_iter_recurse(&iter, &sub);
872 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
875 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
876 log_error("Failed to parse reply.");
881 dbus_message_iter_recurse(&sub, &sub2);
883 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
884 log_error("Failed to parse reply.");
889 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
890 log_error("Failed to parse reply.");
895 dbus_message_iter_recurse(&sub2, &sub3);
898 r = print_property(name, &sub3);
899 else if (strstr(verb, "session"))
900 r = status_property_session(name, &sub3, &session_info);
901 else if (strstr(verb, "user"))
902 r = status_property_user(name, &sub3, &user_info);
904 r = status_property_seat(name, &sub3, &seat_info);
907 log_error("Failed to parse reply.");
911 dbus_message_iter_next(&sub);
914 if (!show_properties) {
915 if (strstr(verb, "session"))
916 print_session_status_info(&session_info);
917 else if (strstr(verb, "user"))
918 print_user_status_info(&user_info);
920 print_seat_status_info(&seat_info);
926 strv_free(seat_info.sessions);
927 strv_free(user_info.sessions);
932 static int show(DBusConnection *bus, char **args, unsigned n) {
933 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
937 bool show_properties, new_line = false;
942 dbus_error_init(&error);
944 show_properties = !strstr(args[0], "status");
946 pager_open_if_enabled();
948 if (show_properties && n <= 1) {
949 /* If not argument is specified inspect the manager
952 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
956 for (i = 1; i < n; i++) {
957 const char *path = NULL;
959 if (strstr(args[0], "session")) {
961 ret = bus_method_call_with_reply (
963 "org.freedesktop.login1",
964 "/org/freedesktop/login1",
965 "org.freedesktop.login1.Manager",
969 DBUS_TYPE_STRING, &args[i],
972 } else if (strstr(args[0], "user")) {
976 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
978 log_error("User %s unknown.", args[i]);
983 ret = bus_method_call_with_reply (
985 "org.freedesktop.login1",
986 "/org/freedesktop/login1",
987 "org.freedesktop.login1.Manager",
991 DBUS_TYPE_UINT32, &u,
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],
1009 if (!dbus_message_get_args(reply, &error,
1010 DBUS_TYPE_OBJECT_PATH, &path,
1011 DBUS_TYPE_INVALID)) {
1012 log_error("Failed to parse reply: %s", bus_error_message(&error));
1017 r = show_one(args[0], bus, path, show_properties, &new_line);
1023 dbus_error_free(&error);
1028 static int activate(DBusConnection *bus, char **args, unsigned n) {
1034 for (i = 1; i < n; i++) {
1036 ret = bus_method_call_with_reply (
1038 "org.freedesktop.login1",
1039 "/org/freedesktop/login1",
1040 "org.freedesktop.login1.Manager",
1041 streq(args[0], "lock-session") ? "LockSession" :
1042 streq(args[0], "unlock-session") ? "UnlockSession" :
1043 streq(args[0], "terminate-session") ? "TerminateSession" :
1047 DBUS_TYPE_STRING, &args[i],
1057 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1063 arg_kill_who = "all";
1065 for (i = 1; i < n; i++) {
1068 r = bus_method_call_with_reply (
1070 "org.freedesktop.login1",
1071 "/org/freedesktop/login1",
1072 "org.freedesktop.login1.Manager",
1076 DBUS_TYPE_STRING, &args[i],
1077 DBUS_TYPE_STRING, &arg_kill_who,
1078 DBUS_TYPE_INT32, &arg_signal,
1087 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1089 dbus_bool_t b, interactive = true;
1093 polkit_agent_open_if_enabled();
1095 b = streq(args[0], "enable-linger");
1097 for (i = 1; i < n; i++) {
1102 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1104 log_error("Failed to resolve user %s: %s", args[i], strerror(-r));
1109 r = bus_method_call_with_reply (
1111 "org.freedesktop.login1",
1112 "/org/freedesktop/login1",
1113 "org.freedesktop.login1.Manager",
1117 DBUS_TYPE_UINT32, &u,
1118 DBUS_TYPE_BOOLEAN, &b,
1119 DBUS_TYPE_BOOLEAN, &interactive,
1128 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1133 for (i = 1; i < n; i++) {
1138 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1140 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1145 r = bus_method_call_with_reply (
1147 "org.freedesktop.login1",
1148 "/org/freedesktop/login1",
1149 "org.freedesktop.login1.Manager",
1153 DBUS_TYPE_UINT32, &u,
1162 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1168 arg_kill_who = "all";
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,
1191 DBUS_TYPE_INT32, &arg_signal,
1200 static int attach(DBusConnection *bus, char **args, unsigned n) {
1202 dbus_bool_t interactive = true;
1206 polkit_agent_open_if_enabled();
1208 for (i = 2; i < n; i++) {
1211 r = bus_method_call_with_reply (
1213 "org.freedesktop.login1",
1214 "/org/freedesktop/login1",
1215 "org.freedesktop.login1.Manager",
1219 DBUS_TYPE_STRING, &args[1],
1220 DBUS_TYPE_STRING, &args[i],
1221 DBUS_TYPE_BOOLEAN, &interactive,
1230 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1231 dbus_bool_t interactive = true;
1235 polkit_agent_open_if_enabled();
1237 return bus_method_call_with_reply (
1239 "org.freedesktop.login1",
1240 "/org/freedesktop/login1",
1241 "org.freedesktop.login1.Manager",
1245 DBUS_TYPE_BOOLEAN, &interactive,
1249 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1252 polkit_agent_open_if_enabled();
1254 return bus_method_call_with_reply (
1256 "org.freedesktop.login1",
1257 "/org/freedesktop/login1",
1258 "org.freedesktop.login1.Manager",
1259 streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1265 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1270 for (i = 1; i < n; i++) {
1273 r = bus_method_call_with_reply (
1275 "org.freedesktop.login1",
1276 "/org/freedesktop/login1",
1277 "org.freedesktop.login1.Manager",
1281 DBUS_TYPE_STRING, &args[i],
1290 static int help(void) {
1292 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1293 "Send control commands to or query the login manager.\n\n"
1294 " -h --help Show this help\n"
1295 " --version Show package version\n"
1296 " -p --property=NAME Show only properties by this name\n"
1297 " -a --all Show all properties, including empty ones\n"
1298 " --kill-who=WHO Who to send signal to\n"
1299 " --full Do not ellipsize output\n"
1300 " -s --signal=SIGNAL Which signal to send\n"
1301 " --no-ask-password Don't prompt for password\n"
1302 " -H --host=[USER@]HOST Show information for remote host\n"
1303 " -P --privileged Acquire privileges before execution\n"
1304 " --no-pager Do not pipe output into a pager\n\n"
1306 " list-sessions List sessions\n"
1307 " session-status [ID...] Show session status\n"
1308 " show-session [ID...] Show properties of one or more sessions\n"
1309 " activate [ID] Activate a session\n"
1310 " lock-session [ID...] Screen lock one or more sessions\n"
1311 " unlock-session [ID...] Screen unlock one or more sessions\n"
1312 " lock-sessions Screen lock all current sessions\n"
1313 " unlock-sessions Screen unlock all current sessions\n"
1314 " terminate-session [ID...] Terminate one or more sessions\n"
1315 " kill-session [ID...] Send signal to processes of a session\n"
1316 " list-users List users\n"
1317 " user-status [USER...] Show user status\n"
1318 " show-user [USER...] Show properties of one or more users\n"
1319 " enable-linger [USER...] Enable linger state of one or more users\n"
1320 " disable-linger [USER...] Disable linger state of one or more users\n"
1321 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1322 " kill-user [USER...] Send signal to processes of a user\n"
1323 " list-seats List seats\n"
1324 " seat-status [NAME...] Show seat status\n"
1325 " show-seat [NAME...] Show properties of one or more seats\n"
1326 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1327 " flush-devices Flush all device associations\n"
1328 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1329 program_invocation_short_name);
1334 static int parse_argv(int argc, char *argv[]) {
1337 ARG_VERSION = 0x100,
1340 ARG_NO_ASK_PASSWORD,
1344 static const struct option options[] = {
1345 { "help", no_argument, NULL, 'h' },
1346 { "version", no_argument, NULL, ARG_VERSION },
1347 { "property", required_argument, NULL, 'p' },
1348 { "all", no_argument, NULL, 'a' },
1349 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1350 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1351 { "signal", required_argument, NULL, 's' },
1352 { "host", required_argument, NULL, 'H' },
1353 { "privileged", no_argument, NULL, 'P' },
1354 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1355 { "full", no_argument, NULL, ARG_FULL },
1356 { NULL, 0, NULL, 0 }
1364 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1373 puts(PACKAGE_STRING);
1374 puts(SYSTEMD_FEATURES);
1380 l = strv_append(arg_property, optarg);
1384 strv_free(arg_property);
1387 /* If the user asked for a particular
1388 * property, show it to him, even if it is
1399 arg_no_pager = true;
1402 case ARG_NO_ASK_PASSWORD:
1403 arg_ask_password = false;
1407 arg_kill_who = optarg;
1411 arg_signal = signal_from_string_try_harder(optarg);
1412 if (arg_signal < 0) {
1413 log_error("Failed to parse signal string %s.", optarg);
1419 arg_transport = TRANSPORT_POLKIT;
1423 arg_transport = TRANSPORT_SSH;
1435 log_error("Unknown option code %c", c);
1443 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1445 static const struct {
1453 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1455 { "list-sessions", LESS, 1, list_sessions },
1456 { "session-status", MORE, 2, show },
1457 { "show-session", MORE, 1, show },
1458 { "activate", EQUAL, 2, activate },
1459 { "lock-session", MORE, 2, activate },
1460 { "unlock-session", MORE, 2, activate },
1461 { "lock-sessions", EQUAL, 1, lock_sessions },
1462 { "unlock-sessions", EQUAL, 1, lock_sessions },
1463 { "terminate-session", MORE, 2, activate },
1464 { "kill-session", MORE, 2, kill_session },
1465 { "list-users", EQUAL, 1, list_users },
1466 { "user-status", MORE, 2, show },
1467 { "show-user", MORE, 1, show },
1468 { "enable-linger", MORE, 2, enable_linger },
1469 { "disable-linger", MORE, 2, enable_linger },
1470 { "terminate-user", MORE, 2, terminate_user },
1471 { "kill-user", MORE, 2, kill_user },
1472 { "list-seats", EQUAL, 1, list_seats },
1473 { "seat-status", MORE, 2, show },
1474 { "show-seat", MORE, 1, show },
1475 { "attach", MORE, 3, attach },
1476 { "flush-devices", EQUAL, 1, flush_devices },
1477 { "terminate-seat", MORE, 2, terminate_seat },
1487 left = argc - optind;
1490 /* Special rule: no arguments means "list-sessions" */
1493 if (streq(argv[optind], "help")) {
1498 for (i = 0; i < ELEMENTSOF(verbs); i++)
1499 if (streq(argv[optind], verbs[i].verb))
1502 if (i >= ELEMENTSOF(verbs)) {
1503 log_error("Unknown operation %s", argv[optind]);
1508 switch (verbs[i].argc_cmp) {
1511 if (left != verbs[i].argc) {
1512 log_error("Invalid number of arguments.");
1519 if (left < verbs[i].argc) {
1520 log_error("Too few arguments.");
1527 if (left > verbs[i].argc) {
1528 log_error("Too many arguments.");
1535 assert_not_reached("Unknown comparison operator.");
1539 log_error("Failed to get D-Bus connection: %s", error->message);
1543 return verbs[i].dispatch(bus, argv + optind, left);
1546 int main(int argc, char*argv[]) {
1547 int r, retval = EXIT_FAILURE;
1548 DBusConnection *bus = NULL;
1551 dbus_error_init(&error);
1553 setlocale(LC_ALL, "");
1554 log_parse_environment();
1557 r = parse_argv(argc, argv);
1561 retval = EXIT_SUCCESS;
1565 if (arg_transport == TRANSPORT_NORMAL)
1566 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1567 else if (arg_transport == TRANSPORT_POLKIT)
1568 bus_connect_system_polkit(&bus, &error);
1569 else if (arg_transport == TRANSPORT_SSH)
1570 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1572 assert_not_reached("Uh, invalid transport...");
1574 r = loginctl_main(bus, argc, argv, &error);
1575 retval = r < 0 ? EXIT_FAILURE : r;
1579 dbus_connection_flush(bus);
1580 dbus_connection_close(bus);
1581 dbus_connection_unref(bus);
1584 dbus_error_free(&error);
1587 strv_free(arg_property);