]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/loginctl.c
logind: add infrastructure to keep track of machines, and move to slices
[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 static int list_machines(DBusConnection *bus, char **args, unsigned n) {
265 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
266 DBusMessageIter iter, sub, sub2;
267 unsigned k = 0;
268 int r;
269
270 pager_open_if_enabled();
271
272 r = bus_method_call_with_reply (
273 bus,
274 "org.freedesktop.login1",
275 "/org/freedesktop/login1",
276 "org.freedesktop.login1.Manager",
277 "ListMachines",
278 &reply,
279 NULL,
280 DBUS_TYPE_INVALID);
281 if (r)
282 return r;
283
284 if (!dbus_message_iter_init(reply, &iter) ||
285 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
286 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT) {
287 log_error("Failed to parse reply.");
288 return -EIO;
289 }
290
291 dbus_message_iter_recurse(&iter, &sub);
292
293 if (on_tty())
294 printf("%-32s %-9s %-16s\n", "MACHINE", "CONTAINER", "SERVICE");
295
296 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
297 const char *name, *class, *service, *object;
298
299 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
300 log_error("Failed to parse reply.");
301 return -EIO;
302 }
303
304 dbus_message_iter_recurse(&sub, &sub2);
305
306 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0 ||
307 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &class, true) < 0 ||
308 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &service, true) < 0 ||
309 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
310 log_error("Failed to parse reply.");
311 return -EIO;
312 }
313
314 printf("%-32s %-9s %-16s\n", name, class, service);
315
316 k++;
317
318 dbus_message_iter_next(&sub);
319 }
320
321 if (on_tty())
322 printf("\n%u machines listed.\n", k);
323
324 return 0;
325 }
326
327 typedef struct SessionStatusInfo {
328 const char *id;
329 uid_t uid;
330 const char *name;
331 usec_t timestamp;
332 const char *default_control_group;
333 int vtnr;
334 const char *seat;
335 const char *tty;
336 const char *display;
337 bool remote;
338 const char *remote_host;
339 const char *remote_user;
340 const char *service;
341 pid_t leader;
342 const char *type;
343 const char *class;
344 const char *state;
345 const char *slice;
346 } SessionStatusInfo;
347
348 typedef struct UserStatusInfo {
349 uid_t uid;
350 const char *name;
351 usec_t timestamp;
352 const char *default_control_group;
353 const char *state;
354 char **sessions;
355 const char *display;
356 const char *slice;
357 } UserStatusInfo;
358
359 typedef struct SeatStatusInfo {
360 const char *id;
361 const char *active_session;
362 char **sessions;
363 } SeatStatusInfo;
364
365 typedef struct MachineStatusInfo {
366 const char *name;
367 sd_id128_t id;
368 const char *default_control_group;
369 const char *class;
370 const char *service;
371 const char *slice;
372 const char *root_directory;
373 pid_t leader;
374 usec_t timestamp;
375 } MachineStatusInfo;
376
377 static void print_session_status_info(SessionStatusInfo *i) {
378 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
379 char since2[FORMAT_TIMESTAMP_MAX], *s2;
380 assert(i);
381
382 printf("%s - ", strna(i->id));
383
384 if (i->name)
385 printf("%s (%u)\n", i->name, (unsigned) i->uid);
386 else
387 printf("%u\n", (unsigned) i->uid);
388
389 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
390 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
391
392 if (s1)
393 printf("\t Since: %s; %s\n", s2, s1);
394 else if (s2)
395 printf("\t Since: %s\n", s2);
396
397 if (i->leader > 0) {
398 _cleanup_free_ char *t = NULL;
399
400 printf("\t Leader: %u", (unsigned) i->leader);
401
402 get_process_comm(i->leader, &t);
403 if (t)
404 printf(" (%s)", t);
405
406 printf("\n");
407 }
408
409 if (i->seat) {
410 printf("\t Seat: %s", i->seat);
411
412 if (i->vtnr > 0)
413 printf("; vc%i", i->vtnr);
414
415 printf("\n");
416 }
417
418 if (i->tty)
419 printf("\t TTY: %s\n", i->tty);
420 else if (i->display)
421 printf("\t Display: %s\n", i->display);
422
423 if (i->remote_host && i->remote_user)
424 printf("\t Remote: %s@%s\n", i->remote_user, i->remote_host);
425 else if (i->remote_host)
426 printf("\t Remote: %s\n", i->remote_host);
427 else if (i->remote_user)
428 printf("\t Remote: user %s\n", i->remote_user);
429 else if (i->remote)
430 printf("\t Remote: Yes\n");
431
432 if (i->service) {
433 printf("\t Service: %s", i->service);
434
435 if (i->type)
436 printf("; type %s", i->type);
437
438 if (i->class)
439 printf("; class %s", i->class);
440
441 printf("\n");
442 } else if (i->type) {
443 printf("\t Type: %s\n", i->type);
444
445 if (i->class)
446 printf("; class %s", i->class);
447 } else if (i->class)
448 printf("\t Class: %s\n", i->class);
449
450 if (i->state)
451 printf("\t State: %s\n", i->state);
452
453 if (i->slice)
454 printf("\t Slice: %s\n", i->slice);
455
456 if (i->default_control_group) {
457 unsigned c;
458 int output_flags =
459 arg_all * OUTPUT_SHOW_ALL |
460 arg_full * OUTPUT_FULL_WIDTH;
461
462 printf("\t CGroup: %s\n", i->default_control_group);
463
464 if (arg_transport != TRANSPORT_SSH) {
465 c = columns();
466 if (c > 18)
467 c -= 18;
468 else
469 c = 0;
470
471 show_cgroup_and_extra_by_spec(i->default_control_group,
472 "\t\t ", c, false, &i->leader,
473 i->leader > 0 ? 1 : 0,
474 output_flags);
475 }
476 }
477 }
478
479 static void print_user_status_info(UserStatusInfo *i) {
480 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
481 char since2[FORMAT_TIMESTAMP_MAX], *s2;
482 assert(i);
483
484 if (i->name)
485 printf("%s (%u)\n", i->name, (unsigned) i->uid);
486 else
487 printf("%u\n", (unsigned) i->uid);
488
489 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
490 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
491
492 if (s1)
493 printf("\t Since: %s; %s\n", s2, s1);
494 else if (s2)
495 printf("\t Since: %s\n", s2);
496
497 if (!isempty(i->state))
498 printf("\t State: %s\n", i->state);
499
500 if (i->slice)
501 printf("\t Slice: %s\n", i->slice);
502
503 if (!strv_isempty(i->sessions)) {
504 char **l;
505 printf("\tSessions:");
506
507 STRV_FOREACH(l, i->sessions) {
508 if (streq_ptr(*l, i->display))
509 printf(" *%s", *l);
510 else
511 printf(" %s", *l);
512 }
513
514 printf("\n");
515 }
516
517 if (i->default_control_group) {
518 unsigned c;
519 int output_flags =
520 arg_all * OUTPUT_SHOW_ALL |
521 arg_full * OUTPUT_FULL_WIDTH;
522
523 printf("\t CGroup: %s\n", i->default_control_group);
524
525 if (arg_transport != TRANSPORT_SSH) {
526 c = columns();
527 if (c > 18)
528 c -= 18;
529 else
530 c = 0;
531
532 show_cgroup_by_path(i->default_control_group, "\t\t ",
533 c, false, output_flags);
534 }
535 }
536 }
537
538 static void print_seat_status_info(SeatStatusInfo *i) {
539 assert(i);
540
541 printf("%s\n", strna(i->id));
542
543 if (!strv_isempty(i->sessions)) {
544 char **l;
545 printf("\tSessions:");
546
547 STRV_FOREACH(l, i->sessions) {
548 if (streq_ptr(*l, i->active_session))
549 printf(" *%s", *l);
550 else
551 printf(" %s", *l);
552 }
553
554 printf("\n");
555 }
556
557 if (arg_transport != TRANSPORT_SSH) {
558 unsigned c;
559
560 c = columns();
561 if (c > 21)
562 c -= 21;
563 else
564 c = 0;
565
566 printf("\t Devices:\n");
567
568 show_sysfs(i->id, "\t\t ", c);
569 }
570 }
571
572 static void print_machine_status_info(MachineStatusInfo *i) {
573 char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
574 char since2[FORMAT_TIMESTAMP_MAX], *s2;
575 assert(i);
576
577 fputs(strna(i->name), stdout);
578
579 if (!sd_id128_equal(i->id, SD_ID128_NULL))
580 printf("(" SD_ID128_FORMAT_STR ")\n", SD_ID128_FORMAT_VAL(i->id));
581 else
582 putchar('\n');
583
584 s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
585 s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
586
587 if (s1)
588 printf("\t Since: %s; %s\n", s2, s1);
589 else if (s2)
590 printf("\t Since: %s\n", s2);
591
592 if (i->leader > 0) {
593 _cleanup_free_ char *t = NULL;
594
595 printf("\t Leader: %u", (unsigned) i->leader);
596
597 get_process_comm(i->leader, &t);
598 if (t)
599 printf(" (%s)", t);
600
601 putchar('\n');
602 }
603
604 if (i->service) {
605 printf("\t Service: %s", i->service);
606
607 if (i->class)
608 printf("; class %s", i->class);
609
610 putchar('\n');
611 } else if (i->class)
612 printf("\t Class: %s\n", i->class);
613
614 if (i->slice)
615 printf("\t Slice: %s\n", i->slice);
616 if (i->root_directory)
617 printf("\t Root: %s\n", i->root_directory);
618
619 if (i->default_control_group) {
620 unsigned c;
621 int output_flags =
622 arg_all * OUTPUT_SHOW_ALL |
623 arg_full * OUTPUT_FULL_WIDTH;
624
625 printf("\t CGroup: %s\n", i->default_control_group);
626
627 if (arg_transport != TRANSPORT_SSH) {
628 c = columns();
629 if (c > 18)
630 c -= 18;
631 else
632 c = 0;
633
634 show_cgroup_and_extra_by_spec(i->default_control_group,
635 "\t\t ", c, false, &i->leader,
636 i->leader > 0 ? 1 : 0,
637 output_flags);
638 }
639 }
640 }
641
642 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
643 assert(name);
644 assert(iter);
645 assert(i);
646
647 switch (dbus_message_iter_get_arg_type(iter)) {
648
649 case DBUS_TYPE_STRING: {
650 const char *s;
651
652 dbus_message_iter_get_basic(iter, &s);
653
654 if (!isempty(s)) {
655 if (streq(name, "Id"))
656 i->id = s;
657 else if (streq(name, "Name"))
658 i->name = s;
659 else if (streq(name, "DefaultControlGroup"))
660 i->default_control_group = s;
661 else if (streq(name, "TTY"))
662 i->tty = s;
663 else if (streq(name, "Display"))
664 i->display = s;
665 else if (streq(name, "RemoteHost"))
666 i->remote_host = s;
667 else if (streq(name, "RemoteUser"))
668 i->remote_user = s;
669 else if (streq(name, "Service"))
670 i->service = s;
671 else if (streq(name, "Type"))
672 i->type = s;
673 else if (streq(name, "Class"))
674 i->class = s;
675 else if (streq(name, "Slice"))
676 i->slice = s;
677 else if (streq(name, "State"))
678 i->state = s;
679 }
680 break;
681 }
682
683 case DBUS_TYPE_UINT32: {
684 uint32_t u;
685
686 dbus_message_iter_get_basic(iter, &u);
687
688 if (streq(name, "VTNr"))
689 i->vtnr = (int) u;
690 else if (streq(name, "Leader"))
691 i->leader = (pid_t) u;
692
693 break;
694 }
695
696 case DBUS_TYPE_BOOLEAN: {
697 dbus_bool_t b;
698
699 dbus_message_iter_get_basic(iter, &b);
700
701 if (streq(name, "Remote"))
702 i->remote = b;
703
704 break;
705 }
706
707 case DBUS_TYPE_UINT64: {
708 uint64_t u;
709
710 dbus_message_iter_get_basic(iter, &u);
711
712 if (streq(name, "Timestamp"))
713 i->timestamp = (usec_t) u;
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_UINT32 && streq(name, "User")) {
724 uint32_t u;
725
726 dbus_message_iter_get_basic(&sub, &u);
727 i->uid = (uid_t) u;
728
729 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
730 const char *s;
731
732 dbus_message_iter_get_basic(&sub, &s);
733
734 if (!isempty(s))
735 i->seat = s;
736 }
737
738 break;
739 }
740 }
741
742 return 0;
743 }
744
745 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
746 assert(name);
747 assert(iter);
748 assert(i);
749
750 switch (dbus_message_iter_get_arg_type(iter)) {
751
752 case DBUS_TYPE_STRING: {
753 const char *s;
754
755 dbus_message_iter_get_basic(iter, &s);
756
757 if (!isempty(s)) {
758 if (streq(name, "Name"))
759 i->name = s;
760 else if (streq(name, "DefaultControlGroup"))
761 i->default_control_group = s;
762 else if (streq(name, "Slice"))
763 i->slice = s;
764 else if (streq(name, "State"))
765 i->state = s;
766 }
767 break;
768 }
769
770 case DBUS_TYPE_UINT32: {
771 uint32_t u;
772
773 dbus_message_iter_get_basic(iter, &u);
774
775 if (streq(name, "UID"))
776 i->uid = (uid_t) u;
777
778 break;
779 }
780
781 case DBUS_TYPE_UINT64: {
782 uint64_t u;
783
784 dbus_message_iter_get_basic(iter, &u);
785
786 if (streq(name, "Timestamp"))
787 i->timestamp = (usec_t) u;
788
789 break;
790 }
791
792 case DBUS_TYPE_STRUCT: {
793 DBusMessageIter sub;
794
795 dbus_message_iter_recurse(iter, &sub);
796
797 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
798 const char *s;
799
800 dbus_message_iter_get_basic(&sub, &s);
801
802 if (!isempty(s))
803 i->display = s;
804 }
805
806 break;
807 }
808
809 case DBUS_TYPE_ARRAY: {
810
811 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
812 DBusMessageIter sub, sub2;
813
814 dbus_message_iter_recurse(iter, &sub);
815 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
816 const char *id;
817 const char *path;
818
819 dbus_message_iter_recurse(&sub, &sub2);
820
821 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
822 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
823 char **l;
824
825 l = strv_append(i->sessions, id);
826 if (!l)
827 return -ENOMEM;
828
829 strv_free(i->sessions);
830 i->sessions = l;
831 }
832
833 dbus_message_iter_next(&sub);
834 }
835
836 return 0;
837 }
838 }
839 }
840
841 return 0;
842 }
843
844 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
845 assert(name);
846 assert(iter);
847 assert(i);
848
849 switch (dbus_message_iter_get_arg_type(iter)) {
850
851 case DBUS_TYPE_STRING: {
852 const char *s;
853
854 dbus_message_iter_get_basic(iter, &s);
855
856 if (!isempty(s)) {
857 if (streq(name, "Id"))
858 i->id = s;
859 }
860 break;
861 }
862
863 case DBUS_TYPE_STRUCT: {
864 DBusMessageIter sub;
865
866 dbus_message_iter_recurse(iter, &sub);
867
868 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
869 const char *s;
870
871 dbus_message_iter_get_basic(&sub, &s);
872
873 if (!isempty(s))
874 i->active_session = s;
875 }
876
877 break;
878 }
879
880 case DBUS_TYPE_ARRAY: {
881
882 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
883 DBusMessageIter sub, sub2;
884
885 dbus_message_iter_recurse(iter, &sub);
886 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
887 const char *id;
888 const char *path;
889
890 dbus_message_iter_recurse(&sub, &sub2);
891
892 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
893 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
894 char **l;
895
896 l = strv_append(i->sessions, id);
897 if (!l)
898 return -ENOMEM;
899
900 strv_free(i->sessions);
901 i->sessions = l;
902 }
903
904 dbus_message_iter_next(&sub);
905 }
906
907 return 0;
908 }
909 }
910 }
911
912 return 0;
913 }
914
915 static int status_property_machine(const char *name, DBusMessageIter *iter, MachineStatusInfo *i) {
916 assert(name);
917 assert(iter);
918 assert(i);
919
920 switch (dbus_message_iter_get_arg_type(iter)) {
921
922 case DBUS_TYPE_STRING: {
923 const char *s;
924
925 dbus_message_iter_get_basic(iter, &s);
926
927 if (!isempty(s)) {
928 if (streq(name, "Name"))
929 i->name = s;
930 else if (streq(name, "DefaultControlGroup"))
931 i->default_control_group = s;
932 else if (streq(name, "Class"))
933 i->class = s;
934 else if (streq(name, "Service"))
935 i->service = s;
936 else if (streq(name, "Slice"))
937 i->slice = s;
938 else if (streq(name, "RootDirectory"))
939 i->root_directory = s;
940 }
941 break;
942 }
943
944 case DBUS_TYPE_UINT32: {
945 uint32_t u;
946
947 dbus_message_iter_get_basic(iter, &u);
948
949 if (streq(name, "Leader"))
950 i->leader = (pid_t) u;
951
952 break;
953 }
954
955 case DBUS_TYPE_UINT64: {
956 uint64_t u;
957
958 dbus_message_iter_get_basic(iter, &u);
959
960 if (streq(name, "Timestamp"))
961 i->timestamp = (usec_t) u;
962
963 break;
964 }
965
966 case DBUS_TYPE_ARRAY: {
967 DBusMessageIter sub;
968
969 dbus_message_iter_recurse(iter, &sub);
970
971 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_BYTE && streq(name, "Id")) {
972 void *v;
973 int n;
974
975 dbus_message_iter_get_fixed_array(&sub, &v, &n);
976 if (n == 0)
977 i->id = SD_ID128_NULL;
978 else if (n == 16)
979 memcpy(&i->id, v, n);
980 }
981
982 break;
983 }
984 }
985
986 return 0;
987 }
988
989 static int print_property(const char *name, DBusMessageIter *iter) {
990 assert(name);
991 assert(iter);
992
993 if (arg_property && !strv_find(arg_property, name))
994 return 0;
995
996 switch (dbus_message_iter_get_arg_type(iter)) {
997
998 case DBUS_TYPE_STRUCT: {
999 DBusMessageIter sub;
1000
1001 dbus_message_iter_recurse(iter, &sub);
1002
1003 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
1004 (streq(name, "Display") || streq(name, "ActiveSession"))) {
1005 const char *s;
1006
1007 dbus_message_iter_get_basic(&sub, &s);
1008
1009 if (arg_all || !isempty(s))
1010 printf("%s=%s\n", name, s);
1011 return 0;
1012 }
1013 break;
1014 }
1015
1016 case DBUS_TYPE_ARRAY:
1017
1018 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
1019 DBusMessageIter sub, sub2;
1020 bool found = false;
1021
1022 dbus_message_iter_recurse(iter, &sub);
1023 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
1024 const char *id;
1025 const char *path;
1026
1027 dbus_message_iter_recurse(&sub, &sub2);
1028
1029 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
1030 bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
1031 if (found)
1032 printf(" %s", id);
1033 else {
1034 printf("%s=%s", name, id);
1035 found = true;
1036 }
1037 }
1038
1039 dbus_message_iter_next(&sub);
1040 }
1041
1042 if (!found && arg_all)
1043 printf("%s=\n", name);
1044 else if (found)
1045 printf("\n");
1046
1047 return 0;
1048 }
1049
1050 break;
1051 }
1052
1053 if (generic_print_property(name, iter, arg_all) > 0)
1054 return 0;
1055
1056 if (arg_all)
1057 printf("%s=[unprintable]\n", name);
1058
1059 return 0;
1060 }
1061
1062 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
1063 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
1064 const char *interface = "";
1065 int r;
1066 DBusMessageIter iter, sub, sub2, sub3;
1067 SessionStatusInfo session_info = {};
1068 UserStatusInfo user_info = {};
1069 SeatStatusInfo seat_info = {};
1070 MachineStatusInfo machine_info = {};
1071
1072 assert(path);
1073 assert(new_line);
1074
1075 r = bus_method_call_with_reply(
1076 bus,
1077 "org.freedesktop.login1",
1078 path,
1079 "org.freedesktop.DBus.Properties",
1080 "GetAll",
1081 &reply,
1082 NULL,
1083 DBUS_TYPE_STRING, &interface,
1084 DBUS_TYPE_INVALID);
1085 if (r < 0)
1086 goto finish;
1087
1088 if (!dbus_message_iter_init(reply, &iter) ||
1089 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
1090 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY) {
1091 log_error("Failed to parse reply.");
1092 r = -EIO;
1093 goto finish;
1094 }
1095
1096 dbus_message_iter_recurse(&iter, &sub);
1097
1098 if (*new_line)
1099 printf("\n");
1100
1101 *new_line = true;
1102
1103 while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
1104 const char *name;
1105
1106 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
1107 log_error("Failed to parse reply.");
1108 r = -EIO;
1109 goto finish;
1110 }
1111
1112 dbus_message_iter_recurse(&sub, &sub2);
1113
1114 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
1115 log_error("Failed to parse reply.");
1116 r = -EIO;
1117 goto finish;
1118 }
1119
1120 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT) {
1121 log_error("Failed to parse reply.");
1122 r = -EIO;
1123 goto finish;
1124 }
1125
1126 dbus_message_iter_recurse(&sub2, &sub3);
1127
1128 if (show_properties)
1129 r = print_property(name, &sub3);
1130 else if (strstr(verb, "session"))
1131 r = status_property_session(name, &sub3, &session_info);
1132 else if (strstr(verb, "user"))
1133 r = status_property_user(name, &sub3, &user_info);
1134 else if (strstr(verb, "seat"))
1135 r = status_property_seat(name, &sub3, &seat_info);
1136 else
1137 r = status_property_machine(name, &sub3, &machine_info);
1138
1139 if (r < 0) {
1140 log_error("Failed to parse reply.");
1141 goto finish;
1142 }
1143
1144 dbus_message_iter_next(&sub);
1145 }
1146
1147 if (!show_properties) {
1148 if (strstr(verb, "session"))
1149 print_session_status_info(&session_info);
1150 else if (strstr(verb, "user"))
1151 print_user_status_info(&user_info);
1152 else if (strstr(verb, "seat"))
1153 print_seat_status_info(&seat_info);
1154 else
1155 print_machine_status_info(&machine_info);
1156 }
1157
1158 r = 0;
1159
1160 finish:
1161 strv_free(seat_info.sessions);
1162 strv_free(user_info.sessions);
1163
1164 return r;
1165 }
1166
1167 static int show(DBusConnection *bus, char **args, unsigned n) {
1168 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
1169 int r, ret = 0;
1170 DBusError error;
1171 unsigned i;
1172 bool show_properties, new_line = false;
1173
1174 assert(bus);
1175 assert(args);
1176
1177 dbus_error_init(&error);
1178
1179 show_properties = !strstr(args[0], "status");
1180
1181 pager_open_if_enabled();
1182
1183 if (show_properties && n <= 1) {
1184 /* If not argument is specified inspect the manager
1185 * itself */
1186
1187 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
1188 goto finish;
1189 }
1190
1191 for (i = 1; i < n; i++) {
1192 const char *path = NULL;
1193
1194 if (strstr(args[0], "session")) {
1195
1196 ret = bus_method_call_with_reply (
1197 bus,
1198 "org.freedesktop.login1",
1199 "/org/freedesktop/login1",
1200 "org.freedesktop.login1.Manager",
1201 "GetSession",
1202 &reply,
1203 NULL,
1204 DBUS_TYPE_STRING, &args[i],
1205 DBUS_TYPE_INVALID);
1206
1207 } else if (strstr(args[0], "user")) {
1208 uid_t uid;
1209 uint32_t u;
1210
1211 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1212 if (ret < 0) {
1213 log_error("User %s unknown.", args[i]);
1214 goto finish;
1215 }
1216
1217 u = (uint32_t) uid;
1218 ret = bus_method_call_with_reply(
1219 bus,
1220 "org.freedesktop.login1",
1221 "/org/freedesktop/login1",
1222 "org.freedesktop.login1.Manager",
1223 "GetUser",
1224 &reply,
1225 NULL,
1226 DBUS_TYPE_UINT32, &u,
1227 DBUS_TYPE_INVALID);
1228
1229 } else if (strstr(args[0], "seat")) {
1230
1231 ret = bus_method_call_with_reply(
1232 bus,
1233 "org.freedesktop.login1",
1234 "/org/freedesktop/login1",
1235 "org.freedesktop.login1.Manager",
1236 "GetSeat",
1237 &reply,
1238 NULL,
1239 DBUS_TYPE_STRING, &args[i],
1240 DBUS_TYPE_INVALID);
1241
1242 } else {
1243
1244 ret = bus_method_call_with_reply(
1245 bus,
1246 "org.freedesktop.login1",
1247 "/org/freedesktop/login1",
1248 "org.freedesktop.login1.Manager",
1249 "GetMachine",
1250 &reply,
1251 NULL,
1252 DBUS_TYPE_STRING, &args[i],
1253 DBUS_TYPE_INVALID);
1254 }
1255
1256 if (ret < 0)
1257 goto finish;
1258
1259 if (!dbus_message_get_args(reply, &error,
1260 DBUS_TYPE_OBJECT_PATH, &path,
1261 DBUS_TYPE_INVALID)) {
1262 log_error("Failed to parse reply: %s", bus_error_message(&error));
1263 ret = -EIO;
1264 goto finish;
1265 }
1266
1267 r = show_one(args[0], bus, path, show_properties, &new_line);
1268 if (r != 0)
1269 ret = r;
1270 }
1271
1272 finish:
1273 dbus_error_free(&error);
1274
1275 return ret;
1276 }
1277
1278 static int activate(DBusConnection *bus, char **args, unsigned n) {
1279 int ret = 0;
1280 unsigned i;
1281
1282 assert(args);
1283
1284 for (i = 1; i < n; i++) {
1285
1286 ret = bus_method_call_with_reply (
1287 bus,
1288 "org.freedesktop.login1",
1289 "/org/freedesktop/login1",
1290 "org.freedesktop.login1.Manager",
1291 streq(args[0], "lock-session") ? "LockSession" :
1292 streq(args[0], "unlock-session") ? "UnlockSession" :
1293 streq(args[0], "terminate-session") ? "TerminateSession" :
1294 "ActivateSession",
1295 NULL,
1296 NULL,
1297 DBUS_TYPE_STRING, &args[i],
1298 DBUS_TYPE_INVALID);
1299 if (ret)
1300 goto finish;
1301 }
1302
1303 finish:
1304 return ret;
1305 }
1306
1307 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1308 unsigned i;
1309
1310 assert(args);
1311
1312 if (!arg_kill_who)
1313 arg_kill_who = "all";
1314
1315 for (i = 1; i < n; i++) {
1316 int r;
1317
1318 r = bus_method_call_with_reply (
1319 bus,
1320 "org.freedesktop.login1",
1321 "/org/freedesktop/login1",
1322 "org.freedesktop.login1.Manager",
1323 "KillSession",
1324 NULL,
1325 NULL,
1326 DBUS_TYPE_STRING, &args[i],
1327 DBUS_TYPE_STRING, &arg_kill_who,
1328 DBUS_TYPE_INT32, &arg_signal,
1329 DBUS_TYPE_INVALID);
1330 if (r)
1331 return r;
1332 }
1333
1334 return 0;
1335 }
1336
1337 static int kill_machine(DBusConnection *bus, char **args, unsigned n) {
1338 unsigned i;
1339
1340 assert(args);
1341
1342 if (!arg_kill_who)
1343 arg_kill_who = "all";
1344
1345 for (i = 1; i < n; i++) {
1346 int r;
1347
1348 r = bus_method_call_with_reply (
1349 bus,
1350 "org.freedesktop.login1",
1351 "/org/freedesktop/login1",
1352 "org.freedesktop.login1.Manager",
1353 "KillMachine",
1354 NULL,
1355 NULL,
1356 DBUS_TYPE_STRING, &args[i],
1357 DBUS_TYPE_STRING, &arg_kill_who,
1358 DBUS_TYPE_INT32, &arg_signal,
1359 DBUS_TYPE_INVALID);
1360 if (r)
1361 return r;
1362 }
1363
1364 return 0;
1365 }
1366
1367 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1368 unsigned i;
1369 dbus_bool_t b, interactive = true;
1370
1371 assert(args);
1372
1373 polkit_agent_open_if_enabled();
1374
1375 b = streq(args[0], "enable-linger");
1376
1377 for (i = 1; i < n; i++) {
1378 uint32_t u;
1379 uid_t uid;
1380 int r;
1381
1382 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1383 if (r < 0) {
1384 log_error("Failed to resolve user %s: %s", args[i], strerror(-r));
1385 return r;
1386 }
1387
1388 u = (uint32_t) uid;
1389 r = bus_method_call_with_reply (
1390 bus,
1391 "org.freedesktop.login1",
1392 "/org/freedesktop/login1",
1393 "org.freedesktop.login1.Manager",
1394 "SetUserLinger",
1395 NULL,
1396 NULL,
1397 DBUS_TYPE_UINT32, &u,
1398 DBUS_TYPE_BOOLEAN, &b,
1399 DBUS_TYPE_BOOLEAN, &interactive,
1400 DBUS_TYPE_INVALID);
1401 if (r)
1402 return r;
1403 }
1404
1405 return 0;
1406 }
1407
1408 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1409 unsigned i;
1410
1411 assert(args);
1412
1413 for (i = 1; i < n; i++) {
1414 uint32_t u;
1415 uid_t uid;
1416 int r;
1417
1418 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1419 if (r < 0) {
1420 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1421 return r;
1422 }
1423
1424 u = (uint32_t) uid;
1425 r = bus_method_call_with_reply (
1426 bus,
1427 "org.freedesktop.login1",
1428 "/org/freedesktop/login1",
1429 "org.freedesktop.login1.Manager",
1430 "TerminateUser",
1431 NULL,
1432 NULL,
1433 DBUS_TYPE_UINT32, &u,
1434 DBUS_TYPE_INVALID);
1435 if (r)
1436 return r;
1437 }
1438
1439 return 0;
1440 }
1441
1442 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1443 unsigned i;
1444
1445 assert(args);
1446
1447 if (!arg_kill_who)
1448 arg_kill_who = "all";
1449
1450 for (i = 1; i < n; i++) {
1451 uid_t uid;
1452 uint32_t u;
1453 int r;
1454
1455 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1456 if (r < 0) {
1457 log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1458 return r;
1459 }
1460
1461 u = (uint32_t) uid;
1462 r = bus_method_call_with_reply (
1463 bus,
1464 "org.freedesktop.login1",
1465 "/org/freedesktop/login1",
1466 "org.freedesktop.login1.Manager",
1467 "KillUser",
1468 NULL,
1469 NULL,
1470 DBUS_TYPE_UINT32, &u,
1471 DBUS_TYPE_INT32, &arg_signal,
1472 DBUS_TYPE_INVALID);
1473 if (r)
1474 return r;
1475 }
1476
1477 return 0;
1478 }
1479
1480 static int attach(DBusConnection *bus, char **args, unsigned n) {
1481 unsigned i;
1482 dbus_bool_t interactive = true;
1483
1484 assert(args);
1485
1486 polkit_agent_open_if_enabled();
1487
1488 for (i = 2; i < n; i++) {
1489 int r;
1490
1491 r = bus_method_call_with_reply (
1492 bus,
1493 "org.freedesktop.login1",
1494 "/org/freedesktop/login1",
1495 "org.freedesktop.login1.Manager",
1496 "AttachDevice",
1497 NULL,
1498 NULL,
1499 DBUS_TYPE_STRING, &args[1],
1500 DBUS_TYPE_STRING, &args[i],
1501 DBUS_TYPE_BOOLEAN, &interactive,
1502 DBUS_TYPE_INVALID);
1503 if (r)
1504 return r;
1505 }
1506
1507 return 0;
1508 }
1509
1510 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1511 dbus_bool_t interactive = true;
1512
1513 assert(args);
1514
1515 polkit_agent_open_if_enabled();
1516
1517 return bus_method_call_with_reply (
1518 bus,
1519 "org.freedesktop.login1",
1520 "/org/freedesktop/login1",
1521 "org.freedesktop.login1.Manager",
1522 "FlushDevices",
1523 NULL,
1524 NULL,
1525 DBUS_TYPE_BOOLEAN, &interactive,
1526 DBUS_TYPE_INVALID);
1527 }
1528
1529 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1530 assert(args);
1531
1532 polkit_agent_open_if_enabled();
1533
1534 return bus_method_call_with_reply (
1535 bus,
1536 "org.freedesktop.login1",
1537 "/org/freedesktop/login1",
1538 "org.freedesktop.login1.Manager",
1539 streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1540 NULL,
1541 NULL,
1542 DBUS_TYPE_INVALID);
1543 }
1544
1545 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1546 unsigned i;
1547
1548 assert(args);
1549
1550 for (i = 1; i < n; i++) {
1551 int r;
1552
1553 r = bus_method_call_with_reply (
1554 bus,
1555 "org.freedesktop.login1",
1556 "/org/freedesktop/login1",
1557 "org.freedesktop.login1.Manager",
1558 "TerminateSeat",
1559 NULL,
1560 NULL,
1561 DBUS_TYPE_STRING, &args[i],
1562 DBUS_TYPE_INVALID);
1563 if (r)
1564 return r;
1565 }
1566
1567 return 0;
1568 }
1569
1570 static int terminate_machine(DBusConnection *bus, char **args, unsigned n) {
1571 unsigned i;
1572
1573 assert(args);
1574
1575 for (i = 1; i < n; i++) {
1576 int r;
1577
1578 r = bus_method_call_with_reply (
1579 bus,
1580 "org.freedesktop.login1",
1581 "/org/freedesktop/login1",
1582 "org.freedesktop.login1.Manager",
1583 "TerminateMachine",
1584 NULL,
1585 NULL,
1586 DBUS_TYPE_STRING, &args[i],
1587 DBUS_TYPE_INVALID);
1588 if (r)
1589 return r;
1590 }
1591
1592 return 0;
1593 }
1594
1595 static int help(void) {
1596
1597 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1598 "Send control commands to or query the login manager.\n\n"
1599 " -h --help Show this help\n"
1600 " --version Show package version\n"
1601 " -p --property=NAME Show only properties by this name\n"
1602 " -a --all Show all properties, including empty ones\n"
1603 " --kill-who=WHO Who to send signal to\n"
1604 " -l --full Do not ellipsize output\n"
1605 " -s --signal=SIGNAL Which signal to send\n"
1606 " --no-ask-password Don't prompt for password\n"
1607 " -H --host=[USER@]HOST Show information for remote host\n"
1608 " -P --privileged Acquire privileges before execution\n"
1609 " --no-pager Do not pipe output into a pager\n\n"
1610 "Commands:\n"
1611 " list-sessions List sessions\n"
1612 " session-status [ID...] Show session status\n"
1613 " show-session [ID...] Show properties of one or more sessions\n"
1614 " activate [ID] Activate a session\n"
1615 " lock-session [ID...] Screen lock one or more sessions\n"
1616 " unlock-session [ID...] Screen unlock one or more sessions\n"
1617 " lock-sessions Screen lock all current sessions\n"
1618 " unlock-sessions Screen unlock all current sessions\n"
1619 " terminate-session [ID...] Terminate one or more sessions\n"
1620 " kill-session [ID...] Send signal to processes of a session\n"
1621 " list-users List users\n"
1622 " user-status [USER...] Show user status\n"
1623 " show-user [USER...] Show properties of one or more users\n"
1624 " enable-linger [USER...] Enable linger state of one or more users\n"
1625 " disable-linger [USER...] Disable linger state of one or more users\n"
1626 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1627 " kill-user [USER...] Send signal to processes of a user\n"
1628 " list-seats List seats\n"
1629 " seat-status [NAME...] Show seat status\n"
1630 " show-seat [NAME...] Show properties of one or more seats\n"
1631 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1632 " flush-devices Flush all device associations\n"
1633 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n"
1634 " list-machines List running VMs and containers\n"
1635 " machine-status [NAME...] Show VM/container status\n"
1636 " show-machine [NAME...] Show properties of one or more VMs/containers\n"
1637 " terminate-machine [NAME...] Terminate one or more VMs/containers\n"
1638 " kill-machine [NAME...] Send signal to processes of a VM/container\n",
1639 program_invocation_short_name);
1640
1641 return 0;
1642 }
1643
1644 static int parse_argv(int argc, char *argv[]) {
1645
1646 enum {
1647 ARG_VERSION = 0x100,
1648 ARG_NO_PAGER,
1649 ARG_KILL_WHO,
1650 ARG_NO_ASK_PASSWORD,
1651 };
1652
1653 static const struct option options[] = {
1654 { "help", no_argument, NULL, 'h' },
1655 { "version", no_argument, NULL, ARG_VERSION },
1656 { "property", required_argument, NULL, 'p' },
1657 { "all", no_argument, NULL, 'a' },
1658 { "full", no_argument, NULL, 'l' },
1659 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
1660 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1661 { "signal", required_argument, NULL, 's' },
1662 { "host", required_argument, NULL, 'H' },
1663 { "privileged", no_argument, NULL, 'P' },
1664 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1665 { NULL, 0, NULL, 0 }
1666 };
1667
1668 int c;
1669
1670 assert(argc >= 0);
1671 assert(argv);
1672
1673 while ((c = getopt_long(argc, argv, "hp:als:H:P", options, NULL)) >= 0) {
1674
1675 switch (c) {
1676
1677 case 'h':
1678 help();
1679 return 0;
1680
1681 case ARG_VERSION:
1682 puts(PACKAGE_STRING);
1683 puts(SYSTEMD_FEATURES);
1684 return 0;
1685
1686 case 'p': {
1687 char **l;
1688
1689 l = strv_append(arg_property, optarg);
1690 if (!l)
1691 return -ENOMEM;
1692
1693 strv_free(arg_property);
1694 arg_property = l;
1695
1696 /* If the user asked for a particular
1697 * property, show it to him, even if it is
1698 * empty. */
1699 arg_all = true;
1700 break;
1701 }
1702
1703 case 'a':
1704 arg_all = true;
1705 break;
1706
1707 case 'l':
1708 arg_full = true;
1709 break;
1710
1711 case ARG_NO_PAGER:
1712 arg_no_pager = true;
1713 break;
1714
1715 case ARG_NO_ASK_PASSWORD:
1716 arg_ask_password = false;
1717 break;
1718
1719 case ARG_KILL_WHO:
1720 arg_kill_who = optarg;
1721 break;
1722
1723 case 's':
1724 arg_signal = signal_from_string_try_harder(optarg);
1725 if (arg_signal < 0) {
1726 log_error("Failed to parse signal string %s.", optarg);
1727 return -EINVAL;
1728 }
1729 break;
1730
1731 case 'P':
1732 arg_transport = TRANSPORT_POLKIT;
1733 break;
1734
1735 case 'H':
1736 arg_transport = TRANSPORT_SSH;
1737 parse_user_at_host(optarg, &arg_user, &arg_host);
1738 break;
1739
1740 case '?':
1741 return -EINVAL;
1742
1743 default:
1744 log_error("Unknown option code %c", c);
1745 return -EINVAL;
1746 }
1747 }
1748
1749 return 1;
1750 }
1751
1752 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1753
1754 static const struct {
1755 const char* verb;
1756 const enum {
1757 MORE,
1758 LESS,
1759 EQUAL
1760 } argc_cmp;
1761 const int argc;
1762 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1763 } verbs[] = {
1764 { "list-sessions", LESS, 1, list_sessions },
1765 { "session-status", MORE, 2, show },
1766 { "show-session", MORE, 1, show },
1767 { "activate", EQUAL, 2, activate },
1768 { "lock-session", MORE, 2, activate },
1769 { "unlock-session", MORE, 2, activate },
1770 { "lock-sessions", EQUAL, 1, lock_sessions },
1771 { "unlock-sessions", EQUAL, 1, lock_sessions },
1772 { "terminate-session", MORE, 2, activate },
1773 { "kill-session", MORE, 2, kill_session },
1774 { "list-users", EQUAL, 1, list_users },
1775 { "user-status", MORE, 2, show },
1776 { "show-user", MORE, 1, show },
1777 { "enable-linger", MORE, 2, enable_linger },
1778 { "disable-linger", MORE, 2, enable_linger },
1779 { "terminate-user", MORE, 2, terminate_user },
1780 { "kill-user", MORE, 2, kill_user },
1781 { "list-seats", EQUAL, 1, list_seats },
1782 { "seat-status", MORE, 2, show },
1783 { "show-seat", MORE, 1, show },
1784 { "attach", MORE, 3, attach },
1785 { "flush-devices", EQUAL, 1, flush_devices },
1786 { "terminate-seat", MORE, 2, terminate_seat },
1787 { "list-machines", EQUAL, 1, list_machines },
1788 { "machine-status", MORE, 2, show },
1789 { "show-machine", MORE, 1, show },
1790 { "terminate-machine", MORE, 2, terminate_machine },
1791 { "kill-machine", MORE, 2, kill_machine },
1792 };
1793
1794 int left;
1795 unsigned i;
1796
1797 assert(argc >= 0);
1798 assert(argv);
1799 assert(error);
1800
1801 left = argc - optind;
1802
1803 if (left <= 0)
1804 /* Special rule: no arguments means "list-sessions" */
1805 i = 0;
1806 else {
1807 if (streq(argv[optind], "help")) {
1808 help();
1809 return 0;
1810 }
1811
1812 for (i = 0; i < ELEMENTSOF(verbs); i++)
1813 if (streq(argv[optind], verbs[i].verb))
1814 break;
1815
1816 if (i >= ELEMENTSOF(verbs)) {
1817 log_error("Unknown operation %s", argv[optind]);
1818 return -EINVAL;
1819 }
1820 }
1821
1822 switch (verbs[i].argc_cmp) {
1823
1824 case EQUAL:
1825 if (left != verbs[i].argc) {
1826 log_error("Invalid number of arguments.");
1827 return -EINVAL;
1828 }
1829
1830 break;
1831
1832 case MORE:
1833 if (left < verbs[i].argc) {
1834 log_error("Too few arguments.");
1835 return -EINVAL;
1836 }
1837
1838 break;
1839
1840 case LESS:
1841 if (left > verbs[i].argc) {
1842 log_error("Too many arguments.");
1843 return -EINVAL;
1844 }
1845
1846 break;
1847
1848 default:
1849 assert_not_reached("Unknown comparison operator.");
1850 }
1851
1852 if (!bus) {
1853 log_error("Failed to get D-Bus connection: %s", error->message);
1854 return -EIO;
1855 }
1856
1857 return verbs[i].dispatch(bus, argv + optind, left);
1858 }
1859
1860 int main(int argc, char*argv[]) {
1861 int r, retval = EXIT_FAILURE;
1862 DBusConnection *bus = NULL;
1863 DBusError error;
1864
1865 dbus_error_init(&error);
1866
1867 setlocale(LC_ALL, "");
1868 log_parse_environment();
1869 log_open();
1870
1871 r = parse_argv(argc, argv);
1872 if (r < 0)
1873 goto finish;
1874 else if (r == 0) {
1875 retval = EXIT_SUCCESS;
1876 goto finish;
1877 }
1878
1879 if (arg_transport == TRANSPORT_NORMAL)
1880 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1881 else if (arg_transport == TRANSPORT_POLKIT)
1882 bus_connect_system_polkit(&bus, &error);
1883 else if (arg_transport == TRANSPORT_SSH)
1884 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1885 else
1886 assert_not_reached("Uh, invalid transport...");
1887
1888 r = loginctl_main(bus, argc, argv, &error);
1889 retval = r < 0 ? EXIT_FAILURE : r;
1890
1891 finish:
1892 if (bus) {
1893 dbus_connection_flush(bus);
1894 dbus_connection_close(bus);
1895 dbus_connection_unref(bus);
1896 }
1897
1898 dbus_error_free(&error);
1899 dbus_shutdown();
1900
1901 strv_free(arg_property);
1902
1903 pager_close();
1904
1905 return retval;
1906 }