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