]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-seat.c
mkdir: append _label to all mkdir() calls that explicitly set the selinux context
[thirdparty/systemd.git] / src / login / logind-seat.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 <assert.h>
23 #include <errno.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/ioctl.h>
27 #include <linux/vt.h>
28 #include <string.h>
29
30 #include "logind-seat.h"
31 #include "logind-acl.h"
32 #include "util.h"
33 #include "mkdir.h"
34 #include "path-util.h"
35
36 Seat *seat_new(Manager *m, const char *id) {
37 Seat *s;
38
39 assert(m);
40 assert(id);
41
42 s = new0(Seat, 1);
43 if (!s)
44 return NULL;
45
46 s->state_file = strappend("/run/systemd/seats/", id);
47 if (!s->state_file) {
48 free(s);
49 return NULL;
50 }
51
52 s->id = path_get_file_name(s->state_file);
53 s->manager = m;
54
55 if (hashmap_put(m->seats, s->id, s) < 0) {
56 free(s->state_file);
57 free(s);
58 return NULL;
59 }
60
61 return s;
62 }
63
64 void seat_free(Seat *s) {
65 assert(s);
66
67 if (s->in_gc_queue)
68 LIST_REMOVE(Seat, gc_queue, s->manager->seat_gc_queue, s);
69
70 while (s->sessions)
71 session_free(s->sessions);
72
73 assert(!s->active);
74
75 while (s->devices)
76 device_free(s->devices);
77
78 hashmap_remove(s->manager->seats, s->id);
79
80 free(s->state_file);
81 free(s);
82 }
83
84 int seat_save(Seat *s) {
85 int r;
86 FILE *f;
87 char *temp_path;
88
89 assert(s);
90
91 if (!s->started)
92 return 0;
93
94 r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0);
95 if (r < 0)
96 goto finish;
97
98 r = fopen_temporary(s->state_file, &f, &temp_path);
99 if (r < 0)
100 goto finish;
101
102 fchmod(fileno(f), 0644);
103
104 fprintf(f,
105 "# This is private data. Do not parse.\n"
106 "IS_VTCONSOLE=%i\n"
107 "CAN_MULTI_SESSION=%i\n",
108 seat_is_vtconsole(s),
109 seat_can_multi_session(s));
110
111 if (s->active) {
112 assert(s->active->user);
113
114 fprintf(f,
115 "ACTIVE=%s\n"
116 "ACTIVE_UID=%lu\n",
117 s->active->id,
118 (unsigned long) s->active->user->uid);
119 }
120
121 if (s->sessions) {
122 Session *i;
123
124 fputs("SESSIONS=", f);
125 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
126 fprintf(f,
127 "%s%c",
128 i->id,
129 i->sessions_by_seat_next ? ' ' : '\n');
130 }
131
132 fputs("UIDS=", f);
133 LIST_FOREACH(sessions_by_seat, i, s->sessions)
134 fprintf(f,
135 "%lu%c",
136 (unsigned long) i->user->uid,
137 i->sessions_by_seat_next ? ' ' : '\n');
138 }
139
140 fflush(f);
141
142 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
143 r = -errno;
144 unlink(s->state_file);
145 unlink(temp_path);
146 }
147
148 fclose(f);
149 free(temp_path);
150
151 finish:
152 if (r < 0)
153 log_error("Failed to save seat data for %s: %s", s->id, strerror(-r));
154
155 return r;
156 }
157
158 int seat_load(Seat *s) {
159 assert(s);
160
161 /* There isn't actually anything to read here ... */
162
163 return 0;
164 }
165
166 static int vt_allocate(int vtnr) {
167 int fd, r;
168 char *p;
169
170 assert(vtnr >= 1);
171
172 if (asprintf(&p, "/dev/tty%i", vtnr) < 0)
173 return -ENOMEM;
174
175 fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
176 free(p);
177
178 r = fd < 0 ? -errno : 0;
179
180 if (fd >= 0)
181 close_nointr_nofail(fd);
182
183 return r;
184 }
185
186 int seat_preallocate_vts(Seat *s) {
187 int r = 0;
188 unsigned i;
189
190 assert(s);
191 assert(s->manager);
192
193 log_debug("Preallocating VTs...");
194
195 if (s->manager->n_autovts <= 0)
196 return 0;
197
198 if (!seat_can_multi_session(s))
199 return 0;
200
201 for (i = 1; i <= s->manager->n_autovts; i++) {
202 int q;
203
204 q = vt_allocate(i);
205 if (q < 0) {
206 log_error("Failed to preallocate VT %i: %s", i, strerror(-q));
207 r = q;
208 }
209 }
210
211 return r;
212 }
213
214 int seat_apply_acls(Seat *s, Session *old_active) {
215 int r;
216
217 assert(s);
218
219 r = devnode_acl_all(s->manager->udev,
220 s->id,
221 false,
222 !!old_active, old_active ? old_active->user->uid : 0,
223 !!s->active, s->active ? s->active->user->uid : 0);
224
225 if (r < 0)
226 log_error("Failed to apply ACLs: %s", strerror(-r));
227
228 return r;
229 }
230
231 int seat_set_active(Seat *s, Session *session) {
232 Session *old_active;
233
234 assert(s);
235 assert(!session || session->seat == s);
236
237 if (session == s->active)
238 return 0;
239
240 old_active = s->active;
241 s->active = session;
242
243 seat_apply_acls(s, old_active);
244
245 if (session && session->started)
246 session_send_changed(session, "Active\0");
247
248 if (!session || session->started)
249 seat_send_changed(s, "ActiveSession\0");
250
251 seat_save(s);
252
253 if (session) {
254 session_save(session);
255 user_save(session->user);
256 }
257
258 if (old_active) {
259 session_save(old_active);
260 user_save(old_active->user);
261 }
262
263 return 0;
264 }
265
266 int seat_active_vt_changed(Seat *s, int vtnr) {
267 Session *i, *new_active = NULL;
268 int r;
269
270 assert(s);
271 assert(vtnr >= 1);
272
273 if (!seat_can_multi_session(s))
274 return -EINVAL;
275
276 log_debug("VT changed to %i", vtnr);
277
278 LIST_FOREACH(sessions_by_seat, i, s->sessions)
279 if (i->vtnr == vtnr) {
280 new_active = i;
281 break;
282 }
283
284 r = seat_set_active(s, new_active);
285 manager_spawn_autovt(s->manager, vtnr);
286
287 return r;
288 }
289
290 int seat_read_active_vt(Seat *s) {
291 char t[64];
292 ssize_t k;
293 int r, vtnr;
294
295 assert(s);
296
297 if (!seat_can_multi_session(s))
298 return 0;
299
300 lseek(s->manager->console_active_fd, SEEK_SET, 0);
301
302 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
303 if (k <= 0) {
304 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
305 return k < 0 ? -errno : -EIO;
306 }
307
308 t[k] = 0;
309 truncate_nl(t);
310
311 if (!startswith(t, "tty")) {
312 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
313 return -EIO;
314 }
315
316 r = safe_atoi(t+3, &vtnr);
317 if (r < 0) {
318 log_error("Failed to parse VT number %s", t+3);
319 return r;
320 }
321
322 if (vtnr <= 0) {
323 log_error("VT number invalid: %s", t+3);
324 return -EIO;
325 }
326
327 return seat_active_vt_changed(s, vtnr);
328 }
329
330 int seat_start(Seat *s) {
331 assert(s);
332
333 if (s->started)
334 return 0;
335
336 log_info("New seat %s.", s->id);
337
338 /* Initialize VT magic stuff */
339 seat_preallocate_vts(s);
340
341 /* Read current VT */
342 seat_read_active_vt(s);
343
344 s->started = true;
345
346 /* Save seat data */
347 seat_save(s);
348
349 seat_send_signal(s, true);
350
351 return 0;
352 }
353
354 int seat_stop(Seat *s) {
355 int r = 0;
356
357 assert(s);
358
359 if (s->started)
360 log_info("Removed seat %s.", s->id);
361
362 seat_stop_sessions(s);
363
364 unlink(s->state_file);
365 seat_add_to_gc_queue(s);
366
367 if (s->started)
368 seat_send_signal(s, false);
369
370 s->started = false;
371
372 return r;
373 }
374
375 int seat_stop_sessions(Seat *s) {
376 Session *session;
377 int r = 0, k;
378
379 assert(s);
380
381 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
382 k = session_stop(session);
383 if (k < 0)
384 r = k;
385 }
386
387 return r;
388 }
389
390 int seat_attach_session(Seat *s, Session *session) {
391 assert(s);
392 assert(session);
393 assert(!session->seat);
394
395 session->seat = s;
396 LIST_PREPEND(Session, sessions_by_seat, s->sessions, session);
397
398 seat_send_changed(s, "Sessions\0");
399
400 /* Note that even if a seat is not multi-session capable it
401 * still might have multiple sessions on it since old, dead
402 * sessions might continue to be tracked until all their
403 * processes are gone. The most recently added session
404 * (i.e. the first in s->sessions) is the one that matters. */
405
406 if (!seat_can_multi_session(s))
407 seat_set_active(s, session);
408
409 return 0;
410 }
411
412 bool seat_is_vtconsole(Seat *s) {
413 assert(s);
414
415 return s->manager->vtconsole == s;
416 }
417
418 bool seat_can_multi_session(Seat *s) {
419 assert(s);
420
421 if (!seat_is_vtconsole(s))
422 return false;
423
424 /* If we can't watch which VT is in the foreground, we don't
425 * support VT switching */
426
427 return s->manager->console_active_fd >= 0;
428 }
429
430 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
431 Session *session;
432 bool idle_hint = true;
433 dual_timestamp ts = { 0, 0 };
434
435 assert(s);
436
437 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
438 dual_timestamp k;
439 int ih;
440
441 ih = session_get_idle_hint(session, &k);
442 if (ih < 0)
443 return ih;
444
445 if (!ih) {
446 if (!idle_hint) {
447 if (k.monotonic < ts.monotonic)
448 ts = k;
449 } else {
450 idle_hint = false;
451 ts = k;
452 }
453 } else if (idle_hint) {
454
455 if (k.monotonic > ts.monotonic)
456 ts = k;
457 }
458 }
459
460 if (t)
461 *t = ts;
462
463 return idle_hint;
464 }
465
466 int seat_check_gc(Seat *s, bool drop_not_started) {
467 assert(s);
468
469 if (drop_not_started && !s->started)
470 return 0;
471
472 if (seat_is_vtconsole(s))
473 return 1;
474
475 return !!s->devices;
476 }
477
478 void seat_add_to_gc_queue(Seat *s) {
479 assert(s);
480
481 if (s->in_gc_queue)
482 return;
483
484 LIST_PREPEND(Seat, gc_queue, s->manager->seat_gc_queue, s);
485 s->in_gc_queue = true;
486 }
487
488 static bool seat_name_valid_char(char c) {
489 return
490 (c >= 'a' && c <= 'z') ||
491 (c >= 'A' && c <= 'Z') ||
492 (c >= '0' && c <= '9') ||
493 c == '-' ||
494 c == '_';
495 }
496
497 bool seat_name_is_valid(const char *name) {
498 const char *p;
499
500 assert(name);
501
502 if (!startswith(name, "seat"))
503 return false;
504
505 if (!name[4])
506 return false;
507
508 for (p = name; *p; p++)
509 if (!seat_name_valid_char(*p))
510 return false;
511
512 if (strlen(name) > 255)
513 return false;
514
515 return true;
516 }