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