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 Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <dbus/dbus.h>
34 #include "dbus-common.h"
37 #include "cgroup-show.h"
38 #include "sysfs-show.h"
39 #include "spawn-polkit-agent.h"
41 static char **arg_property = NULL;
42 static bool arg_all = false;
43 static bool arg_full = false;
44 static bool arg_no_pager = false;
45 static const char *arg_kill_who = NULL;
46 static int arg_signal = SIGTERM;
47 static enum transport {
51 } arg_transport = TRANSPORT_NORMAL;
52 static bool arg_ask_password = true;
53 static char *arg_host = NULL;
54 static char *arg_user = NULL;
56 static void pager_open_if_enabled(void) {
58 /* Cache result before we open the pager */
65 static void polkit_agent_open_if_enabled(void) {
67 /* Open the polkit agent as a child process if necessary */
69 if (!arg_ask_password)
75 static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
76 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
78 DBusMessageIter iter, sub, sub2;
81 pager_open_if_enabled();
83 r = bus_method_call_with_reply (
85 "org.freedesktop.login1",
86 "/org/freedesktop/login1",
87 "org.freedesktop.login1.Manager",
95 if (!dbus_message_iter_init(reply, &iter) ||
96 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
97 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
98 log_error("Failed to parse reply.");
102 dbus_message_iter_recurse(&iter, &sub);
105 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
107 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
108 const char *id, *user, *seat, *object;
111 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
112 log_error("Failed to parse reply.");
116 dbus_message_iter_recurse(&sub, &sub2);
118 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
119 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
120 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
121 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
122 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
123 log_error("Failed to parse reply.");
127 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
131 dbus_message_iter_next(&sub);
135 printf("\n%u sessions listed.\n", k);
140 static int list_users(DBusConnection *bus, char **args, unsigned n) {
141 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
143 DBusMessageIter iter, sub, sub2;
146 pager_open_if_enabled();
148 r = bus_method_call_with_reply (
150 "org.freedesktop.login1",
151 "/org/freedesktop/login1",
152 "org.freedesktop.login1.Manager",
160 if (!dbus_message_iter_init(reply, &iter) ||
161 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
162 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
163 log_error("Failed to parse reply.");
167 dbus_message_iter_recurse(&iter, &sub);
170 printf("%10s %-16s\n", "UID", "USER");
172 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
173 const char *user, *object;
176 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
177 log_error("Failed to parse reply.");
181 dbus_message_iter_recurse(&sub, &sub2);
183 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
184 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
185 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
186 log_error("Failed to parse reply.");
190 printf("%10u %-16s\n", (unsigned) uid, user);
194 dbus_message_iter_next(&sub);
198 printf("\n%u users listed.\n", k);
203 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
204 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
206 DBusMessageIter iter, sub, sub2;
209 pager_open_if_enabled();
211 r = bus_method_call_with_reply (
213 "org.freedesktop.login1",
214 "/org/freedesktop/login1",
215 "org.freedesktop.login1.Manager",
223 if (!dbus_message_iter_init(reply, &iter) ||
224 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
225 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
226 log_error("Failed to parse reply.");
230 dbus_message_iter_recurse(&iter, &sub);
233 printf("%-16s\n", "SEAT");
235 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
236 const char *seat, *object;
238 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
239 log_error("Failed to parse reply.");
243 dbus_message_iter_recurse(&sub, &sub2);
245 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
246 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
247 log_error("Failed to parse reply.");
251 printf("%-16s\n", seat);
255 dbus_message_iter_next(&sub);
259 printf("\n%u seats listed.\n", k);
264 static int list_machines(DBusConnection *bus, char **args, unsigned n) {
265 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
266 DBusMessageIter iter, sub, sub2;
270 pager_open_if_enabled();
272 r = bus_method_call_with_reply (
274 "org.freedesktop.login1",
275 "/org/freedesktop/login1",
276 "org.freedesktop.login1.Manager",
284 if (!dbus_message_iter_init(reply, &iter) ||
285 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
286 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
287 log_error("Failed to parse reply.");
291 dbus_message_iter_recurse(&iter, &sub);
294 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
296 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
297 const char *name, *class, *service, *object;
299 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
300 log_error("Failed to parse reply.");
304 dbus_message_iter_recurse(&sub, &sub2);
306 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
307 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &class, true) < 0 ||
308 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &service, true) < 0 ||
309 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
310 log_error("Failed to parse reply.");
314 printf("%-32s %-9s %-16s\n", name, class, service);
318 dbus_message_iter_next(&sub);
322 printf("\n%u machines listed.\n", k);
327 typedef struct SessionStatusInfo {
332 const char *default_control_group;
338 const char *remote_host;
339 const char *remote_user;
348 typedef struct UserStatusInfo {
352 const char *default_control_group;
359 typedef struct SeatStatusInfo {
361 const char *active_session;
365 typedef struct MachineStatusInfo {
368 const char *default_control_group;
372 const char *root_directory;
377 static void print_session_status_info(SessionStatusInfo *i) {
378 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
379 char since2[FORMAT_TIMESTAMP_MAX], *s2;
382 printf("%s - ", strna(i->id));
385 printf("%s (%u)\n", i->name, (unsigned) i->uid);
387 printf("%u\n", (unsigned) i->uid);
389 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
390 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
393 printf("\t Since: %s; %s\n", s2, s1);
395 printf("\t Since: %s\n", s2);
398 _cleanup_free_ char *t = NULL;
400 printf("\t Leader: %u", (unsigned) i->leader);
402 get_process_comm(i->leader, &t);
410 printf("\t Seat: %s", i->seat);
413 printf("; vc%i", i->vtnr);
419 printf("\t TTY: %s\n", i->tty);
421 printf("\t Display: %s\n", i->display);
423 if (i->remote_host && i->remote_user)
424 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
425 else if (i->remote_host)
426 printf("\t Remote: %s\n", i->remote_host);
427 else if (i->remote_user)
428 printf("\t Remote: user %s\n", i->remote_user);
430 printf("\t Remote: Yes\n");
433 printf("\t Service: %s", i->service);
436 printf("; type %s", i->type);
439 printf("; class %s", i->class);
442 } else if (i->type) {
443 printf("\t Type: %s\n", i->type);
446 printf("; class %s", i->class);
448 printf("\t Class: %s\n", i->class);
451 printf("\t State: %s\n", i->state);
454 printf("\t Slice: %s\n", i->slice);
456 if (i->default_control_group) {
459 arg_all * OUTPUT_SHOW_ALL |
460 arg_full * OUTPUT_FULL_WIDTH;
462 printf("\t CGroup: %s\n", i->default_control_group);
464 if (arg_transport != TRANSPORT_SSH) {
471 show_cgroup_and_extra_by_spec(i->default_control_group,
472 "\t\t ", c, false, &i->leader,
473 i->leader > 0 ? 1 : 0,
479 static void print_user_status_info(UserStatusInfo *i) {
480 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
481 char since2[FORMAT_TIMESTAMP_MAX], *s2;
485 printf("%s (%u)\n", i->name, (unsigned) i->uid);
487 printf("%u\n", (unsigned) i->uid);
489 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
490 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
493 printf("\t Since: %s; %s\n", s2, s1);
495 printf("\t Since: %s\n", s2);
497 if (!isempty(i->state))
498 printf("\t State: %s\n", i->state);
501 printf("\t Slice: %s\n", i->slice);
503 if (!strv_isempty(i->sessions)) {
505 printf("\tSessions:");
507 STRV_FOREACH(l, i->sessions) {
508 if (streq_ptr(*l, i->display))
517 if (i->default_control_group) {
520 arg_all * OUTPUT_SHOW_ALL |
521 arg_full * OUTPUT_FULL_WIDTH;
523 printf("\t CGroup: %s\n", i->default_control_group);
525 if (arg_transport != TRANSPORT_SSH) {
532 show_cgroup_by_path(i->default_control_group, "\t\t ",
533 c, false, output_flags);
538 static void print_seat_status_info(SeatStatusInfo *i) {
541 printf("%s\n", strna(i->id));
543 if (!strv_isempty(i->sessions)) {
545 printf("\tSessions:");
547 STRV_FOREACH(l, i->sessions) {
548 if (streq_ptr(*l, i->active_session))
557 if (arg_transport != TRANSPORT_SSH) {
566 printf("\t Devices:\n");
568 show_sysfs(i->id, "\t\t ", c);
572 static void print_machine_status_info(MachineStatusInfo *i) {
573 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
574 char since2[FORMAT_TIMESTAMP_MAX], *s2;
577 fputs(strna(i->name), stdout);
579 if (!sd_id128_equal(i->id, SD_ID128_NULL))
580 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
584 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
585 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
588 printf("\t Since: %s; %s\n", s2, s1);
590 printf("\t Since: %s\n", s2);
593 _cleanup_free_ char *t = NULL;
595 printf("\t Leader: %u", (unsigned) i->leader);
597 get_process_comm(i->leader, &t);
605 printf("\t Service: %s", i->service);
608 printf("; class %s", i->class);
612 printf("\t Class: %s\n", i->class);
615 printf("\t Slice: %s\n", i->slice);
616 if (i->root_directory)
617 printf("\t Root: %s\n", i->root_directory);
619 if (i->default_control_group) {
622 arg_all * OUTPUT_SHOW_ALL |
623 arg_full * OUTPUT_FULL_WIDTH;
625 printf("\t CGroup: %s\n", i->default_control_group);
627 if (arg_transport != TRANSPORT_SSH) {
634 show_cgroup_and_extra_by_spec(i->default_control_group,
635 "\t\t ", c, false, &i->leader,
636 i->leader > 0 ? 1 : 0,
642 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *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, "Id"))
657 else if (streq(name, "Name"))
659 else if (streq(name, "DefaultControlGroup"))
660 i->default_control_group = s;
661 else if (streq(name, "TTY"))
663 else if (streq(name, "Display"))
665 else if (streq(name, "RemoteHost"))
667 else if (streq(name, "RemoteUser"))
669 else if (streq(name, "Service"))
671 else if (streq(name, "Type"))
673 else if (streq(name, "Class"))
675 else if (streq(name, "Slice"))
677 else if (streq(name, "State"))
683 case DBUS_TYPE_UINT32: {
686 dbus_message_iter_get_basic(iter, &u);
688 if (streq(name, "VTNr"))
690 else if (streq(name, "Leader"))
691 i->leader = (pid_t) u;
696 case DBUS_TYPE_BOOLEAN: {
699 dbus_message_iter_get_basic(iter, &b);
701 if (streq(name, "Remote"))
707 case DBUS_TYPE_UINT64: {
710 dbus_message_iter_get_basic(iter, &u);
712 if (streq(name, "Timestamp"))
713 i->timestamp = (usec_t) u;
718 case DBUS_TYPE_STRUCT: {
721 dbus_message_iter_recurse(iter, &sub);
723 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
726 dbus_message_iter_get_basic(&sub, &u);
729 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
732 dbus_message_iter_get_basic(&sub, &s);
745 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
750 switch (dbus_message_iter_get_arg_type(iter)) {
752 case DBUS_TYPE_STRING: {
755 dbus_message_iter_get_basic(iter, &s);
758 if (streq(name, "Name"))
760 else if (streq(name, "DefaultControlGroup"))
761 i->default_control_group = s;
762 else if (streq(name, "Slice"))
764 else if (streq(name, "State"))
770 case DBUS_TYPE_UINT32: {
773 dbus_message_iter_get_basic(iter, &u);
775 if (streq(name, "UID"))
781 case DBUS_TYPE_UINT64: {
784 dbus_message_iter_get_basic(iter, &u);
786 if (streq(name, "Timestamp"))
787 i->timestamp = (usec_t) u;
792 case DBUS_TYPE_STRUCT: {
795 dbus_message_iter_recurse(iter, &sub);
797 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
800 dbus_message_iter_get_basic(&sub, &s);
809 case DBUS_TYPE_ARRAY: {
811 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
812 DBusMessageIter sub, sub2;
814 dbus_message_iter_recurse(iter, &sub);
815 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
819 dbus_message_iter_recurse(&sub, &sub2);
821 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
822 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
825 l = strv_append(i->sessions, id);
829 strv_free(i->sessions);
833 dbus_message_iter_next(&sub);
844 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
849 switch (dbus_message_iter_get_arg_type(iter)) {
851 case DBUS_TYPE_STRING: {
854 dbus_message_iter_get_basic(iter, &s);
857 if (streq(name, "Id"))
863 case DBUS_TYPE_STRUCT: {
866 dbus_message_iter_recurse(iter, &sub);
868 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
871 dbus_message_iter_get_basic(&sub, &s);
874 i->active_session = s;
880 case DBUS_TYPE_ARRAY: {
882 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
883 DBusMessageIter sub, sub2;
885 dbus_message_iter_recurse(iter, &sub);
886 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
890 dbus_message_iter_recurse(&sub, &sub2);
892 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
893 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
896 l = strv_append(i->sessions, id);
900 strv_free(i->sessions);
904 dbus_message_iter_next(&sub);
915 static int status_property_machine(const char *name, DBusMessageIter *iter, MachineStatusInfo *i) {
920 switch (dbus_message_iter_get_arg_type(iter)) {
922 case DBUS_TYPE_STRING: {
925 dbus_message_iter_get_basic(iter, &s);
928 if (streq(name, "Name"))
930 else if (streq(name, "DefaultControlGroup"))
931 i->default_control_group = s;
932 else if (streq(name, "Class"))
934 else if (streq(name, "Service"))
936 else if (streq(name, "Slice"))
938 else if (streq(name, "RootDirectory"))
939 i->root_directory = s;
944 case DBUS_TYPE_UINT32: {
947 dbus_message_iter_get_basic(iter, &u);
949 if (streq(name, "Leader"))
950 i->leader = (pid_t) u;
955 case DBUS_TYPE_UINT64: {
958 dbus_message_iter_get_basic(iter, &u);
960 if (streq(name, "Timestamp"))
961 i->timestamp = (usec_t) u;
966 case DBUS_TYPE_ARRAY: {
969 dbus_message_iter_recurse(iter, &sub);
971 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE && streq(name, "Id")) {
975 dbus_message_iter_get_fixed_array(&sub, &v, &n);
977 i->id = SD_ID128_NULL;
979 memcpy(&i->id, v, n);
989 static int print_property(const char *name, DBusMessageIter *iter) {
993 if (arg_property && !strv_find(arg_property, name))
996 switch (dbus_message_iter_get_arg_type(iter)) {
998 case DBUS_TYPE_STRUCT: {
1001 dbus_message_iter_recurse(iter, &sub);
1003 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
1004 (streq(name, "Display") || streq(name, "ActiveSession"))) {
1007 dbus_message_iter_get_basic(&sub, &s);
1009 if (arg_all || !isempty(s))
1010 printf("%s=%s\n", name, s);
1016 case DBUS_TYPE_ARRAY:
1018 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
1019 DBusMessageIter sub, sub2;
1022 dbus_message_iter_recurse(iter, &sub);
1023 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
1027 dbus_message_iter_recurse(&sub, &sub2);
1029 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
1030 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
1034 printf("%s=%s", name, id);
1039 dbus_message_iter_next(&sub);
1042 if (!found && arg_all)
1043 printf("%s=\n", name);
1053 if (generic_print_property(name, iter, arg_all) > 0)
1057 printf("%s=[unprintable]\n", name);
1062 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
1063 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
1064 const char *interface = "";
1066 DBusMessageIter iter, sub, sub2, sub3;
1067 SessionStatusInfo session_info = {};
1068 UserStatusInfo user_info = {};
1069 SeatStatusInfo seat_info = {};
1070 MachineStatusInfo machine_info = {};
1075 r = bus_method_call_with_reply(
1077 "org.freedesktop.login1",
1079 "org.freedesktop.DBus.Properties",
1083 DBUS_TYPE_STRING, &interface,
1088 if (!dbus_message_iter_init(reply, &iter) ||
1089 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
1090 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
1091 log_error("Failed to parse reply.");
1096 dbus_message_iter_recurse(&iter, &sub);
1103 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1106 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
1107 log_error("Failed to parse reply.");
1112 dbus_message_iter_recurse(&sub, &sub2);
1114 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
1115 log_error("Failed to parse reply.");
1120 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
1121 log_error("Failed to parse reply.");
1126 dbus_message_iter_recurse(&sub2, &sub3);
1128 if (show_properties)
1129 r = print_property(name, &sub3);
1130 else if (strstr(verb, "session"))
1131 r = status_property_session(name, &sub3, &session_info);
1132 else if (strstr(verb, "user"))
1133 r = status_property_user(name, &sub3, &user_info);
1134 else if (strstr(verb, "seat"))
1135 r = status_property_seat(name, &sub3, &seat_info);
1137 r = status_property_machine(name, &sub3, &machine_info);
1140 log_error("Failed to parse reply.");
1144 dbus_message_iter_next(&sub);
1147 if (!show_properties) {
1148 if (strstr(verb, "session"))
1149 print_session_status_info(&session_info);
1150 else if (strstr(verb, "user"))
1151 print_user_status_info(&user_info);
1152 else if (strstr(verb, "seat"))
1153 print_seat_status_info(&seat_info);
1155 print_machine_status_info(&machine_info);
1161 strv_free(seat_info.sessions);
1162 strv_free(user_info.sessions);
1167 static int show(DBusConnection *bus, char **args, unsigned n) {
1168 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
1172 bool show_properties, new_line = false;
1177 dbus_error_init(&error);
1179 show_properties = !strstr(args[0], "status");
1181 pager_open_if_enabled();
1183 if (show_properties && n <= 1) {
1184 /* If not argument is specified inspect the manager
1187 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
1191 for (i = 1; i < n; i++) {
1192 const char *path = NULL;
1194 if (strstr(args[0], "session")) {
1196 ret = bus_method_call_with_reply (
1198 "org.freedesktop.login1",
1199 "/org/freedesktop/login1",
1200 "org.freedesktop.login1.Manager",
1204 DBUS_TYPE_STRING, &args[i],
1207 } else if (strstr(args[0], "user")) {
1211 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1213 log_error("User %s unknown.", args[i]);
1218 ret = bus_method_call_with_reply(
1220 "org.freedesktop.login1",
1221 "/org/freedesktop/login1",
1222 "org.freedesktop.login1.Manager",
1226 DBUS_TYPE_UINT32, &u,
1229 } else if (strstr(args[0], "seat")) {
1231 ret = bus_method_call_with_reply(
1233 "org.freedesktop.login1",
1234 "/org/freedesktop/login1",
1235 "org.freedesktop.login1.Manager",
1239 DBUS_TYPE_STRING, &args[i],
1244 ret = bus_method_call_with_reply(
1246 "org.freedesktop.login1",
1247 "/org/freedesktop/login1",
1248 "org.freedesktop.login1.Manager",
1252 DBUS_TYPE_STRING, &args[i],
1259 if (!dbus_message_get_args(reply, &error,
1260 DBUS_TYPE_OBJECT_PATH, &path,
1261 DBUS_TYPE_INVALID)) {
1262 log_error("Failed to parse reply: %s", bus_error_message(&error));
1267 r = show_one(args[0], bus, path, show_properties, &new_line);
1273 dbus_error_free(&error);
1278 static int activate(DBusConnection *bus, char **args, unsigned n) {
1284 for (i = 1; i < n; i++) {
1286 ret = bus_method_call_with_reply (
1288 "org.freedesktop.login1",
1289 "/org/freedesktop/login1",
1290 "org.freedesktop.login1.Manager",
1291 streq(args[0], "lock-session") ? "LockSession" :
1292 streq(args[0], "unlock-session") ? "UnlockSession" :
1293 streq(args[0], "terminate-session") ? "TerminateSession" :
1297 DBUS_TYPE_STRING, &args[i],
1307 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1313 arg_kill_who = "all";
1315 for (i = 1; i < n; i++) {
1318 r = bus_method_call_with_reply (
1320 "org.freedesktop.login1",
1321 "/org/freedesktop/login1",
1322 "org.freedesktop.login1.Manager",
1326 DBUS_TYPE_STRING, &args[i],
1327 DBUS_TYPE_STRING, &arg_kill_who,
1328 DBUS_TYPE_INT32, &arg_signal,
1337 static int kill_machine(DBusConnection *bus, char **args, unsigned n) {
1343 arg_kill_who = "all";
1345 for (i = 1; i < n; i++) {
1348 r = bus_method_call_with_reply (
1350 "org.freedesktop.login1",
1351 "/org/freedesktop/login1",
1352 "org.freedesktop.login1.Manager",
1356 DBUS_TYPE_STRING, &args[i],
1357 DBUS_TYPE_STRING, &arg_kill_who,
1358 DBUS_TYPE_INT32, &arg_signal,
1367 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1369 dbus_bool_t b, interactive = true;
1373 polkit_agent_open_if_enabled();
1375 b = streq(args[0], "enable-linger");
1377 for (i = 1; i < n; i++) {
1382 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1384 log_error("Failed to resolve user %s: %s", args[i], strerror(-r));
1389 r = bus_method_call_with_reply (
1391 "org.freedesktop.login1",
1392 "/org/freedesktop/login1",
1393 "org.freedesktop.login1.Manager",
1397 DBUS_TYPE_UINT32, &u,
1398 DBUS_TYPE_BOOLEAN, &b,
1399 DBUS_TYPE_BOOLEAN, &interactive,
1408 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1413 for (i = 1; i < n; i++) {
1418 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1420 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1425 r = bus_method_call_with_reply (
1427 "org.freedesktop.login1",
1428 "/org/freedesktop/login1",
1429 "org.freedesktop.login1.Manager",
1433 DBUS_TYPE_UINT32, &u,
1442 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1448 arg_kill_who = "all";
1450 for (i = 1; i < n; i++) {
1455 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1457 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1462 r = bus_method_call_with_reply (
1464 "org.freedesktop.login1",
1465 "/org/freedesktop/login1",
1466 "org.freedesktop.login1.Manager",
1470 DBUS_TYPE_UINT32, &u,
1471 DBUS_TYPE_INT32, &arg_signal,
1480 static int attach(DBusConnection *bus, char **args, unsigned n) {
1482 dbus_bool_t interactive = true;
1486 polkit_agent_open_if_enabled();
1488 for (i = 2; i < n; i++) {
1491 r = bus_method_call_with_reply (
1493 "org.freedesktop.login1",
1494 "/org/freedesktop/login1",
1495 "org.freedesktop.login1.Manager",
1499 DBUS_TYPE_STRING, &args[1],
1500 DBUS_TYPE_STRING, &args[i],
1501 DBUS_TYPE_BOOLEAN, &interactive,
1510 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1511 dbus_bool_t interactive = true;
1515 polkit_agent_open_if_enabled();
1517 return bus_method_call_with_reply (
1519 "org.freedesktop.login1",
1520 "/org/freedesktop/login1",
1521 "org.freedesktop.login1.Manager",
1525 DBUS_TYPE_BOOLEAN, &interactive,
1529 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1532 polkit_agent_open_if_enabled();
1534 return bus_method_call_with_reply (
1536 "org.freedesktop.login1",
1537 "/org/freedesktop/login1",
1538 "org.freedesktop.login1.Manager",
1539 streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1545 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1550 for (i = 1; i < n; i++) {
1553 r = bus_method_call_with_reply (
1555 "org.freedesktop.login1",
1556 "/org/freedesktop/login1",
1557 "org.freedesktop.login1.Manager",
1561 DBUS_TYPE_STRING, &args[i],
1570 static int terminate_machine(DBusConnection *bus, char **args, unsigned n) {
1575 for (i = 1; i < n; i++) {
1578 r = bus_method_call_with_reply (
1580 "org.freedesktop.login1",
1581 "/org/freedesktop/login1",
1582 "org.freedesktop.login1.Manager",
1586 DBUS_TYPE_STRING, &args[i],
1595 static int help(void) {
1597 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1598 "Send control commands to or query the login manager.\n\n"
1599 " -h --help Show this help\n"
1600 " --version Show package version\n"
1601 " -p --property=NAME Show only properties by this name\n"
1602 " -a --all Show all properties, including empty ones\n"
1603 " --kill-who=WHO Who to send signal to\n"
1604 " -l --full Do not ellipsize output\n"
1605 " -s --signal=SIGNAL Which signal to send\n"
1606 " --no-ask-password Don't prompt for password\n"
1607 " -H --host=[USER@]HOST Show information for remote host\n"
1608 " -P --privileged Acquire privileges before execution\n"
1609 " --no-pager Do not pipe output into a pager\n\n"
1611 " list-sessions List sessions\n"
1612 " session-status [ID...] Show session status\n"
1613 " show-session [ID...] Show properties of one or more sessions\n"
1614 " activate [ID] Activate a session\n"
1615 " lock-session [ID...] Screen lock one or more sessions\n"
1616 " unlock-session [ID...] Screen unlock one or more sessions\n"
1617 " lock-sessions Screen lock all current sessions\n"
1618 " unlock-sessions Screen unlock all current sessions\n"
1619 " terminate-session [ID...] Terminate one or more sessions\n"
1620 " kill-session [ID...] Send signal to processes of a session\n"
1621 " list-users List users\n"
1622 " user-status [USER...] Show user status\n"
1623 " show-user [USER...] Show properties of one or more users\n"
1624 " enable-linger [USER...] Enable linger state of one or more users\n"
1625 " disable-linger [USER...] Disable linger state of one or more users\n"
1626 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1627 " kill-user [USER...] Send signal to processes of a user\n"
1628 " list-seats List seats\n"
1629 " seat-status [NAME...] Show seat status\n"
1630 " show-seat [NAME...] Show properties of one or more seats\n"
1631 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1632 " flush-devices Flush all device associations\n"
1633 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n"
1634 " list-machines List running VMs and containers\n"
1635 " machine-status [NAME...] Show VM/container status\n"
1636 " show-machine [NAME...] Show properties of one or more VMs/containers\n"
1637 " terminate-machine [NAME...] Terminate one or more VMs/containers\n"
1638 " kill-machine [NAME...] Send signal to processes of a VM/container\n",
1639 program_invocation_short_name);
1644 static int parse_argv(int argc, char *argv[]) {
1647 ARG_VERSION = 0x100,
1650 ARG_NO_ASK_PASSWORD,
1653 static const struct option options[] = {
1654 { "help", no_argument, NULL, 'h' },
1655 { "version", no_argument, NULL, ARG_VERSION },
1656 { "property", required_argument, NULL, 'p' },
1657 { "all", no_argument, NULL, 'a' },
1658 { "full", no_argument, NULL, 'l' },
1659 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1660 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1661 { "signal", required_argument, NULL, 's' },
1662 { "host", required_argument, NULL, 'H' },
1663 { "privileged", no_argument, NULL, 'P' },
1664 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1665 { NULL, 0, NULL, 0 }
1673 while ((c = getopt_long(argc, argv, "hp:als:H:P", options, NULL)) >= 0) {
1682 puts(PACKAGE_STRING);
1683 puts(SYSTEMD_FEATURES);
1689 l = strv_append(arg_property, optarg);
1693 strv_free(arg_property);
1696 /* If the user asked for a particular
1697 * property, show it to him, even if it is
1712 arg_no_pager = true;
1715 case ARG_NO_ASK_PASSWORD:
1716 arg_ask_password = false;
1720 arg_kill_who = optarg;
1724 arg_signal = signal_from_string_try_harder(optarg);
1725 if (arg_signal < 0) {
1726 log_error("Failed to parse signal string %s.", optarg);
1732 arg_transport = TRANSPORT_POLKIT;
1736 arg_transport = TRANSPORT_SSH;
1737 parse_user_at_host(optarg, &arg_user, &arg_host);
1744 log_error("Unknown option code %c", c);
1752 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1754 static const struct {
1762 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1764 { "list-sessions", LESS, 1, list_sessions },
1765 { "session-status", MORE, 2, show },
1766 { "show-session", MORE, 1, show },
1767 { "activate", EQUAL, 2, activate },
1768 { "lock-session", MORE, 2, activate },
1769 { "unlock-session", MORE, 2, activate },
1770 { "lock-sessions", EQUAL, 1, lock_sessions },
1771 { "unlock-sessions", EQUAL, 1, lock_sessions },
1772 { "terminate-session", MORE, 2, activate },
1773 { "kill-session", MORE, 2, kill_session },
1774 { "list-users", EQUAL, 1, list_users },
1775 { "user-status", MORE, 2, show },
1776 { "show-user", MORE, 1, show },
1777 { "enable-linger", MORE, 2, enable_linger },
1778 { "disable-linger", MORE, 2, enable_linger },
1779 { "terminate-user", MORE, 2, terminate_user },
1780 { "kill-user", MORE, 2, kill_user },
1781 { "list-seats", EQUAL, 1, list_seats },
1782 { "seat-status", MORE, 2, show },
1783 { "show-seat", MORE, 1, show },
1784 { "attach", MORE, 3, attach },
1785 { "flush-devices", EQUAL, 1, flush_devices },
1786 { "terminate-seat", MORE, 2, terminate_seat },
1787 { "list-machines", EQUAL, 1, list_machines },
1788 { "machine-status", MORE, 2, show },
1789 { "show-machine", MORE, 1, show },
1790 { "terminate-machine", MORE, 2, terminate_machine },
1791 { "kill-machine", MORE, 2, kill_machine },
1801 left = argc - optind;
1804 /* Special rule: no arguments means "list-sessions" */
1807 if (streq(argv[optind], "help")) {
1812 for (i = 0; i < ELEMENTSOF(verbs); i++)
1813 if (streq(argv[optind], verbs[i].verb))
1816 if (i >= ELEMENTSOF(verbs)) {
1817 log_error("Unknown operation %s", argv[optind]);
1822 switch (verbs[i].argc_cmp) {
1825 if (left != verbs[i].argc) {
1826 log_error("Invalid number of arguments.");
1833 if (left < verbs[i].argc) {
1834 log_error("Too few arguments.");
1841 if (left > verbs[i].argc) {
1842 log_error("Too many arguments.");
1849 assert_not_reached("Unknown comparison operator.");
1853 log_error("Failed to get D-Bus connection: %s", error->message);
1857 return verbs[i].dispatch(bus, argv + optind, left);
1860 int main(int argc, char*argv[]) {
1861 int r, retval = EXIT_FAILURE;
1862 DBusConnection *bus = NULL;
1865 dbus_error_init(&error);
1867 setlocale(LC_ALL, "");
1868 log_parse_environment();
1871 r = parse_argv(argc, argv);
1875 retval = EXIT_SUCCESS;
1879 if (arg_transport == TRANSPORT_NORMAL)
1880 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1881 else if (arg_transport == TRANSPORT_POLKIT)
1882 bus_connect_system_polkit(&bus, &error);
1883 else if (arg_transport == TRANSPORT_SSH)
1884 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1886 assert_not_reached("Uh, invalid transport...");
1888 r = loginctl_main(bus, argc, argv, &error);
1889 retval = r < 0 ? EXIT_FAILURE : r;
1893 dbus_connection_flush(bus);
1894 dbus_connection_close(bus);
1895 dbus_connection_unref(bus);
1898 dbus_error_free(&error);
1901 strv_free(arg_property);