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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
18 You should have received a copy of the GNU 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"
39 static char **arg_property = NULL;
40 static bool arg_all = false;
41 static bool arg_no_pager = false;
42 static const char *arg_kill_who = NULL;
43 static int arg_signal = SIGTERM;
44 static enum transport {
48 } arg_transport = TRANSPORT_NORMAL;
49 static const char *arg_host = NULL;
51 static bool on_tty(void) {
54 /* Note that this is invoked relatively early, before we start
55 * the pager. That means the value we return reflects whether
56 * we originally were started on a tty, not if we currently
57 * are. But this is intended, since we want colour and so on
58 * when run in our own pager. */
60 if (_unlikely_(t < 0))
61 t = isatty(STDOUT_FILENO) > 0;
66 static void pager_open_if_enabled(void) {
73 static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
74 DBusMessage *m = NULL, *reply = NULL;
77 DBusMessageIter iter, sub, sub2;
80 dbus_error_init(&error);
84 pager_open_if_enabled();
86 m = dbus_message_new_method_call(
87 "org.freedesktop.login1",
88 "/org/freedesktop/login1",
89 "org.freedesktop.login1.Manager",
92 log_error("Could not allocate message.");
96 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
98 log_error("Failed to issue method call: %s", bus_error_message(&error));
103 if (!dbus_message_iter_init(reply, &iter) ||
104 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
105 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
106 log_error("Failed to parse reply.");
111 dbus_message_iter_recurse(&iter, &sub);
114 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
116 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
117 const char *id, *user, *seat, *object;
120 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
121 log_error("Failed to parse reply.");
126 dbus_message_iter_recurse(&sub, &sub2);
128 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
129 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
130 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
131 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
132 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
133 log_error("Failed to parse reply.");
138 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
142 dbus_message_iter_next(&sub);
146 printf("\n%u sessions listed.\n", k);
152 dbus_message_unref(m);
155 dbus_message_unref(reply);
157 dbus_error_free(&error);
162 static int list_users(DBusConnection *bus, char **args, unsigned n) {
163 DBusMessage *m = NULL, *reply = NULL;
166 DBusMessageIter iter, sub, sub2;
169 dbus_error_init(&error);
173 pager_open_if_enabled();
175 m = dbus_message_new_method_call(
176 "org.freedesktop.login1",
177 "/org/freedesktop/login1",
178 "org.freedesktop.login1.Manager",
181 log_error("Could not allocate message.");
185 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
187 log_error("Failed to issue method call: %s", bus_error_message(&error));
192 if (!dbus_message_iter_init(reply, &iter) ||
193 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
194 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
195 log_error("Failed to parse reply.");
200 dbus_message_iter_recurse(&iter, &sub);
203 printf("%10s %-16s\n", "UID", "USER");
205 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
206 const char *user, *object;
209 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
210 log_error("Failed to parse reply.");
215 dbus_message_iter_recurse(&sub, &sub2);
217 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
218 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
219 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
220 log_error("Failed to parse reply.");
225 printf("%10u %-16s\n", (unsigned) uid, user);
229 dbus_message_iter_next(&sub);
233 printf("\n%u users listed.\n", k);
239 dbus_message_unref(m);
242 dbus_message_unref(reply);
244 dbus_error_free(&error);
249 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
250 DBusMessage *m = NULL, *reply = NULL;
253 DBusMessageIter iter, sub, sub2;
256 dbus_error_init(&error);
260 pager_open_if_enabled();
262 m = dbus_message_new_method_call(
263 "org.freedesktop.login1",
264 "/org/freedesktop/login1",
265 "org.freedesktop.login1.Manager",
268 log_error("Could not allocate message.");
272 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
274 log_error("Failed to issue method call: %s", bus_error_message(&error));
279 if (!dbus_message_iter_init(reply, &iter) ||
280 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
281 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
282 log_error("Failed to parse reply.");
287 dbus_message_iter_recurse(&iter, &sub);
290 printf("%-16s\n", "SEAT");
292 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
293 const char *seat, *object;
295 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
296 log_error("Failed to parse reply.");
301 dbus_message_iter_recurse(&sub, &sub2);
303 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
304 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
305 log_error("Failed to parse reply.");
310 printf("%-16s\n", seat);
314 dbus_message_iter_next(&sub);
318 printf("\n%u seats listed.\n", k);
324 dbus_message_unref(m);
327 dbus_message_unref(reply);
329 dbus_error_free(&error);
334 typedef struct SessionStatusInfo {
339 const char *control_group;
345 const char *remote_host;
346 const char *remote_user;
353 typedef struct UserStatusInfo {
357 const char *control_group;
363 typedef struct SeatStatusInfo {
365 const char *active_session;
369 static void print_session_status_info(SessionStatusInfo *i) {
370 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
371 char since2[FORMAT_TIMESTAMP_MAX], *s2;
374 printf("%s - ", strna(i->id));
377 printf("%s (%u)\n", i->name, (unsigned) i->uid);
379 printf("%u\n", (unsigned) i->uid);
381 s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
382 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
385 printf("\t Since: %s; %s\n", s2, s1);
387 printf("\t Since: %s\n", s2);
392 printf("\t Leader: %u", (unsigned) i->leader);
394 get_process_name(i->leader, &t);
404 printf("\t Seat: %s", i->seat);
407 printf("; vc%i", i->vtnr);
413 printf("\t TTY: %s\n", i->tty);
415 printf("\t Display: %s\n", i->display);
417 if (i->remote_host && i->remote_user)
418 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
419 else if (i->remote_host)
420 printf("\t Remote: %s\n", i->remote_host);
421 else if (i->remote_user)
422 printf("\t Remote: user %s\n", i->remote_user);
424 printf("\t Remote: Yes\n");
427 printf("\t Service: %s", i->service);
430 printf("; type %s", i->type);
434 printf("\t Type: %s\n", i->type);
436 printf("\t Active: %s\n", yes_no(i->active));
438 if (i->control_group) {
441 printf("\t CGroup: %s\n", i->control_group);
443 if (arg_transport != TRANSPORT_SSH) {
450 show_cgroup_by_path(i->control_group, "\t\t ", c);
455 static void print_user_status_info(UserStatusInfo *i) {
456 char since1[FORMAT_TIMESTAMP_PRETTY_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_pretty(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);
476 if (!strv_isempty(i->sessions)) {
478 printf("\tSessions:");
480 STRV_FOREACH(l, i->sessions) {
481 if (streq_ptr(*l, i->display))
490 if (i->control_group) {
493 printf("\t CGroup: %s\n", i->control_group);
495 if (arg_transport != TRANSPORT_SSH) {
502 show_cgroup_by_path(i->control_group, "\t\t ", c);
507 static void print_seat_status_info(SeatStatusInfo *i) {
510 printf("%s\n", strna(i->id));
512 if (!strv_isempty(i->sessions)) {
514 printf("\tSessions:");
516 STRV_FOREACH(l, i->sessions) {
517 if (streq_ptr(*l, i->active_session))
526 if (arg_transport != TRANSPORT_SSH) {
535 printf("\t Devices:\n");
537 show_sysfs(i->id, "\t\t ", c);
541 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
546 switch (dbus_message_iter_get_arg_type(iter)) {
548 case DBUS_TYPE_STRING: {
551 dbus_message_iter_get_basic(iter, &s);
554 if (streq(name, "Id"))
556 else if (streq(name, "Name"))
558 else if (streq(name, "ControlGroupPath"))
559 i->control_group = s;
560 else if (streq(name, "TTY"))
562 else if (streq(name, "Display"))
564 else if (streq(name, "RemoteHost"))
566 else if (streq(name, "RemoteUser"))
568 else if (streq(name, "Service"))
570 else if (streq(name, "Type"))
576 case DBUS_TYPE_UINT32: {
579 dbus_message_iter_get_basic(iter, &u);
581 if (streq(name, "VTNr"))
583 else if (streq(name, "Leader"))
584 i->leader = (pid_t) u;
589 case DBUS_TYPE_BOOLEAN: {
592 dbus_message_iter_get_basic(iter, &b);
594 if (streq(name, "Remote"))
596 else if (streq(name, "Active"))
602 case DBUS_TYPE_UINT64: {
605 dbus_message_iter_get_basic(iter, &u);
607 if (streq(name, "Timestamp"))
608 i->timestamp = (usec_t) u;
613 case DBUS_TYPE_STRUCT: {
616 dbus_message_iter_recurse(iter, &sub);
618 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
621 dbus_message_iter_get_basic(&sub, &u);
624 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
627 dbus_message_iter_get_basic(&sub, &s);
640 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
645 switch (dbus_message_iter_get_arg_type(iter)) {
647 case DBUS_TYPE_STRING: {
650 dbus_message_iter_get_basic(iter, &s);
653 if (streq(name, "Name"))
655 else if (streq(name, "ControlGroupPath"))
656 i->control_group = s;
657 else if (streq(name, "State"))
663 case DBUS_TYPE_UINT32: {
666 dbus_message_iter_get_basic(iter, &u);
668 if (streq(name, "UID"))
674 case DBUS_TYPE_UINT64: {
677 dbus_message_iter_get_basic(iter, &u);
679 if (streq(name, "Timestamp"))
680 i->timestamp = (usec_t) u;
685 case DBUS_TYPE_STRUCT: {
688 dbus_message_iter_recurse(iter, &sub);
690 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
693 dbus_message_iter_get_basic(&sub, &s);
702 case DBUS_TYPE_ARRAY: {
704 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
705 DBusMessageIter sub, sub2;
707 dbus_message_iter_recurse(iter, &sub);
708 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
712 dbus_message_iter_recurse(&sub, &sub2);
714 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
715 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
718 l = strv_append(i->sessions, id);
722 strv_free(i->sessions);
726 dbus_message_iter_next(&sub);
737 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
742 switch (dbus_message_iter_get_arg_type(iter)) {
744 case DBUS_TYPE_STRING: {
747 dbus_message_iter_get_basic(iter, &s);
750 if (streq(name, "Id"))
756 case DBUS_TYPE_STRUCT: {
759 dbus_message_iter_recurse(iter, &sub);
761 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
764 dbus_message_iter_get_basic(&sub, &s);
767 i->active_session = s;
773 case DBUS_TYPE_ARRAY: {
775 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
776 DBusMessageIter sub, sub2;
778 dbus_message_iter_recurse(iter, &sub);
779 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
783 dbus_message_iter_recurse(&sub, &sub2);
785 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
786 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
789 l = strv_append(i->sessions, id);
793 strv_free(i->sessions);
797 dbus_message_iter_next(&sub);
808 static int print_property(const char *name, DBusMessageIter *iter) {
812 if (arg_property && !strv_find(arg_property, name))
815 switch (dbus_message_iter_get_arg_type(iter)) {
817 case DBUS_TYPE_STRUCT: {
820 dbus_message_iter_recurse(iter, &sub);
822 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
823 (streq(name, "Display") || streq(name, "ActiveSession"))) {
826 dbus_message_iter_get_basic(&sub, &s);
828 if (arg_all || !isempty(s))
829 printf("%s=%s\n", name, s);
835 case DBUS_TYPE_ARRAY:
837 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
838 DBusMessageIter sub, sub2;
841 dbus_message_iter_recurse(iter, &sub);
842 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
846 dbus_message_iter_recurse(&sub, &sub2);
848 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
849 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
853 printf("%s=%s", name, id);
858 dbus_message_iter_next(&sub);
861 if (!found && arg_all)
862 printf("%s=\n", name);
872 if (generic_print_property(name, iter, arg_all) > 0)
876 printf("%s=[unprintable]\n", name);
881 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
882 DBusMessage *m = NULL, *reply = NULL;
883 const char *interface = "";
886 DBusMessageIter iter, sub, sub2, sub3;
887 SessionStatusInfo session_info;
888 UserStatusInfo user_info;
889 SeatStatusInfo seat_info;
899 dbus_error_init(&error);
901 m = dbus_message_new_method_call(
902 "org.freedesktop.login1",
904 "org.freedesktop.DBus.Properties",
907 log_error("Could not allocate message.");
912 if (!dbus_message_append_args(m,
913 DBUS_TYPE_STRING, &interface,
914 DBUS_TYPE_INVALID)) {
915 log_error("Could not append arguments to message.");
920 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
922 log_error("Failed to issue method call: %s", bus_error_message(&error));
927 if (!dbus_message_iter_init(reply, &iter) ||
928 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
929 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
930 log_error("Failed to parse reply.");
935 dbus_message_iter_recurse(&iter, &sub);
942 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
945 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
946 log_error("Failed to parse reply.");
951 dbus_message_iter_recurse(&sub, &sub2);
953 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
954 log_error("Failed to parse reply.");
959 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
960 log_error("Failed to parse reply.");
965 dbus_message_iter_recurse(&sub2, &sub3);
968 r = print_property(name, &sub3);
969 else if (strstr(verb, "session"))
970 r = status_property_session(name, &sub3, &session_info);
971 else if (strstr(verb, "user"))
972 r = status_property_user(name, &sub3, &user_info);
974 r = status_property_seat(name, &sub3, &seat_info);
977 log_error("Failed to parse reply.");
982 dbus_message_iter_next(&sub);
985 if (!show_properties) {
986 if (strstr(verb, "session"))
987 print_session_status_info(&session_info);
988 else if (strstr(verb, "user"))
989 print_user_status_info(&user_info);
991 print_seat_status_info(&seat_info);
994 strv_free(seat_info.sessions);
995 strv_free(user_info.sessions);
1001 dbus_message_unref(m);
1004 dbus_message_unref(reply);
1006 dbus_error_free(&error);
1011 static int show(DBusConnection *bus, char **args, unsigned n) {
1012 DBusMessage *m = NULL, *reply = NULL;
1016 bool show_properties, new_line = false;
1021 dbus_error_init(&error);
1023 show_properties = !strstr(args[0], "status");
1025 if (show_properties)
1026 pager_open_if_enabled();
1028 if (show_properties && n <= 1) {
1029 /* If not argument is specified inspect the manager
1032 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
1036 for (i = 1; i < n; i++) {
1037 const char *path = NULL;
1039 if (strstr(args[0], "session")) {
1041 m = dbus_message_new_method_call(
1042 "org.freedesktop.login1",
1043 "/org/freedesktop/login1",
1044 "org.freedesktop.login1.Manager",
1047 log_error("Could not allocate message.");
1052 if (!dbus_message_append_args(m,
1053 DBUS_TYPE_STRING, &args[i],
1054 DBUS_TYPE_INVALID)) {
1055 log_error("Could not append arguments to message.");
1060 } else if (strstr(args[0], "user")) {
1063 if (safe_atou(args[i], &uid) < 0) {
1066 pw = getpwnam(args[i]);
1068 log_error("User %s unknown.", args[i]);
1076 m = dbus_message_new_method_call(
1077 "org.freedesktop.login1",
1078 "/org/freedesktop/login1",
1079 "org.freedesktop.login1.Manager",
1082 log_error("Could not allocate message.");
1087 if (!dbus_message_append_args(m,
1088 DBUS_TYPE_UINT32, &uid,
1089 DBUS_TYPE_INVALID)) {
1090 log_error("Could not append arguments to message.");
1096 m = dbus_message_new_method_call(
1097 "org.freedesktop.login1",
1098 "/org/freedesktop/login1",
1099 "org.freedesktop.login1.Manager",
1102 log_error("Could not allocate message.");
1107 if (!dbus_message_append_args(m,
1108 DBUS_TYPE_STRING, &args[i],
1109 DBUS_TYPE_INVALID)) {
1110 log_error("Could not append arguments to message.");
1116 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1118 log_error("Failed to issue method call: %s", bus_error_message(&error));
1123 if (!dbus_message_get_args(reply, &error,
1124 DBUS_TYPE_OBJECT_PATH, &path,
1125 DBUS_TYPE_INVALID)) {
1126 log_error("Failed to parse reply: %s", bus_error_message(&error));
1131 r = show_one(args[0], bus, path, show_properties, &new_line);
1135 dbus_message_unref(m);
1136 dbus_message_unref(reply);
1142 dbus_message_unref(m);
1145 dbus_message_unref(reply);
1147 dbus_error_free(&error);
1152 static int activate(DBusConnection *bus, char **args, unsigned n) {
1153 DBusMessage *m = NULL, *reply = NULL;
1161 dbus_error_init(&error);
1163 for (i = 1; i < n; i++) {
1164 m = dbus_message_new_method_call(
1165 "org.freedesktop.login1",
1166 "/org/freedesktop/login1",
1167 "org.freedesktop.login1.Manager",
1168 streq(args[0], "lock-session") ? "LockSession" :
1169 streq(args[0], "unlock-session") ? "UnlockSession" :
1170 streq(args[0], "terminate-session") ? "TerminateSession" :
1173 log_error("Could not allocate message.");
1178 if (!dbus_message_append_args(m,
1179 DBUS_TYPE_STRING, &args[i],
1180 DBUS_TYPE_INVALID)) {
1181 log_error("Could not append arguments to message.");
1186 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1188 log_error("Failed to issue method call: %s", bus_error_message(&error));
1193 dbus_message_unref(m);
1194 dbus_message_unref(reply);
1200 dbus_message_unref(m);
1203 dbus_message_unref(reply);
1205 dbus_error_free(&error);
1210 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1211 DBusMessage *m = NULL, *reply = NULL;
1219 dbus_error_init(&error);
1222 arg_kill_who = "all";
1224 for (i = 1; i < n; i++) {
1225 m = dbus_message_new_method_call(
1226 "org.freedesktop.login1",
1227 "/org/freedesktop/login1",
1228 "org.freedesktop.login1.Manager",
1231 log_error("Could not allocate message.");
1236 if (!dbus_message_append_args(m,
1237 DBUS_TYPE_STRING, &args[i],
1238 DBUS_TYPE_STRING, &arg_kill_who,
1239 DBUS_TYPE_INT32, arg_signal,
1240 DBUS_TYPE_INVALID)) {
1241 log_error("Could not append arguments to message.");
1246 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1248 log_error("Failed to issue method call: %s", bus_error_message(&error));
1253 dbus_message_unref(m);
1254 dbus_message_unref(reply);
1260 dbus_message_unref(m);
1263 dbus_message_unref(reply);
1265 dbus_error_free(&error);
1270 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1271 DBusMessage *m = NULL, *reply = NULL;
1275 dbus_bool_t b, interactive = true;
1280 dbus_error_init(&error);
1282 b = streq(args[0], "enable-linger");
1284 for (i = 1; i < n; i++) {
1287 m = dbus_message_new_method_call(
1288 "org.freedesktop.login1",
1289 "/org/freedesktop/login1",
1290 "org.freedesktop.login1.Manager",
1293 log_error("Could not allocate message.");
1298 if (safe_atou32(args[i], &uid) < 0) {
1302 pw = getpwnam(args[i]);
1304 ret = errno ? -errno : -ENOENT;
1305 log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1312 if (!dbus_message_append_args(m,
1313 DBUS_TYPE_UINT32, &uid,
1314 DBUS_TYPE_BOOLEAN, &b,
1315 DBUS_TYPE_BOOLEAN, &interactive,
1316 DBUS_TYPE_INVALID)) {
1317 log_error("Could not append arguments to message.");
1322 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1324 log_error("Failed to issue method call: %s", bus_error_message(&error));
1329 dbus_message_unref(m);
1330 dbus_message_unref(reply);
1336 dbus_message_unref(m);
1339 dbus_message_unref(reply);
1341 dbus_error_free(&error);
1346 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1347 DBusMessage *m = NULL, *reply = NULL;
1355 dbus_error_init(&error);
1357 for (i = 1; i < n; i++) {
1360 m = dbus_message_new_method_call(
1361 "org.freedesktop.login1",
1362 "/org/freedesktop/login1",
1363 "org.freedesktop.login1.Manager",
1366 log_error("Could not allocate message.");
1371 if (safe_atou32(args[i], &u) < 0) {
1375 pw = getpwnam(args[i]);
1377 ret = errno ? -errno : -ENOENT;
1378 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1385 if (!dbus_message_append_args(m,
1386 DBUS_TYPE_UINT32, &u,
1387 DBUS_TYPE_INVALID)) {
1388 log_error("Could not append arguments to message.");
1393 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1395 log_error("Failed to issue method call: %s", bus_error_message(&error));
1400 dbus_message_unref(m);
1401 dbus_message_unref(reply);
1407 dbus_message_unref(m);
1410 dbus_message_unref(reply);
1412 dbus_error_free(&error);
1417 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1418 DBusMessage *m = NULL, *reply = NULL;
1426 dbus_error_init(&error);
1429 arg_kill_who = "all";
1431 for (i = 1; i < n; i++) {
1434 m = dbus_message_new_method_call(
1435 "org.freedesktop.login1",
1436 "/org/freedesktop/login1",
1437 "org.freedesktop.login1.Manager",
1440 log_error("Could not allocate message.");
1445 if (safe_atou32(args[i], &u) < 0) {
1449 pw = getpwnam(args[i]);
1451 ret = errno ? -errno : -ENOENT;
1452 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1459 if (!dbus_message_append_args(m,
1460 DBUS_TYPE_UINT32, &u,
1461 DBUS_TYPE_INT32, arg_signal,
1462 DBUS_TYPE_INVALID)) {
1463 log_error("Could not append arguments to message.");
1468 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1470 log_error("Failed to issue method call: %s", bus_error_message(&error));
1475 dbus_message_unref(m);
1476 dbus_message_unref(reply);
1482 dbus_message_unref(m);
1485 dbus_message_unref(reply);
1487 dbus_error_free(&error);
1492 static int attach(DBusConnection *bus, char **args, unsigned n) {
1493 DBusMessage *m = NULL, *reply = NULL;
1497 dbus_bool_t interactive = true;
1502 dbus_error_init(&error);
1504 for (i = 2; i < n; i++) {
1505 m = dbus_message_new_method_call(
1506 "org.freedesktop.login1",
1507 "/org/freedesktop/login1",
1508 "org.freedesktop.login1.Manager",
1511 log_error("Could not allocate message.");
1516 if (!dbus_message_append_args(m,
1517 DBUS_TYPE_STRING, &args[1],
1518 DBUS_TYPE_STRING, &args[i],
1519 DBUS_TYPE_BOOLEAN, &interactive,
1520 DBUS_TYPE_INVALID)) {
1521 log_error("Could not append arguments to message.");
1526 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1528 log_error("Failed to issue method call: %s", bus_error_message(&error));
1533 dbus_message_unref(m);
1534 dbus_message_unref(reply);
1540 dbus_message_unref(m);
1543 dbus_message_unref(reply);
1545 dbus_error_free(&error);
1550 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1551 DBusMessage *m = NULL, *reply = NULL;
1554 dbus_bool_t interactive = true;
1559 dbus_error_init(&error);
1561 m = dbus_message_new_method_call(
1562 "org.freedesktop.login1",
1563 "/org/freedesktop/login1",
1564 "org.freedesktop.login1.Manager",
1567 log_error("Could not allocate message.");
1572 if (!dbus_message_append_args(m,
1573 DBUS_TYPE_BOOLEAN, &interactive,
1574 DBUS_TYPE_INVALID)) {
1575 log_error("Could not append arguments to message.");
1580 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1582 log_error("Failed to issue method call: %s", bus_error_message(&error));
1589 dbus_message_unref(m);
1592 dbus_message_unref(reply);
1594 dbus_error_free(&error);
1599 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1600 DBusMessage *m = NULL, *reply = NULL;
1608 dbus_error_init(&error);
1610 for (i = 1; i < n; i++) {
1611 m = dbus_message_new_method_call(
1612 "org.freedesktop.login1",
1613 "/org/freedesktop/login1",
1614 "org.freedesktop.login1.Manager",
1617 log_error("Could not allocate message.");
1622 if (!dbus_message_append_args(m,
1623 DBUS_TYPE_STRING, &args[i],
1624 DBUS_TYPE_INVALID)) {
1625 log_error("Could not append arguments to message.");
1630 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1632 log_error("Failed to issue method call: %s", bus_error_message(&error));
1637 dbus_message_unref(m);
1638 dbus_message_unref(reply);
1644 dbus_message_unref(m);
1647 dbus_message_unref(reply);
1649 dbus_error_free(&error);
1654 static int help(void) {
1656 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1657 "Send control commands to or query the login manager.\n\n"
1658 " -h --help Show this help\n"
1659 " --version Show package version\n"
1660 " -p --property=NAME Show only properties by this name\n"
1661 " -a --all Show all properties, including empty ones\n"
1662 " --kill-who=WHO Who to send signal to\n"
1663 " -s --signal=SIGNAL Which signal to send\n"
1664 " -H --host=[USER@]HOST\n"
1665 " Show information for remote host\n"
1666 " -P --privileged Acquire privileges before execution\n"
1667 " --no-pager Do not pipe output into a pager\n\n"
1669 " list-sessions List sessions\n"
1670 " session-status [ID...] Show session status\n"
1671 " show-session [ID...] Show properties of one or more sessions\n"
1672 " activate [ID] Activate a session\n"
1673 " lock-session [ID...] Screen lock one or more sessions\n"
1674 " unlock-session [ID...] Screen unlock one or more sessions\n"
1675 " terminate-session [ID...] Terminate one or more sessions\n"
1676 " kill-session [ID...] Send signal to processes of a session\n"
1677 " list-users List users\n"
1678 " user-status [USER...] Show user status\n"
1679 " show-user [USER...] Show properties of one or more users\n"
1680 " enable-linger [USER...] Enable linger state of one or more users\n"
1681 " disable-linger [USER...] Disable linger state of one or more users\n"
1682 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1683 " kill-user [USER...] Send signal to processes of a user\n"
1684 " list-seats List seats\n"
1685 " seat-status [NAME...] Show seat status\n"
1686 " show-seat [NAME...] Show properties of one or more seats\n"
1687 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1688 " flush-devices Flush all device associations\n"
1689 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1690 program_invocation_short_name);
1695 static int parse_argv(int argc, char *argv[]) {
1698 ARG_VERSION = 0x100,
1703 static const struct option options[] = {
1704 { "help", no_argument, NULL, 'h' },
1705 { "version", no_argument, NULL, ARG_VERSION },
1706 { "property", required_argument, NULL, 'p' },
1707 { "all", no_argument, NULL, 'a' },
1708 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1709 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1710 { "signal", required_argument, NULL, 's' },
1711 { "host", required_argument, NULL, 'H' },
1712 { "privileged",no_argument, NULL, 'P' },
1713 { NULL, 0, NULL, 0 }
1721 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1730 puts(PACKAGE_STRING);
1732 puts(SYSTEMD_FEATURES);
1738 l = strv_append(arg_property, optarg);
1742 strv_free(arg_property);
1745 /* If the user asked for a particular
1746 * property, show it to him, even if it is
1757 arg_no_pager = true;
1761 arg_kill_who = optarg;
1765 arg_signal = signal_from_string_try_harder(optarg);
1766 if (arg_signal < 0) {
1767 log_error("Failed to parse signal string %s.", optarg);
1773 arg_transport = TRANSPORT_POLKIT;
1777 arg_transport = TRANSPORT_SSH;
1785 log_error("Unknown option code %c", c);
1793 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1795 static const struct {
1803 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1805 { "list-sessions", LESS, 1, list_sessions },
1806 { "session-status", MORE, 2, show },
1807 { "show-session", MORE, 1, show },
1808 { "activate", EQUAL, 2, activate },
1809 { "lock-session", MORE, 2, activate },
1810 { "unlock-session", MORE, 2, activate },
1811 { "terminate-session", MORE, 2, activate },
1812 { "kill-session", MORE, 2, kill_session },
1813 { "list-users", EQUAL, 1, list_users },
1814 { "user-status", MORE, 2, show },
1815 { "show-user", MORE, 1, show },
1816 { "enable-linger", MORE, 2, enable_linger },
1817 { "disable-linger", MORE, 2, enable_linger },
1818 { "terminate-user", MORE, 2, terminate_user },
1819 { "kill-user", MORE, 2, kill_user },
1820 { "list-seats", EQUAL, 1, list_seats },
1821 { "seat-status", MORE, 2, show },
1822 { "show-seat", MORE, 1, show },
1823 { "attach", MORE, 3, attach },
1824 { "flush-devices", EQUAL, 1, flush_devices },
1825 { "terminate-seat", MORE, 2, terminate_seat },
1835 left = argc - optind;
1838 /* Special rule: no arguments means "list-sessions" */
1841 if (streq(argv[optind], "help")) {
1846 for (i = 0; i < ELEMENTSOF(verbs); i++)
1847 if (streq(argv[optind], verbs[i].verb))
1850 if (i >= ELEMENTSOF(verbs)) {
1851 log_error("Unknown operation %s", argv[optind]);
1856 switch (verbs[i].argc_cmp) {
1859 if (left != verbs[i].argc) {
1860 log_error("Invalid number of arguments.");
1867 if (left < verbs[i].argc) {
1868 log_error("Too few arguments.");
1875 if (left > verbs[i].argc) {
1876 log_error("Too many arguments.");
1883 assert_not_reached("Unknown comparison operator.");
1887 log_error("Failed to get D-Bus connection: %s", error->message);
1891 return verbs[i].dispatch(bus, argv + optind, left);
1894 int main(int argc, char*argv[]) {
1895 int r, retval = EXIT_FAILURE;
1896 DBusConnection *bus = NULL;
1899 dbus_error_init(&error);
1901 log_parse_environment();
1904 r = parse_argv(argc, argv);
1908 retval = EXIT_SUCCESS;
1912 if (arg_transport == TRANSPORT_NORMAL)
1913 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1914 else if (arg_transport == TRANSPORT_POLKIT)
1915 bus_connect_system_polkit(&bus, &error);
1916 else if (arg_transport == TRANSPORT_SSH)
1917 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1919 assert_not_reached("Uh, invalid transport...");
1921 r = loginctl_main(bus, argc, argv, &error);
1922 retval = r < 0 ? EXIT_FAILURE : r;
1926 dbus_connection_flush(bus);
1927 dbus_connection_close(bus);
1928 dbus_connection_unref(bus);
1931 dbus_error_free(&error);
1934 strv_free(arg_property);