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;
285 typedef struct UserStatusInfo {
289 const char *default_control_group;
296 typedef struct SeatStatusInfo {
298 const char *active_session;
302 static void print_session_status_info(SessionStatusInfo *i) {
303 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
304 char since2[FORMAT_TIMESTAMP_MAX], *s2;
307 printf("%s - ", strna(i->id));
310 printf("%s (%u)\n", i->name, (unsigned) i->uid);
312 printf("%u\n", (unsigned) i->uid);
314 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
315 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
318 printf("\t Since: %s; %s\n", s2, s1);
320 printf("\t Since: %s\n", s2);
323 _cleanup_free_ char *t = NULL;
325 printf("\t Leader: %u", (unsigned) i->leader);
327 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);
379 printf("\t Slice: %s\n", i->slice);
381 if (i->default_control_group) {
384 arg_all * OUTPUT_SHOW_ALL |
385 arg_full * OUTPUT_FULL_WIDTH;
387 printf("\t CGroup: %s\n", i->default_control_group);
389 if (arg_transport != TRANSPORT_SSH) {
396 show_cgroup_and_extra_by_spec(i->default_control_group,
397 "\t\t ", c, false, &i->leader,
398 i->leader > 0 ? 1 : 0,
404 static void print_user_status_info(UserStatusInfo *i) {
405 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
406 char since2[FORMAT_TIMESTAMP_MAX], *s2;
410 printf("%s (%u)\n", i->name, (unsigned) i->uid);
412 printf("%u\n", (unsigned) i->uid);
414 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
415 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
418 printf("\t Since: %s; %s\n", s2, s1);
420 printf("\t Since: %s\n", s2);
422 if (!isempty(i->state))
423 printf("\t State: %s\n", i->state);
426 printf("\t Slice: %s\n", i->slice);
428 if (!strv_isempty(i->sessions)) {
430 printf("\tSessions:");
432 STRV_FOREACH(l, i->sessions) {
433 if (streq_ptr(*l, i->display))
442 if (i->default_control_group) {
445 arg_all * OUTPUT_SHOW_ALL |
446 arg_full * OUTPUT_FULL_WIDTH;
448 printf("\t CGroup: %s\n", i->default_control_group);
450 if (arg_transport != TRANSPORT_SSH) {
457 show_cgroup_by_path(i->default_control_group, "\t\t ",
458 c, false, output_flags);
463 static void print_seat_status_info(SeatStatusInfo *i) {
466 printf("%s\n", strna(i->id));
468 if (!strv_isempty(i->sessions)) {
470 printf("\tSessions:");
472 STRV_FOREACH(l, i->sessions) {
473 if (streq_ptr(*l, i->active_session))
482 if (arg_transport != TRANSPORT_SSH) {
491 printf("\t Devices:\n");
493 show_sysfs(i->id, "\t\t ", c);
497 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
502 switch (dbus_message_iter_get_arg_type(iter)) {
504 case DBUS_TYPE_STRING: {
507 dbus_message_iter_get_basic(iter, &s);
510 if (streq(name, "Id"))
512 else if (streq(name, "Name"))
514 else if (streq(name, "DefaultControlGroup"))
515 i->default_control_group = s;
516 else if (streq(name, "TTY"))
518 else if (streq(name, "Display"))
520 else if (streq(name, "RemoteHost"))
522 else if (streq(name, "RemoteUser"))
524 else if (streq(name, "Service"))
526 else if (streq(name, "Type"))
528 else if (streq(name, "Class"))
530 else if (streq(name, "Slice"))
532 else if (streq(name, "State"))
538 case DBUS_TYPE_UINT32: {
541 dbus_message_iter_get_basic(iter, &u);
543 if (streq(name, "VTNr"))
545 else if (streq(name, "Leader"))
546 i->leader = (pid_t) u;
551 case DBUS_TYPE_BOOLEAN: {
554 dbus_message_iter_get_basic(iter, &b);
556 if (streq(name, "Remote"))
562 case DBUS_TYPE_UINT64: {
565 dbus_message_iter_get_basic(iter, &u);
567 if (streq(name, "Timestamp"))
568 i->timestamp = (usec_t) u;
573 case DBUS_TYPE_STRUCT: {
576 dbus_message_iter_recurse(iter, &sub);
578 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
581 dbus_message_iter_get_basic(&sub, &u);
584 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
587 dbus_message_iter_get_basic(&sub, &s);
600 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
605 switch (dbus_message_iter_get_arg_type(iter)) {
607 case DBUS_TYPE_STRING: {
610 dbus_message_iter_get_basic(iter, &s);
613 if (streq(name, "Name"))
615 else if (streq(name, "DefaultControlGroup"))
616 i->default_control_group = s;
617 else if (streq(name, "Slice"))
619 else if (streq(name, "State"))
625 case DBUS_TYPE_UINT32: {
628 dbus_message_iter_get_basic(iter, &u);
630 if (streq(name, "UID"))
636 case DBUS_TYPE_UINT64: {
639 dbus_message_iter_get_basic(iter, &u);
641 if (streq(name, "Timestamp"))
642 i->timestamp = (usec_t) u;
647 case DBUS_TYPE_STRUCT: {
650 dbus_message_iter_recurse(iter, &sub);
652 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
655 dbus_message_iter_get_basic(&sub, &s);
664 case DBUS_TYPE_ARRAY: {
666 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
667 DBusMessageIter sub, sub2;
669 dbus_message_iter_recurse(iter, &sub);
670 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
674 dbus_message_iter_recurse(&sub, &sub2);
676 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
677 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
680 l = strv_append(i->sessions, id);
684 strv_free(i->sessions);
688 dbus_message_iter_next(&sub);
699 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
704 switch (dbus_message_iter_get_arg_type(iter)) {
706 case DBUS_TYPE_STRING: {
709 dbus_message_iter_get_basic(iter, &s);
712 if (streq(name, "Id"))
718 case DBUS_TYPE_STRUCT: {
721 dbus_message_iter_recurse(iter, &sub);
723 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
726 dbus_message_iter_get_basic(&sub, &s);
729 i->active_session = s;
735 case DBUS_TYPE_ARRAY: {
737 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
738 DBusMessageIter sub, sub2;
740 dbus_message_iter_recurse(iter, &sub);
741 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
745 dbus_message_iter_recurse(&sub, &sub2);
747 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
748 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
751 l = strv_append(i->sessions, id);
755 strv_free(i->sessions);
759 dbus_message_iter_next(&sub);
770 static int print_property(const char *name, DBusMessageIter *iter) {
774 if (arg_property && !strv_find(arg_property, name))
777 switch (dbus_message_iter_get_arg_type(iter)) {
779 case DBUS_TYPE_STRUCT: {
782 dbus_message_iter_recurse(iter, &sub);
784 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
785 (streq(name, "Display") || streq(name, "ActiveSession"))) {
788 dbus_message_iter_get_basic(&sub, &s);
790 if (arg_all || !isempty(s))
791 printf("%s=%s\n", name, s);
797 case DBUS_TYPE_ARRAY:
799 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
800 DBusMessageIter sub, sub2;
803 dbus_message_iter_recurse(iter, &sub);
804 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
808 dbus_message_iter_recurse(&sub, &sub2);
810 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
811 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
815 printf("%s=%s", name, id);
820 dbus_message_iter_next(&sub);
823 if (!found && arg_all)
824 printf("%s=\n", name);
834 if (generic_print_property(name, iter, arg_all) > 0)
838 printf("%s=[unprintable]\n", name);
843 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
844 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
845 const char *interface = "";
847 DBusMessageIter iter, sub, sub2, sub3;
848 SessionStatusInfo session_info = {};
849 UserStatusInfo user_info = {};
850 SeatStatusInfo seat_info = {};
855 r = bus_method_call_with_reply(
857 "org.freedesktop.login1",
859 "org.freedesktop.DBus.Properties",
863 DBUS_TYPE_STRING, &interface,
868 if (!dbus_message_iter_init(reply, &iter) ||
869 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
870 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
871 log_error("Failed to parse reply.");
876 dbus_message_iter_recurse(&iter, &sub);
883 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
886 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
887 log_error("Failed to parse reply.");
892 dbus_message_iter_recurse(&sub, &sub2);
894 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
895 log_error("Failed to parse reply.");
900 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
901 log_error("Failed to parse reply.");
906 dbus_message_iter_recurse(&sub2, &sub3);
909 r = print_property(name, &sub3);
910 else if (strstr(verb, "session"))
911 r = status_property_session(name, &sub3, &session_info);
912 else if (strstr(verb, "user"))
913 r = status_property_user(name, &sub3, &user_info);
915 r = status_property_seat(name, &sub3, &seat_info);
918 log_error("Failed to parse reply.");
922 dbus_message_iter_next(&sub);
925 if (!show_properties) {
926 if (strstr(verb, "session"))
927 print_session_status_info(&session_info);
928 else if (strstr(verb, "user"))
929 print_user_status_info(&user_info);
931 print_seat_status_info(&seat_info);
937 strv_free(seat_info.sessions);
938 strv_free(user_info.sessions);
943 static int show(DBusConnection *bus, char **args, unsigned n) {
944 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
948 bool show_properties, new_line = false;
953 dbus_error_init(&error);
955 show_properties = !strstr(args[0], "status");
957 pager_open_if_enabled();
959 if (show_properties && n <= 1) {
960 /* If not argument is specified inspect the manager
963 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
967 for (i = 1; i < n; i++) {
968 const char *path = NULL;
970 if (strstr(args[0], "session")) {
972 ret = bus_method_call_with_reply (
974 "org.freedesktop.login1",
975 "/org/freedesktop/login1",
976 "org.freedesktop.login1.Manager",
980 DBUS_TYPE_STRING, &args[i],
983 } else if (strstr(args[0], "user")) {
987 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
989 log_error("User %s unknown.", args[i]);
994 ret = bus_method_call_with_reply(
996 "org.freedesktop.login1",
997 "/org/freedesktop/login1",
998 "org.freedesktop.login1.Manager",
1002 DBUS_TYPE_UINT32, &u,
1007 ret = bus_method_call_with_reply(
1009 "org.freedesktop.login1",
1010 "/org/freedesktop/login1",
1011 "org.freedesktop.login1.Manager",
1015 DBUS_TYPE_STRING, &args[i],
1023 if (!dbus_message_get_args(reply, &error,
1024 DBUS_TYPE_OBJECT_PATH, &path,
1025 DBUS_TYPE_INVALID)) {
1026 log_error("Failed to parse reply: %s", bus_error_message(&error));
1031 r = show_one(args[0], bus, path, show_properties, &new_line);
1037 dbus_error_free(&error);
1042 static int activate(DBusConnection *bus, char **args, unsigned n) {
1048 for (i = 1; i < n; i++) {
1050 ret = bus_method_call_with_reply (
1052 "org.freedesktop.login1",
1053 "/org/freedesktop/login1",
1054 "org.freedesktop.login1.Manager",
1055 streq(args[0], "lock-session") ? "LockSession" :
1056 streq(args[0], "unlock-session") ? "UnlockSession" :
1057 streq(args[0], "terminate-session") ? "TerminateSession" :
1061 DBUS_TYPE_STRING, &args[i],
1071 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1077 arg_kill_who = "all";
1079 for (i = 1; i < n; i++) {
1082 r = bus_method_call_with_reply (
1084 "org.freedesktop.login1",
1085 "/org/freedesktop/login1",
1086 "org.freedesktop.login1.Manager",
1090 DBUS_TYPE_STRING, &args[i],
1091 DBUS_TYPE_STRING, &arg_kill_who,
1092 DBUS_TYPE_INT32, &arg_signal,
1101 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1103 dbus_bool_t b, interactive = true;
1107 polkit_agent_open_if_enabled();
1109 b = streq(args[0], "enable-linger");
1111 for (i = 1; i < n; i++) {
1116 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1118 log_error("Failed to resolve user %s: %s", args[i], strerror(-r));
1123 r = bus_method_call_with_reply (
1125 "org.freedesktop.login1",
1126 "/org/freedesktop/login1",
1127 "org.freedesktop.login1.Manager",
1131 DBUS_TYPE_UINT32, &u,
1132 DBUS_TYPE_BOOLEAN, &b,
1133 DBUS_TYPE_BOOLEAN, &interactive,
1142 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1147 for (i = 1; i < n; i++) {
1152 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1154 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1159 r = bus_method_call_with_reply (
1161 "org.freedesktop.login1",
1162 "/org/freedesktop/login1",
1163 "org.freedesktop.login1.Manager",
1167 DBUS_TYPE_UINT32, &u,
1176 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1182 arg_kill_who = "all";
1184 for (i = 1; i < n; i++) {
1189 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1191 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1196 r = bus_method_call_with_reply (
1198 "org.freedesktop.login1",
1199 "/org/freedesktop/login1",
1200 "org.freedesktop.login1.Manager",
1204 DBUS_TYPE_UINT32, &u,
1205 DBUS_TYPE_INT32, &arg_signal,
1214 static int attach(DBusConnection *bus, char **args, unsigned n) {
1216 dbus_bool_t interactive = true;
1220 polkit_agent_open_if_enabled();
1222 for (i = 2; i < n; i++) {
1225 r = bus_method_call_with_reply (
1227 "org.freedesktop.login1",
1228 "/org/freedesktop/login1",
1229 "org.freedesktop.login1.Manager",
1233 DBUS_TYPE_STRING, &args[1],
1234 DBUS_TYPE_STRING, &args[i],
1235 DBUS_TYPE_BOOLEAN, &interactive,
1244 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1245 dbus_bool_t interactive = true;
1249 polkit_agent_open_if_enabled();
1251 return bus_method_call_with_reply (
1253 "org.freedesktop.login1",
1254 "/org/freedesktop/login1",
1255 "org.freedesktop.login1.Manager",
1259 DBUS_TYPE_BOOLEAN, &interactive,
1263 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1266 polkit_agent_open_if_enabled();
1268 return bus_method_call_with_reply (
1270 "org.freedesktop.login1",
1271 "/org/freedesktop/login1",
1272 "org.freedesktop.login1.Manager",
1273 streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1279 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1284 for (i = 1; i < n; i++) {
1287 r = bus_method_call_with_reply (
1289 "org.freedesktop.login1",
1290 "/org/freedesktop/login1",
1291 "org.freedesktop.login1.Manager",
1295 DBUS_TYPE_STRING, &args[i],
1304 static int help(void) {
1306 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1307 "Send control commands to or query the login manager.\n\n"
1308 " -h --help Show this help\n"
1309 " --version Show package version\n"
1310 " -p --property=NAME Show only properties by this name\n"
1311 " -a --all Show all properties, including empty ones\n"
1312 " --kill-who=WHO Who to send signal to\n"
1313 " -l --full Do not ellipsize output\n"
1314 " -s --signal=SIGNAL Which signal to send\n"
1315 " --no-ask-password Don't prompt for password\n"
1316 " -H --host=[USER@]HOST Show information for remote host\n"
1317 " -P --privileged Acquire privileges before execution\n"
1318 " --no-pager Do not pipe output into a pager\n\n"
1320 " list-sessions List sessions\n"
1321 " session-status [ID...] Show session status\n"
1322 " show-session [ID...] Show properties of one or more sessions\n"
1323 " activate [ID] Activate a session\n"
1324 " lock-session [ID...] Screen lock one or more sessions\n"
1325 " unlock-session [ID...] Screen unlock one or more sessions\n"
1326 " lock-sessions Screen lock all current sessions\n"
1327 " unlock-sessions Screen unlock all current sessions\n"
1328 " terminate-session [ID...] Terminate one or more sessions\n"
1329 " kill-session [ID...] Send signal to processes of a session\n"
1330 " list-users List users\n"
1331 " user-status [USER...] Show user status\n"
1332 " show-user [USER...] Show properties of one or more users\n"
1333 " enable-linger [USER...] Enable linger state of one or more users\n"
1334 " disable-linger [USER...] Disable linger state of one or more users\n"
1335 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1336 " kill-user [USER...] Send signal to processes of a user\n"
1337 " list-seats List seats\n"
1338 " seat-status [NAME...] Show seat status\n"
1339 " show-seat [NAME...] Show properties of one or more seats\n"
1340 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1341 " flush-devices Flush all device associations\n"
1342 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1343 program_invocation_short_name);
1348 static int parse_argv(int argc, char *argv[]) {
1351 ARG_VERSION = 0x100,
1354 ARG_NO_ASK_PASSWORD,
1357 static const struct option options[] = {
1358 { "help", no_argument, NULL, 'h' },
1359 { "version", no_argument, NULL, ARG_VERSION },
1360 { "property", required_argument, NULL, 'p' },
1361 { "all", no_argument, NULL, 'a' },
1362 { "full", no_argument, NULL, 'l' },
1363 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1364 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1365 { "signal", required_argument, NULL, 's' },
1366 { "host", required_argument, NULL, 'H' },
1367 { "privileged", no_argument, NULL, 'P' },
1368 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1369 { NULL, 0, NULL, 0 }
1377 while ((c = getopt_long(argc, argv, "hp:als:H:P", options, NULL)) >= 0) {
1386 puts(PACKAGE_STRING);
1387 puts(SYSTEMD_FEATURES);
1393 l = strv_append(arg_property, optarg);
1397 strv_free(arg_property);
1400 /* If the user asked for a particular
1401 * property, show it to him, even if it is
1416 arg_no_pager = true;
1419 case ARG_NO_ASK_PASSWORD:
1420 arg_ask_password = false;
1424 arg_kill_who = optarg;
1428 arg_signal = signal_from_string_try_harder(optarg);
1429 if (arg_signal < 0) {
1430 log_error("Failed to parse signal string %s.", optarg);
1436 arg_transport = TRANSPORT_POLKIT;
1440 arg_transport = TRANSPORT_SSH;
1441 parse_user_at_host(optarg, &arg_user, &arg_host);
1448 log_error("Unknown option code %c", c);
1456 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1458 static const struct {
1466 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1468 { "list-sessions", LESS, 1, list_sessions },
1469 { "session-status", MORE, 2, show },
1470 { "show-session", MORE, 1, show },
1471 { "activate", EQUAL, 2, activate },
1472 { "lock-session", MORE, 2, activate },
1473 { "unlock-session", MORE, 2, activate },
1474 { "lock-sessions", EQUAL, 1, lock_sessions },
1475 { "unlock-sessions", EQUAL, 1, lock_sessions },
1476 { "terminate-session", MORE, 2, activate },
1477 { "kill-session", MORE, 2, kill_session },
1478 { "list-users", EQUAL, 1, list_users },
1479 { "user-status", MORE, 2, show },
1480 { "show-user", MORE, 1, show },
1481 { "enable-linger", MORE, 2, enable_linger },
1482 { "disable-linger", MORE, 2, enable_linger },
1483 { "terminate-user", MORE, 2, terminate_user },
1484 { "kill-user", MORE, 2, kill_user },
1485 { "list-seats", EQUAL, 1, list_seats },
1486 { "seat-status", MORE, 2, show },
1487 { "show-seat", MORE, 1, show },
1488 { "attach", MORE, 3, attach },
1489 { "flush-devices", EQUAL, 1, flush_devices },
1490 { "terminate-seat", MORE, 2, terminate_seat },
1500 left = argc - optind;
1503 /* Special rule: no arguments means "list-sessions" */
1506 if (streq(argv[optind], "help")) {
1511 for (i = 0; i < ELEMENTSOF(verbs); i++)
1512 if (streq(argv[optind], verbs[i].verb))
1515 if (i >= ELEMENTSOF(verbs)) {
1516 log_error("Unknown operation %s", argv[optind]);
1521 switch (verbs[i].argc_cmp) {
1524 if (left != verbs[i].argc) {
1525 log_error("Invalid number of arguments.");
1532 if (left < verbs[i].argc) {
1533 log_error("Too few arguments.");
1540 if (left > verbs[i].argc) {
1541 log_error("Too many arguments.");
1548 assert_not_reached("Unknown comparison operator.");
1552 log_error("Failed to get D-Bus connection: %s", error->message);
1556 return verbs[i].dispatch(bus, argv + optind, left);
1559 int main(int argc, char*argv[]) {
1560 int r, retval = EXIT_FAILURE;
1561 DBusConnection *bus = NULL;
1564 dbus_error_init(&error);
1566 setlocale(LC_ALL, "");
1567 log_parse_environment();
1570 r = parse_argv(argc, argv);
1574 retval = EXIT_SUCCESS;
1578 if (arg_transport == TRANSPORT_NORMAL)
1579 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1580 else if (arg_transport == TRANSPORT_POLKIT)
1581 bus_connect_system_polkit(&bus, &error);
1582 else if (arg_transport == TRANSPORT_SSH)
1583 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1585 assert_not_reached("Uh, invalid transport...");
1587 r = loginctl_main(bus, argc, argv, &error);
1588 retval = r < 0 ? EXIT_FAILURE : r;
1592 dbus_connection_flush(bus);
1593 dbus_connection_close(bus);
1594 dbus_connection_unref(bus);
1597 dbus_error_free(&error);
1600 strv_free(arg_property);