Branch data 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 : : }
|