chiark / gitweb /
Do no isolate in case of emergency or severe problems
[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         zero(session_info);
845         zero(user_info);
846         zero(seat_info);
847
848         r = bus_method_call_with_reply(
849                         bus,
850                         "org.freedesktop.login1",
851                         path,
852                         "org.freedesktop.DBus.Properties",
853                         "GetAll",
854                         &reply,
855                         NULL,
856                         DBUS_TYPE_STRING, &interface,
857                         DBUS_TYPE_INVALID);
858         if (r < 0)
859                 goto finish;
860
861         if (!dbus_message_iter_init(reply, &iter) ||
862             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
863             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY)  {
864                 log_error("Failed to parse reply.");
865                 r = -EIO;
866                 goto finish;
867         }
868
869         dbus_message_iter_recurse(&iter, &sub);
870
871         if (*new_line)
872                 printf("\n");
873
874         *new_line = true;
875
876         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
877                 const char *name;
878
879                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
880                         log_error("Failed to parse reply.");
881                         r = -EIO;
882                         goto finish;
883                 }
884
885                 dbus_message_iter_recurse(&sub, &sub2);
886
887                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
888                         log_error("Failed to parse reply.");
889                         r = -EIO;
890                         goto finish;
891                 }
892
893                 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)  {
894                         log_error("Failed to parse reply.");
895                         r = -EIO;
896                         goto finish;
897                 }
898
899                 dbus_message_iter_recurse(&sub2, &sub3);
900
901                 if (show_properties)
902                         r = print_property(name, &sub3);
903                 else if (strstr(verb, "session"))
904                         r = status_property_session(name, &sub3, &session_info);
905                 else if (strstr(verb, "user"))
906                         r = status_property_user(name, &sub3, &user_info);
907                 else
908                         r = status_property_seat(name, &sub3, &seat_info);
909
910                 if (r < 0) {
911                         log_error("Failed to parse reply.");
912                         goto finish;
913                 }
914
915                 dbus_message_iter_next(&sub);
916         }
917
918         if (!show_properties) {
919                 if (strstr(verb, "session"))
920                         print_session_status_info(&session_info);
921                 else if (strstr(verb, "user"))
922                         print_user_status_info(&user_info);
923                 else
924                         print_seat_status_info(&seat_info);
925         }
926
927         r = 0;
928
929 finish:
930         strv_free(seat_info.sessions);
931         strv_free(user_info.sessions);
932
933         return r;
934 }
935
936 static int show(DBusConnection *bus, char **args, unsigned n) {
937         _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
938         int r, ret = 0;
939         DBusError error;
940         unsigned i;
941         bool show_properties, new_line = false;
942
943         assert(bus);
944         assert(args);
945
946         dbus_error_init(&error);
947
948         show_properties = !strstr(args[0], "status");
949
950         pager_open_if_enabled();
951
952         if (show_properties && n <= 1) {
953                 /* If not argument is specified inspect the manager
954                  * itself */
955
956                 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
957                 goto finish;
958         }
959
960         for (i = 1; i < n; i++) {
961                 const char *path = NULL;
962
963                 if (strstr(args[0], "session")) {
964
965                         ret = bus_method_call_with_reply (
966                                         bus,
967                                         "org.freedesktop.login1",
968                                         "/org/freedesktop/login1",
969                                         "org.freedesktop.login1.Manager",
970                                         "GetSession",
971                                         &reply,
972                                         NULL,
973                                         DBUS_TYPE_STRING, &args[i],
974                                         DBUS_TYPE_INVALID);
975
976                 } else if (strstr(args[0], "user")) {
977                         uid_t uid;
978                         uint32_t u;
979
980                         ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
981                         if (ret < 0) {
982                                 log_error("User %s unknown.", args[i]);
983                                 goto finish;
984                         }
985
986                         u = (uint32_t) uid;
987                         ret = bus_method_call_with_reply (
988                                         bus,
989                                         "org.freedesktop.login1",
990                                         "/org/freedesktop/login1",
991                                         "org.freedesktop.login1.Manager",
992                                         "GetUser",
993                                         &reply,
994                                         NULL,
995                                         DBUS_TYPE_UINT32, &u,
996                                         DBUS_TYPE_INVALID);
997                 } else {
998
999                         ret = bus_method_call_with_reply (
1000                                         bus,
1001                                         "org.freedesktop.login1",
1002                                         "/org/freedesktop/login1",
1003                                         "org.freedesktop.login1.Manager",
1004                                         "GetSeat",
1005                                         &reply,
1006                                         NULL,
1007                                         DBUS_TYPE_STRING, &args[i],
1008                                         DBUS_TYPE_INVALID);
1009                 }
1010                 if (ret)
1011                         goto finish;
1012
1013                 if (!dbus_message_get_args(reply, &error,
1014                                            DBUS_TYPE_OBJECT_PATH, &path,
1015                                            DBUS_TYPE_INVALID)) {
1016                         log_error("Failed to parse reply: %s", bus_error_message(&error));
1017                         ret = -EIO;
1018                         goto finish;
1019                 }
1020
1021                 r = show_one(args[0], bus, path, show_properties, &new_line);
1022                 if (r != 0)
1023                         ret = r;
1024         }
1025
1026 finish:
1027         dbus_error_free(&error);
1028
1029         return ret;
1030 }
1031
1032 static int activate(DBusConnection *bus, char **args, unsigned n) {
1033         int ret = 0;
1034         unsigned i;
1035
1036         assert(args);
1037
1038         for (i = 1; i < n; i++) {
1039
1040                 ret = bus_method_call_with_reply (
1041                                 bus,
1042                                 "org.freedesktop.login1",
1043                                 "/org/freedesktop/login1",
1044                                 "org.freedesktop.login1.Manager",
1045                                 streq(args[0], "lock-session")      ? "LockSession" :
1046                                 streq(args[0], "unlock-session")    ? "UnlockSession" :
1047                                 streq(args[0], "terminate-session") ? "TerminateSession" :
1048                                                                       "ActivateSession",
1049                                 NULL,
1050                                 NULL,
1051                                 DBUS_TYPE_STRING, &args[i],
1052                                 DBUS_TYPE_INVALID);
1053                 if (ret)
1054                         goto finish;
1055         }
1056
1057 finish:
1058         return ret;
1059 }
1060
1061 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1062         unsigned i;
1063
1064         assert(args);
1065
1066         if (!arg_kill_who)
1067                 arg_kill_who = "all";
1068
1069         for (i = 1; i < n; i++) {
1070                 int r;
1071
1072                 r = bus_method_call_with_reply (
1073                         bus,
1074                         "org.freedesktop.login1",
1075                         "/org/freedesktop/login1",
1076                         "org.freedesktop.login1.Manager",
1077                         "KillSession",
1078                         NULL,
1079                         NULL,
1080                         DBUS_TYPE_STRING, &args[i],
1081                         DBUS_TYPE_STRING, &arg_kill_who,
1082                         DBUS_TYPE_INT32, &arg_signal,
1083                         DBUS_TYPE_INVALID);
1084                 if (r)
1085                         return r;
1086         }
1087
1088         return 0;
1089 }
1090
1091 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1092         unsigned i;
1093         dbus_bool_t b, interactive = true;
1094
1095         assert(args);
1096
1097         polkit_agent_open_if_enabled();
1098
1099         b = streq(args[0], "enable-linger");
1100
1101         for (i = 1; i < n; i++) {
1102                 uint32_t u;
1103                 uid_t uid;
1104                 int r;
1105
1106                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1107                 if (r < 0) {
1108                         log_error("Failed to resolve user %s: %s", args[i], strerror(-r));
1109                         return r;
1110                 }
1111
1112                 u = (uint32_t) uid;
1113                 r = bus_method_call_with_reply (
1114                         bus,
1115                         "org.freedesktop.login1",
1116                         "/org/freedesktop/login1",
1117                         "org.freedesktop.login1.Manager",
1118                         "SetUserLinger",
1119                         NULL,
1120                         NULL,
1121                         DBUS_TYPE_UINT32, &u,
1122                         DBUS_TYPE_BOOLEAN, &b,
1123                         DBUS_TYPE_BOOLEAN, &interactive,
1124                         DBUS_TYPE_INVALID);
1125                 if (r)
1126                         return r;
1127         }
1128
1129         return 0;
1130 }
1131
1132 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1133         unsigned i;
1134
1135         assert(args);
1136
1137         for (i = 1; i < n; i++) {
1138                 uint32_t u;
1139                 uid_t uid;
1140                 int r;
1141
1142                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1143                 if (r < 0) {
1144                         log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1145                         return r;
1146                 }
1147
1148                 u = (uint32_t) uid;
1149                 r = bus_method_call_with_reply (
1150                         bus,
1151                         "org.freedesktop.login1",
1152                         "/org/freedesktop/login1",
1153                         "org.freedesktop.login1.Manager",
1154                         "TerminateUser",
1155                         NULL,
1156                         NULL,
1157                         DBUS_TYPE_UINT32, &u,
1158                         DBUS_TYPE_INVALID);
1159                 if (r)
1160                         return r;
1161         }
1162
1163         return 0;
1164 }
1165
1166 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1167         unsigned i;
1168
1169         assert(args);
1170
1171         if (!arg_kill_who)
1172                 arg_kill_who = "all";
1173
1174         for (i = 1; i < n; i++) {
1175                 uid_t uid;
1176                 uint32_t u;
1177                 int r;
1178
1179                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
1180                 if (r < 0) {
1181                         log_error("Failed to look up user %s: %s", args[i], strerror(-r));
1182                         return r;
1183                 }
1184
1185                 u = (uint32_t) uid;
1186                 r = bus_method_call_with_reply (
1187                         bus,
1188                         "org.freedesktop.login1",
1189                         "/org/freedesktop/login1",
1190                         "org.freedesktop.login1.Manager",
1191                         "KillUser",
1192                         NULL,
1193                         NULL,
1194                         DBUS_TYPE_UINT32, &u,
1195                         DBUS_TYPE_INT32, &arg_signal,
1196                         DBUS_TYPE_INVALID);
1197                 if (r)
1198                         return r;
1199         }
1200
1201         return 0;
1202 }
1203
1204 static int attach(DBusConnection *bus, char **args, unsigned n) {
1205         unsigned i;
1206         dbus_bool_t interactive = true;
1207
1208         assert(args);
1209
1210         polkit_agent_open_if_enabled();
1211
1212         for (i = 2; i < n; i++) {
1213                 int r;
1214
1215                 r = bus_method_call_with_reply (
1216                         bus,
1217                         "org.freedesktop.login1",
1218                         "/org/freedesktop/login1",
1219                         "org.freedesktop.login1.Manager",
1220                         "AttachDevice",
1221                         NULL,
1222                         NULL,
1223                         DBUS_TYPE_STRING, &args[1],
1224                         DBUS_TYPE_STRING, &args[i],
1225                         DBUS_TYPE_BOOLEAN, &interactive,
1226                         DBUS_TYPE_INVALID);
1227                 if (r)
1228                         return r;
1229         }
1230
1231         return 0;
1232 }
1233
1234 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1235         dbus_bool_t interactive = true;
1236
1237         assert(args);
1238
1239         polkit_agent_open_if_enabled();
1240
1241         return bus_method_call_with_reply (
1242                         bus,
1243                         "org.freedesktop.login1",
1244                         "/org/freedesktop/login1",
1245                         "org.freedesktop.login1.Manager",
1246                         "FlushDevices",
1247                         NULL,
1248                         NULL,
1249                         DBUS_TYPE_BOOLEAN, &interactive,
1250                         DBUS_TYPE_INVALID);
1251 }
1252
1253 static int lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1254         assert(args);
1255
1256         polkit_agent_open_if_enabled();
1257
1258         return bus_method_call_with_reply (
1259                         bus,
1260                         "org.freedesktop.login1",
1261                         "/org/freedesktop/login1",
1262                         "org.freedesktop.login1.Manager",
1263                         streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1264                         NULL,
1265                         NULL,
1266                         DBUS_TYPE_INVALID);
1267 }
1268
1269 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1270         unsigned i;
1271
1272         assert(args);
1273
1274         for (i = 1; i < n; i++) {
1275                 int r;
1276
1277                 r = bus_method_call_with_reply (
1278                         bus,
1279                         "org.freedesktop.login1",
1280                         "/org/freedesktop/login1",
1281                         "org.freedesktop.login1.Manager",
1282                         "TerminateSeat",
1283                         NULL,
1284                         NULL,
1285                         DBUS_TYPE_STRING, &args[i],
1286                         DBUS_TYPE_INVALID);
1287                 if (r)
1288                         return r;
1289         }
1290
1291         return 0;
1292 }
1293
1294 static int help(void) {
1295
1296         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1297                "Send control commands to or query the login manager.\n\n"
1298                "  -h --help              Show this help\n"
1299                "     --version           Show package version\n"
1300                "  -p --property=NAME     Show only properties by this name\n"
1301                "  -a --all               Show all properties, including empty ones\n"
1302                "     --kill-who=WHO      Who to send signal to\n"
1303                "  --full                 Do not ellipsize output\n"
1304                "  -s --signal=SIGNAL     Which signal to send\n"
1305                "     --no-ask-password   Don't prompt for password\n"
1306                "  -H --host=[USER@]HOST  Show information for remote host\n"
1307                "  -P --privileged        Acquire privileges before execution\n"
1308                "     --no-pager          Do not pipe output into a pager\n\n"
1309                "Commands:\n"
1310                "  list-sessions                   List sessions\n"
1311                "  session-status [ID...]          Show session status\n"
1312                "  show-session [ID...]            Show properties of one or more sessions\n"
1313                "  activate [ID]                   Activate a session\n"
1314                "  lock-session [ID...]            Screen lock one or more sessions\n"
1315                "  unlock-session [ID...]          Screen unlock one or more sessions\n"
1316                "  lock-sessions                   Screen lock all current sessions\n"
1317                "  unlock-sessions                 Screen unlock all current sessions\n"
1318                "  terminate-session [ID...]       Terminate one or more sessions\n"
1319                "  kill-session [ID...]            Send signal to processes of a session\n"
1320                "  list-users                      List users\n"
1321                "  user-status [USER...]           Show user status\n"
1322                "  show-user [USER...]             Show properties of one or more users\n"
1323                "  enable-linger [USER...]         Enable linger state of one or more users\n"
1324                "  disable-linger [USER...]        Disable linger state of one or more users\n"
1325                "  terminate-user [USER...]        Terminate all sessions of one or more users\n"
1326                "  kill-user [USER...]             Send signal to processes of a user\n"
1327                "  list-seats                      List seats\n"
1328                "  seat-status [NAME...]           Show seat status\n"
1329                "  show-seat [NAME...]             Show properties of one or more seats\n"
1330                "  attach [NAME] [DEVICE...]       Attach one or more devices to a seat\n"
1331                "  flush-devices                   Flush all device associations\n"
1332                "  terminate-seat [NAME...]        Terminate all sessions on one or more seats\n",
1333                program_invocation_short_name);
1334
1335         return 0;
1336 }
1337
1338 static int parse_argv(int argc, char *argv[]) {
1339
1340         enum {
1341                 ARG_VERSION = 0x100,
1342                 ARG_NO_PAGER,
1343                 ARG_KILL_WHO,
1344                 ARG_NO_ASK_PASSWORD,
1345                 ARG_FULL,
1346         };
1347
1348         static const struct option options[] = {
1349                 { "help",            no_argument,       NULL, 'h'                 },
1350                 { "version",         no_argument,       NULL, ARG_VERSION         },
1351                 { "property",        required_argument, NULL, 'p'                 },
1352                 { "all",             no_argument,       NULL, 'a'                 },
1353                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
1354                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
1355                 { "signal",          required_argument, NULL, 's'                 },
1356                 { "host",            required_argument, NULL, 'H'                 },
1357                 { "privileged",      no_argument,       NULL, 'P'                 },
1358                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
1359                 { "full",            no_argument,       NULL, ARG_FULL            },
1360                 { NULL,              0,                 NULL, 0                   }
1361         };
1362
1363         int c;
1364
1365         assert(argc >= 0);
1366         assert(argv);
1367
1368         while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1369
1370                 switch (c) {
1371
1372                 case 'h':
1373                         help();
1374                         return 0;
1375
1376                 case ARG_VERSION:
1377                         puts(PACKAGE_STRING);
1378                         puts(SYSTEMD_FEATURES);
1379                         return 0;
1380
1381                 case 'p': {
1382                         char **l;
1383
1384                         l = strv_append(arg_property, optarg);
1385                         if (!l)
1386                                 return -ENOMEM;
1387
1388                         strv_free(arg_property);
1389                         arg_property = l;
1390
1391                         /* If the user asked for a particular
1392                          * property, show it to him, even if it is
1393                          * empty. */
1394                         arg_all = true;
1395                         break;
1396                 }
1397
1398                 case 'a':
1399                         arg_all = true;
1400                         break;
1401
1402                 case ARG_NO_PAGER:
1403                         arg_no_pager = true;
1404                         break;
1405
1406                 case ARG_NO_ASK_PASSWORD:
1407                         arg_ask_password = false;
1408                         break;
1409
1410                 case ARG_KILL_WHO:
1411                         arg_kill_who = optarg;
1412                         break;
1413
1414                 case 's':
1415                         arg_signal = signal_from_string_try_harder(optarg);
1416                         if (arg_signal < 0) {
1417                                 log_error("Failed to parse signal string %s.", optarg);
1418                                 return -EINVAL;
1419                         }
1420                         break;
1421
1422                 case 'P':
1423                         arg_transport = TRANSPORT_POLKIT;
1424                         break;
1425
1426                 case 'H':
1427                         arg_transport = TRANSPORT_SSH;
1428                         arg_host = optarg;
1429                         break;
1430
1431                 case ARG_FULL:
1432                         arg_full = true;
1433                         break;
1434
1435                 case '?':
1436                         return -EINVAL;
1437
1438                 default:
1439                         log_error("Unknown option code %c", c);
1440                         return -EINVAL;
1441                 }
1442         }
1443
1444         return 1;
1445 }
1446
1447 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1448
1449         static const struct {
1450                 const char* verb;
1451                 const enum {
1452                         MORE,
1453                         LESS,
1454                         EQUAL
1455                 } argc_cmp;
1456                 const int argc;
1457                 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1458         } verbs[] = {
1459                 { "list-sessions",         LESS,   1, list_sessions    },
1460                 { "session-status",        MORE,   2, show             },
1461                 { "show-session",          MORE,   1, show             },
1462                 { "activate",              EQUAL,  2, activate         },
1463                 { "lock-session",          MORE,   2, activate         },
1464                 { "unlock-session",        MORE,   2, activate         },
1465                 { "lock-sessions",         EQUAL,  1, lock_sessions    },
1466                 { "unlock-sessions",       EQUAL,  1, lock_sessions    },
1467                 { "terminate-session",     MORE,   2, activate         },
1468                 { "kill-session",          MORE,   2, kill_session     },
1469                 { "list-users",            EQUAL,  1, list_users       },
1470                 { "user-status",           MORE,   2, show             },
1471                 { "show-user",             MORE,   1, show             },
1472                 { "enable-linger",         MORE,   2, enable_linger    },
1473                 { "disable-linger",        MORE,   2, enable_linger    },
1474                 { "terminate-user",        MORE,   2, terminate_user   },
1475                 { "kill-user",             MORE,   2, kill_user        },
1476                 { "list-seats",            EQUAL,  1, list_seats       },
1477                 { "seat-status",           MORE,   2, show             },
1478                 { "show-seat",             MORE,   1, show             },
1479                 { "attach",                MORE,   3, attach           },
1480                 { "flush-devices",         EQUAL,  1, flush_devices    },
1481                 { "terminate-seat",        MORE,   2, terminate_seat   },
1482         };
1483
1484         int left;
1485         unsigned i;
1486
1487         assert(argc >= 0);
1488         assert(argv);
1489         assert(error);
1490
1491         left = argc - optind;
1492
1493         if (left <= 0)
1494                 /* Special rule: no arguments means "list-sessions" */
1495                 i = 0;
1496         else {
1497                 if (streq(argv[optind], "help")) {
1498                         help();
1499                         return 0;
1500                 }
1501
1502                 for (i = 0; i < ELEMENTSOF(verbs); i++)
1503                         if (streq(argv[optind], verbs[i].verb))
1504                                 break;
1505
1506                 if (i >= ELEMENTSOF(verbs)) {
1507                         log_error("Unknown operation %s", argv[optind]);
1508                         return -EINVAL;
1509                 }
1510         }
1511
1512         switch (verbs[i].argc_cmp) {
1513
1514         case EQUAL:
1515                 if (left != verbs[i].argc) {
1516                         log_error("Invalid number of arguments.");
1517                         return -EINVAL;
1518                 }
1519
1520                 break;
1521
1522         case MORE:
1523                 if (left < verbs[i].argc) {
1524                         log_error("Too few arguments.");
1525                         return -EINVAL;
1526                 }
1527
1528                 break;
1529
1530         case LESS:
1531                 if (left > verbs[i].argc) {
1532                         log_error("Too many arguments.");
1533                         return -EINVAL;
1534                 }
1535
1536                 break;
1537
1538         default:
1539                 assert_not_reached("Unknown comparison operator.");
1540         }
1541
1542         if (!bus) {
1543                 log_error("Failed to get D-Bus connection: %s", error->message);
1544                 return -EIO;
1545         }
1546
1547         return verbs[i].dispatch(bus, argv + optind, left);
1548 }
1549
1550 int main(int argc, char*argv[]) {
1551         int r, retval = EXIT_FAILURE;
1552         DBusConnection *bus = NULL;
1553         DBusError error;
1554
1555         dbus_error_init(&error);
1556
1557         setlocale(LC_ALL, "");
1558         log_parse_environment();
1559         log_open();
1560
1561         r = parse_argv(argc, argv);
1562         if (r < 0)
1563                 goto finish;
1564         else if (r == 0) {
1565                 retval = EXIT_SUCCESS;
1566                 goto finish;
1567         }
1568
1569         if (arg_transport == TRANSPORT_NORMAL)
1570                 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1571         else if (arg_transport == TRANSPORT_POLKIT)
1572                 bus_connect_system_polkit(&bus, &error);
1573         else if (arg_transport == TRANSPORT_SSH)
1574                 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1575         else
1576                 assert_not_reached("Uh, invalid transport...");
1577
1578         r = loginctl_main(bus, argc, argv, &error);
1579         retval = r < 0 ? EXIT_FAILURE : r;
1580
1581 finish:
1582         if (bus) {
1583                 dbus_connection_flush(bus);
1584                 dbus_connection_close(bus);
1585                 dbus_connection_unref(bus);
1586         }
1587
1588         dbus_error_free(&error);
1589         dbus_shutdown();
1590
1591         strv_free(arg_property);
1592
1593         pager_close();
1594
1595         return retval;
1596 }