]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/loginctl.c
coverity: fix a couple of bugs found by coverity
[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")) {
ddd88763
LP
1061 uid_t uid;
1062 uint32_t u;
a4c279f8 1063
4b67834e
LP
1064 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1065 if (r < 0) {
1066 log_error("User %s unknown.", args[i]);
1067 r = -ENOENT;
1068 goto finish;
a4c279f8
LP
1069 }
1070
1071 m = dbus_message_new_method_call(
1072 "org.freedesktop.login1",
1073 "/org/freedesktop/login1",
1074 "org.freedesktop.login1.Manager",
1075 "GetUser");
1076 if (!m) {
1077 log_error("Could not allocate message.");
1078 ret = -ENOMEM;
1079 goto finish;
1080 }
1081
ddd88763 1082 u = (uint32_t) uid;
a4c279f8 1083 if (!dbus_message_append_args(m,
ddd88763 1084 DBUS_TYPE_UINT32, &u,
a4c279f8
LP
1085 DBUS_TYPE_INVALID)) {
1086 log_error("Could not append arguments to message.");
1087 ret = -ENOMEM;
1088 goto finish;
1089 }
1090 } else {
1091
1092 m = dbus_message_new_method_call(
1093 "org.freedesktop.login1",
1094 "/org/freedesktop/login1",
1095 "org.freedesktop.login1.Manager",
1096 "GetSeat");
1097 if (!m) {
1098 log_error("Could not allocate message.");
1099 ret = -ENOMEM;
1100 goto finish;
1101 }
1102
1103 if (!dbus_message_append_args(m,
1104 DBUS_TYPE_STRING, &args[i],
1105 DBUS_TYPE_INVALID)) {
1106 log_error("Could not append arguments to message.");
1107 ret = -ENOMEM;
1108 goto finish;
1109 }
1110 }
1111
1112 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1113 if (!reply) {
1114 log_error("Failed to issue method call: %s", bus_error_message(&error));
1115 ret = -EIO;
1116 goto finish;
1117 }
1118
1119 if (!dbus_message_get_args(reply, &error,
1120 DBUS_TYPE_OBJECT_PATH, &path,
1121 DBUS_TYPE_INVALID)) {
1122 log_error("Failed to parse reply: %s", bus_error_message(&error));
1123 ret = -EIO;
1124 goto finish;
1125 }
1126
1127 r = show_one(args[0], bus, path, show_properties, &new_line);
1128 if (r != 0)
1129 ret = r;
1130
1131 dbus_message_unref(m);
1132 dbus_message_unref(reply);
1133 m = reply = NULL;
1134 }
1135
1136finish:
1137 if (m)
1138 dbus_message_unref(m);
1139
1140 if (reply)
1141 dbus_message_unref(reply);
1142
1143 dbus_error_free(&error);
1144
1145 return ret;
1146}
1147
1148static int activate(DBusConnection *bus, char **args, unsigned n) {
24310c11
LP
1149 DBusMessage *m = NULL, *reply = NULL;
1150 int ret = 0;
1151 DBusError error;
1152 unsigned i;
1153
1154 assert(bus);
1155 assert(args);
1156
1157 dbus_error_init(&error);
1158
1159 for (i = 1; i < n; i++) {
1160 m = dbus_message_new_method_call(
1161 "org.freedesktop.login1",
1162 "/org/freedesktop/login1",
1163 "org.freedesktop.login1.Manager",
88e3dc90
LP
1164 streq(args[0], "lock-session") ? "LockSession" :
1165 streq(args[0], "unlock-session") ? "UnlockSession" :
1166 streq(args[0], "terminate-session") ? "TerminateSession" :
1167 "ActivateSession");
24310c11
LP
1168 if (!m) {
1169 log_error("Could not allocate message.");
1170 ret = -ENOMEM;
1171 goto finish;
1172 }
1173
1174 if (!dbus_message_append_args(m,
1175 DBUS_TYPE_STRING, &args[i],
1176 DBUS_TYPE_INVALID)) {
1177 log_error("Could not append arguments to message.");
1178 ret = -ENOMEM;
1179 goto finish;
1180 }
1181
1182 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1183 if (!reply) {
1184 log_error("Failed to issue method call: %s", bus_error_message(&error));
1185 ret = -EIO;
1186 goto finish;
1187 }
1188
1189 dbus_message_unref(m);
1190 dbus_message_unref(reply);
1191 m = reply = NULL;
1192 }
1193
1194finish:
1195 if (m)
1196 dbus_message_unref(m);
1197
1198 if (reply)
1199 dbus_message_unref(reply);
1200
1201 dbus_error_free(&error);
1202
1203 return ret;
a4c279f8
LP
1204}
1205
1206static int kill_session(DBusConnection *bus, char **args, unsigned n) {
de07ab16
LP
1207 DBusMessage *m = NULL, *reply = 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 m = dbus_message_new_method_call(
1222 "org.freedesktop.login1",
1223 "/org/freedesktop/login1",
1224 "org.freedesktop.login1.Manager",
1225 "KillSession");
1226 if (!m) {
1227 log_error("Could not allocate message.");
1228 ret = -ENOMEM;
1229 goto finish;
1230 }
1231
1232 if (!dbus_message_append_args(m,
1233 DBUS_TYPE_STRING, &args[i],
1234 DBUS_TYPE_STRING, &arg_kill_who,
1235 DBUS_TYPE_INT32, arg_signal,
1236 DBUS_TYPE_INVALID)) {
1237 log_error("Could not append arguments to message.");
1238 ret = -ENOMEM;
1239 goto finish;
1240 }
1241
1242 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1243 if (!reply) {
1244 log_error("Failed to issue method call: %s", bus_error_message(&error));
1245 ret = -EIO;
1246 goto finish;
1247 }
1248
1249 dbus_message_unref(m);
1250 dbus_message_unref(reply);
1251 m = reply = NULL;
1252 }
1253
1254finish:
1255 if (m)
1256 dbus_message_unref(m);
1257
1258 if (reply)
1259 dbus_message_unref(reply);
1260
1261 dbus_error_free(&error);
1262
1263 return ret;
a4c279f8
LP
1264}
1265
1266static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
88e3dc90
LP
1267 DBusMessage *m = NULL, *reply = NULL;
1268 int ret = 0;
1269 DBusError error;
1270 unsigned i;
1271 dbus_bool_t b, interactive = true;
1272
1273 assert(bus);
1274 assert(args);
1275
1276 dbus_error_init(&error);
1277
1278 b = streq(args[0], "enable-linger");
1279
1280 for (i = 1; i < n; i++) {
ddd88763
LP
1281 uint32_t u;
1282 uid_t uid;
88e3dc90
LP
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
4b67834e
LP
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;
88e3dc90
LP
1299 }
1300
ddd88763 1301 u = (uint32_t) uid;
88e3dc90 1302 if (!dbus_message_append_args(m,
ddd88763 1303 DBUS_TYPE_UINT32, &u,
88e3dc90
LP
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
4b67834e
LP
1324 ret = 0;
1325
88e3dc90
LP
1326finish:
1327 if (m)
1328 dbus_message_unref(m);
1329
1330 if (reply)
1331 dbus_message_unref(reply);
1332
1333 dbus_error_free(&error);
1334
1335 return ret;
1336}
1337
1338static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1339 DBusMessage *m = NULL, *reply = NULL;
1340 int ret = 0;
1341 DBusError error;
1342 unsigned i;
1343
1344 assert(bus);
1345 assert(args);
1346
1347 dbus_error_init(&error);
1348
1349 for (i = 1; i < n; i++) {
1350 uint32_t u;
ddd88763 1351 uid_t uid;
88e3dc90
LP
1352
1353 m = dbus_message_new_method_call(
1354 "org.freedesktop.login1",
1355 "/org/freedesktop/login1",
1356 "org.freedesktop.login1.Manager",
1357 "TerminateUser");
1358 if (!m) {
1359 log_error("Could not allocate message.");
1360 ret = -ENOMEM;
1361 goto finish;
1362 }
1363
4b67834e
LP
1364 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1365 if (ret < 0) {
1366 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1367 goto finish;
88e3dc90
LP
1368 }
1369
ddd88763 1370 u = (uint32_t) uid;
88e3dc90
LP
1371 if (!dbus_message_append_args(m,
1372 DBUS_TYPE_UINT32, &u,
1373 DBUS_TYPE_INVALID)) {
1374 log_error("Could not append arguments to message.");
1375 ret = -ENOMEM;
1376 goto finish;
1377 }
1378
1379 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1380 if (!reply) {
1381 log_error("Failed to issue method call: %s", bus_error_message(&error));
1382 ret = -EIO;
1383 goto finish;
1384 }
1385
1386 dbus_message_unref(m);
1387 dbus_message_unref(reply);
1388 m = reply = NULL;
1389 }
1390
4b67834e
LP
1391 ret = 0;
1392
88e3dc90
LP
1393finish:
1394 if (m)
1395 dbus_message_unref(m);
1396
1397 if (reply)
1398 dbus_message_unref(reply);
1399
1400 dbus_error_free(&error);
1401
1402 return ret;
a4c279f8
LP
1403}
1404
de07ab16
LP
1405static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1406 DBusMessage *m = NULL, *reply = NULL;
1407 int ret = 0;
1408 DBusError error;
1409 unsigned i;
1410
1411 assert(bus);
1412 assert(args);
1413
1414 dbus_error_init(&error);
1415
1416 if (!arg_kill_who)
1417 arg_kill_who = "all";
1418
1419 for (i = 1; i < n; i++) {
ddd88763 1420 uid_t uid;
de07ab16
LP
1421 uint32_t u;
1422
1423 m = dbus_message_new_method_call(
1424 "org.freedesktop.login1",
1425 "/org/freedesktop/login1",
1426 "org.freedesktop.login1.Manager",
1427 "KillUser");
1428 if (!m) {
1429 log_error("Could not allocate message.");
1430 ret = -ENOMEM;
1431 goto finish;
1432 }
1433
4b67834e
LP
1434 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1435 if (ret < 0) {
1436 log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1437 goto finish;
de07ab16
LP
1438 }
1439
ddd88763 1440 u = (uint32_t) uid;
de07ab16
LP
1441 if (!dbus_message_append_args(m,
1442 DBUS_TYPE_UINT32, &u,
1443 DBUS_TYPE_INT32, arg_signal,
1444 DBUS_TYPE_INVALID)) {
1445 log_error("Could not append arguments to message.");
1446 ret = -ENOMEM;
1447 goto finish;
1448 }
1449
1450 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1451 if (!reply) {
1452 log_error("Failed to issue method call: %s", bus_error_message(&error));
1453 ret = -EIO;
1454 goto finish;
1455 }
1456
1457 dbus_message_unref(m);
1458 dbus_message_unref(reply);
1459 m = reply = NULL;
1460 }
1461
4b67834e
LP
1462 ret = 0;
1463
de07ab16
LP
1464finish:
1465 if (m)
1466 dbus_message_unref(m);
1467
1468 if (reply)
1469 dbus_message_unref(reply);
1470
1471 dbus_error_free(&error);
1472
1473 return ret;
1474}
1475
a4c279f8 1476static int attach(DBusConnection *bus, char **args, unsigned n) {
88e3dc90
LP
1477 DBusMessage *m = NULL, *reply = NULL;
1478 int ret = 0;
1479 DBusError error;
1480 unsigned i;
1481 dbus_bool_t interactive = true;
1482
1483 assert(bus);
1484 assert(args);
1485
1486 dbus_error_init(&error);
1487
1488 for (i = 2; i < n; i++) {
1489 m = dbus_message_new_method_call(
1490 "org.freedesktop.login1",
1491 "/org/freedesktop/login1",
1492 "org.freedesktop.login1.Manager",
1493 "AttachDevice");
1494 if (!m) {
1495 log_error("Could not allocate message.");
1496 ret = -ENOMEM;
1497 goto finish;
1498 }
1499
1500 if (!dbus_message_append_args(m,
1501 DBUS_TYPE_STRING, &args[1],
1502 DBUS_TYPE_STRING, &args[i],
1503 DBUS_TYPE_BOOLEAN, &interactive,
1504 DBUS_TYPE_INVALID)) {
1505 log_error("Could not append arguments to message.");
1506 ret = -ENOMEM;
1507 goto finish;
1508 }
1509
1510 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1511 if (!reply) {
1512 log_error("Failed to issue method call: %s", bus_error_message(&error));
1513 ret = -EIO;
1514 goto finish;
1515 }
1516
1517 dbus_message_unref(m);
1518 dbus_message_unref(reply);
1519 m = reply = NULL;
1520 }
1521
1522finish:
1523 if (m)
1524 dbus_message_unref(m);
1525
1526 if (reply)
1527 dbus_message_unref(reply);
1528
1529 dbus_error_free(&error);
1530
1531 return ret;
a4c279f8
LP
1532}
1533
1534static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
88e3dc90
LP
1535 DBusMessage *m = NULL, *reply = NULL;
1536 int ret = 0;
1537 DBusError error;
1538 dbus_bool_t interactive = true;
1539
1540 assert(bus);
1541 assert(args);
1542
1543 dbus_error_init(&error);
1544
1545 m = dbus_message_new_method_call(
1546 "org.freedesktop.login1",
1547 "/org/freedesktop/login1",
1548 "org.freedesktop.login1.Manager",
1549 "FlushDevices");
1550 if (!m) {
1551 log_error("Could not allocate message.");
1552 ret = -ENOMEM;
1553 goto finish;
1554 }
1555
1556 if (!dbus_message_append_args(m,
1557 DBUS_TYPE_BOOLEAN, &interactive,
1558 DBUS_TYPE_INVALID)) {
1559 log_error("Could not append arguments to message.");
1560 ret = -ENOMEM;
1561 goto finish;
1562 }
1563
1564 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1565 if (!reply) {
1566 log_error("Failed to issue method call: %s", bus_error_message(&error));
1567 ret = -EIO;
1568 goto finish;
1569 }
1570
1571finish:
1572 if (m)
1573 dbus_message_unref(m);
1574
1575 if (reply)
1576 dbus_message_unref(reply);
1577
1578 dbus_error_free(&error);
1579
1580 return ret;
a4c279f8
LP
1581}
1582
1583static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
88e3dc90
LP
1584 DBusMessage *m = NULL, *reply = NULL;
1585 int ret = 0;
1586 DBusError error;
1587 unsigned i;
1588
1589 assert(bus);
1590 assert(args);
1591
1592 dbus_error_init(&error);
1593
1594 for (i = 1; i < n; i++) {
1595 m = dbus_message_new_method_call(
1596 "org.freedesktop.login1",
1597 "/org/freedesktop/login1",
1598 "org.freedesktop.login1.Manager",
1599 "TerminateSeat");
1600 if (!m) {
1601 log_error("Could not allocate message.");
1602 ret = -ENOMEM;
1603 goto finish;
1604 }
1605
1606 if (!dbus_message_append_args(m,
1607 DBUS_TYPE_STRING, &args[i],
1608 DBUS_TYPE_INVALID)) {
1609 log_error("Could not append arguments to message.");
1610 ret = -ENOMEM;
1611 goto finish;
1612 }
1613
1614 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1615 if (!reply) {
1616 log_error("Failed to issue method call: %s", bus_error_message(&error));
1617 ret = -EIO;
1618 goto finish;
1619 }
1620
1621 dbus_message_unref(m);
1622 dbus_message_unref(reply);
1623 m = reply = NULL;
1624 }
1625
1626finish:
1627 if (m)
1628 dbus_message_unref(m);
1629
1630 if (reply)
1631 dbus_message_unref(reply);
1632
1633 dbus_error_free(&error);
1634
1635 return ret;
a4c279f8
LP
1636}
1637
abca4822
LP
1638static int help(void) {
1639
1640 printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1641 "Send control commands to or query the login manager.\n\n"
1642 " -h --help Show this help\n"
1643 " --version Show package version\n"
a4c279f8
LP
1644 " -p --property=NAME Show only properties by this name\n"
1645 " -a --all Show all properties, including empty ones\n"
1646 " --kill-who=WHO Who to send signal to\n"
1647 " -s --signal=SIGNAL Which signal to send\n"
1648 " -H --host=[USER@]HOST\n"
abca4822
LP
1649 " Show information for remote host\n"
1650 " -P --privileged Acquire privileges before execution\n"
a4c279f8 1651 " --no-pager Do not pipe output into a pager\n\n"
abca4822
LP
1652 "Commands:\n"
1653 " list-sessions List sessions\n"
a4c279f8 1654 " session-status [ID...] Show session status\n"
de07ab16 1655 " show-session [ID...] Show properties of one or more sessions\n"
a4c279f8
LP
1656 " activate [ID] Activate a session\n"
1657 " lock-session [ID...] Screen lock one or more sessions\n"
88e3dc90 1658 " unlock-session [ID...] Screen unlock one or more sessions\n"
a4c279f8
LP
1659 " terminate-session [ID...] Terminate one or more sessions\n"
1660 " kill-session [ID...] Send signal to processes of a session\n"
abca4822 1661 " list-users List users\n"
a4c279f8 1662 " user-status [USER...] Show user status\n"
de07ab16 1663 " show-user [USER...] Show properties of one or more users\n"
a4c279f8
LP
1664 " enable-linger [USER...] Enable linger state of one or more users\n"
1665 " disable-linger [USER...] Disable linger state of one or more users\n"
1666 " terminate-user [USER...] Terminate all sessions of one or more users\n"
1667 " kill-user [USER...] Send signal to processes of a user\n"
1668 " list-seats List seats\n"
1669 " seat-status [NAME...] Show seat status\n"
de07ab16 1670 " show-seat [NAME...] Show properties of one or more seats\n"
a4c279f8
LP
1671 " attach [NAME] [DEVICE...] Attach one or more devices to a seat\n"
1672 " flush-devices Flush all device associations\n"
de07ab16 1673 " terminate-seat [NAME...] Terminate all sessions on one or more seats\n",
abca4822
LP
1674 program_invocation_short_name);
1675
1676 return 0;
1677}
1678
1679static int parse_argv(int argc, char *argv[]) {
1680
1681 enum {
1682 ARG_VERSION = 0x100,
a4c279f8
LP
1683 ARG_NO_PAGER,
1684 ARG_KILL_WHO
abca4822
LP
1685 };
1686
1687 static const struct option options[] = {
1688 { "help", no_argument, NULL, 'h' },
1689 { "version", no_argument, NULL, ARG_VERSION },
a4c279f8
LP
1690 { "property", required_argument, NULL, 'p' },
1691 { "all", no_argument, NULL, 'a' },
abca4822 1692 { "no-pager", no_argument, NULL, ARG_NO_PAGER },
a4c279f8
LP
1693 { "kill-who", required_argument, NULL, ARG_KILL_WHO },
1694 { "signal", required_argument, NULL, 's' },
abca4822
LP
1695 { "host", required_argument, NULL, 'H' },
1696 { "privileged",no_argument, NULL, 'P' },
1697 { NULL, 0, NULL, 0 }
1698 };
1699
1700 int c;
1701
1702 assert(argc >= 0);
1703 assert(argv);
1704
a4c279f8 1705 while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
abca4822
LP
1706
1707 switch (c) {
1708
1709 case 'h':
1710 help();
1711 return 0;
1712
1713 case ARG_VERSION:
1714 puts(PACKAGE_STRING);
1715 puts(DISTRIBUTION);
1716 puts(SYSTEMD_FEATURES);
1717 return 0;
1718
a4c279f8
LP
1719 case 'p': {
1720 char **l;
1721
1722 l = strv_append(arg_property, optarg);
1723 if (!l)
1724 return -ENOMEM;
1725
1726 strv_free(arg_property);
1727 arg_property = l;
1728
1729 /* If the user asked for a particular
1730 * property, show it to him, even if it is
1731 * empty. */
1732 arg_all = true;
1733 break;
1734 }
1735
1736 case 'a':
1737 arg_all = true;
1738 break;
1739
abca4822
LP
1740 case ARG_NO_PAGER:
1741 arg_no_pager = true;
1742 break;
1743
a4c279f8
LP
1744 case ARG_KILL_WHO:
1745 arg_kill_who = optarg;
1746 break;
1747
1748 case 's':
1749 arg_signal = signal_from_string_try_harder(optarg);
1750 if (arg_signal < 0) {
1751 log_error("Failed to parse signal string %s.", optarg);
1752 return -EINVAL;
1753 }
1754 break;
1755
abca4822
LP
1756 case 'P':
1757 arg_transport = TRANSPORT_POLKIT;
1758 break;
1759
1760 case 'H':
1761 arg_transport = TRANSPORT_SSH;
1762 arg_host = optarg;
1763 break;
1764
1765 case '?':
1766 return -EINVAL;
1767
1768 default:
1769 log_error("Unknown option code %c", c);
1770 return -EINVAL;
1771 }
1772 }
1773
1774 return 1;
1775}
1776
1777static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1778
1779 static const struct {
1780 const char* verb;
1781 const enum {
1782 MORE,
1783 LESS,
1784 EQUAL
1785 } argc_cmp;
1786 const int argc;
1787 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1788 } verbs[] = {
1789 { "list-sessions", LESS, 1, list_sessions },
a4c279f8 1790 { "session-status", MORE, 2, show },
24310c11 1791 { "show-session", MORE, 1, show },
a4c279f8
LP
1792 { "activate", EQUAL, 2, activate },
1793 { "lock-session", MORE, 2, activate },
88e3dc90 1794 { "unlock-session", MORE, 2, activate },
a4c279f8 1795 { "terminate-session", MORE, 2, activate },
de07ab16 1796 { "kill-session", MORE, 2, kill_session },
abca4822 1797 { "list-users", EQUAL, 1, list_users },
a4c279f8 1798 { "user-status", MORE, 2, show },
24310c11 1799 { "show-user", MORE, 1, show },
a4c279f8
LP
1800 { "enable-linger", MORE, 2, enable_linger },
1801 { "disable-linger", MORE, 2, enable_linger },
88e3dc90 1802 { "terminate-user", MORE, 2, terminate_user },
de07ab16 1803 { "kill-user", MORE, 2, kill_user },
abca4822 1804 { "list-seats", EQUAL, 1, list_seats },
a4c279f8 1805 { "seat-status", MORE, 2, show },
24310c11 1806 { "show-seat", MORE, 1, show },
a4c279f8
LP
1807 { "attach", MORE, 3, attach },
1808 { "flush-devices", EQUAL, 1, flush_devices },
de07ab16 1809 { "terminate-seat", MORE, 2, terminate_seat },
abca4822
LP
1810 };
1811
1812 int left;
1813 unsigned i;
1814
1815 assert(argc >= 0);
1816 assert(argv);
1817 assert(error);
1818
1819 left = argc - optind;
1820
1821 if (left <= 0)
1822 /* Special rule: no arguments means "list-sessions" */
1823 i = 0;
1824 else {
1825 if (streq(argv[optind], "help")) {
1826 help();
1827 return 0;
1828 }
1829
1830 for (i = 0; i < ELEMENTSOF(verbs); i++)
1831 if (streq(argv[optind], verbs[i].verb))
1832 break;
1833
1834 if (i >= ELEMENTSOF(verbs)) {
1835 log_error("Unknown operation %s", argv[optind]);
1836 return -EINVAL;
1837 }
1838 }
1839
1840 switch (verbs[i].argc_cmp) {
1841
1842 case EQUAL:
1843 if (left != verbs[i].argc) {
1844 log_error("Invalid number of arguments.");
1845 return -EINVAL;
1846 }
1847
1848 break;
1849
1850 case MORE:
1851 if (left < verbs[i].argc) {
1852 log_error("Too few arguments.");
1853 return -EINVAL;
1854 }
1855
1856 break;
1857
1858 case LESS:
1859 if (left > verbs[i].argc) {
1860 log_error("Too many arguments.");
1861 return -EINVAL;
1862 }
1863
1864 break;
1865
1866 default:
1867 assert_not_reached("Unknown comparison operator.");
1868 }
1869
1870 if (!bus) {
1871 log_error("Failed to get D-Bus connection: %s", error->message);
1872 return -EIO;
1873 }
1874
1875 return verbs[i].dispatch(bus, argv + optind, left);
1876}
1877
1878int main(int argc, char*argv[]) {
1879 int r, retval = EXIT_FAILURE;
1880 DBusConnection *bus = NULL;
1881 DBusError error;
1882
1883 dbus_error_init(&error);
1884
1885 log_parse_environment();
1886 log_open();
1887
1888 r = parse_argv(argc, argv);
1889 if (r < 0)
1890 goto finish;
1891 else if (r == 0) {
1892 retval = EXIT_SUCCESS;
1893 goto finish;
1894 }
1895
1896 if (arg_transport == TRANSPORT_NORMAL)
1897 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1898 else if (arg_transport == TRANSPORT_POLKIT)
1899 bus_connect_system_polkit(&bus, &error);
1900 else if (arg_transport == TRANSPORT_SSH)
1901 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1902 else
1903 assert_not_reached("Uh, invalid transport...");
1904
1905 r = loginctl_main(bus, argc, argv, &error);
1906 retval = r < 0 ? EXIT_FAILURE : r;
1907
1908finish:
1909 if (bus) {
1910 dbus_connection_flush(bus);
1911 dbus_connection_close(bus);
1912 dbus_connection_unref(bus);
1913 }
1914
1915 dbus_error_free(&error);
1916 dbus_shutdown();
1917
a4c279f8
LP
1918 strv_free(arg_property);
1919
abca4822
LP
1920 pager_close();
1921
1922 return retval;
1923}