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;
356 typedef struct UserStatusInfo {
360 const char *control_group;
366 typedef struct SeatStatusInfo {
368 const char *active_session;
372 static void print_session_status_info(SessionStatusInfo *i) {
373 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
374 char since2[FORMAT_TIMESTAMP_MAX], *s2;
377 printf("%s - ", strna(i->id));
380 printf("%s (%u)\n", i->name, (unsigned) i->uid);
382 printf("%u\n", (unsigned) i->uid);
384 s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
385 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
388 printf("\t Since: %s; %s\n", s2, s1);
390 printf("\t Since: %s\n", s2);
395 printf("\t Leader: %u", (unsigned) i->leader);
397 get_process_comm(i->leader, &t);
407 printf("\t Seat: %s", i->seat);
410 printf("; vc%i", i->vtnr);
416 printf("\t TTY: %s\n", i->tty);
418 printf("\t Display: %s\n", i->display);
420 if (i->remote_host && i->remote_user)
421 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
422 else if (i->remote_host)
423 printf("\t Remote: %s\n", i->remote_host);
424 else if (i->remote_user)
425 printf("\t Remote: user %s\n", i->remote_user);
427 printf("\t Remote: Yes\n");
430 printf("\t Service: %s", i->service);
433 printf("; type %s", i->type);
436 printf("; class %s", i->class);
439 } else if (i->type) {
440 printf("\t Type: %s\n", i->type);
443 printf("; class %s", i->class);
445 printf("\t Class: %s\n", i->class);
448 printf("\t Active: %s\n", yes_no(i->active));
450 if (i->control_group) {
453 printf("\t CGroup: %s\n", i->control_group);
455 if (arg_transport != TRANSPORT_SSH) {
462 show_cgroup_by_path(i->control_group, "\t\t ", c, false);
467 static void print_user_status_info(UserStatusInfo *i) {
468 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
469 char since2[FORMAT_TIMESTAMP_MAX], *s2;
473 printf("%s (%u)\n", i->name, (unsigned) i->uid);
475 printf("%u\n", (unsigned) i->uid);
477 s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
478 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
481 printf("\t Since: %s; %s\n", s2, s1);
483 printf("\t Since: %s\n", s2);
485 if (!isempty(i->state))
486 printf("\t State: %s\n", i->state);
488 if (!strv_isempty(i->sessions)) {
490 printf("\tSessions:");
492 STRV_FOREACH(l, i->sessions) {
493 if (streq_ptr(*l, i->display))
502 if (i->control_group) {
505 printf("\t CGroup: %s\n", i->control_group);
507 if (arg_transport != TRANSPORT_SSH) {
514 show_cgroup_by_path(i->control_group, "\t\t ", c, false);
519 static void print_seat_status_info(SeatStatusInfo *i) {
522 printf("%s\n", strna(i->id));
524 if (!strv_isempty(i->sessions)) {
526 printf("\tSessions:");
528 STRV_FOREACH(l, i->sessions) {
529 if (streq_ptr(*l, i->active_session))
538 if (arg_transport != TRANSPORT_SSH) {
547 printf("\t Devices:\n");
549 show_sysfs(i->id, "\t\t ", c);
553 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
558 switch (dbus_message_iter_get_arg_type(iter)) {
560 case DBUS_TYPE_STRING: {
563 dbus_message_iter_get_basic(iter, &s);
566 if (streq(name, "Id"))
568 else if (streq(name, "Name"))
570 else if (streq(name, "ControlGroupPath"))
571 i->control_group = s;
572 else if (streq(name, "TTY"))
574 else if (streq(name, "Display"))
576 else if (streq(name, "RemoteHost"))
578 else if (streq(name, "RemoteUser"))
580 else if (streq(name, "Service"))
582 else if (streq(name, "Type"))
584 else if (streq(name, "Class"))
590 case DBUS_TYPE_UINT32: {
593 dbus_message_iter_get_basic(iter, &u);
595 if (streq(name, "VTNr"))
597 else if (streq(name, "Leader"))
598 i->leader = (pid_t) u;
603 case DBUS_TYPE_BOOLEAN: {
606 dbus_message_iter_get_basic(iter, &b);
608 if (streq(name, "Remote"))
610 else if (streq(name, "Active"))
616 case DBUS_TYPE_UINT64: {
619 dbus_message_iter_get_basic(iter, &u);
621 if (streq(name, "Timestamp"))
622 i->timestamp = (usec_t) u;
627 case DBUS_TYPE_STRUCT: {
630 dbus_message_iter_recurse(iter, &sub);
632 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
635 dbus_message_iter_get_basic(&sub, &u);
638 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
641 dbus_message_iter_get_basic(&sub, &s);
654 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
659 switch (dbus_message_iter_get_arg_type(iter)) {
661 case DBUS_TYPE_STRING: {
664 dbus_message_iter_get_basic(iter, &s);
667 if (streq(name, "Name"))
669 else if (streq(name, "ControlGroupPath"))
670 i->control_group = s;
671 else if (streq(name, "State"))
677 case DBUS_TYPE_UINT32: {
680 dbus_message_iter_get_basic(iter, &u);
682 if (streq(name, "UID"))
688 case DBUS_TYPE_UINT64: {
691 dbus_message_iter_get_basic(iter, &u);
693 if (streq(name, "Timestamp"))
694 i->timestamp = (usec_t) u;
699 case DBUS_TYPE_STRUCT: {
702 dbus_message_iter_recurse(iter, &sub);
704 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
707 dbus_message_iter_get_basic(&sub, &s);
716 case DBUS_TYPE_ARRAY: {
718 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
719 DBusMessageIter sub, sub2;
721 dbus_message_iter_recurse(iter, &sub);
722 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
726 dbus_message_iter_recurse(&sub, &sub2);
728 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
729 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
732 l = strv_append(i->sessions, id);
736 strv_free(i->sessions);
740 dbus_message_iter_next(&sub);
751 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
756 switch (dbus_message_iter_get_arg_type(iter)) {
758 case DBUS_TYPE_STRING: {
761 dbus_message_iter_get_basic(iter, &s);
764 if (streq(name, "Id"))
770 case DBUS_TYPE_STRUCT: {
773 dbus_message_iter_recurse(iter, &sub);
775 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
778 dbus_message_iter_get_basic(&sub, &s);
781 i->active_session = s;
787 case DBUS_TYPE_ARRAY: {
789 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
790 DBusMessageIter sub, sub2;
792 dbus_message_iter_recurse(iter, &sub);
793 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
797 dbus_message_iter_recurse(&sub, &sub2);
799 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
800 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
803 l = strv_append(i->sessions, id);
807 strv_free(i->sessions);
811 dbus_message_iter_next(&sub);
822 static int print_property(const char *name, DBusMessageIter *iter) {
826 if (arg_property && !strv_find(arg_property, name))
829 switch (dbus_message_iter_get_arg_type(iter)) {
831 case DBUS_TYPE_STRUCT: {
834 dbus_message_iter_recurse(iter, &sub);
836 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
837 (streq(name, "Display") || streq(name, "ActiveSession"))) {
840 dbus_message_iter_get_basic(&sub, &s);
842 if (arg_all || !isempty(s))
843 printf("%s=%s\n", name, s);
849 case DBUS_TYPE_ARRAY:
851 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
852 DBusMessageIter sub, sub2;
855 dbus_message_iter_recurse(iter, &sub);
856 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
860 dbus_message_iter_recurse(&sub, &sub2);
862 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
863 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
867 printf("%s=%s", name, id);
872 dbus_message_iter_next(&sub);
875 if (!found && arg_all)
876 printf("%s=\n", name);
886 if (generic_print_property(name, iter, arg_all) > 0)
890 printf("%s=[unprintable]\n", name);
895 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
896 DBusMessage *m = NULL, *reply = NULL;
897 const char *interface = "";
900 DBusMessageIter iter, sub, sub2, sub3;
901 SessionStatusInfo session_info;
902 UserStatusInfo user_info;
903 SeatStatusInfo seat_info;
913 dbus_error_init(&error);
915 m = dbus_message_new_method_call(
916 "org.freedesktop.login1",
918 "org.freedesktop.DBus.Properties",
921 log_error("Could not allocate message.");
926 if (!dbus_message_append_args(m,
927 DBUS_TYPE_STRING, &interface,
928 DBUS_TYPE_INVALID)) {
929 log_error("Could not append arguments to message.");
934 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
936 log_error("Failed to issue method call: %s", bus_error_message(&error));
941 if (!dbus_message_iter_init(reply, &iter) ||
942 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
943 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
944 log_error("Failed to parse reply.");
949 dbus_message_iter_recurse(&iter, &sub);
956 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
959 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
960 log_error("Failed to parse reply.");
965 dbus_message_iter_recurse(&sub, &sub2);
967 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
968 log_error("Failed to parse reply.");
973 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
974 log_error("Failed to parse reply.");
979 dbus_message_iter_recurse(&sub2, &sub3);
982 r = print_property(name, &sub3);
983 else if (strstr(verb, "session"))
984 r = status_property_session(name, &sub3, &session_info);
985 else if (strstr(verb, "user"))
986 r = status_property_user(name, &sub3, &user_info);
988 r = status_property_seat(name, &sub3, &seat_info);
991 log_error("Failed to parse reply.");
996 dbus_message_iter_next(&sub);
999 if (!show_properties) {
1000 if (strstr(verb, "session"))
1001 print_session_status_info(&session_info);
1002 else if (strstr(verb, "user"))
1003 print_user_status_info(&user_info);
1005 print_seat_status_info(&seat_info);
1008 strv_free(seat_info.sessions);
1009 strv_free(user_info.sessions);
1015 dbus_message_unref(m);
1018 dbus_message_unref(reply);
1020 dbus_error_free(&error);
1025 static int show(DBusConnection *bus, char **args, unsigned n) {
1026 DBusMessage *m = NULL, *reply = NULL;
1030 bool show_properties, new_line = false;
1035 dbus_error_init(&error);
1037 show_properties = !strstr(args[0], "status");
1039 if (show_properties)
1040 pager_open_if_enabled();
1042 if (show_properties && n <= 1) {
1043 /* If not argument is specified inspect the manager
1046 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
1050 for (i = 1; i < n; i++) {
1051 const char *path = NULL;
1053 if (strstr(args[0], "session")) {
1055 m = dbus_message_new_method_call(
1056 "org.freedesktop.login1",
1057 "/org/freedesktop/login1",
1058 "org.freedesktop.login1.Manager",
1061 log_error("Could not allocate message.");
1066 if (!dbus_message_append_args(m,
1067 DBUS_TYPE_STRING, &args[i],
1068 DBUS_TYPE_INVALID)) {
1069 log_error("Could not append arguments to message.");
1074 } else if (strstr(args[0], "user")) {
1078 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1080 log_error("User %s unknown.", args[i]);
1084 m = dbus_message_new_method_call(
1085 "org.freedesktop.login1",
1086 "/org/freedesktop/login1",
1087 "org.freedesktop.login1.Manager",
1090 log_error("Could not allocate message.");
1096 if (!dbus_message_append_args(m,
1097 DBUS_TYPE_UINT32, &u,
1098 DBUS_TYPE_INVALID)) {
1099 log_error("Could not append arguments to message.");
1105 m = dbus_message_new_method_call(
1106 "org.freedesktop.login1",
1107 "/org/freedesktop/login1",
1108 "org.freedesktop.login1.Manager",
1111 log_error("Could not allocate message.");
1116 if (!dbus_message_append_args(m,
1117 DBUS_TYPE_STRING, &args[i],
1118 DBUS_TYPE_INVALID)) {
1119 log_error("Could not append arguments to message.");
1125 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1127 log_error("Failed to issue method call: %s", bus_error_message(&error));
1132 if (!dbus_message_get_args(reply, &error,
1133 DBUS_TYPE_OBJECT_PATH, &path,
1134 DBUS_TYPE_INVALID)) {
1135 log_error("Failed to parse reply: %s", bus_error_message(&error));
1140 r = show_one(args[0], bus, path, show_properties, &new_line);
1144 dbus_message_unref(m);
1145 dbus_message_unref(reply);
1151 dbus_message_unref(m);
1154 dbus_message_unref(reply);
1156 dbus_error_free(&error);
1161 static int activate(DBusConnection *bus, char **args, unsigned n) {
1162 DBusMessage *m = NULL;
1170 dbus_error_init(&error);
1172 for (i = 1; i < n; i++) {
1175 m = dbus_message_new_method_call(
1176 "org.freedesktop.login1",
1177 "/org/freedesktop/login1",
1178 "org.freedesktop.login1.Manager",
1179 streq(args[0], "lock-session") ? "LockSession" :
1180 streq(args[0], "unlock-session") ? "UnlockSession" :
1181 streq(args[0], "terminate-session") ? "TerminateSession" :
1184 log_error("Could not allocate message.");
1189 if (!dbus_message_append_args(m,
1190 DBUS_TYPE_STRING, &args[i],
1191 DBUS_TYPE_INVALID)) {
1192 log_error("Could not append arguments to message.");
1197 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1199 log_error("Failed to issue method call: %s", bus_error_message(&error));
1204 dbus_message_unref(m);
1205 dbus_message_unref(reply);
1211 dbus_message_unref(m);
1213 dbus_error_free(&error);
1218 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1219 DBusMessage *m = NULL;
1227 dbus_error_init(&error);
1230 arg_kill_who = "all";
1232 for (i = 1; i < n; i++) {
1235 m = dbus_message_new_method_call(
1236 "org.freedesktop.login1",
1237 "/org/freedesktop/login1",
1238 "org.freedesktop.login1.Manager",
1241 log_error("Could not allocate message.");
1246 if (!dbus_message_append_args(m,
1247 DBUS_TYPE_STRING, &args[i],
1248 DBUS_TYPE_STRING, &arg_kill_who,
1249 DBUS_TYPE_INT32, arg_signal,
1250 DBUS_TYPE_INVALID)) {
1251 log_error("Could not append arguments to message.");
1256 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1258 log_error("Failed to issue method call: %s", bus_error_message(&error));
1263 dbus_message_unref(m);
1264 dbus_message_unref(reply);
1270 dbus_message_unref(m);
1272 dbus_error_free(&error);
1277 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1278 DBusMessage *m = NULL;
1282 dbus_bool_t b, interactive = true;
1287 dbus_error_init(&error);
1289 b = streq(args[0], "enable-linger");
1291 for (i = 1; i < n; i++) {
1296 m = dbus_message_new_method_call(
1297 "org.freedesktop.login1",
1298 "/org/freedesktop/login1",
1299 "org.freedesktop.login1.Manager",
1302 log_error("Could not allocate message.");
1307 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1309 log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1314 if (!dbus_message_append_args(m,
1315 DBUS_TYPE_UINT32, &u,
1316 DBUS_TYPE_BOOLEAN, &b,
1317 DBUS_TYPE_BOOLEAN, &interactive,
1318 DBUS_TYPE_INVALID)) {
1319 log_error("Could not append arguments to message.");
1324 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1326 log_error("Failed to issue method call: %s", bus_error_message(&error));
1331 dbus_message_unref(m);
1332 dbus_message_unref(reply);
1340 dbus_message_unref(m);
1342 dbus_error_free(&error);
1347 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1348 DBusMessage *m = NULL;
1356 dbus_error_init(&error);
1358 for (i = 1; i < n; i++) {
1363 m = dbus_message_new_method_call(
1364 "org.freedesktop.login1",
1365 "/org/freedesktop/login1",
1366 "org.freedesktop.login1.Manager",
1369 log_error("Could not allocate message.");
1374 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1376 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1381 if (!dbus_message_append_args(m,
1382 DBUS_TYPE_UINT32, &u,
1383 DBUS_TYPE_INVALID)) {
1384 log_error("Could not append arguments to message.");
1389 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1391 log_error("Failed to issue method call: %s", bus_error_message(&error));
1396 dbus_message_unref(m);
1397 dbus_message_unref(reply);
1405 dbus_message_unref(m);
1407 dbus_error_free(&error);
1412 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1413 DBusMessage *m = NULL;
1421 dbus_error_init(&error);
1424 arg_kill_who = "all";
1426 for (i = 1; i < n; i++) {
1431 m = dbus_message_new_method_call(
1432 "org.freedesktop.login1",
1433 "/org/freedesktop/login1",
1434 "org.freedesktop.login1.Manager",
1437 log_error("Could not allocate message.");
1442 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1444 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1449 if (!dbus_message_append_args(m,
1450 DBUS_TYPE_UINT32, &u,
1451 DBUS_TYPE_INT32, arg_signal,
1452 DBUS_TYPE_INVALID)) {
1453 log_error("Could not append arguments to message.");
1458 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1460 log_error("Failed to issue method call: %s", bus_error_message(&error));
1465 dbus_message_unref(m);
1466 dbus_message_unref(reply);
1474 dbus_message_unref(m);
1476 dbus_error_free(&error);
1481 static int attach(DBusConnection *bus, char **args, unsigned n) {
1482 DBusMessage *m = NULL;
1486 dbus_bool_t interactive = true;
1491 dbus_error_init(&error);
1493 for (i = 2; i < n; i++) {
1496 m = dbus_message_new_method_call(
1497 "org.freedesktop.login1",
1498 "/org/freedesktop/login1",
1499 "org.freedesktop.login1.Manager",
1502 log_error("Could not allocate message.");
1507 if (!dbus_message_append_args(m,
1508 DBUS_TYPE_STRING, &args[1],
1509 DBUS_TYPE_STRING, &args[i],
1510 DBUS_TYPE_BOOLEAN, &interactive,
1511 DBUS_TYPE_INVALID)) {
1512 log_error("Could not append arguments to message.");
1517 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1519 log_error("Failed to issue method call: %s", bus_error_message(&error));
1524 dbus_message_unref(m);
1525 dbus_message_unref(reply);
1531 dbus_message_unref(m);
1533 dbus_error_free(&error);
1538 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1539 DBusMessage *m = NULL, *reply = NULL;
1542 dbus_bool_t interactive = true;
1547 dbus_error_init(&error);
1549 m = dbus_message_new_method_call(
1550 "org.freedesktop.login1",
1551 "/org/freedesktop/login1",
1552 "org.freedesktop.login1.Manager",
1555 log_error("Could not allocate message.");
1560 if (!dbus_message_append_args(m,
1561 DBUS_TYPE_BOOLEAN, &interactive,
1562 DBUS_TYPE_INVALID)) {
1563 log_error("Could not append arguments to message.");
1568 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1570 log_error("Failed to issue method call: %s", bus_error_message(&error));
1577 dbus_message_unref(m);
1580 dbus_message_unref(reply);
1582 dbus_error_free(&error);
1587 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1588 DBusMessage *m = NULL;
1596 dbus_error_init(&error);
1598 for (i = 1; i < n; i++) {
1601 m = dbus_message_new_method_call(
1602 "org.freedesktop.login1",
1603 "/org/freedesktop/login1",
1604 "org.freedesktop.login1.Manager",
1607 log_error("Could not allocate message.");
1612 if (!dbus_message_append_args(m,
1613 DBUS_TYPE_STRING, &args[i],
1614 DBUS_TYPE_INVALID)) {
1615 log_error("Could not append arguments to message.");
1620 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1622 log_error("Failed to issue method call: %s", bus_error_message(&error));
1627 dbus_message_unref(m);
1628 dbus_message_unref(reply);
1634 dbus_message_unref(m);
1636 dbus_error_free(&error);
1641 static int help(void) {
1643 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1644 "Send control commands to or query the login manager.\n\n"
1645 " -h --help Show this help\n"
1646 " --version Show package version\n"
1647 " -p --property=NAME Show only properties by this name\n"
1648 " -a --all Show all properties, including empty ones\n"
1649 " --kill-who=WHO Who to send signal to\n"
1650 " -s --signal=SIGNAL Which signal to send\n"
1651 " -H --host=[USER@]HOST\n"
1652 " Show information for remote host\n"
1653 " -P --privileged Acquire privileges before execution\n"
1654 " --no-pager Do not pipe output into a pager\n\n"
1656 " list-sessions List sessions\n"
1657 " session-status [ID...] Show session status\n"
1658 " show-session [ID...] Show properties of one or more sessions\n"
1659 " activate [ID] Activate a session\n"
1660 " lock-session [ID...] Screen lock one or more sessions\n"
1661 " unlock-session [ID...] Screen unlock one or more sessions\n"
1662 " terminate-session [ID...] Terminate one or more sessions\n"
1663 " kill-session [ID...] Send signal to processes of a session\n"
1664 " list-users List users\n"
1665 " user-status [USER...] Show user status\n"
1666 " show-user [USER...] Show properties of one or more users\n"
1667 " enable-linger [USER...] Enable linger state of one or more users\n"
1668 " disable-linger [USER...] Disable linger state of one or more users\n"
1669 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1670 " kill-user [USER...] Send signal to processes of a user\n"
1671 " list-seats List seats\n"
1672 " seat-status [NAME...] Show seat status\n"
1673 " show-seat [NAME...] Show properties of one or more seats\n"
1674 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1675 " flush-devices Flush all device associations\n"
1676 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1677 program_invocation_short_name);
1682 static int parse_argv(int argc, char *argv[]) {
1685 ARG_VERSION = 0x100,
1690 static const struct option options[] = {
1691 { "help", no_argument, NULL, 'h' },
1692 { "version", no_argument, NULL, ARG_VERSION },
1693 { "property", required_argument, NULL, 'p' },
1694 { "all", no_argument, NULL, 'a' },
1695 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1696 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1697 { "signal", required_argument, NULL, 's' },
1698 { "host", required_argument, NULL, 'H' },
1699 { "privileged",no_argument, NULL, 'P' },
1700 { NULL, 0, NULL, 0 }
1708 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1717 puts(PACKAGE_STRING);
1719 puts(SYSTEMD_FEATURES);
1725 l = strv_append(arg_property, optarg);
1729 strv_free(arg_property);
1732 /* If the user asked for a particular
1733 * property, show it to him, even if it is
1744 arg_no_pager = true;
1748 arg_kill_who = optarg;
1752 arg_signal = signal_from_string_try_harder(optarg);
1753 if (arg_signal < 0) {
1754 log_error("Failed to parse signal string %s.", optarg);
1760 arg_transport = TRANSPORT_POLKIT;
1764 arg_transport = TRANSPORT_SSH;
1772 log_error("Unknown option code %c", c);
1780 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1782 static const struct {
1790 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1792 { "list-sessions", LESS, 1, list_sessions },
1793 { "session-status", MORE, 2, show },
1794 { "show-session", MORE, 1, show },
1795 { "activate", EQUAL, 2, activate },
1796 { "lock-session", MORE, 2, activate },
1797 { "unlock-session", MORE, 2, activate },
1798 { "terminate-session", MORE, 2, activate },
1799 { "kill-session", MORE, 2, kill_session },
1800 { "list-users", EQUAL, 1, list_users },
1801 { "user-status", MORE, 2, show },
1802 { "show-user", MORE, 1, show },
1803 { "enable-linger", MORE, 2, enable_linger },
1804 { "disable-linger", MORE, 2, enable_linger },
1805 { "terminate-user", MORE, 2, terminate_user },
1806 { "kill-user", MORE, 2, kill_user },
1807 { "list-seats", EQUAL, 1, list_seats },
1808 { "seat-status", MORE, 2, show },
1809 { "show-seat", MORE, 1, show },
1810 { "attach", MORE, 3, attach },
1811 { "flush-devices", EQUAL, 1, flush_devices },
1812 { "terminate-seat", MORE, 2, terminate_seat },
1822 left = argc - optind;
1825 /* Special rule: no arguments means "list-sessions" */
1828 if (streq(argv[optind], "help")) {
1833 for (i = 0; i < ELEMENTSOF(verbs); i++)
1834 if (streq(argv[optind], verbs[i].verb))
1837 if (i >= ELEMENTSOF(verbs)) {
1838 log_error("Unknown operation %s", argv[optind]);
1843 switch (verbs[i].argc_cmp) {
1846 if (left != verbs[i].argc) {
1847 log_error("Invalid number of arguments.");
1854 if (left < verbs[i].argc) {
1855 log_error("Too few arguments.");
1862 if (left > verbs[i].argc) {
1863 log_error("Too many arguments.");
1870 assert_not_reached("Unknown comparison operator.");
1874 log_error("Failed to get D-Bus connection: %s", error->message);
1878 return verbs[i].dispatch(bus, argv + optind, left);
1881 int main(int argc, char*argv[]) {
1882 int r, retval = EXIT_FAILURE;
1883 DBusConnection *bus = NULL;
1886 dbus_error_init(&error);
1888 log_parse_environment();
1891 r = parse_argv(argc, argv);
1895 retval = EXIT_SUCCESS;
1899 if (arg_transport == TRANSPORT_NORMAL)
1900 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1901 else if (arg_transport == TRANSPORT_POLKIT)
1902 bus_connect_system_polkit(&bus, &error);
1903 else if (arg_transport == TRANSPORT_SSH)
1904 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1906 assert_not_reached("Uh, invalid transport...");
1908 r = loginctl_main(bus, argc, argv, &error);
1909 retval = r < 0 ? EXIT_FAILURE : r;
1913 dbus_connection_flush(bus);
1914 dbus_connection_close(bus);
1915 dbus_connection_unref(bus);
1918 dbus_error_free(&error);
1921 strv_free(arg_property);