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 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.");
948 dbus_message_iter_next(&sub);
951 if (!show_properties) {
952 if (strstr(verb, "session"))
953 print_session_status_info(&session_info);
954 else if (strstr(verb, "user"))
955 print_user_status_info(&user_info);
957 print_seat_status_info(&seat_info);
960 strv_free(seat_info.sessions);
961 strv_free(user_info.sessions);
967 dbus_message_unref(reply);
972 static int show(DBusConnection *bus, char **args, unsigned n) {
973 DBusMessage *reply = NULL;
977 bool show_properties, new_line = false;
982 dbus_error_init(&error);
984 show_properties = !strstr(args[0], "status");
987 pager_open_if_enabled();
989 if (show_properties && n <= 1) {
990 /* If not argument is specified inspect the manager
993 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
997 for (i = 1; i < n; i++) {
998 const char *path = NULL;
1000 if (strstr(args[0], "session")) {
1002 ret = bus_method_call_with_reply (
1004 "org.freedesktop.login1",
1005 "/org/freedesktop/login1",
1006 "org.freedesktop.login1.Manager",
1010 DBUS_TYPE_STRING, &args[i],
1013 } else if (strstr(args[0], "user")) {
1017 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1019 log_error("User %s unknown.", args[i]);
1024 ret = bus_method_call_with_reply (
1026 "org.freedesktop.login1",
1027 "/org/freedesktop/login1",
1028 "org.freedesktop.login1.Manager",
1032 DBUS_TYPE_UINT32, &u,
1036 ret = bus_method_call_with_reply (
1038 "org.freedesktop.login1",
1039 "/org/freedesktop/login1",
1040 "org.freedesktop.login1.Manager",
1044 DBUS_TYPE_STRING, &args[i],
1050 if (!dbus_message_get_args(reply, &error,
1051 DBUS_TYPE_OBJECT_PATH, &path,
1052 DBUS_TYPE_INVALID)) {
1053 log_error("Failed to parse reply: %s", bus_error_message(&error));
1058 r = show_one(args[0], bus, path, show_properties, &new_line);
1062 dbus_message_unref(reply);
1068 dbus_message_unref(reply);
1070 dbus_error_free(&error);
1075 static int activate(DBusConnection *bus, char **args, unsigned n) {
1081 for (i = 1; i < n; i++) {
1083 ret = bus_method_call_with_reply (
1085 "org.freedesktop.login1",
1086 "/org/freedesktop/login1",
1087 "org.freedesktop.login1.Manager",
1088 streq(args[0], "lock-session") ? "LockSession" :
1089 streq(args[0], "unlock-session") ? "UnlockSession" :
1090 streq(args[0], "terminate-session") ? "TerminateSession" :
1094 DBUS_TYPE_STRING, &args[i],
1104 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1111 arg_kill_who = "all";
1113 for (i = 1; i < n; i++) {
1114 ret = bus_method_call_with_reply (
1116 "org.freedesktop.login1",
1117 "/org/freedesktop/login1",
1118 "org.freedesktop.login1.Manager",
1122 DBUS_TYPE_STRING, &args[i],
1123 DBUS_TYPE_STRING, &arg_kill_who,
1124 DBUS_TYPE_INT32, &arg_signal,
1134 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1137 dbus_bool_t b, interactive = true;
1141 polkit_agent_open_if_enabled();
1143 b = streq(args[0], "enable-linger");
1145 for (i = 1; i < n; i++) {
1149 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1151 log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1156 ret = bus_method_call_with_reply (
1158 "org.freedesktop.login1",
1159 "/org/freedesktop/login1",
1160 "org.freedesktop.login1.Manager",
1164 DBUS_TYPE_UINT32, &u,
1165 DBUS_TYPE_BOOLEAN, &b,
1166 DBUS_TYPE_BOOLEAN, &interactive,
1176 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1182 for (i = 1; i < n; i++) {
1186 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1188 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1193 ret = bus_method_call_with_reply (
1195 "org.freedesktop.login1",
1196 "/org/freedesktop/login1",
1197 "org.freedesktop.login1.Manager",
1201 DBUS_TYPE_UINT32, &u,
1211 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1218 arg_kill_who = "all";
1220 for (i = 1; i < n; i++) {
1224 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1226 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1231 ret = bus_method_call_with_reply (
1233 "org.freedesktop.login1",
1234 "/org/freedesktop/login1",
1235 "org.freedesktop.login1.Manager",
1239 DBUS_TYPE_UINT32, &u,
1240 DBUS_TYPE_INT32, &arg_signal,
1250 static int attach(DBusConnection *bus, char **args, unsigned n) {
1253 dbus_bool_t interactive = true;
1257 polkit_agent_open_if_enabled();
1259 for (i = 2; i < n; i++) {
1260 ret = bus_method_call_with_reply (
1262 "org.freedesktop.login1",
1263 "/org/freedesktop/login1",
1264 "org.freedesktop.login1.Manager",
1268 DBUS_TYPE_STRING, &args[1],
1269 DBUS_TYPE_STRING, &args[i],
1270 DBUS_TYPE_BOOLEAN, &interactive,
1280 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1281 dbus_bool_t interactive = true;
1285 polkit_agent_open_if_enabled();
1287 return bus_method_call_with_reply (
1289 "org.freedesktop.login1",
1290 "/org/freedesktop/login1",
1291 "org.freedesktop.login1.Manager",
1295 DBUS_TYPE_BOOLEAN, &interactive,
1299 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1300 polkit_agent_open_if_enabled();
1302 return bus_method_call_with_reply (
1304 "org.freedesktop.login1",
1305 "/org/freedesktop/login1",
1306 "org.freedesktop.login1.Manager",
1313 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1319 for (i = 1; i < n; i++) {
1320 ret = bus_method_call_with_reply (
1322 "org.freedesktop.login1",
1323 "/org/freedesktop/login1",
1324 "org.freedesktop.login1.Manager",
1328 DBUS_TYPE_STRING, &args[i],
1338 static int help(void) {
1340 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1341 "Send control commands to or query the login manager.\n\n"
1342 " -h --help Show this help\n"
1343 " --version Show package version\n"
1344 " -p --property=NAME Show only properties by this name\n"
1345 " -a --all Show all properties, including empty ones\n"
1346 " --kill-who=WHO Who to send signal to\n"
1347 " -s --signal=SIGNAL Which signal to send\n"
1348 " -H --host=[USER@]HOST\n"
1349 " Show information for remote host\n"
1350 " -P --privileged Acquire privileges before execution\n"
1351 " --no-pager Do not pipe output into a pager\n\n"
1353 " list-sessions List sessions\n"
1354 " session-status [ID...] Show session status\n"
1355 " show-session [ID...] Show properties of one or more sessions\n"
1356 " activate [ID] Activate a session\n"
1357 " lock-session [ID...] Screen lock one or more sessions\n"
1358 " unlock-session [ID...] Screen unlock one or more sessions\n"
1359 " lock-sessions Screen lock all current sessions\n"
1360 " terminate-session [ID...] Terminate one or more sessions\n"
1361 " kill-session [ID...] Send signal to processes of a session\n"
1362 " list-users List users\n"
1363 " user-status [USER...] Show user status\n"
1364 " show-user [USER...] Show properties of one or more users\n"
1365 " enable-linger [USER...] Enable linger state of one or more users\n"
1366 " disable-linger [USER...] Disable linger state of one or more users\n"
1367 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1368 " kill-user [USER...] Send signal to processes of a user\n"
1369 " list-seats List seats\n"
1370 " seat-status [NAME...] Show seat status\n"
1371 " show-seat [NAME...] Show properties of one or more seats\n"
1372 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1373 " flush-devices Flush all device associations\n"
1374 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1375 program_invocation_short_name);
1380 static int parse_argv(int argc, char *argv[]) {
1383 ARG_VERSION = 0x100,
1389 static const struct option options[] = {
1390 { "help", no_argument, NULL, 'h' },
1391 { "version", no_argument, NULL, ARG_VERSION },
1392 { "property", required_argument, NULL, 'p' },
1393 { "all", no_argument, NULL, 'a' },
1394 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1395 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1396 { "signal", required_argument, NULL, 's' },
1397 { "host", required_argument, NULL, 'H' },
1398 { "privileged",no_argument, NULL, 'P' },
1399 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1400 { NULL, 0, NULL, 0 }
1408 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1417 puts(PACKAGE_STRING);
1419 puts(SYSTEMD_FEATURES);
1425 l = strv_append(arg_property, optarg);
1429 strv_free(arg_property);
1432 /* If the user asked for a particular
1433 * property, show it to him, even if it is
1444 arg_no_pager = true;
1447 case ARG_NO_ASK_PASSWORD:
1448 arg_ask_password = false;
1452 arg_kill_who = optarg;
1456 arg_signal = signal_from_string_try_harder(optarg);
1457 if (arg_signal < 0) {
1458 log_error("Failed to parse signal string %s.", optarg);
1464 arg_transport = TRANSPORT_POLKIT;
1468 arg_transport = TRANSPORT_SSH;
1476 log_error("Unknown option code %c", c);
1484 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1486 static const struct {
1494 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1496 { "list-sessions", LESS, 1, list_sessions },
1497 { "session-status", MORE, 2, show },
1498 { "show-session", MORE, 1, show },
1499 { "activate", EQUAL, 2, activate },
1500 { "lock-session", MORE, 2, activate },
1501 { "unlock-session", MORE, 2, activate },
1502 { "lock-sessions", EQUAL, 1, lock_sessions },
1503 { "terminate-session", MORE, 2, activate },
1504 { "kill-session", MORE, 2, kill_session },
1505 { "list-users", EQUAL, 1, list_users },
1506 { "user-status", MORE, 2, show },
1507 { "show-user", MORE, 1, show },
1508 { "enable-linger", MORE, 2, enable_linger },
1509 { "disable-linger", MORE, 2, enable_linger },
1510 { "terminate-user", MORE, 2, terminate_user },
1511 { "kill-user", MORE, 2, kill_user },
1512 { "list-seats", EQUAL, 1, list_seats },
1513 { "seat-status", MORE, 2, show },
1514 { "show-seat", MORE, 1, show },
1515 { "attach", MORE, 3, attach },
1516 { "flush-devices", EQUAL, 1, flush_devices },
1517 { "terminate-seat", MORE, 2, terminate_seat },
1527 left = argc - optind;
1530 /* Special rule: no arguments means "list-sessions" */
1533 if (streq(argv[optind], "help")) {
1538 for (i = 0; i < ELEMENTSOF(verbs); i++)
1539 if (streq(argv[optind], verbs[i].verb))
1542 if (i >= ELEMENTSOF(verbs)) {
1543 log_error("Unknown operation %s", argv[optind]);
1548 switch (verbs[i].argc_cmp) {
1551 if (left != verbs[i].argc) {
1552 log_error("Invalid number of arguments.");
1559 if (left < verbs[i].argc) {
1560 log_error("Too few arguments.");
1567 if (left > verbs[i].argc) {
1568 log_error("Too many arguments.");
1575 assert_not_reached("Unknown comparison operator.");
1579 log_error("Failed to get D-Bus connection: %s", error->message);
1583 return verbs[i].dispatch(bus, argv + optind, left);
1586 int main(int argc, char*argv[]) {
1587 int r, retval = EXIT_FAILURE;
1588 DBusConnection *bus = NULL;
1591 dbus_error_init(&error);
1593 log_parse_environment();
1596 r = parse_argv(argc, argv);
1600 retval = EXIT_SUCCESS;
1604 if (arg_transport == TRANSPORT_NORMAL)
1605 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1606 else if (arg_transport == TRANSPORT_POLKIT)
1607 bus_connect_system_polkit(&bus, &error);
1608 else if (arg_transport == TRANSPORT_SSH)
1609 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1611 assert_not_reached("Uh, invalid transport...");
1613 r = loginctl_main(bus, argc, argv, &error);
1614 retval = r < 0 ? EXIT_FAILURE : r;
1618 dbus_connection_flush(bus);
1619 dbus_connection_close(bus);
1620 dbus_connection_unref(bus);
1623 dbus_error_free(&error);
1626 strv_free(arg_property);