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 char *arg_host = NULL;
54 static char *arg_user = NULL;
56 static void pager_open_if_enabled(void) {
58 /* Cache result before we open the pager */
65 static void polkit_agent_open_if_enabled(void) {
67 /* Open the polkit agent as a child process if necessary */
69 if (!arg_ask_password)
75 static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
76 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
78 DBusMessageIter iter, sub, sub2;
81 pager_open_if_enabled();
83 r = bus_method_call_with_reply (
85 "org.freedesktop.login1",
86 "/org/freedesktop/login1",
87 "org.freedesktop.login1.Manager",
95 if (!dbus_message_iter_init(reply, &iter) ||
96 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
97 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
98 log_error("Failed to parse reply.");
102 dbus_message_iter_recurse(&iter, &sub);
105 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
107 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
108 const char *id, *user, *seat, *object;
111 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
112 log_error("Failed to parse reply.");
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.");
127 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
131 dbus_message_iter_next(&sub);
135 printf("\n%u sessions listed.\n", k);
140 static int list_users(DBusConnection *bus, char **args, unsigned n) {
141 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
143 DBusMessageIter iter, sub, sub2;
146 pager_open_if_enabled();
148 r = bus_method_call_with_reply (
150 "org.freedesktop.login1",
151 "/org/freedesktop/login1",
152 "org.freedesktop.login1.Manager",
160 if (!dbus_message_iter_init(reply, &iter) ||
161 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
162 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
163 log_error("Failed to parse reply.");
167 dbus_message_iter_recurse(&iter, &sub);
170 printf("%10s %-16s\n", "UID", "USER");
172 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
173 const char *user, *object;
176 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
177 log_error("Failed to parse reply.");
181 dbus_message_iter_recurse(&sub, &sub2);
183 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
184 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
185 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
186 log_error("Failed to parse reply.");
190 printf("%10u %-16s\n", (unsigned) uid, user);
194 dbus_message_iter_next(&sub);
198 printf("\n%u users listed.\n", k);
203 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
204 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
206 DBusMessageIter iter, sub, sub2;
209 pager_open_if_enabled();
211 r = bus_method_call_with_reply (
213 "org.freedesktop.login1",
214 "/org/freedesktop/login1",
215 "org.freedesktop.login1.Manager",
223 if (!dbus_message_iter_init(reply, &iter) ||
224 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
225 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
226 log_error("Failed to parse reply.");
230 dbus_message_iter_recurse(&iter, &sub);
233 printf("%-16s\n", "SEAT");
235 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
236 const char *seat, *object;
238 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
239 log_error("Failed to parse reply.");
243 dbus_message_iter_recurse(&sub, &sub2);
245 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
246 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
247 log_error("Failed to parse reply.");
251 printf("%-16s\n", seat);
255 dbus_message_iter_next(&sub);
259 printf("\n%u seats listed.\n", k);
264 typedef struct SessionStatusInfo {
269 const char *default_control_group;
275 const char *remote_host;
276 const char *remote_user;
284 typedef struct UserStatusInfo {
288 const char *default_control_group;
294 typedef struct SeatStatusInfo {
296 const char *active_session;
300 static void print_session_status_info(SessionStatusInfo *i) {
301 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
302 char since2[FORMAT_TIMESTAMP_MAX], *s2;
305 printf("%s - ", strna(i->id));
308 printf("%s (%u)\n", i->name, (unsigned) i->uid);
310 printf("%u\n", (unsigned) i->uid);
312 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
313 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
316 printf("\t Since: %s; %s\n", s2, s1);
318 printf("\t Since: %s\n", s2);
323 printf("\t Leader: %u", (unsigned) i->leader);
325 get_process_comm(i->leader, &t);
335 printf("\t Seat: %s", i->seat);
338 printf("; vc%i", i->vtnr);
344 printf("\t TTY: %s\n", i->tty);
346 printf("\t Display: %s\n", i->display);
348 if (i->remote_host && i->remote_user)
349 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
350 else if (i->remote_host)
351 printf("\t Remote: %s\n", i->remote_host);
352 else if (i->remote_user)
353 printf("\t Remote: user %s\n", i->remote_user);
355 printf("\t Remote: Yes\n");
358 printf("\t Service: %s", i->service);
361 printf("; type %s", i->type);
364 printf("; class %s", i->class);
367 } else if (i->type) {
368 printf("\t Type: %s\n", i->type);
371 printf("; class %s", i->class);
373 printf("\t Class: %s\n", i->class);
376 printf("\t State: %s\n", i->state);
378 if (i->default_control_group) {
381 arg_all * OUTPUT_SHOW_ALL |
382 arg_full * OUTPUT_FULL_WIDTH;
384 printf("\t CGroup: %s\n", i->default_control_group);
386 if (arg_transport != TRANSPORT_SSH) {
393 show_cgroup_and_extra_by_spec(i->default_control_group,
394 "\t\t ", c, false, &i->leader,
395 i->leader > 0 ? 1 : 0,
401 static void print_user_status_info(UserStatusInfo *i) {
402 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
403 char since2[FORMAT_TIMESTAMP_MAX], *s2;
407 printf("%s (%u)\n", i->name, (unsigned) i->uid);
409 printf("%u\n", (unsigned) i->uid);
411 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
412 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
415 printf("\t Since: %s; %s\n", s2, s1);
417 printf("\t Since: %s\n", s2);
419 if (!isempty(i->state))
420 printf("\t State: %s\n", i->state);
422 if (!strv_isempty(i->sessions)) {
424 printf("\tSessions:");
426 STRV_FOREACH(l, i->sessions) {
427 if (streq_ptr(*l, i->display))
436 if (i->default_control_group) {
439 arg_all * OUTPUT_SHOW_ALL |
440 arg_full * OUTPUT_FULL_WIDTH;
442 printf("\t CGroup: %s\n", i->default_control_group);
444 if (arg_transport != TRANSPORT_SSH) {
451 show_cgroup_by_path(i->default_control_group, "\t\t ",
452 c, false, output_flags);
457 static void print_seat_status_info(SeatStatusInfo *i) {
460 printf("%s\n", strna(i->id));
462 if (!strv_isempty(i->sessions)) {
464 printf("\tSessions:");
466 STRV_FOREACH(l, i->sessions) {
467 if (streq_ptr(*l, i->active_session))
476 if (arg_transport != TRANSPORT_SSH) {
485 printf("\t Devices:\n");
487 show_sysfs(i->id, "\t\t ", c);
491 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
496 switch (dbus_message_iter_get_arg_type(iter)) {
498 case DBUS_TYPE_STRING: {
501 dbus_message_iter_get_basic(iter, &s);
504 if (streq(name, "Id"))
506 else if (streq(name, "Name"))
508 else if (streq(name, "DefaultControlGroup"))
509 i->default_control_group = s;
510 else if (streq(name, "TTY"))
512 else if (streq(name, "Display"))
514 else if (streq(name, "RemoteHost"))
516 else if (streq(name, "RemoteUser"))
518 else if (streq(name, "Service"))
520 else if (streq(name, "Type"))
522 else if (streq(name, "Class"))
524 else if (streq(name, "State"))
530 case DBUS_TYPE_UINT32: {
533 dbus_message_iter_get_basic(iter, &u);
535 if (streq(name, "VTNr"))
537 else if (streq(name, "Leader"))
538 i->leader = (pid_t) u;
543 case DBUS_TYPE_BOOLEAN: {
546 dbus_message_iter_get_basic(iter, &b);
548 if (streq(name, "Remote"))
554 case DBUS_TYPE_UINT64: {
557 dbus_message_iter_get_basic(iter, &u);
559 if (streq(name, "Timestamp"))
560 i->timestamp = (usec_t) u;
565 case DBUS_TYPE_STRUCT: {
568 dbus_message_iter_recurse(iter, &sub);
570 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
573 dbus_message_iter_get_basic(&sub, &u);
576 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
579 dbus_message_iter_get_basic(&sub, &s);
592 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
597 switch (dbus_message_iter_get_arg_type(iter)) {
599 case DBUS_TYPE_STRING: {
602 dbus_message_iter_get_basic(iter, &s);
605 if (streq(name, "Name"))
607 else if (streq(name, "DefaultControlGroup"))
608 i->default_control_group = s;
609 else if (streq(name, "State"))
615 case DBUS_TYPE_UINT32: {
618 dbus_message_iter_get_basic(iter, &u);
620 if (streq(name, "UID"))
626 case DBUS_TYPE_UINT64: {
629 dbus_message_iter_get_basic(iter, &u);
631 if (streq(name, "Timestamp"))
632 i->timestamp = (usec_t) u;
637 case DBUS_TYPE_STRUCT: {
640 dbus_message_iter_recurse(iter, &sub);
642 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
645 dbus_message_iter_get_basic(&sub, &s);
654 case DBUS_TYPE_ARRAY: {
656 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
657 DBusMessageIter sub, sub2;
659 dbus_message_iter_recurse(iter, &sub);
660 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
664 dbus_message_iter_recurse(&sub, &sub2);
666 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
667 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
670 l = strv_append(i->sessions, id);
674 strv_free(i->sessions);
678 dbus_message_iter_next(&sub);
689 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
694 switch (dbus_message_iter_get_arg_type(iter)) {
696 case DBUS_TYPE_STRING: {
699 dbus_message_iter_get_basic(iter, &s);
702 if (streq(name, "Id"))
708 case DBUS_TYPE_STRUCT: {
711 dbus_message_iter_recurse(iter, &sub);
713 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
716 dbus_message_iter_get_basic(&sub, &s);
719 i->active_session = s;
725 case DBUS_TYPE_ARRAY: {
727 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
728 DBusMessageIter sub, sub2;
730 dbus_message_iter_recurse(iter, &sub);
731 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
735 dbus_message_iter_recurse(&sub, &sub2);
737 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
738 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
741 l = strv_append(i->sessions, id);
745 strv_free(i->sessions);
749 dbus_message_iter_next(&sub);
760 static int print_property(const char *name, DBusMessageIter *iter) {
764 if (arg_property && !strv_find(arg_property, name))
767 switch (dbus_message_iter_get_arg_type(iter)) {
769 case DBUS_TYPE_STRUCT: {
772 dbus_message_iter_recurse(iter, &sub);
774 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
775 (streq(name, "Display") || streq(name, "ActiveSession"))) {
778 dbus_message_iter_get_basic(&sub, &s);
780 if (arg_all || !isempty(s))
781 printf("%s=%s\n", name, s);
787 case DBUS_TYPE_ARRAY:
789 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
790 DBusMessageIter sub, sub2;
793 dbus_message_iter_recurse(iter, &sub);
794 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
798 dbus_message_iter_recurse(&sub, &sub2);
800 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
801 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
805 printf("%s=%s", name, id);
810 dbus_message_iter_next(&sub);
813 if (!found && arg_all)
814 printf("%s=\n", name);
824 if (generic_print_property(name, iter, arg_all) > 0)
828 printf("%s=[unprintable]\n", name);
833 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
834 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
835 const char *interface = "";
837 DBusMessageIter iter, sub, sub2, sub3;
838 SessionStatusInfo session_info = {};
839 UserStatusInfo user_info = {};
840 SeatStatusInfo seat_info = {};
845 r = bus_method_call_with_reply(
847 "org.freedesktop.login1",
849 "org.freedesktop.DBus.Properties",
853 DBUS_TYPE_STRING, &interface,
858 if (!dbus_message_iter_init(reply, &iter) ||
859 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
860 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
861 log_error("Failed to parse reply.");
866 dbus_message_iter_recurse(&iter, &sub);
873 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
876 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
877 log_error("Failed to parse reply.");
882 dbus_message_iter_recurse(&sub, &sub2);
884 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
885 log_error("Failed to parse reply.");
890 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
891 log_error("Failed to parse reply.");
896 dbus_message_iter_recurse(&sub2, &sub3);
899 r = print_property(name, &sub3);
900 else if (strstr(verb, "session"))
901 r = status_property_session(name, &sub3, &session_info);
902 else if (strstr(verb, "user"))
903 r = status_property_user(name, &sub3, &user_info);
905 r = status_property_seat(name, &sub3, &seat_info);
908 log_error("Failed to parse reply.");
912 dbus_message_iter_next(&sub);
915 if (!show_properties) {
916 if (strstr(verb, "session"))
917 print_session_status_info(&session_info);
918 else if (strstr(verb, "user"))
919 print_user_status_info(&user_info);
921 print_seat_status_info(&seat_info);
927 strv_free(seat_info.sessions);
928 strv_free(user_info.sessions);
933 static int show(DBusConnection *bus, char **args, unsigned n) {
934 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
938 bool show_properties, new_line = false;
943 dbus_error_init(&error);
945 show_properties = !strstr(args[0], "status");
947 pager_open_if_enabled();
949 if (show_properties && n <= 1) {
950 /* If not argument is specified inspect the manager
953 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
957 for (i = 1; i < n; i++) {
958 const char *path = NULL;
960 if (strstr(args[0], "session")) {
962 ret = bus_method_call_with_reply (
964 "org.freedesktop.login1",
965 "/org/freedesktop/login1",
966 "org.freedesktop.login1.Manager",
970 DBUS_TYPE_STRING, &args[i],
973 } else if (strstr(args[0], "user")) {
977 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
979 log_error("User %s unknown.", args[i]);
984 ret = bus_method_call_with_reply (
986 "org.freedesktop.login1",
987 "/org/freedesktop/login1",
988 "org.freedesktop.login1.Manager",
992 DBUS_TYPE_UINT32, &u,
996 ret = bus_method_call_with_reply (
998 "org.freedesktop.login1",
999 "/org/freedesktop/login1",
1000 "org.freedesktop.login1.Manager",
1004 DBUS_TYPE_STRING, &args[i],
1010 if (!dbus_message_get_args(reply, &error,
1011 DBUS_TYPE_OBJECT_PATH, &path,
1012 DBUS_TYPE_INVALID)) {
1013 log_error("Failed to parse reply: %s", bus_error_message(&error));
1018 r = show_one(args[0], bus, path, show_properties, &new_line);
1024 dbus_error_free(&error);
1029 static int activate(DBusConnection *bus, char **args, unsigned n) {
1035 for (i = 1; i < n; i++) {
1037 ret = bus_method_call_with_reply (
1039 "org.freedesktop.login1",
1040 "/org/freedesktop/login1",
1041 "org.freedesktop.login1.Manager",
1042 streq(args[0], "lock-session") ? "LockSession" :
1043 streq(args[0], "unlock-session") ? "UnlockSession" :
1044 streq(args[0], "terminate-session") ? "TerminateSession" :
1048 DBUS_TYPE_STRING, &args[i],
1058 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1064 arg_kill_who = "all";
1066 for (i = 1; i < n; i++) {
1069 r = bus_method_call_with_reply (
1071 "org.freedesktop.login1",
1072 "/org/freedesktop/login1",
1073 "org.freedesktop.login1.Manager",
1077 DBUS_TYPE_STRING, &args[i],
1078 DBUS_TYPE_STRING, &arg_kill_who,
1079 DBUS_TYPE_INT32, &arg_signal,
1088 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1090 dbus_bool_t b, interactive = true;
1094 polkit_agent_open_if_enabled();
1096 b = streq(args[0], "enable-linger");
1098 for (i = 1; i < n; i++) {
1103 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1105 log_error("Failed to resolve user %s: %s", args[i], strerror(-r));
1110 r = bus_method_call_with_reply (
1112 "org.freedesktop.login1",
1113 "/org/freedesktop/login1",
1114 "org.freedesktop.login1.Manager",
1118 DBUS_TYPE_UINT32, &u,
1119 DBUS_TYPE_BOOLEAN, &b,
1120 DBUS_TYPE_BOOLEAN, &interactive,
1129 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1134 for (i = 1; i < n; i++) {
1139 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1141 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1146 r = bus_method_call_with_reply (
1148 "org.freedesktop.login1",
1149 "/org/freedesktop/login1",
1150 "org.freedesktop.login1.Manager",
1154 DBUS_TYPE_UINT32, &u,
1163 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1169 arg_kill_who = "all";
1171 for (i = 1; i < n; i++) {
1176 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1178 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1183 r = bus_method_call_with_reply (
1185 "org.freedesktop.login1",
1186 "/org/freedesktop/login1",
1187 "org.freedesktop.login1.Manager",
1191 DBUS_TYPE_UINT32, &u,
1192 DBUS_TYPE_INT32, &arg_signal,
1201 static int attach(DBusConnection *bus, char **args, unsigned n) {
1203 dbus_bool_t interactive = true;
1207 polkit_agent_open_if_enabled();
1209 for (i = 2; i < n; i++) {
1212 r = bus_method_call_with_reply (
1214 "org.freedesktop.login1",
1215 "/org/freedesktop/login1",
1216 "org.freedesktop.login1.Manager",
1220 DBUS_TYPE_STRING, &args[1],
1221 DBUS_TYPE_STRING, &args[i],
1222 DBUS_TYPE_BOOLEAN, &interactive,
1231 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1232 dbus_bool_t interactive = true;
1236 polkit_agent_open_if_enabled();
1238 return bus_method_call_with_reply (
1240 "org.freedesktop.login1",
1241 "/org/freedesktop/login1",
1242 "org.freedesktop.login1.Manager",
1246 DBUS_TYPE_BOOLEAN, &interactive,
1250 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1253 polkit_agent_open_if_enabled();
1255 return bus_method_call_with_reply (
1257 "org.freedesktop.login1",
1258 "/org/freedesktop/login1",
1259 "org.freedesktop.login1.Manager",
1260 streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1266 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1271 for (i = 1; i < n; i++) {
1274 r = bus_method_call_with_reply (
1276 "org.freedesktop.login1",
1277 "/org/freedesktop/login1",
1278 "org.freedesktop.login1.Manager",
1282 DBUS_TYPE_STRING, &args[i],
1291 static int help(void) {
1293 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1294 "Send control commands to or query the login manager.\n\n"
1295 " -h --help Show this help\n"
1296 " --version Show package version\n"
1297 " -p --property=NAME Show only properties by this name\n"
1298 " -a --all Show all properties, including empty ones\n"
1299 " --kill-who=WHO Who to send signal to\n"
1300 " --full Do not ellipsize output\n"
1301 " -s --signal=SIGNAL Which signal to send\n"
1302 " --no-ask-password Don't prompt for password\n"
1303 " -H --host=[USER@]HOST Show information for remote host\n"
1304 " -P --privileged Acquire privileges before execution\n"
1305 " --no-pager Do not pipe output into a pager\n\n"
1307 " list-sessions List sessions\n"
1308 " session-status [ID...] Show session status\n"
1309 " show-session [ID...] Show properties of one or more sessions\n"
1310 " activate [ID] Activate a session\n"
1311 " lock-session [ID...] Screen lock one or more sessions\n"
1312 " unlock-session [ID...] Screen unlock one or more sessions\n"
1313 " lock-sessions Screen lock all current sessions\n"
1314 " unlock-sessions Screen unlock all current sessions\n"
1315 " terminate-session [ID...] Terminate one or more sessions\n"
1316 " kill-session [ID...] Send signal to processes of a session\n"
1317 " list-users List users\n"
1318 " user-status [USER...] Show user status\n"
1319 " show-user [USER...] Show properties of one or more users\n"
1320 " enable-linger [USER...] Enable linger state of one or more users\n"
1321 " disable-linger [USER...] Disable linger state of one or more users\n"
1322 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1323 " kill-user [USER...] Send signal to processes of a user\n"
1324 " list-seats List seats\n"
1325 " seat-status [NAME...] Show seat status\n"
1326 " show-seat [NAME...] Show properties of one or more seats\n"
1327 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1328 " flush-devices Flush all device associations\n"
1329 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1330 program_invocation_short_name);
1335 static int parse_argv(int argc, char *argv[]) {
1338 ARG_VERSION = 0x100,
1341 ARG_NO_ASK_PASSWORD,
1345 static const struct option options[] = {
1346 { "help", no_argument, NULL, 'h' },
1347 { "version", no_argument, NULL, ARG_VERSION },
1348 { "property", required_argument, NULL, 'p' },
1349 { "all", no_argument, NULL, 'a' },
1350 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1351 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1352 { "signal", required_argument, NULL, 's' },
1353 { "host", required_argument, NULL, 'H' },
1354 { "privileged", no_argument, NULL, 'P' },
1355 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1356 { "full", no_argument, NULL, ARG_FULL },
1357 { NULL, 0, NULL, 0 }
1365 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1374 puts(PACKAGE_STRING);
1375 puts(SYSTEMD_FEATURES);
1381 l = strv_append(arg_property, optarg);
1385 strv_free(arg_property);
1388 /* If the user asked for a particular
1389 * property, show it to him, even if it is
1400 arg_no_pager = true;
1403 case ARG_NO_ASK_PASSWORD:
1404 arg_ask_password = false;
1408 arg_kill_who = optarg;
1412 arg_signal = signal_from_string_try_harder(optarg);
1413 if (arg_signal < 0) {
1414 log_error("Failed to parse signal string %s.", optarg);
1420 arg_transport = TRANSPORT_POLKIT;
1424 arg_transport = TRANSPORT_SSH;
1425 parse_user_at_host(optarg, &arg_user, &arg_host);
1436 log_error("Unknown option code %c", c);
1444 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1446 static const struct {
1454 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1456 { "list-sessions", LESS, 1, list_sessions },
1457 { "session-status", MORE, 2, show },
1458 { "show-session", MORE, 1, show },
1459 { "activate", EQUAL, 2, activate },
1460 { "lock-session", MORE, 2, activate },
1461 { "unlock-session", MORE, 2, activate },
1462 { "lock-sessions", EQUAL, 1, lock_sessions },
1463 { "unlock-sessions", EQUAL, 1, lock_sessions },
1464 { "terminate-session", MORE, 2, activate },
1465 { "kill-session", MORE, 2, kill_session },
1466 { "list-users", EQUAL, 1, list_users },
1467 { "user-status", MORE, 2, show },
1468 { "show-user", MORE, 1, show },
1469 { "enable-linger", MORE, 2, enable_linger },
1470 { "disable-linger", MORE, 2, enable_linger },
1471 { "terminate-user", MORE, 2, terminate_user },
1472 { "kill-user", MORE, 2, kill_user },
1473 { "list-seats", EQUAL, 1, list_seats },
1474 { "seat-status", MORE, 2, show },
1475 { "show-seat", MORE, 1, show },
1476 { "attach", MORE, 3, attach },
1477 { "flush-devices", EQUAL, 1, flush_devices },
1478 { "terminate-seat", MORE, 2, terminate_seat },
1488 left = argc - optind;
1491 /* Special rule: no arguments means "list-sessions" */
1494 if (streq(argv[optind], "help")) {
1499 for (i = 0; i < ELEMENTSOF(verbs); i++)
1500 if (streq(argv[optind], verbs[i].verb))
1503 if (i >= ELEMENTSOF(verbs)) {
1504 log_error("Unknown operation %s", argv[optind]);
1509 switch (verbs[i].argc_cmp) {
1512 if (left != verbs[i].argc) {
1513 log_error("Invalid number of arguments.");
1520 if (left < verbs[i].argc) {
1521 log_error("Too few arguments.");
1528 if (left > verbs[i].argc) {
1529 log_error("Too many arguments.");
1536 assert_not_reached("Unknown comparison operator.");
1540 log_error("Failed to get D-Bus connection: %s", error->message);
1544 return verbs[i].dispatch(bus, argv + optind, left);
1547 int main(int argc, char*argv[]) {
1548 int r, retval = EXIT_FAILURE;
1549 DBusConnection *bus = NULL;
1552 dbus_error_init(&error);
1554 setlocale(LC_ALL, "");
1555 log_parse_environment();
1558 r = parse_argv(argc, argv);
1562 retval = EXIT_SUCCESS;
1566 if (arg_transport == TRANSPORT_NORMAL)
1567 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1568 else if (arg_transport == TRANSPORT_POLKIT)
1569 bus_connect_system_polkit(&bus, &error);
1570 else if (arg_transport == TRANSPORT_SSH)
1571 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1573 assert_not_reached("Uh, invalid transport...");
1575 r = loginctl_main(bus, argc, argv, &error);
1576 retval = r < 0 ? EXIT_FAILURE : r;
1580 dbus_connection_flush(bus);
1581 dbus_connection_close(bus);
1582 dbus_connection_unref(bus);
1585 dbus_error_free(&error);
1588 strv_free(arg_property);