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");
538 show_sysfs(i->id, "\t\t ", c);
542 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
547 switch (dbus_message_iter_get_arg_type(iter)) {
549 case DBUS_TYPE_STRING: {
552 dbus_message_iter_get_basic(iter, &s);
555 if (streq(name, "Id"))
557 else if (streq(name, "Name"))
559 else if (streq(name, "ControlGroupPath"))
560 i->control_group = s;
561 else if (streq(name, "TTY"))
563 else if (streq(name, "Display"))
565 else if (streq(name, "RemoteHost"))
567 else if (streq(name, "RemoteUser"))
569 else if (streq(name, "Service"))
571 else if (streq(name, "Type"))
577 case DBUS_TYPE_UINT32: {
580 dbus_message_iter_get_basic(iter, &u);
582 if (streq(name, "VTNr"))
584 else if (streq(name, "Leader"))
585 i->leader = (pid_t) u;
590 case DBUS_TYPE_BOOLEAN: {
593 dbus_message_iter_get_basic(iter, &b);
595 if (streq(name, "Remote"))
597 else if (streq(name, "Active"))
603 case DBUS_TYPE_UINT64: {
606 dbus_message_iter_get_basic(iter, &u);
608 if (streq(name, "Timestamp"))
609 i->timestamp = (usec_t) u;
614 case DBUS_TYPE_STRUCT: {
617 dbus_message_iter_recurse(iter, &sub);
619 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
622 dbus_message_iter_get_basic(&sub, &u);
625 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
628 dbus_message_iter_get_basic(&sub, &s);
641 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
646 switch (dbus_message_iter_get_arg_type(iter)) {
648 case DBUS_TYPE_STRING: {
651 dbus_message_iter_get_basic(iter, &s);
654 if (streq(name, "Name"))
656 else if (streq(name, "ControlGroupPath"))
657 i->control_group = s;
658 else if (streq(name, "State"))
664 case DBUS_TYPE_UINT32: {
667 dbus_message_iter_get_basic(iter, &u);
669 if (streq(name, "UID"))
675 case DBUS_TYPE_UINT64: {
678 dbus_message_iter_get_basic(iter, &u);
680 if (streq(name, "Timestamp"))
681 i->timestamp = (usec_t) u;
686 case DBUS_TYPE_STRUCT: {
689 dbus_message_iter_recurse(iter, &sub);
691 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
694 dbus_message_iter_get_basic(&sub, &s);
703 case DBUS_TYPE_ARRAY: {
705 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
706 DBusMessageIter sub, sub2;
708 dbus_message_iter_recurse(iter, &sub);
709 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
713 dbus_message_iter_recurse(&sub, &sub2);
715 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
716 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
719 l = strv_append(i->sessions, id);
723 strv_free(i->sessions);
727 dbus_message_iter_next(&sub);
738 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
743 switch (dbus_message_iter_get_arg_type(iter)) {
745 case DBUS_TYPE_STRING: {
748 dbus_message_iter_get_basic(iter, &s);
751 if (streq(name, "Id"))
757 case DBUS_TYPE_STRUCT: {
760 dbus_message_iter_recurse(iter, &sub);
762 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
765 dbus_message_iter_get_basic(&sub, &s);
768 i->active_session = s;
774 case DBUS_TYPE_ARRAY: {
776 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
777 DBusMessageIter sub, sub2;
779 dbus_message_iter_recurse(iter, &sub);
780 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
784 dbus_message_iter_recurse(&sub, &sub2);
786 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
787 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
790 l = strv_append(i->sessions, id);
794 strv_free(i->sessions);
798 dbus_message_iter_next(&sub);
809 static int print_property(const char *name, DBusMessageIter *iter) {
813 if (arg_property && !strv_find(arg_property, name))
816 switch (dbus_message_iter_get_arg_type(iter)) {
818 case DBUS_TYPE_STRUCT: {
821 dbus_message_iter_recurse(iter, &sub);
823 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
824 (streq(name, "Display") || streq(name, "ActiveSession"))) {
827 dbus_message_iter_get_basic(&sub, &s);
829 if (arg_all || !isempty(s))
830 printf("%s=%s\n", name, s);
836 case DBUS_TYPE_ARRAY:
838 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
839 DBusMessageIter sub, sub2;
842 dbus_message_iter_recurse(iter, &sub);
843 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
847 dbus_message_iter_recurse(&sub, &sub2);
849 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
850 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
854 printf("%s=%s", name, id);
859 dbus_message_iter_next(&sub);
862 if (!found && arg_all)
863 printf("%s=\n", name);
873 if (generic_print_property(name, iter, arg_all) > 0)
877 printf("%s=[unprintable]\n", name);
882 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
883 DBusMessage *m = NULL, *reply = NULL;
884 const char *interface = "";
887 DBusMessageIter iter, sub, sub2, sub3;
888 SessionStatusInfo session_info;
889 UserStatusInfo user_info;
890 SeatStatusInfo seat_info;
900 dbus_error_init(&error);
902 m = dbus_message_new_method_call(
903 "org.freedesktop.login1",
905 "org.freedesktop.DBus.Properties",
908 log_error("Could not allocate message.");
913 if (!dbus_message_append_args(m,
914 DBUS_TYPE_STRING, &interface,
915 DBUS_TYPE_INVALID)) {
916 log_error("Could not append arguments to message.");
921 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
923 log_error("Failed to issue method call: %s", bus_error_message(&error));
928 if (!dbus_message_iter_init(reply, &iter) ||
929 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
930 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
931 log_error("Failed to parse reply.");
936 dbus_message_iter_recurse(&iter, &sub);
943 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
946 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
947 log_error("Failed to parse reply.");
952 dbus_message_iter_recurse(&sub, &sub2);
954 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
955 log_error("Failed to parse reply.");
960 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
961 log_error("Failed to parse reply.");
966 dbus_message_iter_recurse(&sub2, &sub3);
969 r = print_property(name, &sub3);
970 else if (strstr(verb, "session"))
971 r = status_property_session(name, &sub3, &session_info);
972 else if (strstr(verb, "user"))
973 r = status_property_user(name, &sub3, &user_info);
975 r = status_property_seat(name, &sub3, &seat_info);
978 log_error("Failed to parse reply.");
983 dbus_message_iter_next(&sub);
986 if (!show_properties) {
987 if (strstr(verb, "session"))
988 print_session_status_info(&session_info);
989 else if (strstr(verb, "user"))
990 print_user_status_info(&user_info);
992 print_seat_status_info(&seat_info);
995 strv_free(seat_info.sessions);
996 strv_free(user_info.sessions);
1002 dbus_message_unref(m);
1005 dbus_message_unref(reply);
1007 dbus_error_free(&error);
1012 static int show(DBusConnection *bus, char **args, unsigned n) {
1013 DBusMessage *m = NULL, *reply = NULL;
1017 bool show_properties, new_line = false;
1022 dbus_error_init(&error);
1024 show_properties = !strstr(args[0], "status");
1026 if (show_properties)
1027 pager_open_if_enabled();
1029 if (show_properties && n <= 1) {
1030 /* If not argument is specified inspect the manager
1033 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
1037 for (i = 1; i < n; i++) {
1038 const char *path = NULL;
1040 if (strstr(args[0], "session")) {
1042 m = dbus_message_new_method_call(
1043 "org.freedesktop.login1",
1044 "/org/freedesktop/login1",
1045 "org.freedesktop.login1.Manager",
1048 log_error("Could not allocate message.");
1053 if (!dbus_message_append_args(m,
1054 DBUS_TYPE_STRING, &args[i],
1055 DBUS_TYPE_INVALID)) {
1056 log_error("Could not append arguments to message.");
1061 } else if (strstr(args[0], "user")) {
1064 if (safe_atou(args[i], &uid) < 0) {
1067 pw = getpwnam(args[i]);
1069 log_error("User %s unknown.", args[i]);
1077 m = dbus_message_new_method_call(
1078 "org.freedesktop.login1",
1079 "/org/freedesktop/login1",
1080 "org.freedesktop.login1.Manager",
1083 log_error("Could not allocate message.");
1088 if (!dbus_message_append_args(m,
1089 DBUS_TYPE_UINT32, &uid,
1090 DBUS_TYPE_INVALID)) {
1091 log_error("Could not append arguments to message.");
1097 m = dbus_message_new_method_call(
1098 "org.freedesktop.login1",
1099 "/org/freedesktop/login1",
1100 "org.freedesktop.login1.Manager",
1103 log_error("Could not allocate message.");
1108 if (!dbus_message_append_args(m,
1109 DBUS_TYPE_STRING, &args[i],
1110 DBUS_TYPE_INVALID)) {
1111 log_error("Could not append arguments to message.");
1117 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1119 log_error("Failed to issue method call: %s", bus_error_message(&error));
1124 if (!dbus_message_get_args(reply, &error,
1125 DBUS_TYPE_OBJECT_PATH, &path,
1126 DBUS_TYPE_INVALID)) {
1127 log_error("Failed to parse reply: %s", bus_error_message(&error));
1132 r = show_one(args[0], bus, path, show_properties, &new_line);
1136 dbus_message_unref(m);
1137 dbus_message_unref(reply);
1143 dbus_message_unref(m);
1146 dbus_message_unref(reply);
1148 dbus_error_free(&error);
1153 static int activate(DBusConnection *bus, char **args, unsigned n) {
1154 DBusMessage *m = NULL, *reply = NULL;
1162 dbus_error_init(&error);
1164 for (i = 1; i < n; i++) {
1165 m = dbus_message_new_method_call(
1166 "org.freedesktop.login1",
1167 "/org/freedesktop/login1",
1168 "org.freedesktop.login1.Manager",
1171 log_error("Could not allocate message.");
1176 if (!dbus_message_append_args(m,
1177 DBUS_TYPE_STRING, &args[i],
1178 DBUS_TYPE_INVALID)) {
1179 log_error("Could not append arguments to message.");
1184 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1186 log_error("Failed to issue method call: %s", bus_error_message(&error));
1191 dbus_message_unref(m);
1192 dbus_message_unref(reply);
1198 dbus_message_unref(m);
1201 dbus_message_unref(reply);
1203 dbus_error_free(&error);
1208 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1212 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1216 static int attach(DBusConnection *bus, char **args, unsigned n) {
1220 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1224 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1228 static int help(void) {
1230 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1231 "Send control commands to or query the login manager.\n\n"
1232 " -h --help Show this help\n"
1233 " --version Show package version\n"
1234 " -p --property=NAME Show only properties by this name\n"
1235 " -a --all Show all properties, including empty ones\n"
1236 " --kill-who=WHO Who to send signal to\n"
1237 " -s --signal=SIGNAL Which signal to send\n"
1238 " -H --host=[USER@]HOST\n"
1239 " Show information for remote host\n"
1240 " -P --privileged Acquire privileges before execution\n"
1241 " --no-pager Do not pipe output into a pager\n\n"
1243 " list-sessions List sessions\n"
1244 " session-status [ID...] Show session status\n"
1245 " show-session [ID...] Show property of one or more sessions\n"
1246 " activate [ID] Activate a session\n"
1247 " lock-session [ID...] Screen lock one or more sessions\n"
1248 " terminate-session [ID...] Terminate one or more sessions\n"
1249 " kill-session [ID...] Send signal to processes of a session\n"
1250 " list-users List users\n"
1251 " user-status [USER...] Show user status\n"
1252 " show-user [USER...] Show property of one or more users\n"
1253 " enable-linger [USER...] Enable linger state of one or more users\n"
1254 " disable-linger [USER...] Disable linger state of one or more users\n"
1255 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1256 " kill-user [USER...] Send signal to processes of a user\n"
1257 " list-seats List seats\n"
1258 " seat-status [NAME...] Show seat status\n"
1259 " show-seat [NAME...] Show property of one or more seats\n"
1260 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1261 " flush-devices Flush all device associations\n"
1262 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n"
1263 " kill-seat [NAME...] Send signal to processes of sessions on a seat\n",
1264 program_invocation_short_name);
1269 static int parse_argv(int argc, char *argv[]) {
1272 ARG_VERSION = 0x100,
1277 static const struct option options[] = {
1278 { "help", no_argument, NULL, 'h' },
1279 { "version", no_argument, NULL, ARG_VERSION },
1280 { "property", required_argument, NULL, 'p' },
1281 { "all", no_argument, NULL, 'a' },
1282 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1283 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1284 { "signal", required_argument, NULL, 's' },
1285 { "host", required_argument, NULL, 'H' },
1286 { "privileged",no_argument, NULL, 'P' },
1287 { NULL, 0, NULL, 0 }
1295 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1304 puts(PACKAGE_STRING);
1306 puts(SYSTEMD_FEATURES);
1312 l = strv_append(arg_property, optarg);
1316 strv_free(arg_property);
1319 /* If the user asked for a particular
1320 * property, show it to him, even if it is
1331 arg_no_pager = true;
1335 arg_kill_who = optarg;
1339 arg_signal = signal_from_string_try_harder(optarg);
1340 if (arg_signal < 0) {
1341 log_error("Failed to parse signal string %s.", optarg);
1347 arg_transport = TRANSPORT_POLKIT;
1351 arg_transport = TRANSPORT_SSH;
1359 log_error("Unknown option code %c", c);
1367 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1369 static const struct {
1377 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1379 { "list-sessions", LESS, 1, list_sessions },
1380 { "session-status", MORE, 2, show },
1381 { "show-session", MORE, 1, show },
1382 { "activate", EQUAL, 2, activate },
1383 { "lock-session", MORE, 2, activate },
1384 { "terminate-session", MORE, 2, activate },
1385 { "kill-session", MORE, 2, kill_session },
1386 { "list-users", EQUAL, 1, list_users },
1387 { "user-status", MORE, 2, show },
1388 { "show-user", MORE, 1, show },
1389 { "enable-linger", MORE, 2, enable_linger },
1390 { "disable-linger", MORE, 2, enable_linger },
1391 { "terminate-user", MORE, 2, enable_linger },
1392 { "kill-user", MORE, 2, kill_session },
1393 { "list-seats", EQUAL, 1, list_seats },
1394 { "seat-status", MORE, 2, show },
1395 { "show-seat", MORE, 1, show },
1396 { "attach", MORE, 3, attach },
1397 { "flush-devices", EQUAL, 1, flush_devices },
1398 { "terminate-seat", MORE, 2, terminate_seat },
1399 { "kill-seat", MORE, 2, kill_session },
1409 left = argc - optind;
1412 /* Special rule: no arguments means "list-sessions" */
1415 if (streq(argv[optind], "help")) {
1420 for (i = 0; i < ELEMENTSOF(verbs); i++)
1421 if (streq(argv[optind], verbs[i].verb))
1424 if (i >= ELEMENTSOF(verbs)) {
1425 log_error("Unknown operation %s", argv[optind]);
1430 switch (verbs[i].argc_cmp) {
1433 if (left != verbs[i].argc) {
1434 log_error("Invalid number of arguments.");
1441 if (left < verbs[i].argc) {
1442 log_error("Too few arguments.");
1449 if (left > verbs[i].argc) {
1450 log_error("Too many arguments.");
1457 assert_not_reached("Unknown comparison operator.");
1461 log_error("Failed to get D-Bus connection: %s", error->message);
1465 return verbs[i].dispatch(bus, argv + optind, left);
1468 int main(int argc, char*argv[]) {
1469 int r, retval = EXIT_FAILURE;
1470 DBusConnection *bus = NULL;
1473 dbus_error_init(&error);
1475 log_parse_environment();
1478 r = parse_argv(argc, argv);
1482 retval = EXIT_SUCCESS;
1486 if (arg_transport == TRANSPORT_NORMAL)
1487 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1488 else if (arg_transport == TRANSPORT_POLKIT)
1489 bus_connect_system_polkit(&bus, &error);
1490 else if (arg_transport == TRANSPORT_SSH)
1491 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1493 assert_not_reached("Uh, invalid transport...");
1495 r = loginctl_main(bus, argc, argv, &error);
1496 retval = r < 0 ? EXIT_FAILURE : r;
1500 dbus_connection_flush(bus);
1501 dbus_connection_close(bus);
1502 dbus_connection_unref(bus);
1505 dbus_error_free(&error);
1508 strv_free(arg_property);