]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/loginctl.c
087e52d5039c17c8eaf1e8f0d872aad00f6e1550
[thirdparty/systemd.git] / src / login / loginctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <dbus/dbus.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <getopt.h>
27 #include <pwd.h>
28 #include <locale.h>
29
30 #include "log.h"
31 #include "util.h"
32 #include "macro.h"
33 #include "pager.h"
34 #include "dbus-common.h"
35 #include "build.h"
36 #include "strv.h"
37 #include "cgroup-show.h"
38 #include "sysfs-show.h"
39 #include "spawn-polkit-agent.h"
40
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 {
48 TRANSPORT_NORMAL,
49 TRANSPORT_SSH,
50 TRANSPORT_POLKIT
51 } arg_transport = TRANSPORT_NORMAL;
52 static bool arg_ask_password = true;
53 static char *arg_host = NULL;
54 static char *arg_user = NULL;
55
56 static void pager_open_if_enabled(void) {
57
58 /* Cache result before we open the pager */
59 if (arg_no_pager)
60 return;
61
62 pager_open(false);
63 }
64
65 static void polkit_agent_open_if_enabled(void) {
66
67 /* Open the polkit agent as a child process if necessary */
68
69 if (!arg_ask_password)
70 return;
71
72 polkit_agent_open();
73 }
74
75 static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
76 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
77 int r;
78 DBusMessageIter iter, sub, sub2;
79 unsigned k = 0;
80
81 pager_open_if_enabled();
82
83 r = bus_method_call_with_reply (
84 bus,
85 "org.freedesktop.login1",
86 "/org/freedesktop/login1",
87 "org.freedesktop.login1.Manager",
88 "ListSessions",
89 &reply,
90 NULL,
91 DBUS_TYPE_INVALID);
92 if (r)
93 return r;
94
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.");
99 return -EIO;
100 }
101
102 dbus_message_iter_recurse(&iter, &sub);
103
104 if (on_tty())
105 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
106
107 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
108 const char *id, *user, *seat, *object;
109 uint32_t uid;
110
111 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
112 log_error("Failed to parse reply.");
113 return -EIO;
114 }
115
116 dbus_message_iter_recurse(&sub, &sub2);
117
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.");
124 return -EIO;
125 }
126
127 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
128
129 k++;
130
131 dbus_message_iter_next(&sub);
132 }
133
134 if (on_tty())
135 printf("\n%u sessions listed.\n", k);
136
137 return 0;
138 }
139
140 static int list_users(DBusConnection *bus, char **args, unsigned n) {
141 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
142 int r;
143 DBusMessageIter iter, sub, sub2;
144 unsigned k = 0;
145
146 pager_open_if_enabled();
147
148 r = bus_method_call_with_reply (
149 bus,
150 "org.freedesktop.login1",
151 "/org/freedesktop/login1",
152 "org.freedesktop.login1.Manager",
153 "ListUsers",
154 &reply,
155 NULL,
156 DBUS_TYPE_INVALID);
157 if (r)
158 return r;
159
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.");
164 return -EIO;
165 }
166
167 dbus_message_iter_recurse(&iter, &sub);
168
169 if (on_tty())
170 printf("%10s %-16s\n", "UID", "USER");
171
172 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
173 const char *user, *object;
174 uint32_t uid;
175
176 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
177 log_error("Failed to parse reply.");
178 return -EIO;
179 }
180
181 dbus_message_iter_recurse(&sub, &sub2);
182
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.");
187 return -EIO;
188 }
189
190 printf("%10u %-16s\n", (unsigned) uid, user);
191
192 k++;
193
194 dbus_message_iter_next(&sub);
195 }
196
197 if (on_tty())
198 printf("\n%u users listed.\n", k);
199
200 return 0;
201 }
202
203 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
204 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
205 int r;
206 DBusMessageIter iter, sub, sub2;
207 unsigned k = 0;
208
209 pager_open_if_enabled();
210
211 r = bus_method_call_with_reply (
212 bus,
213 "org.freedesktop.login1",
214 "/org/freedesktop/login1",
215 "org.freedesktop.login1.Manager",
216 "ListSeats",
217 &reply,
218 NULL,
219 DBUS_TYPE_INVALID);
220 if (r)
221 return r;
222
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.");
227 return -EIO;
228 }
229
230 dbus_message_iter_recurse(&iter, &sub);
231
232 if (on_tty())
233 printf("%-16s\n", "SEAT");
234
235 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
236 const char *seat, *object;
237
238 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
239 log_error("Failed to parse reply.");
240 return -EIO;
241 }
242
243 dbus_message_iter_recurse(&sub, &sub2);
244
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.");
248 return -EIO;
249 }
250
251 printf("%-16s\n", seat);
252
253 k++;
254
255 dbus_message_iter_next(&sub);
256 }
257
258 if (on_tty())
259 printf("\n%u seats listed.\n", k);
260
261 return 0;
262 }
263
264 typedef struct SessionStatusInfo {
265 const char *id;
266 uid_t uid;
267 const char *name;
268 usec_t timestamp;
269 const char *default_control_group;
270 int vtnr;
271 const char *seat;
272 const char *tty;
273 const char *display;
274 bool remote;
275 const char *remote_host;
276 const char *remote_user;
277 const char *service;
278 pid_t leader;
279 const char *type;
280 const char *class;
281 const char *state;
282 } SessionStatusInfo;
283
284 typedef struct UserStatusInfo {
285 uid_t uid;
286 const char *name;
287 usec_t timestamp;
288 const char *default_control_group;
289 const char *state;
290 char **sessions;
291 const char *display;
292 } UserStatusInfo;
293
294 typedef struct SeatStatusInfo {
295 const char *id;
296 const char *active_session;
297 char **sessions;
298 } SeatStatusInfo;
299
300 static void print_session_status_info(SessionStatusInfo *i) {
301 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
302 char since2[FORMAT_TIMESTAMP_MAX], *s2;
303 assert(i);
304
305 printf("%s - ", strna(i->id));
306
307 if (i->name)
308 printf("%s (%u)\n", i->name, (unsigned) i->uid);
309 else
310 printf("%u\n", (unsigned) i->uid);
311
312 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
313 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
314
315 if (s1)
316 printf("\t Since: %s; %s\n", s2, s1);
317 else if (s2)
318 printf("\t Since: %s\n", s2);
319
320 if (i->leader > 0) {
321 char *t = NULL;
322
323 printf("\t Leader: %u", (unsigned) i->leader);
324
325 get_process_comm(i->leader, &t);
326 if (t) {
327 printf(" (%s)", t);
328 free(t);
329 }
330
331 printf("\n");
332 }
333
334 if (i->seat) {
335 printf("\t Seat: %s", i->seat);
336
337 if (i->vtnr > 0)
338 printf("; vc%i", i->vtnr);
339
340 printf("\n");
341 }
342
343 if (i->tty)
344 printf("\t TTY: %s\n", i->tty);
345 else if (i->display)
346 printf("\t Display: %s\n", i->display);
347
348 if (i->remote_host && i->remote_user)
349 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
350 else if (i->remote_host)
351 printf("\t Remote: %s\n", i->remote_host);
352 else if (i->remote_user)
353 printf("\t Remote: user %s\n", i->remote_user);
354 else if (i->remote)
355 printf("\t Remote: Yes\n");
356
357 if (i->service) {
358 printf("\t Service: %s", i->service);
359
360 if (i->type)
361 printf("; type %s", i->type);
362
363 if (i->class)
364 printf("; class %s", i->class);
365
366 printf("\n");
367 } else if (i->type) {
368 printf("\t Type: %s\n", i->type);
369
370 if (i->class)
371 printf("; class %s", i->class);
372 } else if (i->class)
373 printf("\t Class: %s\n", i->class);
374
375 if (i->state)
376 printf("\t State: %s\n", i->state);
377
378 if (i->default_control_group) {
379 unsigned c;
380 int output_flags =
381 arg_all * OUTPUT_SHOW_ALL |
382 arg_full * OUTPUT_FULL_WIDTH;
383
384 printf("\t CGroup: %s\n", i->default_control_group);
385
386 if (arg_transport != TRANSPORT_SSH) {
387 c = columns();
388 if (c > 18)
389 c -= 18;
390 else
391 c = 0;
392
393 show_cgroup_and_extra_by_spec(i->default_control_group,
394 "\t\t ", c, false, &i->leader,
395 i->leader > 0 ? 1 : 0,
396 output_flags);
397 }
398 }
399 }
400
401 static void print_user_status_info(UserStatusInfo *i) {
402 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
403 char since2[FORMAT_TIMESTAMP_MAX], *s2;
404 assert(i);
405
406 if (i->name)
407 printf("%s (%u)\n", i->name, (unsigned) i->uid);
408 else
409 printf("%u\n", (unsigned) i->uid);
410
411 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
412 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
413
414 if (s1)
415 printf("\t Since: %s; %s\n", s2, s1);
416 else if (s2)
417 printf("\t Since: %s\n", s2);
418
419 if (!isempty(i->state))
420 printf("\t State: %s\n", i->state);
421
422 if (!strv_isempty(i->sessions)) {
423 char **l;
424 printf("\tSessions:");
425
426 STRV_FOREACH(l, i->sessions) {
427 if (streq_ptr(*l, i->display))
428 printf(" *%s", *l);
429 else
430 printf(" %s", *l);
431 }
432
433 printf("\n");
434 }
435
436 if (i->default_control_group) {
437 unsigned c;
438 int output_flags =
439 arg_all * OUTPUT_SHOW_ALL |
440 arg_full * OUTPUT_FULL_WIDTH;
441
442 printf("\t CGroup: %s\n", i->default_control_group);
443
444 if (arg_transport != TRANSPORT_SSH) {
445 c = columns();
446 if (c > 18)
447 c -= 18;
448 else
449 c = 0;
450
451 show_cgroup_by_path(i->default_control_group, "\t\t ",
452 c, false, output_flags);
453 }
454 }
455 }
456
457 static void print_seat_status_info(SeatStatusInfo *i) {
458 assert(i);
459
460 printf("%s\n", strna(i->id));
461
462 if (!strv_isempty(i->sessions)) {
463 char **l;
464 printf("\tSessions:");
465
466 STRV_FOREACH(l, i->sessions) {
467 if (streq_ptr(*l, i->active_session))
468 printf(" *%s", *l);
469 else
470 printf(" %s", *l);
471 }
472
473 printf("\n");
474 }
475
476 if (arg_transport != TRANSPORT_SSH) {
477 unsigned c;
478
479 c = columns();
480 if (c > 21)
481 c -= 21;
482 else
483 c = 0;
484
485 printf("\t Devices:\n");
486
487 show_sysfs(i->id, "\t\t ", c);
488 }
489 }
490
491 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
492 assert(name);
493 assert(iter);
494 assert(i);
495
496 switch (dbus_message_iter_get_arg_type(iter)) {
497
498 case DBUS_TYPE_STRING: {
499 const char *s;
500
501 dbus_message_iter_get_basic(iter, &s);
502
503 if (!isempty(s)) {
504 if (streq(name, "Id"))
505 i->id = s;
506 else if (streq(name, "Name"))
507 i->name = s;
508 else if (streq(name, "DefaultControlGroup"))
509 i->default_control_group = s;
510 else if (streq(name, "TTY"))
511 i->tty = s;
512 else if (streq(name, "Display"))
513 i->display = s;
514 else if (streq(name, "RemoteHost"))
515 i->remote_host = s;
516 else if (streq(name, "RemoteUser"))
517 i->remote_user = s;
518 else if (streq(name, "Service"))
519 i->service = s;
520 else if (streq(name, "Type"))
521 i->type = s;
522 else if (streq(name, "Class"))
523 i->class = s;
524 else if (streq(name, "State"))
525 i->state = s;
526 }
527 break;
528 }
529
530 case DBUS_TYPE_UINT32: {
531 uint32_t u;
532
533 dbus_message_iter_get_basic(iter, &u);
534
535 if (streq(name, "VTNr"))
536 i->vtnr = (int) u;
537 else if (streq(name, "Leader"))
538 i->leader = (pid_t) u;
539
540 break;
541 }
542
543 case DBUS_TYPE_BOOLEAN: {
544 dbus_bool_t b;
545
546 dbus_message_iter_get_basic(iter, &b);
547
548 if (streq(name, "Remote"))
549 i->remote = b;
550
551 break;
552 }
553
554 case DBUS_TYPE_UINT64: {
555 uint64_t u;
556
557 dbus_message_iter_get_basic(iter, &u);
558
559 if (streq(name, "Timestamp"))
560 i->timestamp = (usec_t) u;
561
562 break;
563 }
564
565 case DBUS_TYPE_STRUCT: {
566 DBusMessageIter sub;
567
568 dbus_message_iter_recurse(iter, &sub);
569
570 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
571 uint32_t u;
572
573 dbus_message_iter_get_basic(&sub, &u);
574 i->uid = (uid_t) u;
575
576 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
577 const char *s;
578
579 dbus_message_iter_get_basic(&sub, &s);
580
581 if (!isempty(s))
582 i->seat = s;
583 }
584
585 break;
586 }
587 }
588
589 return 0;
590 }
591
592 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
593 assert(name);
594 assert(iter);
595 assert(i);
596
597 switch (dbus_message_iter_get_arg_type(iter)) {
598
599 case DBUS_TYPE_STRING: {
600 const char *s;
601
602 dbus_message_iter_get_basic(iter, &s);
603
604 if (!isempty(s)) {
605 if (streq(name, "Name"))
606 i->name = s;
607 else if (streq(name, "DefaultControlGroup"))
608 i->default_control_group = s;
609 else if (streq(name, "State"))
610 i->state = s;
611 }
612 break;
613 }
614
615 case DBUS_TYPE_UINT32: {
616 uint32_t u;
617
618 dbus_message_iter_get_basic(iter, &u);
619
620 if (streq(name, "UID"))
621 i->uid = (uid_t) u;
622
623 break;
624 }
625
626 case DBUS_TYPE_UINT64: {
627 uint64_t u;
628
629 dbus_message_iter_get_basic(iter, &u);
630
631 if (streq(name, "Timestamp"))
632 i->timestamp = (usec_t) u;
633
634 break;
635 }
636
637 case DBUS_TYPE_STRUCT: {
638 DBusMessageIter sub;
639
640 dbus_message_iter_recurse(iter, &sub);
641
642 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
643 const char *s;
644
645 dbus_message_iter_get_basic(&sub, &s);
646
647 if (!isempty(s))
648 i->display = s;
649 }
650
651 break;
652 }
653
654 case DBUS_TYPE_ARRAY: {
655
656 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
657 DBusMessageIter sub, sub2;
658
659 dbus_message_iter_recurse(iter, &sub);
660 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
661 const char *id;
662 const char *path;
663
664 dbus_message_iter_recurse(&sub, &sub2);
665
666 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
667 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
668 char **l;
669
670 l = strv_append(i->sessions, id);
671 if (!l)
672 return -ENOMEM;
673
674 strv_free(i->sessions);
675 i->sessions = l;
676 }
677
678 dbus_message_iter_next(&sub);
679 }
680
681 return 0;
682 }
683 }
684 }
685
686 return 0;
687 }
688
689 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
690 assert(name);
691 assert(iter);
692 assert(i);
693
694 switch (dbus_message_iter_get_arg_type(iter)) {
695
696 case DBUS_TYPE_STRING: {
697 const char *s;
698
699 dbus_message_iter_get_basic(iter, &s);
700
701 if (!isempty(s)) {
702 if (streq(name, "Id"))
703 i->id = s;
704 }
705 break;
706 }
707
708 case DBUS_TYPE_STRUCT: {
709 DBusMessageIter sub;
710
711 dbus_message_iter_recurse(iter, &sub);
712
713 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
714 const char *s;
715
716 dbus_message_iter_get_basic(&sub, &s);
717
718 if (!isempty(s))
719 i->active_session = s;
720 }
721
722 break;
723 }
724
725 case DBUS_TYPE_ARRAY: {
726
727 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
728 DBusMessageIter sub, sub2;
729
730 dbus_message_iter_recurse(iter, &sub);
731 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
732 const char *id;
733 const char *path;
734
735 dbus_message_iter_recurse(&sub, &sub2);
736
737 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
738 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
739 char **l;
740
741 l = strv_append(i->sessions, id);
742 if (!l)
743 return -ENOMEM;
744
745 strv_free(i->sessions);
746 i->sessions = l;
747 }
748
749 dbus_message_iter_next(&sub);
750 }
751
752 return 0;
753 }
754 }
755 }
756
757 return 0;
758 }
759
760 static int print_property(const char *name, DBusMessageIter *iter) {
761 assert(name);
762 assert(iter);
763
764 if (arg_property && !strv_find(arg_property, name))
765 return 0;
766
767 switch (dbus_message_iter_get_arg_type(iter)) {
768
769 case DBUS_TYPE_STRUCT: {
770 DBusMessageIter sub;
771
772 dbus_message_iter_recurse(iter, &sub);
773
774 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
775 (streq(name, "Display") || streq(name, "ActiveSession"))) {
776 const char *s;
777
778 dbus_message_iter_get_basic(&sub, &s);
779
780 if (arg_all || !isempty(s))
781 printf("%s=%s\n", name, s);
782 return 0;
783 }
784 break;
785 }
786
787 case DBUS_TYPE_ARRAY:
788
789 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
790 DBusMessageIter sub, sub2;
791 bool found = false;
792
793 dbus_message_iter_recurse(iter, &sub);
794 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
795 const char *id;
796 const char *path;
797
798 dbus_message_iter_recurse(&sub, &sub2);
799
800 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
801 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
802 if (found)
803 printf(" %s", id);
804 else {
805 printf("%s=%s", name, id);
806 found = true;
807 }
808 }
809
810 dbus_message_iter_next(&sub);
811 }
812
813 if (!found && arg_all)
814 printf("%s=\n", name);
815 else if (found)
816 printf("\n");
817
818 return 0;
819 }
820
821 break;
822 }
823
824 if (generic_print_property(name, iter, arg_all) > 0)
825 return 0;
826
827 if (arg_all)
828 printf("%s=[unprintable]\n", name);
829
830 return 0;
831 }
832
833 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
834 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
835 const char *interface = "";
836 int r;
837 DBusMessageIter iter, sub, sub2, sub3;
838 SessionStatusInfo session_info = {};
839 UserStatusInfo user_info = {};
840 SeatStatusInfo seat_info = {};
841
842 assert(path);
843 assert(new_line);
844
845 r = bus_method_call_with_reply(
846 bus,
847 "org.freedesktop.login1",
848 path,
849 "org.freedesktop.DBus.Properties",
850 "GetAll",
851 &reply,
852 NULL,
853 DBUS_TYPE_STRING, &interface,
854 DBUS_TYPE_INVALID);
855 if (r < 0)
856 goto finish;
857
858 if (!dbus_message_iter_init(reply, &iter) ||
859 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
860 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
861 log_error("Failed to parse reply.");
862 r = -EIO;
863 goto finish;
864 }
865
866 dbus_message_iter_recurse(&iter, &sub);
867
868 if (*new_line)
869 printf("\n");
870
871 *new_line = true;
872
873 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
874 const char *name;
875
876 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
877 log_error("Failed to parse reply.");
878 r = -EIO;
879 goto finish;
880 }
881
882 dbus_message_iter_recurse(&sub, &sub2);
883
884 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
885 log_error("Failed to parse reply.");
886 r = -EIO;
887 goto finish;
888 }
889
890 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
891 log_error("Failed to parse reply.");
892 r = -EIO;
893 goto finish;
894 }
895
896 dbus_message_iter_recurse(&sub2, &sub3);
897
898 if (show_properties)
899 r = print_property(name, &sub3);
900 else if (strstr(verb, "session"))
901 r = status_property_session(name, &sub3, &session_info);
902 else if (strstr(verb, "user"))
903 r = status_property_user(name, &sub3, &user_info);
904 else
905 r = status_property_seat(name, &sub3, &seat_info);
906
907 if (r < 0) {
908 log_error("Failed to parse reply.");
909 goto finish;
910 }
911
912 dbus_message_iter_next(&sub);
913 }
914
915 if (!show_properties) {
916 if (strstr(verb, "session"))
917 print_session_status_info(&session_info);
918 else if (strstr(verb, "user"))
919 print_user_status_info(&user_info);
920 else
921 print_seat_status_info(&seat_info);
922 }
923
924 r = 0;
925
926 finish:
927 strv_free(seat_info.sessions);
928 strv_free(user_info.sessions);
929
930 return r;
931 }
932
933 static int show(DBusConnection *bus, char **args, unsigned n) {
934 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
935 int r, ret = 0;
936 DBusError error;
937 unsigned i;
938 bool show_properties, new_line = false;
939
940 assert(bus);
941 assert(args);
942
943 dbus_error_init(&error);
944
945 show_properties = !strstr(args[0], "status");
946
947 pager_open_if_enabled();
948
949 if (show_properties && n <= 1) {
950 /* If not argument is specified inspect the manager
951 * itself */
952
953 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
954 goto finish;
955 }
956
957 for (i = 1; i < n; i++) {
958 const char *path = NULL;
959
960 if (strstr(args[0], "session")) {
961
962 ret = bus_method_call_with_reply (
963 bus,
964 "org.freedesktop.login1",
965 "/org/freedesktop/login1",
966 "org.freedesktop.login1.Manager",
967 "GetSession",
968 &reply,
969 NULL,
970 DBUS_TYPE_STRING, &args[i],
971 DBUS_TYPE_INVALID);
972
973 } else if (strstr(args[0], "user")) {
974 uid_t uid;
975 uint32_t u;
976
977 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
978 if (ret < 0) {
979 log_error("User %s unknown.", args[i]);
980 goto finish;
981 }
982
983 u = (uint32_t) uid;
984 ret = bus_method_call_with_reply (
985 bus,
986 "org.freedesktop.login1",
987 "/org/freedesktop/login1",
988 "org.freedesktop.login1.Manager",
989 "GetUser",
990 &reply,
991 NULL,
992 DBUS_TYPE_UINT32, &u,
993 DBUS_TYPE_INVALID);
994 } else {
995
996 ret = bus_method_call_with_reply (
997 bus,
998 "org.freedesktop.login1",
999 "/org/freedesktop/login1",
1000 "org.freedesktop.login1.Manager",
1001 "GetSeat",
1002 &reply,
1003 NULL,
1004 DBUS_TYPE_STRING, &args[i],
1005 DBUS_TYPE_INVALID);
1006 }
1007 if (ret)
1008 goto finish;
1009
1010 if (!dbus_message_get_args(reply, &error,
1011 DBUS_TYPE_OBJECT_PATH, &path,
1012 DBUS_TYPE_INVALID)) {
1013 log_error("Failed to parse reply: %s", bus_error_message(&error));
1014 ret = -EIO;
1015 goto finish;
1016 }
1017
1018 r = show_one(args[0], bus, path, show_properties, &new_line);
1019 if (r != 0)
1020 ret = r;
1021 }
1022
1023 finish:
1024 dbus_error_free(&error);
1025
1026 return ret;
1027 }
1028
1029 static int activate(DBusConnection *bus, char **args, unsigned n) {
1030 int ret = 0;
1031 unsigned i;
1032
1033 assert(args);
1034
1035 for (i = 1; i < n; i++) {
1036
1037 ret = bus_method_call_with_reply (
1038 bus,
1039 "org.freedesktop.login1",
1040 "/org/freedesktop/login1",
1041 "org.freedesktop.login1.Manager",
1042 streq(args[0], "lock-session") ? "LockSession" :
1043 streq(args[0], "unlock-session") ? "UnlockSession" :
1044 streq(args[0], "terminate-session") ? "TerminateSession" :
1045 "ActivateSession",
1046 NULL,
1047 NULL,
1048 DBUS_TYPE_STRING, &args[i],
1049 DBUS_TYPE_INVALID);
1050 if (ret)
1051 goto finish;
1052 }
1053
1054 finish:
1055 return ret;
1056 }
1057
1058 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1059 unsigned i;
1060
1061 assert(args);
1062
1063 if (!arg_kill_who)
1064 arg_kill_who = "all";
1065
1066 for (i = 1; i < n; i++) {
1067 int r;
1068
1069 r = bus_method_call_with_reply (
1070 bus,
1071 "org.freedesktop.login1",
1072 "/org/freedesktop/login1",
1073 "org.freedesktop.login1.Manager",
1074 "KillSession",
1075 NULL,
1076 NULL,
1077 DBUS_TYPE_STRING, &args[i],
1078 DBUS_TYPE_STRING, &arg_kill_who,
1079 DBUS_TYPE_INT32, &arg_signal,
1080 DBUS_TYPE_INVALID);
1081 if (r)
1082 return r;
1083 }
1084
1085 return 0;
1086 }
1087
1088 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1089 unsigned i;
1090 dbus_bool_t b, interactive = true;
1091
1092 assert(args);
1093
1094 polkit_agent_open_if_enabled();
1095
1096 b = streq(args[0], "enable-linger");
1097
1098 for (i = 1; i < n; i++) {
1099 uint32_t u;
1100 uid_t uid;
1101 int r;
1102
1103 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1104 if (r < 0) {
1105 log_error("Failed to resolve user %s: %s", args[i], strerror(-r));
1106 return r;
1107 }
1108
1109 u = (uint32_t) uid;
1110 r = bus_method_call_with_reply (
1111 bus,
1112 "org.freedesktop.login1",
1113 "/org/freedesktop/login1",
1114 "org.freedesktop.login1.Manager",
1115 "SetUserLinger",
1116 NULL,
1117 NULL,
1118 DBUS_TYPE_UINT32, &u,
1119 DBUS_TYPE_BOOLEAN, &b,
1120 DBUS_TYPE_BOOLEAN, &interactive,
1121 DBUS_TYPE_INVALID);
1122 if (r)
1123 return r;
1124 }
1125
1126 return 0;
1127 }
1128
1129 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1130 unsigned i;
1131
1132 assert(args);
1133
1134 for (i = 1; i < n; i++) {
1135 uint32_t u;
1136 uid_t uid;
1137 int r;
1138
1139 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1140 if (r < 0) {
1141 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1142 return r;
1143 }
1144
1145 u = (uint32_t) uid;
1146 r = bus_method_call_with_reply (
1147 bus,
1148 "org.freedesktop.login1",
1149 "/org/freedesktop/login1",
1150 "org.freedesktop.login1.Manager",
1151 "TerminateUser",
1152 NULL,
1153 NULL,
1154 DBUS_TYPE_UINT32, &u,
1155 DBUS_TYPE_INVALID);
1156 if (r)
1157 return r;
1158 }
1159
1160 return 0;
1161 }
1162
1163 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1164 unsigned i;
1165
1166 assert(args);
1167
1168 if (!arg_kill_who)
1169 arg_kill_who = "all";
1170
1171 for (i = 1; i < n; i++) {
1172 uid_t uid;
1173 uint32_t u;
1174 int r;
1175
1176 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1177 if (r < 0) {
1178 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1179 return r;
1180 }
1181
1182 u = (uint32_t) uid;
1183 r = bus_method_call_with_reply (
1184 bus,
1185 "org.freedesktop.login1",
1186 "/org/freedesktop/login1",
1187 "org.freedesktop.login1.Manager",
1188 "KillUser",
1189 NULL,
1190 NULL,
1191 DBUS_TYPE_UINT32, &u,
1192 DBUS_TYPE_INT32, &arg_signal,
1193 DBUS_TYPE_INVALID);
1194 if (r)
1195 return r;
1196 }
1197
1198 return 0;
1199 }
1200
1201 static int attach(DBusConnection *bus, char **args, unsigned n) {
1202 unsigned i;
1203 dbus_bool_t interactive = true;
1204
1205 assert(args);
1206
1207 polkit_agent_open_if_enabled();
1208
1209 for (i = 2; i < n; i++) {
1210 int r;
1211
1212 r = bus_method_call_with_reply (
1213 bus,
1214 "org.freedesktop.login1",
1215 "/org/freedesktop/login1",
1216 "org.freedesktop.login1.Manager",
1217 "AttachDevice",
1218 NULL,
1219 NULL,
1220 DBUS_TYPE_STRING, &args[1],
1221 DBUS_TYPE_STRING, &args[i],
1222 DBUS_TYPE_BOOLEAN, &interactive,
1223 DBUS_TYPE_INVALID);
1224 if (r)
1225 return r;
1226 }
1227
1228 return 0;
1229 }
1230
1231 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1232 dbus_bool_t interactive = true;
1233
1234 assert(args);
1235
1236 polkit_agent_open_if_enabled();
1237
1238 return bus_method_call_with_reply (
1239 bus,
1240 "org.freedesktop.login1",
1241 "/org/freedesktop/login1",
1242 "org.freedesktop.login1.Manager",
1243 "FlushDevices",
1244 NULL,
1245 NULL,
1246 DBUS_TYPE_BOOLEAN, &interactive,
1247 DBUS_TYPE_INVALID);
1248 }
1249
1250 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1251 assert(args);
1252
1253 polkit_agent_open_if_enabled();
1254
1255 return bus_method_call_with_reply (
1256 bus,
1257 "org.freedesktop.login1",
1258 "/org/freedesktop/login1",
1259 "org.freedesktop.login1.Manager",
1260 streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1261 NULL,
1262 NULL,
1263 DBUS_TYPE_INVALID);
1264 }
1265
1266 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1267 unsigned i;
1268
1269 assert(args);
1270
1271 for (i = 1; i < n; i++) {
1272 int r;
1273
1274 r = bus_method_call_with_reply (
1275 bus,
1276 "org.freedesktop.login1",
1277 "/org/freedesktop/login1",
1278 "org.freedesktop.login1.Manager",
1279 "TerminateSeat",
1280 NULL,
1281 NULL,
1282 DBUS_TYPE_STRING, &args[i],
1283 DBUS_TYPE_INVALID);
1284 if (r)
1285 return r;
1286 }
1287
1288 return 0;
1289 }
1290
1291 static int help(void) {
1292
1293 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1294 "Send control commands to or query the login manager.\n\n"
1295 " -h --help Show this help\n"
1296 " --version Show package version\n"
1297 " -p --property=NAME Show only properties by this name\n"
1298 " -a --all Show all properties, including empty ones\n"
1299 " --kill-who=WHO Who to send signal to\n"
1300 " -l --full Do not ellipsize output\n"
1301 " -s --signal=SIGNAL Which signal to send\n"
1302 " --no-ask-password Don't prompt for password\n"
1303 " -H --host=[USER@]HOST Show information for remote host\n"
1304 " -P --privileged Acquire privileges before execution\n"
1305 " --no-pager Do not pipe output into a pager\n\n"
1306 "Commands:\n"
1307 " list-sessions List sessions\n"
1308 " session-status [ID...] Show session status\n"
1309 " show-session [ID...] Show properties of one or more sessions\n"
1310 " activate [ID] Activate a session\n"
1311 " lock-session [ID...] Screen lock one or more sessions\n"
1312 " unlock-session [ID...] Screen unlock one or more sessions\n"
1313 " lock-sessions Screen lock all current sessions\n"
1314 " unlock-sessions Screen unlock all current sessions\n"
1315 " terminate-session [ID...] Terminate one or more sessions\n"
1316 " kill-session [ID...] Send signal to processes of a session\n"
1317 " list-users List users\n"
1318 " user-status [USER...] Show user status\n"
1319 " show-user [USER...] Show properties of one or more users\n"
1320 " enable-linger [USER...] Enable linger state of one or more users\n"
1321 " disable-linger [USER...] Disable linger state of one or more users\n"
1322 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1323 " kill-user [USER...] Send signal to processes of a user\n"
1324 " list-seats List seats\n"
1325 " seat-status [NAME...] Show seat status\n"
1326 " show-seat [NAME...] Show properties of one or more seats\n"
1327 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1328 " flush-devices Flush all device associations\n"
1329 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
1330 program_invocation_short_name);
1331
1332 return 0;
1333 }
1334
1335 static int parse_argv(int argc, char *argv[]) {
1336
1337 enum {
1338 ARG_VERSION = 0x100,
1339 ARG_NO_PAGER,
1340 ARG_KILL_WHO,
1341 ARG_NO_ASK_PASSWORD,
1342 };
1343
1344 static const struct option options[] = {
1345 { "help", no_argument, NULL, 'h' },
1346 { "version", no_argument, NULL, ARG_VERSION },
1347 { "property", required_argument, NULL, 'p' },
1348 { "all", no_argument, NULL, 'a' },
1349 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1350 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1351 { "signal", required_argument, NULL, 's' },
1352 { "host", required_argument, NULL, 'H' },
1353 { "privileged", no_argument, NULL, 'P' },
1354 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1355 { "full", no_argument, NULL, 'l' },
1356 { NULL, 0, NULL, 0 }
1357 };
1358
1359 int c;
1360
1361 assert(argc >= 0);
1362 assert(argv);
1363
1364 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1365
1366 switch (c) {
1367
1368 case 'h':
1369 help();
1370 return 0;
1371
1372 case ARG_VERSION:
1373 puts(PACKAGE_STRING);
1374 puts(SYSTEMD_FEATURES);
1375 return 0;
1376
1377 case 'p': {
1378 char **l;
1379
1380 l = strv_append(arg_property, optarg);
1381 if (!l)
1382 return -ENOMEM;
1383
1384 strv_free(arg_property);
1385 arg_property = l;
1386
1387 /* If the user asked for a particular
1388 * property, show it to him, even if it is
1389 * empty. */
1390 arg_all = true;
1391 break;
1392 }
1393
1394 case 'a':
1395 arg_all = true;
1396 break;
1397
1398 case ARG_NO_PAGER:
1399 arg_no_pager = true;
1400 break;
1401
1402 case ARG_NO_ASK_PASSWORD:
1403 arg_ask_password = false;
1404 break;
1405
1406 case ARG_KILL_WHO:
1407 arg_kill_who = optarg;
1408 break;
1409
1410 case 's':
1411 arg_signal = signal_from_string_try_harder(optarg);
1412 if (arg_signal < 0) {
1413 log_error("Failed to parse signal string %s.", optarg);
1414 return -EINVAL;
1415 }
1416 break;
1417
1418 case 'P':
1419 arg_transport = TRANSPORT_POLKIT;
1420 break;
1421
1422 case 'H':
1423 arg_transport = TRANSPORT_SSH;
1424 parse_user_at_host(optarg, &arg_user, &arg_host);
1425 break;
1426
1427 case 'l':
1428 arg_full = true;
1429 break;
1430
1431 case '?':
1432 return -EINVAL;
1433
1434 default:
1435 log_error("Unknown option code %c", c);
1436 return -EINVAL;
1437 }
1438 }
1439
1440 return 1;
1441 }
1442
1443 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1444
1445 static const struct {
1446 const char* verb;
1447 const enum {
1448 MORE,
1449 LESS,
1450 EQUAL
1451 } argc_cmp;
1452 const int argc;
1453 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1454 } verbs[] = {
1455 { "list-sessions", LESS, 1, list_sessions },
1456 { "session-status", MORE, 2, show },
1457 { "show-session", MORE, 1, show },
1458 { "activate", EQUAL, 2, activate },
1459 { "lock-session", MORE, 2, activate },
1460 { "unlock-session", MORE, 2, activate },
1461 { "lock-sessions", EQUAL, 1, lock_sessions },
1462 { "unlock-sessions", EQUAL, 1, lock_sessions },
1463 { "terminate-session", MORE, 2, activate },
1464 { "kill-session", MORE, 2, kill_session },
1465 { "list-users", EQUAL, 1, list_users },
1466 { "user-status", MORE, 2, show },
1467 { "show-user", MORE, 1, show },
1468 { "enable-linger", MORE, 2, enable_linger },
1469 { "disable-linger", MORE, 2, enable_linger },
1470 { "terminate-user", MORE, 2, terminate_user },
1471 { "kill-user", MORE, 2, kill_user },
1472 { "list-seats", EQUAL, 1, list_seats },
1473 { "seat-status", MORE, 2, show },
1474 { "show-seat", MORE, 1, show },
1475 { "attach", MORE, 3, attach },
1476 { "flush-devices", EQUAL, 1, flush_devices },
1477 { "terminate-seat", MORE, 2, terminate_seat },
1478 };
1479
1480 int left;
1481 unsigned i;
1482
1483 assert(argc >= 0);
1484 assert(argv);
1485 assert(error);
1486
1487 left = argc - optind;
1488
1489 if (left <= 0)
1490 /* Special rule: no arguments means "list-sessions" */
1491 i = 0;
1492 else {
1493 if (streq(argv[optind], "help")) {
1494 help();
1495 return 0;
1496 }
1497
1498 for (i = 0; i < ELEMENTSOF(verbs); i++)
1499 if (streq(argv[optind], verbs[i].verb))
1500 break;
1501
1502 if (i >= ELEMENTSOF(verbs)) {
1503 log_error("Unknown operation %s", argv[optind]);
1504 return -EINVAL;
1505 }
1506 }
1507
1508 switch (verbs[i].argc_cmp) {
1509
1510 case EQUAL:
1511 if (left != verbs[i].argc) {
1512 log_error("Invalid number of arguments.");
1513 return -EINVAL;
1514 }
1515
1516 break;
1517
1518 case MORE:
1519 if (left < verbs[i].argc) {
1520 log_error("Too few arguments.");
1521 return -EINVAL;
1522 }
1523
1524 break;
1525
1526 case LESS:
1527 if (left > verbs[i].argc) {
1528 log_error("Too many arguments.");
1529 return -EINVAL;
1530 }
1531
1532 break;
1533
1534 default:
1535 assert_not_reached("Unknown comparison operator.");
1536 }
1537
1538 if (!bus) {
1539 log_error("Failed to get D-Bus connection: %s", error->message);
1540 return -EIO;
1541 }
1542
1543 return verbs[i].dispatch(bus, argv + optind, left);
1544 }
1545
1546 int main(int argc, char*argv[]) {
1547 int r, retval = EXIT_FAILURE;
1548 DBusConnection *bus = NULL;
1549 DBusError error;
1550
1551 dbus_error_init(&error);
1552
1553 setlocale(LC_ALL, "");
1554 log_parse_environment();
1555 log_open();
1556
1557 r = parse_argv(argc, argv);
1558 if (r < 0)
1559 goto finish;
1560 else if (r == 0) {
1561 retval = EXIT_SUCCESS;
1562 goto finish;
1563 }
1564
1565 if (arg_transport == TRANSPORT_NORMAL)
1566 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1567 else if (arg_transport == TRANSPORT_POLKIT)
1568 bus_connect_system_polkit(&bus, &error);
1569 else if (arg_transport == TRANSPORT_SSH)
1570 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1571 else
1572 assert_not_reached("Uh, invalid transport...");
1573
1574 r = loginctl_main(bus, argc, argv, &error);
1575 retval = r < 0 ? EXIT_FAILURE : r;
1576
1577 finish:
1578 if (bus) {
1579 dbus_connection_flush(bus);
1580 dbus_connection_close(bus);
1581 dbus_connection_unref(bus);
1582 }
1583
1584 dbus_error_free(&error);
1585 dbus_shutdown();
1586
1587 strv_free(arg_property);
1588
1589 pager_close();
1590
1591 return retval;
1592 }