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