chiark / gitweb /
clients: unify how we invoke getopt_long()
[elogind.git] / src / login / loginctl.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <dbus/dbus.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <getopt.h>
27 #include <pwd.h>
28 #include <locale.h>
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"
36 #include "strv.h"
37 #include "unit-name.h"
38 #include "sysfs-show.h"
39 #include "cgroup-show.h"
40 #include "cgroup-util.h"
41 #include "spawn-polkit-agent.h"
42
43 static char **arg_property = NULL;
44 static bool arg_all = false;
45 static bool arg_full = false;
46 static bool arg_no_pager = false;
47 static const char *arg_kill_who = NULL;
48 static int arg_signal = SIGTERM;
49 static enum transport {
50         TRANSPORT_NORMAL,
51         TRANSPORT_SSH,
52         TRANSPORT_POLKIT
53 } arg_transport = TRANSPORT_NORMAL;
54 static bool arg_ask_password = true;
55 static char *arg_host = NULL;
56 static char *arg_user = NULL;
57
58 static void pager_open_if_enabled(void) {
59
60         /* Cache result before we open the pager */
61         if (arg_no_pager)
62                 return;
63
64         pager_open(false);
65 }
66
67 static 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();
75 }
76
77 static int list_sessions(DBusConnection *bus, char **args, unsigned n) {
78         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
79         int r;
80         DBusMessageIter iter, sub, sub2;
81         unsigned k = 0;
82
83         pager_open_if_enabled();
84
85         r = bus_method_call_with_reply (
86                         bus,
87                         "org.freedesktop.login1",
88                         "/org/freedesktop/login1",
89                         "org.freedesktop.login1.Manager",
90                         "ListSessions",
91                         &reply,
92                         NULL,
93                         DBUS_TYPE_INVALID);
94         if (r)
95                 return r;
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.");
101                 return -EIO;
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.");
115                         return -EIO;
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.");
126                         return -EIO;
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         return 0;
140 }
141
142 static int list_users(DBusConnection *bus, char **args, unsigned n) {
143         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
144         int r;
145         DBusMessageIter iter, sub, sub2;
146         unsigned k = 0;
147
148         pager_open_if_enabled();
149
150         r = bus_method_call_with_reply (
151                         bus,
152                         "org.freedesktop.login1",
153                         "/org/freedesktop/login1",
154                         "org.freedesktop.login1.Manager",
155                         "ListUsers",
156                         &reply,
157                         NULL,
158                         DBUS_TYPE_INVALID);
159         if (r)
160                 return r;
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.");
166                 return -EIO;
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.");
180                         return -EIO;
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.");
189                         return -EIO;
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
202         return 0;
203 }
204
205 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
206         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
207         int r;
208         DBusMessageIter iter, sub, sub2;
209         unsigned k = 0;
210
211         pager_open_if_enabled();
212
213         r = bus_method_call_with_reply (
214                         bus,
215                         "org.freedesktop.login1",
216                         "/org/freedesktop/login1",
217                         "org.freedesktop.login1.Manager",
218                         "ListSeats",
219                         &reply,
220                         NULL,
221                         DBUS_TYPE_INVALID);
222         if (r)
223                 return r;
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.");
229                 return -EIO;
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.");
242                         return -EIO;
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.");
250                         return -EIO;
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
263         return 0;
264 }
265
266 static int show_unit_cgroup(DBusConnection *bus, const char *interface, const char *unit, pid_t leader) {
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
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
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
333         show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t  ", c, false, &leader, leader > 0, output_flags);
334         return 0;
335 }
336
337 typedef struct SessionStatusInfo {
338         const char *id;
339         uid_t uid;
340         const char *name;
341         usec_t timestamp;
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;
352         const char *class;
353         const char *state;
354         const char *scope;
355 } SessionStatusInfo;
356
357 typedef struct UserStatusInfo {
358         uid_t uid;
359         const char *name;
360         usec_t timestamp;
361         const char *state;
362         char **sessions;
363         const char *display;
364         const char *slice;
365 } UserStatusInfo;
366
367 typedef struct SeatStatusInfo {
368         const char *id;
369         const char *active_session;
370         char **sessions;
371 } SeatStatusInfo;
372
373 static void print_session_status_info(DBusConnection *bus, SessionStatusInfo *i) {
374         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
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
385         s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
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) {
394                 _cleanup_free_ char *t = NULL;
395
396                 printf("\t  Leader: %u", (unsigned) i->leader);
397
398                 get_process_comm(i->leader, &t);
399                 if (t)
400                         printf(" (%s)", t);
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
434                 if (i->class)
435                         printf("; class %s", i->class);
436
437                 printf("\n");
438         } else if (i->type) {
439                 printf("\t    Type: %s\n", i->type);
440
441                 if (i->class)
442                         printf("; class %s", i->class);
443         } else if (i->class)
444                 printf("\t   Class: %s\n", i->class);
445
446         if (i->state)
447                 printf("\t   State: %s\n", i->state);
448
449         if (i->scope) {
450                 printf("\t    Unit: %s\n", i->scope);
451                 show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i->scope, i->leader);
452         }
453 }
454
455 static void print_user_status_info(DBusConnection *bus, UserStatusInfo *i) {
456         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
457         char since2[FORMAT_TIMESTAMP_MAX], *s2;
458         assert(i);
459
460         if (i->name)
461                 printf("%s (%u)\n", i->name, (unsigned) i->uid);
462         else
463                 printf("%u\n", (unsigned) i->uid);
464
465         s1 = format_timestamp_relative(since1, sizeof(since1), i->timestamp);
466         s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
467
468         if (s1)
469                 printf("\t   Since: %s; %s\n", s2, s1);
470         else if (s2)
471                 printf("\t   Since: %s\n", s2);
472
473         if (!isempty(i->state))
474                 printf("\t   State: %s\n", i->state);
475
476
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
491         if (i->slice) {
492                 printf("\t    Unit: %s\n", i->slice);
493                 show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i->slice, 0);
494         }
495 }
496
497 static 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();
520                 if (c > 21)
521                         c -= 21;
522                 else
523                         c = 0;
524
525                 printf("\t Devices:\n");
526
527                 show_sysfs(i->id, "\t\t  ", c);
528         }
529 }
530
531 static 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;
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;
560                         else if (streq(name, "Class"))
561                                 i->class = s;
562                         else if (streq(name, "Scope"))
563                                 i->scope = s;
564                         else if (streq(name, "State"))
565                                 i->state = s;
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;
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
632 static 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;
647                         else if (streq(name, "Slice"))
648                                 i->slice = s;
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
729 static 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
800 static 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
873 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
874         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
875         const char *interface = "";
876         int r;
877         DBusMessageIter iter, sub, sub2, sub3;
878         SessionStatusInfo session_info = {};
879         UserStatusInfo user_info = {};
880         SeatStatusInfo seat_info = {};
881
882         assert(path);
883         assert(new_line);
884
885         r = bus_method_call_with_reply(
886                         bus,
887                         "org.freedesktop.login1",
888                         path,
889                         "org.freedesktop.DBus.Properties",
890                         "GetAll",
891                         &reply,
892                         NULL,
893                         DBUS_TYPE_STRING, &interface,
894                         DBUS_TYPE_INVALID);
895         if (r < 0)
896                 goto finish;
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);
944                 else
945                         r = status_property_seat(name, &sub3, &seat_info);
946
947                 if (r < 0) {
948                         log_error("Failed to parse reply.");
949                         goto finish;
950                 }
951
952                 dbus_message_iter_next(&sub);
953         }
954
955         if (!show_properties) {
956                 if (strstr(verb, "session"))
957                         print_session_status_info(bus, &session_info);
958                 else if (strstr(verb, "user"))
959                         print_user_status_info(bus, &user_info);
960                 else
961                         print_seat_status_info(&seat_info);
962         }
963
964         r = 0;
965
966 finish:
967         strv_free(seat_info.sessions);
968         strv_free(user_info.sessions);
969
970         return r;
971 }
972
973 static int show(DBusConnection *bus, char **args, unsigned n) {
974         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
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
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
1002                         ret = bus_method_call_with_reply (
1003                                         bus,
1004                                         "org.freedesktop.login1",
1005                                         "/org/freedesktop/login1",
1006                                         "org.freedesktop.login1.Manager",
1007                                         "GetSession",
1008                                         &reply,
1009                                         NULL,
1010                                         DBUS_TYPE_STRING, &args[i],
1011                                         DBUS_TYPE_INVALID);
1012
1013                 } else if (strstr(args[0], "user")) {
1014                         uid_t uid;
1015                         uint32_t u;
1016
1017                         ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1018                         if (ret < 0) {
1019                                 log_error("User %s unknown.", args[i]);
1020                                 goto finish;
1021                         }
1022
1023                         u = (uint32_t) uid;
1024                         ret = bus_method_call_with_reply(
1025                                         bus,
1026                                         "org.freedesktop.login1",
1027                                         "/org/freedesktop/login1",
1028                                         "org.freedesktop.login1.Manager",
1029                                         "GetUser",
1030                                         &reply,
1031                                         NULL,
1032                                         DBUS_TYPE_UINT32, &u,
1033                                         DBUS_TYPE_INVALID);
1034
1035                 } else {
1036
1037                         ret = bus_method_call_with_reply(
1038                                         bus,
1039                                         "org.freedesktop.login1",
1040                                         "/org/freedesktop/login1",
1041                                         "org.freedesktop.login1.Manager",
1042                                         "GetSeat",
1043                                         &reply,
1044                                         NULL,
1045                                         DBUS_TYPE_STRING, &args[i],
1046                                         DBUS_TYPE_INVALID);
1047
1048                 }
1049
1050                 if (ret < 0)
1051                         goto finish;
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;
1064         }
1065
1066 finish:
1067         dbus_error_free(&error);
1068
1069         return ret;
1070 }
1071
1072 static int activate(DBusConnection *bus, char **args, unsigned n) {
1073         int ret = 0;
1074         unsigned i;
1075
1076         assert(args);
1077
1078         for (i = 1; i < n; i++) {
1079
1080                 ret = bus_method_call_with_reply (
1081                                 bus,
1082                                 "org.freedesktop.login1",
1083                                 "/org/freedesktop/login1",
1084                                 "org.freedesktop.login1.Manager",
1085                                 streq(args[0], "lock-session")      ? "LockSession" :
1086                                 streq(args[0], "unlock-session")    ? "UnlockSession" :
1087                                 streq(args[0], "terminate-session") ? "TerminateSession" :
1088                                                                       "ActivateSession",
1089                                 NULL,
1090                                 NULL,
1091                                 DBUS_TYPE_STRING, &args[i],
1092                                 DBUS_TYPE_INVALID);
1093                 if (ret)
1094                         goto finish;
1095         }
1096
1097 finish:
1098         return ret;
1099 }
1100
1101 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1102         unsigned i;
1103
1104         assert(args);
1105
1106         if (!arg_kill_who)
1107                 arg_kill_who = "all";
1108
1109         for (i = 1; i < n; i++) {
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;
1126         }
1127
1128         return 0;
1129 }
1130
1131 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1132         unsigned i;
1133         dbus_bool_t b, interactive = true;
1134
1135         assert(args);
1136
1137         polkit_agent_open_if_enabled();
1138
1139         b = streq(args[0], "enable-linger");
1140
1141         for (i = 1; i < n; i++) {
1142                 uint32_t u;
1143                 uid_t uid;
1144                 int r;
1145
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;
1150                 }
1151
1152                 u = (uint32_t) uid;
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;
1167         }
1168
1169         return 0;
1170 }
1171
1172 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1173         unsigned i;
1174
1175         assert(args);
1176
1177         for (i = 1; i < n; i++) {
1178                 uint32_t u;
1179                 uid_t uid;
1180                 int r;
1181
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;
1186                 }
1187
1188                 u = (uint32_t) uid;
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;
1201         }
1202
1203         return 0;
1204 }
1205
1206 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1207         unsigned i;
1208
1209         assert(args);
1210
1211         if (!arg_kill_who)
1212                 arg_kill_who = "all";
1213
1214         for (i = 1; i < n; i++) {
1215                 uid_t uid;
1216                 uint32_t u;
1217                 int r;
1218
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;
1223                 }
1224
1225                 u = (uint32_t) uid;
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;
1239         }
1240
1241         return 0;
1242 }
1243
1244 static int attach(DBusConnection *bus, char **args, unsigned n) {
1245         unsigned i;
1246         dbus_bool_t interactive = true;
1247
1248         assert(args);
1249
1250         polkit_agent_open_if_enabled();
1251
1252         for (i = 2; i < n; i++) {
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;
1269         }
1270
1271         return 0;
1272 }
1273
1274 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1275         dbus_bool_t interactive = true;
1276
1277         assert(args);
1278
1279         polkit_agent_open_if_enabled();
1280
1281         return bus_method_call_with_reply (
1282                         bus,
1283                         "org.freedesktop.login1",
1284                         "/org/freedesktop/login1",
1285                         "org.freedesktop.login1.Manager",
1286                         "FlushDevices",
1287                         NULL,
1288                         NULL,
1289                         DBUS_TYPE_BOOLEAN, &interactive,
1290                         DBUS_TYPE_INVALID);
1291 }
1292
1293 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1294         assert(args);
1295
1296         polkit_agent_open_if_enabled();
1297
1298         return bus_method_call_with_reply (
1299                         bus,
1300                         "org.freedesktop.login1",
1301                         "/org/freedesktop/login1",
1302                         "org.freedesktop.login1.Manager",
1303                         streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1304                         NULL,
1305                         NULL,
1306                         DBUS_TYPE_INVALID);
1307 }
1308
1309 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1310         unsigned i;
1311
1312         assert(args);
1313
1314         for (i = 1; i < n; i++) {
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;
1329         }
1330
1331         return 0;
1332 }
1333
1334 static int help(void) {
1335
1336         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1337                "Send control commands to or query the login manager.\n\n"
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"
1343                "  -l --full              Do not ellipsize output\n"
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"
1349                "Commands:\n"
1350                "  list-sessions                   List sessions\n"
1351                "  session-status [ID...]          Show session status\n"
1352                "  show-session [ID...]            Show properties of one or more sessions\n"
1353                "  activate [ID]                   Activate a session\n"
1354                "  lock-session [ID...]            Screen lock one or more sessions\n"
1355                "  unlock-session [ID...]          Screen unlock one or more sessions\n"
1356                "  lock-sessions                   Screen lock all current sessions\n"
1357                "  unlock-sessions                 Screen unlock all current sessions\n"
1358                "  terminate-session [ID...]       Terminate one or more sessions\n"
1359                "  kill-session [ID...]            Send signal to processes of a session\n"
1360                "  list-users                      List users\n"
1361                "  user-status [USER...]           Show user status\n"
1362                "  show-user [USER...]             Show properties of one or more users\n"
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"
1369                "  show-seat [NAME...]             Show properties of one or more seats\n"
1370                "  attach [NAME] [DEVICE...]       Attach one or more devices to a seat\n"
1371                "  flush-devices                   Flush all device associations\n"
1372                "  terminate-seat [NAME...]        Terminate all sessions on one or more seats\n",
1373                program_invocation_short_name);
1374
1375         return 0;
1376 }
1377
1378 static int parse_argv(int argc, char *argv[]) {
1379
1380         enum {
1381                 ARG_VERSION = 0x100,
1382                 ARG_NO_PAGER,
1383                 ARG_KILL_WHO,
1384                 ARG_NO_ASK_PASSWORD,
1385         };
1386
1387         static const struct option options[] = {
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'                 },
1392                 { "full",            no_argument,       NULL, 'l'                 },
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 },
1399                 {}
1400         };
1401
1402         int c;
1403
1404         assert(argc >= 0);
1405         assert(argv);
1406
1407         while ((c = getopt_long(argc, argv, "hp:als:H:P", options, NULL)) >= 0) {
1408
1409                 switch (c) {
1410
1411                 case 'h':
1412                         return help();
1413
1414                 case ARG_VERSION:
1415                         puts(PACKAGE_STRING);
1416                         puts(SYSTEMD_FEATURES);
1417                         return 0;
1418
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
1440                 case 'l':
1441                         arg_full = true;
1442                         break;
1443
1444                 case ARG_NO_PAGER:
1445                         arg_no_pager = true;
1446                         break;
1447
1448                 case ARG_NO_ASK_PASSWORD:
1449                         arg_ask_password = false;
1450                         break;
1451
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
1464                 case 'P':
1465                         arg_transport = TRANSPORT_POLKIT;
1466                         break;
1467
1468                 case 'H':
1469                         arg_transport = TRANSPORT_SSH;
1470                         parse_user_at_host(optarg, &arg_user, &arg_host);
1471                         break;
1472
1473                 case '?':
1474                         return -EINVAL;
1475
1476                 default:
1477                         assert_not_reached("Unhandled option");
1478                 }
1479         }
1480
1481         return 1;
1482 }
1483
1484 static 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     },
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    },
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
1587 int 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
1594         setlocale(LC_ALL, "");
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
1618 finish:
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
1628         strv_free(arg_property);
1629
1630         pager_close();
1631
1632         return retval;
1633 }