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