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