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