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