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
) {
1157 static int kill_session(DBusConnection
*bus
, char **args
, unsigned n
) {
1161 static int enable_linger(DBusConnection
*bus
, char **args
, unsigned n
) {
1165 static int attach(DBusConnection
*bus
, char **args
, unsigned n
) {
1169 static int flush_devices(DBusConnection
*bus
, char **args
, unsigned n
) {
1173 static int terminate_seat(DBusConnection
*bus
, char **args
, unsigned n
) {
1177 static int help(void) {
1179 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1180 "Send control commands to or query the login manager.\n\n"
1181 " -h --help Show this help\n"
1182 " --version Show package version\n"
1183 " -p --property=NAME Show only properties by this name\n"
1184 " -a --all Show all properties, including empty ones\n"
1185 " --kill-who=WHO Who to send signal to\n"
1186 " -s --signal=SIGNAL Which signal to send\n"
1187 " -H --host=[USER@]HOST\n"
1188 " Show information for remote host\n"
1189 " -P --privileged Acquire privileges before execution\n"
1190 " --no-pager Do not pipe output into a pager\n\n"
1192 " list-sessions List sessions\n"
1193 " session-status [ID...] Show session status\n"
1194 " show-session [ID...] Show property of one or more sessions\n"
1195 " activate [ID] Activate a session\n"
1196 " lock-session [ID...] Screen lock one or more sessions\n"
1197 " terminate-session [ID...] Terminate one or more sessions\n"
1198 " kill-session [ID...] Send signal to processes of a session\n"
1199 " list-users List users\n"
1200 " user-status [USER...] Show user status\n"
1201 " show-user [USER...] Show property of one or more users\n"
1202 " enable-linger [USER...] Enable linger state of one or more users\n"
1203 " disable-linger [USER...] Disable linger state of one or more users\n"
1204 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1205 " kill-user [USER...] Send signal to processes of a user\n"
1206 " list-seats List seats\n"
1207 " seat-status [NAME...] Show seat status\n"
1208 " show-seat [NAME...] Show property of one or more seats\n"
1209 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1210 " flush-devices Flush all device associations\n"
1211 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n"
1212 " kill-seat [NAME...] Send signal to processes of sessions on a seat\n",
1213 program_invocation_short_name
);
1218 static int parse_argv(int argc
, char *argv
[]) {
1221 ARG_VERSION
= 0x100,
1226 static const struct option options
[] = {
1227 { "help", no_argument
, NULL
, 'h' },
1228 { "version", no_argument
, NULL
, ARG_VERSION
},
1229 { "property", required_argument
, NULL
, 'p' },
1230 { "all", no_argument
, NULL
, 'a' },
1231 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1232 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
1233 { "signal", required_argument
, NULL
, 's' },
1234 { "host", required_argument
, NULL
, 'H' },
1235 { "privileged",no_argument
, NULL
, 'P' },
1236 { NULL
, 0, NULL
, 0 }
1244 while ((c
= getopt_long(argc
, argv
, "hp:as:H:P", options
, NULL
)) >= 0) {
1253 puts(PACKAGE_STRING
);
1255 puts(SYSTEMD_FEATURES
);
1261 l
= strv_append(arg_property
, optarg
);
1265 strv_free(arg_property
);
1268 /* If the user asked for a particular
1269 * property, show it to him, even if it is
1280 arg_no_pager
= true;
1284 arg_kill_who
= optarg
;
1288 arg_signal
= signal_from_string_try_harder(optarg
);
1289 if (arg_signal
< 0) {
1290 log_error("Failed to parse signal string %s.", optarg
);
1296 arg_transport
= TRANSPORT_POLKIT
;
1300 arg_transport
= TRANSPORT_SSH
;
1308 log_error("Unknown option code %c", c
);
1316 static int loginctl_main(DBusConnection
*bus
, int argc
, char *argv
[], DBusError
*error
) {
1318 static const struct {
1326 int (* const dispatch
)(DBusConnection
*bus
, char **args
, unsigned n
);
1328 { "list-sessions", LESS
, 1, list_sessions
},
1329 { "session-status", MORE
, 2, show
},
1330 { "show-session", MORE
, 2, show
},
1331 { "activate", EQUAL
, 2, activate
},
1332 { "lock-session", MORE
, 2, activate
},
1333 { "terminate-session", MORE
, 2, activate
},
1334 { "kill-session", MORE
, 2, kill_session
},
1335 { "list-users", EQUAL
, 1, list_users
},
1336 { "user-status", MORE
, 2, show
},
1337 { "show-user", MORE
, 2, show
},
1338 { "enable-linger", MORE
, 2, enable_linger
},
1339 { "disable-linger", MORE
, 2, enable_linger
},
1340 { "terminate-user", MORE
, 2, enable_linger
},
1341 { "kill-user", MORE
, 2, kill_session
},
1342 { "list-seats", EQUAL
, 1, list_seats
},
1343 { "seat-status", MORE
, 2, show
},
1344 { "show-seat", MORE
, 2, show
},
1345 { "attach", MORE
, 3, attach
},
1346 { "flush-devices", EQUAL
, 1, flush_devices
},
1347 { "terminate-seat", MORE
, 2, terminate_seat
},
1348 { "kill-seat", MORE
, 2, kill_session
},
1358 left
= argc
- optind
;
1361 /* Special rule: no arguments means "list-sessions" */
1364 if (streq(argv
[optind
], "help")) {
1369 for (i
= 0; i
< ELEMENTSOF(verbs
); i
++)
1370 if (streq(argv
[optind
], verbs
[i
].verb
))
1373 if (i
>= ELEMENTSOF(verbs
)) {
1374 log_error("Unknown operation %s", argv
[optind
]);
1379 switch (verbs
[i
].argc_cmp
) {
1382 if (left
!= verbs
[i
].argc
) {
1383 log_error("Invalid number of arguments.");
1390 if (left
< verbs
[i
].argc
) {
1391 log_error("Too few arguments.");
1398 if (left
> verbs
[i
].argc
) {
1399 log_error("Too many arguments.");
1406 assert_not_reached("Unknown comparison operator.");
1410 log_error("Failed to get D-Bus connection: %s", error
->message
);
1414 return verbs
[i
].dispatch(bus
, argv
+ optind
, left
);
1417 int main(int argc
, char*argv
[]) {
1418 int r
, retval
= EXIT_FAILURE
;
1419 DBusConnection
*bus
= NULL
;
1422 dbus_error_init(&error
);
1424 log_parse_environment();
1427 r
= parse_argv(argc
, argv
);
1431 retval
= EXIT_SUCCESS
;
1435 if (arg_transport
== TRANSPORT_NORMAL
)
1436 bus
= dbus_bus_get_private(DBUS_BUS_SYSTEM
, &error
);
1437 else if (arg_transport
== TRANSPORT_POLKIT
)
1438 bus_connect_system_polkit(&bus
, &error
);
1439 else if (arg_transport
== TRANSPORT_SSH
)
1440 bus_connect_system_ssh(NULL
, arg_host
, &bus
, &error
);
1442 assert_not_reached("Uh, invalid transport...");
1444 r
= loginctl_main(bus
, argc
, argv
, &error
);
1445 retval
= r
< 0 ? EXIT_FAILURE
: r
;
1449 dbus_connection_flush(bus
);
1450 dbus_connection_close(bus
);
1451 dbus_connection_unref(bus
);
1454 dbus_error_free(&error
);
1457 strv_free(arg_property
);