]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-core.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / login / logind-core.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
2b3ab29d 2
2b3ab29d
ZJS
3#include <fcntl.h>
4#include <pwd.h>
cf0fbc49
TA
5#include <sys/ioctl.h>
6#include <sys/types.h>
2b3ab29d 7#include <linux/vt.h>
3d0ef5c7
LP
8#if ENABLE_UTMP
9#include <utmpx.h>
10#endif
2b3ab29d 11
4f209af7
YW
12#include "sd-device.h"
13
b5efdb8a 14#include "alloc-util.h"
cc377381 15#include "bus-error.h"
3ffd4af2
LP
16#include "bus-util.h"
17#include "cgroup-util.h"
ae98d374 18#include "conf-parser.h"
8437c059 19#include "device-util.h"
3ffd4af2 20#include "fd-util.h"
cc377381 21#include "logind.h"
c29b65f7 22#include "parse-util.h"
3d0ef5c7 23#include "path-util.h"
dccca82b 24#include "process-util.h"
3ffd4af2 25#include "strv.h"
288a74cc 26#include "terminal-util.h"
b1d4f8e1 27#include "user-util.h"
2b3ab29d 28
ae98d374 29void manager_reset_config(Manager *m) {
23462168
LP
30 assert(m);
31
ae98d374
ZJS
32 m->n_autovts = 6;
33 m->reserve_vt = 6;
34 m->remove_ipc = true;
35 m->inhibit_delay_max = 5 * USEC_PER_SEC;
9afe9efb
LP
36 m->user_stop_delay = 10 * USEC_PER_SEC;
37
ae98d374
ZJS
38 m->handle_power_key = HANDLE_POWEROFF;
39 m->handle_suspend_key = HANDLE_SUSPEND;
40 m->handle_hibernate_key = HANDLE_HIBERNATE;
41 m->handle_lid_switch = HANDLE_SUSPEND;
42 m->handle_lid_switch_ep = _HANDLE_ACTION_INVALID;
43 m->handle_lid_switch_docked = HANDLE_IGNORE;
44 m->power_key_ignore_inhibited = false;
45 m->suspend_key_ignore_inhibited = false;
46 m->hibernate_key_ignore_inhibited = false;
47 m->lid_switch_ignore_inhibited = true;
48
49 m->holdoff_timeout_usec = 30 * USEC_PER_SEC;
50
51 m->idle_action_usec = 30 * USEC_PER_MINUTE;
52 m->idle_action = HANDLE_IGNORE;
53
54 m->runtime_dir_size = physical_memory_scale(10U, 100U); /* 10% */
55 m->user_tasks_max = system_tasks_max_scale(DEFAULT_USER_TASKS_MAX_PERCENTAGE, 100U); /* 33% */
56 m->sessions_max = 8192;
57 m->inhibitors_max = 8192;
58
59 m->kill_user_processes = KILL_USER_PROCESSES;
60
61 m->kill_only_users = strv_free(m->kill_only_users);
62 m->kill_exclude_users = strv_free(m->kill_exclude_users);
63}
64
65int manager_parse_config_file(Manager *m) {
66 assert(m);
67
68 return config_parse_many_nulstr(PKGSYSCONFDIR "/logind.conf",
69 CONF_PATHS_NULSTR("systemd/logind.conf.d"),
70 "Login\0",
71 config_item_perf_lookup, logind_gperf_lookup,
72 CONFIG_PARSE_WARN, m);
73}
74
2b3ab29d
ZJS
75int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) {
76 Device *d;
77
78 assert(m);
79 assert(sysfs);
80
81 d = hashmap_get(m->devices, sysfs);
61376f96 82 if (d)
2b3ab29d
ZJS
83 /* we support adding master-flags, but not removing them */
84 d->master = d->master || master;
61376f96
ZJS
85 else {
86 d = device_new(m, sysfs, master);
87 if (!d)
88 return -ENOMEM;
2b3ab29d
ZJS
89 }
90
2b3ab29d
ZJS
91 if (_device)
92 *_device = d;
93
94 return 0;
95}
96
97int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
98 Seat *s;
8c29a457 99 int r;
2b3ab29d
ZJS
100
101 assert(m);
102 assert(id);
103
104 s = hashmap_get(m->seats, id);
61376f96 105 if (!s) {
8c29a457
LP
106 r = seat_new(&s, m, id);
107 if (r < 0)
108 return r;
2b3ab29d
ZJS
109 }
110
2b3ab29d
ZJS
111 if (_seat)
112 *_seat = s;
113
114 return 0;
115}
116
117int manager_add_session(Manager *m, const char *id, Session **_session) {
118 Session *s;
8c29a457 119 int r;
2b3ab29d
ZJS
120
121 assert(m);
122 assert(id);
123
124 s = hashmap_get(m->sessions, id);
61376f96 125 if (!s) {
8c29a457
LP
126 r = session_new(&s, m, id);
127 if (r < 0)
128 return r;
2b3ab29d
ZJS
129 }
130
2b3ab29d
ZJS
131 if (_session)
132 *_session = s;
133
134 return 0;
135}
136
d5ac9d06
LP
137int manager_add_user(
138 Manager *m,
139 uid_t uid,
140 gid_t gid,
141 const char *name,
142 const char *home,
143 User **_user) {
144
2b3ab29d 145 User *u;
157f5057 146 int r;
2b3ab29d
ZJS
147
148 assert(m);
149 assert(name);
150
8cb4ab00 151 u = hashmap_get(m->users, UID_TO_PTR(uid));
61376f96 152 if (!u) {
d5ac9d06 153 r = user_new(&u, m, uid, gid, name, home);
157f5057
DH
154 if (r < 0)
155 return r;
2b3ab29d
ZJS
156 }
157
2b3ab29d
ZJS
158 if (_user)
159 *_user = u;
160
161 return 0;
162}
163
d5ac9d06
LP
164int manager_add_user_by_name(
165 Manager *m,
166 const char *name,
167 User **_user) {
168
169 const char *home = NULL;
2b3ab29d
ZJS
170 uid_t uid;
171 gid_t gid;
172 int r;
173
174 assert(m);
175 assert(name);
176
d5ac9d06 177 r = get_user_creds(&name, &uid, &gid, &home, NULL, 0);
2b3ab29d
ZJS
178 if (r < 0)
179 return r;
180
d5ac9d06 181 return manager_add_user(m, uid, gid, name, home, _user);
2b3ab29d
ZJS
182}
183
184int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
185 struct passwd *p;
186
187 assert(m);
188
189 errno = 0;
190 p = getpwuid(uid);
191 if (!p)
f5e5c28f 192 return errno > 0 ? -errno : -ENOENT;
2b3ab29d 193
d5ac9d06 194 return manager_add_user(m, uid, p->pw_gid, p->pw_name, p->pw_dir, _user);
2b3ab29d
ZJS
195}
196
197int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) {
198 Inhibitor *i;
199
200 assert(m);
201 assert(id);
202
203 i = hashmap_get(m->inhibitors, id);
204 if (i) {
205 if (_inhibitor)
206 *_inhibitor = i;
207
208 return 0;
209 }
210
211 i = inhibitor_new(m, id);
212 if (!i)
213 return -ENOMEM;
214
215 if (_inhibitor)
216 *_inhibitor = i;
217
218 return 0;
219}
220
221int manager_add_button(Manager *m, const char *name, Button **_button) {
222 Button *b;
223
224 assert(m);
225 assert(name);
226
227 b = hashmap_get(m->buttons, name);
61376f96
ZJS
228 if (!b) {
229 b = button_new(m, name);
230 if (!b)
231 return -ENOMEM;
2b3ab29d
ZJS
232 }
233
2b3ab29d
ZJS
234 if (_button)
235 *_button = b;
236
237 return 0;
238}
239
4f209af7 240int manager_process_seat_device(Manager *m, sd_device *d) {
403660c5 241 const char *action;
2b3ab29d
ZJS
242 Device *device;
243 int r;
244
245 assert(m);
246
403660c5
YW
247 if (sd_device_get_property_value(d, "ACTION", &action) >= 0 &&
248 streq(action, "remove")) {
4f209af7
YW
249 const char *syspath;
250
251 r = sd_device_get_syspath(d, &syspath);
252 if (r < 0)
253 return 0;
2b3ab29d 254
4f209af7 255 device = hashmap_get(m->devices, syspath);
2b3ab29d
ZJS
256 if (!device)
257 return 0;
258
259 seat_add_to_gc_queue(device->seat);
260 device_free(device);
261
262 } else {
4f209af7 263 const char *sn, *syspath;
2b3ab29d 264 bool master;
4f209af7 265 Seat *seat;
2b3ab29d 266
4f209af7 267 if (sd_device_get_property_value(d, "ID_SEAT", &sn) < 0 || isempty(sn))
2b3ab29d
ZJS
268 sn = "seat0";
269
270 if (!seat_name_is_valid(sn)) {
76386309 271 log_device_warning(d, "Device with invalid seat name %s found, ignoring.", sn);
2b3ab29d
ZJS
272 return 0;
273 }
274
6a79c586 275 seat = hashmap_get(m->seats, sn);
4f209af7 276 master = sd_device_has_tag(d, "master-of-seat") > 0;
6a79c586
LP
277
278 /* Ignore non-master devices for unknown seats */
279 if (!master && !seat)
2b3ab29d
ZJS
280 return 0;
281
4f209af7
YW
282 r = sd_device_get_syspath(d, &syspath);
283 if (r < 0)
284 return r;
285
286 r = manager_add_device(m, syspath, master, &device);
2b3ab29d
ZJS
287 if (r < 0)
288 return r;
289
290 if (!seat) {
291 r = manager_add_seat(m, sn, &seat);
292 if (r < 0) {
293 if (!device->seat)
294 device_free(device);
295
296 return r;
297 }
298 }
299
300 device_attach(device, seat);
301 seat_start(seat);
302 }
303
304 return 0;
305}
306
4f209af7 307int manager_process_button_device(Manager *m, sd_device *d) {
403660c5 308 const char *action, *sysname;
2b3ab29d 309 Button *b;
2b3ab29d
ZJS
310 int r;
311
312 assert(m);
313
4f209af7
YW
314 r = sd_device_get_sysname(d, &sysname);
315 if (r < 0)
316 return r;
317
403660c5
YW
318 if (sd_device_get_property_value(d, "ACTION", &action) >= 0 &&
319 streq(action, "remove")) {
2b3ab29d 320
4f209af7 321 b = hashmap_get(m->buttons, sysname);
2b3ab29d
ZJS
322 if (!b)
323 return 0;
324
325 button_free(b);
326
327 } else {
c679e12a 328 const char *sn;
2b3ab29d 329
4f209af7 330 r = manager_add_button(m, sysname, &b);
2b3ab29d
ZJS
331 if (r < 0)
332 return r;
333
4f209af7 334 if (sd_device_get_property_value(d, "ID_SEAT", &sn) < 0 || isempty(sn))
2b3ab29d
ZJS
335 sn = "seat0";
336
337 button_set_seat(b, sn);
2546b70a
LP
338
339 r = button_open(b);
340 if (r < 0) /* event device doesn't have any keys or switches relevant to us? (or any other error
341 * opening the device?) let's close the button again. */
342 button_free(b);
2b3ab29d
ZJS
343 }
344
345 return 0;
346}
347
14ef4a65 348int manager_get_session_by_pid(Manager *m, pid_t pid, Session **ret) {
2b3ab29d
ZJS
349 _cleanup_free_ char *unit = NULL;
350 Session *s;
351 int r;
352
353 assert(m);
2b3ab29d 354
6f876815 355 if (!pid_is_valid(pid))
2b3ab29d
ZJS
356 return -EINVAL;
357
238794b1
LP
358 s = hashmap_get(m->sessions_by_leader, PID_TO_PTR(pid));
359 if (!s) {
360 r = cg_pid_get_unit(pid, &unit);
361 if (r < 0)
362 goto not_found;
2b3ab29d 363
238794b1
LP
364 s = hashmap_get(m->session_units, unit);
365 if (!s)
366 goto not_found;
367 }
14ef4a65
LP
368
369 if (ret)
370 *ret = s;
2b3ab29d 371
2b3ab29d 372 return 1;
14ef4a65
LP
373
374not_found:
375 if (ret)
376 *ret = NULL;
377 return 0;
2b3ab29d
ZJS
378}
379
14ef4a65 380int manager_get_user_by_pid(Manager *m, pid_t pid, User **ret) {
2b3ab29d
ZJS
381 _cleanup_free_ char *unit = NULL;
382 User *u;
383 int r;
384
385 assert(m);
2b3ab29d 386
6f876815 387 if (!pid_is_valid(pid))
2b3ab29d
ZJS
388 return -EINVAL;
389
390 r = cg_pid_get_slice(pid, &unit);
391 if (r < 0)
14ef4a65 392 goto not_found;
2b3ab29d
ZJS
393
394 u = hashmap_get(m->user_units, unit);
395 if (!u)
14ef4a65
LP
396 goto not_found;
397
398 if (ret)
399 *ret = u;
2b3ab29d 400
2b3ab29d 401 return 1;
14ef4a65
LP
402
403not_found:
404 if (ret)
405 *ret = NULL;
406
407 return 0;
2b3ab29d
ZJS
408}
409
410int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
411 Session *s;
412 bool idle_hint;
5cb14b37 413 dual_timestamp ts = DUAL_TIMESTAMP_NULL;
2b3ab29d
ZJS
414 Iterator i;
415
416 assert(m);
417
85a428c6 418 idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL);
2b3ab29d
ZJS
419
420 HASHMAP_FOREACH(s, m->sessions, i) {
421 dual_timestamp k;
422 int ih;
423
424 ih = session_get_idle_hint(s, &k);
425 if (ih < 0)
426 return ih;
427
428 if (!ih) {
429 if (!idle_hint) {
430 if (k.monotonic < ts.monotonic)
431 ts = k;
432 } else {
433 idle_hint = false;
434 ts = k;
435 }
436 } else if (idle_hint) {
437
438 if (k.monotonic > ts.monotonic)
439 ts = k;
440 }
441 }
442
443 if (t)
444 *t = ts;
445
446 return idle_hint;
447}
448
449bool manager_shall_kill(Manager *m, const char *user) {
450 assert(m);
451 assert(user);
452
a2ed7077
ZJS
453 if (!m->kill_exclude_users && streq(user, "root"))
454 return false;
455
2b3ab29d
ZJS
456 if (strv_contains(m->kill_exclude_users, user))
457 return false;
458
921f831d
ZJS
459 if (!strv_isempty(m->kill_only_users))
460 return strv_contains(m->kill_only_users, user);
2b3ab29d 461
921f831d 462 return m->kill_user_processes;
2b3ab29d
ZJS
463}
464
c29b65f7
AJ
465int config_parse_n_autovts(
466 const char *unit,
467 const char *filename,
468 unsigned line,
469 const char *section,
470 unsigned section_line,
471 const char *lvalue,
472 int ltype,
473 const char *rvalue,
474 void *data,
475 void *userdata) {
476
477 unsigned *n = data;
478 unsigned o;
479 int r;
480
481 assert(filename);
482 assert(lvalue);
483 assert(rvalue);
484 assert(data);
485
486 r = safe_atou(rvalue, &o);
487 if (r < 0) {
488 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse number of autovts, ignoring: %s", rvalue);
489 return 0;
490 }
491
492 if (o > 15) {
493 log_syntax(unit, LOG_ERR, filename, line, r, "A maximum of 15 autovts are supported, ignoring: %s", rvalue);
494 return 0;
495 }
496
497 *n = o;
498 return 0;
499}
500
14cb109d 501static int vt_is_busy(unsigned vtnr) {
2b3ab29d 502 struct vt_stat vt_stat;
61376f96
ZJS
503 int r = 0;
504 _cleanup_close_ int fd;
2b3ab29d
ZJS
505
506 assert(vtnr >= 1);
507
c29b65f7
AJ
508 /* VT_GETSTATE "cannot return state for more than 16 VTs, since v_state is short" */
509 assert(vtnr <= 15);
510
2b3ab29d
ZJS
511 /* We explicitly open /dev/tty1 here instead of /dev/tty0. If
512 * we'd open the latter we'd open the foreground tty which
513 * hence would be unconditionally busy. By opening /dev/tty1
514 * we avoid this. Since tty1 is special and needs to be an
515 * explicitly loaded getty or DM this is safe. */
516
517 fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC);
518 if (fd < 0)
519 return -errno;
520
521 if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
522 r = -errno;
523 else
524 r = !!(vt_stat.v_state & (1 << vtnr));
525
2b3ab29d
ZJS
526 return r;
527}
528
14cb109d 529int manager_spawn_autovt(Manager *m, unsigned vtnr) {
4afd3348 530 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
14cb109d 531 char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned)];
2b3ab29d 532 int r;
2b3ab29d
ZJS
533
534 assert(m);
535 assert(vtnr >= 1);
536
92bd5ff3
DH
537 if (vtnr > m->n_autovts &&
538 vtnr != m->reserve_vt)
2b3ab29d
ZJS
539 return 0;
540
92bd5ff3 541 if (vtnr != m->reserve_vt) {
2b3ab29d
ZJS
542 /* If this is the reserved TTY, we'll start the getty
543 * on it in any case, but otherwise only if it is not
544 * busy. */
545
546 r = vt_is_busy(vtnr);
547 if (r < 0)
548 return r;
549 else if (r > 0)
550 return -EBUSY;
551 }
552
61376f96 553 snprintf(name, sizeof(name), "autovt@tty%u.service", vtnr);
cc377381 554 r = sd_bus_call_method(
2b3ab29d
ZJS
555 m->bus,
556 "org.freedesktop.systemd1",
557 "/org/freedesktop/systemd1",
558 "org.freedesktop.systemd1.Manager",
559 "StartUnit",
cc377381 560 &error,
2b3ab29d 561 NULL,
cc377381
LP
562 "ss", name, "fail");
563 if (r < 0)
4ae25393 564 return log_error_errno(r, "Failed to start %s: %s", name, bus_error_message(&error, r));
2b3ab29d 565
4ae25393 566 return 0;
2b3ab29d 567}
2d62c530 568
9b9c23da
LP
569bool manager_is_lid_closed(Manager *m) {
570 Iterator i;
571 Button *b;
572
573 HASHMAP_FOREACH(b, m->buttons, i)
574 if (b->lid_closed)
575 return true;
576
577 return false;
578}
579
602a41c2 580static bool manager_is_docked(Manager *m) {
2d62c530
LP
581 Iterator i;
582 Button *b;
583
584 HASHMAP_FOREACH(b, m->buttons, i)
585 if (b->docked)
586 return true;
587
588 return false;
589}
6a79c586 590
602a41c2 591static int manager_count_external_displays(Manager *m) {
4f209af7
YW
592 _cleanup_(sd_device_enumerator_unrefp) sd_device_enumerator *e = NULL;
593 sd_device *d;
594 int r, n = 0;
6a79c586 595
4f209af7
YW
596 r = sd_device_enumerator_new(&e);
597 if (r < 0)
598 return r;
6a79c586 599
4f209af7 600 r = sd_device_enumerator_allow_uninitialized(e);
6a79c586
LP
601 if (r < 0)
602 return r;
603
4f209af7 604 r = sd_device_enumerator_add_match_subsystem(e, "drm", true);
6a79c586
LP
605 if (r < 0)
606 return r;
607
8437c059 608 FOREACH_DEVICE(e, d) {
49fe5c09 609 const char *status, *enabled, *dash, *nn, *subsys;
4f209af7 610 sd_device *p;
6a79c586 611
4f209af7 612 if (sd_device_get_parent(d, &p) < 0)
94036de8 613 continue;
6a79c586
LP
614
615 /* If the parent shares the same subsystem as the
616 * device we are looking at then it is a connector,
617 * which is what we are interested in. */
4f209af7 618 if (sd_device_get_subsystem(p, &subsys) < 0 || !streq(subsys, "drm"))
6a79c586
LP
619 continue;
620
4f209af7 621 if (sd_device_get_sysname(d, &nn) < 0)
602a41c2
LP
622 continue;
623
6ac38685
LP
624 /* Ignore internal displays: the type is encoded in the sysfs name, as the second dash separated item
625 * (the first is the card name, the last the connector number). We implement a blacklist of external
626 * displays here, rather than a whitelist of internal ones, to ensure we don't block suspends too
627 * eagerly. */
602a41c2
LP
628 dash = strchr(nn, '-');
629 if (!dash)
630 continue;
631
632 dash++;
49fe5c09
LP
633 if (!STARTSWITH_SET(dash,
634 "VGA-", "DVI-I-", "DVI-D-", "DVI-A-"
635 "Composite-", "SVIDEO-", "Component-",
636 "DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-"))
602a41c2
LP
637 continue;
638
639 /* Ignore ports that are not enabled */
4f209af7 640 if (sd_device_get_sysattr_value(d, "enabled", &enabled) < 0 || !streq(enabled, "enabled"))
602a41c2
LP
641 continue;
642
6a79c586
LP
643 /* We count any connector which is not explicitly
644 * "disconnected" as connected. */
4f209af7 645 if (sd_device_get_sysattr_value(d, "status", &status) < 0 || !streq(status, "disconnected"))
6a79c586
LP
646 n++;
647 }
648
649 return n;
650}
3c56cab4 651
602a41c2 652bool manager_is_docked_or_external_displays(Manager *m) {
3c56cab4
BW
653 int n;
654
655 /* If we are docked don't react to lid closing */
656 if (manager_is_docked(m)) {
657 log_debug("System is docked.");
658 return true;
659 }
660
661 /* If we have more than one display connected,
662 * assume that we are docked. */
602a41c2 663 n = manager_count_external_displays(m);
3c56cab4 664 if (n < 0)
da927ba9 665 log_warning_errno(n, "Display counting failed: %m");
602a41c2
LP
666 else if (n >= 1) {
667 log_debug("External (%i) displays connected.", n);
3c56cab4
BW
668 return true;
669 }
670
671 return false;
672}
e25937a3
SF
673
674bool manager_is_on_external_power(void) {
675 int r;
676
e455380b
LP
677 /* For now we only check for AC power, but 'external power' can apply to anything that isn't an internal
678 * battery */
e25937a3
SF
679 r = on_ac_power();
680 if (r < 0)
681 log_warning_errno(r, "Failed to read AC power status: %m");
e25937a3 682
e455380b 683 return r != 0; /* Treat failure as 'on AC' */
e25937a3
SF
684}
685
686bool manager_all_buttons_ignored(Manager *m) {
864fe630
LP
687 assert(m);
688
e25937a3
SF
689 if (m->handle_power_key != HANDLE_IGNORE)
690 return false;
691 if (m->handle_suspend_key != HANDLE_IGNORE)
692 return false;
693 if (m->handle_hibernate_key != HANDLE_IGNORE)
694 return false;
695 if (m->handle_lid_switch != HANDLE_IGNORE)
696 return false;
697 if (m->handle_lid_switch_ep != _HANDLE_ACTION_INVALID &&
698 m->handle_lid_switch_ep != HANDLE_IGNORE)
699 return false;
700 if (m->handle_lid_switch_docked != HANDLE_IGNORE)
701 return false;
864fe630 702
e25937a3
SF
703 return true;
704}
3d0ef5c7
LP
705
706int manager_read_utmp(Manager *m) {
707#if ENABLE_UTMP
708 int r;
709
710 assert(m);
711
712 if (utmpxname(_PATH_UTMPX) < 0)
713 return log_error_errno(errno, "Failed to set utmp path to " _PATH_UTMPX ": %m");
714
715 setutxent();
716
717 for (;;) {
718 _cleanup_free_ char *t = NULL;
719 struct utmpx *u;
720 const char *c;
721 Session *s;
722
723 errno = 0;
724 u = getutxent();
725 if (!u) {
726 if (errno != 0)
727 log_warning_errno(errno, "Failed to read " _PATH_UTMPX ", ignoring: %m");
728 r = 0;
729 break;
730 }
731
732 if (u->ut_type != USER_PROCESS)
733 continue;
734
735 if (!pid_is_valid(u->ut_pid))
736 continue;
737
738 t = strndup(u->ut_line, sizeof(u->ut_line));
739 if (!t) {
740 r = log_oom();
741 break;
742 }
743
744 c = path_startswith(t, "/dev/");
745 if (c) {
746 r = free_and_strdup(&t, c);
747 if (r < 0) {
748 log_oom();
749 break;
750 }
751 }
752
753 if (isempty(t))
754 continue;
755
756 s = hashmap_get(m->sessions_by_leader, PID_TO_PTR(u->ut_pid));
757 if (!s)
758 continue;
759
760 if (s->tty_validity == TTY_FROM_UTMP && !streq_ptr(s->tty, t)) {
761 /* This may happen on multiplexed SSH connection (i.e. 'SSH connection sharing'). In
762 * this case PAM and utmp sessions don't match. In such a case let's invalidate the TTY
763 * information and never acquire it again. */
764
765 s->tty = mfree(s->tty);
766 s->tty_validity = TTY_UTMP_INCONSISTENT;
767 log_debug("Session '%s' has inconsistent TTY information, dropping TTY information.", s->id);
768 continue;
769 }
770
771 /* Never override what we figured out once */
772 if (s->tty || s->tty_validity >= 0)
773 continue;
774
775 s->tty = TAKE_PTR(t);
776 s->tty_validity = TTY_FROM_UTMP;
777 log_debug("Acquired TTY information '%s' from utmp for session '%s'.", s->tty, s->id);
778 }
779
780 endutxent();
781 return r;
782#else
86cf4554 783 return 0;
3d0ef5c7
LP
784#endif
785}
786
787#if ENABLE_UTMP
788static int manager_dispatch_utmp(sd_event_source *s, const struct inotify_event *event, void *userdata) {
789 Manager *m = userdata;
790
791 assert(m);
792
793 /* If there's indication the file itself might have been removed or became otherwise unavailable, then let's
794 * reestablish the watch on whatever there's now. */
795 if ((event->mask & (IN_ATTRIB|IN_DELETE_SELF|IN_MOVE_SELF|IN_Q_OVERFLOW|IN_UNMOUNT)) != 0)
796 manager_connect_utmp(m);
797
798 (void) manager_read_utmp(m);
799 return 0;
800}
801#endif
802
803void manager_connect_utmp(Manager *m) {
804#if ENABLE_UTMP
805 sd_event_source *s = NULL;
806 int r;
807
808 assert(m);
809
810 /* Watch utmp for changes via inotify. We do this to deal with tools such as ssh, which will register the PAM
811 * session early, and acquire a TTY only much later for the connection. Thus during PAM the TTY won't be known
812 * yet. ssh will register itself with utmp when it finally acquired the TTY. Hence, let's make use of this, and
813 * watch utmp for the TTY asynchronously. We use the PAM session's leader PID as key, to find the right entry.
814 *
815 * Yes, relying on utmp is pretty ugly, but it's good enough for informational purposes, as well as idle
816 * detection (which, for tty sessions, relies on the TTY used) */
817
818 r = sd_event_add_inotify(m->event, &s, _PATH_UTMPX, IN_MODIFY|IN_MOVE_SELF|IN_DELETE_SELF|IN_ATTRIB, manager_dispatch_utmp, m);
819 if (r < 0)
820 log_full_errno(r == -ENOENT ? LOG_DEBUG: LOG_WARNING, r, "Failed to create inotify watch on " _PATH_UTMPX ", ignoring: %m");
821 else {
822 r = sd_event_source_set_priority(s, SD_EVENT_PRIORITY_IDLE);
823 if (r < 0)
824 log_warning_errno(r, "Failed to adjust utmp event source priority, ignoring: %m");
825
826 (void) sd_event_source_set_description(s, "utmp");
827 }
828
829 sd_event_source_unref(m->utmp_event_source);
830 m->utmp_event_source = s;
831#endif
832}
833
834void manager_reconnect_utmp(Manager *m) {
835#if ENABLE_UTMP
836 assert(m);
837
838 if (m->utmp_event_source)
839 return;
840
841 manager_connect_utmp(m);
842#endif
843}