chiark / gitweb /
18f56f0124a795fbcc429b3488968050b6d66a3e
[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 *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 *default_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 *default_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->default_control_group) {
465                 unsigned c;
466
467                 printf("\t  CGroup: %s\n", i->default_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_and_extra_by_spec(i->default_control_group, "\t\t  ", c, false, arg_all, &i->leader, i->leader > 0 ? 1 : 0);
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->default_control_group) {
517                 unsigned c;
518
519                 printf("\t  CGroup: %s\n", i->default_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->default_control_group, "\t\t  ", c, false, arg_all);
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, "DefaultControlGroup"))
585                                 i->default_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, "DefaultControlGroup"))
684                                 i->default_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 }