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