chiark / gitweb /
busctl: exit cleanly when the bus connection is severed
[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 <unistd.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <getopt.h>
26 #include <pwd.h>
27 #include <locale.h>
28
29 #include "sd-bus.h"
30 #include "bus-util.h"
31 #include "bus-error.h"
32 #include "log.h"
33 #include "util.h"
34 #include "macro.h"
35 #include "pager.h"
36 #include "build.h"
37 #include "strv.h"
38 #include "unit-name.h"
39 #include "sysfs-show.h"
40 #include "cgroup-show.h"
41 #include "cgroup-util.h"
42 #include "spawn-polkit-agent.h"
43
44 static char **arg_property = NULL;
45 static bool arg_all = false;
46 static bool arg_full = false;
47 static bool arg_no_pager = false;
48 static bool arg_legend = true;
49 static const char *arg_kill_who = NULL;
50 static int arg_signal = SIGTERM;
51 static BusTransport arg_transport = BUS_TRANSPORT_LOCAL;
52 static bool arg_ask_password = true;
53 static char *arg_host = NULL;
54
55 static void pager_open_if_enabled(void) {
56
57         if (arg_no_pager)
58                 return;
59
60         pager_open(false);
61 }
62
63 static void polkit_agent_open_if_enabled(void) {
64
65         /* Open the polkit agent as a child process if necessary */
66
67         if (!arg_ask_password)
68                 return;
69
70         if (arg_transport != BUS_TRANSPORT_LOCAL)
71                 return;
72
73         polkit_agent_open();
74 }
75
76 static int list_sessions(sd_bus *bus, char **args, unsigned n) {
77         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
78         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
79         const char *id, *user, *seat, *object;
80         unsigned k = 0;
81         uint32_t uid;
82         int r;
83
84         pager_open_if_enabled();
85
86         r = sd_bus_call_method(
87                         bus,
88                         "org.freedesktop.login1",
89                         "/org/freedesktop/login1",
90                         "org.freedesktop.login1.Manager",
91                         "ListSessions",
92                         &error, &reply,
93                         "");
94         if (r < 0) {
95                 log_error("Failed to list sessions: %s", bus_error_message(&error, r));
96                 return r;
97         }
98
99         r = sd_bus_message_enter_container(reply, 'a', "(susso)");
100         if (r < 0)
101                 return bus_log_parse_error(r);
102
103         if (arg_legend)
104                 printf("%10s %10s %-16s %-16s\n", "SESSION", "UID", "USER", "SEAT");
105
106         while ((r = sd_bus_message_read(reply, "(susso)", &id, &uid, &user, &seat, &object)) > 0) {
107                 printf("%10s %10u %-16s %-16s\n", id, (unsigned) uid, user, seat);
108                 k++;
109         }
110         if (r < 0)
111                 return bus_log_parse_error(r);
112
113         if (arg_legend)
114                 printf("\n%u sessions listed.\n", k);
115
116         return 0;
117 }
118
119 static int list_users(sd_bus *bus, char **args, unsigned n) {
120         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
121         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
122         const char *user, *object;
123         unsigned k = 0;
124         uint32_t uid;
125         int r;
126
127         pager_open_if_enabled();
128
129         r = sd_bus_call_method(
130                         bus,
131                         "org.freedesktop.login1",
132                         "/org/freedesktop/login1",
133                         "org.freedesktop.login1.Manager",
134                         "ListUsers",
135                         &error, &reply,
136                         "");
137         if (r < 0) {
138                 log_error("Failed to list users: %s", bus_error_message(&error, r));
139                 return r;
140         }
141
142         r = sd_bus_message_enter_container(reply, 'a', "(uso)");
143         if (r < 0)
144                 return bus_log_parse_error(r);
145
146         if (arg_legend)
147                 printf("%10s %-16s\n", "UID", "USER");
148
149         while ((r = sd_bus_message_read(reply, "(uso)", &uid, &user, &object)) > 0) {
150                 printf("%10u %-16s\n", (unsigned) uid, user);
151                 k++;
152         }
153         if (r < 0)
154                 return bus_log_parse_error(r);
155
156         if (arg_legend)
157                 printf("\n%u users listed.\n", k);
158
159         return 0;
160 }
161
162 static int list_seats(sd_bus *bus, char **args, unsigned n) {
163         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
164         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
165         const char *seat, *object;
166         unsigned k = 0;
167         int r;
168
169         pager_open_if_enabled();
170
171         r = sd_bus_call_method(
172                         bus,
173                         "org.freedesktop.login1",
174                         "/org/freedesktop/login1",
175                         "org.freedesktop.login1.Manager",
176                         "ListSeats",
177                         &error, &reply,
178                         "");
179         if (r < 0) {
180                 log_error("Failed to list seats: %s", bus_error_message(&error, r));
181                 return r;
182         }
183
184         r = sd_bus_message_enter_container(reply, 'a', "(so)");
185         if (r < 0)
186                 return bus_log_parse_error(r);
187
188         if (arg_legend)
189                 printf("%-16s\n", "SEAT");
190
191         while ((r = sd_bus_message_read(reply, "(so)", &seat, &object)) > 0) {
192                 printf("%-16s\n", seat);
193                 k++;
194         }
195         if (r < 0)
196                 return bus_log_parse_error(r);
197
198         if (arg_legend)
199                 printf("\n%u seats listed.\n", k);
200
201         return 0;
202 }
203
204 static int show_unit_cgroup(sd_bus *bus, const char *interface, const char *unit, pid_t leader) {
205         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
206         _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
207         _cleanup_free_ char *path = NULL;
208         const char *cgroup;
209         int r, output_flags;
210         unsigned c;
211
212         assert(bus);
213         assert(unit);
214
215         if (arg_transport != BUS_TRANSPORT_LOCAL)
216                 return 0;
217
218         path = unit_dbus_path_from_name(unit);
219         if (!path)
220                 return -ENOMEM;
221
222         r = sd_bus_get_property(
223                         bus,
224                         "org.freedesktop.systemd1",
225                         path,
226                         interface,
227                         "ControlGroup",
228                         &error, &reply, "s");
229         if (r < 0)
230                 return r;
231
232         r = sd_bus_message_read(reply, "s", &cgroup);
233         if (r < 0)
234                 return r;
235
236         if (isempty(cgroup))
237                 return 0;
238
239         if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup, false) != 0 && leader <= 0)
240                 return 0;
241
242         output_flags =
243                 arg_all * OUTPUT_SHOW_ALL |
244                 arg_full * OUTPUT_FULL_WIDTH;
245
246         c = columns();
247         if (c > 18)
248                 c -= 18;
249         else
250                 c = 0;
251
252         show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t  ", c, false, &leader, leader > 0, output_flags);
253         return 0;
254 }
255
256 typedef struct SessionStatusInfo {
257         const char *id;
258         uid_t uid;
259         const char *name;
260         usec_t timestamp;
261         unsigned int vtnr;
262         const char *seat;
263         const char *tty;
264         const char *display;
265         bool remote;
266         const char *remote_host;
267         const char *remote_user;
268         const char *service;
269         pid_t leader;
270         const char *type;
271         const char *class;
272         const char *state;
273         const char *scope;
274         const char *desktop;
275 } SessionStatusInfo;
276
277 typedef struct UserStatusInfo {
278         uid_t uid;
279         const char *name;
280         usec_t timestamp;
281         const char *state;
282         char **sessions;
283         const char *display;
284         const char *slice;
285 } UserStatusInfo;
286
287 typedef struct SeatStatusInfo {
288         const char *id;
289         const char *active_session;
290         char **sessions;
291 } SeatStatusInfo;
292
293 static int prop_map_first_of_struct(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
294         const char *contents;
295         int r;
296
297         r = sd_bus_message_peek_type(m, NULL, &contents);
298         if (r < 0)
299                 return r;
300
301         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_STRUCT, contents);
302         if (r < 0)
303                 return r;
304
305         if (contents[0] == 's' || contents[0] == 'o') {
306                 const char *s;
307                 char **p = (char **) userdata;
308
309                 r = sd_bus_message_read_basic(m, contents[0], &s);
310                 if (r < 0)
311                         return r;
312
313                 free(*p);
314                 *p = strdup(s);
315
316                 if (!*p)
317                         return -ENOMEM;
318         } else {
319                 r = sd_bus_message_read_basic(m, contents[0], userdata);
320                 if (r < 0)
321                         return r;
322         }
323
324         r = sd_bus_message_skip(m, contents+1);
325         if (r < 0)
326                 return r;
327
328         r = sd_bus_message_exit_container(m);
329         if (r < 0)
330                 return r;
331
332         return 0;
333 }
334
335 static int prop_map_sessions_strv(sd_bus *bus, const char *member, sd_bus_message *m, sd_bus_error *error, void *userdata) {
336         const char *name;
337         int r;
338
339         assert(bus);
340         assert(m);
341
342         r = sd_bus_message_enter_container(m, 'a', "(so)");
343         if (r < 0)
344                 return r;
345
346         while ((r = sd_bus_message_read(m, "(so)", &name, NULL)) > 0) {
347                 r = strv_extend(userdata, name);
348                 if (r < 0)
349                         return r;
350         }
351         if (r < 0)
352                 return r;
353
354         return sd_bus_message_exit_container(m);
355 }
356
357 static int print_session_status_info(sd_bus *bus, const char *path, bool *new_line) {
358
359         static const struct bus_properties_map map[]  = {
360                 { "Id",         "s", NULL, offsetof(SessionStatusInfo, id) },
361                 { "Name",       "s", NULL, offsetof(SessionStatusInfo, name) },
362                 { "TTY",        "s", NULL, offsetof(SessionStatusInfo, tty) },
363                 { "Display",    "s", NULL, offsetof(SessionStatusInfo, display) },
364                 { "RemoteHost", "s", NULL, offsetof(SessionStatusInfo, remote_host) },
365                 { "RemoteUser", "s", NULL, offsetof(SessionStatusInfo, remote_user) },
366                 { "Service",    "s", NULL, offsetof(SessionStatusInfo, service) },
367                 { "Desktop",    "s", NULL, offsetof(SessionStatusInfo, desktop) },
368                 { "Type",       "s", NULL, offsetof(SessionStatusInfo, type) },
369                 { "Class",      "s", NULL, offsetof(SessionStatusInfo, class) },
370                 { "Scope",      "s", NULL, offsetof(SessionStatusInfo, scope) },
371                 { "State",      "s", NULL, offsetof(SessionStatusInfo, state) },
372                 { "VTNr",       "u", NULL, offsetof(SessionStatusInfo, vtnr) },
373                 { "Leader",     "u", NULL, offsetof(SessionStatusInfo, leader) },
374                 { "Remote",     "b", NULL, offsetof(SessionStatusInfo, remote) },
375                 { "Timestamp",  "t", NULL, offsetof(SessionStatusInfo, timestamp) },
376                 { "User",       "(uo)", prop_map_first_of_struct, offsetof(SessionStatusInfo, uid) },
377                 { "Seat",       "(so)", prop_map_first_of_struct, offsetof(SessionStatusInfo, seat) },
378                 {}
379         };
380
381         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
382         char since2[FORMAT_TIMESTAMP_MAX], *s2;
383         SessionStatusInfo i = {};
384         int r;
385
386         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
387         if (r < 0)
388                 return log_error_errno(r, "Could not get properties: %m");
389
390         if (*new_line)
391                 printf("\n");
392
393         *new_line = true;
394
395         printf("%s - ", strna(i.id));
396
397         if (i.name)
398                 printf("%s (%u)\n", i.name, (unsigned) i.uid);
399         else
400                 printf("%u\n", (unsigned) i.uid);
401
402         s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp);
403         s2 = format_timestamp(since2, sizeof(since2), i.timestamp);
404
405         if (s1)
406                 printf("\t   Since: %s; %s\n", s2, s1);
407         else if (s2)
408                 printf("\t   Since: %s\n", s2);
409
410         if (i.leader > 0) {
411                 _cleanup_free_ char *t = NULL;
412
413                 printf("\t  Leader: %u", (unsigned) i.leader);
414
415                 get_process_comm(i.leader, &t);
416                 if (t)
417                         printf(" (%s)", t);
418
419                 printf("\n");
420         }
421
422         if (!isempty(i.seat)) {
423                 printf("\t    Seat: %s", i.seat);
424
425                 if (i.vtnr > 0)
426                         printf("; vc%u", i.vtnr);
427
428                 printf("\n");
429         }
430
431         if (i.tty)
432                 printf("\t     TTY: %s\n", i.tty);
433         else if (i.display)
434                 printf("\t Display: %s\n", i.display);
435
436         if (i.remote_host && i.remote_user)
437                 printf("\t  Remote: %s@%s\n", i.remote_user, i.remote_host);
438         else if (i.remote_host)
439                 printf("\t  Remote: %s\n", i.remote_host);
440         else if (i.remote_user)
441                 printf("\t  Remote: user %s\n", i.remote_user);
442         else if (i.remote)
443                 printf("\t  Remote: Yes\n");
444
445         if (i.service) {
446                 printf("\t Service: %s", i.service);
447
448                 if (i.type)
449                         printf("; type %s", i.type);
450
451                 if (i.class)
452                         printf("; class %s", i.class);
453
454                 printf("\n");
455         } else if (i.type) {
456                 printf("\t    Type: %s", i.type);
457
458                 if (i.class)
459                         printf("; class %s", i.class);
460
461                 printf("\n");
462         } else if (i.class)
463                 printf("\t   Class: %s\n", i.class);
464
465         if (!isempty(i.desktop))
466                 printf("\t Desktop: %s\n", i.desktop);
467
468         if (i.state)
469                 printf("\t   State: %s\n", i.state);
470
471         if (i.scope) {
472                 printf("\t    Unit: %s\n", i.scope);
473                 show_unit_cgroup(bus, "org.freedesktop.systemd1.Scope", i.scope, i.leader);
474         }
475
476         return 0;
477 }
478
479 static int print_user_status_info(sd_bus *bus, const char *path, bool *new_line) {
480
481         static const struct bus_properties_map map[]  = {
482                 { "Name",       "s",     NULL, offsetof(UserStatusInfo, name) },
483                 { "Slice",      "s",     NULL, offsetof(UserStatusInfo, slice) },
484                 { "State",      "s",     NULL, offsetof(UserStatusInfo, state) },
485                 { "UID",        "u",     NULL, offsetof(UserStatusInfo, uid) },
486                 { "Timestamp",  "t",     NULL, offsetof(UserStatusInfo, timestamp) },
487                 { "Display",    "(so)",  prop_map_first_of_struct, offsetof(UserStatusInfo, display) },
488                 { "Sessions",   "a(so)", prop_map_sessions_strv,   offsetof(UserStatusInfo, sessions) },
489                 {}
490         };
491
492         char since1[FORMAT_TIMESTAMP_RELATIVE_MAX], *s1;
493         char since2[FORMAT_TIMESTAMP_MAX], *s2;
494         UserStatusInfo i = {};
495         int r;
496
497         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
498         if (r < 0) {
499                 log_error_errno(r, "Could not get properties: %m");
500                 goto finish;
501         }
502
503         if (*new_line)
504                 printf("\n");
505
506         *new_line = true;
507
508         if (i.name)
509                 printf("%s (%u)\n", i.name, (unsigned) i.uid);
510         else
511                 printf("%u\n", (unsigned) i.uid);
512
513         s1 = format_timestamp_relative(since1, sizeof(since1), i.timestamp);
514         s2 = format_timestamp(since2, sizeof(since2), i.timestamp);
515
516         if (s1)
517                 printf("\t   Since: %s; %s\n", s2, s1);
518         else if (s2)
519                 printf("\t   Since: %s\n", s2);
520
521         if (!isempty(i.state))
522                 printf("\t   State: %s\n", i.state);
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.display))
530                                 printf(" *%s", *l);
531                         else
532                                 printf(" %s", *l);
533                 }
534
535                 printf("\n");
536         }
537
538         if (i.slice) {
539                 printf("\t    Unit: %s\n", i.slice);
540                 show_unit_cgroup(bus, "org.freedesktop.systemd1.Slice", i.slice, 0);
541         }
542
543 finish:
544         strv_free(i.sessions);
545
546         return r;
547 }
548
549 static int print_seat_status_info(sd_bus *bus, const char *path, bool *new_line) {
550
551         static const struct bus_properties_map map[]  = {
552                 { "Id",            "s",     NULL, offsetof(SeatStatusInfo, id) },
553                 { "ActiveSession", "(so)",  prop_map_first_of_struct, offsetof(SeatStatusInfo, active_session) },
554                 { "Sessions",      "a(so)", prop_map_sessions_strv, offsetof(SeatStatusInfo, sessions) },
555                 {}
556         };
557
558         SeatStatusInfo i = {};
559         int r;
560
561         r = bus_map_all_properties(bus, "org.freedesktop.login1", path, map, &i);
562         if (r < 0) {
563                 log_error_errno(r, "Could not get properties: %m");
564                 goto finish;
565         }
566
567         if (*new_line)
568                 printf("\n");
569
570         *new_line = true;
571
572         printf("%s\n", strna(i.id));
573
574         if (!strv_isempty(i.sessions)) {
575                 char **l;
576                 printf("\tSessions:");
577
578                 STRV_FOREACH(l, i.sessions) {
579                         if (streq_ptr(*l, i.active_session))
580                                 printf(" *%s", *l);
581                         else
582                                 printf(" %s", *l);
583                 }
584
585                 printf("\n");
586         }
587
588         if (arg_transport == BUS_TRANSPORT_LOCAL) {
589                 unsigned c;
590
591                 c = columns();
592                 if (c > 21)
593                         c -= 21;
594                 else
595                         c = 0;
596
597                 printf("\t Devices:\n");
598
599                 show_sysfs(i.id, "\t\t  ", c);
600         }
601
602 finish:
603         strv_free(i.sessions);
604
605         return r;
606 }
607
608 static int show_properties(sd_bus *bus, const char *path, bool *new_line) {
609         int r;
610
611         if (*new_line)
612                 printf("\n");
613
614         *new_line = true;
615
616         r = bus_print_all_properties(bus, "org.freedesktop.login1", path, arg_property, arg_all);
617         if (r < 0)
618                 log_error_errno(r, "Could not get properties: %m");
619
620         return r;
621 }
622
623 static int show_session(sd_bus *bus, char **args, unsigned n) {
624         bool properties, new_line = false;
625         unsigned i;
626         int r;
627
628         assert(bus);
629         assert(args);
630
631         properties = !strstr(args[0], "status");
632
633         pager_open_if_enabled();
634
635         if (properties && n <= 1) {
636                 /* If not argument is specified inspect the manager
637                  * itself */
638                 return show_properties(bus, "/org/freedesktop/login1", &new_line);
639         }
640
641         for (i = 1; i < n; i++) {
642                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
643                 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
644                 const char *path = NULL;
645
646                 r = sd_bus_call_method(
647                                 bus,
648                                 "org.freedesktop.login1",
649                                 "/org/freedesktop/login1",
650                                 "org.freedesktop.login1.Manager",
651                                 "GetSession",
652                                 &error, &reply,
653                                 "s", args[i]);
654                 if (r < 0) {
655                         log_error("Failed to get session: %s", bus_error_message(&error, r));
656                         return r;
657                 }
658
659                 r = sd_bus_message_read(reply, "o", &path);
660                 if (r < 0)
661                         return bus_log_parse_error(r);
662
663                 if (properties)
664                         r = show_properties(bus, path, &new_line);
665                 else
666                         r = print_session_status_info(bus, path, &new_line);
667
668                 if (r < 0)
669                         return r;
670         }
671
672         return 0;
673 }
674
675 static int show_user(sd_bus *bus, char **args, unsigned n) {
676         bool properties, new_line = false;
677         unsigned i;
678         int r;
679
680         assert(bus);
681         assert(args);
682
683         properties = !strstr(args[0], "status");
684
685         pager_open_if_enabled();
686
687         if (properties && n <= 1) {
688                 /* If not argument is specified inspect the manager
689                  * itself */
690                 return show_properties(bus, "/org/freedesktop/login1", &new_line);
691         }
692
693         for (i = 1; i < n; i++) {
694                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
695                 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
696                 const char *path = NULL;
697                 uid_t uid;
698
699                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
700                 if (r < 0)
701                         return log_error_errno(r, "Failed to look up user %s: %m", args[i]);
702
703                 r = sd_bus_call_method(
704                                 bus,
705                                 "org.freedesktop.login1",
706                                 "/org/freedesktop/login1",
707                                 "org.freedesktop.login1.Manager",
708                                 "GetUser",
709                                 &error, &reply,
710                                 "u", (uint32_t) uid);
711                 if (r < 0) {
712                         log_error("Failed to get user: %s", bus_error_message(&error, r));
713                         return r;
714                 }
715
716                 r = sd_bus_message_read(reply, "o", &path);
717                 if (r < 0)
718                         return bus_log_parse_error(r);
719
720                 if (properties)
721                         r = show_properties(bus, path, &new_line);
722                 else
723                         r = print_user_status_info(bus, path, &new_line);
724
725                 if (r < 0)
726                         return r;
727         }
728
729         return 0;
730 }
731
732 static int show_seat(sd_bus *bus, char **args, unsigned n) {
733         bool properties, new_line = false;
734         unsigned i;
735         int r;
736
737         assert(bus);
738         assert(args);
739
740         properties = !strstr(args[0], "status");
741
742         pager_open_if_enabled();
743
744         if (properties && n <= 1) {
745                 /* If not argument is specified inspect the manager
746                  * itself */
747                 return show_properties(bus, "/org/freedesktop/login1", &new_line);
748         }
749
750         for (i = 1; i < n; i++) {
751                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
752                 _cleanup_bus_message_unref_ sd_bus_message * reply = NULL;
753                 const char *path = NULL;
754
755                 r = sd_bus_call_method(
756                                 bus,
757                                 "org.freedesktop.login1",
758                                 "/org/freedesktop/login1",
759                                 "org.freedesktop.login1.Manager",
760                                 "GetSeat",
761                                 &error, &reply,
762                                 "s", args[i]);
763                 if (r < 0) {
764                         log_error("Failed to get seat: %s", bus_error_message(&error, r));
765                         return r;
766                 }
767
768                 r = sd_bus_message_read(reply, "o", &path);
769                 if (r < 0)
770                         return bus_log_parse_error(r);
771
772                 if (properties)
773                         r = show_properties(bus, path, &new_line);
774                 else
775                         r = print_seat_status_info(bus, path, &new_line);
776
777                 if (r < 0)
778                         return r;
779         }
780
781         return 0;
782 }
783
784 static int activate(sd_bus *bus, char **args, unsigned n) {
785         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
786         unsigned i;
787         int r;
788
789         assert(args);
790
791         for (i = 1; i < n; i++) {
792
793                 r = sd_bus_call_method (
794                                 bus,
795                                 "org.freedesktop.login1",
796                                 "/org/freedesktop/login1",
797                                 "org.freedesktop.login1.Manager",
798                                 streq(args[0], "lock-session")      ? "LockSession" :
799                                 streq(args[0], "unlock-session")    ? "UnlockSession" :
800                                 streq(args[0], "terminate-session") ? "TerminateSession" :
801                                                                       "ActivateSession",
802                                 &error, NULL,
803                                 "s", args[i]);
804                 if (r < 0) {
805                         log_error("Failed to issue method call: %s", bus_error_message(&error, -r));
806                         return r;
807                 }
808         }
809
810         return 0;
811 }
812
813 static int kill_session(sd_bus *bus, char **args, unsigned n) {
814         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
815         unsigned i;
816         int r;
817
818         assert(args);
819
820         if (!arg_kill_who)
821                 arg_kill_who = "all";
822
823         for (i = 1; i < n; i++) {
824
825                 r = sd_bus_call_method (
826                         bus,
827                         "org.freedesktop.login1",
828                         "/org/freedesktop/login1",
829                         "org.freedesktop.login1.Manager",
830                         "KillSession",
831                         &error, NULL,
832                         "ssi", args[i], arg_kill_who, arg_signal);
833                 if (r < 0) {
834                         log_error("Could not kill session: %s", bus_error_message(&error, -r));
835                         return r;
836                 }
837         }
838
839         return 0;
840 }
841
842 static int enable_linger(sd_bus *bus, char **args, unsigned n) {
843         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
844         unsigned i;
845         bool b;
846         int r;
847
848         assert(args);
849
850         polkit_agent_open_if_enabled();
851
852         b = streq(args[0], "enable-linger");
853
854         for (i = 1; i < n; i++) {
855                 uid_t uid;
856
857                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
858                 if (r < 0)
859                         return log_error_errno(r, "Failed to look up user %s: %m", args[i]);
860
861                 r = sd_bus_call_method (
862                         bus,
863                         "org.freedesktop.login1",
864                         "/org/freedesktop/login1",
865                         "org.freedesktop.login1.Manager",
866                         "SetUserLinger",
867                         &error, NULL,
868                         "ubb", (uint32_t) uid, b, true);
869                 if (r < 0) {
870                         log_error("Could not enable linger: %s", bus_error_message(&error, -r));
871                         return r;
872                 }
873         }
874
875         return 0;
876 }
877
878 static int terminate_user(sd_bus *bus, char **args, unsigned n) {
879         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
880         unsigned i;
881         int r;
882
883         assert(args);
884
885         for (i = 1; i < n; i++) {
886                 uid_t uid;
887
888                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
889                 if (r < 0)
890                         return log_error_errno(r, "Failed to look up user %s: %m", args[i]);
891
892                 r = sd_bus_call_method (
893                         bus,
894                         "org.freedesktop.login1",
895                         "/org/freedesktop/login1",
896                         "org.freedesktop.login1.Manager",
897                         "TerminateUser",
898                         &error, NULL,
899                         "u", (uint32_t) uid);
900                 if (r < 0) {
901                         log_error("Could not terminate user: %s", bus_error_message(&error, -r));
902                         return r;
903                 }
904         }
905
906         return 0;
907 }
908
909 static int kill_user(sd_bus *bus, char **args, unsigned n) {
910         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
911         unsigned i;
912         int r;
913
914         assert(args);
915
916         if (!arg_kill_who)
917                 arg_kill_who = "all";
918
919         for (i = 1; i < n; i++) {
920                 uid_t uid;
921
922                 r = get_user_creds((const char**) (args+i), &uid, NULL, NULL, NULL);
923                 if (r < 0)
924                         return log_error_errno(r, "Failed to look up user %s: %m", args[i]);
925
926                 r = sd_bus_call_method (
927                         bus,
928                         "org.freedesktop.login1",
929                         "/org/freedesktop/login1",
930                         "org.freedesktop.login1.Manager",
931                         "KillUser",
932                         &error, NULL,
933                         "ui", (uint32_t) uid, arg_signal);
934                 if (r < 0) {
935                         log_error("Could not kill user: %s", bus_error_message(&error, -r));
936                         return r;
937                 }
938         }
939
940         return 0;
941 }
942
943 static int attach(sd_bus *bus, char **args, unsigned n) {
944         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
945         unsigned i;
946         int r;
947
948         assert(args);
949
950         polkit_agent_open_if_enabled();
951
952         for (i = 2; i < n; i++) {
953
954                 r = sd_bus_call_method (
955                         bus,
956                         "org.freedesktop.login1",
957                         "/org/freedesktop/login1",
958                         "org.freedesktop.login1.Manager",
959                         "AttachDevice",
960                         &error, NULL,
961                         "ssb", args[1], args[i], true);
962
963                 if (r < 0) {
964                         log_error("Could not attach device: %s", bus_error_message(&error, -r));
965                         return r;
966                 }
967         }
968
969         return 0;
970 }
971
972 static int flush_devices(sd_bus *bus, char **args, unsigned n) {
973         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
974         int r;
975
976         assert(args);
977
978         polkit_agent_open_if_enabled();
979
980         r = sd_bus_call_method (
981                         bus,
982                         "org.freedesktop.login1",
983                         "/org/freedesktop/login1",
984                         "org.freedesktop.login1.Manager",
985                         "FlushDevices",
986                         &error, NULL,
987                         "b", true);
988         if (r < 0)
989                 log_error("Could not flush devices: %s", bus_error_message(&error, -r));
990
991         return r;
992 }
993
994 static int lock_sessions(sd_bus *bus, char **args, unsigned n) {
995         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
996         int r;
997
998         assert(args);
999
1000         r = sd_bus_call_method (
1001                         bus,
1002                         "org.freedesktop.login1",
1003                         "/org/freedesktop/login1",
1004                         "org.freedesktop.login1.Manager",
1005                         streq(args[0], "lock-sessions") ? "LockSessions" : "UnlockSessions",
1006                         &error, NULL,
1007                         NULL);
1008         if (r < 0)
1009                 log_error("Could not lock sessions: %s", bus_error_message(&error, -r));
1010
1011         return r;
1012 }
1013
1014 static int terminate_seat(sd_bus *bus, char **args, unsigned n) {
1015         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
1016         unsigned i;
1017         int r;
1018
1019         assert(args);
1020
1021         for (i = 1; i < n; i++) {
1022
1023                 r = sd_bus_call_method (
1024                         bus,
1025                         "org.freedesktop.login1",
1026                         "/org/freedesktop/login1",
1027                         "org.freedesktop.login1.Manager",
1028                         "TerminateSeat",
1029                         &error, NULL,
1030                         "s", args[i]);
1031                 if (r < 0) {
1032                         log_error("Could not terminate seat: %s", bus_error_message(&error, -r));
1033                         return r;
1034                 }
1035         }
1036
1037         return 0;
1038 }
1039
1040 static void help(void) {
1041         printf("%s [OPTIONS...] {COMMAND} ...\n\n"
1042                "Send control commands to or query the login manager.\n\n"
1043                "  -h --help                Show this help\n"
1044                "     --version             Show package version\n"
1045                "     --no-pager            Do not pipe output into a pager\n"
1046                "     --no-legend           Do not show the headers and footers\n"
1047                "     --no-ask-password     Don't prompt for password\n"
1048                "  -H --host=[USER@]HOST    Operate on remote host\n"
1049                "  -M --machine=CONTAINER   Operate on local container\n"
1050                "  -p --property=NAME       Show only properties by this name\n"
1051                "  -a --all                 Show all properties, including empty ones\n"
1052                "  -l --full                Do not ellipsize output\n"
1053                "     --kill-who=WHO        Who to send signal to\n"
1054                "  -s --signal=SIGNAL       Which signal to send\n\n"
1055                "Session Commands:\n"
1056                "  list-sessions            List sessions\n"
1057                "  session-status ID...     Show session status\n"
1058                "  show-session [ID...]     Show properties of sessions or the manager\n"
1059                "  activate ID              Activate a session\n"
1060                "  lock-session ID...       Screen lock one or more sessions\n"
1061                "  unlock-session ID...     Screen unlock one or more sessions\n"
1062                "  lock-sessions            Screen lock all current sessions\n"
1063                "  unlock-sessions          Screen unlock all current sessions\n"
1064                "  terminate-session ID...  Terminate one or more sessions\n"
1065                "  kill-session ID...       Send signal to processes of a session\n\n"
1066                "User Commands:\n"
1067                "  list-users               List users\n"
1068                "  user-status USER...      Show user status\n"
1069                "  show-user [USER...]      Show properties of users or the manager\n"
1070                "  enable-linger USER...    Enable linger state of one or more users\n"
1071                "  disable-linger USER...   Disable linger state of one or more users\n"
1072                "  terminate-user USER...   Terminate all sessions of one or more users\n"
1073                "  kill-user USER...        Send signal to processes of a user\n\n"
1074                "Seat Commands:\n"
1075                "  list-seats               List seats\n"
1076                "  seat-status NAME...      Show seat status\n"
1077                "  show-seat NAME...        Show properties of one or more seats\n"
1078                "  attach NAME DEVICE...    Attach one or more devices to a seat\n"
1079                "  flush-devices            Flush all device associations\n"
1080                "  terminate-seat NAME...   Terminate all sessions on one or more seats\n"
1081                , program_invocation_short_name);
1082 }
1083
1084 static int parse_argv(int argc, char *argv[]) {
1085
1086         enum {
1087                 ARG_VERSION = 0x100,
1088                 ARG_NO_PAGER,
1089                 ARG_NO_LEGEND,
1090                 ARG_KILL_WHO,
1091                 ARG_NO_ASK_PASSWORD,
1092         };
1093
1094         static const struct option options[] = {
1095                 { "help",            no_argument,       NULL, 'h'                 },
1096                 { "version",         no_argument,       NULL, ARG_VERSION         },
1097                 { "property",        required_argument, NULL, 'p'                 },
1098                 { "all",             no_argument,       NULL, 'a'                 },
1099                 { "full",            no_argument,       NULL, 'l'                 },
1100                 { "no-pager",        no_argument,       NULL, ARG_NO_PAGER        },
1101                 { "no-legend",       no_argument,       NULL, ARG_NO_LEGEND       },
1102                 { "kill-who",        required_argument, NULL, ARG_KILL_WHO        },
1103                 { "signal",          required_argument, NULL, 's'                 },
1104                 { "host",            required_argument, NULL, 'H'                 },
1105                 { "machine",         required_argument, NULL, 'M'                 },
1106                 { "no-ask-password", no_argument,       NULL, ARG_NO_ASK_PASSWORD },
1107                 {}
1108         };
1109
1110         int c, r;
1111
1112         assert(argc >= 0);
1113         assert(argv);
1114
1115         while ((c = getopt_long(argc, argv, "hp:als:H:M:", options, NULL)) >= 0)
1116
1117                 switch (c) {
1118
1119                 case 'h':
1120                         help();
1121                         return 0;
1122
1123                 case ARG_VERSION:
1124                         puts(PACKAGE_STRING);
1125                         puts(SYSTEMD_FEATURES);
1126                         return 0;
1127
1128                 case 'p': {
1129                         r = strv_extend(&arg_property, optarg);
1130                         if (r < 0)
1131                                 return log_oom();
1132
1133                         /* If the user asked for a particular
1134                          * property, show it to him, even if it is
1135                          * empty. */
1136                         arg_all = true;
1137                         break;
1138                 }
1139
1140                 case 'a':
1141                         arg_all = true;
1142                         break;
1143
1144                 case 'l':
1145                         arg_full = true;
1146                         break;
1147
1148                 case ARG_NO_PAGER:
1149                         arg_no_pager = true;
1150                         break;
1151
1152                 case ARG_NO_LEGEND:
1153                         arg_legend = false;
1154                         break;
1155
1156                 case ARG_NO_ASK_PASSWORD:
1157                         arg_ask_password = false;
1158                         break;
1159
1160                 case ARG_KILL_WHO:
1161                         arg_kill_who = optarg;
1162                         break;
1163
1164                 case 's':
1165                         arg_signal = signal_from_string_try_harder(optarg);
1166                         if (arg_signal < 0) {
1167                                 log_error("Failed to parse signal string %s.", optarg);
1168                                 return -EINVAL;
1169                         }
1170                         break;
1171
1172                 case 'H':
1173                         arg_transport = BUS_TRANSPORT_REMOTE;
1174                         arg_host = optarg;
1175                         break;
1176
1177                 case 'M':
1178                         arg_transport = BUS_TRANSPORT_MACHINE;
1179                         arg_host = optarg;
1180                         break;
1181
1182                 case '?':
1183                         return -EINVAL;
1184
1185                 default:
1186                         assert_not_reached("Unhandled option");
1187                 }
1188
1189         return 1;
1190 }
1191
1192 static int loginctl_main(sd_bus *bus, int argc, char *argv[]) {
1193
1194         static const struct {
1195                 const char* verb;
1196                 const enum {
1197                         MORE,
1198                         LESS,
1199                         EQUAL
1200                 } argc_cmp;
1201                 const int argc;
1202                 int (* const dispatch)(sd_bus *bus, char **args, unsigned n);
1203         } verbs[] = {
1204                 { "list-sessions",         LESS,   1, list_sessions     },
1205                 { "session-status",        MORE,   2, show_session      },
1206                 { "show-session",          MORE,   1, show_session      },
1207                 { "activate",              EQUAL,  2, activate          },
1208                 { "lock-session",          MORE,   2, activate          },
1209                 { "unlock-session",        MORE,   2, activate          },
1210                 { "lock-sessions",         EQUAL,  1, lock_sessions     },
1211                 { "unlock-sessions",       EQUAL,  1, lock_sessions     },
1212                 { "terminate-session",     MORE,   2, activate          },
1213                 { "kill-session",          MORE,   2, kill_session      },
1214                 { "list-users",            EQUAL,  1, list_users        },
1215                 { "user-status",           MORE,   2, show_user         },
1216                 { "show-user",             MORE,   1, show_user         },
1217                 { "enable-linger",         MORE,   2, enable_linger     },
1218                 { "disable-linger",        MORE,   2, enable_linger     },
1219                 { "terminate-user",        MORE,   2, terminate_user    },
1220                 { "kill-user",             MORE,   2, kill_user         },
1221                 { "list-seats",            EQUAL,  1, list_seats        },
1222                 { "seat-status",           MORE,   2, show_seat         },
1223                 { "show-seat",             MORE,   1, show_seat         },
1224                 { "attach",                MORE,   3, attach            },
1225                 { "flush-devices",         EQUAL,  1, flush_devices     },
1226                 { "terminate-seat",        MORE,   2, terminate_seat    },
1227         };
1228
1229         int left;
1230         unsigned i;
1231
1232         assert(argc >= 0);
1233         assert(argv);
1234
1235         left = argc - optind;
1236
1237         if (left <= 0)
1238                 /* Special rule: no arguments means "list-sessions" */
1239                 i = 0;
1240         else {
1241                 if (streq(argv[optind], "help")) {
1242                         help();
1243                         return 0;
1244                 }
1245
1246                 for (i = 0; i < ELEMENTSOF(verbs); i++)
1247                         if (streq(argv[optind], verbs[i].verb))
1248                                 break;
1249
1250                 if (i >= ELEMENTSOF(verbs)) {
1251                         log_error("Unknown operation %s", argv[optind]);
1252                         return -EINVAL;
1253                 }
1254         }
1255
1256         switch (verbs[i].argc_cmp) {
1257
1258         case EQUAL:
1259                 if (left != verbs[i].argc) {
1260                         log_error("Invalid number of arguments.");
1261                         return -EINVAL;
1262                 }
1263
1264                 break;
1265
1266         case MORE:
1267                 if (left < verbs[i].argc) {
1268                         log_error("Too few arguments.");
1269                         return -EINVAL;
1270                 }
1271
1272                 break;
1273
1274         case LESS:
1275                 if (left > verbs[i].argc) {
1276                         log_error("Too many arguments.");
1277                         return -EINVAL;
1278                 }
1279
1280                 break;
1281
1282         default:
1283                 assert_not_reached("Unknown comparison operator.");
1284         }
1285
1286         return verbs[i].dispatch(bus, argv + optind, left);
1287 }
1288
1289 int main(int argc, char *argv[]) {
1290         _cleanup_bus_close_unref_ sd_bus *bus = NULL;
1291         int r;
1292
1293         setlocale(LC_ALL, "");
1294         log_parse_environment();
1295         log_open();
1296
1297         r = parse_argv(argc, argv);
1298         if (r <= 0)
1299                 goto finish;
1300
1301         r = bus_open_transport(arg_transport, arg_host, false, &bus);
1302         if (r < 0) {
1303                 log_error_errno(r, "Failed to create bus connection: %m");
1304                 goto finish;
1305         }
1306
1307         r = loginctl_main(bus, argc, argv);
1308
1309 finish:
1310         pager_close();
1311
1312         strv_free(arg_property);
1313
1314         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1315 }