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) {
68 /* Cache result before we open the pager */
75 static int list_sessions(DBusConnection
*bus
, char **args
, unsigned n
) {
76 DBusMessage
*m
= NULL
, *reply
= NULL
;
79 DBusMessageIter iter
, sub
, sub2
;
82 dbus_error_init(&error
);
86 pager_open_if_enabled();
88 m
= dbus_message_new_method_call(
89 "org.freedesktop.login1",
90 "/org/freedesktop/login1",
91 "org.freedesktop.login1.Manager",
94 log_error("Could not allocate message.");
98 reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
);
100 log_error("Failed to issue method call: %s", bus_error_message(&error
));
105 if (!dbus_message_iter_init(reply
, &iter
) ||
106 dbus_message_iter_get_arg_type(&iter
) != DBUS_TYPE_ARRAY
||
107 dbus_message_iter_get_element_type(&iter
) != DBUS_TYPE_STRUCT
) {
108 log_error("Failed to parse reply.");
113 dbus_message_iter_recurse(&iter
, &sub
);
116 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
118 while (dbus_message_iter_get_arg_type(&sub
) != DBUS_TYPE_INVALID
) {
119 const char *id
, *user
, *seat
, *object
;
122 if (dbus_message_iter_get_arg_type(&sub
) != DBUS_TYPE_STRUCT
) {
123 log_error("Failed to parse reply.");
128 dbus_message_iter_recurse(&sub
, &sub2
);
130 if (bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_STRING
, &id
, true) < 0 ||
131 bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_UINT32
, &uid
, true) < 0 ||
132 bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_STRING
, &user
, true) < 0 ||
133 bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_STRING
, &seat
, true) < 0 ||
134 bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_OBJECT_PATH
, &object
, false) < 0) {
135 log_error("Failed to parse reply.");
140 printf("%10s %10u %-16s %-16s\n", id
, (unsigned) uid
, user
, seat
);
144 dbus_message_iter_next(&sub
);
148 printf("\n%u sessions listed.\n", k
);
154 dbus_message_unref(m
);
157 dbus_message_unref(reply
);
159 dbus_error_free(&error
);
164 static int list_users(DBusConnection
*bus
, char **args
, unsigned n
) {
165 DBusMessage
*m
= NULL
, *reply
= NULL
;
168 DBusMessageIter iter
, sub
, sub2
;
171 dbus_error_init(&error
);
175 pager_open_if_enabled();
177 m
= dbus_message_new_method_call(
178 "org.freedesktop.login1",
179 "/org/freedesktop/login1",
180 "org.freedesktop.login1.Manager",
183 log_error("Could not allocate message.");
187 reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
);
189 log_error("Failed to issue method call: %s", bus_error_message(&error
));
194 if (!dbus_message_iter_init(reply
, &iter
) ||
195 dbus_message_iter_get_arg_type(&iter
) != DBUS_TYPE_ARRAY
||
196 dbus_message_iter_get_element_type(&iter
) != DBUS_TYPE_STRUCT
) {
197 log_error("Failed to parse reply.");
202 dbus_message_iter_recurse(&iter
, &sub
);
205 printf("%10s %-16s\n", "UID", "USER");
207 while (dbus_message_iter_get_arg_type(&sub
) != DBUS_TYPE_INVALID
) {
208 const char *user
, *object
;
211 if (dbus_message_iter_get_arg_type(&sub
) != DBUS_TYPE_STRUCT
) {
212 log_error("Failed to parse reply.");
217 dbus_message_iter_recurse(&sub
, &sub2
);
219 if (bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_UINT32
, &uid
, true) < 0 ||
220 bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_STRING
, &user
, true) < 0 ||
221 bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_OBJECT_PATH
, &object
, false) < 0) {
222 log_error("Failed to parse reply.");
227 printf("%10u %-16s\n", (unsigned) uid
, user
);
231 dbus_message_iter_next(&sub
);
235 printf("\n%u users listed.\n", k
);
241 dbus_message_unref(m
);
244 dbus_message_unref(reply
);
246 dbus_error_free(&error
);
251 static int list_seats(DBusConnection
*bus
, char **args
, unsigned n
) {
252 DBusMessage
*m
= NULL
, *reply
= NULL
;
255 DBusMessageIter iter
, sub
, sub2
;
258 dbus_error_init(&error
);
262 pager_open_if_enabled();
264 m
= dbus_message_new_method_call(
265 "org.freedesktop.login1",
266 "/org/freedesktop/login1",
267 "org.freedesktop.login1.Manager",
270 log_error("Could not allocate message.");
274 reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
);
276 log_error("Failed to issue method call: %s", bus_error_message(&error
));
281 if (!dbus_message_iter_init(reply
, &iter
) ||
282 dbus_message_iter_get_arg_type(&iter
) != DBUS_TYPE_ARRAY
||
283 dbus_message_iter_get_element_type(&iter
) != DBUS_TYPE_STRUCT
) {
284 log_error("Failed to parse reply.");
289 dbus_message_iter_recurse(&iter
, &sub
);
292 printf("%-16s\n", "SEAT");
294 while (dbus_message_iter_get_arg_type(&sub
) != DBUS_TYPE_INVALID
) {
295 const char *seat
, *object
;
297 if (dbus_message_iter_get_arg_type(&sub
) != DBUS_TYPE_STRUCT
) {
298 log_error("Failed to parse reply.");
303 dbus_message_iter_recurse(&sub
, &sub2
);
305 if (bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_STRING
, &seat
, true) < 0 ||
306 bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_OBJECT_PATH
, &object
, false) < 0) {
307 log_error("Failed to parse reply.");
312 printf("%-16s\n", seat
);
316 dbus_message_iter_next(&sub
);
320 printf("\n%u seats listed.\n", k
);
326 dbus_message_unref(m
);
329 dbus_message_unref(reply
);
331 dbus_error_free(&error
);
336 typedef struct SessionStatusInfo
{
341 const char *control_group
;
347 const char *remote_host
;
348 const char *remote_user
;
355 typedef struct UserStatusInfo
{
359 const char *control_group
;
365 typedef struct SeatStatusInfo
{
367 const char *active_session
;
371 static void print_session_status_info(SessionStatusInfo
*i
) {
372 char since1
[FORMAT_TIMESTAMP_PRETTY_MAX
], *s1
;
373 char since2
[FORMAT_TIMESTAMP_MAX
], *s2
;
376 printf("%s - ", strna(i
->id
));
379 printf("%s (%u)\n", i
->name
, (unsigned) i
->uid
);
381 printf("%u\n", (unsigned) i
->uid
);
383 s1
= format_timestamp_pretty(since1
, sizeof(since1
), i
->timestamp
);
384 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
);
387 printf("\t Since: %s; %s\n", s2
, s1
);
389 printf("\t Since: %s\n", s2
);
394 printf("\t Leader: %u", (unsigned) i
->leader
);
396 get_process_comm(i
->leader
, &t
);
406 printf("\t Seat: %s", i
->seat
);
409 printf("; vc%i", i
->vtnr
);
415 printf("\t TTY: %s\n", i
->tty
);
417 printf("\t Display: %s\n", i
->display
);
419 if (i
->remote_host
&& i
->remote_user
)
420 printf("\t Remote: %s@%s\n", i
->remote_user
, i
->remote_host
);
421 else if (i
->remote_host
)
422 printf("\t Remote: %s\n", i
->remote_host
);
423 else if (i
->remote_user
)
424 printf("\t Remote: user %s\n", i
->remote_user
);
426 printf("\t Remote: Yes\n");
429 printf("\t Service: %s", i
->service
);
432 printf("; type %s", i
->type
);
436 printf("\t Type: %s\n", i
->type
);
438 printf("\t Active: %s\n", yes_no(i
->active
));
440 if (i
->control_group
) {
443 printf("\t CGroup: %s\n", i
->control_group
);
445 if (arg_transport
!= TRANSPORT_SSH
) {
452 show_cgroup_by_path(i
->control_group
, "\t\t ", c
, false);
457 static void print_user_status_info(UserStatusInfo
*i
) {
458 char since1
[FORMAT_TIMESTAMP_PRETTY_MAX
], *s1
;
459 char since2
[FORMAT_TIMESTAMP_MAX
], *s2
;
463 printf("%s (%u)\n", i
->name
, (unsigned) i
->uid
);
465 printf("%u\n", (unsigned) i
->uid
);
467 s1
= format_timestamp_pretty(since1
, sizeof(since1
), i
->timestamp
);
468 s2
= format_timestamp(since2
, sizeof(since2
), i
->timestamp
);
471 printf("\t Since: %s; %s\n", s2
, s1
);
473 printf("\t Since: %s\n", s2
);
475 if (!isempty(i
->state
))
476 printf("\t State: %s\n", i
->state
);
478 if (!strv_isempty(i
->sessions
)) {
480 printf("\tSessions:");
482 STRV_FOREACH(l
, i
->sessions
) {
483 if (streq_ptr(*l
, i
->display
))
492 if (i
->control_group
) {
495 printf("\t CGroup: %s\n", i
->control_group
);
497 if (arg_transport
!= TRANSPORT_SSH
) {
504 show_cgroup_by_path(i
->control_group
, "\t\t ", c
, false);
509 static void print_seat_status_info(SeatStatusInfo
*i
) {
512 printf("%s\n", strna(i
->id
));
514 if (!strv_isempty(i
->sessions
)) {
516 printf("\tSessions:");
518 STRV_FOREACH(l
, i
->sessions
) {
519 if (streq_ptr(*l
, i
->active_session
))
528 if (arg_transport
!= TRANSPORT_SSH
) {
537 printf("\t Devices:\n");
539 show_sysfs(i
->id
, "\t\t ", c
);
543 static int status_property_session(const char *name
, DBusMessageIter
*iter
, SessionStatusInfo
*i
) {
548 switch (dbus_message_iter_get_arg_type(iter
)) {
550 case DBUS_TYPE_STRING
: {
553 dbus_message_iter_get_basic(iter
, &s
);
556 if (streq(name
, "Id"))
558 else if (streq(name
, "Name"))
560 else if (streq(name
, "ControlGroupPath"))
561 i
->control_group
= s
;
562 else if (streq(name
, "TTY"))
564 else if (streq(name
, "Display"))
566 else if (streq(name
, "RemoteHost"))
568 else if (streq(name
, "RemoteUser"))
570 else if (streq(name
, "Service"))
572 else if (streq(name
, "Type"))
578 case DBUS_TYPE_UINT32
: {
581 dbus_message_iter_get_basic(iter
, &u
);
583 if (streq(name
, "VTNr"))
585 else if (streq(name
, "Leader"))
586 i
->leader
= (pid_t
) u
;
591 case DBUS_TYPE_BOOLEAN
: {
594 dbus_message_iter_get_basic(iter
, &b
);
596 if (streq(name
, "Remote"))
598 else if (streq(name
, "Active"))
604 case DBUS_TYPE_UINT64
: {
607 dbus_message_iter_get_basic(iter
, &u
);
609 if (streq(name
, "Timestamp"))
610 i
->timestamp
= (usec_t
) u
;
615 case DBUS_TYPE_STRUCT
: {
618 dbus_message_iter_recurse(iter
, &sub
);
620 if (dbus_message_iter_get_arg_type(&sub
) == DBUS_TYPE_UINT32
&& streq(name
, "User")) {
623 dbus_message_iter_get_basic(&sub
, &u
);
626 } else if (dbus_message_iter_get_arg_type(&sub
) == DBUS_TYPE_STRING
&& streq(name
, "Seat")) {
629 dbus_message_iter_get_basic(&sub
, &s
);
642 static int status_property_user(const char *name
, DBusMessageIter
*iter
, UserStatusInfo
*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
, "Name"))
657 else if (streq(name
, "ControlGroupPath"))
658 i
->control_group
= s
;
659 else if (streq(name
, "State"))
665 case DBUS_TYPE_UINT32
: {
668 dbus_message_iter_get_basic(iter
, &u
);
670 if (streq(name
, "UID"))
676 case DBUS_TYPE_UINT64
: {
679 dbus_message_iter_get_basic(iter
, &u
);
681 if (streq(name
, "Timestamp"))
682 i
->timestamp
= (usec_t
) u
;
687 case DBUS_TYPE_STRUCT
: {
690 dbus_message_iter_recurse(iter
, &sub
);
692 if (dbus_message_iter_get_arg_type(&sub
) == DBUS_TYPE_STRING
&& streq(name
, "Display")) {
695 dbus_message_iter_get_basic(&sub
, &s
);
704 case DBUS_TYPE_ARRAY
: {
706 if (dbus_message_iter_get_element_type(iter
) == DBUS_TYPE_STRUCT
&& streq(name
, "Sessions")) {
707 DBusMessageIter sub
, sub2
;
709 dbus_message_iter_recurse(iter
, &sub
);
710 while (dbus_message_iter_get_arg_type(&sub
) == DBUS_TYPE_STRUCT
) {
714 dbus_message_iter_recurse(&sub
, &sub2
);
716 if (bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_STRING
, &id
, true) >= 0 &&
717 bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_OBJECT_PATH
, &path
, false) >= 0) {
720 l
= strv_append(i
->sessions
, id
);
724 strv_free(i
->sessions
);
728 dbus_message_iter_next(&sub
);
739 static int status_property_seat(const char *name
, DBusMessageIter
*iter
, SeatStatusInfo
*i
) {
744 switch (dbus_message_iter_get_arg_type(iter
)) {
746 case DBUS_TYPE_STRING
: {
749 dbus_message_iter_get_basic(iter
, &s
);
752 if (streq(name
, "Id"))
758 case DBUS_TYPE_STRUCT
: {
761 dbus_message_iter_recurse(iter
, &sub
);
763 if (dbus_message_iter_get_arg_type(&sub
) == DBUS_TYPE_STRING
&& streq(name
, "ActiveSession")) {
766 dbus_message_iter_get_basic(&sub
, &s
);
769 i
->active_session
= s
;
775 case DBUS_TYPE_ARRAY
: {
777 if (dbus_message_iter_get_element_type(iter
) == DBUS_TYPE_STRUCT
&& streq(name
, "Sessions")) {
778 DBusMessageIter sub
, sub2
;
780 dbus_message_iter_recurse(iter
, &sub
);
781 while (dbus_message_iter_get_arg_type(&sub
) == DBUS_TYPE_STRUCT
) {
785 dbus_message_iter_recurse(&sub
, &sub2
);
787 if (bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_STRING
, &id
, true) >= 0 &&
788 bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_OBJECT_PATH
, &path
, false) >= 0) {
791 l
= strv_append(i
->sessions
, id
);
795 strv_free(i
->sessions
);
799 dbus_message_iter_next(&sub
);
810 static int print_property(const char *name
, DBusMessageIter
*iter
) {
814 if (arg_property
&& !strv_find(arg_property
, name
))
817 switch (dbus_message_iter_get_arg_type(iter
)) {
819 case DBUS_TYPE_STRUCT
: {
822 dbus_message_iter_recurse(iter
, &sub
);
824 if (dbus_message_iter_get_arg_type(&sub
) == DBUS_TYPE_STRING
&&
825 (streq(name
, "Display") || streq(name
, "ActiveSession"))) {
828 dbus_message_iter_get_basic(&sub
, &s
);
830 if (arg_all
|| !isempty(s
))
831 printf("%s=%s\n", name
, s
);
837 case DBUS_TYPE_ARRAY
:
839 if (dbus_message_iter_get_element_type(iter
) == DBUS_TYPE_STRUCT
&& streq(name
, "Sessions")) {
840 DBusMessageIter sub
, sub2
;
843 dbus_message_iter_recurse(iter
, &sub
);
844 while (dbus_message_iter_get_arg_type(&sub
) == DBUS_TYPE_STRUCT
) {
848 dbus_message_iter_recurse(&sub
, &sub2
);
850 if (bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_STRING
, &id
, true) >= 0 &&
851 bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_OBJECT_PATH
, &path
, false) >= 0) {
855 printf("%s=%s", name
, id
);
860 dbus_message_iter_next(&sub
);
863 if (!found
&& arg_all
)
864 printf("%s=\n", name
);
874 if (generic_print_property(name
, iter
, arg_all
) > 0)
878 printf("%s=[unprintable]\n", name
);
883 static int show_one(const char *verb
, DBusConnection
*bus
, const char *path
, bool show_properties
, bool *new_line
) {
884 DBusMessage
*m
= NULL
, *reply
= NULL
;
885 const char *interface
= "";
888 DBusMessageIter iter
, sub
, sub2
, sub3
;
889 SessionStatusInfo session_info
;
890 UserStatusInfo user_info
;
891 SeatStatusInfo seat_info
;
901 dbus_error_init(&error
);
903 m
= dbus_message_new_method_call(
904 "org.freedesktop.login1",
906 "org.freedesktop.DBus.Properties",
909 log_error("Could not allocate message.");
914 if (!dbus_message_append_args(m
,
915 DBUS_TYPE_STRING
, &interface
,
916 DBUS_TYPE_INVALID
)) {
917 log_error("Could not append arguments to message.");
922 reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
);
924 log_error("Failed to issue method call: %s", bus_error_message(&error
));
929 if (!dbus_message_iter_init(reply
, &iter
) ||
930 dbus_message_iter_get_arg_type(&iter
) != DBUS_TYPE_ARRAY
||
931 dbus_message_iter_get_element_type(&iter
) != DBUS_TYPE_DICT_ENTRY
) {
932 log_error("Failed to parse reply.");
937 dbus_message_iter_recurse(&iter
, &sub
);
944 while (dbus_message_iter_get_arg_type(&sub
) != DBUS_TYPE_INVALID
) {
947 if (dbus_message_iter_get_arg_type(&sub
) != DBUS_TYPE_DICT_ENTRY
) {
948 log_error("Failed to parse reply.");
953 dbus_message_iter_recurse(&sub
, &sub2
);
955 if (bus_iter_get_basic_and_next(&sub2
, DBUS_TYPE_STRING
, &name
, true) < 0) {
956 log_error("Failed to parse reply.");
961 if (dbus_message_iter_get_arg_type(&sub2
) != DBUS_TYPE_VARIANT
) {
962 log_error("Failed to parse reply.");
967 dbus_message_iter_recurse(&sub2
, &sub3
);
970 r
= print_property(name
, &sub3
);
971 else if (strstr(verb
, "session"))
972 r
= status_property_session(name
, &sub3
, &session_info
);
973 else if (strstr(verb
, "user"))
974 r
= status_property_user(name
, &sub3
, &user_info
);
976 r
= status_property_seat(name
, &sub3
, &seat_info
);
979 log_error("Failed to parse reply.");
984 dbus_message_iter_next(&sub
);
987 if (!show_properties
) {
988 if (strstr(verb
, "session"))
989 print_session_status_info(&session_info
);
990 else if (strstr(verb
, "user"))
991 print_user_status_info(&user_info
);
993 print_seat_status_info(&seat_info
);
996 strv_free(seat_info
.sessions
);
997 strv_free(user_info
.sessions
);
1003 dbus_message_unref(m
);
1006 dbus_message_unref(reply
);
1008 dbus_error_free(&error
);
1013 static int show(DBusConnection
*bus
, char **args
, unsigned n
) {
1014 DBusMessage
*m
= NULL
, *reply
= NULL
;
1018 bool show_properties
, new_line
= false;
1023 dbus_error_init(&error
);
1025 show_properties
= !strstr(args
[0], "status");
1027 if (show_properties
)
1028 pager_open_if_enabled();
1030 if (show_properties
&& n
<= 1) {
1031 /* If not argument is specified inspect the manager
1034 ret
= show_one(args
[0], bus
, "/org/freedesktop/login1", show_properties
, &new_line
);
1038 for (i
= 1; i
< n
; i
++) {
1039 const char *path
= NULL
;
1041 if (strstr(args
[0], "session")) {
1043 m
= dbus_message_new_method_call(
1044 "org.freedesktop.login1",
1045 "/org/freedesktop/login1",
1046 "org.freedesktop.login1.Manager",
1049 log_error("Could not allocate message.");
1054 if (!dbus_message_append_args(m
,
1055 DBUS_TYPE_STRING
, &args
[i
],
1056 DBUS_TYPE_INVALID
)) {
1057 log_error("Could not append arguments to message.");
1062 } else if (strstr(args
[0], "user")) {
1066 ret
= get_user_creds((const char**) (args
+i
), &uid
, NULL
, NULL
);
1068 log_error("User %s unknown.", args
[i
]);
1072 m
= dbus_message_new_method_call(
1073 "org.freedesktop.login1",
1074 "/org/freedesktop/login1",
1075 "org.freedesktop.login1.Manager",
1078 log_error("Could not allocate message.");
1084 if (!dbus_message_append_args(m
,
1085 DBUS_TYPE_UINT32
, &u
,
1086 DBUS_TYPE_INVALID
)) {
1087 log_error("Could not append arguments to message.");
1093 m
= dbus_message_new_method_call(
1094 "org.freedesktop.login1",
1095 "/org/freedesktop/login1",
1096 "org.freedesktop.login1.Manager",
1099 log_error("Could not allocate message.");
1104 if (!dbus_message_append_args(m
,
1105 DBUS_TYPE_STRING
, &args
[i
],
1106 DBUS_TYPE_INVALID
)) {
1107 log_error("Could not append arguments to message.");
1113 reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
);
1115 log_error("Failed to issue method call: %s", bus_error_message(&error
));
1120 if (!dbus_message_get_args(reply
, &error
,
1121 DBUS_TYPE_OBJECT_PATH
, &path
,
1122 DBUS_TYPE_INVALID
)) {
1123 log_error("Failed to parse reply: %s", bus_error_message(&error
));
1128 r
= show_one(args
[0], bus
, path
, show_properties
, &new_line
);
1132 dbus_message_unref(m
);
1133 dbus_message_unref(reply
);
1139 dbus_message_unref(m
);
1142 dbus_message_unref(reply
);
1144 dbus_error_free(&error
);
1149 static int activate(DBusConnection
*bus
, char **args
, unsigned n
) {
1150 DBusMessage
*m
= NULL
;
1158 dbus_error_init(&error
);
1160 for (i
= 1; i
< n
; i
++) {
1163 m
= dbus_message_new_method_call(
1164 "org.freedesktop.login1",
1165 "/org/freedesktop/login1",
1166 "org.freedesktop.login1.Manager",
1167 streq(args
[0], "lock-session") ? "LockSession" :
1168 streq(args
[0], "unlock-session") ? "UnlockSession" :
1169 streq(args
[0], "terminate-session") ? "TerminateSession" :
1172 log_error("Could not allocate message.");
1177 if (!dbus_message_append_args(m
,
1178 DBUS_TYPE_STRING
, &args
[i
],
1179 DBUS_TYPE_INVALID
)) {
1180 log_error("Could not append arguments to message.");
1185 reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
);
1187 log_error("Failed to issue method call: %s", bus_error_message(&error
));
1192 dbus_message_unref(m
);
1193 dbus_message_unref(reply
);
1199 dbus_message_unref(m
);
1201 dbus_error_free(&error
);
1206 static int kill_session(DBusConnection
*bus
, char **args
, unsigned n
) {
1207 DBusMessage
*m
= NULL
;
1215 dbus_error_init(&error
);
1218 arg_kill_who
= "all";
1220 for (i
= 1; i
< n
; i
++) {
1223 m
= dbus_message_new_method_call(
1224 "org.freedesktop.login1",
1225 "/org/freedesktop/login1",
1226 "org.freedesktop.login1.Manager",
1229 log_error("Could not allocate message.");
1234 if (!dbus_message_append_args(m
,
1235 DBUS_TYPE_STRING
, &args
[i
],
1236 DBUS_TYPE_STRING
, &arg_kill_who
,
1237 DBUS_TYPE_INT32
, arg_signal
,
1238 DBUS_TYPE_INVALID
)) {
1239 log_error("Could not append arguments to message.");
1244 reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
);
1246 log_error("Failed to issue method call: %s", bus_error_message(&error
));
1251 dbus_message_unref(m
);
1252 dbus_message_unref(reply
);
1258 dbus_message_unref(m
);
1260 dbus_error_free(&error
);
1265 static int enable_linger(DBusConnection
*bus
, char **args
, unsigned n
) {
1266 DBusMessage
*m
= NULL
;
1270 dbus_bool_t b
, interactive
= true;
1275 dbus_error_init(&error
);
1277 b
= streq(args
[0], "enable-linger");
1279 for (i
= 1; i
< n
; i
++) {
1284 m
= dbus_message_new_method_call(
1285 "org.freedesktop.login1",
1286 "/org/freedesktop/login1",
1287 "org.freedesktop.login1.Manager",
1290 log_error("Could not allocate message.");
1295 ret
= get_user_creds((const char**) (args
+i
), &uid
, NULL
, NULL
);
1297 log_error("Failed to resolve user %s: %s", args
[i
], strerror(-ret
));
1302 if (!dbus_message_append_args(m
,
1303 DBUS_TYPE_UINT32
, &u
,
1304 DBUS_TYPE_BOOLEAN
, &b
,
1305 DBUS_TYPE_BOOLEAN
, &interactive
,
1306 DBUS_TYPE_INVALID
)) {
1307 log_error("Could not append arguments to message.");
1312 reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
);
1314 log_error("Failed to issue method call: %s", bus_error_message(&error
));
1319 dbus_message_unref(m
);
1320 dbus_message_unref(reply
);
1328 dbus_message_unref(m
);
1330 dbus_error_free(&error
);
1335 static int terminate_user(DBusConnection
*bus
, char **args
, unsigned n
) {
1336 DBusMessage
*m
= NULL
;
1344 dbus_error_init(&error
);
1346 for (i
= 1; i
< n
; i
++) {
1351 m
= dbus_message_new_method_call(
1352 "org.freedesktop.login1",
1353 "/org/freedesktop/login1",
1354 "org.freedesktop.login1.Manager",
1357 log_error("Could not allocate message.");
1362 ret
= get_user_creds((const char**) (args
+i
), &uid
, NULL
, NULL
);
1364 log_error("Failed to look up user %s: %s", args
[i
], strerror(-ret
));
1369 if (!dbus_message_append_args(m
,
1370 DBUS_TYPE_UINT32
, &u
,
1371 DBUS_TYPE_INVALID
)) {
1372 log_error("Could not append arguments to message.");
1377 reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
);
1379 log_error("Failed to issue method call: %s", bus_error_message(&error
));
1384 dbus_message_unref(m
);
1385 dbus_message_unref(reply
);
1393 dbus_message_unref(m
);
1395 dbus_error_free(&error
);
1400 static int kill_user(DBusConnection
*bus
, char **args
, unsigned n
) {
1401 DBusMessage
*m
= NULL
;
1409 dbus_error_init(&error
);
1412 arg_kill_who
= "all";
1414 for (i
= 1; i
< n
; i
++) {
1419 m
= dbus_message_new_method_call(
1420 "org.freedesktop.login1",
1421 "/org/freedesktop/login1",
1422 "org.freedesktop.login1.Manager",
1425 log_error("Could not allocate message.");
1430 ret
= get_user_creds((const char**) (args
+i
), &uid
, NULL
, NULL
);
1432 log_error("Failed to look up user %s: %s", args
[i
], strerror(-ret
));
1437 if (!dbus_message_append_args(m
,
1438 DBUS_TYPE_UINT32
, &u
,
1439 DBUS_TYPE_INT32
, arg_signal
,
1440 DBUS_TYPE_INVALID
)) {
1441 log_error("Could not append arguments to message.");
1446 reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
);
1448 log_error("Failed to issue method call: %s", bus_error_message(&error
));
1453 dbus_message_unref(m
);
1454 dbus_message_unref(reply
);
1462 dbus_message_unref(m
);
1464 dbus_error_free(&error
);
1469 static int attach(DBusConnection
*bus
, char **args
, unsigned n
) {
1470 DBusMessage
*m
= NULL
;
1474 dbus_bool_t interactive
= true;
1479 dbus_error_init(&error
);
1481 for (i
= 2; i
< n
; i
++) {
1484 m
= dbus_message_new_method_call(
1485 "org.freedesktop.login1",
1486 "/org/freedesktop/login1",
1487 "org.freedesktop.login1.Manager",
1490 log_error("Could not allocate message.");
1495 if (!dbus_message_append_args(m
,
1496 DBUS_TYPE_STRING
, &args
[1],
1497 DBUS_TYPE_STRING
, &args
[i
],
1498 DBUS_TYPE_BOOLEAN
, &interactive
,
1499 DBUS_TYPE_INVALID
)) {
1500 log_error("Could not append arguments to message.");
1505 reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
);
1507 log_error("Failed to issue method call: %s", bus_error_message(&error
));
1512 dbus_message_unref(m
);
1513 dbus_message_unref(reply
);
1519 dbus_message_unref(m
);
1521 dbus_error_free(&error
);
1526 static int flush_devices(DBusConnection
*bus
, char **args
, unsigned n
) {
1527 DBusMessage
*m
= NULL
, *reply
= NULL
;
1530 dbus_bool_t interactive
= true;
1535 dbus_error_init(&error
);
1537 m
= dbus_message_new_method_call(
1538 "org.freedesktop.login1",
1539 "/org/freedesktop/login1",
1540 "org.freedesktop.login1.Manager",
1543 log_error("Could not allocate message.");
1548 if (!dbus_message_append_args(m
,
1549 DBUS_TYPE_BOOLEAN
, &interactive
,
1550 DBUS_TYPE_INVALID
)) {
1551 log_error("Could not append arguments to message.");
1556 reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
);
1558 log_error("Failed to issue method call: %s", bus_error_message(&error
));
1565 dbus_message_unref(m
);
1568 dbus_message_unref(reply
);
1570 dbus_error_free(&error
);
1575 static int terminate_seat(DBusConnection
*bus
, char **args
, unsigned n
) {
1576 DBusMessage
*m
= NULL
;
1584 dbus_error_init(&error
);
1586 for (i
= 1; i
< n
; i
++) {
1589 m
= dbus_message_new_method_call(
1590 "org.freedesktop.login1",
1591 "/org/freedesktop/login1",
1592 "org.freedesktop.login1.Manager",
1595 log_error("Could not allocate message.");
1600 if (!dbus_message_append_args(m
,
1601 DBUS_TYPE_STRING
, &args
[i
],
1602 DBUS_TYPE_INVALID
)) {
1603 log_error("Could not append arguments to message.");
1608 reply
= dbus_connection_send_with_reply_and_block(bus
, m
, -1, &error
);
1610 log_error("Failed to issue method call: %s", bus_error_message(&error
));
1615 dbus_message_unref(m
);
1616 dbus_message_unref(reply
);
1622 dbus_message_unref(m
);
1624 dbus_error_free(&error
);
1629 static int help(void) {
1631 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1632 "Send control commands to or query the login manager.\n\n"
1633 " -h --help Show this help\n"
1634 " --version Show package version\n"
1635 " -p --property=NAME Show only properties by this name\n"
1636 " -a --all Show all properties, including empty ones\n"
1637 " --kill-who=WHO Who to send signal to\n"
1638 " -s --signal=SIGNAL Which signal to send\n"
1639 " -H --host=[USER@]HOST\n"
1640 " Show information for remote host\n"
1641 " -P --privileged Acquire privileges before execution\n"
1642 " --no-pager Do not pipe output into a pager\n\n"
1644 " list-sessions List sessions\n"
1645 " session-status [ID...] Show session status\n"
1646 " show-session [ID...] Show properties of one or more sessions\n"
1647 " activate [ID] Activate a session\n"
1648 " lock-session [ID...] Screen lock one or more sessions\n"
1649 " unlock-session [ID...] Screen unlock one or more sessions\n"
1650 " terminate-session [ID...] Terminate one or more sessions\n"
1651 " kill-session [ID...] Send signal to processes of a session\n"
1652 " list-users List users\n"
1653 " user-status [USER...] Show user status\n"
1654 " show-user [USER...] Show properties of one or more users\n"
1655 " enable-linger [USER...] Enable linger state of one or more users\n"
1656 " disable-linger [USER...] Disable linger state of one or more users\n"
1657 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1658 " kill-user [USER...] Send signal to processes of a user\n"
1659 " list-seats List seats\n"
1660 " seat-status [NAME...] Show seat status\n"
1661 " show-seat [NAME...] Show properties of one or more seats\n"
1662 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1663 " flush-devices Flush all device associations\n"
1664 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1665 program_invocation_short_name
);
1670 static int parse_argv(int argc
, char *argv
[]) {
1673 ARG_VERSION
= 0x100,
1678 static const struct option options
[] = {
1679 { "help", no_argument
, NULL
, 'h' },
1680 { "version", no_argument
, NULL
, ARG_VERSION
},
1681 { "property", required_argument
, NULL
, 'p' },
1682 { "all", no_argument
, NULL
, 'a' },
1683 { "no-pager", no_argument
, NULL
, ARG_NO_PAGER
},
1684 { "kill-who", required_argument
, NULL
, ARG_KILL_WHO
},
1685 { "signal", required_argument
, NULL
, 's' },
1686 { "host", required_argument
, NULL
, 'H' },
1687 { "privileged",no_argument
, NULL
, 'P' },
1688 { NULL
, 0, NULL
, 0 }
1696 while ((c
= getopt_long(argc
, argv
, "hp:as:H:P", options
, NULL
)) >= 0) {
1705 puts(PACKAGE_STRING
);
1707 puts(SYSTEMD_FEATURES
);
1713 l
= strv_append(arg_property
, optarg
);
1717 strv_free(arg_property
);
1720 /* If the user asked for a particular
1721 * property, show it to him, even if it is
1732 arg_no_pager
= true;
1736 arg_kill_who
= optarg
;
1740 arg_signal
= signal_from_string_try_harder(optarg
);
1741 if (arg_signal
< 0) {
1742 log_error("Failed to parse signal string %s.", optarg
);
1748 arg_transport
= TRANSPORT_POLKIT
;
1752 arg_transport
= TRANSPORT_SSH
;
1760 log_error("Unknown option code %c", c
);
1768 static int loginctl_main(DBusConnection
*bus
, int argc
, char *argv
[], DBusError
*error
) {
1770 static const struct {
1778 int (* const dispatch
)(DBusConnection
*bus
, char **args
, unsigned n
);
1780 { "list-sessions", LESS
, 1, list_sessions
},
1781 { "session-status", MORE
, 2, show
},
1782 { "show-session", MORE
, 1, show
},
1783 { "activate", EQUAL
, 2, activate
},
1784 { "lock-session", MORE
, 2, activate
},
1785 { "unlock-session", MORE
, 2, activate
},
1786 { "terminate-session", MORE
, 2, activate
},
1787 { "kill-session", MORE
, 2, kill_session
},
1788 { "list-users", EQUAL
, 1, list_users
},
1789 { "user-status", MORE
, 2, show
},
1790 { "show-user", MORE
, 1, show
},
1791 { "enable-linger", MORE
, 2, enable_linger
},
1792 { "disable-linger", MORE
, 2, enable_linger
},
1793 { "terminate-user", MORE
, 2, terminate_user
},
1794 { "kill-user", MORE
, 2, kill_user
},
1795 { "list-seats", EQUAL
, 1, list_seats
},
1796 { "seat-status", MORE
, 2, show
},
1797 { "show-seat", MORE
, 1, show
},
1798 { "attach", MORE
, 3, attach
},
1799 { "flush-devices", EQUAL
, 1, flush_devices
},
1800 { "terminate-seat", MORE
, 2, terminate_seat
},
1810 left
= argc
- optind
;
1813 /* Special rule: no arguments means "list-sessions" */
1816 if (streq(argv
[optind
], "help")) {
1821 for (i
= 0; i
< ELEMENTSOF(verbs
); i
++)
1822 if (streq(argv
[optind
], verbs
[i
].verb
))
1825 if (i
>= ELEMENTSOF(verbs
)) {
1826 log_error("Unknown operation %s", argv
[optind
]);
1831 switch (verbs
[i
].argc_cmp
) {
1834 if (left
!= verbs
[i
].argc
) {
1835 log_error("Invalid number of arguments.");
1842 if (left
< verbs
[i
].argc
) {
1843 log_error("Too few arguments.");
1850 if (left
> verbs
[i
].argc
) {
1851 log_error("Too many arguments.");
1858 assert_not_reached("Unknown comparison operator.");
1862 log_error("Failed to get D-Bus connection: %s", error
->message
);
1866 return verbs
[i
].dispatch(bus
, argv
+ optind
, left
);
1869 int main(int argc
, char*argv
[]) {
1870 int r
, retval
= EXIT_FAILURE
;
1871 DBusConnection
*bus
= NULL
;
1874 dbus_error_init(&error
);
1876 log_parse_environment();
1879 r
= parse_argv(argc
, argv
);
1883 retval
= EXIT_SUCCESS
;
1887 if (arg_transport
== TRANSPORT_NORMAL
)
1888 bus
= dbus_bus_get_private(DBUS_BUS_SYSTEM
, &error
);
1889 else if (arg_transport
== TRANSPORT_POLKIT
)
1890 bus_connect_system_polkit(&bus
, &error
);
1891 else if (arg_transport
== TRANSPORT_SSH
)
1892 bus_connect_system_ssh(NULL
, arg_host
, &bus
, &error
);
1894 assert_not_reached("Uh, invalid transport...");
1896 r
= loginctl_main(bus
, argc
, argv
, &error
);
1897 retval
= r
< 0 ? EXIT_FAILURE
: r
;
1901 dbus_connection_flush(bus
);
1902 dbus_connection_close(bus
);
1903 dbus_connection_unref(bus
);
1906 dbus_error_free(&error
);
1909 strv_free(arg_property
);