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