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