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