chiark / gitweb /
login: missing break for getopt ARG_NO_ASK_PASSWORD in loginctl
[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                         break;
1450
1451                 case ARG_KILL_WHO:
1452                         arg_kill_who = optarg;
1453                         break;
1454
1455                 case 's':
1456                         arg_signal = signal_from_string_try_harder(optarg);
1457                         if (arg_signal < 0) {
1458                                 log_error("Failed to parse signal string %s.", optarg);
1459                                 return -EINVAL;
1460                         }
1461                         break;
1462
1463                 case 'P':
1464                         arg_transport = TRANSPORT_POLKIT;
1465                         break;
1466
1467                 case 'H':
1468                         arg_transport = TRANSPORT_SSH;
1469                         arg_host = optarg;
1470                         break;
1471
1472                 case '?':
1473                         return -EINVAL;
1474
1475                 default:
1476                         log_error("Unknown option code %c", c);
1477                         return -EINVAL;
1478                 }
1479         }
1480
1481         return 1;
1482 }
1483
1484 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1485
1486         static const struct {
1487                 const char* verb;
1488                 const enum {
1489                         MORE,
1490                         LESS,
1491                         EQUAL
1492                 } argc_cmp;
1493                 const int argc;
1494                 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1495         } verbs[] = {
1496                 { "list-sessions",         LESS,   1, list_sessions    },
1497                 { "session-status",        MORE,   2, show             },
1498                 { "show-session",          MORE,   1, show             },
1499                 { "activate",              EQUAL,  2, activate         },
1500                 { "lock-session",          MORE,   2, activate         },
1501                 { "unlock-session",        MORE,   2, activate         },
1502                 { "lock-sessions",         EQUAL,  1, lock_sessions    },
1503                 { "terminate-session",     MORE,   2, activate         },
1504                 { "kill-session",          MORE,   2, kill_session     },
1505                 { "list-users",            EQUAL,  1, list_users       },
1506                 { "user-status",           MORE,   2, show             },
1507                 { "show-user",             MORE,   1, show             },
1508                 { "enable-linger",         MORE,   2, enable_linger    },
1509                 { "disable-linger",        MORE,   2, enable_linger    },
1510                 { "terminate-user",        MORE,   2, terminate_user   },
1511                 { "kill-user",             MORE,   2, kill_user        },
1512                 { "list-seats",            EQUAL,  1, list_seats       },
1513                 { "seat-status",           MORE,   2, show             },
1514                 { "show-seat",             MORE,   1, show             },
1515                 { "attach",                MORE,   3, attach           },
1516                 { "flush-devices",         EQUAL,  1, flush_devices    },
1517                 { "terminate-seat",        MORE,   2, terminate_seat   },
1518         };
1519
1520         int left;
1521         unsigned i;
1522
1523         assert(argc >= 0);
1524         assert(argv);
1525         assert(error);
1526
1527         left = argc - optind;
1528
1529         if (left <= 0)
1530                 /* Special rule: no arguments means "list-sessions" */
1531                 i = 0;
1532         else {
1533                 if (streq(argv[optind], "help")) {
1534                         help();
1535                         return 0;
1536                 }
1537
1538                 for (i = 0; i < ELEMENTSOF(verbs); i++)
1539                         if (streq(argv[optind], verbs[i].verb))
1540                                 break;
1541
1542                 if (i >= ELEMENTSOF(verbs)) {
1543                         log_error("Unknown operation %s", argv[optind]);
1544                         return -EINVAL;
1545                 }
1546         }
1547
1548         switch (verbs[i].argc_cmp) {
1549
1550         case EQUAL:
1551                 if (left != verbs[i].argc) {
1552                         log_error("Invalid number of arguments.");
1553                         return -EINVAL;
1554                 }
1555
1556                 break;
1557
1558         case MORE:
1559                 if (left < verbs[i].argc) {
1560                         log_error("Too few arguments.");
1561                         return -EINVAL;
1562                 }
1563
1564                 break;
1565
1566         case LESS:
1567                 if (left > verbs[i].argc) {
1568                         log_error("Too many arguments.");
1569                         return -EINVAL;
1570                 }
1571
1572                 break;
1573
1574         default:
1575                 assert_not_reached("Unknown comparison operator.");
1576         }
1577
1578         if (!bus) {
1579                 log_error("Failed to get D-Bus connection: %s", error->message);
1580                 return -EIO;
1581         }
1582
1583         return verbs[i].dispatch(bus, argv + optind, left);
1584 }
1585
1586 int main(int argc, char*argv[]) {
1587         int r, retval = EXIT_FAILURE;
1588         DBusConnection *bus = NULL;
1589         DBusError error;
1590
1591         dbus_error_init(&error);
1592
1593         log_parse_environment();
1594         log_open();
1595
1596         r = parse_argv(argc, argv);
1597         if (r < 0)
1598                 goto finish;
1599         else if (r == 0) {
1600                 retval = EXIT_SUCCESS;
1601                 goto finish;
1602         }
1603
1604         if (arg_transport == TRANSPORT_NORMAL)
1605                 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1606         else if (arg_transport == TRANSPORT_POLKIT)
1607                 bus_connect_system_polkit(&bus, &error);
1608         else if (arg_transport == TRANSPORT_SSH)
1609                 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1610         else
1611                 assert_not_reached("Uh, invalid transport...");
1612
1613         r = loginctl_main(bus, argc, argv, &error);
1614         retval = r < 0 ? EXIT_FAILURE : r;
1615
1616 finish:
1617         if (bus) {
1618                 dbus_connection_flush(bus);
1619                 dbus_connection_close(bus);
1620                 dbus_connection_unref(bus);
1621         }
1622
1623         dbus_error_free(&error);
1624         dbus_shutdown();
1625
1626         strv_free(arg_property);
1627
1628         pager_close();
1629
1630         return retval;
1631 }