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