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