chiark / gitweb /
logind: add new loginctl lock-sessions command
[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         const char *state;
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         if (i->state)
462                 printf("\t   State: %s\n", i->state);
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                         else if (streq(name, "State"))
601                                 i->state = s;
602                 }
603                 break;
604         }
605
606         case DBUS_TYPE_UINT32: {
607                 uint32_t u;
608
609                 dbus_message_iter_get_basic(iter, &u);
610
611                 if (streq(name, "VTNr"))
612                         i->vtnr = (int) u;
613                 else if (streq(name, "Leader"))
614                         i->leader = (pid_t) u;
615
616                 break;
617         }
618
619         case DBUS_TYPE_BOOLEAN: {
620                 dbus_bool_t b;
621
622                 dbus_message_iter_get_basic(iter, &b);
623
624                 if (streq(name, "Remote"))
625                         i->remote = 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, 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, 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, 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, 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 lock_sessions(DBusConnection *bus, char **args, unsigned n) {
1608         DBusMessage *m = NULL, *reply = NULL;
1609         int ret = 0;
1610         DBusError error;
1611
1612         assert(bus);
1613         assert(args);
1614
1615         dbus_error_init(&error);
1616
1617         polkit_agent_open_if_enabled();
1618
1619         m = dbus_message_new_method_call(
1620                         "org.freedesktop.login1",
1621                         "/org/freedesktop/login1",
1622                         "org.freedesktop.login1.Manager",
1623                         "LockSessions");
1624         if (!m) {
1625                 log_error("Could not allocate message.");
1626                 ret = -ENOMEM;
1627                 goto finish;
1628         }
1629
1630         reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1631         if (!reply) {
1632                 log_error("Failed to issue method call: %s", bus_error_message(&error));
1633                 ret = -EIO;
1634                 goto finish;
1635         }
1636
1637 finish:
1638         if (m)
1639                 dbus_message_unref(m);
1640
1641         if (reply)
1642                 dbus_message_unref(reply);
1643
1644         dbus_error_free(&error);
1645
1646         return ret;
1647 }
1648
1649 static int terminate_seat(DBusConnection *bus, char **args, unsigned n) {
1650         DBusMessage *m = NULL;
1651         int ret = 0;
1652         DBusError error;
1653         unsigned i;
1654
1655         assert(bus);
1656         assert(args);
1657
1658         dbus_error_init(&error);
1659
1660         for (i = 1; i < n; i++) {
1661                 DBusMessage *reply;
1662
1663                 m = dbus_message_new_method_call(
1664                                 "org.freedesktop.login1",
1665                                 "/org/freedesktop/login1",
1666                                 "org.freedesktop.login1.Manager",
1667                                 "TerminateSeat");
1668                 if (!m) {
1669                         log_error("Could not allocate message.");
1670                         ret = -ENOMEM;
1671                         goto finish;
1672                 }
1673
1674                 if (!dbus_message_append_args(m,
1675                                               DBUS_TYPE_STRING, &args[i],
1676                                               DBUS_TYPE_INVALID)) {
1677                         log_error("Could not append arguments to message.");
1678                         ret = -ENOMEM;
1679                         goto finish;
1680                 }
1681
1682                 reply = dbus_connection_send_with_reply_and_block(bus, m, -1, &error);
1683                 if (!reply) {
1684                         log_error("Failed to issue method call: %s", bus_error_message(&error));
1685                         ret = -EIO;
1686                         goto finish;
1687                 }
1688
1689                 dbus_message_unref(m);
1690                 dbus_message_unref(reply);
1691                 m = reply = NULL;
1692         }
1693
1694 finish:
1695         if (m)
1696                 dbus_message_unref(m);
1697
1698         dbus_error_free(&error);
1699
1700         return ret;
1701 }
1702
1703 static int help(void) {
1704
1705         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1706                "Send control commands to or query the login manager.\n\n"
1707                "  -h --help           Show this help\n"
1708                "     --version        Show package version\n"
1709                "  -p --property=NAME  Show only properties by this name\n"
1710                "  -a --all            Show all properties, including empty ones\n"
1711                "     --kill-who=WHO   Who to send signal to\n"
1712                "  -s --signal=SIGNAL  Which signal to send\n"
1713                "  -H --host=[USER@]HOST\n"
1714                "                      Show information for remote host\n"
1715                "  -P --privileged     Acquire privileges before execution\n"
1716                "     --no-pager       Do not pipe output into a pager\n\n"
1717                "Commands:\n"
1718                "  list-sessions                   List sessions\n"
1719                "  session-status [ID...]          Show session status\n"
1720                "  show-session [ID...]            Show properties of one or more sessions\n"
1721                "  activate [ID]                   Activate a session\n"
1722                "  lock-session [ID...]            Screen lock one or more sessions\n"
1723                "  unlock-session [ID...]          Screen unlock one or more sessions\n"
1724                "  lock-sessions                   Screen lock all current sessions\n"
1725                "  terminate-session [ID...]       Terminate one or more sessions\n"
1726                "  kill-session [ID...]            Send signal to processes of a session\n"
1727                "  list-users                      List users\n"
1728                "  user-status [USER...]           Show user status\n"
1729                "  show-user [USER...]             Show properties of one or more users\n"
1730                "  enable-linger [USER...]         Enable linger state of one or more users\n"
1731                "  disable-linger [USER...]        Disable linger state of one or more users\n"
1732                "  terminate-user [USER...]        Terminate all sessions of one or more users\n"
1733                "  kill-user [USER...]             Send signal to processes of a user\n"
1734                "  list-seats                      List seats\n"
1735                "  seat-status [NAME...]           Show seat status\n"
1736                "  show-seat [NAME...]             Show properties of one or more seats\n"
1737                "  attach [NAME] [DEVICE...]       Attach one or more devices to a seat\n"
1738                "  flush-devices                   Flush all device associations\n"
1739                "  terminate-seat [NAME...]        Terminate all sessions on one or more seats\n",
1740                program_invocation_short_name);
1741
1742         return 0;
1743 }
1744
1745 static int parse_argv(int argc, char *argv[]) {
1746
1747         enum {
1748                 ARG_VERSION = 0x100,
1749                 ARG_NO_PAGER,
1750                 ARG_KILL_WHO,
1751                 ARG_NO_ASK_PASSWORD
1752         };
1753
1754         static const struct option options[] = {
1755                 { "help",      no_argument,       NULL, 'h'           },
1756                 { "version",   no_argument,       NULL, ARG_VERSION   },
1757                 { "property",  required_argument, NULL, 'p'           },
1758                 { "all",       no_argument,       NULL, 'a'           },
1759                 { "no-pager",  no_argument,       NULL, ARG_NO_PAGER  },
1760                 { "kill-who",  required_argument, NULL, ARG_KILL_WHO  },
1761                 { "signal",    required_argument, NULL, 's'           },
1762                 { "host",      required_argument, NULL, 'H'           },
1763                 { "privileged",no_argument,       NULL, 'P'           },
1764                 { "no-ask-password", no_argument, NULL, ARG_NO_ASK_PASSWORD },
1765                 { NULL,        0,                 NULL, 0             }
1766         };
1767
1768         int c;
1769
1770         assert(argc >= 0);
1771         assert(argv);
1772
1773         while ((c = getopt_long(argc, argv, "hp:as:H:P", options, NULL)) >= 0) {
1774
1775                 switch (c) {
1776
1777                 case 'h':
1778                         help();
1779                         return 0;
1780
1781                 case ARG_VERSION:
1782                         puts(PACKAGE_STRING);
1783                         puts(DISTRIBUTION);
1784                         puts(SYSTEMD_FEATURES);
1785                         return 0;
1786
1787                 case 'p': {
1788                         char **l;
1789
1790                         l = strv_append(arg_property, optarg);
1791                         if (!l)
1792                                 return -ENOMEM;
1793
1794                         strv_free(arg_property);
1795                         arg_property = l;
1796
1797                         /* If the user asked for a particular
1798                          * property, show it to him, even if it is
1799                          * empty. */
1800                         arg_all = true;
1801                         break;
1802                 }
1803
1804                 case 'a':
1805                         arg_all = true;
1806                         break;
1807
1808                 case ARG_NO_PAGER:
1809                         arg_no_pager = true;
1810                         break;
1811
1812                 case ARG_NO_ASK_PASSWORD:
1813                         arg_ask_password = false;
1814
1815                 case ARG_KILL_WHO:
1816                         arg_kill_who = optarg;
1817                         break;
1818
1819                 case 's':
1820                         arg_signal = signal_from_string_try_harder(optarg);
1821                         if (arg_signal < 0) {
1822                                 log_error("Failed to parse signal string %s.", optarg);
1823                                 return -EINVAL;
1824                         }
1825                         break;
1826
1827                 case 'P':
1828                         arg_transport = TRANSPORT_POLKIT;
1829                         break;
1830
1831                 case 'H':
1832                         arg_transport = TRANSPORT_SSH;
1833                         arg_host = optarg;
1834                         break;
1835
1836                 case '?':
1837                         return -EINVAL;
1838
1839                 default:
1840                         log_error("Unknown option code %c", c);
1841                         return -EINVAL;
1842                 }
1843         }
1844
1845         return 1;
1846 }
1847
1848 static int loginctl_main(DBusConnection *bus, int argc, char *argv[], DBusError *error) {
1849
1850         static const struct {
1851                 const char* verb;
1852                 const enum {
1853                         MORE,
1854                         LESS,
1855                         EQUAL
1856                 } argc_cmp;
1857                 const int argc;
1858                 int (* const dispatch)(DBusConnection *bus, char **args, unsigned n);
1859         } verbs[] = {
1860                 { "list-sessions",         LESS,   1, list_sessions    },
1861                 { "session-status",        MORE,   2, show             },
1862                 { "show-session",          MORE,   1, show             },
1863                 { "activate",              EQUAL,  2, activate         },
1864                 { "lock-session",          MORE,   2, activate         },
1865                 { "unlock-session",        MORE,   2, activate         },
1866                 { "lock-sessions",         EQUAL,  1, lock_sessions    },
1867                 { "terminate-session",     MORE,   2, activate         },
1868                 { "kill-session",          MORE,   2, kill_session     },
1869                 { "list-users",            EQUAL,  1, list_users       },
1870                 { "user-status",           MORE,   2, show             },
1871                 { "show-user",             MORE,   1, show             },
1872                 { "enable-linger",         MORE,   2, enable_linger    },
1873                 { "disable-linger",        MORE,   2, enable_linger    },
1874                 { "terminate-user",        MORE,   2, terminate_user   },
1875                 { "kill-user",             MORE,   2, kill_user        },
1876                 { "list-seats",            EQUAL,  1, list_seats       },
1877                 { "seat-status",           MORE,   2, show             },
1878                 { "show-seat",             MORE,   1, show             },
1879                 { "attach",                MORE,   3, attach           },
1880                 { "flush-devices",         EQUAL,  1, flush_devices    },
1881                 { "terminate-seat",        MORE,   2, terminate_seat   },
1882         };
1883
1884         int left;
1885         unsigned i;
1886
1887         assert(argc >= 0);
1888         assert(argv);
1889         assert(error);
1890
1891         left = argc - optind;
1892
1893         if (left <= 0)
1894                 /* Special rule: no arguments means "list-sessions" */
1895                 i = 0;
1896         else {
1897                 if (streq(argv[optind], "help")) {
1898                         help();
1899                         return 0;
1900                 }
1901
1902                 for (i = 0; i < ELEMENTSOF(verbs); i++)
1903                         if (streq(argv[optind], verbs[i].verb))
1904                                 break;
1905
1906                 if (i >= ELEMENTSOF(verbs)) {
1907                         log_error("Unknown operation %s", argv[optind]);
1908                         return -EINVAL;
1909                 }
1910         }
1911
1912         switch (verbs[i].argc_cmp) {
1913
1914         case EQUAL:
1915                 if (left != verbs[i].argc) {
1916                         log_error("Invalid number of arguments.");
1917                         return -EINVAL;
1918                 }
1919
1920                 break;
1921
1922         case MORE:
1923                 if (left < verbs[i].argc) {
1924                         log_error("Too few arguments.");
1925                         return -EINVAL;
1926                 }
1927
1928                 break;
1929
1930         case LESS:
1931                 if (left > verbs[i].argc) {
1932                         log_error("Too many arguments.");
1933                         return -EINVAL;
1934                 }
1935
1936                 break;
1937
1938         default:
1939                 assert_not_reached("Unknown comparison operator.");
1940         }
1941
1942         if (!bus) {
1943                 log_error("Failed to get D-Bus connection: %s", error->message);
1944                 return -EIO;
1945         }
1946
1947         return verbs[i].dispatch(bus, argv + optind, left);
1948 }
1949
1950 int main(int argc, char*argv[]) {
1951         int r, retval = EXIT_FAILURE;
1952         DBusConnection *bus = NULL;
1953         DBusError error;
1954
1955         dbus_error_init(&error);
1956
1957         log_parse_environment();
1958         log_open();
1959
1960         r = parse_argv(argc, argv);
1961         if (r < 0)
1962                 goto finish;
1963         else if (r == 0) {
1964                 retval = EXIT_SUCCESS;
1965                 goto finish;
1966         }
1967
1968         if (arg_transport == TRANSPORT_NORMAL)
1969                 bus = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
1970         else if (arg_transport == TRANSPORT_POLKIT)
1971                 bus_connect_system_polkit(&bus, &error);
1972         else if (arg_transport == TRANSPORT_SSH)
1973                 bus_connect_system_ssh(NULL, arg_host, &bus, &error);
1974         else
1975                 assert_not_reached("Uh, invalid transport...");
1976
1977         r = loginctl_main(bus, argc, argv, &error);
1978         retval = r < 0 ? EXIT_FAILURE : r;
1979
1980 finish:
1981         if (bus) {
1982                 dbus_connection_flush(bus);
1983                 dbus_connection_close(bus);
1984                 dbus_connection_unref(bus);
1985         }
1986
1987         dbus_error_free(&error);
1988         dbus_shutdown();
1989
1990         strv_free(arg_property);
1991
1992         pager_close();
1993
1994         return retval;
1995 }