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