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