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