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