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