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