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