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