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