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