1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <dbus/dbus.h>
33 #include "dbus-common.h"
36 #include "cgroup-show.h"
37 #include "sysfs-show.h"
39 static char **arg_property = NULL;
40 static bool arg_all = false;
41 static bool arg_no_pager = false;
42 static const char *arg_kill_who = NULL;
43 static int arg_signal = SIGTERM;
44 static enum transport {
48 } arg_transport = TRANSPORT_NORMAL;
49 static const char *arg_host = NULL;
51 static bool on_tty(void) {
54 /* Note that this is invoked relatively early, before we start
55 * the pager. That means the value we return reflects whether
56 * we originally were started on a tty, not if we currently
57 * are. But this is intended, since we want colour and so on
58 * when run in our own pager. */
60 if (_unlikely_(t < 0))
61 t = isatty(STDOUT_FILENO) > 0;
66 static void pager_open_if_enabled(void) {
73 static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
74 DBusMessage *m = NULL, *reply = NULL;
77 DBusMessageIter iter, sub, sub2;
80 dbus_error_init(&error);
84 pager_open_if_enabled();
86 m = dbus_message_new_method_call(
87 "org.freedesktop.login1",
88 "/org/freedesktop/login1",
89 "org.freedesktop.login1.Manager",
92 log_error("Could not allocate message.");
96 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
98 log_error("Failed to issue method call: %s", bus_error_message(&error));
103 if (!dbus_message_iter_init(reply, &iter) ||
104 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
105 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
106 log_error("Failed to parse reply.");
111 dbus_message_iter_recurse(&iter, &sub);
114 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
116 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
117 const char *id, *user, *seat, *object;
120 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
121 log_error("Failed to parse reply.");
126 dbus_message_iter_recurse(&sub, &sub2);
128 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
129 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
130 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
131 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
132 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
133 log_error("Failed to parse reply.");
138 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
142 dbus_message_iter_next(&sub);
146 printf("\n%u sessions listed.\n", k);
152 dbus_message_unref(m);
155 dbus_message_unref(reply);
157 dbus_error_free(&error);
162 static int list_users(DBusConnection *bus, char **args, unsigned n) {
163 DBusMessage *m = NULL, *reply = NULL;
166 DBusMessageIter iter, sub, sub2;
169 dbus_error_init(&error);
173 pager_open_if_enabled();
175 m = dbus_message_new_method_call(
176 "org.freedesktop.login1",
177 "/org/freedesktop/login1",
178 "org.freedesktop.login1.Manager",
181 log_error("Could not allocate message.");
185 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
187 log_error("Failed to issue method call: %s", bus_error_message(&error));
192 if (!dbus_message_iter_init(reply, &iter) ||
193 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
194 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
195 log_error("Failed to parse reply.");
200 dbus_message_iter_recurse(&iter, &sub);
203 printf("%10s %-16s\n", "UID", "USER");
205 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
206 const char *user, *object;
209 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
210 log_error("Failed to parse reply.");
215 dbus_message_iter_recurse(&sub, &sub2);
217 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
218 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
219 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
220 log_error("Failed to parse reply.");
225 printf("%10u %-16s\n", (unsigned) uid, user);
229 dbus_message_iter_next(&sub);
233 printf("\n%u users listed.\n", k);
239 dbus_message_unref(m);
242 dbus_message_unref(reply);
244 dbus_error_free(&error);
249 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
250 DBusMessage *m = NULL, *reply = NULL;
253 DBusMessageIter iter, sub, sub2;
256 dbus_error_init(&error);
260 pager_open_if_enabled();
262 m = dbus_message_new_method_call(
263 "org.freedesktop.login1",
264 "/org/freedesktop/login1",
265 "org.freedesktop.login1.Manager",
268 log_error("Could not allocate message.");
272 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
274 log_error("Failed to issue method call: %s", bus_error_message(&error));
279 if (!dbus_message_iter_init(reply, &iter) ||
280 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
281 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
282 log_error("Failed to parse reply.");
287 dbus_message_iter_recurse(&iter, &sub);
290 printf("%-16s\n", "SEAT");
292 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
293 const char *seat, *object;
295 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
296 log_error("Failed to parse reply.");
301 dbus_message_iter_recurse(&sub, &sub2);
303 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
304 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
305 log_error("Failed to parse reply.");
310 printf("%-16s\n", seat);
314 dbus_message_iter_next(&sub);
318 printf("\n%u seats listed.\n", k);
324 dbus_message_unref(m);
327 dbus_message_unref(reply);
329 dbus_error_free(&error);
334 typedef struct SessionStatusInfo {
339 const char *control_group;
345 const char *remote_host;
346 const char *remote_user;
353 typedef struct UserStatusInfo {
357 const char *control_group;
363 typedef struct SeatStatusInfo {
365 const char *active_session;
369 static void print_session_status_info(SessionStatusInfo *i) {
370 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
371 char since2[FORMAT_TIMESTAMP_MAX], *s2;
374 printf("%s - ", strna(i->id));
377 printf("%s (%u)\n", i->name, (unsigned) i->uid);
379 printf("%u\n", (unsigned) i->uid);
381 s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
382 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
385 printf("\t Since: %s; %s\n", s2, s1);
387 printf("\t Since: %s\n", s2);
392 printf("\t Leader: %u", (unsigned) i->leader);
394 get_process_name(i->leader, &t);
404 printf("\t Seat: %s", i->seat);
407 printf("; vc%i", i->vtnr);
413 printf("\t TTY: %s\n", i->tty);
415 printf("\t Display: %s\n", i->display);
417 if (i->remote_host && i->remote_user)
418 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
419 else if (i->remote_host)
420 printf("\t Remote: %s\n", i->remote_host);
421 else if (i->remote_user)
422 printf("\t Remote: user %s\n", i->remote_user);
424 printf("\t Remote: Yes\n");
427 printf("\t Service: %s", i->service);
430 printf("; type %s", i->type);
434 printf("\t Type: %s\n", i->type);
436 printf("\t Active: %s\n", yes_no(i->active));
438 if (i->control_group) {
441 printf("\t CGroup: %s\n", i->control_group);
443 if (arg_transport != TRANSPORT_SSH) {
450 show_cgroup_by_path(i->control_group, "\t\t ", c);
455 static void print_user_status_info(UserStatusInfo *i) {
456 char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
457 char since2[FORMAT_TIMESTAMP_MAX], *s2;
461 printf("%s (%u)\n", i->name, (unsigned) i->uid);
463 printf("%u\n", (unsigned) i->uid);
465 s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
466 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
469 printf("\t Since: %s; %s\n", s2, s1);
471 printf("\t Since: %s\n", s2);
473 if (!isempty(i->state))
474 printf("\t State: %s\n", i->state);
476 if (!strv_isempty(i->sessions)) {
478 printf("\tSessions:");
480 STRV_FOREACH(l, i->sessions) {
481 if (streq_ptr(*l, i->display))
490 if (i->control_group) {
493 printf("\t CGroup: %s\n", i->control_group);
495 if (arg_transport != TRANSPORT_SSH) {
502 show_cgroup_by_path(i->control_group, "\t\t ", c);
507 static void print_seat_status_info(SeatStatusInfo *i) {
510 printf("%s\n", strna(i->id));
512 if (!strv_isempty(i->sessions)) {
514 printf("\tSessions:");
516 STRV_FOREACH(l, i->sessions) {
517 if (streq_ptr(*l, i->active_session))
526 if (arg_transport != TRANSPORT_SSH) {
535 printf("\t Devices:\n");
537 show_sysfs(i->id, "\t\t ", c);
541 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
546 switch (dbus_message_iter_get_arg_type(iter)) {
548 case DBUS_TYPE_STRING: {
551 dbus_message_iter_get_basic(iter, &s);
554 if (streq(name, "Id"))
556 else if (streq(name, "Name"))
558 else if (streq(name, "ControlGroupPath"))
559 i->control_group = s;
560 else if (streq(name, "TTY"))
562 else if (streq(name, "Display"))
564 else if (streq(name, "RemoteHost"))
566 else if (streq(name, "RemoteUser"))
568 else if (streq(name, "Service"))
570 else if (streq(name, "Type"))
576 case DBUS_TYPE_UINT32: {
579 dbus_message_iter_get_basic(iter, &u);
581 if (streq(name, "VTNr"))
583 else if (streq(name, "Leader"))
584 i->leader = (pid_t) u;
589 case DBUS_TYPE_BOOLEAN: {
592 dbus_message_iter_get_basic(iter, &b);
594 if (streq(name, "Remote"))
596 else if (streq(name, "Active"))
602 case DBUS_TYPE_UINT64: {
605 dbus_message_iter_get_basic(iter, &u);
607 if (streq(name, "Timestamp"))
608 i->timestamp = (usec_t) u;
613 case DBUS_TYPE_STRUCT: {
616 dbus_message_iter_recurse(iter, &sub);
618 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
621 dbus_message_iter_get_basic(&sub, &u);
624 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
627 dbus_message_iter_get_basic(&sub, &s);
640 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
645 switch (dbus_message_iter_get_arg_type(iter)) {
647 case DBUS_TYPE_STRING: {
650 dbus_message_iter_get_basic(iter, &s);
653 if (streq(name, "Name"))
655 else if (streq(name, "ControlGroupPath"))
656 i->control_group = s;
657 else if (streq(name, "State"))
663 case DBUS_TYPE_UINT32: {
666 dbus_message_iter_get_basic(iter, &u);
668 if (streq(name, "UID"))
674 case DBUS_TYPE_UINT64: {
677 dbus_message_iter_get_basic(iter, &u);
679 if (streq(name, "Timestamp"))
680 i->timestamp = (usec_t) u;
685 case DBUS_TYPE_STRUCT: {
688 dbus_message_iter_recurse(iter, &sub);
690 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
693 dbus_message_iter_get_basic(&sub, &s);
702 case DBUS_TYPE_ARRAY: {
704 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
705 DBusMessageIter sub, sub2;
707 dbus_message_iter_recurse(iter, &sub);
708 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
712 dbus_message_iter_recurse(&sub, &sub2);
714 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
715 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
718 l = strv_append(i->sessions, id);
722 strv_free(i->sessions);
726 dbus_message_iter_next(&sub);
737 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
742 switch (dbus_message_iter_get_arg_type(iter)) {
744 case DBUS_TYPE_STRING: {
747 dbus_message_iter_get_basic(iter, &s);
750 if (streq(name, "Id"))
756 case DBUS_TYPE_STRUCT: {
759 dbus_message_iter_recurse(iter, &sub);
761 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
764 dbus_message_iter_get_basic(&sub, &s);
767 i->active_session = s;
773 case DBUS_TYPE_ARRAY: {
775 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
776 DBusMessageIter sub, sub2;
778 dbus_message_iter_recurse(iter, &sub);
779 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
783 dbus_message_iter_recurse(&sub, &sub2);
785 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
786 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
789 l = strv_append(i->sessions, id);
793 strv_free(i->sessions);
797 dbus_message_iter_next(&sub);
808 static int print_property(const char *name, DBusMessageIter *iter) {
812 if (arg_property && !strv_find(arg_property, name))
815 switch (dbus_message_iter_get_arg_type(iter)) {
817 case DBUS_TYPE_STRUCT: {
820 dbus_message_iter_recurse(iter, &sub);
822 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
823 (streq(name, "Display") || streq(name, "ActiveSession"))) {
826 dbus_message_iter_get_basic(&sub, &s);
828 if (arg_all || !isempty(s))
829 printf("%s=%s\n", name, s);
835 case DBUS_TYPE_ARRAY:
837 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
838 DBusMessageIter sub, sub2;
841 dbus_message_iter_recurse(iter, &sub);
842 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
846 dbus_message_iter_recurse(&sub, &sub2);
848 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
849 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
853 printf("%s=%s", name, id);
858 dbus_message_iter_next(&sub);
861 if (!found && arg_all)
862 printf("%s=\n", name);
872 if (generic_print_property(name, iter, arg_all) > 0)
876 printf("%s=[unprintable]\n", name);
881 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
882 DBusMessage *m = NULL, *reply = NULL;
883 const char *interface = "";
886 DBusMessageIter iter, sub, sub2, sub3;
887 SessionStatusInfo session_info;
888 UserStatusInfo user_info;
889 SeatStatusInfo seat_info;
899 dbus_error_init(&error);
901 m = dbus_message_new_method_call(
902 "org.freedesktop.login1",
904 "org.freedesktop.DBus.Properties",
907 log_error("Could not allocate message.");
912 if (!dbus_message_append_args(m,
913 DBUS_TYPE_STRING, &interface,
914 DBUS_TYPE_INVALID)) {
915 log_error("Could not append arguments to message.");
920 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
922 log_error("Failed to issue method call: %s", bus_error_message(&error));
927 if (!dbus_message_iter_init(reply, &iter) ||
928 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
929 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
930 log_error("Failed to parse reply.");
935 dbus_message_iter_recurse(&iter, &sub);
942 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
945 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
946 log_error("Failed to parse reply.");
951 dbus_message_iter_recurse(&sub, &sub2);
953 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
954 log_error("Failed to parse reply.");
959 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
960 log_error("Failed to parse reply.");
965 dbus_message_iter_recurse(&sub2, &sub3);
968 r = print_property(name, &sub3);
969 else if (strstr(verb, "session"))
970 r = status_property_session(name, &sub3, &session_info);
971 else if (strstr(verb, "user"))
972 r = status_property_user(name, &sub3, &user_info);
974 r = status_property_seat(name, &sub3, &seat_info);
977 log_error("Failed to parse reply.");
982 dbus_message_iter_next(&sub);
985 if (!show_properties) {
986 if (strstr(verb, "session"))
987 print_session_status_info(&session_info);
988 else if (strstr(verb, "user"))
989 print_user_status_info(&user_info);
991 print_seat_status_info(&seat_info);
994 strv_free(seat_info.sessions);
995 strv_free(user_info.sessions);
1001 dbus_message_unref(m);
1004 dbus_message_unref(reply);
1006 dbus_error_free(&error);
1011 static int show(DBusConnection *bus, char **args, unsigned n) {
1012 DBusMessage *m = NULL, *reply = NULL;
1016 bool show_properties, new_line = false;
1021 dbus_error_init(&error);
1023 show_properties = !strstr(args[0], "status");
1025 if (show_properties)
1026 pager_open_if_enabled();
1028 if (show_properties && n <= 1) {
1029 /* If not argument is specified inspect the manager
1032 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
1036 for (i = 1; i < n; i++) {
1037 const char *path = NULL;
1039 if (strstr(args[0], "session")) {
1041 m = dbus_message_new_method_call(
1042 "org.freedesktop.login1",
1043 "/org/freedesktop/login1",
1044 "org.freedesktop.login1.Manager",
1047 log_error("Could not allocate message.");
1052 if (!dbus_message_append_args(m,
1053 DBUS_TYPE_STRING, &args[i],
1054 DBUS_TYPE_INVALID)) {
1055 log_error("Could not append arguments to message.");
1060 } else if (strstr(args[0], "user")) {
1063 if (safe_atou(args[i], &uid) < 0) {
1066 pw = getpwnam(args[i]);
1068 log_error("User %s unknown.", args[i]);
1076 m = dbus_message_new_method_call(
1077 "org.freedesktop.login1",
1078 "/org/freedesktop/login1",
1079 "org.freedesktop.login1.Manager",
1082 log_error("Could not allocate message.");
1087 if (!dbus_message_append_args(m,
1088 DBUS_TYPE_UINT32, &uid,
1089 DBUS_TYPE_INVALID)) {
1090 log_error("Could not append arguments to message.");
1096 m = dbus_message_new_method_call(
1097 "org.freedesktop.login1",
1098 "/org/freedesktop/login1",
1099 "org.freedesktop.login1.Manager",
1102 log_error("Could not allocate message.");
1107 if (!dbus_message_append_args(m,
1108 DBUS_TYPE_STRING, &args[i],
1109 DBUS_TYPE_INVALID)) {
1110 log_error("Could not append arguments to message.");
1116 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1118 log_error("Failed to issue method call: %s", bus_error_message(&error));
1123 if (!dbus_message_get_args(reply, &error,
1124 DBUS_TYPE_OBJECT_PATH, &path,
1125 DBUS_TYPE_INVALID)) {
1126 log_error("Failed to parse reply: %s", bus_error_message(&error));
1131 r = show_one(args[0], bus, path, show_properties, &new_line);
1135 dbus_message_unref(m);
1136 dbus_message_unref(reply);
1142 dbus_message_unref(m);
1145 dbus_message_unref(reply);
1147 dbus_error_free(&error);
1152 static int activate(DBusConnection *bus, char **args, unsigned n) {
1153 DBusMessage *m = NULL, *reply = NULL;
1161 dbus_error_init(&error);
1163 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);
1203 dbus_message_unref(reply);
1205 dbus_error_free(&error);
1210 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1214 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1215 DBusMessage *m = NULL, *reply = NULL;
1219 dbus_bool_t b, interactive = true;
1224 dbus_error_init(&error);
1226 b = streq(args[0], "enable-linger");
1228 for (i = 1; i < n; i++) {
1231 m = dbus_message_new_method_call(
1232 "org.freedesktop.login1",
1233 "/org/freedesktop/login1",
1234 "org.freedesktop.login1.Manager",
1237 log_error("Could not allocate message.");
1242 if (safe_atou32(args[i], &uid) < 0) {
1246 pw = getpwnam(args[i]);
1248 ret = errno ? -errno : -ENOENT;
1249 log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1256 if (!dbus_message_append_args(m,
1257 DBUS_TYPE_UINT32, &uid,
1258 DBUS_TYPE_BOOLEAN, &b,
1259 DBUS_TYPE_BOOLEAN, &interactive,
1260 DBUS_TYPE_INVALID)) {
1261 log_error("Could not append arguments to message.");
1266 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1268 log_error("Failed to issue method call: %s", bus_error_message(&error));
1273 dbus_message_unref(m);
1274 dbus_message_unref(reply);
1280 dbus_message_unref(m);
1283 dbus_message_unref(reply);
1285 dbus_error_free(&error);
1290 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1291 DBusMessage *m = NULL, *reply = NULL;
1299 dbus_error_init(&error);
1301 for (i = 1; i < n; i++) {
1304 m = dbus_message_new_method_call(
1305 "org.freedesktop.login1",
1306 "/org/freedesktop/login1",
1307 "org.freedesktop.login1.Manager",
1310 log_error("Could not allocate message.");
1315 if (safe_atou32(args[i], &u) < 0) {
1319 pw = getpwnam(args[i]);
1321 ret = errno ? -errno : -ENOENT;
1322 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1329 if (!dbus_message_append_args(m,
1330 DBUS_TYPE_UINT32, &u,
1331 DBUS_TYPE_INVALID)) {
1332 log_error("Could not append arguments to message.");
1337 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1339 log_error("Failed to issue method call: %s", bus_error_message(&error));
1344 dbus_message_unref(m);
1345 dbus_message_unref(reply);
1351 dbus_message_unref(m);
1354 dbus_message_unref(reply);
1356 dbus_error_free(&error);
1361 static int attach(DBusConnection *bus, char **args, unsigned n) {
1362 DBusMessage *m = NULL, *reply = NULL;
1366 dbus_bool_t interactive = true;
1371 dbus_error_init(&error);
1373 for (i = 2; i < n; i++) {
1374 m = dbus_message_new_method_call(
1375 "org.freedesktop.login1",
1376 "/org/freedesktop/login1",
1377 "org.freedesktop.login1.Manager",
1380 log_error("Could not allocate message.");
1385 if (!dbus_message_append_args(m,
1386 DBUS_TYPE_STRING, &args[1],
1387 DBUS_TYPE_STRING, &args[i],
1388 DBUS_TYPE_BOOLEAN, &interactive,
1389 DBUS_TYPE_INVALID)) {
1390 log_error("Could not append arguments to message.");
1395 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1397 log_error("Failed to issue method call: %s", bus_error_message(&error));
1402 dbus_message_unref(m);
1403 dbus_message_unref(reply);
1409 dbus_message_unref(m);
1412 dbus_message_unref(reply);
1414 dbus_error_free(&error);
1419 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1420 DBusMessage *m = NULL, *reply = NULL;
1423 dbus_bool_t interactive = true;
1428 dbus_error_init(&error);
1430 m = dbus_message_new_method_call(
1431 "org.freedesktop.login1",
1432 "/org/freedesktop/login1",
1433 "org.freedesktop.login1.Manager",
1436 log_error("Could not allocate message.");
1441 if (!dbus_message_append_args(m,
1442 DBUS_TYPE_BOOLEAN, &interactive,
1443 DBUS_TYPE_INVALID)) {
1444 log_error("Could not append arguments to message.");
1449 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1451 log_error("Failed to issue method call: %s", bus_error_message(&error));
1458 dbus_message_unref(m);
1461 dbus_message_unref(reply);
1463 dbus_error_free(&error);
1468 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1469 DBusMessage *m = NULL, *reply = NULL;
1477 dbus_error_init(&error);
1479 for (i = 1; i < n; i++) {
1480 m = dbus_message_new_method_call(
1481 "org.freedesktop.login1",
1482 "/org/freedesktop/login1",
1483 "org.freedesktop.login1.Manager",
1486 log_error("Could not allocate message.");
1491 if (!dbus_message_append_args(m,
1492 DBUS_TYPE_STRING, &args[i],
1493 DBUS_TYPE_INVALID)) {
1494 log_error("Could not append arguments to message.");
1499 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1501 log_error("Failed to issue method call: %s", bus_error_message(&error));
1506 dbus_message_unref(m);
1507 dbus_message_unref(reply);
1513 dbus_message_unref(m);
1516 dbus_message_unref(reply);
1518 dbus_error_free(&error);
1523 static int help(void) {
1525 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1526 "Send control commands to or query the login manager.\n\n"
1527 " -h --help Show this help\n"
1528 " --version Show package version\n"
1529 " -p --property=NAME Show only properties by this name\n"
1530 " -a --all Show all properties, including empty ones\n"
1531 " --kill-who=WHO Who to send signal to\n"
1532 " -s --signal=SIGNAL Which signal to send\n"
1533 " -H --host=[USER@]HOST\n"
1534 " Show information for remote host\n"
1535 " -P --privileged Acquire privileges before execution\n"
1536 " --no-pager Do not pipe output into a pager\n\n"
1538 " list-sessions List sessions\n"
1539 " session-status [ID...] Show session status\n"
1540 " show-session [ID...] Show property of one or more sessions\n"
1541 " activate [ID] Activate a session\n"
1542 " lock-session [ID...] Screen lock one or more sessions\n"
1543 " unlock-session [ID...] Screen unlock one or more sessions\n"
1544 " terminate-session [ID...] Terminate one or more sessions\n"
1545 " kill-session [ID...] Send signal to processes of a session\n"
1546 " list-users List users\n"
1547 " user-status [USER...] Show user status\n"
1548 " show-user [USER...] Show property of one or more users\n"
1549 " enable-linger [USER...] Enable linger state of one or more users\n"
1550 " disable-linger [USER...] Disable linger state of one or more users\n"
1551 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1552 " kill-user [USER...] Send signal to processes of a user\n"
1553 " list-seats List seats\n"
1554 " seat-status [NAME...] Show seat status\n"
1555 " show-seat [NAME...] Show property of one or more seats\n"
1556 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1557 " flush-devices Flush all device associations\n"
1558 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n"
1559 " kill-seat [NAME...] Send signal to processes of sessions on a seat\n",
1560 program_invocation_short_name);
1565 static int parse_argv(int argc, char *argv[]) {
1568 ARG_VERSION = 0x100,
1573 static const struct option options[] = {
1574 { "help", no_argument, NULL, 'h' },
1575 { "version", no_argument, NULL, ARG_VERSION },
1576 { "property", required_argument, NULL, 'p' },
1577 { "all", no_argument, NULL, 'a' },
1578 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1579 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1580 { "signal", required_argument, NULL, 's' },
1581 { "host", required_argument, NULL, 'H' },
1582 { "privileged",no_argument, NULL, 'P' },
1583 { NULL, 0, NULL, 0 }
1591 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1600 puts(PACKAGE_STRING);
1602 puts(SYSTEMD_FEATURES);
1608 l = strv_append(arg_property, optarg);
1612 strv_free(arg_property);
1615 /* If the user asked for a particular
1616 * property, show it to him, even if it is
1627 arg_no_pager = true;
1631 arg_kill_who = optarg;
1635 arg_signal = signal_from_string_try_harder(optarg);
1636 if (arg_signal < 0) {
1637 log_error("Failed to parse signal string %s.", optarg);
1643 arg_transport = TRANSPORT_POLKIT;
1647 arg_transport = TRANSPORT_SSH;
1655 log_error("Unknown option code %c", c);
1663 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1665 static const struct {
1673 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1675 { "list-sessions", LESS, 1, list_sessions },
1676 { "session-status", MORE, 2, show },
1677 { "show-session", MORE, 1, show },
1678 { "activate", EQUAL, 2, activate },
1679 { "lock-session", MORE, 2, activate },
1680 { "unlock-session", MORE, 2, activate },
1681 { "terminate-session", MORE, 2, activate },
1682 { "kill-session", MORE, 2, kill_session }, /* missing */
1683 { "list-users", EQUAL, 1, list_users },
1684 { "user-status", MORE, 2, show },
1685 { "show-user", MORE, 1, show },
1686 { "enable-linger", MORE, 2, enable_linger },
1687 { "disable-linger", MORE, 2, enable_linger },
1688 { "terminate-user", MORE, 2, terminate_user },
1689 { "kill-user", MORE, 2, kill_session }, /* missing */
1690 { "list-seats", EQUAL, 1, list_seats },
1691 { "seat-status", MORE, 2, show },
1692 { "show-seat", MORE, 1, show },
1693 { "attach", MORE, 3, attach },
1694 { "flush-devices", EQUAL, 1, flush_devices },
1695 { "terminate-seat", MORE, 2, terminate_seat }, /* missing */
1696 { "kill-seat", MORE, 2, kill_session }, /* missing */
1706 left = argc - optind;
1709 /* Special rule: no arguments means "list-sessions" */
1712 if (streq(argv[optind], "help")) {
1717 for (i = 0; i < ELEMENTSOF(verbs); i++)
1718 if (streq(argv[optind], verbs[i].verb))
1721 if (i >= ELEMENTSOF(verbs)) {
1722 log_error("Unknown operation %s", argv[optind]);
1727 switch (verbs[i].argc_cmp) {
1730 if (left != verbs[i].argc) {
1731 log_error("Invalid number of arguments.");
1738 if (left < verbs[i].argc) {
1739 log_error("Too few arguments.");
1746 if (left > verbs[i].argc) {
1747 log_error("Too many arguments.");
1754 assert_not_reached("Unknown comparison operator.");
1758 log_error("Failed to get D-Bus connection: %s", error->message);
1762 return verbs[i].dispatch(bus, argv + optind, left);
1765 int main(int argc, char*argv[]) {
1766 int r, retval = EXIT_FAILURE;
1767 DBusConnection *bus = NULL;
1770 dbus_error_init(&error);
1772 log_parse_environment();
1775 r = parse_argv(argc, argv);
1779 retval = EXIT_SUCCESS;
1783 if (arg_transport == TRANSPORT_NORMAL)
1784 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1785 else if (arg_transport == TRANSPORT_POLKIT)
1786 bus_connect_system_polkit(&bus, &error);
1787 else if (arg_transport == TRANSPORT_SSH)
1788 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1790 assert_not_reached("Uh, invalid transport...");
1792 r = loginctl_main(bus, argc, argv, &error);
1793 retval = r < 0 ? EXIT_FAILURE : r;
1797 dbus_connection_flush(bus);
1798 dbus_connection_close(bus);
1799 dbus_connection_unref(bus);
1802 dbus_error_free(&error);
1805 strv_free(arg_property);