Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <errno.h>
4 : : #include <fcntl.h>
5 : : #include <linux/kd.h>
6 : : #include <linux/vt.h>
7 : : #include <signal.h>
8 : : #include <string.h>
9 : : #include <sys/ioctl.h>
10 : : #include <sys/stat.h>
11 : : #include <unistd.h>
12 : :
13 : : #include "sd-messages.h"
14 : :
15 : : #include "alloc-util.h"
16 : : #include "audit-util.h"
17 : : #include "bus-error.h"
18 : : #include "bus-util.h"
19 : : #include "env-file.h"
20 : : #include "escape.h"
21 : : #include "fd-util.h"
22 : : #include "fileio.h"
23 : : #include "format-util.h"
24 : : #include "io-util.h"
25 : : #include "logind-dbus.h"
26 : : #include "logind-seat-dbus.h"
27 : : #include "logind-session-dbus.h"
28 : : #include "logind-session.h"
29 : : #include "logind-user-dbus.h"
30 : : #include "mkdir.h"
31 : : #include "parse-util.h"
32 : : #include "path-util.h"
33 : : #include "process-util.h"
34 : : #include "serialize.h"
35 : : #include "string-table.h"
36 : : #include "strv.h"
37 : : #include "terminal-util.h"
38 : : #include "tmpfile-util.h"
39 : : #include "user-util.h"
40 : : #include "util.h"
41 : :
42 : : #define RELEASE_USEC (20*USEC_PER_SEC)
43 : :
44 : : static void session_remove_fifo(Session *s);
45 : : static void session_restore_vt(Session *s);
46 : :
47 : 0 : int session_new(Session **ret, Manager *m, const char *id) {
48 : 0 : _cleanup_(session_freep) Session *s = NULL;
49 : : int r;
50 : :
51 [ # # ]: 0 : assert(ret);
52 [ # # ]: 0 : assert(m);
53 [ # # ]: 0 : assert(id);
54 : :
55 [ # # ]: 0 : if (!session_id_valid(id))
56 : 0 : return -EINVAL;
57 : :
58 : 0 : s = new(Session, 1);
59 [ # # ]: 0 : if (!s)
60 : 0 : return -ENOMEM;
61 : :
62 : 0 : *s = (Session) {
63 : : .manager = m,
64 : : .fifo_fd = -1,
65 : : .vtfd = -1,
66 : : .audit_id = AUDIT_SESSION_INVALID,
67 : : .tty_validity = _TTY_VALIDITY_INVALID,
68 : : };
69 : :
70 : 0 : s->state_file = path_join("/run/systemd/sessions", id);
71 [ # # ]: 0 : if (!s->state_file)
72 : 0 : return -ENOMEM;
73 : :
74 : 0 : s->id = basename(s->state_file);
75 : :
76 : 0 : s->devices = hashmap_new(&devt_hash_ops);
77 [ # # ]: 0 : if (!s->devices)
78 : 0 : return -ENOMEM;
79 : :
80 : 0 : r = hashmap_put(m->sessions, s->id, s);
81 [ # # ]: 0 : if (r < 0)
82 : 0 : return r;
83 : :
84 : 0 : *ret = TAKE_PTR(s);
85 : 0 : return 0;
86 : : }
87 : :
88 : 0 : Session* session_free(Session *s) {
89 : : SessionDevice *sd;
90 : :
91 [ # # ]: 0 : if (!s)
92 : 0 : return NULL;
93 : :
94 [ # # ]: 0 : if (s->in_gc_queue)
95 [ # # # # : 0 : LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s);
# # # # ]
96 : :
97 : 0 : s->timer_event_source = sd_event_source_unref(s->timer_event_source);
98 : :
99 : 0 : session_drop_controller(s);
100 : :
101 [ # # ]: 0 : while ((sd = hashmap_first(s->devices)))
102 : 0 : session_device_free(sd);
103 : :
104 : 0 : hashmap_free(s->devices);
105 : :
106 [ # # ]: 0 : if (s->user) {
107 [ # # # # : 0 : LIST_REMOVE(sessions_by_user, s->user->sessions, s);
# # # # ]
108 : :
109 [ # # ]: 0 : if (s->user->display == s)
110 : 0 : s->user->display = NULL;
111 : :
112 : 0 : user_update_last_session_timer(s->user);
113 : : }
114 : :
115 [ # # ]: 0 : if (s->seat) {
116 [ # # ]: 0 : if (s->seat->active == s)
117 : 0 : s->seat->active = NULL;
118 [ # # ]: 0 : if (s->seat->pending_switch == s)
119 : 0 : s->seat->pending_switch = NULL;
120 : :
121 : 0 : seat_evict_position(s->seat, s);
122 [ # # # # : 0 : LIST_REMOVE(sessions_by_seat, s->seat->sessions, s);
# # # # ]
123 : : }
124 : :
125 [ # # ]: 0 : if (s->scope) {
126 : 0 : hashmap_remove(s->manager->session_units, s->scope);
127 : 0 : free(s->scope);
128 : : }
129 : :
130 [ # # ]: 0 : if (pid_is_valid(s->leader))
131 : 0 : (void) hashmap_remove_value(s->manager->sessions_by_leader, PID_TO_PTR(s->leader), s);
132 : :
133 : 0 : free(s->scope_job);
134 : :
135 : 0 : sd_bus_message_unref(s->create_message);
136 : :
137 : 0 : free(s->tty);
138 : 0 : free(s->display);
139 : 0 : free(s->remote_host);
140 : 0 : free(s->remote_user);
141 : 0 : free(s->service);
142 : 0 : free(s->desktop);
143 : :
144 : 0 : hashmap_remove(s->manager->sessions, s->id);
145 : :
146 : 0 : sd_event_source_unref(s->fifo_event_source);
147 : 0 : safe_close(s->fifo_fd);
148 : :
149 : : /* Note that we remove neither the state file nor the fifo path here, since we want both to survive
150 : : * daemon restarts */
151 : 0 : free(s->state_file);
152 : 0 : free(s->fifo_path);
153 : :
154 : 0 : return mfree(s);
155 : : }
156 : :
157 : 0 : void session_set_user(Session *s, User *u) {
158 [ # # ]: 0 : assert(s);
159 [ # # ]: 0 : assert(!s->user);
160 : :
161 : 0 : s->user = u;
162 [ # # # # ]: 0 : LIST_PREPEND(sessions_by_user, u->sessions, s);
163 : :
164 : 0 : user_update_last_session_timer(u);
165 : 0 : }
166 : :
167 : 0 : int session_set_leader(Session *s, pid_t pid) {
168 : : int r;
169 : :
170 [ # # ]: 0 : assert(s);
171 : :
172 [ # # ]: 0 : if (!pid_is_valid(pid))
173 : 0 : return -EINVAL;
174 : :
175 [ # # ]: 0 : if (s->leader == pid)
176 : 0 : return 0;
177 : :
178 : 0 : r = hashmap_put(s->manager->sessions_by_leader, PID_TO_PTR(pid), s);
179 [ # # ]: 0 : if (r < 0)
180 : 0 : return r;
181 : :
182 [ # # ]: 0 : if (pid_is_valid(s->leader))
183 : 0 : (void) hashmap_remove_value(s->manager->sessions_by_leader, PID_TO_PTR(s->leader), s);
184 : :
185 : 0 : s->leader = pid;
186 : 0 : (void) audit_session_from_pid(pid, &s->audit_id);
187 : :
188 : 0 : return 1;
189 : : }
190 : :
191 : 0 : static void session_save_devices(Session *s, FILE *f) {
192 : : SessionDevice *sd;
193 : : Iterator i;
194 : :
195 [ # # ]: 0 : if (!hashmap_isempty(s->devices)) {
196 : 0 : fprintf(f, "DEVICES=");
197 [ # # ]: 0 : HASHMAP_FOREACH(sd, s->devices, i)
198 : 0 : fprintf(f, "%u:%u ", major(sd->dev), minor(sd->dev));
199 : 0 : fprintf(f, "\n");
200 : : }
201 : 0 : }
202 : :
203 : 0 : int session_save(Session *s) {
204 : 0 : _cleanup_free_ char *temp_path = NULL;
205 : 0 : _cleanup_fclose_ FILE *f = NULL;
206 : 0 : int r = 0;
207 : :
208 [ # # ]: 0 : assert(s);
209 : :
210 [ # # ]: 0 : if (!s->user)
211 : 0 : return -ESTALE;
212 : :
213 [ # # ]: 0 : if (!s->started)
214 : 0 : return 0;
215 : :
216 : 0 : r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0, MKDIR_WARN_MODE);
217 [ # # ]: 0 : if (r < 0)
218 : 0 : goto fail;
219 : :
220 : 0 : r = fopen_temporary(s->state_file, &f, &temp_path);
221 [ # # ]: 0 : if (r < 0)
222 : 0 : goto fail;
223 : :
224 : 0 : (void) fchmod(fileno(f), 0644);
225 : :
226 : 0 : fprintf(f,
227 : : "# This is private data. Do not parse.\n"
228 : : "UID="UID_FMT"\n"
229 : : "USER=%s\n"
230 : : "ACTIVE=%i\n"
231 : : "IS_DISPLAY=%i\n"
232 : : "STATE=%s\n"
233 : : "REMOTE=%i\n",
234 : 0 : s->user->uid,
235 : 0 : s->user->name,
236 : 0 : session_is_active(s),
237 : 0 : s->user->display == s,
238 : : session_state_to_string(session_get_state(s)),
239 : 0 : s->remote);
240 : :
241 [ # # ]: 0 : if (s->type >= 0)
242 : 0 : fprintf(f, "TYPE=%s\n", session_type_to_string(s->type));
243 : :
244 [ # # ]: 0 : if (s->class >= 0)
245 : 0 : fprintf(f, "CLASS=%s\n", session_class_to_string(s->class));
246 : :
247 [ # # ]: 0 : if (s->scope)
248 : 0 : fprintf(f, "SCOPE=%s\n", s->scope);
249 [ # # ]: 0 : if (s->scope_job)
250 : 0 : fprintf(f, "SCOPE_JOB=%s\n", s->scope_job);
251 : :
252 [ # # ]: 0 : if (s->fifo_path)
253 : 0 : fprintf(f, "FIFO=%s\n", s->fifo_path);
254 : :
255 [ # # ]: 0 : if (s->seat)
256 : 0 : fprintf(f, "SEAT=%s\n", s->seat->id);
257 : :
258 [ # # ]: 0 : if (s->tty)
259 : 0 : fprintf(f, "TTY=%s\n", s->tty);
260 : :
261 [ # # ]: 0 : if (s->tty_validity >= 0)
262 : 0 : fprintf(f, "TTY_VALIDITY=%s\n", tty_validity_to_string(s->tty_validity));
263 : :
264 [ # # ]: 0 : if (s->display)
265 : 0 : fprintf(f, "DISPLAY=%s\n", s->display);
266 : :
267 [ # # ]: 0 : if (s->remote_host) {
268 [ # # ]: 0 : _cleanup_free_ char *escaped;
269 : :
270 : 0 : escaped = cescape(s->remote_host);
271 [ # # ]: 0 : if (!escaped) {
272 : 0 : r = -ENOMEM;
273 : 0 : goto fail;
274 : : }
275 : :
276 : 0 : fprintf(f, "REMOTE_HOST=%s\n", escaped);
277 : : }
278 : :
279 [ # # ]: 0 : if (s->remote_user) {
280 [ # # ]: 0 : _cleanup_free_ char *escaped;
281 : :
282 : 0 : escaped = cescape(s->remote_user);
283 [ # # ]: 0 : if (!escaped) {
284 : 0 : r = -ENOMEM;
285 : 0 : goto fail;
286 : : }
287 : :
288 : 0 : fprintf(f, "REMOTE_USER=%s\n", escaped);
289 : : }
290 : :
291 [ # # ]: 0 : if (s->service) {
292 [ # # ]: 0 : _cleanup_free_ char *escaped;
293 : :
294 : 0 : escaped = cescape(s->service);
295 [ # # ]: 0 : if (!escaped) {
296 : 0 : r = -ENOMEM;
297 : 0 : goto fail;
298 : : }
299 : :
300 : 0 : fprintf(f, "SERVICE=%s\n", escaped);
301 : : }
302 : :
303 [ # # ]: 0 : if (s->desktop) {
304 [ # # ]: 0 : _cleanup_free_ char *escaped;
305 : :
306 : 0 : escaped = cescape(s->desktop);
307 [ # # ]: 0 : if (!escaped) {
308 : 0 : r = -ENOMEM;
309 : 0 : goto fail;
310 : : }
311 : :
312 : 0 : fprintf(f, "DESKTOP=%s\n", escaped);
313 : : }
314 : :
315 [ # # # # ]: 0 : if (s->seat && seat_has_vts(s->seat))
316 : 0 : fprintf(f, "VTNR=%u\n", s->vtnr);
317 : :
318 [ # # ]: 0 : if (!s->vtnr)
319 : 0 : fprintf(f, "POSITION=%u\n", s->position);
320 : :
321 [ # # ]: 0 : if (pid_is_valid(s->leader))
322 : 0 : fprintf(f, "LEADER="PID_FMT"\n", s->leader);
323 : :
324 [ # # ]: 0 : if (audit_session_is_valid(s->audit_id))
325 : 0 : fprintf(f, "AUDIT=%"PRIu32"\n", s->audit_id);
326 : :
327 [ # # ]: 0 : if (dual_timestamp_is_set(&s->timestamp))
328 : 0 : fprintf(f,
329 : : "REALTIME="USEC_FMT"\n"
330 : : "MONOTONIC="USEC_FMT"\n",
331 : : s->timestamp.realtime,
332 : : s->timestamp.monotonic);
333 : :
334 [ # # ]: 0 : if (s->controller) {
335 : 0 : fprintf(f, "CONTROLLER=%s\n", s->controller);
336 : 0 : session_save_devices(s, f);
337 : : }
338 : :
339 : 0 : r = fflush_and_check(f);
340 [ # # ]: 0 : if (r < 0)
341 : 0 : goto fail;
342 : :
343 [ # # ]: 0 : if (rename(temp_path, s->state_file) < 0) {
344 : 0 : r = -errno;
345 : 0 : goto fail;
346 : : }
347 : :
348 : 0 : return 0;
349 : :
350 : 0 : fail:
351 : 0 : (void) unlink(s->state_file);
352 : :
353 [ # # ]: 0 : if (temp_path)
354 : 0 : (void) unlink(temp_path);
355 : :
356 [ # # ]: 0 : return log_error_errno(r, "Failed to save session data %s: %m", s->state_file);
357 : : }
358 : :
359 : 0 : static int session_load_devices(Session *s, const char *devices) {
360 : : const char *p;
361 : 0 : int r = 0;
362 : :
363 [ # # ]: 0 : assert(s);
364 : :
365 : 0 : for (p = devices;;) {
366 [ # # # ]: 0 : _cleanup_free_ char *word = NULL;
367 : : SessionDevice *sd;
368 : : dev_t dev;
369 : : int k;
370 : :
371 : 0 : k = extract_first_word(&p, &word, NULL, 0);
372 [ # # ]: 0 : if (k == 0)
373 : 0 : break;
374 [ # # ]: 0 : if (k < 0) {
375 : 0 : r = k;
376 : 0 : break;
377 : : }
378 : :
379 : 0 : k = parse_dev(word, &dev);
380 [ # # ]: 0 : if (k < 0) {
381 : 0 : r = k;
382 : 0 : continue;
383 : : }
384 : :
385 : : /* The file descriptors for loaded devices will be reattached later. */
386 : 0 : k = session_device_new(s, dev, false, &sd);
387 [ # # ]: 0 : if (k < 0)
388 : 0 : r = k;
389 : : }
390 : :
391 [ # # ]: 0 : if (r < 0)
392 [ # # ]: 0 : log_error_errno(r, "Loading session devices for session %s failed: %m", s->id);
393 : :
394 : 0 : return r;
395 : : }
396 : :
397 : 0 : int session_load(Session *s) {
398 : 0 : _cleanup_free_ char *remote = NULL,
399 : 0 : *seat = NULL,
400 : 0 : *tty_validity = NULL,
401 : 0 : *vtnr = NULL,
402 : 0 : *state = NULL,
403 : 0 : *position = NULL,
404 : 0 : *leader = NULL,
405 : 0 : *type = NULL,
406 : 0 : *class = NULL,
407 : 0 : *uid = NULL,
408 : 0 : *realtime = NULL,
409 : 0 : *monotonic = NULL,
410 : 0 : *controller = NULL,
411 : 0 : *active = NULL,
412 : 0 : *devices = NULL,
413 : 0 : *is_display = NULL;
414 : :
415 : : int k, r;
416 : :
417 [ # # ]: 0 : assert(s);
418 : :
419 : 0 : r = parse_env_file(NULL, s->state_file,
420 : : "REMOTE", &remote,
421 : : "SCOPE", &s->scope,
422 : : "SCOPE_JOB", &s->scope_job,
423 : : "FIFO", &s->fifo_path,
424 : : "SEAT", &seat,
425 : : "TTY", &s->tty,
426 : : "TTY_VALIDITY", &tty_validity,
427 : : "DISPLAY", &s->display,
428 : : "REMOTE_HOST", &s->remote_host,
429 : : "REMOTE_USER", &s->remote_user,
430 : : "SERVICE", &s->service,
431 : : "DESKTOP", &s->desktop,
432 : : "VTNR", &vtnr,
433 : : "STATE", &state,
434 : : "POSITION", &position,
435 : : "LEADER", &leader,
436 : : "TYPE", &type,
437 : : "CLASS", &class,
438 : : "UID", &uid,
439 : : "REALTIME", &realtime,
440 : : "MONOTONIC", &monotonic,
441 : : "CONTROLLER", &controller,
442 : : "ACTIVE", &active,
443 : : "DEVICES", &devices,
444 : : "IS_DISPLAY", &is_display);
445 : :
446 [ # # ]: 0 : if (r < 0)
447 [ # # ]: 0 : return log_error_errno(r, "Failed to read %s: %m", s->state_file);
448 : :
449 [ # # ]: 0 : if (!s->user) {
450 : : uid_t u;
451 : : User *user;
452 : :
453 [ # # ]: 0 : if (!uid)
454 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
455 : : "UID not specified for session %s",
456 : : s->id);
457 : :
458 : 0 : r = parse_uid(uid, &u);
459 [ # # ]: 0 : if (r < 0) {
460 [ # # ]: 0 : log_error("Failed to parse UID value %s for session %s.", uid, s->id);
461 : 0 : return r;
462 : : }
463 : :
464 : 0 : user = hashmap_get(s->manager->users, UID_TO_PTR(u));
465 [ # # ]: 0 : if (!user)
466 [ # # ]: 0 : return log_error_errno(SYNTHETIC_ERRNO(ENOENT),
467 : : "User of session %s not known.",
468 : : s->id);
469 : :
470 : 0 : session_set_user(s, user);
471 : : }
472 : :
473 [ # # ]: 0 : if (remote) {
474 : 0 : k = parse_boolean(remote);
475 [ # # ]: 0 : if (k >= 0)
476 : 0 : s->remote = k;
477 : : }
478 : :
479 [ # # ]: 0 : if (vtnr)
480 : 0 : safe_atou(vtnr, &s->vtnr);
481 : :
482 [ # # # # ]: 0 : if (seat && !s->seat) {
483 : : Seat *o;
484 : :
485 : 0 : o = hashmap_get(s->manager->seats, seat);
486 [ # # ]: 0 : if (o)
487 : 0 : r = seat_attach_session(o, s);
488 [ # # # # ]: 0 : if (!o || r < 0)
489 [ # # ]: 0 : log_error("Cannot attach session %s to seat %s", s->id, seat);
490 : : }
491 : :
492 [ # # # # ]: 0 : if (!s->seat || !seat_has_vts(s->seat))
493 : 0 : s->vtnr = 0;
494 : :
495 [ # # # # ]: 0 : if (position && s->seat) {
496 : : unsigned npos;
497 : :
498 : 0 : safe_atou(position, &npos);
499 : 0 : seat_claim_position(s->seat, s, npos);
500 : : }
501 : :
502 [ # # ]: 0 : if (tty_validity) {
503 : : TTYValidity v;
504 : :
505 : 0 : v = tty_validity_from_string(tty_validity);
506 [ # # ]: 0 : if (v < 0)
507 [ # # ]: 0 : log_debug("Failed to parse TTY validity: %s", tty_validity);
508 : : else
509 : 0 : s->tty_validity = v;
510 : : }
511 : :
512 [ # # ]: 0 : if (leader) {
513 : : pid_t pid;
514 : :
515 : 0 : r = parse_pid(leader, &pid);
516 [ # # ]: 0 : if (r < 0)
517 [ # # ]: 0 : log_debug_errno(r, "Failed to parse leader PID of session: %s", leader);
518 : : else {
519 : 0 : r = session_set_leader(s, pid);
520 [ # # ]: 0 : if (r < 0)
521 [ # # ]: 0 : log_warning_errno(r, "Failed to set session leader PID, ignoring: %m");
522 : : }
523 : : }
524 : :
525 [ # # ]: 0 : if (type) {
526 : : SessionType t;
527 : :
528 : 0 : t = session_type_from_string(type);
529 [ # # ]: 0 : if (t >= 0)
530 : 0 : s->type = t;
531 : : }
532 : :
533 [ # # ]: 0 : if (class) {
534 : : SessionClass c;
535 : :
536 : 0 : c = session_class_from_string(class);
537 [ # # ]: 0 : if (c >= 0)
538 : 0 : s->class = c;
539 : : }
540 : :
541 [ # # # # ]: 0 : if (state && streq(state, "closing"))
542 : 0 : s->stopping = true;
543 : :
544 [ # # ]: 0 : if (s->fifo_path) {
545 : : int fd;
546 : :
547 : : /* If we open an unopened pipe for reading we will not
548 : : get an EOF. to trigger an EOF we hence open it for
549 : : writing, but close it right away which then will
550 : : trigger the EOF. This will happen immediately if no
551 : : other process has the FIFO open for writing, i. e.
552 : : when the session died before logind (re)started. */
553 : :
554 : 0 : fd = session_create_fifo(s);
555 : 0 : safe_close(fd);
556 : : }
557 : :
558 [ # # ]: 0 : if (realtime)
559 : 0 : (void) deserialize_usec(realtime, &s->timestamp.realtime);
560 [ # # ]: 0 : if (monotonic)
561 : 0 : (void) deserialize_usec(monotonic, &s->timestamp.monotonic);
562 : :
563 [ # # ]: 0 : if (active) {
564 : 0 : k = parse_boolean(active);
565 [ # # ]: 0 : if (k >= 0)
566 : 0 : s->was_active = k;
567 : : }
568 : :
569 [ # # ]: 0 : if (is_display) {
570 : : /* Note that when enumerating users are loaded before sessions, hence the display session to use is
571 : : * something we have to store along with the session and not the user, as in that case we couldn't
572 : : * apply it at the time we load the user. */
573 : :
574 : 0 : k = parse_boolean(is_display);
575 [ # # ]: 0 : if (k < 0)
576 [ # # ]: 0 : log_warning_errno(k, "Failed to parse IS_DISPLAY session property: %m");
577 [ # # ]: 0 : else if (k > 0)
578 : 0 : s->user->display = s;
579 : : }
580 : :
581 [ # # ]: 0 : if (controller) {
582 [ # # ]: 0 : if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0) {
583 : 0 : session_set_controller(s, controller, false, false);
584 : 0 : session_load_devices(s, devices);
585 : : } else
586 : 0 : session_restore_vt(s);
587 : : }
588 : :
589 : 0 : return r;
590 : : }
591 : :
592 : 0 : int session_activate(Session *s) {
593 : : unsigned num_pending;
594 : :
595 [ # # ]: 0 : assert(s);
596 [ # # ]: 0 : assert(s->user);
597 : :
598 [ # # ]: 0 : if (!s->seat)
599 : 0 : return -EOPNOTSUPP;
600 : :
601 [ # # ]: 0 : if (s->seat->active == s)
602 : 0 : return 0;
603 : :
604 : : /* on seats with VTs, we let VTs manage session-switching */
605 [ # # ]: 0 : if (seat_has_vts(s->seat)) {
606 [ # # ]: 0 : if (s->vtnr == 0)
607 : 0 : return -EOPNOTSUPP;
608 : :
609 : 0 : return chvt(s->vtnr);
610 : : }
611 : :
612 : : /* On seats without VTs, we implement session-switching in logind. We
613 : : * try to pause all session-devices and wait until the session
614 : : * controller acknowledged them. Once all devices are asleep, we simply
615 : : * switch the active session and be done.
616 : : * We save the session we want to switch to in seat->pending_switch and
617 : : * seat_complete_switch() will perform the final switch. */
618 : :
619 : 0 : s->seat->pending_switch = s;
620 : :
621 : : /* if no devices are running, immediately perform the session switch */
622 : 0 : num_pending = session_device_try_pause_all(s);
623 [ # # ]: 0 : if (!num_pending)
624 : 0 : seat_complete_switch(s->seat);
625 : :
626 : 0 : return 0;
627 : : }
628 : :
629 : 0 : static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_error *error) {
630 : : int r;
631 : :
632 [ # # ]: 0 : assert(s);
633 [ # # ]: 0 : assert(s->user);
634 : :
635 [ # # ]: 0 : if (!s->scope) {
636 [ # # ]: 0 : _cleanup_free_ char *scope = NULL;
637 : : const char *description;
638 : :
639 : 0 : s->scope_job = mfree(s->scope_job);
640 : :
641 : 0 : scope = strjoin("session-", s->id, ".scope");
642 [ # # ]: 0 : if (!scope)
643 : 0 : return log_oom();
644 : :
645 [ # # # # : 0 : description = strjoina("Session ", s->id, " of user ", s->user->name);
# # # # #
# # # ]
646 : :
647 : 0 : r = manager_start_scope(
648 : : s->manager,
649 : : scope,
650 : : s->leader,
651 : 0 : s->user->slice,
652 : : description,
653 : : /* These two have StopWhenUnneeded= set, hence add a dep towards them */
654 : 0 : STRV_MAKE(s->user->runtime_dir_service,
655 : : s->user->service),
656 : : /* And order us after some more */
657 : 0 : STRV_MAKE("systemd-logind.service",
658 : : "systemd-user-sessions.service",
659 : : s->user->runtime_dir_service,
660 : : s->user->service),
661 : 0 : s->user->home,
662 : : properties,
663 : : error,
664 : : &s->scope_job);
665 [ # # ]: 0 : if (r < 0)
666 [ # # ]: 0 : return log_error_errno(r, "Failed to start session scope %s: %s",
667 : : scope, bus_error_message(error, r));
668 : :
669 : 0 : s->scope = TAKE_PTR(scope);
670 : : }
671 : :
672 : 0 : (void) hashmap_put(s->manager->session_units, s->scope, s);
673 : :
674 : 0 : return 0;
675 : : }
676 : :
677 : 0 : int session_start(Session *s, sd_bus_message *properties, sd_bus_error *error) {
678 : : int r;
679 : :
680 [ # # ]: 0 : assert(s);
681 : :
682 [ # # ]: 0 : if (!s->user)
683 : 0 : return -ESTALE;
684 : :
685 [ # # ]: 0 : if (s->stopping)
686 : 0 : return -EINVAL;
687 : :
688 [ # # ]: 0 : if (s->started)
689 : 0 : return 0;
690 : :
691 : 0 : r = user_start(s->user);
692 [ # # ]: 0 : if (r < 0)
693 : 0 : return r;
694 : :
695 : 0 : r = session_start_scope(s, properties, error);
696 [ # # ]: 0 : if (r < 0)
697 : 0 : return r;
698 : :
699 [ # # ]: 0 : log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
700 : : "MESSAGE_ID=" SD_MESSAGE_SESSION_START_STR,
701 : : "SESSION_ID=%s", s->id,
702 : : "USER_ID=%s", s->user->name,
703 : : "LEADER="PID_FMT, s->leader,
704 : : LOG_MESSAGE("New session %s of user %s.", s->id, s->user->name));
705 : :
706 [ # # ]: 0 : if (!dual_timestamp_is_set(&s->timestamp))
707 : 0 : dual_timestamp_get(&s->timestamp);
708 : :
709 [ # # ]: 0 : if (s->seat)
710 : 0 : seat_read_active_vt(s->seat);
711 : :
712 : 0 : s->started = true;
713 : :
714 : 0 : user_elect_display(s->user);
715 : :
716 : : /* Save data */
717 : 0 : session_save(s);
718 : 0 : user_save(s->user);
719 [ # # ]: 0 : if (s->seat)
720 : 0 : seat_save(s->seat);
721 : :
722 : : /* Send signals */
723 : 0 : session_send_signal(s, true);
724 : 0 : user_send_changed(s->user, "Display", NULL);
725 [ # # ]: 0 : if (s->seat) {
726 [ # # ]: 0 : if (s->seat->active == s)
727 : 0 : seat_send_changed(s->seat, "ActiveSession", NULL);
728 : : }
729 : :
730 : 0 : return 0;
731 : : }
732 : :
733 : 0 : static int session_stop_scope(Session *s, bool force) {
734 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
735 : : int r;
736 : :
737 [ # # ]: 0 : assert(s);
738 : :
739 [ # # ]: 0 : if (!s->scope)
740 : 0 : return 0;
741 : :
742 : : /* Let's always abandon the scope first. This tells systemd that we are not interested anymore, and everything
743 : : * that is left in the scope is "left-over". Informing systemd about this has the benefit that it will log
744 : : * when killing any processes left after this point. */
745 : 0 : r = manager_abandon_scope(s->manager, s->scope, &error);
746 [ # # ]: 0 : if (r < 0) {
747 [ # # ]: 0 : log_warning_errno(r, "Failed to abandon session scope, ignoring: %s", bus_error_message(&error, r));
748 : 0 : sd_bus_error_free(&error);
749 : : }
750 : :
751 : 0 : s->scope_job = mfree(s->scope_job);
752 : :
753 : : /* Optionally, let's kill everything that's left now. */
754 [ # # # # ]: 0 : if (force || manager_shall_kill(s->manager, s->user->name)) {
755 : :
756 : 0 : r = manager_stop_unit(s->manager, s->scope, &error, &s->scope_job);
757 [ # # ]: 0 : if (r < 0) {
758 [ # # ]: 0 : if (force)
759 [ # # ]: 0 : return log_error_errno(r, "Failed to stop session scope: %s", bus_error_message(&error, r));
760 : :
761 [ # # ]: 0 : log_warning_errno(r, "Failed to stop session scope, ignoring: %s", bus_error_message(&error, r));
762 : : }
763 : : } else {
764 : :
765 : : /* With no killing, this session is allowed to persist in "closing" state indefinitely.
766 : : * Therefore session stop and session removal may be two distinct events.
767 : : * Session stop is quite significant on its own, let's log it. */
768 [ # # ]: 0 : log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
769 : : "SESSION_ID=%s", s->id,
770 : : "USER_ID=%s", s->user->name,
771 : : "LEADER="PID_FMT, s->leader,
772 : : LOG_MESSAGE("Session %s logged out. Waiting for processes to exit.", s->id));
773 : : }
774 : :
775 : 0 : return 0;
776 : : }
777 : :
778 : 0 : int session_stop(Session *s, bool force) {
779 : : int r;
780 : :
781 [ # # ]: 0 : assert(s);
782 : :
783 : : /* This is called whenever we begin with tearing down a session record. It's called in four cases: explicit API
784 : : * request via the bus (either directly for the session object or for the seat or user object this session
785 : : * belongs to; 'force' is true), or due to automatic GC (i.e. scope vanished; 'force' is false), or because the
786 : : * session FIFO saw an EOF ('force' is false), or because the release timer hit ('force' is false). */
787 : :
788 [ # # ]: 0 : if (!s->user)
789 : 0 : return -ESTALE;
790 [ # # ]: 0 : if (!s->started)
791 : 0 : return 0;
792 [ # # ]: 0 : if (s->stopping)
793 : 0 : return 0;
794 : :
795 : 0 : s->timer_event_source = sd_event_source_unref(s->timer_event_source);
796 : :
797 [ # # ]: 0 : if (s->seat)
798 : 0 : seat_evict_position(s->seat, s);
799 : :
800 : : /* We are going down, don't care about FIFOs anymore */
801 : 0 : session_remove_fifo(s);
802 : :
803 : : /* Kill cgroup */
804 : 0 : r = session_stop_scope(s, force);
805 : :
806 : 0 : s->stopping = true;
807 : :
808 : 0 : user_elect_display(s->user);
809 : :
810 : 0 : session_save(s);
811 : 0 : user_save(s->user);
812 : :
813 : 0 : return r;
814 : : }
815 : :
816 : 0 : int session_finalize(Session *s) {
817 : : SessionDevice *sd;
818 : :
819 [ # # ]: 0 : assert(s);
820 : :
821 [ # # ]: 0 : if (!s->user)
822 : 0 : return -ESTALE;
823 : :
824 [ # # ]: 0 : if (s->started)
825 [ # # ]: 0 : log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
826 : : "MESSAGE_ID=" SD_MESSAGE_SESSION_STOP_STR,
827 : : "SESSION_ID=%s", s->id,
828 : : "USER_ID=%s", s->user->name,
829 : : "LEADER="PID_FMT, s->leader,
830 : : LOG_MESSAGE("Removed session %s.", s->id));
831 : :
832 : 0 : s->timer_event_source = sd_event_source_unref(s->timer_event_source);
833 : :
834 [ # # ]: 0 : if (s->seat)
835 : 0 : seat_evict_position(s->seat, s);
836 : :
837 : : /* Kill session devices */
838 [ # # ]: 0 : while ((sd = hashmap_first(s->devices)))
839 : 0 : session_device_free(sd);
840 : :
841 : 0 : (void) unlink(s->state_file);
842 : 0 : session_add_to_gc_queue(s);
843 : 0 : user_add_to_gc_queue(s->user);
844 : :
845 [ # # ]: 0 : if (s->started) {
846 : 0 : session_send_signal(s, false);
847 : 0 : s->started = false;
848 : : }
849 : :
850 [ # # ]: 0 : if (s->seat) {
851 [ # # ]: 0 : if (s->seat->active == s)
852 : 0 : seat_set_active(s->seat, NULL);
853 : :
854 : 0 : seat_save(s->seat);
855 : : }
856 : :
857 : 0 : user_save(s->user);
858 : 0 : user_send_changed(s->user, "Display", NULL);
859 : :
860 : 0 : return 0;
861 : : }
862 : :
863 : 0 : static int release_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) {
864 : 0 : Session *s = userdata;
865 : :
866 [ # # ]: 0 : assert(es);
867 [ # # ]: 0 : assert(s);
868 : :
869 : 0 : session_stop(s, false);
870 : 0 : return 0;
871 : : }
872 : :
873 : 0 : int session_release(Session *s) {
874 [ # # ]: 0 : assert(s);
875 : :
876 [ # # # # ]: 0 : if (!s->started || s->stopping)
877 : 0 : return 0;
878 : :
879 [ # # ]: 0 : if (s->timer_event_source)
880 : 0 : return 0;
881 : :
882 : 0 : return sd_event_add_time(s->manager->event,
883 : : &s->timer_event_source,
884 : : CLOCK_MONOTONIC,
885 : : usec_add(now(CLOCK_MONOTONIC), RELEASE_USEC), 0,
886 : : release_timeout_callback, s);
887 : : }
888 : :
889 : 0 : bool session_is_active(Session *s) {
890 [ # # ]: 0 : assert(s);
891 : :
892 [ # # ]: 0 : if (!s->seat)
893 : 0 : return true;
894 : :
895 : 0 : return s->seat->active == s;
896 : : }
897 : :
898 : 0 : static int get_tty_atime(const char *tty, usec_t *atime) {
899 : 0 : _cleanup_free_ char *p = NULL;
900 : : struct stat st;
901 : :
902 [ # # ]: 0 : assert(tty);
903 [ # # ]: 0 : assert(atime);
904 : :
905 [ # # ]: 0 : if (!path_is_absolute(tty)) {
906 : 0 : p = path_join("/dev", tty);
907 [ # # ]: 0 : if (!p)
908 : 0 : return -ENOMEM;
909 : :
910 : 0 : tty = p;
911 [ # # ]: 0 : } else if (!path_startswith(tty, "/dev/"))
912 : 0 : return -ENOENT;
913 : :
914 [ # # ]: 0 : if (lstat(tty, &st) < 0)
915 : 0 : return -errno;
916 : :
917 : 0 : *atime = timespec_load(&st.st_atim);
918 : 0 : return 0;
919 : : }
920 : :
921 : 0 : static int get_process_ctty_atime(pid_t pid, usec_t *atime) {
922 : 0 : _cleanup_free_ char *p = NULL;
923 : : int r;
924 : :
925 [ # # ]: 0 : assert(pid > 0);
926 [ # # ]: 0 : assert(atime);
927 : :
928 : 0 : r = get_ctty(pid, NULL, &p);
929 [ # # ]: 0 : if (r < 0)
930 : 0 : return r;
931 : :
932 : 0 : return get_tty_atime(p, atime);
933 : : }
934 : :
935 : 0 : int session_get_idle_hint(Session *s, dual_timestamp *t) {
936 : 0 : usec_t atime = 0, n;
937 : : int r;
938 : :
939 [ # # ]: 0 : assert(s);
940 : :
941 : : /* Explicit idle hint is set */
942 [ # # ]: 0 : if (s->idle_hint) {
943 [ # # ]: 0 : if (t)
944 : 0 : *t = s->idle_hint_timestamp;
945 : :
946 : 0 : return s->idle_hint;
947 : : }
948 : :
949 : : /* Graphical sessions should really implement a real
950 : : * idle hint logic */
951 [ # # # # ]: 0 : if (SESSION_TYPE_IS_GRAPHICAL(s->type))
952 : 0 : goto dont_know;
953 : :
954 : : /* For sessions with an explicitly configured tty, let's check
955 : : * its atime */
956 [ # # ]: 0 : if (s->tty) {
957 : 0 : r = get_tty_atime(s->tty, &atime);
958 [ # # ]: 0 : if (r >= 0)
959 : 0 : goto found_atime;
960 : : }
961 : :
962 : : /* For sessions with a leader but no explicitly configured
963 : : * tty, let's check the controlling tty of the leader */
964 [ # # ]: 0 : if (pid_is_valid(s->leader)) {
965 : 0 : r = get_process_ctty_atime(s->leader, &atime);
966 [ # # ]: 0 : if (r >= 0)
967 : 0 : goto found_atime;
968 : : }
969 : :
970 : 0 : dont_know:
971 [ # # ]: 0 : if (t)
972 : 0 : *t = s->idle_hint_timestamp;
973 : :
974 : 0 : return 0;
975 : :
976 : 0 : found_atime:
977 [ # # ]: 0 : if (t)
978 : 0 : dual_timestamp_from_realtime(t, atime);
979 : :
980 : 0 : n = now(CLOCK_REALTIME);
981 : :
982 [ # # ]: 0 : if (s->manager->idle_action_usec <= 0)
983 : 0 : return 0;
984 : :
985 : 0 : return atime + s->manager->idle_action_usec <= n;
986 : : }
987 : :
988 : 0 : void session_set_idle_hint(Session *s, bool b) {
989 [ # # ]: 0 : assert(s);
990 : :
991 [ # # ]: 0 : if (s->idle_hint == b)
992 : 0 : return;
993 : :
994 : 0 : s->idle_hint = b;
995 : 0 : dual_timestamp_get(&s->idle_hint_timestamp);
996 : :
997 : 0 : session_send_changed(s, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
998 : :
999 [ # # ]: 0 : if (s->seat)
1000 : 0 : seat_send_changed(s->seat, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
1001 : :
1002 : 0 : user_send_changed(s->user, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
1003 : 0 : manager_send_changed(s->manager, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
1004 : : }
1005 : :
1006 : 0 : int session_get_locked_hint(Session *s) {
1007 [ # # ]: 0 : assert(s);
1008 : :
1009 : 0 : return s->locked_hint;
1010 : : }
1011 : :
1012 : 0 : void session_set_locked_hint(Session *s, bool b) {
1013 [ # # ]: 0 : assert(s);
1014 : :
1015 [ # # ]: 0 : if (s->locked_hint == b)
1016 : 0 : return;
1017 : :
1018 : 0 : s->locked_hint = b;
1019 : :
1020 : 0 : session_send_changed(s, "LockedHint", NULL);
1021 : : }
1022 : :
1023 : 0 : static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
1024 : 0 : Session *s = userdata;
1025 : :
1026 [ # # ]: 0 : assert(s);
1027 [ # # ]: 0 : assert(s->fifo_fd == fd);
1028 : :
1029 : : /* EOF on the FIFO means the session died abnormally. */
1030 : :
1031 : 0 : session_remove_fifo(s);
1032 : 0 : session_stop(s, false);
1033 : :
1034 : 0 : return 1;
1035 : : }
1036 : :
1037 : 0 : int session_create_fifo(Session *s) {
1038 : : int r;
1039 : :
1040 [ # # ]: 0 : assert(s);
1041 : :
1042 : : /* Create FIFO */
1043 [ # # ]: 0 : if (!s->fifo_path) {
1044 : 0 : r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0, MKDIR_WARN_MODE);
1045 [ # # ]: 0 : if (r < 0)
1046 : 0 : return r;
1047 : :
1048 : 0 : s->fifo_path = strjoin("/run/systemd/sessions/", s->id, ".ref");
1049 [ # # ]: 0 : if (!s->fifo_path)
1050 : 0 : return -ENOMEM;
1051 : :
1052 [ # # # # ]: 0 : if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
1053 : 0 : return -errno;
1054 : : }
1055 : :
1056 : : /* Open reading side */
1057 [ # # ]: 0 : if (s->fifo_fd < 0) {
1058 : 0 : s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
1059 [ # # ]: 0 : if (s->fifo_fd < 0)
1060 : 0 : return -errno;
1061 : : }
1062 : :
1063 [ # # ]: 0 : if (!s->fifo_event_source) {
1064 : 0 : r = sd_event_add_io(s->manager->event, &s->fifo_event_source, s->fifo_fd, 0, session_dispatch_fifo, s);
1065 [ # # ]: 0 : if (r < 0)
1066 : 0 : return r;
1067 : :
1068 : : /* Let's make sure we noticed dead sessions before we process new bus requests (which might create new
1069 : : * sessions). */
1070 : 0 : r = sd_event_source_set_priority(s->fifo_event_source, SD_EVENT_PRIORITY_NORMAL-10);
1071 [ # # ]: 0 : if (r < 0)
1072 : 0 : return r;
1073 : : }
1074 : :
1075 : : /* Open writing side */
1076 : 0 : r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NONBLOCK);
1077 [ # # ]: 0 : if (r < 0)
1078 : 0 : return -errno;
1079 : :
1080 : 0 : return r;
1081 : : }
1082 : :
1083 : 0 : static void session_remove_fifo(Session *s) {
1084 [ # # ]: 0 : assert(s);
1085 : :
1086 : 0 : s->fifo_event_source = sd_event_source_unref(s->fifo_event_source);
1087 : 0 : s->fifo_fd = safe_close(s->fifo_fd);
1088 : :
1089 [ # # ]: 0 : if (s->fifo_path) {
1090 : 0 : (void) unlink(s->fifo_path);
1091 : 0 : s->fifo_path = mfree(s->fifo_path);
1092 : : }
1093 : 0 : }
1094 : :
1095 : 0 : bool session_may_gc(Session *s, bool drop_not_started) {
1096 : : int r;
1097 : :
1098 [ # # ]: 0 : assert(s);
1099 : :
1100 [ # # # # ]: 0 : if (drop_not_started && !s->started)
1101 : 0 : return true;
1102 : :
1103 [ # # ]: 0 : if (!s->user)
1104 : 0 : return true;
1105 : :
1106 [ # # ]: 0 : if (s->fifo_fd >= 0) {
1107 [ # # ]: 0 : if (pipe_eof(s->fifo_fd) <= 0)
1108 : 0 : return false;
1109 : : }
1110 : :
1111 [ # # ]: 0 : if (s->scope_job) {
1112 [ # # ]: 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1113 : :
1114 : 0 : r = manager_job_is_active(s->manager, s->scope_job, &error);
1115 [ # # ]: 0 : if (r < 0)
1116 [ # # ]: 0 : log_debug_errno(r, "Failed to determine whether job '%s' is pending, ignoring: %s", s->scope_job, bus_error_message(&error, r));
1117 [ # # ]: 0 : if (r != 0)
1118 : 0 : return false;
1119 : : }
1120 : :
1121 [ # # ]: 0 : if (s->scope) {
1122 [ # # ]: 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
1123 : :
1124 : 0 : r = manager_unit_is_active(s->manager, s->scope, &error);
1125 [ # # ]: 0 : if (r < 0)
1126 [ # # ]: 0 : log_debug_errno(r, "Failed to determine whether unit '%s' is active, ignoring: %s", s->scope, bus_error_message(&error, r));
1127 [ # # ]: 0 : if (r != 0)
1128 : 0 : return false;
1129 : : }
1130 : :
1131 : 0 : return true;
1132 : : }
1133 : :
1134 : 0 : void session_add_to_gc_queue(Session *s) {
1135 [ # # ]: 0 : assert(s);
1136 : :
1137 [ # # ]: 0 : if (s->in_gc_queue)
1138 : 0 : return;
1139 : :
1140 [ # # # # ]: 0 : LIST_PREPEND(gc_queue, s->manager->session_gc_queue, s);
1141 : 0 : s->in_gc_queue = true;
1142 : : }
1143 : :
1144 : 0 : SessionState session_get_state(Session *s) {
1145 [ # # ]: 0 : assert(s);
1146 : :
1147 : : /* always check closing first */
1148 [ # # # # ]: 0 : if (s->stopping || s->timer_event_source)
1149 : 0 : return SESSION_CLOSING;
1150 : :
1151 [ # # # # ]: 0 : if (s->scope_job || s->fifo_fd < 0)
1152 : 0 : return SESSION_OPENING;
1153 : :
1154 [ # # ]: 0 : if (session_is_active(s))
1155 : 0 : return SESSION_ACTIVE;
1156 : :
1157 : 0 : return SESSION_ONLINE;
1158 : : }
1159 : :
1160 : 0 : int session_kill(Session *s, KillWho who, int signo) {
1161 [ # # ]: 0 : assert(s);
1162 : :
1163 [ # # ]: 0 : if (!s->scope)
1164 : 0 : return -ESRCH;
1165 : :
1166 : 0 : return manager_kill_unit(s->manager, s->scope, who, signo, NULL);
1167 : : }
1168 : :
1169 : 0 : static int session_open_vt(Session *s) {
1170 : : char path[sizeof("/dev/tty") + DECIMAL_STR_MAX(s->vtnr)];
1171 : :
1172 [ # # ]: 0 : if (s->vtnr < 1)
1173 : 0 : return -ENODEV;
1174 : :
1175 [ # # ]: 0 : if (s->vtfd >= 0)
1176 : 0 : return s->vtfd;
1177 : :
1178 : 0 : sprintf(path, "/dev/tty%u", s->vtnr);
1179 : 0 : s->vtfd = open_terminal(path, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY);
1180 [ # # ]: 0 : if (s->vtfd < 0)
1181 [ # # ]: 0 : return log_error_errno(s->vtfd, "cannot open VT %s of session %s: %m", path, s->id);
1182 : :
1183 : 0 : return s->vtfd;
1184 : : }
1185 : :
1186 : 0 : int session_prepare_vt(Session *s) {
1187 : : int vt, r;
1188 : 0 : struct vt_mode mode = {};
1189 : :
1190 [ # # ]: 0 : if (s->vtnr < 1)
1191 : 0 : return 0;
1192 : :
1193 : 0 : vt = session_open_vt(s);
1194 [ # # ]: 0 : if (vt < 0)
1195 : 0 : return vt;
1196 : :
1197 : 0 : r = fchown(vt, s->user->uid, -1);
1198 [ # # ]: 0 : if (r < 0) {
1199 [ # # ]: 0 : r = log_error_errno(errno,
1200 : : "Cannot change owner of /dev/tty%u: %m",
1201 : : s->vtnr);
1202 : 0 : goto error;
1203 : : }
1204 : :
1205 : 0 : r = ioctl(vt, KDSKBMODE, K_OFF);
1206 [ # # ]: 0 : if (r < 0) {
1207 [ # # ]: 0 : r = log_error_errno(errno,
1208 : : "Cannot set K_OFF on /dev/tty%u: %m",
1209 : : s->vtnr);
1210 : 0 : goto error;
1211 : : }
1212 : :
1213 : 0 : r = ioctl(vt, KDSETMODE, KD_GRAPHICS);
1214 [ # # ]: 0 : if (r < 0) {
1215 [ # # ]: 0 : r = log_error_errno(errno,
1216 : : "Cannot set KD_GRAPHICS on /dev/tty%u: %m",
1217 : : s->vtnr);
1218 : 0 : goto error;
1219 : : }
1220 : :
1221 : : /* Oh, thanks to the VT layer, VT_AUTO does not work with KD_GRAPHICS.
1222 : : * So we need a dummy handler here which just acknowledges *all* VT
1223 : : * switch requests. */
1224 : 0 : mode.mode = VT_PROCESS;
1225 : 0 : mode.relsig = SIGRTMIN;
1226 : 0 : mode.acqsig = SIGRTMIN + 1;
1227 : 0 : r = ioctl(vt, VT_SETMODE, &mode);
1228 [ # # ]: 0 : if (r < 0) {
1229 [ # # ]: 0 : r = log_error_errno(errno,
1230 : : "Cannot set VT_PROCESS on /dev/tty%u: %m",
1231 : : s->vtnr);
1232 : 0 : goto error;
1233 : : }
1234 : :
1235 : 0 : return 0;
1236 : :
1237 : 0 : error:
1238 : 0 : session_restore_vt(s);
1239 : 0 : return r;
1240 : : }
1241 : :
1242 : 0 : static void session_restore_vt(Session *s) {
1243 : : int r, vt, old_fd;
1244 : :
1245 : : /* We need to get a fresh handle to the virtual terminal,
1246 : : * since the old file-descriptor is potentially in a hung-up
1247 : : * state after the controlling process exited; we do a
1248 : : * little dance to avoid having the terminal be available
1249 : : * for reuse before we've cleaned it up.
1250 : : */
1251 : 0 : old_fd = TAKE_FD(s->vtfd);
1252 : :
1253 : 0 : vt = session_open_vt(s);
1254 : 0 : safe_close(old_fd);
1255 : :
1256 [ # # ]: 0 : if (vt < 0)
1257 : 0 : return;
1258 : :
1259 : 0 : r = vt_restore(vt);
1260 [ # # ]: 0 : if (r < 0)
1261 [ # # ]: 0 : log_warning_errno(r, "Failed to restore VT, ignoring: %m");
1262 : :
1263 : 0 : s->vtfd = safe_close(s->vtfd);
1264 : : }
1265 : :
1266 : 0 : void session_leave_vt(Session *s) {
1267 : : int r;
1268 : :
1269 [ # # ]: 0 : assert(s);
1270 : :
1271 : : /* This is called whenever we get a VT-switch signal from the kernel.
1272 : : * We acknowledge all of them unconditionally. Note that session are
1273 : : * free to overwrite those handlers and we only register them for
1274 : : * sessions with controllers. Legacy sessions are not affected.
1275 : : * However, if we switch from a non-legacy to a legacy session, we must
1276 : : * make sure to pause all device before acknowledging the switch. We
1277 : : * process the real switch only after we are notified via sysfs, so the
1278 : : * legacy session might have already started using the devices. If we
1279 : : * don't pause the devices before the switch, we might confuse the
1280 : : * session we switch to. */
1281 : :
1282 [ # # ]: 0 : if (s->vtfd < 0)
1283 : 0 : return;
1284 : :
1285 : 0 : session_device_pause_all(s);
1286 : 0 : r = vt_release(s->vtfd, false);
1287 [ # # ]: 0 : if (r < 0)
1288 [ # # ]: 0 : log_debug_errno(r, "Cannot release VT of session %s: %m", s->id);
1289 : : }
1290 : :
1291 : 0 : bool session_is_controller(Session *s, const char *sender) {
1292 [ # # ]: 0 : assert(s);
1293 : :
1294 : 0 : return streq_ptr(s->controller, sender);
1295 : : }
1296 : :
1297 : 0 : static void session_release_controller(Session *s, bool notify) {
1298 [ # # ]: 0 : _cleanup_free_ char *name = NULL;
1299 : : SessionDevice *sd;
1300 : :
1301 [ # # ]: 0 : if (!s->controller)
1302 : 0 : return;
1303 : :
1304 : 0 : name = s->controller;
1305 : :
1306 : : /* By resetting the controller before releasing the devices, we won't
1307 : : * send notification signals. This avoids sending useless notifications
1308 : : * if the controller is released on disconnects. */
1309 [ # # ]: 0 : if (!notify)
1310 : 0 : s->controller = NULL;
1311 : :
1312 [ # # ]: 0 : while ((sd = hashmap_first(s->devices)))
1313 : 0 : session_device_free(sd);
1314 : :
1315 : 0 : s->controller = NULL;
1316 : 0 : s->track = sd_bus_track_unref(s->track);
1317 : : }
1318 : :
1319 : 0 : static int on_bus_track(sd_bus_track *track, void *userdata) {
1320 : 0 : Session *s = userdata;
1321 : :
1322 [ # # ]: 0 : assert(track);
1323 [ # # ]: 0 : assert(s);
1324 : :
1325 : 0 : session_drop_controller(s);
1326 : :
1327 : 0 : return 0;
1328 : : }
1329 : :
1330 : 0 : int session_set_controller(Session *s, const char *sender, bool force, bool prepare) {
1331 : 0 : _cleanup_free_ char *name = NULL;
1332 : : int r;
1333 : :
1334 [ # # ]: 0 : assert(s);
1335 [ # # ]: 0 : assert(sender);
1336 : :
1337 [ # # ]: 0 : if (session_is_controller(s, sender))
1338 : 0 : return 0;
1339 [ # # # # ]: 0 : if (s->controller && !force)
1340 : 0 : return -EBUSY;
1341 : :
1342 : 0 : name = strdup(sender);
1343 [ # # ]: 0 : if (!name)
1344 : 0 : return -ENOMEM;
1345 : :
1346 : 0 : s->track = sd_bus_track_unref(s->track);
1347 : 0 : r = sd_bus_track_new(s->manager->bus, &s->track, on_bus_track, s);
1348 [ # # ]: 0 : if (r < 0)
1349 : 0 : return r;
1350 : :
1351 : 0 : r = sd_bus_track_add_name(s->track, name);
1352 [ # # ]: 0 : if (r < 0)
1353 : 0 : return r;
1354 : :
1355 : : /* When setting a session controller, we forcibly mute the VT and set
1356 : : * it into graphics-mode. Applications can override that by changing
1357 : : * VT state after calling TakeControl(). However, this serves as a good
1358 : : * default and well-behaving controllers can now ignore VTs entirely.
1359 : : * Note that we reset the VT on ReleaseControl() and if the controller
1360 : : * exits.
1361 : : * If logind crashes/restarts, we restore the controller during restart
1362 : : * (without preparing the VT since the controller has probably overridden
1363 : : * VT state by now) or reset the VT in case it crashed/exited, too. */
1364 [ # # ]: 0 : if (prepare) {
1365 : 0 : r = session_prepare_vt(s);
1366 [ # # ]: 0 : if (r < 0) {
1367 : 0 : s->track = sd_bus_track_unref(s->track);
1368 : 0 : return r;
1369 : : }
1370 : : }
1371 : :
1372 : 0 : session_release_controller(s, true);
1373 : 0 : s->controller = TAKE_PTR(name);
1374 : 0 : session_save(s);
1375 : :
1376 : 0 : return 0;
1377 : : }
1378 : :
1379 : 0 : void session_drop_controller(Session *s) {
1380 [ # # ]: 0 : assert(s);
1381 : :
1382 [ # # ]: 0 : if (!s->controller)
1383 : 0 : return;
1384 : :
1385 : 0 : s->track = sd_bus_track_unref(s->track);
1386 : 0 : session_release_controller(s, false);
1387 : 0 : session_save(s);
1388 : 0 : session_restore_vt(s);
1389 : : }
1390 : :
1391 : : static const char* const session_state_table[_SESSION_STATE_MAX] = {
1392 : : [SESSION_OPENING] = "opening",
1393 : : [SESSION_ONLINE] = "online",
1394 : : [SESSION_ACTIVE] = "active",
1395 : : [SESSION_CLOSING] = "closing"
1396 : : };
1397 : :
1398 [ + + + + ]: 48 : DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState);
1399 : :
1400 : : static const char* const session_type_table[_SESSION_TYPE_MAX] = {
1401 : : [SESSION_UNSPECIFIED] = "unspecified",
1402 : : [SESSION_TTY] = "tty",
1403 : : [SESSION_X11] = "x11",
1404 : : [SESSION_WAYLAND] = "wayland",
1405 : : [SESSION_MIR] = "mir",
1406 : : [SESSION_WEB] = "web",
1407 : : };
1408 : :
1409 [ + + + + ]: 64 : DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
1410 : :
1411 : : static const char* const session_class_table[_SESSION_CLASS_MAX] = {
1412 : : [SESSION_USER] = "user",
1413 : : [SESSION_GREETER] = "greeter",
1414 : : [SESSION_LOCK_SCREEN] = "lock-screen",
1415 : : [SESSION_BACKGROUND] = "background"
1416 : : };
1417 : :
1418 [ + + + + ]: 48 : DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
1419 : :
1420 : : static const char* const kill_who_table[_KILL_WHO_MAX] = {
1421 : : [KILL_LEADER] = "leader",
1422 : : [KILL_ALL] = "all"
1423 : : };
1424 : :
1425 [ + + + + ]: 32 : DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);
1426 : :
1427 : : static const char* const tty_validity_table[_TTY_VALIDITY_MAX] = {
1428 : : [TTY_FROM_PAM] = "from-pam",
1429 : : [TTY_FROM_UTMP] = "from-utmp",
1430 : : [TTY_UTMP_INCONSISTENT] = "utmp-inconsistent",
1431 : : };
1432 : :
1433 [ # # # # ]: 0 : DEFINE_STRING_TABLE_LOOKUP(tty_validity, TTYValidity);
|