]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-core.c
util-lib: split out fd-related operations into fd-util.[ch]
[thirdparty/systemd.git] / src / login / logind-core.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2011 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/types.h>
23 #include <sys/ioctl.h>
24 #include <fcntl.h>
25 #include <pwd.h>
26 #include <linux/vt.h>
27
28 #include "bus-error.h"
29 #include "bus-util.h"
30 #include "cgroup-util.h"
31 #include "fd-util.h"
32 #include "logind.h"
33 #include "strv.h"
34 #include "terminal-util.h"
35 #include "udev-util.h"
36
37 int manager_add_device(Manager *m, const char *sysfs, bool master, Device **_device) {
38 Device *d;
39
40 assert(m);
41 assert(sysfs);
42
43 d = hashmap_get(m->devices, sysfs);
44 if (d)
45 /* we support adding master-flags, but not removing them */
46 d->master = d->master || master;
47 else {
48 d = device_new(m, sysfs, master);
49 if (!d)
50 return -ENOMEM;
51 }
52
53 if (_device)
54 *_device = d;
55
56 return 0;
57 }
58
59 int manager_add_seat(Manager *m, const char *id, Seat **_seat) {
60 Seat *s;
61
62 assert(m);
63 assert(id);
64
65 s = hashmap_get(m->seats, id);
66 if (!s) {
67 s = seat_new(m, id);
68 if (!s)
69 return -ENOMEM;
70 }
71
72 if (_seat)
73 *_seat = s;
74
75 return 0;
76 }
77
78 int manager_add_session(Manager *m, const char *id, Session **_session) {
79 Session *s;
80
81 assert(m);
82 assert(id);
83
84 s = hashmap_get(m->sessions, id);
85 if (!s) {
86 s = session_new(m, id);
87 if (!s)
88 return -ENOMEM;
89 }
90
91 if (_session)
92 *_session = s;
93
94 return 0;
95 }
96
97 int manager_add_user(Manager *m, uid_t uid, gid_t gid, const char *name, User **_user) {
98 User *u;
99
100 assert(m);
101 assert(name);
102
103 u = hashmap_get(m->users, UID_TO_PTR(uid));
104 if (!u) {
105 u = user_new(m, uid, gid, name);
106 if (!u)
107 return -ENOMEM;
108 }
109
110 if (_user)
111 *_user = u;
112
113 return 0;
114 }
115
116 int manager_add_user_by_name(Manager *m, const char *name, User **_user) {
117 uid_t uid;
118 gid_t gid;
119 int r;
120
121 assert(m);
122 assert(name);
123
124 r = get_user_creds(&name, &uid, &gid, NULL, NULL);
125 if (r < 0)
126 return r;
127
128 return manager_add_user(m, uid, gid, name, _user);
129 }
130
131 int manager_add_user_by_uid(Manager *m, uid_t uid, User **_user) {
132 struct passwd *p;
133
134 assert(m);
135
136 errno = 0;
137 p = getpwuid(uid);
138 if (!p)
139 return errno ? -errno : -ENOENT;
140
141 return manager_add_user(m, uid, p->pw_gid, p->pw_name, _user);
142 }
143
144 int manager_add_inhibitor(Manager *m, const char* id, Inhibitor **_inhibitor) {
145 Inhibitor *i;
146
147 assert(m);
148 assert(id);
149
150 i = hashmap_get(m->inhibitors, id);
151 if (i) {
152 if (_inhibitor)
153 *_inhibitor = i;
154
155 return 0;
156 }
157
158 i = inhibitor_new(m, id);
159 if (!i)
160 return -ENOMEM;
161
162 if (_inhibitor)
163 *_inhibitor = i;
164
165 return 0;
166 }
167
168 int manager_add_button(Manager *m, const char *name, Button **_button) {
169 Button *b;
170
171 assert(m);
172 assert(name);
173
174 b = hashmap_get(m->buttons, name);
175 if (!b) {
176 b = button_new(m, name);
177 if (!b)
178 return -ENOMEM;
179 }
180
181 if (_button)
182 *_button = b;
183
184 return 0;
185 }
186
187 int manager_process_seat_device(Manager *m, struct udev_device *d) {
188 Device *device;
189 int r;
190
191 assert(m);
192
193 if (streq_ptr(udev_device_get_action(d), "remove")) {
194
195 device = hashmap_get(m->devices, udev_device_get_syspath(d));
196 if (!device)
197 return 0;
198
199 seat_add_to_gc_queue(device->seat);
200 device_free(device);
201
202 } else {
203 const char *sn;
204 Seat *seat = NULL;
205 bool master;
206
207 sn = udev_device_get_property_value(d, "ID_SEAT");
208 if (isempty(sn))
209 sn = "seat0";
210
211 if (!seat_name_is_valid(sn)) {
212 log_warning("Device with invalid seat name %s found, ignoring.", sn);
213 return 0;
214 }
215
216 seat = hashmap_get(m->seats, sn);
217 master = udev_device_has_tag(d, "master-of-seat");
218
219 /* Ignore non-master devices for unknown seats */
220 if (!master && !seat)
221 return 0;
222
223 r = manager_add_device(m, udev_device_get_syspath(d), master, &device);
224 if (r < 0)
225 return r;
226
227 if (!seat) {
228 r = manager_add_seat(m, sn, &seat);
229 if (r < 0) {
230 if (!device->seat)
231 device_free(device);
232
233 return r;
234 }
235 }
236
237 device_attach(device, seat);
238 seat_start(seat);
239 }
240
241 return 0;
242 }
243
244 int manager_process_button_device(Manager *m, struct udev_device *d) {
245 Button *b;
246
247 int r;
248
249 assert(m);
250
251 if (streq_ptr(udev_device_get_action(d), "remove")) {
252
253 b = hashmap_get(m->buttons, udev_device_get_sysname(d));
254 if (!b)
255 return 0;
256
257 button_free(b);
258
259 } else {
260 const char *sn;
261
262 r = manager_add_button(m, udev_device_get_sysname(d), &b);
263 if (r < 0)
264 return r;
265
266 sn = udev_device_get_property_value(d, "ID_SEAT");
267 if (isempty(sn))
268 sn = "seat0";
269
270 button_set_seat(b, sn);
271 button_open(b);
272 }
273
274 return 0;
275 }
276
277 int manager_get_session_by_pid(Manager *m, pid_t pid, Session **session) {
278 _cleanup_free_ char *unit = NULL;
279 Session *s;
280 int r;
281
282 assert(m);
283
284 if (pid < 1)
285 return -EINVAL;
286
287 r = cg_pid_get_unit(pid, &unit);
288 if (r < 0)
289 return 0;
290
291 s = hashmap_get(m->session_units, unit);
292 if (!s)
293 return 0;
294
295 if (session)
296 *session = s;
297 return 1;
298 }
299
300 int manager_get_user_by_pid(Manager *m, pid_t pid, User **user) {
301 _cleanup_free_ char *unit = NULL;
302 User *u;
303 int r;
304
305 assert(m);
306 assert(user);
307
308 if (pid < 1)
309 return -EINVAL;
310
311 r = cg_pid_get_slice(pid, &unit);
312 if (r < 0)
313 return 0;
314
315 u = hashmap_get(m->user_units, unit);
316 if (!u)
317 return 0;
318
319 *user = u;
320 return 1;
321 }
322
323 int manager_get_idle_hint(Manager *m, dual_timestamp *t) {
324 Session *s;
325 bool idle_hint;
326 dual_timestamp ts = DUAL_TIMESTAMP_NULL;
327 Iterator i;
328
329 assert(m);
330
331 idle_hint = !manager_is_inhibited(m, INHIBIT_IDLE, INHIBIT_BLOCK, t, false, false, 0, NULL);
332
333 HASHMAP_FOREACH(s, m->sessions, i) {
334 dual_timestamp k;
335 int ih;
336
337 ih = session_get_idle_hint(s, &k);
338 if (ih < 0)
339 return ih;
340
341 if (!ih) {
342 if (!idle_hint) {
343 if (k.monotonic < ts.monotonic)
344 ts = k;
345 } else {
346 idle_hint = false;
347 ts = k;
348 }
349 } else if (idle_hint) {
350
351 if (k.monotonic > ts.monotonic)
352 ts = k;
353 }
354 }
355
356 if (t)
357 *t = ts;
358
359 return idle_hint;
360 }
361
362 bool manager_shall_kill(Manager *m, const char *user) {
363 assert(m);
364 assert(user);
365
366 if (!m->kill_user_processes)
367 return false;
368
369 if (strv_contains(m->kill_exclude_users, user))
370 return false;
371
372 if (strv_isempty(m->kill_only_users))
373 return true;
374
375 return strv_contains(m->kill_only_users, user);
376 }
377
378 static int vt_is_busy(unsigned int vtnr) {
379 struct vt_stat vt_stat;
380 int r = 0;
381 _cleanup_close_ int fd;
382
383 assert(vtnr >= 1);
384
385 /* We explicitly open /dev/tty1 here instead of /dev/tty0. If
386 * we'd open the latter we'd open the foreground tty which
387 * hence would be unconditionally busy. By opening /dev/tty1
388 * we avoid this. Since tty1 is special and needs to be an
389 * explicitly loaded getty or DM this is safe. */
390
391 fd = open_terminal("/dev/tty1", O_RDWR|O_NOCTTY|O_CLOEXEC);
392 if (fd < 0)
393 return -errno;
394
395 if (ioctl(fd, VT_GETSTATE, &vt_stat) < 0)
396 r = -errno;
397 else
398 r = !!(vt_stat.v_state & (1 << vtnr));
399
400 return r;
401 }
402
403 int manager_spawn_autovt(Manager *m, unsigned int vtnr) {
404 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
405 char name[sizeof("autovt@tty.service") + DECIMAL_STR_MAX(unsigned int)];
406 int r;
407
408 assert(m);
409 assert(vtnr >= 1);
410
411 if (vtnr > m->n_autovts &&
412 vtnr != m->reserve_vt)
413 return 0;
414
415 if (vtnr != m->reserve_vt) {
416 /* If this is the reserved TTY, we'll start the getty
417 * on it in any case, but otherwise only if it is not
418 * busy. */
419
420 r = vt_is_busy(vtnr);
421 if (r < 0)
422 return r;
423 else if (r > 0)
424 return -EBUSY;
425 }
426
427 snprintf(name, sizeof(name), "autovt@tty%u.service", vtnr);
428 r = sd_bus_call_method(
429 m->bus,
430 "org.freedesktop.systemd1",
431 "/org/freedesktop/systemd1",
432 "org.freedesktop.systemd1.Manager",
433 "StartUnit",
434 &error,
435 NULL,
436 "ss", name, "fail");
437 if (r < 0)
438 log_error("Failed to start %s: %s", name, bus_error_message(&error, r));
439
440 return r;
441 }
442
443 static bool manager_is_docked(Manager *m) {
444 Iterator i;
445 Button *b;
446
447 HASHMAP_FOREACH(b, m->buttons, i)
448 if (b->docked)
449 return true;
450
451 return false;
452 }
453
454 static int manager_count_external_displays(Manager *m) {
455 _cleanup_udev_enumerate_unref_ struct udev_enumerate *e = NULL;
456 struct udev_list_entry *item = NULL, *first = NULL;
457 int r;
458 int n = 0;
459
460 e = udev_enumerate_new(m->udev);
461 if (!e)
462 return -ENOMEM;
463
464 r = udev_enumerate_add_match_subsystem(e, "drm");
465 if (r < 0)
466 return r;
467
468 r = udev_enumerate_scan_devices(e);
469 if (r < 0)
470 return r;
471
472 first = udev_enumerate_get_list_entry(e);
473 udev_list_entry_foreach(item, first) {
474 _cleanup_udev_device_unref_ struct udev_device *d = NULL;
475 struct udev_device *p;
476 const char *status, *enabled, *dash, *nn, *i;
477 bool external = false;
478
479 d = udev_device_new_from_syspath(m->udev, udev_list_entry_get_name(item));
480 if (!d)
481 return -ENOMEM;
482
483 p = udev_device_get_parent(d);
484 if (!p)
485 continue;
486
487 /* If the parent shares the same subsystem as the
488 * device we are looking at then it is a connector,
489 * which is what we are interested in. */
490 if (!streq_ptr(udev_device_get_subsystem(p), "drm"))
491 continue;
492
493 nn = udev_device_get_sysname(d);
494 if (!nn)
495 continue;
496
497 /* Ignore internal displays: the type is encoded in
498 * the sysfs name, as the second dash seperated item
499 * (the first is the card name, the last the connector
500 * number). We implement a whitelist of external
501 * displays here, rather than a whitelist, to ensure
502 * we don't block suspends too eagerly. */
503 dash = strchr(nn, '-');
504 if (!dash)
505 continue;
506
507 dash++;
508 FOREACH_STRING(i, "VGA-", "DVI-I-", "DVI-D-", "DVI-A-"
509 "Composite-", "SVIDEO-", "Component-",
510 "DIN-", "DP-", "HDMI-A-", "HDMI-B-", "TV-") {
511
512 if (startswith(dash, i)) {
513 external = true;
514 break;
515 }
516 }
517 if (!external)
518 continue;
519
520 /* Ignore ports that are not enabled */
521 enabled = udev_device_get_sysattr_value(d, "enabled");
522 if (!enabled)
523 continue;
524 if (!streq_ptr(enabled, "enabled"))
525 continue;
526
527 /* We count any connector which is not explicitly
528 * "disconnected" as connected. */
529 status = udev_device_get_sysattr_value(d, "status");
530 if (!streq_ptr(status, "disconnected"))
531 n++;
532 }
533
534 return n;
535 }
536
537 bool manager_is_docked_or_external_displays(Manager *m) {
538 int n;
539
540 /* If we are docked don't react to lid closing */
541 if (manager_is_docked(m)) {
542 log_debug("System is docked.");
543 return true;
544 }
545
546 /* If we have more than one display connected,
547 * assume that we are docked. */
548 n = manager_count_external_displays(m);
549 if (n < 0)
550 log_warning_errno(n, "Display counting failed: %m");
551 else if (n >= 1) {
552 log_debug("External (%i) displays connected.", n);
553 return true;
554 }
555
556 return false;
557 }