]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-seat.c
login: revert lazy session-activation on non-VT seats
[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 "sd-id128.h"
31 #include "sd-messages.h"
32 #include "logind-seat.h"
33 #include "logind-acl.h"
34 #include "util.h"
35 #include "mkdir.h"
36 #include "path-util.h"
37
38 Seat *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
48 s->state_file = strappend("/run/systemd/seats/", id);
49 if (!s->state_file) {
50 free(s);
51 return NULL;
52 }
53
54 s->id = path_get_file_name(s->state_file);
55 s->manager = m;
56
57 if (hashmap_put(m->seats, s->id, s) < 0) {
58 free(s->state_file);
59 free(s);
60 return NULL;
61 }
62
63 return s;
64 }
65
66 void seat_free(Seat *s) {
67 assert(s);
68
69 if (s->in_gc_queue)
70 LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s);
71
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
82 free(s->state_file);
83 free(s);
84 }
85
86 int seat_save(Seat *s) {
87 _cleanup_free_ char *temp_path = NULL;
88 _cleanup_fclose_ FILE *f = NULL;
89 int r;
90
91 assert(s);
92
93 if (!s->started)
94 return 0;
95
96 r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0);
97 if (r < 0)
98 goto finish;
99
100 r = fopen_temporary(s->state_file, &f, &temp_path);
101 if (r < 0)
102 goto finish;
103
104 fchmod(fileno(f), 0644);
105
106 fprintf(f,
107 "# This is private data. Do not parse.\n"
108 "IS_SEAT0=%i\n"
109 "CAN_MULTI_SESSION=%i\n"
110 "CAN_TTY=%i\n"
111 "CAN_GRAPHICAL=%i\n",
112 seat_is_seat0(s),
113 seat_can_multi_session(s),
114 seat_can_tty(s),
115 seat_can_graphical(s));
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;
129
130 fputs("SESSIONS=", f);
131 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
132 fprintf(f,
133 "%s%c",
134 i->id,
135 i->sessions_by_seat_next ? ' ' : '\n');
136 }
137
138 fputs("UIDS=", f);
139 LIST_FOREACH(sessions_by_seat, i, s->sessions)
140 fprintf(f,
141 "%lu%c",
142 (unsigned long) i->user->uid,
143 i->sessions_by_seat_next ? ' ' : '\n');
144 }
145
146 fflush(f);
147
148 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
149 r = -errno;
150 unlink(s->state_file);
151 unlink(temp_path);
152 }
153
154 finish:
155 if (r < 0)
156 log_error("Failed to save seat data for %s: %s", s->id, strerror(-r));
157
158 return r;
159 }
160
161 int seat_load(Seat *s) {
162 assert(s);
163
164 /* There isn't actually anything to read here ... */
165
166 return 0;
167 }
168
169 static int vt_allocate(int vtnr) {
170 _cleanup_free_ char *p = NULL;
171 _cleanup_close_ int fd = -1;
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 if (fd < 0)
180 return -errno;
181
182 return 0;
183 }
184
185 int seat_preallocate_vts(Seat *s) {
186 int r = 0;
187 unsigned i;
188
189 assert(s);
190 assert(s->manager);
191
192 log_debug("Preallocating VTs...");
193
194 if (s->manager->n_autovts <= 0)
195 return 0;
196
197 if (!seat_has_vts(s))
198 return 0;
199
200 for (i = 1; i <= s->manager->n_autovts; i++) {
201 int q;
202
203 q = vt_allocate(i);
204 if (q < 0) {
205 log_error("Failed to preallocate VT %i: %s", i, strerror(-q));
206 r = q;
207 }
208 }
209
210 return r;
211 }
212
213 int seat_apply_acls(Seat *s, Session *old_active) {
214 int r;
215
216 assert(s);
217
218 r = devnode_acl_all(s->manager->udev,
219 s->id,
220 false,
221 !!old_active, old_active ? old_active->user->uid : 0,
222 !!s->active, s->active ? s->active->user->uid : 0);
223
224 if (r < 0)
225 log_error("Failed to apply ACLs: %s", strerror(-r));
226
227 return r;
228 }
229
230 int seat_set_active(Seat *s, Session *session) {
231 Session *old_active;
232
233 assert(s);
234 assert(!session || session->seat == s);
235
236 if (session == s->active)
237 return 0;
238
239 old_active = s->active;
240 s->active = session;
241
242 if (old_active) {
243 session_device_pause_all(old_active);
244 session_send_changed(old_active, "Active", NULL);
245 }
246
247 seat_apply_acls(s, old_active);
248
249 if (session && session->started) {
250 session_send_changed(session, "Active", NULL);
251 session_device_resume_all(session);
252 }
253
254 if (!session || session->started)
255 seat_send_changed(s, "ActiveSession", NULL);
256
257 seat_save(s);
258
259 if (session) {
260 session_save(session);
261 user_save(session->user);
262 }
263
264 if (old_active) {
265 session_save(old_active);
266 if (!session || session->user != old_active->user)
267 user_save(old_active->user);
268 }
269
270 return 0;
271 }
272
273 int seat_active_vt_changed(Seat *s, int vtnr) {
274 Session *i, *new_active = NULL;
275 int r;
276
277 assert(s);
278 assert(vtnr >= 1);
279
280 if (!seat_has_vts(s))
281 return -EINVAL;
282
283 log_debug("VT changed to %i", vtnr);
284
285 LIST_FOREACH(sessions_by_seat, i, s->sessions)
286 if (i->vtnr == vtnr) {
287 new_active = i;
288 break;
289 }
290
291 r = seat_set_active(s, new_active);
292 manager_spawn_autovt(s->manager, vtnr);
293
294 return r;
295 }
296
297 int seat_read_active_vt(Seat *s) {
298 char t[64];
299 ssize_t k;
300 int r, vtnr;
301
302 assert(s);
303
304 if (!seat_has_vts(s))
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
337 int seat_start(Seat *s) {
338 assert(s);
339
340 if (s->started)
341 return 0;
342
343 log_struct(LOG_INFO,
344 MESSAGE_ID(SD_MESSAGE_SEAT_START),
345 "SEAT_ID=%s", s->id,
346 "MESSAGE=New seat %s.", s->id,
347 NULL);
348
349 /* Initialize VT magic stuff */
350 seat_preallocate_vts(s);
351
352 /* Read current VT */
353 seat_read_active_vt(s);
354
355 s->started = true;
356
357 /* Save seat data */
358 seat_save(s);
359
360 seat_send_signal(s, true);
361
362 return 0;
363 }
364
365 int seat_stop(Seat *s) {
366 int r = 0;
367
368 assert(s);
369
370 if (s->started)
371 log_struct(LOG_INFO,
372 MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
373 "SEAT_ID=%s", s->id,
374 "MESSAGE=Removed seat %s.", s->id,
375 NULL);
376
377 seat_stop_sessions(s);
378
379 unlink(s->state_file);
380 seat_add_to_gc_queue(s);
381
382 if (s->started)
383 seat_send_signal(s, false);
384
385 s->started = false;
386
387 return r;
388 }
389
390 int seat_stop_sessions(Seat *s) {
391 Session *session;
392 int r = 0, k;
393
394 assert(s);
395
396 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
397 k = session_stop(session);
398 if (k < 0)
399 r = k;
400 }
401
402 return r;
403 }
404
405 int seat_attach_session(Seat *s, Session *session) {
406 assert(s);
407 assert(session);
408 assert(!session->seat);
409
410 session->seat = s;
411 LIST_PREPEND(sessions_by_seat, s->sessions, session);
412
413 seat_send_changed(s, "Sessions", NULL);
414
415 /* On seats with VTs, the VT logic defines which session is active. On
416 * seats without VTs, we automatically activate new sessions. */
417 if (!seat_has_vts(s))
418 seat_set_active(s, session);
419
420 return 0;
421 }
422
423 void seat_complete_switch(Seat *s) {
424 Session *session;
425
426 assert(s);
427
428 /* if no session-switch is pending or if it got canceled, do nothing */
429 if (!s->pending_switch)
430 return;
431
432 session = s->pending_switch;
433 s->pending_switch = NULL;
434
435 seat_set_active(s, session);
436 }
437
438 bool seat_has_vts(Seat *s) {
439 assert(s);
440
441 return seat_is_seat0(s) && s->manager->console_active_fd >= 0;
442 }
443
444 bool seat_is_seat0(Seat *s) {
445 assert(s);
446
447 return s->manager->seat0 == s;
448 }
449
450 bool seat_can_multi_session(Seat *s) {
451 assert(s);
452
453 return seat_has_vts(s);
454 }
455
456 bool seat_can_tty(Seat *s) {
457 assert(s);
458
459 return seat_has_vts(s);
460 }
461
462 bool seat_has_master_device(Seat *s) {
463 assert(s);
464
465 /* device list is ordered by "master" flag */
466 return !!s->devices && s->devices->master;
467 }
468
469 bool seat_can_graphical(Seat *s) {
470 assert(s);
471
472 return seat_has_master_device(s);
473 }
474
475 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
476 Session *session;
477 bool idle_hint = true;
478 dual_timestamp ts = { 0, 0 };
479
480 assert(s);
481
482 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
483 dual_timestamp k;
484 int ih;
485
486 ih = session_get_idle_hint(session, &k);
487 if (ih < 0)
488 return ih;
489
490 if (!ih) {
491 if (!idle_hint) {
492 if (k.monotonic > ts.monotonic)
493 ts = k;
494 } else {
495 idle_hint = false;
496 ts = k;
497 }
498 } else if (idle_hint) {
499
500 if (k.monotonic > ts.monotonic)
501 ts = k;
502 }
503 }
504
505 if (t)
506 *t = ts;
507
508 return idle_hint;
509 }
510
511 bool seat_check_gc(Seat *s, bool drop_not_started) {
512 assert(s);
513
514 if (drop_not_started && !s->started)
515 return false;
516
517 if (seat_is_seat0(s))
518 return true;
519
520 return seat_has_master_device(s);
521 }
522
523 void seat_add_to_gc_queue(Seat *s) {
524 assert(s);
525
526 if (s->in_gc_queue)
527 return;
528
529 LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s);
530 s->in_gc_queue = true;
531 }
532
533 static bool seat_name_valid_char(char c) {
534 return
535 (c >= 'a' && c <= 'z') ||
536 (c >= 'A' && c <= 'Z') ||
537 (c >= '0' && c <= '9') ||
538 c == '-' ||
539 c == '_';
540 }
541
542 bool seat_name_is_valid(const char *name) {
543 const char *p;
544
545 assert(name);
546
547 if (!startswith(name, "seat"))
548 return false;
549
550 if (!name[4])
551 return false;
552
553 for (p = name; *p; p++)
554 if (!seat_name_valid_char(*p))
555 return false;
556
557 if (strlen(name) > 255)
558 return false;
559
560 return true;
561 }