chiark / gitweb /
polkit: temporarily spawn of a polkit agent in terminals for possibly authenticated...
[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 General Public License as published by
10   the Free Software Foundation; either version 2 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   General Public License for more details.
17
18   You should have received a copy of the GNU 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 *m = NULL, *reply = NULL;
91         DBusError error;
92         int r;
93         DBusMessageIter iter, sub, sub2;
94         unsigned k = 0;
95
96         dbus_error_init(&error);
97
98         assert(bus);
99
100         pager_open_if_enabled();
101
102         m = dbus_message_new_method_call(
103                         "org.freedesktop.login1",
104                         "/org/freedesktop/login1",
105                         "org.freedesktop.login1.Manager",
106                         "ListSessions");
107         if (!m) {
108                 log_error("Could not allocate message.");
109                 return -ENOMEM;
110         }
111
112         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
113         if (!reply) {
114                 log_error("Failed to issue method call: %s", bus_error_message(&error));
115                 r = -EIO;
116                 goto finish;
117         }
118
119         if (!dbus_message_iter_init(reply, &iter) ||
120             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
121             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
122                 log_error("Failed to parse reply.");
123                 r = -EIO;
124                 goto finish;
125         }
126
127         dbus_message_iter_recurse(&iter, &sub);
128
129         if (on_tty())
130                 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
131
132         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
133                 const char *id, *user, *seat, *object;
134                 uint32_t uid;
135
136                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
137                         log_error("Failed to parse reply.");
138                         r = -EIO;
139                         goto finish;
140                 }
141
142                 dbus_message_iter_recurse(&sub, &sub2);
143
144                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) < 0 ||
145                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
146                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
147                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
148                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
149                         log_error("Failed to parse reply.");
150                         r = -EIO;
151                         goto finish;
152                 }
153
154                 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
155
156                 k++;
157
158                 dbus_message_iter_next(&sub);
159         }
160
161         if (on_tty())
162                 printf("\n%u sessions listed.\n", k);
163
164         r = 0;
165
166 finish:
167         if (m)
168                 dbus_message_unref(m);
169
170         if (reply)
171                 dbus_message_unref(reply);
172
173         dbus_error_free(&error);
174
175         return r;
176 }
177
178 static int list_users(DBusConnection *bus, char **args, unsigned n) {
179         DBusMessage *m = NULL, *reply = NULL;
180         DBusError error;
181         int r;
182         DBusMessageIter iter, sub, sub2;
183         unsigned k = 0;
184
185         dbus_error_init(&error);
186
187         assert(bus);
188
189         pager_open_if_enabled();
190
191         m = dbus_message_new_method_call(
192                         "org.freedesktop.login1",
193                         "/org/freedesktop/login1",
194                         "org.freedesktop.login1.Manager",
195                         "ListUsers");
196         if (!m) {
197                 log_error("Could not allocate message.");
198                 return -ENOMEM;
199         }
200
201         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
202         if (!reply) {
203                 log_error("Failed to issue method call: %s", bus_error_message(&error));
204                 r = -EIO;
205                 goto finish;
206         }
207
208         if (!dbus_message_iter_init(reply, &iter) ||
209             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
210             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
211                 log_error("Failed to parse reply.");
212                 r = -EIO;
213                 goto finish;
214         }
215
216         dbus_message_iter_recurse(&iter, &sub);
217
218         if (on_tty())
219                 printf("%10s %-16s\n", "UID", "USER");
220
221         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
222                 const char *user, *object;
223                 uint32_t uid;
224
225                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
226                         log_error("Failed to parse reply.");
227                         r = -EIO;
228                         goto finish;
229                 }
230
231                 dbus_message_iter_recurse(&sub, &sub2);
232
233                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_UINT32, &uid, true) < 0 ||
234                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &user, true) < 0 ||
235                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
236                         log_error("Failed to parse reply.");
237                         r = -EIO;
238                         goto finish;
239                 }
240
241                 printf("%10u %-16s\n", (unsigned) uid, user);
242
243                 k++;
244
245                 dbus_message_iter_next(&sub);
246         }
247
248         if (on_tty())
249                 printf("\n%u users listed.\n", k);
250
251         r = 0;
252
253 finish:
254         if (m)
255                 dbus_message_unref(m);
256
257         if (reply)
258                 dbus_message_unref(reply);
259
260         dbus_error_free(&error);
261
262         return r;
263 }
264
265 static int list_seats(DBusConnection *bus, char **args, unsigned n) {
266         DBusMessage *m = NULL, *reply = NULL;
267         DBusError error;
268         int r;
269         DBusMessageIter iter, sub, sub2;
270         unsigned k = 0;
271
272         dbus_error_init(&error);
273
274         assert(bus);
275
276         pager_open_if_enabled();
277
278         m = dbus_message_new_method_call(
279                         "org.freedesktop.login1",
280                         "/org/freedesktop/login1",
281                         "org.freedesktop.login1.Manager",
282                         "ListSeats");
283         if (!m) {
284                 log_error("Could not allocate message.");
285                 return -ENOMEM;
286         }
287
288         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
289         if (!reply) {
290                 log_error("Failed to issue method call: %s", bus_error_message(&error));
291                 r = -EIO;
292                 goto finish;
293         }
294
295         if (!dbus_message_iter_init(reply, &iter) ||
296             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
297             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)  {
298                 log_error("Failed to parse reply.");
299                 r = -EIO;
300                 goto finish;
301         }
302
303         dbus_message_iter_recurse(&iter, &sub);
304
305         if (on_tty())
306                 printf("%-16s\n", "SEAT");
307
308         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
309                 const char *seat, *object;
310
311                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
312                         log_error("Failed to parse reply.");
313                         r = -EIO;
314                         goto finish;
315                 }
316
317                 dbus_message_iter_recurse(&sub, &sub2);
318
319                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &seat, true) < 0 ||
320                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &object, false) < 0) {
321                         log_error("Failed to parse reply.");
322                         r = -EIO;
323                         goto finish;
324                 }
325
326                 printf("%-16s\n", seat);
327
328                 k++;
329
330                 dbus_message_iter_next(&sub);
331         }
332
333         if (on_tty())
334                 printf("\n%u seats listed.\n", k);
335
336         r = 0;
337
338 finish:
339         if (m)
340                 dbus_message_unref(m);
341
342         if (reply)
343                 dbus_message_unref(reply);
344
345         dbus_error_free(&error);
346
347         return r;
348 }
349
350 typedef struct SessionStatusInfo {
351         const char *id;
352         uid_t uid;
353         const char *name;
354         usec_t timestamp;
355         const char *control_group;
356         int vtnr;
357         const char *seat;
358         const char *tty;
359         const char *display;
360         bool remote;
361         const char *remote_host;
362         const char *remote_user;
363         const char *service;
364         pid_t leader;
365         const char *type;
366         const char *class;
367         bool active;
368 } SessionStatusInfo;
369
370 typedef struct UserStatusInfo {
371         uid_t uid;
372         const char *name;
373         usec_t timestamp;
374         const char *control_group;
375         const char *state;
376         char **sessions;
377         const char *display;
378 } UserStatusInfo;
379
380 typedef struct SeatStatusInfo {
381         const char *id;
382         const char *active_session;
383         char **sessions;
384 } SeatStatusInfo;
385
386 static void print_session_status_info(SessionStatusInfo *i) {
387         char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
388         char since2[FORMAT_TIMESTAMP_MAX], *s2;
389         assert(i);
390
391         printf("%s - ", strna(i->id));
392
393         if (i->name)
394                 printf("%s (%u)\n", i->name, (unsigned) i->uid);
395         else
396                 printf("%u\n", (unsigned) i->uid);
397
398         s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
399         s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
400
401         if (s1)
402                 printf("\t   Since: %s; %s\n", s2, s1);
403         else if (s2)
404                 printf("\t   Since: %s\n", s2);
405
406         if (i->leader > 0) {
407                 char *t = NULL;
408
409                 printf("\t  Leader: %u", (unsigned) i->leader);
410
411                 get_process_comm(i->leader, &t);
412                 if (t) {
413                         printf(" (%s)", t);
414                         free(t);
415                 }
416
417                 printf("\n");
418         }
419
420         if (i->seat) {
421                 printf("\t    Seat: %s", i->seat);
422
423                 if (i->vtnr > 0)
424                         printf("; vc%i", i->vtnr);
425
426                 printf("\n");
427         }
428
429         if (i->tty)
430                 printf("\t     TTY: %s\n", i->tty);
431         else if (i->display)
432                 printf("\t Display: %s\n", i->display);
433
434         if (i->remote_host && i->remote_user)
435                 printf("\t  Remote: %s@%s\n", i->remote_user, i->remote_host);
436         else if (i->remote_host)
437                 printf("\t  Remote: %s\n", i->remote_host);
438         else if (i->remote_user)
439                 printf("\t  Remote: user %s\n", i->remote_user);
440         else if (i->remote)
441                 printf("\t  Remote: Yes\n");
442
443         if (i->service) {
444                 printf("\t Service: %s", i->service);
445
446                 if (i->type)
447                         printf("; type %s", i->type);
448
449                 if (i->class)
450                         printf("; class %s", i->class);
451
452                 printf("\n");
453         } else if (i->type) {
454                 printf("\t    Type: %s\n", i->type);
455
456                 if (i->class)
457                         printf("; class %s", i->class);
458         } else if (i->class)
459                 printf("\t   Class: %s\n", i->class);
460
461
462         printf("\t  Active: %s\n", yes_no(i->active));
463
464         if (i->control_group) {
465                 unsigned c;
466
467                 printf("\t  CGroup: %s\n", i->control_group);
468
469                 if (arg_transport != TRANSPORT_SSH) {
470                         c = columns();
471                         if (c > 18)
472                                 c -= 18;
473                         else
474                                 c = 0;
475
476                         show_cgroup_by_path(i->control_group, "\t\t  ", c, false);
477                 }
478         }
479 }
480
481 static void print_user_status_info(UserStatusInfo *i) {
482         char since1[FORMAT_TIMESTAMP_PRETTY_MAX], *s1;
483         char since2[FORMAT_TIMESTAMP_MAX], *s2;
484         assert(i);
485
486         if (i->name)
487                 printf("%s (%u)\n", i->name, (unsigned) i->uid);
488         else
489                 printf("%u\n", (unsigned) i->uid);
490
491         s1 = format_timestamp_pretty(since1, sizeof(since1), i->timestamp);
492         s2 = format_timestamp(since2, sizeof(since2), i->timestamp);
493
494         if (s1)
495                 printf("\t   Since: %s; %s\n", s2, s1);
496         else if (s2)
497                 printf("\t   Since: %s\n", s2);
498
499         if (!isempty(i->state))
500                 printf("\t   State: %s\n", i->state);
501
502         if (!strv_isempty(i->sessions)) {
503                 char **l;
504                 printf("\tSessions:");
505
506                 STRV_FOREACH(l, i->sessions) {
507                         if (streq_ptr(*l, i->display))
508                                 printf(" *%s", *l);
509                         else
510                                 printf(" %s", *l);
511                 }
512
513                 printf("\n");
514         }
515
516         if (i->control_group) {
517                 unsigned c;
518
519                 printf("\t  CGroup: %s\n", i->control_group);
520
521                 if (arg_transport != TRANSPORT_SSH) {
522                         c = columns();
523                         if (c > 18)
524                                 c -= 18;
525                         else
526                                 c = 0;
527
528                         show_cgroup_by_path(i->control_group, "\t\t  ", c, false);
529                 }
530         }
531 }
532
533 static void print_seat_status_info(SeatStatusInfo *i) {
534         assert(i);
535
536         printf("%s\n", strna(i->id));
537
538         if (!strv_isempty(i->sessions)) {
539                 char **l;
540                 printf("\tSessions:");
541
542                 STRV_FOREACH(l, i->sessions) {
543                         if (streq_ptr(*l, i->active_session))
544                                 printf(" *%s", *l);
545                         else
546                                 printf(" %s", *l);
547                 }
548
549                 printf("\n");
550         }
551
552         if (arg_transport != TRANSPORT_SSH) {
553                 unsigned c;
554
555                 c = columns();
556                 if (c > 21)
557                         c -= 21;
558                 else
559                         c = 0;
560
561                 printf("\t Devices:\n");
562
563                 show_sysfs(i->id, "\t\t  ", c);
564         }
565 }
566
567 static int status_property_session(const char *name, DBusMessageIter *iter, SessionStatusInfo *i) {
568         assert(name);
569         assert(iter);
570         assert(i);
571
572         switch (dbus_message_iter_get_arg_type(iter)) {
573
574         case DBUS_TYPE_STRING: {
575                 const char *s;
576
577                 dbus_message_iter_get_basic(iter, &s);
578
579                 if (!isempty(s)) {
580                         if (streq(name, "Id"))
581                                 i->id = s;
582                         else if (streq(name, "Name"))
583                                 i->name = s;
584                         else if (streq(name, "ControlGroupPath"))
585                                 i->control_group = s;
586                         else if (streq(name, "TTY"))
587                                 i->tty = s;
588                         else if (streq(name, "Display"))
589                                 i->display = s;
590                         else if (streq(name, "RemoteHost"))
591                                 i->remote_host = s;
592                         else if (streq(name, "RemoteUser"))
593                                 i->remote_user = s;
594                         else if (streq(name, "Service"))
595                                 i->service = s;
596                         else if (streq(name, "Type"))
597                                 i->type = s;
598                         else if (streq(name, "Class"))
599                                 i->class = s;
600                 }
601                 break;
602         }
603
604         case DBUS_TYPE_UINT32: {
605                 uint32_t u;
606
607                 dbus_message_iter_get_basic(iter, &u);
608
609                 if (streq(name, "VTNr"))
610                         i->vtnr = (int) u;
611                 else if (streq(name, "Leader"))
612                         i->leader = (pid_t) u;
613
614                 break;
615         }
616
617         case DBUS_TYPE_BOOLEAN: {
618                 dbus_bool_t b;
619
620                 dbus_message_iter_get_basic(iter, &b);
621
622                 if (streq(name, "Remote"))
623                         i->remote = b;
624                 else if (streq(name, "Active"))
625                         i->active = b;
626
627                 break;
628         }
629
630         case DBUS_TYPE_UINT64: {
631                 uint64_t u;
632
633                 dbus_message_iter_get_basic(iter, &u);
634
635                 if (streq(name, "Timestamp"))
636                         i->timestamp = (usec_t) u;
637
638                 break;
639         }
640
641         case DBUS_TYPE_STRUCT: {
642                 DBusMessageIter sub;
643
644                 dbus_message_iter_recurse(iter, &sub);
645
646                 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32 && streq(name, "User")) {
647                         uint32_t u;
648
649                         dbus_message_iter_get_basic(&sub, &u);
650                         i->uid = (uid_t) u;
651
652                 } else if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Seat")) {
653                         const char *s;
654
655                         dbus_message_iter_get_basic(&sub, &s);
656
657                         if (!isempty(s))
658                                 i->seat = s;
659                 }
660
661                 break;
662         }
663         }
664
665         return 0;
666 }
667
668 static int status_property_user(const char *name, DBusMessageIter *iter, UserStatusInfo *i) {
669         assert(name);
670         assert(iter);
671         assert(i);
672
673         switch (dbus_message_iter_get_arg_type(iter)) {
674
675         case DBUS_TYPE_STRING: {
676                 const char *s;
677
678                 dbus_message_iter_get_basic(iter, &s);
679
680                 if (!isempty(s)) {
681                         if (streq(name, "Name"))
682                                 i->name = s;
683                         else if (streq(name, "ControlGroupPath"))
684                                 i->control_group = s;
685                         else if (streq(name, "State"))
686                                 i->state = s;
687                 }
688                 break;
689         }
690
691         case DBUS_TYPE_UINT32: {
692                 uint32_t u;
693
694                 dbus_message_iter_get_basic(iter, &u);
695
696                 if (streq(name, "UID"))
697                         i->uid = (uid_t) u;
698
699                 break;
700         }
701
702         case DBUS_TYPE_UINT64: {
703                 uint64_t u;
704
705                 dbus_message_iter_get_basic(iter, &u);
706
707                 if (streq(name, "Timestamp"))
708                         i->timestamp = (usec_t) u;
709
710                 break;
711         }
712
713         case DBUS_TYPE_STRUCT: {
714                 DBusMessageIter sub;
715
716                 dbus_message_iter_recurse(iter, &sub);
717
718                 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "Display")) {
719                         const char *s;
720
721                         dbus_message_iter_get_basic(&sub, &s);
722
723                         if (!isempty(s))
724                                 i->display = s;
725                 }
726
727                 break;
728         }
729
730         case DBUS_TYPE_ARRAY: {
731
732                 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
733                         DBusMessageIter sub, sub2;
734
735                         dbus_message_iter_recurse(iter, &sub);
736                         while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
737                                 const char *id;
738                                 const char *path;
739
740                                 dbus_message_iter_recurse(&sub, &sub2);
741
742                                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
743                                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
744                                         char **l;
745
746                                         l = strv_append(i->sessions, id);
747                                         if (!l)
748                                                 return -ENOMEM;
749
750                                         strv_free(i->sessions);
751                                         i->sessions = l;
752                                 }
753
754                                 dbus_message_iter_next(&sub);
755                         }
756
757                         return 0;
758                 }
759         }
760         }
761
762         return 0;
763 }
764
765 static int status_property_seat(const char *name, DBusMessageIter *iter, SeatStatusInfo *i) {
766         assert(name);
767         assert(iter);
768         assert(i);
769
770         switch (dbus_message_iter_get_arg_type(iter)) {
771
772         case DBUS_TYPE_STRING: {
773                 const char *s;
774
775                 dbus_message_iter_get_basic(iter, &s);
776
777                 if (!isempty(s)) {
778                         if (streq(name, "Id"))
779                                 i->id = s;
780                 }
781                 break;
782         }
783
784         case DBUS_TYPE_STRUCT: {
785                 DBusMessageIter sub;
786
787                 dbus_message_iter_recurse(iter, &sub);
788
789                 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING && streq(name, "ActiveSession")) {
790                         const char *s;
791
792                         dbus_message_iter_get_basic(&sub, &s);
793
794                         if (!isempty(s))
795                                 i->active_session = s;
796                 }
797
798                 break;
799         }
800
801         case DBUS_TYPE_ARRAY: {
802
803                 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
804                         DBusMessageIter sub, sub2;
805
806                         dbus_message_iter_recurse(iter, &sub);
807                         while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
808                                 const char *id;
809                                 const char *path;
810
811                                 dbus_message_iter_recurse(&sub, &sub2);
812
813                                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
814                                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
815                                         char **l;
816
817                                         l = strv_append(i->sessions, id);
818                                         if (!l)
819                                                 return -ENOMEM;
820
821                                         strv_free(i->sessions);
822                                         i->sessions = l;
823                                 }
824
825                                 dbus_message_iter_next(&sub);
826                         }
827
828                         return 0;
829                 }
830         }
831         }
832
833         return 0;
834 }
835
836 static int print_property(const char *name, DBusMessageIter *iter) {
837         assert(name);
838         assert(iter);
839
840         if (arg_property && !strv_find(arg_property, name))
841                 return 0;
842
843         switch (dbus_message_iter_get_arg_type(iter)) {
844
845         case DBUS_TYPE_STRUCT: {
846                 DBusMessageIter sub;
847
848                 dbus_message_iter_recurse(iter, &sub);
849
850                 if (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING &&
851                     (streq(name, "Display") || streq(name, "ActiveSession"))) {
852                         const char *s;
853
854                         dbus_message_iter_get_basic(&sub, &s);
855
856                         if (arg_all || !isempty(s))
857                                 printf("%s=%s\n", name, s);
858                         return 0;
859                 }
860                 break;
861         }
862
863         case DBUS_TYPE_ARRAY:
864
865                 if (dbus_message_iter_get_element_type(iter) == DBUS_TYPE_STRUCT && streq(name, "Sessions")) {
866                         DBusMessageIter sub, sub2;
867                         bool found = false;
868
869                         dbus_message_iter_recurse(iter, &sub);
870                         while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRUCT) {
871                                 const char *id;
872                                 const char *path;
873
874                                 dbus_message_iter_recurse(&sub, &sub2);
875
876                                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &id, true) >= 0 &&
877                                     bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_OBJECT_PATH, &path, false) >= 0) {
878                                         if (found)
879                                                 printf(" %s", id);
880                                         else {
881                                                 printf("%s=%s", name, id);
882                                                 found = true;
883                                         }
884                                 }
885
886                                 dbus_message_iter_next(&sub);
887                         }
888
889                         if (!found && arg_all)
890                                 printf("%s=\n", name);
891                         else if (found)
892                                 printf("\n");
893
894                         return 0;
895                 }
896
897                 break;
898         }
899
900         if (generic_print_property(name, iter, arg_all) > 0)
901                 return 0;
902
903         if (arg_all)
904                 printf("%s=[unprintable]\n", name);
905
906         return 0;
907 }
908
909 static int show_one(const char *verb, DBusConnection *bus, const char *path, bool show_properties, bool *new_line) {
910         DBusMessage *m = NULL, *reply = NULL;
911         const char *interface = "";
912         int r;
913         DBusError error;
914         DBusMessageIter iter, sub, sub2, sub3;
915         SessionStatusInfo session_info;
916         UserStatusInfo user_info;
917         SeatStatusInfo seat_info;
918
919         assert(bus);
920         assert(path);
921         assert(new_line);
922
923         zero(session_info);
924         zero(user_info);
925         zero(seat_info);
926
927         dbus_error_init(&error);
928
929         m = dbus_message_new_method_call(
930                         "org.freedesktop.login1",
931                         path,
932                         "org.freedesktop.DBus.Properties",
933                         "GetAll");
934         if (!m) {
935                 log_error("Could not allocate message.");
936                 r = -ENOMEM;
937                 goto finish;
938         }
939
940         if (!dbus_message_append_args(m,
941                                       DBUS_TYPE_STRING, &interface,
942                                       DBUS_TYPE_INVALID)) {
943                 log_error("Could not append arguments to message.");
944                 r = -ENOMEM;
945                 goto finish;
946         }
947
948         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
949         if (!reply) {
950                 log_error("Failed to issue method call: %s", bus_error_message(&error));
951                 r = -EIO;
952                 goto finish;
953         }
954
955         if (!dbus_message_iter_init(reply, &iter) ||
956             dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
957             dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY)  {
958                 log_error("Failed to parse reply.");
959                 r = -EIO;
960                 goto finish;
961         }
962
963         dbus_message_iter_recurse(&iter, &sub);
964
965         if (*new_line)
966                 printf("\n");
967
968         *new_line = true;
969
970         while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
971                 const char *name;
972
973                 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_DICT_ENTRY) {
974                         log_error("Failed to parse reply.");
975                         r = -EIO;
976                         goto finish;
977                 }
978
979                 dbus_message_iter_recurse(&sub, &sub2);
980
981                 if (bus_iter_get_basic_and_next(&sub2, DBUS_TYPE_STRING, &name, true) < 0) {
982                         log_error("Failed to parse reply.");
983                         r = -EIO;
984                         goto finish;
985                 }
986
987                 if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_VARIANT)  {
988                         log_error("Failed to parse reply.");
989                         r = -EIO;
990                         goto finish;
991                 }
992
993                 dbus_message_iter_recurse(&sub2, &sub3);
994
995                 if (show_properties)
996                         r = print_property(name, &sub3);
997                 else if (strstr(verb, "session"))
998                         r = status_property_session(name, &sub3, &session_info);
999                 else if (strstr(verb, "user"))
1000                         r = status_property_user(name, &sub3, &user_info);
1001                 else
1002                         r = status_property_seat(name, &sub3, &seat_info);
1003
1004                 if (r < 0) {
1005                         log_error("Failed to parse reply.");
1006                         r = -EIO;
1007                         goto finish;
1008                 }
1009
1010                 dbus_message_iter_next(&sub);
1011         }
1012
1013         if (!show_properties) {
1014                 if (strstr(verb, "session"))
1015                         print_session_status_info(&session_info);
1016                 else if (strstr(verb, "user"))
1017                         print_user_status_info(&user_info);
1018                 else
1019                         print_seat_status_info(&seat_info);
1020         }
1021
1022         strv_free(seat_info.sessions);
1023         strv_free(user_info.sessions);
1024
1025         r = 0;
1026
1027 finish:
1028         if (m)
1029                 dbus_message_unref(m);
1030
1031         if (reply)
1032                 dbus_message_unref(reply);
1033
1034         dbus_error_free(&error);
1035
1036         return r;
1037 }
1038
1039 static int show(DBusConnection *bus, char **args, unsigned n) {
1040         DBusMessage *m = NULL, *reply = NULL;
1041         int r, ret = 0;
1042         DBusError error;
1043         unsigned i;
1044         bool show_properties, new_line = false;
1045
1046         assert(bus);
1047         assert(args);
1048
1049         dbus_error_init(&error);
1050
1051         show_properties = !strstr(args[0], "status");
1052
1053         if (show_properties)
1054                 pager_open_if_enabled();
1055
1056         if (show_properties && n <= 1) {
1057                 /* If not argument is specified inspect the manager
1058                  * itself */
1059
1060                 ret = show_one(args[0], bus, "/org/freedesktop/login1", show_properties, &new_line);
1061                 goto finish;
1062         }
1063
1064         for (i = 1; i < n; i++) {
1065                 const char *path = NULL;
1066
1067                 if (strstr(args[0], "session")) {
1068
1069                         m = dbus_message_new_method_call(
1070                                         "org.freedesktop.login1",
1071                                         "/org/freedesktop/login1",
1072                                         "org.freedesktop.login1.Manager",
1073                                         "GetSession");
1074                         if (!m) {
1075                                 log_error("Could not allocate message.");
1076                                 ret = -ENOMEM;
1077                                 goto finish;
1078                         }
1079
1080                         if (!dbus_message_append_args(m,
1081                                                       DBUS_TYPE_STRING, &args[i],
1082                                                       DBUS_TYPE_INVALID)) {
1083                                 log_error("Could not append arguments to message.");
1084                                 ret = -ENOMEM;
1085                                 goto finish;
1086                         }
1087
1088                 } else if (strstr(args[0], "user")) {
1089                         uid_t uid;
1090                         uint32_t u;
1091
1092                         ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1093                         if (ret < 0) {
1094                                 log_error("User %s unknown.", args[i]);
1095                                 goto finish;
1096                         }
1097
1098                         m = dbus_message_new_method_call(
1099                                         "org.freedesktop.login1",
1100                                         "/org/freedesktop/login1",
1101                                         "org.freedesktop.login1.Manager",
1102                                         "GetUser");
1103                         if (!m) {
1104                                 log_error("Could not allocate message.");
1105                                 ret = -ENOMEM;
1106                                 goto finish;
1107                         }
1108
1109                         u = (uint32_t) uid;
1110                         if (!dbus_message_append_args(m,
1111                                                       DBUS_TYPE_UINT32, &u,
1112                                                       DBUS_TYPE_INVALID)) {
1113                                 log_error("Could not append arguments to message.");
1114                                 ret = -ENOMEM;
1115                                 goto finish;
1116                         }
1117                 } else {
1118
1119                         m = dbus_message_new_method_call(
1120                                         "org.freedesktop.login1",
1121                                         "/org/freedesktop/login1",
1122                                         "org.freedesktop.login1.Manager",
1123                                         "GetSeat");
1124                         if (!m) {
1125                                 log_error("Could not allocate message.");
1126                                 ret = -ENOMEM;
1127                                 goto finish;
1128                         }
1129
1130                         if (!dbus_message_append_args(m,
1131                                                       DBUS_TYPE_STRING, &args[i],
1132                                                       DBUS_TYPE_INVALID)) {
1133                                 log_error("Could not append arguments to message.");
1134                                 ret = -ENOMEM;
1135                                 goto finish;
1136                         }
1137                 }
1138
1139                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1140                 if (!reply) {
1141                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1142                         ret = -EIO;
1143                         goto finish;
1144                 }
1145
1146                 if (!dbus_message_get_args(reply, &error,
1147                                            DBUS_TYPE_OBJECT_PATH, &path,
1148                                            DBUS_TYPE_INVALID)) {
1149                         log_error("Failed to parse reply: %s", bus_error_message(&error));
1150                         ret = -EIO;
1151                         goto finish;
1152                 }
1153
1154                 r = show_one(args[0], bus, path, show_properties, &new_line);
1155                 if (r != 0)
1156                         ret = r;
1157
1158                 dbus_message_unref(m);
1159                 dbus_message_unref(reply);
1160                 m = reply = NULL;
1161         }
1162
1163 finish:
1164         if (m)
1165                 dbus_message_unref(m);
1166
1167         if (reply)
1168                 dbus_message_unref(reply);
1169
1170         dbus_error_free(&error);
1171
1172         return ret;
1173 }
1174
1175 static int activate(DBusConnection *bus, char **args, unsigned n) {
1176         DBusMessage *m = NULL;
1177         int ret = 0;
1178         DBusError error;
1179         unsigned i;
1180
1181         assert(bus);
1182         assert(args);
1183
1184         dbus_error_init(&error);
1185
1186         for (i = 1; i < n; i++) {
1187                 DBusMessage *reply;
1188
1189                 m = dbus_message_new_method_call(
1190                                 "org.freedesktop.login1",
1191                                 "/org/freedesktop/login1",
1192                                 "org.freedesktop.login1.Manager",
1193                                 streq(args[0], "lock-session")      ? "LockSession" :
1194                                 streq(args[0], "unlock-session")    ? "UnlockSession" :
1195                                 streq(args[0], "terminate-session") ? "TerminateSession" :
1196                                                                       "ActivateSession");
1197                 if (!m) {
1198                         log_error("Could not allocate message.");
1199                         ret = -ENOMEM;
1200                         goto finish;
1201                 }
1202
1203                 if (!dbus_message_append_args(m,
1204                                               DBUS_TYPE_STRING, &args[i],
1205                                               DBUS_TYPE_INVALID)) {
1206                         log_error("Could not append arguments to message.");
1207                         ret = -ENOMEM;
1208                         goto finish;
1209                 }
1210
1211                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1212                 if (!reply) {
1213                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1214                         ret = -EIO;
1215                         goto finish;
1216                 }
1217
1218                 dbus_message_unref(m);
1219                 dbus_message_unref(reply);
1220                 m = reply = NULL;
1221         }
1222
1223 finish:
1224         if (m)
1225                 dbus_message_unref(m);
1226
1227         dbus_error_free(&error);
1228
1229         return ret;
1230 }
1231
1232 static int kill_session(DBusConnection *bus, char **args, unsigned n) {
1233         DBusMessage *m = NULL;
1234         int ret = 0;
1235         DBusError error;
1236         unsigned i;
1237
1238         assert(bus);
1239         assert(args);
1240
1241         dbus_error_init(&error);
1242
1243         if (!arg_kill_who)
1244                 arg_kill_who = "all";
1245
1246         for (i = 1; i < n; i++) {
1247                 DBusMessage *reply;
1248
1249                 m = dbus_message_new_method_call(
1250                                 "org.freedesktop.login1",
1251                                 "/org/freedesktop/login1",
1252                                 "org.freedesktop.login1.Manager",
1253                                 "KillSession");
1254                 if (!m) {
1255                         log_error("Could not allocate message.");
1256                         ret = -ENOMEM;
1257                         goto finish;
1258                 }
1259
1260                 if (!dbus_message_append_args(m,
1261                                               DBUS_TYPE_STRING, &args[i],
1262                                               DBUS_TYPE_STRING, &arg_kill_who,
1263                                               DBUS_TYPE_INT32, arg_signal,
1264                                               DBUS_TYPE_INVALID)) {
1265                         log_error("Could not append arguments to message.");
1266                         ret = -ENOMEM;
1267                         goto finish;
1268                 }
1269
1270                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1271                 if (!reply) {
1272                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1273                         ret = -EIO;
1274                         goto finish;
1275                 }
1276
1277                 dbus_message_unref(m);
1278                 dbus_message_unref(reply);
1279                 m = reply = NULL;
1280         }
1281
1282 finish:
1283         if (m)
1284                 dbus_message_unref(m);
1285
1286         dbus_error_free(&error);
1287
1288         return ret;
1289 }
1290
1291 static int enable_linger(DBusConnection *bus, char **args, unsigned n) {
1292         DBusMessage *m = NULL;
1293         int ret = 0;
1294         DBusError error;
1295         unsigned i;
1296         dbus_bool_t b, interactive = true;
1297
1298         assert(bus);
1299         assert(args);
1300
1301         dbus_error_init(&error);
1302
1303         polkit_agent_open_if_enabled();
1304
1305         b = streq(args[0], "enable-linger");
1306
1307         for (i = 1; i < n; i++) {
1308                 DBusMessage *reply;
1309                 uint32_t u;
1310                 uid_t uid;
1311
1312                 m = dbus_message_new_method_call(
1313                                 "org.freedesktop.login1",
1314                                 "/org/freedesktop/login1",
1315                                 "org.freedesktop.login1.Manager",
1316                                 "SetUserLinger");
1317                 if (!m) {
1318                         log_error("Could not allocate message.");
1319                         ret = -ENOMEM;
1320                         goto finish;
1321                 }
1322
1323                 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1324                 if (ret < 0) {
1325                         log_error("Failed to resolve user %s: %s", args[i], strerror(-ret));
1326                         goto finish;
1327                 }
1328
1329                 u = (uint32_t) uid;
1330                 if (!dbus_message_append_args(m,
1331                                               DBUS_TYPE_UINT32, &u,
1332                                               DBUS_TYPE_BOOLEAN, &b,
1333                                               DBUS_TYPE_BOOLEAN, &interactive,
1334                                               DBUS_TYPE_INVALID)) {
1335                         log_error("Could not append arguments to message.");
1336                         ret = -ENOMEM;
1337                         goto finish;
1338                 }
1339
1340                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1341                 if (!reply) {
1342                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1343                         ret = -EIO;
1344                         goto finish;
1345                 }
1346
1347                 dbus_message_unref(m);
1348                 dbus_message_unref(reply);
1349                 m = reply = NULL;
1350         }
1351
1352         ret = 0;
1353
1354 finish:
1355         if (m)
1356                 dbus_message_unref(m);
1357
1358         dbus_error_free(&error);
1359
1360         return ret;
1361 }
1362
1363 static int terminate_user(DBusConnection *bus, char **args, unsigned n) {
1364         DBusMessage *m = NULL;
1365         int ret = 0;
1366         DBusError error;
1367         unsigned i;
1368
1369         assert(bus);
1370         assert(args);
1371
1372         dbus_error_init(&error);
1373
1374         for (i = 1; i < n; i++) {
1375                 uint32_t u;
1376                 uid_t uid;
1377                 DBusMessage *reply;
1378
1379                 m = dbus_message_new_method_call(
1380                                 "org.freedesktop.login1",
1381                                 "/org/freedesktop/login1",
1382                                 "org.freedesktop.login1.Manager",
1383                                 "TerminateUser");
1384                 if (!m) {
1385                         log_error("Could not allocate message.");
1386                         ret = -ENOMEM;
1387                         goto finish;
1388                 }
1389
1390                 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1391                 if (ret < 0) {
1392                         log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1393                         goto finish;
1394                 }
1395
1396                 u = (uint32_t) uid;
1397                 if (!dbus_message_append_args(m,
1398                                               DBUS_TYPE_UINT32, &u,
1399                                               DBUS_TYPE_INVALID)) {
1400                         log_error("Could not append arguments to message.");
1401                         ret = -ENOMEM;
1402                         goto finish;
1403                 }
1404
1405                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1406                 if (!reply) {
1407                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1408                         ret = -EIO;
1409                         goto finish;
1410                 }
1411
1412                 dbus_message_unref(m);
1413                 dbus_message_unref(reply);
1414                 m = reply = NULL;
1415         }
1416
1417         ret = 0;
1418
1419 finish:
1420         if (m)
1421                 dbus_message_unref(m);
1422
1423         dbus_error_free(&error);
1424
1425         return ret;
1426 }
1427
1428 static int kill_user(DBusConnection *bus, char **args, unsigned n) {
1429         DBusMessage *m = NULL;
1430         int ret = 0;
1431         DBusError error;
1432         unsigned i;
1433
1434         assert(bus);
1435         assert(args);
1436
1437         dbus_error_init(&error);
1438
1439         if (!arg_kill_who)
1440                 arg_kill_who = "all";
1441
1442         for (i = 1; i < n; i++) {
1443                 DBusMessage *reply;
1444                 uid_t uid;
1445                 uint32_t u;
1446
1447                 m = dbus_message_new_method_call(
1448                                 "org.freedesktop.login1",
1449                                 "/org/freedesktop/login1",
1450                                 "org.freedesktop.login1.Manager",
1451                                 "KillUser");
1452                 if (!m) {
1453                         log_error("Could not allocate message.");
1454                         ret = -ENOMEM;
1455                         goto finish;
1456                 }
1457
1458                 ret = get_user_creds((const char**) (args+i), &uid, NULL, NULL);
1459                 if (ret < 0) {
1460                         log_error("Failed to look up user %s: %s", args[i], strerror(-ret));
1461                         goto finish;
1462                 }
1463
1464                 u = (uint32_t) uid;
1465                 if (!dbus_message_append_args(m,
1466                                               DBUS_TYPE_UINT32, &u,
1467                                               DBUS_TYPE_INT32, arg_signal,
1468                                               DBUS_TYPE_INVALID)) {
1469                         log_error("Could not append arguments to message.");
1470                         ret = -ENOMEM;
1471                         goto finish;
1472                 }
1473
1474                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1475                 if (!reply) {
1476                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1477                         ret = -EIO;
1478                         goto finish;
1479                 }
1480
1481                 dbus_message_unref(m);
1482                 dbus_message_unref(reply);
1483                 m = reply = NULL;
1484         }
1485
1486         ret = 0;
1487
1488 finish:
1489         if (m)
1490                 dbus_message_unref(m);
1491
1492         dbus_error_free(&error);
1493
1494         return ret;
1495 }
1496
1497 static int attach(DBusConnection *bus, char **args, unsigned n) {
1498         DBusMessage *m = NULL;
1499         int ret = 0;
1500         DBusError error;
1501         unsigned i;
1502         dbus_bool_t interactive = true;
1503
1504         assert(bus);
1505         assert(args);
1506
1507         dbus_error_init(&error);
1508
1509         polkit_agent_open_if_enabled();
1510
1511         for (i = 2; i < n; i++) {
1512                 DBusMessage *reply;
1513
1514                 m = dbus_message_new_method_call(
1515                                 "org.freedesktop.login1",
1516                                 "/org/freedesktop/login1",
1517                                 "org.freedesktop.login1.Manager",
1518                                 "AttachDevice");
1519                 if (!m) {
1520                         log_error("Could not allocate message.");
1521                         ret = -ENOMEM;
1522                         goto finish;
1523                 }
1524
1525                 if (!dbus_message_append_args(m,
1526                                               DBUS_TYPE_STRING, &args[1],
1527                                               DBUS_TYPE_STRING, &args[i],
1528                                               DBUS_TYPE_BOOLEAN, &interactive,
1529                                               DBUS_TYPE_INVALID)) {
1530                         log_error("Could not append arguments to message.");
1531                         ret = -ENOMEM;
1532                         goto finish;
1533                 }
1534
1535                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1536                 if (!reply) {
1537                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1538                         ret = -EIO;
1539                         goto finish;
1540                 }
1541
1542                 dbus_message_unref(m);
1543                 dbus_message_unref(reply);
1544                 m = reply = NULL;
1545         }
1546
1547 finish:
1548         if (m)
1549                 dbus_message_unref(m);
1550
1551         dbus_error_free(&error);
1552
1553         return ret;
1554 }
1555
1556 static int flush_devices(DBusConnection *bus, char **args, unsigned n) {
1557         DBusMessage *m = NULL, *reply = NULL;
1558         int ret = 0;
1559         DBusError error;
1560         dbus_bool_t interactive = true;
1561
1562         assert(bus);
1563         assert(args);
1564
1565         dbus_error_init(&error);
1566
1567         polkit_agent_open_if_enabled();
1568
1569         m = dbus_message_new_method_call(
1570                         "org.freedesktop.login1",
1571                         "/org/freedesktop/login1",
1572                         "org.freedesktop.login1.Manager",
1573                         "FlushDevices");
1574         if (!m) {
1575                 log_error("Could not allocate message.");
1576                 ret = -ENOMEM;
1577                 goto finish;
1578         }
1579
1580         if (!dbus_message_append_args(m,
1581                                       DBUS_TYPE_BOOLEAN, &interactive,
1582                                       DBUS_TYPE_INVALID)) {
1583                 log_error("Could not append arguments to message.");
1584                 ret = -ENOMEM;
1585                 goto finish;
1586         }
1587
1588         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1589         if (!reply) {
1590                 log_error("Failed to issue method call: %s", bus_error_message(&error));
1591                 ret = -EIO;
1592                 goto finish;
1593         }
1594
1595 finish:
1596         if (m)
1597                 dbus_message_unref(m);
1598
1599         if (reply)
1600                 dbus_message_unref(reply);
1601
1602         dbus_error_free(&error);
1603
1604         return ret;
1605 }
1606
1607 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1608         DBusMessage *m = NULL;
1609         int ret = 0;
1610         DBusError error;
1611         unsigned i;
1612
1613         assert(bus);
1614         assert(args);
1615
1616         dbus_error_init(&error);
1617
1618         for (i = 1; i < n; i++) {
1619                 DBusMessage *reply;
1620
1621                 m = dbus_message_new_method_call(
1622                                 "org.freedesktop.login1",
1623                                 "/org/freedesktop/login1",
1624                                 "org.freedesktop.login1.Manager",
1625                                 "TerminateSeat");
1626                 if (!m) {
1627                         log_error("Could not allocate message.");
1628                         ret = -ENOMEM;
1629                         goto finish;
1630                 }
1631
1632                 if (!dbus_message_append_args(m,
1633                                               DBUS_TYPE_STRING, &args[i],
1634                                               DBUS_TYPE_INVALID)) {
1635                         log_error("Could not append arguments to message.");
1636                         ret = -ENOMEM;
1637                         goto finish;
1638                 }
1639
1640                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1641                 if (!reply) {
1642                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1643                         ret = -EIO;
1644                         goto finish;
1645                 }
1646
1647                 dbus_message_unref(m);
1648                 dbus_message_unref(reply);
1649                 m = reply = NULL;
1650         }
1651
1652 finish:
1653         if (m)
1654                 dbus_message_unref(m);
1655
1656         dbus_error_free(&error);
1657
1658         return ret;
1659 }
1660
1661 static int help(void) {
1662
1663         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1664                "Send control commands to or query the login manager.\n\n"
1665                "  -h --help           Show this help\n"
1666                "     --version        Show package version\n"
1667                "  -p --property=NAME  Show only properties by this name\n"
1668                "  -a --all            Show all properties, including empty ones\n"
1669                "     --kill-who=WHO   Who to send signal to\n"
1670                "  -s --signal=SIGNAL  Which signal to send\n"
1671                "  -H --host=[USER@]HOST\n"
1672                "                      Show information for remote host\n"
1673                "  -P --privileged     Acquire privileges before execution\n"
1674                "     --no-pager       Do not pipe output into a pager\n\n"
1675                "Commands:\n"
1676                "  list-sessions                   List sessions\n"
1677                "  session-status [ID...]          Show session status\n"
1678                "  show-session [ID...]            Show properties of one or more sessions\n"
1679                "  activate [ID]                   Activate a session\n"
1680                "  lock-session [ID...]            Screen lock one or more sessions\n"
1681                "  unlock-session [ID...]          Screen unlock one or more sessions\n"
1682                "  terminate-session [ID...]       Terminate one or more sessions\n"
1683                "  kill-session [ID...]            Send signal to processes of a session\n"
1684                "  list-users                      List users\n"
1685                "  user-status [USER...]           Show user status\n"
1686                "  show-user [USER...]             Show properties of one or more users\n"
1687                "  enable-linger [USER...]         Enable linger state of one or more users\n"
1688                "  disable-linger [USER...]        Disable linger state of one or more users\n"
1689                "  terminate-user [USER...]        Terminate all sessions of one or more users\n"
1690                "  kill-user [USER...]             Send signal to processes of a user\n"
1691                "  list-seats                      List seats\n"
1692                "  seat-status [NAME...]           Show seat status\n"
1693                "  show-seat [NAME...]             Show properties of one or more seats\n"
1694                "  attach [NAME] [DEVICE...]       Attach one or more devices to a seat\n"
1695                "  flush-devices                   Flush all device associations\n"
1696                "  terminate-seat [NAME...]        Terminate all sessions on one or more seats\n",
1697                program_invocation_short_name);
1698
1699         return 0;
1700 }
1701
1702 static int parse_argv(int argc, char *argv[]) {
1703
1704         enum {
1705                 ARG_VERSION = 0x100,
1706                 ARG_NO_PAGER,
1707                 ARG_KILL_WHO,
1708                 ARG_NO_ASK_PASSWORD
1709         };
1710
1711         static const struct option options[] = {
1712                 { "help",      no_argument,       NULL, 'h'           },
1713                 { "version",   no_argument,       NULL, ARG_VERSION   },
1714                 { "property",  required_argument, NULL, 'p'           },
1715                 { "all",       no_argument,       NULL, 'a'           },
1716                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
1717                 { "kill-who",  required_argument, NULL, ARG_KILL_WHO  },
1718                 { "signal",    required_argument, NULL, 's'           },
1719                 { "host",      required_argument, NULL, 'H'           },
1720                 { "privileged",no_argument,       NULL, 'P'           },
1721                 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1722                 { NULL,        0,                 NULL, 0             }
1723         };
1724
1725         int c;
1726
1727         assert(argc >= 0);
1728         assert(argv);
1729
1730         while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1731
1732                 switch (c) {
1733
1734                 case 'h':
1735                         help();
1736                         return 0;
1737
1738                 case ARG_VERSION:
1739                         puts(PACKAGE_STRING);
1740                         puts(DISTRIBUTION);
1741                         puts(SYSTEMD_FEATURES);
1742                         return 0;
1743
1744                 case 'p': {
1745                         char **l;
1746
1747                         l = strv_append(arg_property, optarg);
1748                         if (!l)
1749                                 return -ENOMEM;
1750
1751                         strv_free(arg_property);
1752                         arg_property = l;
1753
1754                         /* If the user asked for a particular
1755                          * property, show it to him, even if it is
1756                          * empty. */
1757                         arg_all = true;
1758                         break;
1759                 }
1760
1761                 case 'a':
1762                         arg_all = true;
1763                         break;
1764
1765                 case ARG_NO_PAGER:
1766                         arg_no_pager = true;
1767                         break;
1768
1769                 case ARG_NO_ASK_PASSWORD:
1770                         arg_ask_password = false;
1771
1772                 case ARG_KILL_WHO:
1773                         arg_kill_who = optarg;
1774                         break;
1775
1776                 case 's':
1777                         arg_signal = signal_from_string_try_harder(optarg);
1778                         if (arg_signal < 0) {
1779                                 log_error("Failed to parse signal string %s.", optarg);
1780                                 return -EINVAL;
1781                         }
1782                         break;
1783
1784                 case 'P':
1785                         arg_transport = TRANSPORT_POLKIT;
1786                         break;
1787
1788                 case 'H':
1789                         arg_transport = TRANSPORT_SSH;
1790                         arg_host = optarg;
1791                         break;
1792
1793                 case '?':
1794                         return -EINVAL;
1795
1796                 default:
1797                         log_error("Unknown option code %c", c);
1798                         return -EINVAL;
1799                 }
1800         }
1801
1802         return 1;
1803 }
1804
1805 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1806
1807         static const struct {
1808                 const char* verb;
1809                 const enum {
1810                         MORE,
1811                         LESS,
1812                         EQUAL
1813                 } argc_cmp;
1814                 const int argc;
1815                 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1816         } verbs[] = {
1817                 { "list-sessions",         LESS,   1, list_sessions    },
1818                 { "session-status",        MORE,   2, show             },
1819                 { "show-session",          MORE,   1, show             },
1820                 { "activate",              EQUAL,  2, activate         },
1821                 { "lock-session",          MORE,   2, activate         },
1822                 { "unlock-session",        MORE,   2, activate         },
1823                 { "terminate-session",     MORE,   2, activate         },
1824                 { "kill-session",          MORE,   2, kill_session     },
1825                 { "list-users",            EQUAL,  1, list_users       },
1826                 { "user-status",           MORE,   2, show             },
1827                 { "show-user",             MORE,   1, show             },
1828                 { "enable-linger",         MORE,   2, enable_linger    },
1829                 { "disable-linger",        MORE,   2, enable_linger    },
1830                 { "terminate-user",        MORE,   2, terminate_user   },
1831                 { "kill-user",             MORE,   2, kill_user        },
1832                 { "list-seats",            EQUAL,  1, list_seats       },
1833                 { "seat-status",           MORE,   2, show             },
1834                 { "show-seat",             MORE,   1, show             },
1835                 { "attach",                MORE,   3, attach           },
1836                 { "flush-devices",         EQUAL,  1, flush_devices    },
1837                 { "terminate-seat",        MORE,   2, terminate_seat   },
1838         };
1839
1840         int left;
1841         unsigned i;
1842
1843         assert(argc >= 0);
1844         assert(argv);
1845         assert(error);
1846
1847         left = argc - optind;
1848
1849         if (left <= 0)
1850                 /* Special rule: no arguments means "list-sessions" */
1851                 i = 0;
1852         else {
1853                 if (streq(argv[optind], "help")) {
1854                         help();
1855                         return 0;
1856                 }
1857
1858                 for (i = 0; i < ELEMENTSOF(verbs); i++)
1859                         if (streq(argv[optind], verbs[i].verb))
1860                                 break;
1861
1862                 if (i >= ELEMENTSOF(verbs)) {
1863                         log_error("Unknown operation %s", argv[optind]);
1864                         return -EINVAL;
1865                 }
1866         }
1867
1868         switch (verbs[i].argc_cmp) {
1869
1870         case EQUAL:
1871                 if (left != verbs[i].argc) {
1872                         log_error("Invalid number of arguments.");
1873                         return -EINVAL;
1874                 }
1875
1876                 break;
1877
1878         case MORE:
1879                 if (left < verbs[i].argc) {
1880                         log_error("Too few arguments.");
1881                         return -EINVAL;
1882                 }
1883
1884                 break;
1885
1886         case LESS:
1887                 if (left > verbs[i].argc) {
1888                         log_error("Too many arguments.");
1889                         return -EINVAL;
1890                 }
1891
1892                 break;
1893
1894         default:
1895                 assert_not_reached("Unknown comparison operator.");
1896         }
1897
1898         if (!bus) {
1899                 log_error("Failed to get D-Bus connection: %s", error->message);
1900                 return -EIO;
1901         }
1902
1903         return verbs[i].dispatch(bus, argv + optind, left);
1904 }
1905
1906 int main(int argc, char*argv[]) {
1907         int r, retval = EXIT_FAILURE;
1908         DBusConnection *bus = NULL;
1909         DBusError error;
1910
1911         dbus_error_init(&error);
1912
1913         log_parse_environment();
1914         log_open();
1915
1916         r = parse_argv(argc, argv);
1917         if (r < 0)
1918                 goto finish;
1919         else if (r == 0) {
1920                 retval = EXIT_SUCCESS;
1921                 goto finish;
1922         }
1923
1924         if (arg_transport == TRANSPORT_NORMAL)
1925                 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1926         else if (arg_transport == TRANSPORT_POLKIT)
1927                 bus_connect_system_polkit(&bus, &error);
1928         else if (arg_transport == TRANSPORT_SSH)
1929                 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1930         else
1931                 assert_not_reached("Uh, invalid transport...");
1932
1933         r = loginctl_main(bus, argc, argv, &error);
1934         retval = r < 0 ? EXIT_FAILURE : r;
1935
1936 finish:
1937         if (bus) {
1938                 dbus_connection_flush(bus);
1939                 dbus_connection_close(bus);
1940                 dbus_connection_unref(bus);
1941         }
1942
1943         dbus_error_free(&error);
1944         dbus_shutdown();
1945
1946         strv_free(arg_property);
1947
1948         pager_close();
1949
1950         return retval;
1951 }