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 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1066 log_error("User %s unknown.", args[i]);
1071 m = dbus_message_new_method_call(
1072 "org.freedesktop.login1",
1073 "/org/freedesktop/login1",
1074 "org.freedesktop.login1.Manager",
1077 log_error("Could not allocate message.");
1083 if (!dbus_message_append_args(m,
1084 DBUS_TYPE_UINT32, &u,
1085 DBUS_TYPE_INVALID)) {
1086 log_error("Could not append arguments to message.");
1092 m = dbus_message_new_method_call(
1093 "org.freedesktop.login1",
1094 "/org/freedesktop/login1",
1095 "org.freedesktop.login1.Manager",
1098 log_error("Could not allocate message.");
1103 if (!dbus_message_append_args(m,
1104 DBUS_TYPE_STRING, &args[i],
1105 DBUS_TYPE_INVALID)) {
1106 log_error("Could not append arguments to message.");
1112 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1114 log_error("Failed to issue method call: %s", bus_error_message(&error));
1119 if (!dbus_message_get_args(reply, &error,
1120 DBUS_TYPE_OBJECT_PATH, &path,
1121 DBUS_TYPE_INVALID)) {
1122 log_error("Failed to parse reply: %s", bus_error_message(&error));
1127 r = show_one(args[0], bus, path, show_properties, &new_line);
1131 dbus_message_unref(m);
1132 dbus_message_unref(reply);
1138 dbus_message_unref(m);
1141 dbus_message_unref(reply);
1143 dbus_error_free(&error);
1148 static int activate(DBusConnection *bus, char **args, unsigned n) {
1149 DBusMessage *m = NULL, *reply = NULL;
1157 dbus_error_init(&error);
1159 for (i = 1; i < n; i++) {
1160 m = dbus_message_new_method_call(
1161 "org.freedesktop.login1",
1162 "/org/freedesktop/login1",
1163 "org.freedesktop.login1.Manager",
1164 streq(args[0], "lock-session") ? "LockSession" :
1165 streq(args[0], "unlock-session") ? "UnlockSession" :
1166 streq(args[0], "terminate-session") ? "TerminateSession" :
1169 log_error("Could not allocate message.");
1174 if (!dbus_message_append_args(m,
1175 DBUS_TYPE_STRING, &args[i],
1176 DBUS_TYPE_INVALID)) {
1177 log_error("Could not append arguments to message.");
1182 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1184 log_error("Failed to issue method call: %s", bus_error_message(&error));
1189 dbus_message_unref(m);
1190 dbus_message_unref(reply);
1196 dbus_message_unref(m);
1199 dbus_message_unref(reply);
1201 dbus_error_free(&error);
1206 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1207 DBusMessage *m = NULL, *reply = NULL;
1215 dbus_error_init(&error);
1218 arg_kill_who = "all";
1220 for (i = 1; i < n; i++) {
1221 m = dbus_message_new_method_call(
1222 "org.freedesktop.login1",
1223 "/org/freedesktop/login1",
1224 "org.freedesktop.login1.Manager",
1227 log_error("Could not allocate message.");
1232 if (!dbus_message_append_args(m,
1233 DBUS_TYPE_STRING, &args[i],
1234 DBUS_TYPE_STRING, &arg_kill_who,
1235 DBUS_TYPE_INT32, arg_signal,
1236 DBUS_TYPE_INVALID)) {
1237 log_error("Could not append arguments to message.");
1242 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1244 log_error("Failed to issue method call: %s", bus_error_message(&error));
1249 dbus_message_unref(m);
1250 dbus_message_unref(reply);
1256 dbus_message_unref(m);
1259 dbus_message_unref(reply);
1261 dbus_error_free(&error);
1266 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1267 DBusMessage *m = NULL, *reply = NULL;
1271 dbus_bool_t b, interactive = true;
1276 dbus_error_init(&error);
1278 b = streq(args[0], "enable-linger");
1280 for (i = 1; i < n; i++) {
1284 m = dbus_message_new_method_call(
1285 "org.freedesktop.login1",
1286 "/org/freedesktop/login1",
1287 "org.freedesktop.login1.Manager",
1290 log_error("Could not allocate message.");
1295 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1297 log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1302 if (!dbus_message_append_args(m,
1303 DBUS_TYPE_UINT32, &u,
1304 DBUS_TYPE_BOOLEAN, &b,
1305 DBUS_TYPE_BOOLEAN, &interactive,
1306 DBUS_TYPE_INVALID)) {
1307 log_error("Could not append arguments to message.");
1312 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1314 log_error("Failed to issue method call: %s", bus_error_message(&error));
1319 dbus_message_unref(m);
1320 dbus_message_unref(reply);
1328 dbus_message_unref(m);
1331 dbus_message_unref(reply);
1333 dbus_error_free(&error);
1338 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1339 DBusMessage *m = NULL, *reply = NULL;
1347 dbus_error_init(&error);
1349 for (i = 1; i < n; i++) {
1353 m = dbus_message_new_method_call(
1354 "org.freedesktop.login1",
1355 "/org/freedesktop/login1",
1356 "org.freedesktop.login1.Manager",
1359 log_error("Could not allocate message.");
1364 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1366 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1371 if (!dbus_message_append_args(m,
1372 DBUS_TYPE_UINT32, &u,
1373 DBUS_TYPE_INVALID)) {
1374 log_error("Could not append arguments to message.");
1379 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1381 log_error("Failed to issue method call: %s", bus_error_message(&error));
1386 dbus_message_unref(m);
1387 dbus_message_unref(reply);
1395 dbus_message_unref(m);
1398 dbus_message_unref(reply);
1400 dbus_error_free(&error);
1405 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1406 DBusMessage *m = NULL, *reply = NULL;
1414 dbus_error_init(&error);
1417 arg_kill_who = "all";
1419 for (i = 1; i < n; i++) {
1423 m = dbus_message_new_method_call(
1424 "org.freedesktop.login1",
1425 "/org/freedesktop/login1",
1426 "org.freedesktop.login1.Manager",
1429 log_error("Could not allocate message.");
1434 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1436 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1441 if (!dbus_message_append_args(m,
1442 DBUS_TYPE_UINT32, &u,
1443 DBUS_TYPE_INT32, arg_signal,
1444 DBUS_TYPE_INVALID)) {
1445 log_error("Could not append arguments to message.");
1450 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1452 log_error("Failed to issue method call: %s", bus_error_message(&error));
1457 dbus_message_unref(m);
1458 dbus_message_unref(reply);
1466 dbus_message_unref(m);
1469 dbus_message_unref(reply);
1471 dbus_error_free(&error);
1476 static int attach(DBusConnection *bus, char **args, unsigned n) {
1477 DBusMessage *m = NULL, *reply = NULL;
1481 dbus_bool_t interactive = true;
1486 dbus_error_init(&error);
1488 for (i = 2; i < n; i++) {
1489 m = dbus_message_new_method_call(
1490 "org.freedesktop.login1",
1491 "/org/freedesktop/login1",
1492 "org.freedesktop.login1.Manager",
1495 log_error("Could not allocate message.");
1500 if (!dbus_message_append_args(m,
1501 DBUS_TYPE_STRING, &args[1],
1502 DBUS_TYPE_STRING, &args[i],
1503 DBUS_TYPE_BOOLEAN, &interactive,
1504 DBUS_TYPE_INVALID)) {
1505 log_error("Could not append arguments to message.");
1510 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1512 log_error("Failed to issue method call: %s", bus_error_message(&error));
1517 dbus_message_unref(m);
1518 dbus_message_unref(reply);
1524 dbus_message_unref(m);
1527 dbus_message_unref(reply);
1529 dbus_error_free(&error);
1534 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1535 DBusMessage *m = NULL, *reply = NULL;
1538 dbus_bool_t interactive = true;
1543 dbus_error_init(&error);
1545 m = dbus_message_new_method_call(
1546 "org.freedesktop.login1",
1547 "/org/freedesktop/login1",
1548 "org.freedesktop.login1.Manager",
1551 log_error("Could not allocate message.");
1556 if (!dbus_message_append_args(m,
1557 DBUS_TYPE_BOOLEAN, &interactive,
1558 DBUS_TYPE_INVALID)) {
1559 log_error("Could not append arguments to message.");
1564 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1566 log_error("Failed to issue method call: %s", bus_error_message(&error));
1573 dbus_message_unref(m);
1576 dbus_message_unref(reply);
1578 dbus_error_free(&error);
1583 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1584 DBusMessage *m = NULL, *reply = NULL;
1592 dbus_error_init(&error);
1594 for (i = 1; i < n; i++) {
1595 m = dbus_message_new_method_call(
1596 "org.freedesktop.login1",
1597 "/org/freedesktop/login1",
1598 "org.freedesktop.login1.Manager",
1601 log_error("Could not allocate message.");
1606 if (!dbus_message_append_args(m,
1607 DBUS_TYPE_STRING, &args[i],
1608 DBUS_TYPE_INVALID)) {
1609 log_error("Could not append arguments to message.");
1614 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1616 log_error("Failed to issue method call: %s", bus_error_message(&error));
1621 dbus_message_unref(m);
1622 dbus_message_unref(reply);
1628 dbus_message_unref(m);
1631 dbus_message_unref(reply);
1633 dbus_error_free(&error);
1638 static int help(void) {
1640 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1641 "Send control commands to or query the login manager.\n\n"
1642 " -h --help Show this help\n"
1643 " --version Show package version\n"
1644 " -p --property=NAME Show only properties by this name\n"
1645 " -a --all Show all properties, including empty ones\n"
1646 " --kill-who=WHO Who to send signal to\n"
1647 " -s --signal=SIGNAL Which signal to send\n"
1648 " -H --host=[USER@]HOST\n"
1649 " Show information for remote host\n"
1650 " -P --privileged Acquire privileges before execution\n"
1651 " --no-pager Do not pipe output into a pager\n\n"
1653 " list-sessions List sessions\n"
1654 " session-status [ID...] Show session status\n"
1655 " show-session [ID...] Show properties of one or more sessions\n"
1656 " activate [ID] Activate a session\n"
1657 " lock-session [ID...] Screen lock one or more sessions\n"
1658 " unlock-session [ID...] Screen unlock one or more sessions\n"
1659 " terminate-session [ID...] Terminate one or more sessions\n"
1660 " kill-session [ID...] Send signal to processes of a session\n"
1661 " list-users List users\n"
1662 " user-status [USER...] Show user status\n"
1663 " show-user [USER...] Show properties of one or more users\n"
1664 " enable-linger [USER...] Enable linger state of one or more users\n"
1665 " disable-linger [USER...] Disable linger state of one or more users\n"
1666 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1667 " kill-user [USER...] Send signal to processes of a user\n"
1668 " list-seats List seats\n"
1669 " seat-status [NAME...] Show seat status\n"
1670 " show-seat [NAME...] Show properties of one or more seats\n"
1671 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1672 " flush-devices Flush all device associations\n"
1673 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1674 program_invocation_short_name);
1679 static int parse_argv(int argc, char *argv[]) {
1682 ARG_VERSION = 0x100,
1687 static const struct option options[] = {
1688 { "help", no_argument, NULL, 'h' },
1689 { "version", no_argument, NULL, ARG_VERSION },
1690 { "property", required_argument, NULL, 'p' },
1691 { "all", no_argument, NULL, 'a' },
1692 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1693 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1694 { "signal", required_argument, NULL, 's' },
1695 { "host", required_argument, NULL, 'H' },
1696 { "privileged",no_argument, NULL, 'P' },
1697 { NULL, 0, NULL, 0 }
1705 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1714 puts(PACKAGE_STRING);
1716 puts(SYSTEMD_FEATURES);
1722 l = strv_append(arg_property, optarg);
1726 strv_free(arg_property);
1729 /* If the user asked for a particular
1730 * property, show it to him, even if it is
1741 arg_no_pager = true;
1745 arg_kill_who = optarg;
1749 arg_signal = signal_from_string_try_harder(optarg);
1750 if (arg_signal < 0) {
1751 log_error("Failed to parse signal string %s.", optarg);
1757 arg_transport = TRANSPORT_POLKIT;
1761 arg_transport = TRANSPORT_SSH;
1769 log_error("Unknown option code %c", c);
1777 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1779 static const struct {
1787 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1789 { "list-sessions", LESS, 1, list_sessions },
1790 { "session-status", MORE, 2, show },
1791 { "show-session", MORE, 1, show },
1792 { "activate", EQUAL, 2, activate },
1793 { "lock-session", MORE, 2, activate },
1794 { "unlock-session", MORE, 2, activate },
1795 { "terminate-session", MORE, 2, activate },
1796 { "kill-session", MORE, 2, kill_session },
1797 { "list-users", EQUAL, 1, list_users },
1798 { "user-status", MORE, 2, show },
1799 { "show-user", MORE, 1, show },
1800 { "enable-linger", MORE, 2, enable_linger },
1801 { "disable-linger", MORE, 2, enable_linger },
1802 { "terminate-user", MORE, 2, terminate_user },
1803 { "kill-user", MORE, 2, kill_user },
1804 { "list-seats", EQUAL, 1, list_seats },
1805 { "seat-status", MORE, 2, show },
1806 { "show-seat", MORE, 1, show },
1807 { "attach", MORE, 3, attach },
1808 { "flush-devices", EQUAL, 1, flush_devices },
1809 { "terminate-seat", MORE, 2, terminate_seat },
1819 left = argc - optind;
1822 /* Special rule: no arguments means "list-sessions" */
1825 if (streq(argv[optind], "help")) {
1830 for (i = 0; i < ELEMENTSOF(verbs); i++)
1831 if (streq(argv[optind], verbs[i].verb))
1834 if (i >= ELEMENTSOF(verbs)) {
1835 log_error("Unknown operation %s", argv[optind]);
1840 switch (verbs[i].argc_cmp) {
1843 if (left != verbs[i].argc) {
1844 log_error("Invalid number of arguments.");
1851 if (left < verbs[i].argc) {
1852 log_error("Too few arguments.");
1859 if (left > verbs[i].argc) {
1860 log_error("Too many arguments.");
1867 assert_not_reached("Unknown comparison operator.");
1871 log_error("Failed to get D-Bus connection: %s", error->message);
1875 return verbs[i].dispatch(bus, argv + optind, left);
1878 int main(int argc, char*argv[]) {
1879 int r, retval = EXIT_FAILURE;
1880 DBusConnection *bus = NULL;
1883 dbus_error_init(&error);
1885 log_parse_environment();
1888 r = parse_argv(argc, argv);
1892 retval = EXIT_SUCCESS;
1896 if (arg_transport == TRANSPORT_NORMAL)
1897 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1898 else if (arg_transport == TRANSPORT_POLKIT)
1899 bus_connect_system_polkit(&bus, &error);
1900 else if (arg_transport == TRANSPORT_SSH)
1901 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1903 assert_not_reached("Uh, invalid transport...");
1905 r = loginctl_main(bus, argc, argv, &error);
1906 retval = r < 0 ? EXIT_FAILURE : r;
1910 dbus_connection_flush(bus);
1911 dbus_connection_close(bus);
1912 dbus_connection_unref(bus);
1915 dbus_error_free(&error);
1918 strv_free(arg_property);