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_no_pager = false;
44 static const char *arg_kill_who = NULL;
45 static int arg_signal = SIGTERM;
46 static enum transport {
50 } arg_transport = TRANSPORT_NORMAL;
51 static bool arg_ask_password = true;
52 static const char *arg_host = NULL;
54 static void pager_open_if_enabled(void) {
56 /* Cache result before we open the pager */
63 static void polkit_agent_open_if_enabled(void) {
65 /* Open the polkit agent as a child process if necessary */
67 if (!arg_ask_password)
73 static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
74 DBusMessage *reply = NULL;
76 DBusMessageIter iter, sub, sub2;
79 pager_open_if_enabled();
81 r = bus_method_call_with_reply (
83 "org.freedesktop.login1",
84 "/org/freedesktop/login1",
85 "org.freedesktop.login1.Manager",
93 if (!dbus_message_iter_init(reply, &iter) ||
94 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
95 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
96 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.");
116 dbus_message_iter_recurse(&sub, &sub2);
118 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
119 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
120 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
121 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
122 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
123 log_error("Failed to parse reply.");
128 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
132 dbus_message_iter_next(&sub);
136 printf("\n%u sessions listed.\n", k);
142 dbus_message_unref(reply);
147 static int list_users(DBusConnection *bus, char **args, unsigned n) {
148 DBusMessage *reply = NULL;
150 DBusMessageIter iter, sub, sub2;
153 pager_open_if_enabled();
155 r = bus_method_call_with_reply (
157 "org.freedesktop.login1",
158 "/org/freedesktop/login1",
159 "org.freedesktop.login1.Manager",
167 if (!dbus_message_iter_init(reply, &iter) ||
168 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
169 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
170 log_error("Failed to parse reply.");
175 dbus_message_iter_recurse(&iter, &sub);
178 printf("%10s %-16s\n", "UID", "USER");
180 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
181 const char *user, *object;
184 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
185 log_error("Failed to parse reply.");
190 dbus_message_iter_recurse(&sub, &sub2);
192 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
193 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
194 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
195 log_error("Failed to parse reply.");
200 printf("%10u %-16s\n", (unsigned) uid, user);
204 dbus_message_iter_next(&sub);
208 printf("\n%u users listed.\n", k);
214 dbus_message_unref(reply);
219 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
220 DBusMessage *reply = NULL;
222 DBusMessageIter iter, sub, sub2;
225 pager_open_if_enabled();
227 r = bus_method_call_with_reply (
229 "org.freedesktop.login1",
230 "/org/freedesktop/login1",
231 "org.freedesktop.login1.Manager",
239 if (!dbus_message_iter_init(reply, &iter) ||
240 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
241 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
242 log_error("Failed to parse reply.");
247 dbus_message_iter_recurse(&iter, &sub);
250 printf("%-16s\n", "SEAT");
252 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
253 const char *seat, *object;
255 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
256 log_error("Failed to parse reply.");
261 dbus_message_iter_recurse(&sub, &sub2);
263 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
264 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
265 log_error("Failed to parse reply.");
270 printf("%-16s\n", seat);
274 dbus_message_iter_next(&sub);
278 printf("\n%u seats listed.\n", k);
284 dbus_message_unref(reply);
289 typedef struct SessionStatusInfo {
294 const char *default_control_group;
300 const char *remote_host;
301 const char *remote_user;
309 typedef struct UserStatusInfo {
313 const char *default_control_group;
319 typedef struct SeatStatusInfo {
321 const char *active_session;
325 static void print_session_status_info(SessionStatusInfo *i) {
326 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
327 char since2[FORMAT_TIMESTAMP_MAX], *s2;
330 printf("%s - ", strna(i->id));
333 printf("%s (%u)\n", i->name, (unsigned) i->uid);
335 printf("%u\n", (unsigned) i->uid);
337 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
338 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
341 printf("\t Since: %s; %s\n", s2, s1);
343 printf("\t Since: %s\n", s2);
348 printf("\t Leader: %u", (unsigned) i->leader);
350 get_process_comm(i->leader, &t);
360 printf("\t Seat: %s", i->seat);
363 printf("; vc%i", i->vtnr);
369 printf("\t TTY: %s\n", i->tty);
371 printf("\t Display: %s\n", i->display);
373 if (i->remote_host && i->remote_user)
374 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
375 else if (i->remote_host)
376 printf("\t Remote: %s\n", i->remote_host);
377 else if (i->remote_user)
378 printf("\t Remote: user %s\n", i->remote_user);
380 printf("\t Remote: Yes\n");
383 printf("\t Service: %s", i->service);
386 printf("; type %s", i->type);
389 printf("; class %s", i->class);
392 } else if (i->type) {
393 printf("\t Type: %s\n", i->type);
396 printf("; class %s", i->class);
398 printf("\t Class: %s\n", i->class);
401 printf("\t State: %s\n", i->state);
403 if (i->default_control_group) {
406 printf("\t CGroup: %s\n", i->default_control_group);
408 if (arg_transport != TRANSPORT_SSH) {
415 show_cgroup_and_extra_by_spec(i->default_control_group, "\t\t ", c, false, arg_all, &i->leader, i->leader > 0 ? 1 : 0);
420 static void print_user_status_info(UserStatusInfo *i) {
421 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
422 char since2[FORMAT_TIMESTAMP_MAX], *s2;
426 printf("%s (%u)\n", i->name, (unsigned) i->uid);
428 printf("%u\n", (unsigned) i->uid);
430 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
431 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
434 printf("\t Since: %s; %s\n", s2, s1);
436 printf("\t Since: %s\n", s2);
438 if (!isempty(i->state))
439 printf("\t State: %s\n", i->state);
441 if (!strv_isempty(i->sessions)) {
443 printf("\tSessions:");
445 STRV_FOREACH(l, i->sessions) {
446 if (streq_ptr(*l, i->display))
455 if (i->default_control_group) {
458 printf("\t CGroup: %s\n", i->default_control_group);
460 if (arg_transport != TRANSPORT_SSH) {
467 show_cgroup_by_path(i->default_control_group, "\t\t ", c, false, arg_all);
472 static void print_seat_status_info(SeatStatusInfo *i) {
475 printf("%s\n", strna(i->id));
477 if (!strv_isempty(i->sessions)) {
479 printf("\tSessions:");
481 STRV_FOREACH(l, i->sessions) {
482 if (streq_ptr(*l, i->active_session))
491 if (arg_transport != TRANSPORT_SSH) {
500 printf("\t Devices:\n");
502 show_sysfs(i->id, "\t\t ", c);
506 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
511 switch (dbus_message_iter_get_arg_type(iter)) {
513 case DBUS_TYPE_STRING: {
516 dbus_message_iter_get_basic(iter, &s);
519 if (streq(name, "Id"))
521 else if (streq(name, "Name"))
523 else if (streq(name, "DefaultControlGroup"))
524 i->default_control_group = s;
525 else if (streq(name, "TTY"))
527 else if (streq(name, "Display"))
529 else if (streq(name, "RemoteHost"))
531 else if (streq(name, "RemoteUser"))
533 else if (streq(name, "Service"))
535 else if (streq(name, "Type"))
537 else if (streq(name, "Class"))
539 else if (streq(name, "State"))
545 case DBUS_TYPE_UINT32: {
548 dbus_message_iter_get_basic(iter, &u);
550 if (streq(name, "VTNr"))
552 else if (streq(name, "Leader"))
553 i->leader = (pid_t) u;
558 case DBUS_TYPE_BOOLEAN: {
561 dbus_message_iter_get_basic(iter, &b);
563 if (streq(name, "Remote"))
569 case DBUS_TYPE_UINT64: {
572 dbus_message_iter_get_basic(iter, &u);
574 if (streq(name, "Timestamp"))
575 i->timestamp = (usec_t) u;
580 case DBUS_TYPE_STRUCT: {
583 dbus_message_iter_recurse(iter, &sub);
585 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
588 dbus_message_iter_get_basic(&sub, &u);
591 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
594 dbus_message_iter_get_basic(&sub, &s);
607 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
612 switch (dbus_message_iter_get_arg_type(iter)) {
614 case DBUS_TYPE_STRING: {
617 dbus_message_iter_get_basic(iter, &s);
620 if (streq(name, "Name"))
622 else if (streq(name, "DefaultControlGroup"))
623 i->default_control_group = s;
624 else if (streq(name, "State"))
630 case DBUS_TYPE_UINT32: {
633 dbus_message_iter_get_basic(iter, &u);
635 if (streq(name, "UID"))
641 case DBUS_TYPE_UINT64: {
644 dbus_message_iter_get_basic(iter, &u);
646 if (streq(name, "Timestamp"))
647 i->timestamp = (usec_t) u;
652 case DBUS_TYPE_STRUCT: {
655 dbus_message_iter_recurse(iter, &sub);
657 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
660 dbus_message_iter_get_basic(&sub, &s);
669 case DBUS_TYPE_ARRAY: {
671 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
672 DBusMessageIter sub, sub2;
674 dbus_message_iter_recurse(iter, &sub);
675 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
679 dbus_message_iter_recurse(&sub, &sub2);
681 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
682 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
685 l = strv_append(i->sessions, id);
689 strv_free(i->sessions);
693 dbus_message_iter_next(&sub);
704 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
709 switch (dbus_message_iter_get_arg_type(iter)) {
711 case DBUS_TYPE_STRING: {
714 dbus_message_iter_get_basic(iter, &s);
717 if (streq(name, "Id"))
723 case DBUS_TYPE_STRUCT: {
726 dbus_message_iter_recurse(iter, &sub);
728 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
731 dbus_message_iter_get_basic(&sub, &s);
734 i->active_session = s;
740 case DBUS_TYPE_ARRAY: {
742 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
743 DBusMessageIter sub, sub2;
745 dbus_message_iter_recurse(iter, &sub);
746 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
750 dbus_message_iter_recurse(&sub, &sub2);
752 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
753 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
756 l = strv_append(i->sessions, id);
760 strv_free(i->sessions);
764 dbus_message_iter_next(&sub);
775 static int print_property(const char *name, DBusMessageIter *iter) {
779 if (arg_property && !strv_find(arg_property, name))
782 switch (dbus_message_iter_get_arg_type(iter)) {
784 case DBUS_TYPE_STRUCT: {
787 dbus_message_iter_recurse(iter, &sub);
789 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
790 (streq(name, "Display") || streq(name, "ActiveSession"))) {
793 dbus_message_iter_get_basic(&sub, &s);
795 if (arg_all || !isempty(s))
796 printf("%s=%s\n", name, s);
802 case DBUS_TYPE_ARRAY:
804 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
805 DBusMessageIter sub, sub2;
808 dbus_message_iter_recurse(iter, &sub);
809 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
813 dbus_message_iter_recurse(&sub, &sub2);
815 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
816 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
820 printf("%s=%s", name, id);
825 dbus_message_iter_next(&sub);
828 if (!found && arg_all)
829 printf("%s=\n", name);
839 if (generic_print_property(name, iter, arg_all) > 0)
843 printf("%s=[unprintable]\n", name);
848 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
849 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
850 const char *interface = "";
852 DBusMessageIter iter, sub, sub2, sub3;
853 SessionStatusInfo session_info;
854 UserStatusInfo user_info;
855 SeatStatusInfo seat_info;
864 r = bus_method_call_with_reply(
866 "org.freedesktop.login1",
868 "org.freedesktop.DBus.Properties",
872 DBUS_TYPE_STRING, &interface,
877 if (!dbus_message_iter_init(reply, &iter) ||
878 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
879 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
880 log_error("Failed to parse reply.");
885 dbus_message_iter_recurse(&iter, &sub);
892 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
895 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
896 log_error("Failed to parse reply.");
901 dbus_message_iter_recurse(&sub, &sub2);
903 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
904 log_error("Failed to parse reply.");
909 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
910 log_error("Failed to parse reply.");
915 dbus_message_iter_recurse(&sub2, &sub3);
918 r = print_property(name, &sub3);
919 else if (strstr(verb, "session"))
920 r = status_property_session(name, &sub3, &session_info);
921 else if (strstr(verb, "user"))
922 r = status_property_user(name, &sub3, &user_info);
924 r = status_property_seat(name, &sub3, &seat_info);
927 log_error("Failed to parse reply.");
931 dbus_message_iter_next(&sub);
934 if (!show_properties) {
935 if (strstr(verb, "session"))
936 print_session_status_info(&session_info);
937 else if (strstr(verb, "user"))
938 print_user_status_info(&user_info);
940 print_seat_status_info(&seat_info);
946 strv_free(seat_info.sessions);
947 strv_free(user_info.sessions);
952 static int show(DBusConnection *bus, char **args, unsigned n) {
953 DBusMessage *reply = NULL;
957 bool show_properties, new_line = false;
962 dbus_error_init(&error);
964 show_properties = !strstr(args[0], "status");
966 pager_open_if_enabled();
968 if (show_properties && n <= 1) {
969 /* If not argument is specified inspect the manager
972 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
976 for (i = 1; i < n; i++) {
977 const char *path = NULL;
979 if (strstr(args[0], "session")) {
981 ret = bus_method_call_with_reply (
983 "org.freedesktop.login1",
984 "/org/freedesktop/login1",
985 "org.freedesktop.login1.Manager",
989 DBUS_TYPE_STRING, &args[i],
992 } else if (strstr(args[0], "user")) {
996 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
998 log_error("User %s unknown.", args[i]);
1003 ret = bus_method_call_with_reply (
1005 "org.freedesktop.login1",
1006 "/org/freedesktop/login1",
1007 "org.freedesktop.login1.Manager",
1011 DBUS_TYPE_UINT32, &u,
1015 ret = bus_method_call_with_reply (
1017 "org.freedesktop.login1",
1018 "/org/freedesktop/login1",
1019 "org.freedesktop.login1.Manager",
1023 DBUS_TYPE_STRING, &args[i],
1029 if (!dbus_message_get_args(reply, &error,
1030 DBUS_TYPE_OBJECT_PATH, &path,
1031 DBUS_TYPE_INVALID)) {
1032 log_error("Failed to parse reply: %s", bus_error_message(&error));
1037 r = show_one(args[0], bus, path, show_properties, &new_line);
1041 dbus_message_unref(reply);
1047 dbus_message_unref(reply);
1049 dbus_error_free(&error);
1054 static int activate(DBusConnection *bus, char **args, unsigned n) {
1060 for (i = 1; i < n; i++) {
1062 ret = bus_method_call_with_reply (
1064 "org.freedesktop.login1",
1065 "/org/freedesktop/login1",
1066 "org.freedesktop.login1.Manager",
1067 streq(args[0], "lock-session") ? "LockSession" :
1068 streq(args[0], "unlock-session") ? "UnlockSession" :
1069 streq(args[0], "terminate-session") ? "TerminateSession" :
1073 DBUS_TYPE_STRING, &args[i],
1083 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1090 arg_kill_who = "all";
1092 for (i = 1; i < n; i++) {
1093 ret = bus_method_call_with_reply (
1095 "org.freedesktop.login1",
1096 "/org/freedesktop/login1",
1097 "org.freedesktop.login1.Manager",
1101 DBUS_TYPE_STRING, &args[i],
1102 DBUS_TYPE_STRING, &arg_kill_who,
1103 DBUS_TYPE_INT32, &arg_signal,
1113 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1116 dbus_bool_t b, interactive = true;
1120 polkit_agent_open_if_enabled();
1122 b = streq(args[0], "enable-linger");
1124 for (i = 1; i < n; i++) {
1128 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1130 log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1135 ret = bus_method_call_with_reply (
1137 "org.freedesktop.login1",
1138 "/org/freedesktop/login1",
1139 "org.freedesktop.login1.Manager",
1143 DBUS_TYPE_UINT32, &u,
1144 DBUS_TYPE_BOOLEAN, &b,
1145 DBUS_TYPE_BOOLEAN, &interactive,
1155 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1161 for (i = 1; i < n; i++) {
1165 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1167 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1172 ret = bus_method_call_with_reply (
1174 "org.freedesktop.login1",
1175 "/org/freedesktop/login1",
1176 "org.freedesktop.login1.Manager",
1180 DBUS_TYPE_UINT32, &u,
1190 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1197 arg_kill_who = "all";
1199 for (i = 1; i < n; i++) {
1203 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1205 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1210 ret = bus_method_call_with_reply (
1212 "org.freedesktop.login1",
1213 "/org/freedesktop/login1",
1214 "org.freedesktop.login1.Manager",
1218 DBUS_TYPE_UINT32, &u,
1219 DBUS_TYPE_INT32, &arg_signal,
1229 static int attach(DBusConnection *bus, char **args, unsigned n) {
1232 dbus_bool_t interactive = true;
1236 polkit_agent_open_if_enabled();
1238 for (i = 2; i < n; i++) {
1239 ret = bus_method_call_with_reply (
1241 "org.freedesktop.login1",
1242 "/org/freedesktop/login1",
1243 "org.freedesktop.login1.Manager",
1247 DBUS_TYPE_STRING, &args[1],
1248 DBUS_TYPE_STRING, &args[i],
1249 DBUS_TYPE_BOOLEAN, &interactive,
1259 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1260 dbus_bool_t interactive = true;
1264 polkit_agent_open_if_enabled();
1266 return bus_method_call_with_reply (
1268 "org.freedesktop.login1",
1269 "/org/freedesktop/login1",
1270 "org.freedesktop.login1.Manager",
1274 DBUS_TYPE_BOOLEAN, &interactive,
1278 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1279 polkit_agent_open_if_enabled();
1281 return bus_method_call_with_reply (
1283 "org.freedesktop.login1",
1284 "/org/freedesktop/login1",
1285 "org.freedesktop.login1.Manager",
1292 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1298 for (i = 1; i < n; i++) {
1299 ret = bus_method_call_with_reply (
1301 "org.freedesktop.login1",
1302 "/org/freedesktop/login1",
1303 "org.freedesktop.login1.Manager",
1307 DBUS_TYPE_STRING, &args[i],
1317 static int help(void) {
1319 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1320 "Send control commands to or query the login manager.\n\n"
1321 " -h --help Show this help\n"
1322 " --version Show package version\n"
1323 " -p --property=NAME Show only properties by this name\n"
1324 " -a --all Show all properties, including empty ones\n"
1325 " --kill-who=WHO Who to send signal to\n"
1326 " -s --signal=SIGNAL Which signal to send\n"
1327 " --no-ask-password Don't prompt for password\n"
1328 " -H --host=[USER@]HOST Show information for remote host\n"
1329 " -P --privileged Acquire privileges before execution\n"
1330 " --no-pager Do not pipe output into a pager\n\n"
1332 " list-sessions List sessions\n"
1333 " session-status [ID...] Show session status\n"
1334 " show-session [ID...] Show properties of one or more sessions\n"
1335 " activate [ID] Activate a session\n"
1336 " lock-session [ID...] Screen lock one or more sessions\n"
1337 " unlock-session [ID...] Screen unlock one or more sessions\n"
1338 " lock-sessions Screen lock 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,
1368 static const struct option options[] = {
1369 { "help", no_argument, NULL, 'h' },
1370 { "version", no_argument, NULL, ARG_VERSION },
1371 { "property", required_argument, NULL, 'p' },
1372 { "all", no_argument, NULL, 'a' },
1373 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1374 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1375 { "signal", required_argument, NULL, 's' },
1376 { "host", required_argument, NULL, 'H' },
1377 { "privileged", no_argument, NULL, 'P' },
1378 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1379 { NULL, 0, NULL, 0 }
1387 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1396 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 setlocale(LC_ALL, "");
1572 log_parse_environment();
1575 r = parse_argv(argc, argv);
1579 retval = EXIT_SUCCESS;
1583 if (arg_transport == TRANSPORT_NORMAL)
1584 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1585 else if (arg_transport == TRANSPORT_POLKIT)
1586 bus_connect_system_polkit(&bus, &error);
1587 else if (arg_transport == TRANSPORT_SSH)
1588 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1590 assert_not_reached("Uh, invalid transport...");
1592 r = loginctl_main(bus, argc, argv, &error);
1593 retval = r < 0 ? EXIT_FAILURE : r;
1597 dbus_connection_flush(bus);
1598 dbus_connection_close(bus);
1599 dbus_connection_unref(bus);
1602 dbus_error_free(&error);
1605 strv_free(arg_property);