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 *m = NULL, *reply = NULL;
93 DBusMessageIter iter, sub, sub2;
96 dbus_error_init(&error);
100 pager_open_if_enabled();
102 m = dbus_message_new_method_call(
103 "org.freedesktop.login1",
104 "/org/freedesktop/login1",
105 "org.freedesktop.login1.Manager",
108 log_error("Could not allocate message.");
112 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
114 log_error("Failed to issue method call: %s", bus_error_message(&error));
119 if (!dbus_message_iter_init(reply, &iter) ||
120 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
121 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
122 log_error("Failed to parse reply.");
127 dbus_message_iter_recurse(&iter, &sub);
130 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
132 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
133 const char *id, *user, *seat, *object;
136 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
137 log_error("Failed to parse reply.");
142 dbus_message_iter_recurse(&sub, &sub2);
144 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
145 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
146 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
147 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
148 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
149 log_error("Failed to parse reply.");
154 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
158 dbus_message_iter_next(&sub);
162 printf("\n%u sessions listed.\n", k);
168 dbus_message_unref(m);
171 dbus_message_unref(reply);
173 dbus_error_free(&error);
178 static int list_users(DBusConnection *bus, char **args, unsigned n) {
179 DBusMessage *m = NULL, *reply = NULL;
182 DBusMessageIter iter, sub, sub2;
185 dbus_error_init(&error);
189 pager_open_if_enabled();
191 m = dbus_message_new_method_call(
192 "org.freedesktop.login1",
193 "/org/freedesktop/login1",
194 "org.freedesktop.login1.Manager",
197 log_error("Could not allocate message.");
201 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
203 log_error("Failed to issue method call: %s", bus_error_message(&error));
208 if (!dbus_message_iter_init(reply, &iter) ||
209 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
210 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
211 log_error("Failed to parse reply.");
216 dbus_message_iter_recurse(&iter, &sub);
219 printf("%10s %-16s\n", "UID", "USER");
221 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
222 const char *user, *object;
225 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
226 log_error("Failed to parse reply.");
231 dbus_message_iter_recurse(&sub, &sub2);
233 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
234 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
235 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
236 log_error("Failed to parse reply.");
241 printf("%10u %-16s\n", (unsigned) uid, user);
245 dbus_message_iter_next(&sub);
249 printf("\n%u users listed.\n", k);
255 dbus_message_unref(m);
258 dbus_message_unref(reply);
260 dbus_error_free(&error);
265 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
266 DBusMessage *m = NULL, *reply = NULL;
269 DBusMessageIter iter, sub, sub2;
272 dbus_error_init(&error);
276 pager_open_if_enabled();
278 m = dbus_message_new_method_call(
279 "org.freedesktop.login1",
280 "/org/freedesktop/login1",
281 "org.freedesktop.login1.Manager",
284 log_error("Could not allocate message.");
288 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
290 log_error("Failed to issue method call: %s", bus_error_message(&error));
295 if (!dbus_message_iter_init(reply, &iter) ||
296 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
297 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
298 log_error("Failed to parse reply.");
303 dbus_message_iter_recurse(&iter, &sub);
306 printf("%-16s\n", "SEAT");
308 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
309 const char *seat, *object;
311 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
312 log_error("Failed to parse reply.");
317 dbus_message_iter_recurse(&sub, &sub2);
319 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
320 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
321 log_error("Failed to parse reply.");
326 printf("%-16s\n", seat);
330 dbus_message_iter_next(&sub);
334 printf("\n%u seats listed.\n", k);
340 dbus_message_unref(m);
343 dbus_message_unref(reply);
345 dbus_error_free(&error);
350 typedef struct SessionStatusInfo {
355 const char *default_control_group;
361 const char *remote_host;
362 const char *remote_user;
370 typedef struct UserStatusInfo {
374 const char *default_control_group;
380 typedef struct SeatStatusInfo {
382 const char *active_session;
386 static void print_session_status_info(SessionStatusInfo *i) {
387 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
388 char since2[FORMAT_TIMESTAMP_MAX], *s2;
391 printf("%s - ", strna(i->id));
394 printf("%s (%u)\n", i->name, (unsigned) i->uid);
396 printf("%u\n", (unsigned) i->uid);
398 s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
399 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
402 printf("\t Since: %s; %s\n", s2, s1);
404 printf("\t Since: %s\n", s2);
409 printf("\t Leader: %u", (unsigned) i->leader);
411 get_process_comm(i->leader, &t);
421 printf("\t Seat: %s", i->seat);
424 printf("; vc%i", i->vtnr);
430 printf("\t TTY: %s\n", i->tty);
432 printf("\t Display: %s\n", i->display);
434 if (i->remote_host && i->remote_user)
435 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
436 else if (i->remote_host)
437 printf("\t Remote: %s\n", i->remote_host);
438 else if (i->remote_user)
439 printf("\t Remote: user %s\n", i->remote_user);
441 printf("\t Remote: Yes\n");
444 printf("\t Service: %s", i->service);
447 printf("; type %s", i->type);
450 printf("; class %s", i->class);
453 } else if (i->type) {
454 printf("\t Type: %s\n", i->type);
457 printf("; class %s", i->class);
459 printf("\t Class: %s\n", i->class);
462 printf("\t State: %s\n", i->state);
464 if (i->default_control_group) {
467 printf("\t CGroup: %s\n", i->default_control_group);
469 if (arg_transport != TRANSPORT_SSH) {
476 show_cgroup_and_extra_by_spec(i->default_control_group, "\t\t ", c, false, arg_all, &i->leader, i->leader > 0 ? 1 : 0);
481 static void print_user_status_info(UserStatusInfo *i) {
482 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
483 char since2[FORMAT_TIMESTAMP_MAX], *s2;
487 printf("%s (%u)\n", i->name, (unsigned) i->uid);
489 printf("%u\n", (unsigned) i->uid);
491 s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
492 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
495 printf("\t Since: %s; %s\n", s2, s1);
497 printf("\t Since: %s\n", s2);
499 if (!isempty(i->state))
500 printf("\t State: %s\n", i->state);
502 if (!strv_isempty(i->sessions)) {
504 printf("\tSessions:");
506 STRV_FOREACH(l, i->sessions) {
507 if (streq_ptr(*l, i->display))
516 if (i->default_control_group) {
519 printf("\t CGroup: %s\n", i->default_control_group);
521 if (arg_transport != TRANSPORT_SSH) {
528 show_cgroup_by_path(i->default_control_group, "\t\t ", c, false, arg_all);
533 static void print_seat_status_info(SeatStatusInfo *i) {
536 printf("%s\n", strna(i->id));
538 if (!strv_isempty(i->sessions)) {
540 printf("\tSessions:");
542 STRV_FOREACH(l, i->sessions) {
543 if (streq_ptr(*l, i->active_session))
552 if (arg_transport != TRANSPORT_SSH) {
561 printf("\t Devices:\n");
563 show_sysfs(i->id, "\t\t ", c);
567 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
572 switch (dbus_message_iter_get_arg_type(iter)) {
574 case DBUS_TYPE_STRING: {
577 dbus_message_iter_get_basic(iter, &s);
580 if (streq(name, "Id"))
582 else if (streq(name, "Name"))
584 else if (streq(name, "DefaultControlGroup"))
585 i->default_control_group = s;
586 else if (streq(name, "TTY"))
588 else if (streq(name, "Display"))
590 else if (streq(name, "RemoteHost"))
592 else if (streq(name, "RemoteUser"))
594 else if (streq(name, "Service"))
596 else if (streq(name, "Type"))
598 else if (streq(name, "Class"))
600 else if (streq(name, "State"))
606 case DBUS_TYPE_UINT32: {
609 dbus_message_iter_get_basic(iter, &u);
611 if (streq(name, "VTNr"))
613 else if (streq(name, "Leader"))
614 i->leader = (pid_t) u;
619 case DBUS_TYPE_BOOLEAN: {
622 dbus_message_iter_get_basic(iter, &b);
624 if (streq(name, "Remote"))
630 case DBUS_TYPE_UINT64: {
633 dbus_message_iter_get_basic(iter, &u);
635 if (streq(name, "Timestamp"))
636 i->timestamp = (usec_t) u;
641 case DBUS_TYPE_STRUCT: {
644 dbus_message_iter_recurse(iter, &sub);
646 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
649 dbus_message_iter_get_basic(&sub, &u);
652 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
655 dbus_message_iter_get_basic(&sub, &s);
668 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
673 switch (dbus_message_iter_get_arg_type(iter)) {
675 case DBUS_TYPE_STRING: {
678 dbus_message_iter_get_basic(iter, &s);
681 if (streq(name, "Name"))
683 else if (streq(name, "DefaultControlGroup"))
684 i->default_control_group = s;
685 else if (streq(name, "State"))
691 case DBUS_TYPE_UINT32: {
694 dbus_message_iter_get_basic(iter, &u);
696 if (streq(name, "UID"))
702 case DBUS_TYPE_UINT64: {
705 dbus_message_iter_get_basic(iter, &u);
707 if (streq(name, "Timestamp"))
708 i->timestamp = (usec_t) u;
713 case DBUS_TYPE_STRUCT: {
716 dbus_message_iter_recurse(iter, &sub);
718 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
721 dbus_message_iter_get_basic(&sub, &s);
730 case DBUS_TYPE_ARRAY: {
732 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
733 DBusMessageIter sub, sub2;
735 dbus_message_iter_recurse(iter, &sub);
736 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
740 dbus_message_iter_recurse(&sub, &sub2);
742 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
743 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
746 l = strv_append(i->sessions, id);
750 strv_free(i->sessions);
754 dbus_message_iter_next(&sub);
765 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
770 switch (dbus_message_iter_get_arg_type(iter)) {
772 case DBUS_TYPE_STRING: {
775 dbus_message_iter_get_basic(iter, &s);
778 if (streq(name, "Id"))
784 case DBUS_TYPE_STRUCT: {
787 dbus_message_iter_recurse(iter, &sub);
789 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
792 dbus_message_iter_get_basic(&sub, &s);
795 i->active_session = s;
801 case DBUS_TYPE_ARRAY: {
803 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
804 DBusMessageIter sub, sub2;
806 dbus_message_iter_recurse(iter, &sub);
807 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
811 dbus_message_iter_recurse(&sub, &sub2);
813 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
814 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
817 l = strv_append(i->sessions, id);
821 strv_free(i->sessions);
825 dbus_message_iter_next(&sub);
836 static int print_property(const char *name, DBusMessageIter *iter) {
840 if (arg_property && !strv_find(arg_property, name))
843 switch (dbus_message_iter_get_arg_type(iter)) {
845 case DBUS_TYPE_STRUCT: {
848 dbus_message_iter_recurse(iter, &sub);
850 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
851 (streq(name, "Display") || streq(name, "ActiveSession"))) {
854 dbus_message_iter_get_basic(&sub, &s);
856 if (arg_all || !isempty(s))
857 printf("%s=%s\n", name, s);
863 case DBUS_TYPE_ARRAY:
865 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
866 DBusMessageIter sub, sub2;
869 dbus_message_iter_recurse(iter, &sub);
870 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
874 dbus_message_iter_recurse(&sub, &sub2);
876 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
877 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
881 printf("%s=%s", name, id);
886 dbus_message_iter_next(&sub);
889 if (!found && arg_all)
890 printf("%s=\n", name);
900 if (generic_print_property(name, iter, arg_all) > 0)
904 printf("%s=[unprintable]\n", name);
909 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
910 DBusMessage *m = NULL, *reply = NULL;
911 const char *interface = "";
914 DBusMessageIter iter, sub, sub2, sub3;
915 SessionStatusInfo session_info;
916 UserStatusInfo user_info;
917 SeatStatusInfo seat_info;
927 dbus_error_init(&error);
929 m = dbus_message_new_method_call(
930 "org.freedesktop.login1",
932 "org.freedesktop.DBus.Properties",
935 log_error("Could not allocate message.");
940 if (!dbus_message_append_args(m,
941 DBUS_TYPE_STRING, &interface,
942 DBUS_TYPE_INVALID)) {
943 log_error("Could not append arguments to message.");
948 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
950 log_error("Failed to issue method call: %s", bus_error_message(&error));
955 if (!dbus_message_iter_init(reply, &iter) ||
956 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
957 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
958 log_error("Failed to parse reply.");
963 dbus_message_iter_recurse(&iter, &sub);
970 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
973 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
974 log_error("Failed to parse reply.");
979 dbus_message_iter_recurse(&sub, &sub2);
981 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
982 log_error("Failed to parse reply.");
987 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
988 log_error("Failed to parse reply.");
993 dbus_message_iter_recurse(&sub2, &sub3);
996 r = print_property(name, &sub3);
997 else if (strstr(verb, "session"))
998 r = status_property_session(name, &sub3, &session_info);
999 else if (strstr(verb, "user"))
1000 r = status_property_user(name, &sub3, &user_info);
1002 r = status_property_seat(name, &sub3, &seat_info);
1005 log_error("Failed to parse reply.");
1010 dbus_message_iter_next(&sub);
1013 if (!show_properties) {
1014 if (strstr(verb, "session"))
1015 print_session_status_info(&session_info);
1016 else if (strstr(verb, "user"))
1017 print_user_status_info(&user_info);
1019 print_seat_status_info(&seat_info);
1022 strv_free(seat_info.sessions);
1023 strv_free(user_info.sessions);
1029 dbus_message_unref(m);
1032 dbus_message_unref(reply);
1034 dbus_error_free(&error);
1039 static int show(DBusConnection *bus, char **args, unsigned n) {
1040 DBusMessage *m = NULL, *reply = NULL;
1044 bool show_properties, new_line = false;
1049 dbus_error_init(&error);
1051 show_properties = !strstr(args[0], "status");
1053 if (show_properties)
1054 pager_open_if_enabled();
1056 if (show_properties && n <= 1) {
1057 /* If not argument is specified inspect the manager
1060 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
1064 for (i = 1; i < n; i++) {
1065 const char *path = NULL;
1067 if (strstr(args[0], "session")) {
1069 m = dbus_message_new_method_call(
1070 "org.freedesktop.login1",
1071 "/org/freedesktop/login1",
1072 "org.freedesktop.login1.Manager",
1075 log_error("Could not allocate message.");
1080 if (!dbus_message_append_args(m,
1081 DBUS_TYPE_STRING, &args[i],
1082 DBUS_TYPE_INVALID)) {
1083 log_error("Could not append arguments to message.");
1088 } else if (strstr(args[0], "user")) {
1092 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1094 log_error("User %s unknown.", args[i]);
1098 m = dbus_message_new_method_call(
1099 "org.freedesktop.login1",
1100 "/org/freedesktop/login1",
1101 "org.freedesktop.login1.Manager",
1104 log_error("Could not allocate message.");
1110 if (!dbus_message_append_args(m,
1111 DBUS_TYPE_UINT32, &u,
1112 DBUS_TYPE_INVALID)) {
1113 log_error("Could not append arguments to message.");
1119 m = dbus_message_new_method_call(
1120 "org.freedesktop.login1",
1121 "/org/freedesktop/login1",
1122 "org.freedesktop.login1.Manager",
1125 log_error("Could not allocate message.");
1130 if (!dbus_message_append_args(m,
1131 DBUS_TYPE_STRING, &args[i],
1132 DBUS_TYPE_INVALID)) {
1133 log_error("Could not append arguments to message.");
1139 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1141 log_error("Failed to issue method call: %s", bus_error_message(&error));
1146 if (!dbus_message_get_args(reply, &error,
1147 DBUS_TYPE_OBJECT_PATH, &path,
1148 DBUS_TYPE_INVALID)) {
1149 log_error("Failed to parse reply: %s", bus_error_message(&error));
1154 r = show_one(args[0], bus, path, show_properties, &new_line);
1158 dbus_message_unref(m);
1159 dbus_message_unref(reply);
1165 dbus_message_unref(m);
1168 dbus_message_unref(reply);
1170 dbus_error_free(&error);
1175 static int activate(DBusConnection *bus, char **args, unsigned n) {
1176 DBusMessage *m = NULL;
1184 dbus_error_init(&error);
1186 for (i = 1; i < n; i++) {
1189 m = dbus_message_new_method_call(
1190 "org.freedesktop.login1",
1191 "/org/freedesktop/login1",
1192 "org.freedesktop.login1.Manager",
1193 streq(args[0], "lock-session") ? "LockSession" :
1194 streq(args[0], "unlock-session") ? "UnlockSession" :
1195 streq(args[0], "terminate-session") ? "TerminateSession" :
1198 log_error("Could not allocate message.");
1203 if (!dbus_message_append_args(m,
1204 DBUS_TYPE_STRING, &args[i],
1205 DBUS_TYPE_INVALID)) {
1206 log_error("Could not append arguments to message.");
1211 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1213 log_error("Failed to issue method call: %s", bus_error_message(&error));
1218 dbus_message_unref(m);
1219 dbus_message_unref(reply);
1225 dbus_message_unref(m);
1227 dbus_error_free(&error);
1232 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1233 DBusMessage *m = NULL;
1241 dbus_error_init(&error);
1244 arg_kill_who = "all";
1246 for (i = 1; i < n; i++) {
1249 m = dbus_message_new_method_call(
1250 "org.freedesktop.login1",
1251 "/org/freedesktop/login1",
1252 "org.freedesktop.login1.Manager",
1255 log_error("Could not allocate message.");
1260 if (!dbus_message_append_args(m,
1261 DBUS_TYPE_STRING, &args[i],
1262 DBUS_TYPE_STRING, &arg_kill_who,
1263 DBUS_TYPE_INT32, &arg_signal,
1264 DBUS_TYPE_INVALID)) {
1265 log_error("Could not append arguments to message.");
1270 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1272 log_error("Failed to issue method call: %s", bus_error_message(&error));
1277 dbus_message_unref(m);
1278 dbus_message_unref(reply);
1284 dbus_message_unref(m);
1286 dbus_error_free(&error);
1291 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1292 DBusMessage *m = NULL;
1296 dbus_bool_t b, interactive = true;
1301 dbus_error_init(&error);
1303 polkit_agent_open_if_enabled();
1305 b = streq(args[0], "enable-linger");
1307 for (i = 1; i < n; i++) {
1312 m = dbus_message_new_method_call(
1313 "org.freedesktop.login1",
1314 "/org/freedesktop/login1",
1315 "org.freedesktop.login1.Manager",
1318 log_error("Could not allocate message.");
1323 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1325 log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1330 if (!dbus_message_append_args(m,
1331 DBUS_TYPE_UINT32, &u,
1332 DBUS_TYPE_BOOLEAN, &b,
1333 DBUS_TYPE_BOOLEAN, &interactive,
1334 DBUS_TYPE_INVALID)) {
1335 log_error("Could not append arguments to message.");
1340 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1342 log_error("Failed to issue method call: %s", bus_error_message(&error));
1347 dbus_message_unref(m);
1348 dbus_message_unref(reply);
1356 dbus_message_unref(m);
1358 dbus_error_free(&error);
1363 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1364 DBusMessage *m = NULL;
1372 dbus_error_init(&error);
1374 for (i = 1; i < n; i++) {
1379 m = dbus_message_new_method_call(
1380 "org.freedesktop.login1",
1381 "/org/freedesktop/login1",
1382 "org.freedesktop.login1.Manager",
1385 log_error("Could not allocate message.");
1390 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1392 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1397 if (!dbus_message_append_args(m,
1398 DBUS_TYPE_UINT32, &u,
1399 DBUS_TYPE_INVALID)) {
1400 log_error("Could not append arguments to message.");
1405 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1407 log_error("Failed to issue method call: %s", bus_error_message(&error));
1412 dbus_message_unref(m);
1413 dbus_message_unref(reply);
1421 dbus_message_unref(m);
1423 dbus_error_free(&error);
1428 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1429 DBusMessage *m = NULL;
1437 dbus_error_init(&error);
1440 arg_kill_who = "all";
1442 for (i = 1; i < n; i++) {
1447 m = dbus_message_new_method_call(
1448 "org.freedesktop.login1",
1449 "/org/freedesktop/login1",
1450 "org.freedesktop.login1.Manager",
1453 log_error("Could not allocate message.");
1458 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1460 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1465 if (!dbus_message_append_args(m,
1466 DBUS_TYPE_UINT32, &u,
1467 DBUS_TYPE_INT32, &arg_signal,
1468 DBUS_TYPE_INVALID)) {
1469 log_error("Could not append arguments to message.");
1474 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1476 log_error("Failed to issue method call: %s", bus_error_message(&error));
1481 dbus_message_unref(m);
1482 dbus_message_unref(reply);
1490 dbus_message_unref(m);
1492 dbus_error_free(&error);
1497 static int attach(DBusConnection *bus, char **args, unsigned n) {
1498 DBusMessage *m = NULL;
1502 dbus_bool_t interactive = true;
1507 dbus_error_init(&error);
1509 polkit_agent_open_if_enabled();
1511 for (i = 2; i < n; i++) {
1514 m = dbus_message_new_method_call(
1515 "org.freedesktop.login1",
1516 "/org/freedesktop/login1",
1517 "org.freedesktop.login1.Manager",
1520 log_error("Could not allocate message.");
1525 if (!dbus_message_append_args(m,
1526 DBUS_TYPE_STRING, &args[1],
1527 DBUS_TYPE_STRING, &args[i],
1528 DBUS_TYPE_BOOLEAN, &interactive,
1529 DBUS_TYPE_INVALID)) {
1530 log_error("Could not append arguments to message.");
1535 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1537 log_error("Failed to issue method call: %s", bus_error_message(&error));
1542 dbus_message_unref(m);
1543 dbus_message_unref(reply);
1549 dbus_message_unref(m);
1551 dbus_error_free(&error);
1556 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1557 DBusMessage *m = NULL, *reply = NULL;
1560 dbus_bool_t interactive = true;
1565 dbus_error_init(&error);
1567 polkit_agent_open_if_enabled();
1569 m = dbus_message_new_method_call(
1570 "org.freedesktop.login1",
1571 "/org/freedesktop/login1",
1572 "org.freedesktop.login1.Manager",
1575 log_error("Could not allocate message.");
1580 if (!dbus_message_append_args(m,
1581 DBUS_TYPE_BOOLEAN, &interactive,
1582 DBUS_TYPE_INVALID)) {
1583 log_error("Could not append arguments to message.");
1588 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1590 log_error("Failed to issue method call: %s", bus_error_message(&error));
1597 dbus_message_unref(m);
1600 dbus_message_unref(reply);
1602 dbus_error_free(&error);
1607 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1608 DBusMessage *m = NULL;
1616 dbus_error_init(&error);
1618 for (i = 1; i < n; i++) {
1621 m = dbus_message_new_method_call(
1622 "org.freedesktop.login1",
1623 "/org/freedesktop/login1",
1624 "org.freedesktop.login1.Manager",
1627 log_error("Could not allocate message.");
1632 if (!dbus_message_append_args(m,
1633 DBUS_TYPE_STRING, &args[i],
1634 DBUS_TYPE_INVALID)) {
1635 log_error("Could not append arguments to message.");
1640 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1642 log_error("Failed to issue method call: %s", bus_error_message(&error));
1647 dbus_message_unref(m);
1648 dbus_message_unref(reply);
1654 dbus_message_unref(m);
1656 dbus_error_free(&error);
1661 static int help(void) {
1663 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1664 "Send control commands to or query the login manager.\n\n"
1665 " -h --help Show this help\n"
1666 " --version Show package version\n"
1667 " -p --property=NAME Show only properties by this name\n"
1668 " -a --all Show all properties, including empty ones\n"
1669 " --kill-who=WHO Who to send signal to\n"
1670 " -s --signal=SIGNAL Which signal to send\n"
1671 " -H --host=[USER@]HOST\n"
1672 " Show information for remote host\n"
1673 " -P --privileged Acquire privileges before execution\n"
1674 " --no-pager Do not pipe output into a pager\n\n"
1676 " list-sessions List sessions\n"
1677 " session-status [ID...] Show session status\n"
1678 " show-session [ID...] Show properties of one or more sessions\n"
1679 " activate [ID] Activate a session\n"
1680 " lock-session [ID...] Screen lock one or more sessions\n"
1681 " unlock-session [ID...] Screen unlock one or more sessions\n"
1682 " terminate-session [ID...] Terminate one or more sessions\n"
1683 " kill-session [ID...] Send signal to processes of a session\n"
1684 " list-users List users\n"
1685 " user-status [USER...] Show user status\n"
1686 " show-user [USER...] Show properties of one or more users\n"
1687 " enable-linger [USER...] Enable linger state of one or more users\n"
1688 " disable-linger [USER...] Disable linger state of one or more users\n"
1689 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1690 " kill-user [USER...] Send signal to processes of a user\n"
1691 " list-seats List seats\n"
1692 " seat-status [NAME...] Show seat status\n"
1693 " show-seat [NAME...] Show properties of one or more seats\n"
1694 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1695 " flush-devices Flush all device associations\n"
1696 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1697 program_invocation_short_name);
1702 static int parse_argv(int argc, char *argv[]) {
1705 ARG_VERSION = 0x100,
1711 static const struct option options[] = {
1712 { "help", no_argument, NULL, 'h' },
1713 { "version", no_argument, NULL, ARG_VERSION },
1714 { "property", required_argument, NULL, 'p' },
1715 { "all", no_argument, NULL, 'a' },
1716 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1717 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1718 { "signal", required_argument, NULL, 's' },
1719 { "host", required_argument, NULL, 'H' },
1720 { "privileged",no_argument, NULL, 'P' },
1721 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1722 { NULL, 0, NULL, 0 }
1730 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1739 puts(PACKAGE_STRING);
1741 puts(SYSTEMD_FEATURES);
1747 l = strv_append(arg_property, optarg);
1751 strv_free(arg_property);
1754 /* If the user asked for a particular
1755 * property, show it to him, even if it is
1766 arg_no_pager = true;
1769 case ARG_NO_ASK_PASSWORD:
1770 arg_ask_password = false;
1773 arg_kill_who = optarg;
1777 arg_signal = signal_from_string_try_harder(optarg);
1778 if (arg_signal < 0) {
1779 log_error("Failed to parse signal string %s.", optarg);
1785 arg_transport = TRANSPORT_POLKIT;
1789 arg_transport = TRANSPORT_SSH;
1797 log_error("Unknown option code %c", c);
1805 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1807 static const struct {
1815 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1817 { "list-sessions", LESS, 1, list_sessions },
1818 { "session-status", MORE, 2, show },
1819 { "show-session", MORE, 1, show },
1820 { "activate", EQUAL, 2, activate },
1821 { "lock-session", MORE, 2, activate },
1822 { "unlock-session", MORE, 2, activate },
1823 { "terminate-session", MORE, 2, activate },
1824 { "kill-session", MORE, 2, kill_session },
1825 { "list-users", EQUAL, 1, list_users },
1826 { "user-status", MORE, 2, show },
1827 { "show-user", MORE, 1, show },
1828 { "enable-linger", MORE, 2, enable_linger },
1829 { "disable-linger", MORE, 2, enable_linger },
1830 { "terminate-user", MORE, 2, terminate_user },
1831 { "kill-user", MORE, 2, kill_user },
1832 { "list-seats", EQUAL, 1, list_seats },
1833 { "seat-status", MORE, 2, show },
1834 { "show-seat", MORE, 1, show },
1835 { "attach", MORE, 3, attach },
1836 { "flush-devices", EQUAL, 1, flush_devices },
1837 { "terminate-seat", MORE, 2, terminate_seat },
1847 left = argc - optind;
1850 /* Special rule: no arguments means "list-sessions" */
1853 if (streq(argv[optind], "help")) {
1858 for (i = 0; i < ELEMENTSOF(verbs); i++)
1859 if (streq(argv[optind], verbs[i].verb))
1862 if (i >= ELEMENTSOF(verbs)) {
1863 log_error("Unknown operation %s", argv[optind]);
1868 switch (verbs[i].argc_cmp) {
1871 if (left != verbs[i].argc) {
1872 log_error("Invalid number of arguments.");
1879 if (left < verbs[i].argc) {
1880 log_error("Too few arguments.");
1887 if (left > verbs[i].argc) {
1888 log_error("Too many arguments.");
1895 assert_not_reached("Unknown comparison operator.");
1899 log_error("Failed to get D-Bus connection: %s", error->message);
1903 return verbs[i].dispatch(bus, argv + optind, left);
1906 int main(int argc, char*argv[]) {
1907 int r, retval = EXIT_FAILURE;
1908 DBusConnection *bus = NULL;
1911 dbus_error_init(&error);
1913 log_parse_environment();
1916 r = parse_argv(argc, argv);
1920 retval = EXIT_SUCCESS;
1924 if (arg_transport == TRANSPORT_NORMAL)
1925 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1926 else if (arg_transport == TRANSPORT_POLKIT)
1927 bus_connect_system_polkit(&bus, &error);
1928 else if (arg_transport == TRANSPORT_SSH)
1929 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1931 assert_not_reached("Uh, invalid transport...");
1933 r = loginctl_main(bus, argc, argv, &error);
1934 retval = r < 0 ? EXIT_FAILURE : r;
1938 dbus_connection_flush(bus);
1939 dbus_connection_close(bus);
1940 dbus_connection_unref(bus);
1943 dbus_error_free(&error);
1946 strv_free(arg_property);