]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-seat.c
remove unused includes
[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 <errno.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <string.h>
26
27 #include "sd-messages.h"
28 #include "logind-seat.h"
29 #include "logind-acl.h"
30 #include "util.h"
31 #include "mkdir.h"
32
33 Seat *seat_new(Manager *m, const char *id) {
34 Seat *s;
35
36 assert(m);
37 assert(id);
38
39 s = new0(Seat, 1);
40 if (!s)
41 return NULL;
42
43 s->state_file = strappend("/run/systemd/seats/", id);
44 if (!s->state_file) {
45 free(s);
46 return NULL;
47 }
48
49 s->id = basename(s->state_file);
50 s->manager = m;
51
52 if (hashmap_put(m->seats, s->id, s) < 0) {
53 free(s->state_file);
54 free(s);
55 return NULL;
56 }
57
58 return s;
59 }
60
61 void seat_free(Seat *s) {
62 assert(s);
63
64 if (s->in_gc_queue)
65 LIST_REMOVE(gc_queue, s->manager->seat_gc_queue, s);
66
67 while (s->sessions)
68 session_free(s->sessions);
69
70 assert(!s->active);
71
72 while (s->devices)
73 device_free(s->devices);
74
75 hashmap_remove(s->manager->seats, s->id);
76
77 free(s->positions);
78 free(s->state_file);
79 free(s);
80 }
81
82 int seat_save(Seat *s) {
83 _cleanup_free_ char *temp_path = NULL;
84 _cleanup_fclose_ FILE *f = NULL;
85 int r;
86
87 assert(s);
88
89 if (!s->started)
90 return 0;
91
92 r = mkdir_safe_label("/run/systemd/seats", 0755, 0, 0);
93 if (r < 0)
94 goto finish;
95
96 r = fopen_temporary(s->state_file, &f, &temp_path);
97 if (r < 0)
98 goto finish;
99
100 fchmod(fileno(f), 0644);
101
102 fprintf(f,
103 "# This is private data. Do not parse.\n"
104 "IS_SEAT0=%i\n"
105 "CAN_MULTI_SESSION=%i\n"
106 "CAN_TTY=%i\n"
107 "CAN_GRAPHICAL=%i\n",
108 seat_is_seat0(s),
109 seat_can_multi_session(s),
110 seat_can_tty(s),
111 seat_can_graphical(s));
112
113 if (s->active) {
114 assert(s->active->user);
115
116 fprintf(f,
117 "ACTIVE=%s\n"
118 "ACTIVE_UID="UID_FMT"\n",
119 s->active->id,
120 s->active->user->uid);
121 }
122
123 if (s->sessions) {
124 Session *i;
125
126 fputs("SESSIONS=", f);
127 LIST_FOREACH(sessions_by_seat, i, s->sessions) {
128 fprintf(f,
129 "%s%c",
130 i->id,
131 i->sessions_by_seat_next ? ' ' : '\n');
132 }
133
134 fputs("UIDS=", f);
135 LIST_FOREACH(sessions_by_seat, i, s->sessions)
136 fprintf(f,
137 UID_FMT"%c",
138 i->user->uid,
139 i->sessions_by_seat_next ? ' ' : '\n');
140 }
141
142 fflush(f);
143
144 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
145 r = -errno;
146 unlink(s->state_file);
147 unlink(temp_path);
148 }
149
150 finish:
151 if (r < 0)
152 log_error_errno(r, "Failed to save seat data %s: %m", s->state_file);
153
154 return r;
155 }
156
157 int seat_load(Seat *s) {
158 assert(s);
159
160 /* There isn't actually anything to read here ... */
161
162 return 0;
163 }
164
165 static int vt_allocate(unsigned int vtnr) {
166 char p[sizeof("/dev/tty") + DECIMAL_STR_MAX(unsigned int)];
167 _cleanup_close_ int fd = -1;
168
169 assert(vtnr >= 1);
170
171 snprintf(p, sizeof(p), "/dev/tty%u", vtnr);
172 fd = open_terminal(p, O_RDWR|O_NOCTTY|O_CLOEXEC);
173 if (fd < 0)
174 return -errno;
175
176 return 0;
177 }
178
179 int seat_preallocate_vts(Seat *s) {
180 int r = 0;
181 unsigned i;
182
183 assert(s);
184 assert(s->manager);
185
186 log_debug("Preallocating VTs...");
187
188 if (s->manager->n_autovts <= 0)
189 return 0;
190
191 if (!seat_has_vts(s))
192 return 0;
193
194 for (i = 1; i <= s->manager->n_autovts; i++) {
195 int q;
196
197 q = vt_allocate(i);
198 if (q < 0) {
199 log_error_errno(q, "Failed to preallocate VT %u: %m", i);
200 r = q;
201 }
202 }
203
204 return r;
205 }
206
207 int seat_apply_acls(Seat *s, Session *old_active) {
208 int r;
209
210 assert(s);
211
212 r = devnode_acl_all(s->manager->udev,
213 s->id,
214 false,
215 !!old_active, old_active ? old_active->user->uid : 0,
216 !!s->active, s->active ? s->active->user->uid : 0);
217
218 if (r < 0)
219 log_error_errno(r, "Failed to apply ACLs: %m");
220
221 return r;
222 }
223
224 int seat_set_active(Seat *s, Session *session) {
225 Session *old_active;
226
227 assert(s);
228 assert(!session || session->seat == s);
229
230 if (session == s->active)
231 return 0;
232
233 old_active = s->active;
234 s->active = session;
235
236 if (old_active) {
237 session_device_pause_all(old_active);
238 session_send_changed(old_active, "Active", NULL);
239 }
240
241 seat_apply_acls(s, old_active);
242
243 if (session && session->started) {
244 session_send_changed(session, "Active", NULL);
245 session_device_resume_all(session);
246 }
247
248 if (!session || session->started)
249 seat_send_changed(s, "ActiveSession", NULL);
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 if (!session || session->user != old_active->user)
261 user_save(old_active->user);
262 }
263
264 return 0;
265 }
266
267 int seat_switch_to(Seat *s, unsigned int num) {
268 /* Public session positions skip 0 (there is only F1-F12). Maybe it
269 * will get reassigned in the future, so return error for now. */
270 if (!num)
271 return -EINVAL;
272
273 if (num >= s->position_count || !s->positions[num]) {
274 /* allow switching to unused VTs to trigger auto-activate */
275 if (seat_has_vts(s) && num < 64)
276 return chvt(num);
277
278 return -EINVAL;
279 }
280
281 return session_activate(s->positions[num]);
282 }
283
284 int seat_switch_to_next(Seat *s) {
285 unsigned int start, i;
286
287 if (!s->position_count)
288 return -EINVAL;
289
290 start = 1;
291 if (s->active && s->active->pos > 0)
292 start = s->active->pos;
293
294 for (i = start + 1; i < s->position_count; ++i)
295 if (s->positions[i])
296 return session_activate(s->positions[i]);
297
298 for (i = 1; i < start; ++i)
299 if (s->positions[i])
300 return session_activate(s->positions[i]);
301
302 return -EINVAL;
303 }
304
305 int seat_switch_to_previous(Seat *s) {
306 unsigned int start, i;
307
308 if (!s->position_count)
309 return -EINVAL;
310
311 start = 1;
312 if (s->active && s->active->pos > 0)
313 start = s->active->pos;
314
315 for (i = start - 1; i > 0; --i)
316 if (s->positions[i])
317 return session_activate(s->positions[i]);
318
319 for (i = s->position_count - 1; i > start; --i)
320 if (s->positions[i])
321 return session_activate(s->positions[i]);
322
323 return -EINVAL;
324 }
325
326 int seat_active_vt_changed(Seat *s, unsigned int vtnr) {
327 Session *i, *new_active = NULL;
328 int r;
329
330 assert(s);
331 assert(vtnr >= 1);
332
333 if (!seat_has_vts(s))
334 return -EINVAL;
335
336 log_debug("VT changed to %u", vtnr);
337
338 /* we might have earlier closing sessions on the same VT, so try to
339 * find a running one first */
340 LIST_FOREACH(sessions_by_seat, i, s->sessions)
341 if (i->vtnr == vtnr && !i->stopping) {
342 new_active = i;
343 break;
344 }
345
346 if (!new_active) {
347 /* no running one? then we can't decide which one is the
348 * active one, let the first one win */
349 LIST_FOREACH(sessions_by_seat, i, s->sessions)
350 if (i->vtnr == vtnr) {
351 new_active = i;
352 break;
353 }
354 }
355
356 r = seat_set_active(s, new_active);
357 manager_spawn_autovt(s->manager, vtnr);
358
359 return r;
360 }
361
362 int seat_read_active_vt(Seat *s) {
363 char t[64];
364 ssize_t k;
365 unsigned int vtnr;
366 int r;
367
368 assert(s);
369
370 if (!seat_has_vts(s))
371 return 0;
372
373 lseek(s->manager->console_active_fd, SEEK_SET, 0);
374
375 k = read(s->manager->console_active_fd, t, sizeof(t)-1);
376 if (k <= 0) {
377 log_error("Failed to read current console: %s", k < 0 ? strerror(-errno) : "EOF");
378 return k < 0 ? -errno : -EIO;
379 }
380
381 t[k] = 0;
382 truncate_nl(t);
383
384 if (!startswith(t, "tty")) {
385 log_error("Hm, /sys/class/tty/tty0/active is badly formatted.");
386 return -EIO;
387 }
388
389 r = safe_atou(t+3, &vtnr);
390 if (r < 0) {
391 log_error("Failed to parse VT number %s", t+3);
392 return r;
393 }
394
395 if (!vtnr) {
396 log_error("VT number invalid: %s", t+3);
397 return -EIO;
398 }
399
400 return seat_active_vt_changed(s, vtnr);
401 }
402
403 int seat_start(Seat *s) {
404 assert(s);
405
406 if (s->started)
407 return 0;
408
409 log_struct(LOG_INFO,
410 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_START),
411 "SEAT_ID=%s", s->id,
412 LOG_MESSAGE("New seat %s.", s->id),
413 NULL);
414
415 /* Initialize VT magic stuff */
416 seat_preallocate_vts(s);
417
418 /* Read current VT */
419 seat_read_active_vt(s);
420
421 s->started = true;
422
423 /* Save seat data */
424 seat_save(s);
425
426 seat_send_signal(s, true);
427
428 return 0;
429 }
430
431 int seat_stop(Seat *s, bool force) {
432 int r = 0;
433
434 assert(s);
435
436 if (s->started)
437 log_struct(LOG_INFO,
438 LOG_MESSAGE_ID(SD_MESSAGE_SEAT_STOP),
439 "SEAT_ID=%s", s->id,
440 LOG_MESSAGE("Removed seat %s.", s->id),
441 NULL);
442
443 seat_stop_sessions(s, force);
444
445 unlink(s->state_file);
446 seat_add_to_gc_queue(s);
447
448 if (s->started)
449 seat_send_signal(s, false);
450
451 s->started = false;
452
453 return r;
454 }
455
456 int seat_stop_sessions(Seat *s, bool force) {
457 Session *session;
458 int r = 0, k;
459
460 assert(s);
461
462 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
463 k = session_stop(session, force);
464 if (k < 0)
465 r = k;
466 }
467
468 return r;
469 }
470
471 void seat_evict_position(Seat *s, Session *session) {
472 Session *iter;
473 unsigned int pos = session->pos;
474
475 session->pos = 0;
476
477 if (!pos)
478 return;
479
480 if (pos < s->position_count && s->positions[pos] == session) {
481 s->positions[pos] = NULL;
482
483 /* There might be another session claiming the same
484 * position (eg., during gdm->session transition), so lets look
485 * for it and set it on the free slot. */
486 LIST_FOREACH(sessions_by_seat, iter, s->sessions) {
487 if (iter->pos == pos) {
488 s->positions[pos] = iter;
489 break;
490 }
491 }
492 }
493 }
494
495 void seat_claim_position(Seat *s, Session *session, unsigned int pos) {
496 /* with VTs, the position is always the same as the VTnr */
497 if (seat_has_vts(s))
498 pos = session->vtnr;
499
500 if (!GREEDY_REALLOC0(s->positions, s->position_count, pos + 1))
501 return;
502
503 seat_evict_position(s, session);
504
505 session->pos = pos;
506 if (pos > 0 && !s->positions[pos])
507 s->positions[pos] = session;
508 }
509
510 static void seat_assign_position(Seat *s, Session *session) {
511 unsigned int pos;
512
513 if (session->pos > 0)
514 return;
515
516 for (pos = 1; pos < s->position_count; ++pos)
517 if (!s->positions[pos])
518 break;
519
520 seat_claim_position(s, session, pos);
521 }
522
523 int seat_attach_session(Seat *s, Session *session) {
524 assert(s);
525 assert(session);
526 assert(!session->seat);
527
528 if (!seat_has_vts(s) != !session->vtnr)
529 return -EINVAL;
530
531 session->seat = s;
532 LIST_PREPEND(sessions_by_seat, s->sessions, session);
533 seat_assign_position(s, session);
534
535 seat_send_changed(s, "Sessions", NULL);
536
537 /* On seats with VTs, the VT logic defines which session is active. On
538 * seats without VTs, we automatically activate new sessions. */
539 if (!seat_has_vts(s))
540 seat_set_active(s, session);
541
542 return 0;
543 }
544
545 void seat_complete_switch(Seat *s) {
546 Session *session;
547
548 assert(s);
549
550 /* if no session-switch is pending or if it got canceled, do nothing */
551 if (!s->pending_switch)
552 return;
553
554 session = s->pending_switch;
555 s->pending_switch = NULL;
556
557 seat_set_active(s, session);
558 }
559
560 bool seat_has_vts(Seat *s) {
561 assert(s);
562
563 return seat_is_seat0(s) && s->manager->console_active_fd >= 0;
564 }
565
566 bool seat_is_seat0(Seat *s) {
567 assert(s);
568
569 return s->manager->seat0 == s;
570 }
571
572 bool seat_can_multi_session(Seat *s) {
573 assert(s);
574
575 return seat_has_vts(s);
576 }
577
578 bool seat_can_tty(Seat *s) {
579 assert(s);
580
581 return seat_has_vts(s);
582 }
583
584 bool seat_has_master_device(Seat *s) {
585 assert(s);
586
587 /* device list is ordered by "master" flag */
588 return !!s->devices && s->devices->master;
589 }
590
591 bool seat_can_graphical(Seat *s) {
592 assert(s);
593
594 return seat_has_master_device(s);
595 }
596
597 int seat_get_idle_hint(Seat *s, dual_timestamp *t) {
598 Session *session;
599 bool idle_hint = true;
600 dual_timestamp ts = { 0, 0 };
601
602 assert(s);
603
604 LIST_FOREACH(sessions_by_seat, session, s->sessions) {
605 dual_timestamp k;
606 int ih;
607
608 ih = session_get_idle_hint(session, &k);
609 if (ih < 0)
610 return ih;
611
612 if (!ih) {
613 if (!idle_hint) {
614 if (k.monotonic > ts.monotonic)
615 ts = k;
616 } else {
617 idle_hint = false;
618 ts = k;
619 }
620 } else if (idle_hint) {
621
622 if (k.monotonic > ts.monotonic)
623 ts = k;
624 }
625 }
626
627 if (t)
628 *t = ts;
629
630 return idle_hint;
631 }
632
633 bool seat_check_gc(Seat *s, bool drop_not_started) {
634 assert(s);
635
636 if (drop_not_started && !s->started)
637 return false;
638
639 if (seat_is_seat0(s))
640 return true;
641
642 return seat_has_master_device(s);
643 }
644
645 void seat_add_to_gc_queue(Seat *s) {
646 assert(s);
647
648 if (s->in_gc_queue)
649 return;
650
651 LIST_PREPEND(gc_queue, s->manager->seat_gc_queue, s);
652 s->in_gc_queue = true;
653 }
654
655 static bool seat_name_valid_char(char c) {
656 return
657 (c >= 'a' && c <= 'z') ||
658 (c >= 'A' && c <= 'Z') ||
659 (c >= '0' && c <= '9') ||
660 c == '-' ||
661 c == '_';
662 }
663
664 bool seat_name_is_valid(const char *name) {
665 const char *p;
666
667 assert(name);
668
669 if (!startswith(name, "seat"))
670 return false;
671
672 if (!name[4])
673 return false;
674
675 for (p = name; *p; p++)
676 if (!seat_name_valid_char(*p))
677 return false;
678
679 if (strlen(name) > 255)
680 return false;
681
682 return true;
683 }