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