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