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