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) {
68 /* Cache result before we open the pager */
75 static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
76 DBusMessage *m = NULL, *reply = NULL;
79 DBusMessageIter iter, sub, sub2;
82 dbus_error_init(&error);
86 pager_open_if_enabled();
88 m = dbus_message_new_method_call(
89 "org.freedesktop.login1",
90 "/org/freedesktop/login1",
91 "org.freedesktop.login1.Manager",
94 log_error("Could not allocate message.");
98 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
100 log_error("Failed to issue method call: %s", bus_error_message(&error));
105 if (!dbus_message_iter_init(reply, &iter) ||
106 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
107 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
108 log_error("Failed to parse reply.");
113 dbus_message_iter_recurse(&iter, &sub);
116 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
118 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
119 const char *id, *user, *seat, *object;
122 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
123 log_error("Failed to parse reply.");
128 dbus_message_iter_recurse(&sub, &sub2);
130 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
131 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
132 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
133 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
134 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
135 log_error("Failed to parse reply.");
140 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
144 dbus_message_iter_next(&sub);
148 printf("\n%u sessions listed.\n", k);
154 dbus_message_unref(m);
157 dbus_message_unref(reply);
159 dbus_error_free(&error);
164 static int list_users(DBusConnection *bus, char **args, unsigned n) {
165 DBusMessage *m = NULL, *reply = NULL;
168 DBusMessageIter iter, sub, sub2;
171 dbus_error_init(&error);
175 pager_open_if_enabled();
177 m = dbus_message_new_method_call(
178 "org.freedesktop.login1",
179 "/org/freedesktop/login1",
180 "org.freedesktop.login1.Manager",
183 log_error("Could not allocate message.");
187 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
189 log_error("Failed to issue method call: %s", bus_error_message(&error));
194 if (!dbus_message_iter_init(reply, &iter) ||
195 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
196 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
197 log_error("Failed to parse reply.");
202 dbus_message_iter_recurse(&iter, &sub);
205 printf("%10s %-16s\n", "UID", "USER");
207 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
208 const char *user, *object;
211 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
212 log_error("Failed to parse reply.");
217 dbus_message_iter_recurse(&sub, &sub2);
219 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
220 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
221 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
222 log_error("Failed to parse reply.");
227 printf("%10u %-16s\n", (unsigned) uid, user);
231 dbus_message_iter_next(&sub);
235 printf("\n%u users listed.\n", k);
241 dbus_message_unref(m);
244 dbus_message_unref(reply);
246 dbus_error_free(&error);
251 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
252 DBusMessage *m = NULL, *reply = NULL;
255 DBusMessageIter iter, sub, sub2;
258 dbus_error_init(&error);
262 pager_open_if_enabled();
264 m = dbus_message_new_method_call(
265 "org.freedesktop.login1",
266 "/org/freedesktop/login1",
267 "org.freedesktop.login1.Manager",
270 log_error("Could not allocate message.");
274 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
276 log_error("Failed to issue method call: %s", bus_error_message(&error));
281 if (!dbus_message_iter_init(reply, &iter) ||
282 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
283 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
284 log_error("Failed to parse reply.");
289 dbus_message_iter_recurse(&iter, &sub);
292 printf("%-16s\n", "SEAT");
294 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
295 const char *seat, *object;
297 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
298 log_error("Failed to parse reply.");
303 dbus_message_iter_recurse(&sub, &sub2);
305 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
306 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
307 log_error("Failed to parse reply.");
312 printf("%-16s\n", seat);
316 dbus_message_iter_next(&sub);
320 printf("\n%u seats listed.\n", k);
326 dbus_message_unref(m);
329 dbus_message_unref(reply);
331 dbus_error_free(&error);
336 typedef struct SessionStatusInfo {
341 const char *control_group;
347 const char *remote_host;
348 const char *remote_user;
355 typedef struct UserStatusInfo {
359 const char *control_group;
365 typedef struct SeatStatusInfo {
367 const char *active_session;
371 static void print_session_status_info(SessionStatusInfo *i) {
372 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
373 char since2[FORMAT_TIMESTAMP_MAX], *s2;
376 printf("%s - ", strna(i->id));
379 printf("%s (%u)\n", i->name, (unsigned) i->uid);
381 printf("%u\n", (unsigned) i->uid);
383 s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
384 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
387 printf("\t Since: %s; %s\n", s2, s1);
389 printf("\t Since: %s\n", s2);
394 printf("\t Leader: %u", (unsigned) i->leader);
396 get_process_comm(i->leader, &t);
406 printf("\t Seat: %s", i->seat);
409 printf("; vc%i", i->vtnr);
415 printf("\t TTY: %s\n", i->tty);
417 printf("\t Display: %s\n", i->display);
419 if (i->remote_host && i->remote_user)
420 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
421 else if (i->remote_host)
422 printf("\t Remote: %s\n", i->remote_host);
423 else if (i->remote_user)
424 printf("\t Remote: user %s\n", i->remote_user);
426 printf("\t Remote: Yes\n");
429 printf("\t Service: %s", i->service);
432 printf("; type %s", i->type);
436 printf("\t Type: %s\n", i->type);
438 printf("\t Active: %s\n", yes_no(i->active));
440 if (i->control_group) {
443 printf("\t CGroup: %s\n", i->control_group);
445 if (arg_transport != TRANSPORT_SSH) {
452 show_cgroup_by_path(i->control_group, "\t\t ", c);
457 static void print_user_status_info(UserStatusInfo *i) {
458 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
459 char since2[FORMAT_TIMESTAMP_MAX], *s2;
463 printf("%s (%u)\n", i->name, (unsigned) i->uid);
465 printf("%u\n", (unsigned) i->uid);
467 s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
468 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
471 printf("\t Since: %s; %s\n", s2, s1);
473 printf("\t Since: %s\n", s2);
475 if (!isempty(i->state))
476 printf("\t State: %s\n", i->state);
478 if (!strv_isempty(i->sessions)) {
480 printf("\tSessions:");
482 STRV_FOREACH(l, i->sessions) {
483 if (streq_ptr(*l, i->display))
492 if (i->control_group) {
495 printf("\t CGroup: %s\n", i->control_group);
497 if (arg_transport != TRANSPORT_SSH) {
504 show_cgroup_by_path(i->control_group, "\t\t ", c);
509 static void print_seat_status_info(SeatStatusInfo *i) {
512 printf("%s\n", strna(i->id));
514 if (!strv_isempty(i->sessions)) {
516 printf("\tSessions:");
518 STRV_FOREACH(l, i->sessions) {
519 if (streq_ptr(*l, i->active_session))
528 if (arg_transport != TRANSPORT_SSH) {
537 printf("\t Devices:\n");
539 show_sysfs(i->id, "\t\t ", c);
543 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
548 switch (dbus_message_iter_get_arg_type(iter)) {
550 case DBUS_TYPE_STRING: {
553 dbus_message_iter_get_basic(iter, &s);
556 if (streq(name, "Id"))
558 else if (streq(name, "Name"))
560 else if (streq(name, "ControlGroupPath"))
561 i->control_group = s;
562 else if (streq(name, "TTY"))
564 else if (streq(name, "Display"))
566 else if (streq(name, "RemoteHost"))
568 else if (streq(name, "RemoteUser"))
570 else if (streq(name, "Service"))
572 else if (streq(name, "Type"))
578 case DBUS_TYPE_UINT32: {
581 dbus_message_iter_get_basic(iter, &u);
583 if (streq(name, "VTNr"))
585 else if (streq(name, "Leader"))
586 i->leader = (pid_t) u;
591 case DBUS_TYPE_BOOLEAN: {
594 dbus_message_iter_get_basic(iter, &b);
596 if (streq(name, "Remote"))
598 else if (streq(name, "Active"))
604 case DBUS_TYPE_UINT64: {
607 dbus_message_iter_get_basic(iter, &u);
609 if (streq(name, "Timestamp"))
610 i->timestamp = (usec_t) u;
615 case DBUS_TYPE_STRUCT: {
618 dbus_message_iter_recurse(iter, &sub);
620 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
623 dbus_message_iter_get_basic(&sub, &u);
626 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
629 dbus_message_iter_get_basic(&sub, &s);
642 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
647 switch (dbus_message_iter_get_arg_type(iter)) {
649 case DBUS_TYPE_STRING: {
652 dbus_message_iter_get_basic(iter, &s);
655 if (streq(name, "Name"))
657 else if (streq(name, "ControlGroupPath"))
658 i->control_group = s;
659 else if (streq(name, "State"))
665 case DBUS_TYPE_UINT32: {
668 dbus_message_iter_get_basic(iter, &u);
670 if (streq(name, "UID"))
676 case DBUS_TYPE_UINT64: {
679 dbus_message_iter_get_basic(iter, &u);
681 if (streq(name, "Timestamp"))
682 i->timestamp = (usec_t) u;
687 case DBUS_TYPE_STRUCT: {
690 dbus_message_iter_recurse(iter, &sub);
692 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
695 dbus_message_iter_get_basic(&sub, &s);
704 case DBUS_TYPE_ARRAY: {
706 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
707 DBusMessageIter sub, sub2;
709 dbus_message_iter_recurse(iter, &sub);
710 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
714 dbus_message_iter_recurse(&sub, &sub2);
716 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
717 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
720 l = strv_append(i->sessions, id);
724 strv_free(i->sessions);
728 dbus_message_iter_next(&sub);
739 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
744 switch (dbus_message_iter_get_arg_type(iter)) {
746 case DBUS_TYPE_STRING: {
749 dbus_message_iter_get_basic(iter, &s);
752 if (streq(name, "Id"))
758 case DBUS_TYPE_STRUCT: {
761 dbus_message_iter_recurse(iter, &sub);
763 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
766 dbus_message_iter_get_basic(&sub, &s);
769 i->active_session = s;
775 case DBUS_TYPE_ARRAY: {
777 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
778 DBusMessageIter sub, sub2;
780 dbus_message_iter_recurse(iter, &sub);
781 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
785 dbus_message_iter_recurse(&sub, &sub2);
787 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
788 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
791 l = strv_append(i->sessions, id);
795 strv_free(i->sessions);
799 dbus_message_iter_next(&sub);
810 static int print_property(const char *name, DBusMessageIter *iter) {
814 if (arg_property && !strv_find(arg_property, name))
817 switch (dbus_message_iter_get_arg_type(iter)) {
819 case DBUS_TYPE_STRUCT: {
822 dbus_message_iter_recurse(iter, &sub);
824 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
825 (streq(name, "Display") || streq(name, "ActiveSession"))) {
828 dbus_message_iter_get_basic(&sub, &s);
830 if (arg_all || !isempty(s))
831 printf("%s=%s\n", name, s);
837 case DBUS_TYPE_ARRAY:
839 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
840 DBusMessageIter sub, sub2;
843 dbus_message_iter_recurse(iter, &sub);
844 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
848 dbus_message_iter_recurse(&sub, &sub2);
850 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
851 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
855 printf("%s=%s", name, id);
860 dbus_message_iter_next(&sub);
863 if (!found && arg_all)
864 printf("%s=\n", name);
874 if (generic_print_property(name, iter, arg_all) > 0)
878 printf("%s=[unprintable]\n", name);
883 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
884 DBusMessage *m = NULL, *reply = NULL;
885 const char *interface = "";
888 DBusMessageIter iter, sub, sub2, sub3;
889 SessionStatusInfo session_info;
890 UserStatusInfo user_info;
891 SeatStatusInfo seat_info;
901 dbus_error_init(&error);
903 m = dbus_message_new_method_call(
904 "org.freedesktop.login1",
906 "org.freedesktop.DBus.Properties",
909 log_error("Could not allocate message.");
914 if (!dbus_message_append_args(m,
915 DBUS_TYPE_STRING, &interface,
916 DBUS_TYPE_INVALID)) {
917 log_error("Could not append arguments to message.");
922 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
924 log_error("Failed to issue method call: %s", bus_error_message(&error));
929 if (!dbus_message_iter_init(reply, &iter) ||
930 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
931 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
932 log_error("Failed to parse reply.");
937 dbus_message_iter_recurse(&iter, &sub);
944 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
947 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
948 log_error("Failed to parse reply.");
953 dbus_message_iter_recurse(&sub, &sub2);
955 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
956 log_error("Failed to parse reply.");
961 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
962 log_error("Failed to parse reply.");
967 dbus_message_iter_recurse(&sub2, &sub3);
970 r = print_property(name, &sub3);
971 else if (strstr(verb, "session"))
972 r = status_property_session(name, &sub3, &session_info);
973 else if (strstr(verb, "user"))
974 r = status_property_user(name, &sub3, &user_info);
976 r = status_property_seat(name, &sub3, &seat_info);
979 log_error("Failed to parse reply.");
984 dbus_message_iter_next(&sub);
987 if (!show_properties) {
988 if (strstr(verb, "session"))
989 print_session_status_info(&session_info);
990 else if (strstr(verb, "user"))
991 print_user_status_info(&user_info);
993 print_seat_status_info(&seat_info);
996 strv_free(seat_info.sessions);
997 strv_free(user_info.sessions);
1003 dbus_message_unref(m);
1006 dbus_message_unref(reply);
1008 dbus_error_free(&error);
1013 static int show(DBusConnection *bus, char **args, unsigned n) {
1014 DBusMessage *m = NULL, *reply = NULL;
1018 bool show_properties, new_line = false;
1023 dbus_error_init(&error);
1025 show_properties = !strstr(args[0], "status");
1027 if (show_properties)
1028 pager_open_if_enabled();
1030 if (show_properties && n <= 1) {
1031 /* If not argument is specified inspect the manager
1034 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
1038 for (i = 1; i < n; i++) {
1039 const char *path = NULL;
1041 if (strstr(args[0], "session")) {
1043 m = dbus_message_new_method_call(
1044 "org.freedesktop.login1",
1045 "/org/freedesktop/login1",
1046 "org.freedesktop.login1.Manager",
1049 log_error("Could not allocate message.");
1054 if (!dbus_message_append_args(m,
1055 DBUS_TYPE_STRING, &args[i],
1056 DBUS_TYPE_INVALID)) {
1057 log_error("Could not append arguments to message.");
1062 } else if (strstr(args[0], "user")) {
1066 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1068 log_error("User %s unknown.", args[i]);
1072 m = dbus_message_new_method_call(
1073 "org.freedesktop.login1",
1074 "/org/freedesktop/login1",
1075 "org.freedesktop.login1.Manager",
1078 log_error("Could not allocate message.");
1084 if (!dbus_message_append_args(m,
1085 DBUS_TYPE_UINT32, &u,
1086 DBUS_TYPE_INVALID)) {
1087 log_error("Could not append arguments to message.");
1093 m = dbus_message_new_method_call(
1094 "org.freedesktop.login1",
1095 "/org/freedesktop/login1",
1096 "org.freedesktop.login1.Manager",
1099 log_error("Could not allocate message.");
1104 if (!dbus_message_append_args(m,
1105 DBUS_TYPE_STRING, &args[i],
1106 DBUS_TYPE_INVALID)) {
1107 log_error("Could not append arguments to message.");
1113 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1115 log_error("Failed to issue method call: %s", bus_error_message(&error));
1120 if (!dbus_message_get_args(reply, &error,
1121 DBUS_TYPE_OBJECT_PATH, &path,
1122 DBUS_TYPE_INVALID)) {
1123 log_error("Failed to parse reply: %s", bus_error_message(&error));
1128 r = show_one(args[0], bus, path, show_properties, &new_line);
1132 dbus_message_unref(m);
1133 dbus_message_unref(reply);
1139 dbus_message_unref(m);
1142 dbus_message_unref(reply);
1144 dbus_error_free(&error);
1149 static int activate(DBusConnection *bus, char **args, unsigned n) {
1150 DBusMessage *m = NULL;
1158 dbus_error_init(&error);
1160 for (i = 1; i < n; i++) {
1163 m = dbus_message_new_method_call(
1164 "org.freedesktop.login1",
1165 "/org/freedesktop/login1",
1166 "org.freedesktop.login1.Manager",
1167 streq(args[0], "lock-session") ? "LockSession" :
1168 streq(args[0], "unlock-session") ? "UnlockSession" :
1169 streq(args[0], "terminate-session") ? "TerminateSession" :
1172 log_error("Could not allocate message.");
1177 if (!dbus_message_append_args(m,
1178 DBUS_TYPE_STRING, &args[i],
1179 DBUS_TYPE_INVALID)) {
1180 log_error("Could not append arguments to message.");
1185 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1187 log_error("Failed to issue method call: %s", bus_error_message(&error));
1192 dbus_message_unref(m);
1193 dbus_message_unref(reply);
1199 dbus_message_unref(m);
1201 dbus_error_free(&error);
1206 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1207 DBusMessage *m = NULL;
1215 dbus_error_init(&error);
1218 arg_kill_who = "all";
1220 for (i = 1; i < n; i++) {
1223 m = dbus_message_new_method_call(
1224 "org.freedesktop.login1",
1225 "/org/freedesktop/login1",
1226 "org.freedesktop.login1.Manager",
1229 log_error("Could not allocate message.");
1234 if (!dbus_message_append_args(m,
1235 DBUS_TYPE_STRING, &args[i],
1236 DBUS_TYPE_STRING, &arg_kill_who,
1237 DBUS_TYPE_INT32, arg_signal,
1238 DBUS_TYPE_INVALID)) {
1239 log_error("Could not append arguments to message.");
1244 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1246 log_error("Failed to issue method call: %s", bus_error_message(&error));
1251 dbus_message_unref(m);
1252 dbus_message_unref(reply);
1258 dbus_message_unref(m);
1260 dbus_error_free(&error);
1265 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1266 DBusMessage *m = NULL;
1270 dbus_bool_t b, interactive = true;
1275 dbus_error_init(&error);
1277 b = streq(args[0], "enable-linger");
1279 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);
1330 dbus_error_free(&error);
1335 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1336 DBusMessage *m = NULL;
1344 dbus_error_init(&error);
1346 for (i = 1; i < n; i++) {
1351 m = dbus_message_new_method_call(
1352 "org.freedesktop.login1",
1353 "/org/freedesktop/login1",
1354 "org.freedesktop.login1.Manager",
1357 log_error("Could not allocate message.");
1362 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1364 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1369 if (!dbus_message_append_args(m,
1370 DBUS_TYPE_UINT32, &u,
1371 DBUS_TYPE_INVALID)) {
1372 log_error("Could not append arguments to message.");
1377 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1379 log_error("Failed to issue method call: %s", bus_error_message(&error));
1384 dbus_message_unref(m);
1385 dbus_message_unref(reply);
1393 dbus_message_unref(m);
1395 dbus_error_free(&error);
1400 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1401 DBusMessage *m = NULL;
1409 dbus_error_init(&error);
1412 arg_kill_who = "all";
1414 for (i = 1; i < n; i++) {
1419 m = dbus_message_new_method_call(
1420 "org.freedesktop.login1",
1421 "/org/freedesktop/login1",
1422 "org.freedesktop.login1.Manager",
1425 log_error("Could not allocate message.");
1430 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1432 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1437 if (!dbus_message_append_args(m,
1438 DBUS_TYPE_UINT32, &u,
1439 DBUS_TYPE_INT32, arg_signal,
1440 DBUS_TYPE_INVALID)) {
1441 log_error("Could not append arguments to message.");
1446 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1448 log_error("Failed to issue method call: %s", bus_error_message(&error));
1453 dbus_message_unref(m);
1454 dbus_message_unref(reply);
1462 dbus_message_unref(m);
1464 dbus_error_free(&error);
1469 static int attach(DBusConnection *bus, char **args, unsigned n) {
1470 DBusMessage *m = NULL;
1474 dbus_bool_t interactive = true;
1479 dbus_error_init(&error);
1481 for (i = 2; i < n; i++) {
1484 m = dbus_message_new_method_call(
1485 "org.freedesktop.login1",
1486 "/org/freedesktop/login1",
1487 "org.freedesktop.login1.Manager",
1490 log_error("Could not allocate message.");
1495 if (!dbus_message_append_args(m,
1496 DBUS_TYPE_STRING, &args[1],
1497 DBUS_TYPE_STRING, &args[i],
1498 DBUS_TYPE_BOOLEAN, &interactive,
1499 DBUS_TYPE_INVALID)) {
1500 log_error("Could not append arguments to message.");
1505 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1507 log_error("Failed to issue method call: %s", bus_error_message(&error));
1512 dbus_message_unref(m);
1513 dbus_message_unref(reply);
1519 dbus_message_unref(m);
1521 dbus_error_free(&error);
1526 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1527 DBusMessage *m = NULL, *reply = NULL;
1530 dbus_bool_t interactive = true;
1535 dbus_error_init(&error);
1537 m = dbus_message_new_method_call(
1538 "org.freedesktop.login1",
1539 "/org/freedesktop/login1",
1540 "org.freedesktop.login1.Manager",
1543 log_error("Could not allocate message.");
1548 if (!dbus_message_append_args(m,
1549 DBUS_TYPE_BOOLEAN, &interactive,
1550 DBUS_TYPE_INVALID)) {
1551 log_error("Could not append arguments to message.");
1556 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1558 log_error("Failed to issue method call: %s", bus_error_message(&error));
1565 dbus_message_unref(m);
1568 dbus_message_unref(reply);
1570 dbus_error_free(&error);
1575 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1576 DBusMessage *m = NULL;
1584 dbus_error_init(&error);
1586 for (i = 1; i < n; i++) {
1589 m = dbus_message_new_method_call(
1590 "org.freedesktop.login1",
1591 "/org/freedesktop/login1",
1592 "org.freedesktop.login1.Manager",
1595 log_error("Could not allocate message.");
1600 if (!dbus_message_append_args(m,
1601 DBUS_TYPE_STRING, &args[i],
1602 DBUS_TYPE_INVALID)) {
1603 log_error("Could not append arguments to message.");
1608 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1610 log_error("Failed to issue method call: %s", bus_error_message(&error));
1615 dbus_message_unref(m);
1616 dbus_message_unref(reply);
1622 dbus_message_unref(m);
1624 dbus_error_free(&error);
1629 static int help(void) {
1631 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1632 "Send control commands to or query the login manager.\n\n"
1633 " -h --help Show this help\n"
1634 " --version Show package version\n"
1635 " -p --property=NAME Show only properties by this name\n"
1636 " -a --all Show all properties, including empty ones\n"
1637 " --kill-who=WHO Who to send signal to\n"
1638 " -s --signal=SIGNAL Which signal to send\n"
1639 " -H --host=[USER@]HOST\n"
1640 " Show information for remote host\n"
1641 " -P --privileged Acquire privileges before execution\n"
1642 " --no-pager Do not pipe output into a pager\n\n"
1644 " list-sessions List sessions\n"
1645 " session-status [ID...] Show session status\n"
1646 " show-session [ID...] Show properties of one or more sessions\n"
1647 " activate [ID] Activate a session\n"
1648 " lock-session [ID...] Screen lock one or more sessions\n"
1649 " unlock-session [ID...] Screen unlock one or more sessions\n"
1650 " terminate-session [ID...] Terminate one or more sessions\n"
1651 " kill-session [ID...] Send signal to processes of a session\n"
1652 " list-users List users\n"
1653 " user-status [USER...] Show user status\n"
1654 " show-user [USER...] Show properties of one or more users\n"
1655 " enable-linger [USER...] Enable linger state of one or more users\n"
1656 " disable-linger [USER...] Disable linger state of one or more users\n"
1657 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1658 " kill-user [USER...] Send signal to processes of a user\n"
1659 " list-seats List seats\n"
1660 " seat-status [NAME...] Show seat status\n"
1661 " show-seat [NAME...] Show properties of one or more seats\n"
1662 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1663 " flush-devices Flush all device associations\n"
1664 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1665 program_invocation_short_name);
1670 static int parse_argv(int argc, char *argv[]) {
1673 ARG_VERSION = 0x100,
1678 static const struct option options[] = {
1679 { "help", no_argument, NULL, 'h' },
1680 { "version", no_argument, NULL, ARG_VERSION },
1681 { "property", required_argument, NULL, 'p' },
1682 { "all", no_argument, NULL, 'a' },
1683 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1684 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1685 { "signal", required_argument, NULL, 's' },
1686 { "host", required_argument, NULL, 'H' },
1687 { "privileged",no_argument, NULL, 'P' },
1688 { NULL, 0, NULL, 0 }
1696 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1705 puts(PACKAGE_STRING);
1707 puts(SYSTEMD_FEATURES);
1713 l = strv_append(arg_property, optarg);
1717 strv_free(arg_property);
1720 /* If the user asked for a particular
1721 * property, show it to him, even if it is
1732 arg_no_pager = true;
1736 arg_kill_who = optarg;
1740 arg_signal = signal_from_string_try_harder(optarg);
1741 if (arg_signal < 0) {
1742 log_error("Failed to parse signal string %s.", optarg);
1748 arg_transport = TRANSPORT_POLKIT;
1752 arg_transport = TRANSPORT_SSH;
1760 log_error("Unknown option code %c", c);
1768 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1770 static const struct {
1778 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1780 { "list-sessions", LESS, 1, list_sessions },
1781 { "session-status", MORE, 2, show },
1782 { "show-session", MORE, 1, show },
1783 { "activate", EQUAL, 2, activate },
1784 { "lock-session", MORE, 2, activate },
1785 { "unlock-session", MORE, 2, activate },
1786 { "terminate-session", MORE, 2, activate },
1787 { "kill-session", MORE, 2, kill_session },
1788 { "list-users", EQUAL, 1, list_users },
1789 { "user-status", MORE, 2, show },
1790 { "show-user", MORE, 1, show },
1791 { "enable-linger", MORE, 2, enable_linger },
1792 { "disable-linger", MORE, 2, enable_linger },
1793 { "terminate-user", MORE, 2, terminate_user },
1794 { "kill-user", MORE, 2, kill_user },
1795 { "list-seats", EQUAL, 1, list_seats },
1796 { "seat-status", MORE, 2, show },
1797 { "show-seat", MORE, 1, show },
1798 { "attach", MORE, 3, attach },
1799 { "flush-devices", EQUAL, 1, flush_devices },
1800 { "terminate-seat", MORE, 2, terminate_seat },
1810 left = argc - optind;
1813 /* Special rule: no arguments means "list-sessions" */
1816 if (streq(argv[optind], "help")) {
1821 for (i = 0; i < ELEMENTSOF(verbs); i++)
1822 if (streq(argv[optind], verbs[i].verb))
1825 if (i >= ELEMENTSOF(verbs)) {
1826 log_error("Unknown operation %s", argv[optind]);
1831 switch (verbs[i].argc_cmp) {
1834 if (left != verbs[i].argc) {
1835 log_error("Invalid number of arguments.");
1842 if (left < verbs[i].argc) {
1843 log_error("Too few arguments.");
1850 if (left > verbs[i].argc) {
1851 log_error("Too many arguments.");
1858 assert_not_reached("Unknown comparison operator.");
1862 log_error("Failed to get D-Bus connection: %s", error->message);
1866 return verbs[i].dispatch(bus, argv + optind, left);
1869 int main(int argc, char*argv[]) {
1870 int r, retval = EXIT_FAILURE;
1871 DBusConnection *bus = NULL;
1874 dbus_error_init(&error);
1876 log_parse_environment();
1879 r = parse_argv(argc, argv);
1883 retval = EXIT_SUCCESS;
1887 if (arg_transport == TRANSPORT_NORMAL)
1888 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1889 else if (arg_transport == TRANSPORT_POLKIT)
1890 bus_connect_system_polkit(&bus, &error);
1891 else if (arg_transport == TRANSPORT_SSH)
1892 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1894 assert_not_reached("Uh, invalid transport...");
1896 r = loginctl_main(bus, argc, argv, &error);
1897 retval = r < 0 ? EXIT_FAILURE : r;
1901 dbus_connection_flush(bus);
1902 dbus_connection_close(bus);
1903 dbus_connection_unref(bus);
1906 dbus_error_free(&error);
1909 strv_free(arg_property);