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