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_name(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 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1068 log_error("User %s unknown.", args[i]);
1073 m = dbus_message_new_method_call(
1074 "org.freedesktop.login1",
1075 "/org/freedesktop/login1",
1076 "org.freedesktop.login1.Manager",
1079 log_error("Could not allocate message.");
1085 if (!dbus_message_append_args(m,
1086 DBUS_TYPE_UINT32, &u,
1087 DBUS_TYPE_INVALID)) {
1088 log_error("Could not append arguments to message.");
1094 m = dbus_message_new_method_call(
1095 "org.freedesktop.login1",
1096 "/org/freedesktop/login1",
1097 "org.freedesktop.login1.Manager",
1100 log_error("Could not allocate message.");
1105 if (!dbus_message_append_args(m,
1106 DBUS_TYPE_STRING, &args[i],
1107 DBUS_TYPE_INVALID)) {
1108 log_error("Could not append arguments to message.");
1114 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1116 log_error("Failed to issue method call: %s", bus_error_message(&error));
1121 if (!dbus_message_get_args(reply, &error,
1122 DBUS_TYPE_OBJECT_PATH, &path,
1123 DBUS_TYPE_INVALID)) {
1124 log_error("Failed to parse reply: %s", bus_error_message(&error));
1129 r = show_one(args[0], bus, path, show_properties, &new_line);
1133 dbus_message_unref(m);
1134 dbus_message_unref(reply);
1140 dbus_message_unref(m);
1143 dbus_message_unref(reply);
1145 dbus_error_free(&error);
1150 static int activate(DBusConnection *bus, char **args, unsigned n) {
1151 DBusMessage *m = NULL;
1159 dbus_error_init(&error);
1161 for (i = 1; i < n; i++) {
1164 m = dbus_message_new_method_call(
1165 "org.freedesktop.login1",
1166 "/org/freedesktop/login1",
1167 "org.freedesktop.login1.Manager",
1168 streq(args[0], "lock-session") ? "LockSession" :
1169 streq(args[0], "unlock-session") ? "UnlockSession" :
1170 streq(args[0], "terminate-session") ? "TerminateSession" :
1173 log_error("Could not allocate message.");
1178 if (!dbus_message_append_args(m,
1179 DBUS_TYPE_STRING, &args[i],
1180 DBUS_TYPE_INVALID)) {
1181 log_error("Could not append arguments to message.");
1186 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1188 log_error("Failed to issue method call: %s", bus_error_message(&error));
1193 dbus_message_unref(m);
1194 dbus_message_unref(reply);
1200 dbus_message_unref(m);
1202 dbus_error_free(&error);
1207 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1208 DBusMessage *m = NULL;
1216 dbus_error_init(&error);
1219 arg_kill_who = "all";
1221 for (i = 1; i < n; i++) {
1224 m = dbus_message_new_method_call(
1225 "org.freedesktop.login1",
1226 "/org/freedesktop/login1",
1227 "org.freedesktop.login1.Manager",
1230 log_error("Could not allocate message.");
1235 if (!dbus_message_append_args(m,
1236 DBUS_TYPE_STRING, &args[i],
1237 DBUS_TYPE_STRING, &arg_kill_who,
1238 DBUS_TYPE_INT32, arg_signal,
1239 DBUS_TYPE_INVALID)) {
1240 log_error("Could not append arguments to message.");
1245 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1247 log_error("Failed to issue method call: %s", bus_error_message(&error));
1252 dbus_message_unref(m);
1253 dbus_message_unref(reply);
1259 dbus_message_unref(m);
1261 dbus_error_free(&error);
1266 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1267 DBusMessage *m = 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++) {
1285 m = dbus_message_new_method_call(
1286 "org.freedesktop.login1",
1287 "/org/freedesktop/login1",
1288 "org.freedesktop.login1.Manager",
1291 log_error("Could not allocate message.");
1296 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1298 log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1303 if (!dbus_message_append_args(m,
1304 DBUS_TYPE_UINT32, &u,
1305 DBUS_TYPE_BOOLEAN, &b,
1306 DBUS_TYPE_BOOLEAN, &interactive,
1307 DBUS_TYPE_INVALID)) {
1308 log_error("Could not append arguments to message.");
1313 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1315 log_error("Failed to issue method call: %s", bus_error_message(&error));
1320 dbus_message_unref(m);
1321 dbus_message_unref(reply);
1329 dbus_message_unref(m);
1331 dbus_error_free(&error);
1336 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1337 DBusMessage *m = NULL;
1345 dbus_error_init(&error);
1347 for (i = 1; i < n; i++) {
1352 m = dbus_message_new_method_call(
1353 "org.freedesktop.login1",
1354 "/org/freedesktop/login1",
1355 "org.freedesktop.login1.Manager",
1358 log_error("Could not allocate message.");
1363 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1365 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1370 if (!dbus_message_append_args(m,
1371 DBUS_TYPE_UINT32, &u,
1372 DBUS_TYPE_INVALID)) {
1373 log_error("Could not append arguments to message.");
1378 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1380 log_error("Failed to issue method call: %s", bus_error_message(&error));
1385 dbus_message_unref(m);
1386 dbus_message_unref(reply);
1394 dbus_message_unref(m);
1396 dbus_error_free(&error);
1401 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1402 DBusMessage *m = NULL;
1410 dbus_error_init(&error);
1413 arg_kill_who = "all";
1415 for (i = 1; i < n; i++) {
1420 m = dbus_message_new_method_call(
1421 "org.freedesktop.login1",
1422 "/org/freedesktop/login1",
1423 "org.freedesktop.login1.Manager",
1426 log_error("Could not allocate message.");
1431 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1433 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1438 if (!dbus_message_append_args(m,
1439 DBUS_TYPE_UINT32, &u,
1440 DBUS_TYPE_INT32, arg_signal,
1441 DBUS_TYPE_INVALID)) {
1442 log_error("Could not append arguments to message.");
1447 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1449 log_error("Failed to issue method call: %s", bus_error_message(&error));
1454 dbus_message_unref(m);
1455 dbus_message_unref(reply);
1463 dbus_message_unref(m);
1465 dbus_error_free(&error);
1470 static int attach(DBusConnection *bus, char **args, unsigned n) {
1471 DBusMessage *m = NULL;
1475 dbus_bool_t interactive = true;
1480 dbus_error_init(&error);
1482 for (i = 2; i < n; i++) {
1485 m = dbus_message_new_method_call(
1486 "org.freedesktop.login1",
1487 "/org/freedesktop/login1",
1488 "org.freedesktop.login1.Manager",
1491 log_error("Could not allocate message.");
1496 if (!dbus_message_append_args(m,
1497 DBUS_TYPE_STRING, &args[1],
1498 DBUS_TYPE_STRING, &args[i],
1499 DBUS_TYPE_BOOLEAN, &interactive,
1500 DBUS_TYPE_INVALID)) {
1501 log_error("Could not append arguments to message.");
1506 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1508 log_error("Failed to issue method call: %s", bus_error_message(&error));
1513 dbus_message_unref(m);
1514 dbus_message_unref(reply);
1520 dbus_message_unref(m);
1522 dbus_error_free(&error);
1527 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1528 DBusMessage *m = NULL, *reply = NULL;
1531 dbus_bool_t interactive = true;
1536 dbus_error_init(&error);
1538 m = dbus_message_new_method_call(
1539 "org.freedesktop.login1",
1540 "/org/freedesktop/login1",
1541 "org.freedesktop.login1.Manager",
1544 log_error("Could not allocate message.");
1549 if (!dbus_message_append_args(m,
1550 DBUS_TYPE_BOOLEAN, &interactive,
1551 DBUS_TYPE_INVALID)) {
1552 log_error("Could not append arguments to message.");
1557 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1559 log_error("Failed to issue method call: %s", bus_error_message(&error));
1566 dbus_message_unref(m);
1569 dbus_message_unref(reply);
1571 dbus_error_free(&error);
1576 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1577 DBusMessage *m = NULL;
1585 dbus_error_init(&error);
1587 for (i = 1; i < n; i++) {
1590 m = dbus_message_new_method_call(
1591 "org.freedesktop.login1",
1592 "/org/freedesktop/login1",
1593 "org.freedesktop.login1.Manager",
1596 log_error("Could not allocate message.");
1601 if (!dbus_message_append_args(m,
1602 DBUS_TYPE_STRING, &args[i],
1603 DBUS_TYPE_INVALID)) {
1604 log_error("Could not append arguments to message.");
1609 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1611 log_error("Failed to issue method call: %s", bus_error_message(&error));
1616 dbus_message_unref(m);
1617 dbus_message_unref(reply);
1623 dbus_message_unref(m);
1625 dbus_error_free(&error);
1630 static int help(void) {
1632 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1633 "Send control commands to or query the login manager.\n\n"
1634 " -h --help Show this help\n"
1635 " --version Show package version\n"
1636 " -p --property=NAME Show only properties by this name\n"
1637 " -a --all Show all properties, including empty ones\n"
1638 " --kill-who=WHO Who to send signal to\n"
1639 " -s --signal=SIGNAL Which signal to send\n"
1640 " -H --host=[USER@]HOST\n"
1641 " Show information for remote host\n"
1642 " -P --privileged Acquire privileges before execution\n"
1643 " --no-pager Do not pipe output into a pager\n\n"
1645 " list-sessions List sessions\n"
1646 " session-status [ID...] Show session status\n"
1647 " show-session [ID...] Show properties of one or more sessions\n"
1648 " activate [ID] Activate a session\n"
1649 " lock-session [ID...] Screen lock one or more sessions\n"
1650 " unlock-session [ID...] Screen unlock one or more sessions\n"
1651 " terminate-session [ID...] Terminate one or more sessions\n"
1652 " kill-session [ID...] Send signal to processes of a session\n"
1653 " list-users List users\n"
1654 " user-status [USER...] Show user status\n"
1655 " show-user [USER...] Show properties of one or more users\n"
1656 " enable-linger [USER...] Enable linger state of one or more users\n"
1657 " disable-linger [USER...] Disable linger state of one or more users\n"
1658 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1659 " kill-user [USER...] Send signal to processes of a user\n"
1660 " list-seats List seats\n"
1661 " seat-status [NAME...] Show seat status\n"
1662 " show-seat [NAME...] Show properties of one or more seats\n"
1663 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1664 " flush-devices Flush all device associations\n"
1665 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1666 program_invocation_short_name);
1671 static int parse_argv(int argc, char *argv[]) {
1674 ARG_VERSION = 0x100,
1679 static const struct option options[] = {
1680 { "help", no_argument, NULL, 'h' },
1681 { "version", no_argument, NULL, ARG_VERSION },
1682 { "property", required_argument, NULL, 'p' },
1683 { "all", no_argument, NULL, 'a' },
1684 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1685 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1686 { "signal", required_argument, NULL, 's' },
1687 { "host", required_argument, NULL, 'H' },
1688 { "privileged",no_argument, NULL, 'P' },
1689 { NULL, 0, NULL, 0 }
1697 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1706 puts(PACKAGE_STRING);
1708 puts(SYSTEMD_FEATURES);
1714 l = strv_append(arg_property, optarg);
1718 strv_free(arg_property);
1721 /* If the user asked for a particular
1722 * property, show it to him, even if it is
1733 arg_no_pager = true;
1737 arg_kill_who = optarg;
1741 arg_signal = signal_from_string_try_harder(optarg);
1742 if (arg_signal < 0) {
1743 log_error("Failed to parse signal string %s.", optarg);
1749 arg_transport = TRANSPORT_POLKIT;
1753 arg_transport = TRANSPORT_SSH;
1761 log_error("Unknown option code %c", c);
1769 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1771 static const struct {
1779 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1781 { "list-sessions", LESS, 1, list_sessions },
1782 { "session-status", MORE, 2, show },
1783 { "show-session", MORE, 1, show },
1784 { "activate", EQUAL, 2, activate },
1785 { "lock-session", MORE, 2, activate },
1786 { "unlock-session", MORE, 2, activate },
1787 { "terminate-session", MORE, 2, activate },
1788 { "kill-session", MORE, 2, kill_session },
1789 { "list-users", EQUAL, 1, list_users },
1790 { "user-status", MORE, 2, show },
1791 { "show-user", MORE, 1, show },
1792 { "enable-linger", MORE, 2, enable_linger },
1793 { "disable-linger", MORE, 2, enable_linger },
1794 { "terminate-user", MORE, 2, terminate_user },
1795 { "kill-user", MORE, 2, kill_user },
1796 { "list-seats", EQUAL, 1, list_seats },
1797 { "seat-status", MORE, 2, show },
1798 { "show-seat", MORE, 1, show },
1799 { "attach", MORE, 3, attach },
1800 { "flush-devices", EQUAL, 1, flush_devices },
1801 { "terminate-seat", MORE, 2, terminate_seat },
1811 left = argc - optind;
1814 /* Special rule: no arguments means "list-sessions" */
1817 if (streq(argv[optind], "help")) {
1822 for (i = 0; i < ELEMENTSOF(verbs); i++)
1823 if (streq(argv[optind], verbs[i].verb))
1826 if (i >= ELEMENTSOF(verbs)) {
1827 log_error("Unknown operation %s", argv[optind]);
1832 switch (verbs[i].argc_cmp) {
1835 if (left != verbs[i].argc) {
1836 log_error("Invalid number of arguments.");
1843 if (left < verbs[i].argc) {
1844 log_error("Too few arguments.");
1851 if (left > verbs[i].argc) {
1852 log_error("Too many arguments.");
1859 assert_not_reached("Unknown comparison operator.");
1863 log_error("Failed to get D-Bus connection: %s", error->message);
1867 return verbs[i].dispatch(bus, argv + optind, left);
1870 int main(int argc, char*argv[]) {
1871 int r, retval = EXIT_FAILURE;
1872 DBusConnection *bus = NULL;
1875 dbus_error_init(&error);
1877 log_parse_environment();
1880 r = parse_argv(argc, argv);
1884 retval = EXIT_SUCCESS;
1888 if (arg_transport == TRANSPORT_NORMAL)
1889 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1890 else if (arg_transport == TRANSPORT_POLKIT)
1891 bus_connect_system_polkit(&bus, &error);
1892 else if (arg_transport == TRANSPORT_SSH)
1893 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1895 assert_not_reached("Uh, invalid transport...");
1897 r = loginctl_main(bus, argc, argv, &error);
1898 retval = r < 0 ? EXIT_FAILURE : r;
1902 dbus_connection_flush(bus);
1903 dbus_connection_close(bus);
1904 dbus_connection_unref(bus);
1907 dbus_error_free(&error);
1910 strv_free(arg_property);