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")) {
1064 if (parse_uid(args[i], &uid) < 0) {
1067 pw = getpwnam(args[i]);
1069 log_error("User %s unknown.", args[i]);
1077 m = dbus_message_new_method_call(
1078 "org.freedesktop.login1",
1079 "/org/freedesktop/login1",
1080 "org.freedesktop.login1.Manager",
1083 log_error("Could not allocate message.");
1089 if (!dbus_message_append_args(m,
1090 DBUS_TYPE_UINT32, &u,
1091 DBUS_TYPE_INVALID)) {
1092 log_error("Could not append arguments to message.");
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.");
1109 if (!dbus_message_append_args(m,
1110 DBUS_TYPE_STRING, &args[i],
1111 DBUS_TYPE_INVALID)) {
1112 log_error("Could not append arguments to message.");
1118 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1120 log_error("Failed to issue method call: %s", bus_error_message(&error));
1125 if (!dbus_message_get_args(reply, &error,
1126 DBUS_TYPE_OBJECT_PATH, &path,
1127 DBUS_TYPE_INVALID)) {
1128 log_error("Failed to parse reply: %s", bus_error_message(&error));
1133 r = show_one(args[0], bus, path, show_properties, &new_line);
1137 dbus_message_unref(m);
1138 dbus_message_unref(reply);
1144 dbus_message_unref(m);
1147 dbus_message_unref(reply);
1149 dbus_error_free(&error);
1154 static int activate(DBusConnection *bus, char **args, unsigned n) {
1155 DBusMessage *m = NULL, *reply = NULL;
1163 dbus_error_init(&error);
1165 for (i = 1; i < n; i++) {
1166 m = dbus_message_new_method_call(
1167 "org.freedesktop.login1",
1168 "/org/freedesktop/login1",
1169 "org.freedesktop.login1.Manager",
1170 streq(args[0], "lock-session") ? "LockSession" :
1171 streq(args[0], "unlock-session") ? "UnlockSession" :
1172 streq(args[0], "terminate-session") ? "TerminateSession" :
1175 log_error("Could not allocate message.");
1180 if (!dbus_message_append_args(m,
1181 DBUS_TYPE_STRING, &args[i],
1182 DBUS_TYPE_INVALID)) {
1183 log_error("Could not append arguments to message.");
1188 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1190 log_error("Failed to issue method call: %s", bus_error_message(&error));
1195 dbus_message_unref(m);
1196 dbus_message_unref(reply);
1202 dbus_message_unref(m);
1205 dbus_message_unref(reply);
1207 dbus_error_free(&error);
1212 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1213 DBusMessage *m = NULL, *reply = NULL;
1221 dbus_error_init(&error);
1224 arg_kill_who = "all";
1226 for (i = 1; i < n; i++) {
1227 m = dbus_message_new_method_call(
1228 "org.freedesktop.login1",
1229 "/org/freedesktop/login1",
1230 "org.freedesktop.login1.Manager",
1233 log_error("Could not allocate message.");
1238 if (!dbus_message_append_args(m,
1239 DBUS_TYPE_STRING, &args[i],
1240 DBUS_TYPE_STRING, &arg_kill_who,
1241 DBUS_TYPE_INT32, arg_signal,
1242 DBUS_TYPE_INVALID)) {
1243 log_error("Could not append arguments to message.");
1248 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1250 log_error("Failed to issue method call: %s", bus_error_message(&error));
1255 dbus_message_unref(m);
1256 dbus_message_unref(reply);
1262 dbus_message_unref(m);
1265 dbus_message_unref(reply);
1267 dbus_error_free(&error);
1272 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1273 DBusMessage *m = NULL, *reply = NULL;
1277 dbus_bool_t b, interactive = true;
1282 dbus_error_init(&error);
1284 b = streq(args[0], "enable-linger");
1286 for (i = 1; i < n; i++) {
1290 m = dbus_message_new_method_call(
1291 "org.freedesktop.login1",
1292 "/org/freedesktop/login1",
1293 "org.freedesktop.login1.Manager",
1296 log_error("Could not allocate message.");
1301 if (parse_uid(args[i], &uid) < 0) {
1305 pw = getpwnam(args[i]);
1307 ret = errno ? -errno : -ENOENT;
1308 log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1316 if (!dbus_message_append_args(m,
1317 DBUS_TYPE_UINT32, &u,
1318 DBUS_TYPE_BOOLEAN, &b,
1319 DBUS_TYPE_BOOLEAN, &interactive,
1320 DBUS_TYPE_INVALID)) {
1321 log_error("Could not append arguments to message.");
1326 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1328 log_error("Failed to issue method call: %s", bus_error_message(&error));
1333 dbus_message_unref(m);
1334 dbus_message_unref(reply);
1340 dbus_message_unref(m);
1343 dbus_message_unref(reply);
1345 dbus_error_free(&error);
1350 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1351 DBusMessage *m = NULL, *reply = NULL;
1359 dbus_error_init(&error);
1361 for (i = 1; i < n; i++) {
1365 m = dbus_message_new_method_call(
1366 "org.freedesktop.login1",
1367 "/org/freedesktop/login1",
1368 "org.freedesktop.login1.Manager",
1371 log_error("Could not allocate message.");
1376 if (parse_uid(args[i], &uid) < 0) {
1380 pw = getpwnam(args[i]);
1382 ret = errno ? -errno : -ENOENT;
1383 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1391 if (!dbus_message_append_args(m,
1392 DBUS_TYPE_UINT32, &u,
1393 DBUS_TYPE_INVALID)) {
1394 log_error("Could not append arguments to message.");
1399 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1401 log_error("Failed to issue method call: %s", bus_error_message(&error));
1406 dbus_message_unref(m);
1407 dbus_message_unref(reply);
1413 dbus_message_unref(m);
1416 dbus_message_unref(reply);
1418 dbus_error_free(&error);
1423 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1424 DBusMessage *m = NULL, *reply = NULL;
1432 dbus_error_init(&error);
1435 arg_kill_who = "all";
1437 for (i = 1; i < n; i++) {
1441 m = dbus_message_new_method_call(
1442 "org.freedesktop.login1",
1443 "/org/freedesktop/login1",
1444 "org.freedesktop.login1.Manager",
1447 log_error("Could not allocate message.");
1452 if (parse_uid(args[i], &uid) < 0) {
1456 pw = getpwnam(args[i]);
1458 ret = errno ? -errno : -ENOENT;
1459 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1467 if (!dbus_message_append_args(m,
1468 DBUS_TYPE_UINT32, &u,
1469 DBUS_TYPE_INT32, arg_signal,
1470 DBUS_TYPE_INVALID)) {
1471 log_error("Could not append arguments to message.");
1476 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1478 log_error("Failed to issue method call: %s", bus_error_message(&error));
1483 dbus_message_unref(m);
1484 dbus_message_unref(reply);
1490 dbus_message_unref(m);
1493 dbus_message_unref(reply);
1495 dbus_error_free(&error);
1500 static int attach(DBusConnection *bus, char **args, unsigned n) {
1501 DBusMessage *m = NULL, *reply = NULL;
1505 dbus_bool_t interactive = true;
1510 dbus_error_init(&error);
1512 for (i = 2; i < n; i++) {
1513 m = dbus_message_new_method_call(
1514 "org.freedesktop.login1",
1515 "/org/freedesktop/login1",
1516 "org.freedesktop.login1.Manager",
1519 log_error("Could not allocate message.");
1524 if (!dbus_message_append_args(m,
1525 DBUS_TYPE_STRING, &args[1],
1526 DBUS_TYPE_STRING, &args[i],
1527 DBUS_TYPE_BOOLEAN, &interactive,
1528 DBUS_TYPE_INVALID)) {
1529 log_error("Could not append arguments to message.");
1534 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1536 log_error("Failed to issue method call: %s", bus_error_message(&error));
1541 dbus_message_unref(m);
1542 dbus_message_unref(reply);
1548 dbus_message_unref(m);
1551 dbus_message_unref(reply);
1553 dbus_error_free(&error);
1558 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1559 DBusMessage *m = NULL, *reply = NULL;
1562 dbus_bool_t interactive = true;
1567 dbus_error_init(&error);
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, *reply = NULL;
1616 dbus_error_init(&error);
1618 for (i = 1; i < n; i++) {
1619 m = dbus_message_new_method_call(
1620 "org.freedesktop.login1",
1621 "/org/freedesktop/login1",
1622 "org.freedesktop.login1.Manager",
1625 log_error("Could not allocate message.");
1630 if (!dbus_message_append_args(m,
1631 DBUS_TYPE_STRING, &args[i],
1632 DBUS_TYPE_INVALID)) {
1633 log_error("Could not append arguments to message.");
1638 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1640 log_error("Failed to issue method call: %s", bus_error_message(&error));
1645 dbus_message_unref(m);
1646 dbus_message_unref(reply);
1652 dbus_message_unref(m);
1655 dbus_message_unref(reply);
1657 dbus_error_free(&error);
1662 static int help(void) {
1664 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1665 "Send control commands to or query the login manager.\n\n"
1666 " -h --help Show this help\n"
1667 " --version Show package version\n"
1668 " -p --property=NAME Show only properties by this name\n"
1669 " -a --all Show all properties, including empty ones\n"
1670 " --kill-who=WHO Who to send signal to\n"
1671 " -s --signal=SIGNAL Which signal to send\n"
1672 " -H --host=[USER@]HOST\n"
1673 " Show information for remote host\n"
1674 " -P --privileged Acquire privileges before execution\n"
1675 " --no-pager Do not pipe output into a pager\n\n"
1677 " list-sessions List sessions\n"
1678 " session-status [ID...] Show session status\n"
1679 " show-session [ID...] Show properties of one or more sessions\n"
1680 " activate [ID] Activate a session\n"
1681 " lock-session [ID...] Screen lock one or more sessions\n"
1682 " unlock-session [ID...] Screen unlock one or more sessions\n"
1683 " terminate-session [ID...] Terminate one or more sessions\n"
1684 " kill-session [ID...] Send signal to processes of a session\n"
1685 " list-users List users\n"
1686 " user-status [USER...] Show user status\n"
1687 " show-user [USER...] Show properties of one or more users\n"
1688 " enable-linger [USER...] Enable linger state of one or more users\n"
1689 " disable-linger [USER...] Disable linger state of one or more users\n"
1690 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1691 " kill-user [USER...] Send signal to processes of a user\n"
1692 " list-seats List seats\n"
1693 " seat-status [NAME...] Show seat status\n"
1694 " show-seat [NAME...] Show properties of one or more seats\n"
1695 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1696 " flush-devices Flush all device associations\n"
1697 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1698 program_invocation_short_name);
1703 static int parse_argv(int argc, char *argv[]) {
1706 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 { NULL, 0, NULL, 0 }
1729 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1738 puts(PACKAGE_STRING);
1740 puts(SYSTEMD_FEATURES);
1746 l = strv_append(arg_property, optarg);
1750 strv_free(arg_property);
1753 /* If the user asked for a particular
1754 * property, show it to him, even if it is
1765 arg_no_pager = true;
1769 arg_kill_who = optarg;
1773 arg_signal = signal_from_string_try_harder(optarg);
1774 if (arg_signal < 0) {
1775 log_error("Failed to parse signal string %s.", optarg);
1781 arg_transport = TRANSPORT_POLKIT;
1785 arg_transport = TRANSPORT_SSH;
1793 log_error("Unknown option code %c", c);
1801 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1803 static const struct {
1811 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1813 { "list-sessions", LESS, 1, list_sessions },
1814 { "session-status", MORE, 2, show },
1815 { "show-session", MORE, 1, show },
1816 { "activate", EQUAL, 2, activate },
1817 { "lock-session", MORE, 2, activate },
1818 { "unlock-session", MORE, 2, activate },
1819 { "terminate-session", MORE, 2, activate },
1820 { "kill-session", MORE, 2, kill_session },
1821 { "list-users", EQUAL, 1, list_users },
1822 { "user-status", MORE, 2, show },
1823 { "show-user", MORE, 1, show },
1824 { "enable-linger", MORE, 2, enable_linger },
1825 { "disable-linger", MORE, 2, enable_linger },
1826 { "terminate-user", MORE, 2, terminate_user },
1827 { "kill-user", MORE, 2, kill_user },
1828 { "list-seats", EQUAL, 1, list_seats },
1829 { "seat-status", MORE, 2, show },
1830 { "show-seat", MORE, 1, show },
1831 { "attach", MORE, 3, attach },
1832 { "flush-devices", EQUAL, 1, flush_devices },
1833 { "terminate-seat", MORE, 2, terminate_seat },
1843 left = argc - optind;
1846 /* Special rule: no arguments means "list-sessions" */
1849 if (streq(argv[optind], "help")) {
1854 for (i = 0; i < ELEMENTSOF(verbs); i++)
1855 if (streq(argv[optind], verbs[i].verb))
1858 if (i >= ELEMENTSOF(verbs)) {
1859 log_error("Unknown operation %s", argv[optind]);
1864 switch (verbs[i].argc_cmp) {
1867 if (left != verbs[i].argc) {
1868 log_error("Invalid number of arguments.");
1875 if (left < verbs[i].argc) {
1876 log_error("Too few arguments.");
1883 if (left > verbs[i].argc) {
1884 log_error("Too many arguments.");
1891 assert_not_reached("Unknown comparison operator.");
1895 log_error("Failed to get D-Bus connection: %s", error->message);
1899 return verbs[i].dispatch(bus, argv + optind, left);
1902 int main(int argc, char*argv[]) {
1903 int r, retval = EXIT_FAILURE;
1904 DBusConnection *bus = NULL;
1907 dbus_error_init(&error);
1909 log_parse_environment();
1912 r = parse_argv(argc, argv);
1916 retval = EXIT_SUCCESS;
1920 if (arg_transport == TRANSPORT_NORMAL)
1921 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1922 else if (arg_transport == TRANSPORT_POLKIT)
1923 bus_connect_system_polkit(&bus, &error);
1924 else if (arg_transport == TRANSPORT_SSH)
1925 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1927 assert_not_reached("Uh, invalid transport...");
1929 r = loginctl_main(bus, argc, argv, &error);
1930 retval = r < 0 ? EXIT_FAILURE : r;
1934 dbus_connection_flush(bus);
1935 dbus_connection_close(bus);
1936 dbus_connection_unref(bus);
1939 dbus_error_free(&error);
1942 strv_free(arg_property);