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
);