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