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