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 "unit-name.h"
38 #include "sysfs-show.h"
39 #include "cgroup-show.h"
40 #include "cgroup-util.h"
41 #include "spawn-polkit-agent.h"
43 static char **arg_property = NULL;
44 static bool arg_all = false;
45 static bool arg_full = false;
46 static bool arg_no_pager = false;
47 static const char *arg_kill_who = NULL;
48 static int arg_signal = SIGTERM;
49 static enum transport {
53 } arg_transport = TRANSPORT_NORMAL;
54 static bool arg_ask_password = true;
55 static char *arg_host = NULL;
56 static char *arg_user = NULL;
58 static void pager_open_if_enabled(void) {
60 /* Cache result before we open the pager */
67 static void polkit_agent_open_if_enabled(void) {
69 /* Open the polkit agent as a child process if necessary */
71 if (!arg_ask_password)
77 static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
78 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
80 DBusMessageIter iter, sub, sub2;
83 pager_open_if_enabled();
85 r = bus_method_call_with_reply (
87 "org.freedesktop.login1",
88 "/org/freedesktop/login1",
89 "org.freedesktop.login1.Manager",
97 if (!dbus_message_iter_init(reply, &iter) ||
98 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
99 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
100 log_error("Failed to parse reply.");
104 dbus_message_iter_recurse(&iter, &sub);
107 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
109 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
110 const char *id, *user, *seat, *object;
113 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
114 log_error("Failed to parse reply.");
118 dbus_message_iter_recurse(&sub, &sub2);
120 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
121 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
122 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
123 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
124 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
125 log_error("Failed to parse reply.");
129 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
133 dbus_message_iter_next(&sub);
137 printf("\n%u sessions listed.\n", k);
142 static int list_users(DBusConnection *bus, char **args, unsigned n) {
143 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
145 DBusMessageIter iter, sub, sub2;
148 pager_open_if_enabled();
150 r = bus_method_call_with_reply (
152 "org.freedesktop.login1",
153 "/org/freedesktop/login1",
154 "org.freedesktop.login1.Manager",
162 if (!dbus_message_iter_init(reply, &iter) ||
163 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
164 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
165 log_error("Failed to parse reply.");
169 dbus_message_iter_recurse(&iter, &sub);
172 printf("%10s %-16s\n", "UID", "USER");
174 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
175 const char *user, *object;
178 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
179 log_error("Failed to parse reply.");
183 dbus_message_iter_recurse(&sub, &sub2);
185 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
186 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
187 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
188 log_error("Failed to parse reply.");
192 printf("%10u %-16s\n", (unsigned) uid, user);
196 dbus_message_iter_next(&sub);
200 printf("\n%u users listed.\n", k);
205 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
206 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
208 DBusMessageIter iter, sub, sub2;
211 pager_open_if_enabled();
213 r = bus_method_call_with_reply (
215 "org.freedesktop.login1",
216 "/org/freedesktop/login1",
217 "org.freedesktop.login1.Manager",
225 if (!dbus_message_iter_init(reply, &iter) ||
226 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
227 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
228 log_error("Failed to parse reply.");
232 dbus_message_iter_recurse(&iter, &sub);
235 printf("%-16s\n", "SEAT");
237 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
238 const char *seat, *object;
240 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
241 log_error("Failed to parse reply.");
245 dbus_message_iter_recurse(&sub, &sub2);
247 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
248 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
249 log_error("Failed to parse reply.");
253 printf("%-16s\n", seat);
257 dbus_message_iter_next(&sub);
261 printf("\n%u seats listed.\n", k);
266 static int show_unit_cgroup(DBusConnection *bus, const char *interface, const char *unit, pid_t leader) {
267 const char *property = "ControlGroup";
268 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
269 _cleanup_free_ char *path = NULL;
270 DBusMessageIter iter, sub;
279 if (arg_transport == TRANSPORT_SSH)
282 path = unit_dbus_path_from_name(unit);
286 r = bus_method_call_with_reply(
288 "org.freedesktop.systemd1",
290 "org.freedesktop.DBus.Properties",
294 DBUS_TYPE_STRING, &interface,
295 DBUS_TYPE_STRING, &property,
298 log_error("Failed to query ControlGroup: %s", bus_error(&error, r));
299 dbus_error_free(&error);
303 if (!dbus_message_iter_init(reply, &iter) ||
304 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
305 log_error("Failed to parse reply.");
309 dbus_message_iter_recurse(&iter, &sub);
310 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
311 log_error("Failed to parse reply.");
315 dbus_message_iter_get_basic(&sub, &cgroup);
320 if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
324 arg_all * OUTPUT_SHOW_ALL |
325 arg_full * OUTPUT_FULL_WIDTH;
333 show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, false, &leader, leader > 0, output_flags);
337 typedef struct SessionStatusInfo {
347 const char *remote_host;
348 const char *remote_user;
357 typedef struct UserStatusInfo {
367 typedef struct SeatStatusInfo {
369 const char *active_session;
373 static void print_session_status_info(DBusConnection *bus, SessionStatusInfo *i) {
374 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
375 char since2[FORMAT_TIMESTAMP_MAX], *s2;
378 printf("%s - ", strna(i->id));
381 printf("%s (%u)\n", i->name, (unsigned) i->uid);
383 printf("%u\n", (unsigned) i->uid);
385 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
386 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
389 printf("\t Since: %s; %s\n", s2, s1);
391 printf("\t Since: %s\n", s2);
394 _cleanup_free_ char *t = NULL;
396 printf("\t Leader: %u", (unsigned) i->leader);
398 get_process_comm(i->leader, &t);
406 printf("\t Seat: %s", i->seat);
409 printf("; vc%i", i->vtnr);
415 printf("\t TTY: %s\n", i->tty);
417 printf("\t Display: %s\n", i->display);
419 if (i->remote_host && i->remote_user)
420 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
421 else if (i->remote_host)
422 printf("\t Remote: %s\n", i->remote_host);
423 else if (i->remote_user)
424 printf("\t Remote: user %s\n", i->remote_user);
426 printf("\t Remote: Yes\n");
429 printf("\t Service: %s", i->service);
432 printf("; type %s", i->type);
435 printf("; class %s", i->class);
438 } else if (i->type) {
439 printf("\t Type: %s\n", i->type);
442 printf("; class %s", i->class);
444 printf("\t Class: %s\n", i->class);
447 printf("\t State: %s\n", i->state);
450 printf("\t Unit: %s\n", i->scope);
451 show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i->scope, i->leader);
455 static void print_user_status_info(DBusConnection *bus, UserStatusInfo *i) {
456 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
457 char since2[FORMAT_TIMESTAMP_MAX], *s2;
461 printf("%s (%u)\n", i->name, (unsigned) i->uid);
463 printf("%u\n", (unsigned) i->uid);
465 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
466 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
469 printf("\t Since: %s; %s\n", s2, s1);
471 printf("\t Since: %s\n", s2);
473 if (!isempty(i->state))
474 printf("\t State: %s\n", i->state);
477 if (!strv_isempty(i->sessions)) {
479 printf("\tSessions:");
481 STRV_FOREACH(l, i->sessions) {
482 if (streq_ptr(*l, i->display))
492 printf("\t Unit: %s\n", i->slice);
493 show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i->slice, 0);
497 static void print_seat_status_info(SeatStatusInfo *i) {
500 printf("%s\n", strna(i->id));
502 if (!strv_isempty(i->sessions)) {
504 printf("\tSessions:");
506 STRV_FOREACH(l, i->sessions) {
507 if (streq_ptr(*l, i->active_session))
516 if (arg_transport != TRANSPORT_SSH) {
525 printf("\t Devices:\n");
527 show_sysfs(i->id, "\t\t ", c);
531 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
536 switch (dbus_message_iter_get_arg_type(iter)) {
538 case DBUS_TYPE_STRING: {
541 dbus_message_iter_get_basic(iter, &s);
544 if (streq(name, "Id"))
546 else if (streq(name, "Name"))
548 else if (streq(name, "TTY"))
550 else if (streq(name, "Display"))
552 else if (streq(name, "RemoteHost"))
554 else if (streq(name, "RemoteUser"))
556 else if (streq(name, "Service"))
558 else if (streq(name, "Type"))
560 else if (streq(name, "Class"))
562 else if (streq(name, "Scope"))
564 else if (streq(name, "State"))
570 case DBUS_TYPE_UINT32: {
573 dbus_message_iter_get_basic(iter, &u);
575 if (streq(name, "VTNr"))
577 else if (streq(name, "Leader"))
578 i->leader = (pid_t) u;
583 case DBUS_TYPE_BOOLEAN: {
586 dbus_message_iter_get_basic(iter, &b);
588 if (streq(name, "Remote"))
594 case DBUS_TYPE_UINT64: {
597 dbus_message_iter_get_basic(iter, &u);
599 if (streq(name, "Timestamp"))
600 i->timestamp = (usec_t) u;
605 case DBUS_TYPE_STRUCT: {
608 dbus_message_iter_recurse(iter, &sub);
610 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
613 dbus_message_iter_get_basic(&sub, &u);
616 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
619 dbus_message_iter_get_basic(&sub, &s);
632 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
637 switch (dbus_message_iter_get_arg_type(iter)) {
639 case DBUS_TYPE_STRING: {
642 dbus_message_iter_get_basic(iter, &s);
645 if (streq(name, "Name"))
647 else if (streq(name, "Slice"))
649 else if (streq(name, "State"))
655 case DBUS_TYPE_UINT32: {
658 dbus_message_iter_get_basic(iter, &u);
660 if (streq(name, "UID"))
666 case DBUS_TYPE_UINT64: {
669 dbus_message_iter_get_basic(iter, &u);
671 if (streq(name, "Timestamp"))
672 i->timestamp = (usec_t) u;
677 case DBUS_TYPE_STRUCT: {
680 dbus_message_iter_recurse(iter, &sub);
682 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
685 dbus_message_iter_get_basic(&sub, &s);
694 case DBUS_TYPE_ARRAY: {
696 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
697 DBusMessageIter sub, sub2;
699 dbus_message_iter_recurse(iter, &sub);
700 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
704 dbus_message_iter_recurse(&sub, &sub2);
706 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
707 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
710 l = strv_append(i->sessions, id);
714 strv_free(i->sessions);
718 dbus_message_iter_next(&sub);
729 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
734 switch (dbus_message_iter_get_arg_type(iter)) {
736 case DBUS_TYPE_STRING: {
739 dbus_message_iter_get_basic(iter, &s);
742 if (streq(name, "Id"))
748 case DBUS_TYPE_STRUCT: {
751 dbus_message_iter_recurse(iter, &sub);
753 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
756 dbus_message_iter_get_basic(&sub, &s);
759 i->active_session = s;
765 case DBUS_TYPE_ARRAY: {
767 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
768 DBusMessageIter sub, sub2;
770 dbus_message_iter_recurse(iter, &sub);
771 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
775 dbus_message_iter_recurse(&sub, &sub2);
777 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
778 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
781 l = strv_append(i->sessions, id);
785 strv_free(i->sessions);
789 dbus_message_iter_next(&sub);
800 static int print_property(const char *name, DBusMessageIter *iter) {
804 if (arg_property && !strv_find(arg_property, name))
807 switch (dbus_message_iter_get_arg_type(iter)) {
809 case DBUS_TYPE_STRUCT: {
812 dbus_message_iter_recurse(iter, &sub);
814 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
815 (streq(name, "Display") || streq(name, "ActiveSession"))) {
818 dbus_message_iter_get_basic(&sub, &s);
820 if (arg_all || !isempty(s))
821 printf("%s=%s\n", name, s);
827 case DBUS_TYPE_ARRAY:
829 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
830 DBusMessageIter sub, sub2;
833 dbus_message_iter_recurse(iter, &sub);
834 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
838 dbus_message_iter_recurse(&sub, &sub2);
840 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
841 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
845 printf("%s=%s", name, id);
850 dbus_message_iter_next(&sub);
853 if (!found && arg_all)
854 printf("%s=\n", name);
864 if (generic_print_property(name, iter, arg_all) > 0)
868 printf("%s=[unprintable]\n", name);
873 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
874 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
875 const char *interface = "";
877 DBusMessageIter iter, sub, sub2, sub3;
878 SessionStatusInfo session_info = {};
879 UserStatusInfo user_info = {};
880 SeatStatusInfo seat_info = {};
885 r = bus_method_call_with_reply(
887 "org.freedesktop.login1",
889 "org.freedesktop.DBus.Properties",
893 DBUS_TYPE_STRING, &interface,
898 if (!dbus_message_iter_init(reply, &iter) ||
899 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
900 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
901 log_error("Failed to parse reply.");
906 dbus_message_iter_recurse(&iter, &sub);
913 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
916 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
917 log_error("Failed to parse reply.");
922 dbus_message_iter_recurse(&sub, &sub2);
924 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
925 log_error("Failed to parse reply.");
930 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
931 log_error("Failed to parse reply.");
936 dbus_message_iter_recurse(&sub2, &sub3);
939 r = print_property(name, &sub3);
940 else if (strstr(verb, "session"))
941 r = status_property_session(name, &sub3, &session_info);
942 else if (strstr(verb, "user"))
943 r = status_property_user(name, &sub3, &user_info);
945 r = status_property_seat(name, &sub3, &seat_info);
948 log_error("Failed to parse reply.");
952 dbus_message_iter_next(&sub);
955 if (!show_properties) {
956 if (strstr(verb, "session"))
957 print_session_status_info(bus, &session_info);
958 else if (strstr(verb, "user"))
959 print_user_status_info(bus, &user_info);
961 print_seat_status_info(&seat_info);
967 strv_free(seat_info.sessions);
968 strv_free(user_info.sessions);
973 static int show(DBusConnection *bus, char **args, unsigned n) {
974 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
978 bool show_properties, new_line = false;
983 dbus_error_init(&error);
985 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,
1037 ret = bus_method_call_with_reply(
1039 "org.freedesktop.login1",
1040 "/org/freedesktop/login1",
1041 "org.freedesktop.login1.Manager",
1045 DBUS_TYPE_STRING, &args[i],
1053 if (!dbus_message_get_args(reply, &error,
1054 DBUS_TYPE_OBJECT_PATH, &path,
1055 DBUS_TYPE_INVALID)) {
1056 log_error("Failed to parse reply: %s", bus_error_message(&error));
1061 r = show_one(args[0], bus, path, show_properties, &new_line);
1067 dbus_error_free(&error);
1072 static int activate(DBusConnection *bus, char **args, unsigned n) {
1078 for (i = 1; i < n; i++) {
1080 ret = bus_method_call_with_reply (
1082 "org.freedesktop.login1",
1083 "/org/freedesktop/login1",
1084 "org.freedesktop.login1.Manager",
1085 streq(args[0], "lock-session") ? "LockSession" :
1086 streq(args[0], "unlock-session") ? "UnlockSession" :
1087 streq(args[0], "terminate-session") ? "TerminateSession" :
1091 DBUS_TYPE_STRING, &args[i],
1101 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1107 arg_kill_who = "all";
1109 for (i = 1; i < n; i++) {
1112 r = bus_method_call_with_reply (
1114 "org.freedesktop.login1",
1115 "/org/freedesktop/login1",
1116 "org.freedesktop.login1.Manager",
1120 DBUS_TYPE_STRING, &args[i],
1121 DBUS_TYPE_STRING, &arg_kill_who,
1122 DBUS_TYPE_INT32, &arg_signal,
1131 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1133 dbus_bool_t b, interactive = true;
1137 polkit_agent_open_if_enabled();
1139 b = streq(args[0], "enable-linger");
1141 for (i = 1; i < n; i++) {
1146 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1148 log_error("Failed to resolve user %s: %s", args[i], strerror(-r));
1153 r = bus_method_call_with_reply (
1155 "org.freedesktop.login1",
1156 "/org/freedesktop/login1",
1157 "org.freedesktop.login1.Manager",
1161 DBUS_TYPE_UINT32, &u,
1162 DBUS_TYPE_BOOLEAN, &b,
1163 DBUS_TYPE_BOOLEAN, &interactive,
1172 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1177 for (i = 1; i < n; i++) {
1182 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1184 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1189 r = bus_method_call_with_reply (
1191 "org.freedesktop.login1",
1192 "/org/freedesktop/login1",
1193 "org.freedesktop.login1.Manager",
1197 DBUS_TYPE_UINT32, &u,
1206 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1212 arg_kill_who = "all";
1214 for (i = 1; i < n; i++) {
1219 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1221 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1226 r = 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,
1244 static int attach(DBusConnection *bus, char **args, unsigned n) {
1246 dbus_bool_t interactive = true;
1250 polkit_agent_open_if_enabled();
1252 for (i = 2; i < n; i++) {
1255 r = 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,
1274 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1275 dbus_bool_t interactive = true;
1279 polkit_agent_open_if_enabled();
1281 return bus_method_call_with_reply (
1283 "org.freedesktop.login1",
1284 "/org/freedesktop/login1",
1285 "org.freedesktop.login1.Manager",
1289 DBUS_TYPE_BOOLEAN, &interactive,
1293 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1296 polkit_agent_open_if_enabled();
1298 return bus_method_call_with_reply (
1300 "org.freedesktop.login1",
1301 "/org/freedesktop/login1",
1302 "org.freedesktop.login1.Manager",
1303 streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1309 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1314 for (i = 1; i < n; i++) {
1317 r = bus_method_call_with_reply (
1319 "org.freedesktop.login1",
1320 "/org/freedesktop/login1",
1321 "org.freedesktop.login1.Manager",
1325 DBUS_TYPE_STRING, &args[i],
1334 static int help(void) {
1336 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1337 "Send control commands to or query the login manager.\n\n"
1338 " -h --help Show this help\n"
1339 " --version Show package version\n"
1340 " -p --property=NAME Show only properties by this name\n"
1341 " -a --all Show all properties, including empty ones\n"
1342 " --kill-who=WHO Who to send signal to\n"
1343 " -l --full Do not ellipsize output\n"
1344 " -s --signal=SIGNAL Which signal to send\n"
1345 " --no-ask-password Don't prompt for password\n"
1346 " -H --host=[USER@]HOST Show information for remote host\n"
1347 " -P --privileged Acquire privileges before execution\n"
1348 " --no-pager Do not pipe output into a pager\n\n"
1350 " list-sessions List sessions\n"
1351 " session-status [ID...] Show session status\n"
1352 " show-session [ID...] Show properties of one or more sessions\n"
1353 " activate [ID] Activate a session\n"
1354 " lock-session [ID...] Screen lock one or more sessions\n"
1355 " unlock-session [ID...] Screen unlock one or more sessions\n"
1356 " lock-sessions Screen lock all current sessions\n"
1357 " unlock-sessions Screen unlock all current sessions\n"
1358 " terminate-session [ID...] Terminate one or more sessions\n"
1359 " kill-session [ID...] Send signal to processes of a session\n"
1360 " list-users List users\n"
1361 " user-status [USER...] Show user status\n"
1362 " show-user [USER...] Show properties of one or more users\n"
1363 " enable-linger [USER...] Enable linger state of one or more users\n"
1364 " disable-linger [USER...] Disable linger state of one or more users\n"
1365 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1366 " kill-user [USER...] Send signal to processes of a user\n"
1367 " list-seats List seats\n"
1368 " seat-status [NAME...] Show seat status\n"
1369 " show-seat [NAME...] Show properties of one or more seats\n"
1370 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1371 " flush-devices Flush all device associations\n"
1372 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1373 program_invocation_short_name);
1378 static int parse_argv(int argc, char *argv[]) {
1381 ARG_VERSION = 0x100,
1384 ARG_NO_ASK_PASSWORD,
1387 static const struct option options[] = {
1388 { "help", no_argument, NULL, 'h' },
1389 { "version", no_argument, NULL, ARG_VERSION },
1390 { "property", required_argument, NULL, 'p' },
1391 { "all", no_argument, NULL, 'a' },
1392 { "full", no_argument, NULL, 'l' },
1393 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1394 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1395 { "signal", required_argument, NULL, 's' },
1396 { "host", required_argument, NULL, 'H' },
1397 { "privileged", no_argument, NULL, 'P' },
1398 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1399 { NULL, 0, NULL, 0 }
1407 while ((c = getopt_long(argc, argv, "hp:als:H:P", options, NULL)) >= 0) {
1416 puts(PACKAGE_STRING);
1417 puts(SYSTEMD_FEATURES);
1423 l = strv_append(arg_property, optarg);
1427 strv_free(arg_property);
1430 /* If the user asked for a particular
1431 * property, show it to him, even if it is
1446 arg_no_pager = true;
1449 case ARG_NO_ASK_PASSWORD:
1450 arg_ask_password = false;
1454 arg_kill_who = optarg;
1458 arg_signal = signal_from_string_try_harder(optarg);
1459 if (arg_signal < 0) {
1460 log_error("Failed to parse signal string %s.", optarg);
1466 arg_transport = TRANSPORT_POLKIT;
1470 arg_transport = TRANSPORT_SSH;
1471 parse_user_at_host(optarg, &arg_user, &arg_host);
1478 log_error("Unknown option code %c", c);
1486 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1488 static const struct {
1496 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1498 { "list-sessions", LESS, 1, list_sessions },
1499 { "session-status", MORE, 2, show },
1500 { "show-session", MORE, 1, show },
1501 { "activate", EQUAL, 2, activate },
1502 { "lock-session", MORE, 2, activate },
1503 { "unlock-session", MORE, 2, activate },
1504 { "lock-sessions", EQUAL, 1, lock_sessions },
1505 { "unlock-sessions", EQUAL, 1, lock_sessions },
1506 { "terminate-session", MORE, 2, activate },
1507 { "kill-session", MORE, 2, kill_session },
1508 { "list-users", EQUAL, 1, list_users },
1509 { "user-status", MORE, 2, show },
1510 { "show-user", MORE, 1, show },
1511 { "enable-linger", MORE, 2, enable_linger },
1512 { "disable-linger", MORE, 2, enable_linger },
1513 { "terminate-user", MORE, 2, terminate_user },
1514 { "kill-user", MORE, 2, kill_user },
1515 { "list-seats", EQUAL, 1, list_seats },
1516 { "seat-status", MORE, 2, show },
1517 { "show-seat", MORE, 1, show },
1518 { "attach", MORE, 3, attach },
1519 { "flush-devices", EQUAL, 1, flush_devices },
1520 { "terminate-seat", MORE, 2, terminate_seat },
1530 left = argc - optind;
1533 /* Special rule: no arguments means "list-sessions" */
1536 if (streq(argv[optind], "help")) {
1541 for (i = 0; i < ELEMENTSOF(verbs); i++)
1542 if (streq(argv[optind], verbs[i].verb))
1545 if (i >= ELEMENTSOF(verbs)) {
1546 log_error("Unknown operation %s", argv[optind]);
1551 switch (verbs[i].argc_cmp) {
1554 if (left != verbs[i].argc) {
1555 log_error("Invalid number of arguments.");
1562 if (left < verbs[i].argc) {
1563 log_error("Too few arguments.");
1570 if (left > verbs[i].argc) {
1571 log_error("Too many arguments.");
1578 assert_not_reached("Unknown comparison operator.");
1582 log_error("Failed to get D-Bus connection: %s", error->message);
1586 return verbs[i].dispatch(bus, argv + optind, left);
1589 int main(int argc, char*argv[]) {
1590 int r, retval = EXIT_FAILURE;
1591 DBusConnection *bus = NULL;
1594 dbus_error_init(&error);
1596 setlocale(LC_ALL, "");
1597 log_parse_environment();
1600 r = parse_argv(argc, argv);
1604 retval = EXIT_SUCCESS;
1608 if (arg_transport == TRANSPORT_NORMAL)
1609 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1610 else if (arg_transport == TRANSPORT_POLKIT)
1611 bus_connect_system_polkit(&bus, &error);
1612 else if (arg_transport == TRANSPORT_SSH)
1613 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1615 assert_not_reached("Uh, invalid transport...");
1617 r = loginctl_main(bus, argc, argv, &error);
1618 retval = r < 0 ? EXIT_FAILURE : r;
1622 dbus_connection_flush(bus);
1623 dbus_connection_close(bus);
1624 dbus_connection_unref(bus);
1627 dbus_error_free(&error);
1630 strv_free(arg_property);