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