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