1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <dbus/dbus.h>
33 #include "dbus-common.h"
36 #include "cgroup-show.h"
37 #include "sysfs-show.h"
38 #include "spawn-polkit-agent.h"
40 static char **arg_property = NULL;
41 static bool arg_all = false;
42 static bool arg_no_pager = false;
43 static const char *arg_kill_who = NULL;
44 static int arg_signal = SIGTERM;
45 static enum transport {
49 } arg_transport = TRANSPORT_NORMAL;
50 static bool arg_ask_password = true;
51 static const char *arg_host = NULL;
53 static bool on_tty(void) {
56 /* Note that this is invoked relatively early, before we start
57 * the pager. That means the value we return reflects whether
58 * we originally were started on a tty, not if we currently
59 * are. But this is intended, since we want colour and so on
60 * when run in our own pager. */
62 if (_unlikely_(t < 0))
63 t = isatty(STDOUT_FILENO) > 0;
68 static void pager_open_if_enabled(void) {
70 /* Cache result before we open the pager */
79 static void polkit_agent_open_if_enabled(void) {
81 /* Open the polkit agent as a child process if necessary */
83 if (!arg_ask_password)
89 static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
90 DBusMessage *reply = NULL;
92 DBusMessageIter iter, sub, sub2;
95 pager_open_if_enabled();
97 r = bus_method_call_with_reply (
99 "org.freedesktop.login1",
100 "/org/freedesktop/login1",
101 "org.freedesktop.login1.Manager",
109 if (!dbus_message_iter_init(reply, &iter) ||
110 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
111 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
112 log_error("Failed to parse reply.");
117 dbus_message_iter_recurse(&iter, &sub);
120 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
122 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
123 const char *id, *user, *seat, *object;
126 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
127 log_error("Failed to parse reply.");
132 dbus_message_iter_recurse(&sub, &sub2);
134 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
135 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
136 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
137 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
138 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
139 log_error("Failed to parse reply.");
144 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
148 dbus_message_iter_next(&sub);
152 printf("\n%u sessions listed.\n", k);
158 dbus_message_unref(reply);
163 static int list_users(DBusConnection *bus, char **args, unsigned n) {
164 DBusMessage *reply = NULL;
166 DBusMessageIter iter, sub, sub2;
169 pager_open_if_enabled();
171 r = bus_method_call_with_reply (
173 "org.freedesktop.login1",
174 "/org/freedesktop/login1",
175 "org.freedesktop.login1.Manager",
183 if (!dbus_message_iter_init(reply, &iter) ||
184 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
185 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
186 log_error("Failed to parse reply.");
191 dbus_message_iter_recurse(&iter, &sub);
194 printf("%10s %-16s\n", "UID", "USER");
196 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
197 const char *user, *object;
200 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
201 log_error("Failed to parse reply.");
206 dbus_message_iter_recurse(&sub, &sub2);
208 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
209 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
210 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
211 log_error("Failed to parse reply.");
216 printf("%10u %-16s\n", (unsigned) uid, user);
220 dbus_message_iter_next(&sub);
224 printf("\n%u users listed.\n", k);
230 dbus_message_unref(reply);
235 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
236 DBusMessage *reply = NULL;
238 DBusMessageIter iter, sub, sub2;
241 pager_open_if_enabled();
243 r = bus_method_call_with_reply (
245 "org.freedesktop.login1",
246 "/org/freedesktop/login1",
247 "org.freedesktop.login1.Manager",
255 if (!dbus_message_iter_init(reply, &iter) ||
256 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
257 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
258 log_error("Failed to parse reply.");
263 dbus_message_iter_recurse(&iter, &sub);
266 printf("%-16s\n", "SEAT");
268 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
269 const char *seat, *object;
271 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
272 log_error("Failed to parse reply.");
277 dbus_message_iter_recurse(&sub, &sub2);
279 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
280 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
281 log_error("Failed to parse reply.");
286 printf("%-16s\n", seat);
290 dbus_message_iter_next(&sub);
294 printf("\n%u seats listed.\n", k);
300 dbus_message_unref(reply);
305 typedef struct SessionStatusInfo {
310 const char *default_control_group;
316 const char *remote_host;
317 const char *remote_user;
325 typedef struct UserStatusInfo {
329 const char *default_control_group;
335 typedef struct SeatStatusInfo {
337 const char *active_session;
341 static void print_session_status_info(SessionStatusInfo *i) {
342 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
343 char since2[FORMAT_TIMESTAMP_MAX], *s2;
346 printf("%s - ", strna(i->id));
349 printf("%s (%u)\n", i->name, (unsigned) i->uid);
351 printf("%u\n", (unsigned) i->uid);
353 s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
354 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
357 printf("\t Since: %s; %s\n", s2, s1);
359 printf("\t Since: %s\n", s2);
364 printf("\t Leader: %u", (unsigned) i->leader);
366 get_process_comm(i->leader, &t);
376 printf("\t Seat: %s", i->seat);
379 printf("; vc%i", i->vtnr);
385 printf("\t TTY: %s\n", i->tty);
387 printf("\t Display: %s\n", i->display);
389 if (i->remote_host && i->remote_user)
390 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
391 else if (i->remote_host)
392 printf("\t Remote: %s\n", i->remote_host);
393 else if (i->remote_user)
394 printf("\t Remote: user %s\n", i->remote_user);
396 printf("\t Remote: Yes\n");
399 printf("\t Service: %s", i->service);
402 printf("; type %s", i->type);
405 printf("; class %s", i->class);
408 } else if (i->type) {
409 printf("\t Type: %s\n", i->type);
412 printf("; class %s", i->class);
414 printf("\t Class: %s\n", i->class);
417 printf("\t State: %s\n", i->state);
419 if (i->default_control_group) {
422 printf("\t CGroup: %s\n", i->default_control_group);
424 if (arg_transport != TRANSPORT_SSH) {
431 show_cgroup_and_extra_by_spec(i->default_control_group, "\t\t ", c, false, arg_all, &i->leader, i->leader > 0 ? 1 : 0);
436 static void print_user_status_info(UserStatusInfo *i) {
437 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
438 char since2[FORMAT_TIMESTAMP_MAX], *s2;
442 printf("%s (%u)\n", i->name, (unsigned) i->uid);
444 printf("%u\n", (unsigned) i->uid);
446 s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
447 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
450 printf("\t Since: %s; %s\n", s2, s1);
452 printf("\t Since: %s\n", s2);
454 if (!isempty(i->state))
455 printf("\t State: %s\n", i->state);
457 if (!strv_isempty(i->sessions)) {
459 printf("\tSessions:");
461 STRV_FOREACH(l, i->sessions) {
462 if (streq_ptr(*l, i->display))
471 if (i->default_control_group) {
474 printf("\t CGroup: %s\n", i->default_control_group);
476 if (arg_transport != TRANSPORT_SSH) {
483 show_cgroup_by_path(i->default_control_group, "\t\t ", c, false, arg_all);
488 static void print_seat_status_info(SeatStatusInfo *i) {
491 printf("%s\n", strna(i->id));
493 if (!strv_isempty(i->sessions)) {
495 printf("\tSessions:");
497 STRV_FOREACH(l, i->sessions) {
498 if (streq_ptr(*l, i->active_session))
507 if (arg_transport != TRANSPORT_SSH) {
516 printf("\t Devices:\n");
518 show_sysfs(i->id, "\t\t ", c);
522 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
527 switch (dbus_message_iter_get_arg_type(iter)) {
529 case DBUS_TYPE_STRING: {
532 dbus_message_iter_get_basic(iter, &s);
535 if (streq(name, "Id"))
537 else if (streq(name, "Name"))
539 else if (streq(name, "DefaultControlGroup"))
540 i->default_control_group = s;
541 else if (streq(name, "TTY"))
543 else if (streq(name, "Display"))
545 else if (streq(name, "RemoteHost"))
547 else if (streq(name, "RemoteUser"))
549 else if (streq(name, "Service"))
551 else if (streq(name, "Type"))
553 else if (streq(name, "Class"))
555 else if (streq(name, "State"))
561 case DBUS_TYPE_UINT32: {
564 dbus_message_iter_get_basic(iter, &u);
566 if (streq(name, "VTNr"))
568 else if (streq(name, "Leader"))
569 i->leader = (pid_t) u;
574 case DBUS_TYPE_BOOLEAN: {
577 dbus_message_iter_get_basic(iter, &b);
579 if (streq(name, "Remote"))
585 case DBUS_TYPE_UINT64: {
588 dbus_message_iter_get_basic(iter, &u);
590 if (streq(name, "Timestamp"))
591 i->timestamp = (usec_t) u;
596 case DBUS_TYPE_STRUCT: {
599 dbus_message_iter_recurse(iter, &sub);
601 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
604 dbus_message_iter_get_basic(&sub, &u);
607 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
610 dbus_message_iter_get_basic(&sub, &s);
623 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
628 switch (dbus_message_iter_get_arg_type(iter)) {
630 case DBUS_TYPE_STRING: {
633 dbus_message_iter_get_basic(iter, &s);
636 if (streq(name, "Name"))
638 else if (streq(name, "DefaultControlGroup"))
639 i->default_control_group = s;
640 else if (streq(name, "State"))
646 case DBUS_TYPE_UINT32: {
649 dbus_message_iter_get_basic(iter, &u);
651 if (streq(name, "UID"))
657 case DBUS_TYPE_UINT64: {
660 dbus_message_iter_get_basic(iter, &u);
662 if (streq(name, "Timestamp"))
663 i->timestamp = (usec_t) u;
668 case DBUS_TYPE_STRUCT: {
671 dbus_message_iter_recurse(iter, &sub);
673 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
676 dbus_message_iter_get_basic(&sub, &s);
685 case DBUS_TYPE_ARRAY: {
687 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
688 DBusMessageIter sub, sub2;
690 dbus_message_iter_recurse(iter, &sub);
691 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
695 dbus_message_iter_recurse(&sub, &sub2);
697 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
698 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
701 l = strv_append(i->sessions, id);
705 strv_free(i->sessions);
709 dbus_message_iter_next(&sub);
720 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
725 switch (dbus_message_iter_get_arg_type(iter)) {
727 case DBUS_TYPE_STRING: {
730 dbus_message_iter_get_basic(iter, &s);
733 if (streq(name, "Id"))
739 case DBUS_TYPE_STRUCT: {
742 dbus_message_iter_recurse(iter, &sub);
744 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
747 dbus_message_iter_get_basic(&sub, &s);
750 i->active_session = s;
756 case DBUS_TYPE_ARRAY: {
758 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
759 DBusMessageIter sub, sub2;
761 dbus_message_iter_recurse(iter, &sub);
762 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
766 dbus_message_iter_recurse(&sub, &sub2);
768 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
769 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
772 l = strv_append(i->sessions, id);
776 strv_free(i->sessions);
780 dbus_message_iter_next(&sub);
791 static int print_property(const char *name, DBusMessageIter *iter) {
795 if (arg_property && !strv_find(arg_property, name))
798 switch (dbus_message_iter_get_arg_type(iter)) {
800 case DBUS_TYPE_STRUCT: {
803 dbus_message_iter_recurse(iter, &sub);
805 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
806 (streq(name, "Display") || streq(name, "ActiveSession"))) {
809 dbus_message_iter_get_basic(&sub, &s);
811 if (arg_all || !isempty(s))
812 printf("%s=%s\n", name, s);
818 case DBUS_TYPE_ARRAY:
820 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
821 DBusMessageIter sub, sub2;
824 dbus_message_iter_recurse(iter, &sub);
825 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
829 dbus_message_iter_recurse(&sub, &sub2);
831 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
832 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
836 printf("%s=%s", name, id);
841 dbus_message_iter_next(&sub);
844 if (!found && arg_all)
845 printf("%s=\n", name);
855 if (generic_print_property(name, iter, arg_all) > 0)
859 printf("%s=[unprintable]\n", name);
864 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
865 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
866 const char *interface = "";
868 DBusMessageIter iter, sub, sub2, sub3;
869 SessionStatusInfo session_info;
870 UserStatusInfo user_info;
871 SeatStatusInfo seat_info;
880 r = bus_method_call_with_reply(
882 "org.freedesktop.login1",
884 "org.freedesktop.DBus.Properties",
888 DBUS_TYPE_STRING, &interface,
893 if (!dbus_message_iter_init(reply, &iter) ||
894 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
895 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
896 log_error("Failed to parse reply.");
901 dbus_message_iter_recurse(&iter, &sub);
908 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
911 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
912 log_error("Failed to parse reply.");
917 dbus_message_iter_recurse(&sub, &sub2);
919 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
920 log_error("Failed to parse reply.");
925 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
926 log_error("Failed to parse reply.");
931 dbus_message_iter_recurse(&sub2, &sub3);
934 r = print_property(name, &sub3);
935 else if (strstr(verb, "session"))
936 r = status_property_session(name, &sub3, &session_info);
937 else if (strstr(verb, "user"))
938 r = status_property_user(name, &sub3, &user_info);
940 r = status_property_seat(name, &sub3, &seat_info);
943 log_error("Failed to parse reply.");
947 dbus_message_iter_next(&sub);
950 if (!show_properties) {
951 if (strstr(verb, "session"))
952 print_session_status_info(&session_info);
953 else if (strstr(verb, "user"))
954 print_user_status_info(&user_info);
956 print_seat_status_info(&seat_info);
962 strv_free(seat_info.sessions);
963 strv_free(user_info.sessions);
968 static int show(DBusConnection *bus, char **args, unsigned n) {
969 DBusMessage *reply = NULL;
973 bool show_properties, new_line = false;
978 dbus_error_init(&error);
980 show_properties = !strstr(args[0], "status");
982 pager_open_if_enabled();
984 if (show_properties && n <= 1) {
985 /* If not argument is specified inspect the manager
988 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
992 for (i = 1; i < n; i++) {
993 const char *path = NULL;
995 if (strstr(args[0], "session")) {
997 ret = bus_method_call_with_reply (
999 "org.freedesktop.login1",
1000 "/org/freedesktop/login1",
1001 "org.freedesktop.login1.Manager",
1005 DBUS_TYPE_STRING, &args[i],
1008 } else if (strstr(args[0], "user")) {
1012 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1014 log_error("User %s unknown.", args[i]);
1019 ret = bus_method_call_with_reply (
1021 "org.freedesktop.login1",
1022 "/org/freedesktop/login1",
1023 "org.freedesktop.login1.Manager",
1027 DBUS_TYPE_UINT32, &u,
1031 ret = bus_method_call_with_reply (
1033 "org.freedesktop.login1",
1034 "/org/freedesktop/login1",
1035 "org.freedesktop.login1.Manager",
1039 DBUS_TYPE_STRING, &args[i],
1045 if (!dbus_message_get_args(reply, &error,
1046 DBUS_TYPE_OBJECT_PATH, &path,
1047 DBUS_TYPE_INVALID)) {
1048 log_error("Failed to parse reply: %s", bus_error_message(&error));
1053 r = show_one(args[0], bus, path, show_properties, &new_line);
1057 dbus_message_unref(reply);
1063 dbus_message_unref(reply);
1065 dbus_error_free(&error);
1070 static int activate(DBusConnection *bus, char **args, unsigned n) {
1076 for (i = 1; i < n; i++) {
1078 ret = bus_method_call_with_reply (
1080 "org.freedesktop.login1",
1081 "/org/freedesktop/login1",
1082 "org.freedesktop.login1.Manager",
1083 streq(args[0], "lock-session") ? "LockSession" :
1084 streq(args[0], "unlock-session") ? "UnlockSession" :
1085 streq(args[0], "terminate-session") ? "TerminateSession" :
1089 DBUS_TYPE_STRING, &args[i],
1099 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1106 arg_kill_who = "all";
1108 for (i = 1; i < n; i++) {
1109 ret = bus_method_call_with_reply (
1111 "org.freedesktop.login1",
1112 "/org/freedesktop/login1",
1113 "org.freedesktop.login1.Manager",
1117 DBUS_TYPE_STRING, &args[i],
1118 DBUS_TYPE_STRING, &arg_kill_who,
1119 DBUS_TYPE_INT32, &arg_signal,
1129 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1132 dbus_bool_t b, interactive = true;
1136 polkit_agent_open_if_enabled();
1138 b = streq(args[0], "enable-linger");
1140 for (i = 1; i < n; i++) {
1144 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1146 log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1151 ret = bus_method_call_with_reply (
1153 "org.freedesktop.login1",
1154 "/org/freedesktop/login1",
1155 "org.freedesktop.login1.Manager",
1159 DBUS_TYPE_UINT32, &u,
1160 DBUS_TYPE_BOOLEAN, &b,
1161 DBUS_TYPE_BOOLEAN, &interactive,
1171 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1177 for (i = 1; i < n; i++) {
1181 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1183 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1188 ret = bus_method_call_with_reply (
1190 "org.freedesktop.login1",
1191 "/org/freedesktop/login1",
1192 "org.freedesktop.login1.Manager",
1196 DBUS_TYPE_UINT32, &u,
1206 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1213 arg_kill_who = "all";
1215 for (i = 1; i < n; i++) {
1219 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1221 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1226 ret = bus_method_call_with_reply (
1228 "org.freedesktop.login1",
1229 "/org/freedesktop/login1",
1230 "org.freedesktop.login1.Manager",
1234 DBUS_TYPE_UINT32, &u,
1235 DBUS_TYPE_INT32, &arg_signal,
1245 static int attach(DBusConnection *bus, char **args, unsigned n) {
1248 dbus_bool_t interactive = true;
1252 polkit_agent_open_if_enabled();
1254 for (i = 2; i < n; i++) {
1255 ret = bus_method_call_with_reply (
1257 "org.freedesktop.login1",
1258 "/org/freedesktop/login1",
1259 "org.freedesktop.login1.Manager",
1263 DBUS_TYPE_STRING, &args[1],
1264 DBUS_TYPE_STRING, &args[i],
1265 DBUS_TYPE_BOOLEAN, &interactive,
1275 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1276 dbus_bool_t interactive = true;
1280 polkit_agent_open_if_enabled();
1282 return bus_method_call_with_reply (
1284 "org.freedesktop.login1",
1285 "/org/freedesktop/login1",
1286 "org.freedesktop.login1.Manager",
1290 DBUS_TYPE_BOOLEAN, &interactive,
1294 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1295 polkit_agent_open_if_enabled();
1297 return bus_method_call_with_reply (
1299 "org.freedesktop.login1",
1300 "/org/freedesktop/login1",
1301 "org.freedesktop.login1.Manager",
1308 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1314 for (i = 1; i < n; i++) {
1315 ret = bus_method_call_with_reply (
1317 "org.freedesktop.login1",
1318 "/org/freedesktop/login1",
1319 "org.freedesktop.login1.Manager",
1323 DBUS_TYPE_STRING, &args[i],
1333 static int help(void) {
1335 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1336 "Send control commands to or query the login manager.\n\n"
1337 " -h --help Show this help\n"
1338 " --version Show package version\n"
1339 " -p --property=NAME Show only properties by this name\n"
1340 " -a --all Show all properties, including empty ones\n"
1341 " --kill-who=WHO Who to send signal to\n"
1342 " -s --signal=SIGNAL Which signal to send\n"
1343 " --no-ask-password Don't prompt for password\n"
1344 " -H --host=[USER@]HOST Show information for remote host\n"
1345 " -P --privileged Acquire privileges before execution\n"
1346 " --no-pager Do not pipe output into a pager\n\n"
1348 " list-sessions List sessions\n"
1349 " session-status [ID...] Show session status\n"
1350 " show-session [ID...] Show properties of one or more sessions\n"
1351 " activate [ID] Activate a session\n"
1352 " lock-session [ID...] Screen lock one or more sessions\n"
1353 " unlock-session [ID...] Screen unlock one or more sessions\n"
1354 " lock-sessions Screen lock all current sessions\n"
1355 " terminate-session [ID...] Terminate one or more sessions\n"
1356 " kill-session [ID...] Send signal to processes of a session\n"
1357 " list-users List users\n"
1358 " user-status [USER...] Show user status\n"
1359 " show-user [USER...] Show properties of one or more users\n"
1360 " enable-linger [USER...] Enable linger state of one or more users\n"
1361 " disable-linger [USER...] Disable linger state of one or more users\n"
1362 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1363 " kill-user [USER...] Send signal to processes of a user\n"
1364 " list-seats List seats\n"
1365 " seat-status [NAME...] Show seat status\n"
1366 " show-seat [NAME...] Show properties of one or more seats\n"
1367 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1368 " flush-devices Flush all device associations\n"
1369 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1370 program_invocation_short_name);
1375 static int parse_argv(int argc, char *argv[]) {
1378 ARG_VERSION = 0x100,
1384 static const struct option options[] = {
1385 { "help", no_argument, NULL, 'h' },
1386 { "version", no_argument, NULL, ARG_VERSION },
1387 { "property", required_argument, NULL, 'p' },
1388 { "all", no_argument, NULL, 'a' },
1389 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1390 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1391 { "signal", required_argument, NULL, 's' },
1392 { "host", required_argument, NULL, 'H' },
1393 { "privileged", no_argument, NULL, 'P' },
1394 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1395 { NULL, 0, NULL, 0 }
1403 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1412 puts(PACKAGE_STRING);
1414 puts(SYSTEMD_FEATURES);
1420 l = strv_append(arg_property, optarg);
1424 strv_free(arg_property);
1427 /* If the user asked for a particular
1428 * property, show it to him, even if it is
1439 arg_no_pager = true;
1442 case ARG_NO_ASK_PASSWORD:
1443 arg_ask_password = false;
1447 arg_kill_who = optarg;
1451 arg_signal = signal_from_string_try_harder(optarg);
1452 if (arg_signal < 0) {
1453 log_error("Failed to parse signal string %s.", optarg);
1459 arg_transport = TRANSPORT_POLKIT;
1463 arg_transport = TRANSPORT_SSH;
1471 log_error("Unknown option code %c", c);
1479 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1481 static const struct {
1489 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1491 { "list-sessions", LESS, 1, list_sessions },
1492 { "session-status", MORE, 2, show },
1493 { "show-session", MORE, 1, show },
1494 { "activate", EQUAL, 2, activate },
1495 { "lock-session", MORE, 2, activate },
1496 { "unlock-session", MORE, 2, activate },
1497 { "lock-sessions", EQUAL, 1, lock_sessions },
1498 { "terminate-session", MORE, 2, activate },
1499 { "kill-session", MORE, 2, kill_session },
1500 { "list-users", EQUAL, 1, list_users },
1501 { "user-status", MORE, 2, show },
1502 { "show-user", MORE, 1, show },
1503 { "enable-linger", MORE, 2, enable_linger },
1504 { "disable-linger", MORE, 2, enable_linger },
1505 { "terminate-user", MORE, 2, terminate_user },
1506 { "kill-user", MORE, 2, kill_user },
1507 { "list-seats", EQUAL, 1, list_seats },
1508 { "seat-status", MORE, 2, show },
1509 { "show-seat", MORE, 1, show },
1510 { "attach", MORE, 3, attach },
1511 { "flush-devices", EQUAL, 1, flush_devices },
1512 { "terminate-seat", MORE, 2, terminate_seat },
1522 left = argc - optind;
1525 /* Special rule: no arguments means "list-sessions" */
1528 if (streq(argv[optind], "help")) {
1533 for (i = 0; i < ELEMENTSOF(verbs); i++)
1534 if (streq(argv[optind], verbs[i].verb))
1537 if (i >= ELEMENTSOF(verbs)) {
1538 log_error("Unknown operation %s", argv[optind]);
1543 switch (verbs[i].argc_cmp) {
1546 if (left != verbs[i].argc) {
1547 log_error("Invalid number of arguments.");
1554 if (left < verbs[i].argc) {
1555 log_error("Too few arguments.");
1562 if (left > verbs[i].argc) {
1563 log_error("Too many arguments.");
1570 assert_not_reached("Unknown comparison operator.");
1574 log_error("Failed to get D-Bus connection: %s", error->message);
1578 return verbs[i].dispatch(bus, argv + optind, left);
1581 int main(int argc, char*argv[]) {
1582 int r, retval = EXIT_FAILURE;
1583 DBusConnection *bus = NULL;
1586 dbus_error_init(&error);
1588 log_parse_environment();
1591 r = parse_argv(argc, argv);
1595 retval = EXIT_SUCCESS;
1599 if (arg_transport == TRANSPORT_NORMAL)
1600 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1601 else if (arg_transport == TRANSPORT_POLKIT)
1602 bus_connect_system_polkit(&bus, &error);
1603 else if (arg_transport == TRANSPORT_SSH)
1604 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1606 assert_not_reached("Uh, invalid transport...");
1608 r = loginctl_main(bus, argc, argv, &error);
1609 retval = r < 0 ? EXIT_FAILURE : r;
1613 dbus_connection_flush(bus);
1614 dbus_connection_close(bus);
1615 dbus_connection_unref(bus);
1618 dbus_error_free(&error);
1621 strv_free(arg_property);