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