]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-session.c
machined,logind: don't generate errors on signal match functions
[thirdparty/systemd.git] / src / login / logind-session.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 <errno.h>
90a18413
DH
23#include <fcntl.h>
24#include <linux/vt.h>
25#include <linux/kd.h>
26#include <signal.h>
20263082 27#include <string.h>
90a18413 28#include <sys/ioctl.h>
20263082
LP
29#include <unistd.h>
30
cc377381 31#include "sd-messages.h"
20263082 32#include "util.h"
49e942b2 33#include "mkdir.h"
9eb977db 34#include "path-util.h"
a5c32cff 35#include "fileio.h"
cc377381
LP
36#include "audit.h"
37#include "bus-util.h"
38#include "bus-error.h"
fb6becb4 39#include "logind-session.h"
6482f626 40#include "formats-util.h"
288a74cc 41#include "terminal-util.h"
20263082 42
5f41d1f1
LP
43#define RELEASE_USEC (20*USEC_PER_SEC)
44
45static void session_remove_fifo(Session *s);
46
9444b1f2 47Session* session_new(Manager *m, const char *id) {
20263082
LP
48 Session *s;
49
50 assert(m);
51 assert(id);
4b549144 52 assert(session_id_valid(id));
20263082 53
14c3baca 54 s = new0(Session, 1);
20263082
LP
55 if (!s)
56 return NULL;
57
98a28fef 58 s->state_file = strappend("/run/systemd/sessions/", id);
20263082
LP
59 if (!s->state_file) {
60 free(s);
61 return NULL;
62 }
63
d5099efc 64 s->devices = hashmap_new(&devt_hash_ops);
118ecf32
DH
65 if (!s->devices) {
66 free(s->state_file);
67 free(s);
68 return NULL;
69 }
70
2b6bf07d 71 s->id = basename(s->state_file);
20263082
LP
72
73 if (hashmap_put(m->sessions, s->id, s) < 0) {
118ecf32 74 hashmap_free(s->devices);
f8e2fb7b 75 free(s->state_file);
20263082
LP
76 free(s);
77 return NULL;
78 }
79
80 s->manager = m;
932e3ee7 81 s->fifo_fd = -1;
90a18413 82 s->vtfd = -1;
20263082
LP
83
84 return s;
85}
86
87void session_free(Session *s) {
118ecf32
DH
88 SessionDevice *sd;
89
20263082
LP
90 assert(s);
91
14c3baca 92 if (s->in_gc_queue)
71fda00f 93 LIST_REMOVE(gc_queue, s->manager->session_gc_queue, s);
14c3baca 94
5f41d1f1
LP
95 s->timer_event_source = sd_event_source_unref(s->timer_event_source);
96
cc377381
LP
97 session_remove_fifo(s);
98
ae5e06bd
DH
99 session_drop_controller(s);
100
118ecf32
DH
101 while ((sd = hashmap_first(s->devices)))
102 session_device_free(sd);
103
104 hashmap_free(s->devices);
105
20263082 106 if (s->user) {
71fda00f 107 LIST_REMOVE(sessions_by_user, s->user->sessions, s);
20263082
LP
108
109 if (s->user->display == s)
110 s->user->display = NULL;
111 }
112
9418f147
LP
113 if (s->seat) {
114 if (s->seat->active == s)
115 s->seat->active = NULL;
d7bd01b5
DH
116 if (s->seat->pending_switch == s)
117 s->seat->pending_switch = NULL;
9418f147 118
49e6fdbf 119 seat_evict_position(s->seat, s);
71fda00f 120 LIST_REMOVE(sessions_by_seat, s->seat->sessions, s);
9418f147 121 }
20263082 122
fb6becb4
LP
123 if (s->scope) {
124 hashmap_remove(s->manager->session_units, s->scope);
125 free(s->scope);
126 }
127
128 free(s->scope_job);
1713813d 129
cc377381 130 sd_bus_message_unref(s->create_message);
20263082
LP
131
132 free(s->tty);
133 free(s->display);
134 free(s->remote_host);
3f49d45a 135 free(s->remote_user);
98a28fef 136 free(s->service);
a4cd87e9 137 free(s->desktop);
20263082
LP
138
139 hashmap_remove(s->manager->sessions, s->id);
98a28fef 140
d2f92cdf 141 free(s->state_file);
20263082
LP
142 free(s);
143}
144
9444b1f2
LP
145void session_set_user(Session *s, User *u) {
146 assert(s);
147 assert(!s->user);
148
149 s->user = u;
71fda00f 150 LIST_PREPEND(sessions_by_user, u->sessions, s);
9444b1f2
LP
151}
152
20263082 153int session_save(Session *s) {
507f22bd 154 _cleanup_free_ char *temp_path = NULL;
cc377381 155 _cleanup_fclose_ FILE *f = NULL;
20263082
LP
156 int r = 0;
157
158 assert(s);
159
9444b1f2
LP
160 if (!s->user)
161 return -ESTALE;
162
accaeded
LP
163 if (!s->started)
164 return 0;
165
d2e54fae 166 r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
20263082 167 if (r < 0)
dacd6cee 168 goto fail;
20263082 169
14c3baca
LP
170 r = fopen_temporary(s->state_file, &f, &temp_path);
171 if (r < 0)
dacd6cee 172 goto fail;
20263082
LP
173
174 assert(s->user);
175
14c3baca
LP
176 fchmod(fileno(f), 0644);
177
20263082
LP
178 fprintf(f,
179 "# This is private data. Do not parse.\n"
90b2de37 180 "UID="UID_FMT"\n"
20263082
LP
181 "USER=%s\n"
182 "ACTIVE=%i\n"
0604381b 183 "STATE=%s\n"
fb6becb4 184 "REMOTE=%i\n",
90b2de37 185 s->user->uid,
20263082
LP
186 s->user->name,
187 session_is_active(s),
0604381b 188 session_state_to_string(session_get_state(s)),
fb6becb4 189 s->remote);
20263082 190
a91e4e53 191 if (s->type >= 0)
507f22bd 192 fprintf(f, "TYPE=%s\n", session_type_to_string(s->type));
a91e4e53 193
55efac6c 194 if (s->class >= 0)
507f22bd 195 fprintf(f, "CLASS=%s\n", session_class_to_string(s->class));
55efac6c 196
fb6becb4
LP
197 if (s->scope)
198 fprintf(f, "SCOPE=%s\n", s->scope);
fb6becb4
LP
199 if (s->scope_job)
200 fprintf(f, "SCOPE_JOB=%s\n", s->scope_job);
20263082 201
932e3ee7 202 if (s->fifo_path)
507f22bd 203 fprintf(f, "FIFO=%s\n", s->fifo_path);
932e3ee7 204
20263082 205 if (s->seat)
507f22bd 206 fprintf(f, "SEAT=%s\n", s->seat->id);
20263082
LP
207
208 if (s->tty)
507f22bd 209 fprintf(f, "TTY=%s\n", s->tty);
20263082
LP
210
211 if (s->display)
507f22bd 212 fprintf(f, "DISPLAY=%s\n", s->display);
20263082 213
558c6490
LP
214 if (s->remote_host) {
215 _cleanup_free_ char *escaped;
216
217 escaped = cescape(s->remote_host);
218 if (!escaped) {
219 r = -ENOMEM;
dacd6cee 220 goto fail;
558c6490
LP
221 }
222
223 fprintf(f, "REMOTE_HOST=%s\n", escaped);
224 }
225
226 if (s->remote_user) {
227 _cleanup_free_ char *escaped;
228
229 escaped = cescape(s->remote_user);
230 if (!escaped) {
231 r = -ENOMEM;
dacd6cee 232 goto fail;
558c6490
LP
233 }
234
235 fprintf(f, "REMOTE_USER=%s\n", escaped);
236 }
237
238 if (s->service) {
239 _cleanup_free_ char *escaped;
20263082 240
558c6490
LP
241 escaped = cescape(s->service);
242 if (!escaped) {
243 r = -ENOMEM;
dacd6cee 244 goto fail;
558c6490
LP
245 }
246
247 fprintf(f, "SERVICE=%s\n", escaped);
248 }
3f49d45a 249
558c6490
LP
250 if (s->desktop) {
251 _cleanup_free_ char *escaped;
98a28fef 252
558c6490
LP
253
254 escaped = cescape(s->desktop);
255 if (!escaped) {
256 r = -ENOMEM;
dacd6cee 257 goto fail;
558c6490
LP
258 }
259
260 fprintf(f, "DESKTOP=%s\n", escaped);
261 }
a4cd87e9 262
bf7825ae 263 if (s->seat && seat_has_vts(s->seat))
92bd5ff3 264 fprintf(f, "VTNR=%u\n", s->vtnr);
20263082 265
49e6fdbf 266 if (!s->vtnr)
e6494a07 267 fprintf(f, "POSITION=%u\n", s->position);
49e6fdbf 268
20263082 269 if (s->leader > 0)
90b2de37 270 fprintf(f, "LEADER="PID_FMT"\n", s->leader);
20263082
LP
271
272 if (s->audit_id > 0)
507f22bd 273 fprintf(f, "AUDIT=%"PRIu32"\n", s->audit_id);
20263082 274
9444b1f2
LP
275 if (dual_timestamp_is_set(&s->timestamp))
276 fprintf(f,
90b2de37
ZJS
277 "REALTIME="USEC_FMT"\n"
278 "MONOTONIC="USEC_FMT"\n",
279 s->timestamp.realtime,
280 s->timestamp.monotonic);
9444b1f2 281
6d33772f
DH
282 if (s->controller)
283 fprintf(f, "CONTROLLER=%s\n", s->controller);
284
dacd6cee
LP
285 r = fflush_and_check(f);
286 if (r < 0)
287 goto fail;
14c3baca 288
dacd6cee 289 if (rename(temp_path, s->state_file) < 0) {
20263082 290 r = -errno;
dacd6cee 291 goto fail;
20263082
LP
292 }
293
dacd6cee
LP
294 return 0;
295
296fail:
297 (void) unlink(s->state_file);
14c3baca 298
dacd6cee
LP
299 if (temp_path)
300 (void) unlink(temp_path);
301
302 return log_error_errno(r, "Failed to save session data %s: %m", s->state_file);
20263082
LP
303}
304
dacd6cee 305
20263082 306int session_load(Session *s) {
9444b1f2 307 _cleanup_free_ char *remote = NULL,
a185c5aa
LP
308 *seat = NULL,
309 *vtnr = NULL,
be94d954 310 *state = NULL,
e6494a07 311 *position = NULL,
a185c5aa 312 *leader = NULL,
55efac6c 313 *type = NULL,
9444b1f2
LP
314 *class = NULL,
315 *uid = NULL,
316 *realtime = NULL,
6d33772f
DH
317 *monotonic = NULL,
318 *controller = NULL;
a185c5aa
LP
319
320 int k, r;
321
20263082
LP
322 assert(s);
323
a185c5aa
LP
324 r = parse_env_file(s->state_file, NEWLINE,
325 "REMOTE", &remote,
fb6becb4
LP
326 "SCOPE", &s->scope,
327 "SCOPE_JOB", &s->scope_job,
932e3ee7 328 "FIFO", &s->fifo_path,
a185c5aa
LP
329 "SEAT", &seat,
330 "TTY", &s->tty,
331 "DISPLAY", &s->display,
332 "REMOTE_HOST", &s->remote_host,
333 "REMOTE_USER", &s->remote_user,
98a28fef 334 "SERVICE", &s->service,
a4cd87e9 335 "DESKTOP", &s->desktop,
a185c5aa 336 "VTNR", &vtnr,
be94d954 337 "STATE", &state,
e6494a07 338 "POSITION", &position,
a185c5aa 339 "LEADER", &leader,
a91e4e53 340 "TYPE", &type,
55efac6c 341 "CLASS", &class,
9444b1f2
LP
342 "UID", &uid,
343 "REALTIME", &realtime,
344 "MONOTONIC", &monotonic,
6d33772f 345 "CONTROLLER", &controller,
a185c5aa
LP
346 NULL);
347
f647962d
MS
348 if (r < 0)
349 return log_error_errno(r, "Failed to read %s: %m", s->state_file);
9444b1f2
LP
350
351 if (!s->user) {
352 uid_t u;
353 User *user;
354
355 if (!uid) {
356 log_error("UID not specified for session %s", s->id);
357 return -ENOENT;
358 }
359
360 r = parse_uid(uid, &u);
361 if (r < 0) {
362 log_error("Failed to parse UID value %s for session %s.", uid, s->id);
363 return r;
364 }
365
8cb4ab00 366 user = hashmap_get(s->manager->users, UID_TO_PTR(u));
9444b1f2
LP
367 if (!user) {
368 log_error("User of session %s not known.", s->id);
369 return -ENOENT;
370 }
371
372 session_set_user(s, user);
373 }
a185c5aa
LP
374
375 if (remote) {
376 k = parse_boolean(remote);
377 if (k >= 0)
378 s->remote = k;
379 }
380
c506027a
DH
381 if (vtnr)
382 safe_atou(vtnr, &s->vtnr);
383
9418f147 384 if (seat && !s->seat) {
a185c5aa
LP
385 Seat *o;
386
387 o = hashmap_get(s->manager->seats, seat);
388 if (o)
c506027a
DH
389 r = seat_attach_session(o, s);
390 if (!o || r < 0)
391 log_error("Cannot attach session %s to seat %s", s->id, seat);
a185c5aa
LP
392 }
393
c506027a
DH
394 if (!s->seat || !seat_has_vts(s->seat))
395 s->vtnr = 0;
a185c5aa 396
e6494a07 397 if (position && s->seat) {
49e6fdbf
DH
398 unsigned int npos;
399
e6494a07 400 safe_atou(position, &npos);
49e6fdbf
DH
401 seat_claim_position(s->seat, s, npos);
402 }
403
a185c5aa 404 if (leader) {
f8e2fb7b
LP
405 k = parse_pid(leader, &s->leader);
406 if (k >= 0)
407 audit_session_from_pid(s->leader, &s->audit_id);
a185c5aa
LP
408 }
409
a91e4e53
LP
410 if (type) {
411 SessionType t;
412
413 t = session_type_from_string(type);
414 if (t >= 0)
415 s->type = t;
416 }
417
55efac6c
LP
418 if (class) {
419 SessionClass c;
420
421 c = session_class_from_string(class);
422 if (c >= 0)
423 s->class = c;
424 }
425
be94d954
MP
426 if (state && streq(state, "closing"))
427 s->stopping = true;
428
b4f78aea
LP
429 if (s->fifo_path) {
430 int fd;
431
432 /* If we open an unopened pipe for reading we will not
433 get an EOF. to trigger an EOF we hence open it for
be94d954
MP
434 writing, but close it right away which then will
435 trigger the EOF. This will happen immediately if no
436 other process has the FIFO open for writing, i. e.
437 when the session died before logind (re)started. */
b4f78aea
LP
438
439 fd = session_create_fifo(s);
03e334a1 440 safe_close(fd);
b4f78aea
LP
441 }
442
9444b1f2
LP
443 if (realtime) {
444 unsigned long long l;
445 if (sscanf(realtime, "%llu", &l) > 0)
446 s->timestamp.realtime = l;
447 }
448
449 if (monotonic) {
450 unsigned long long l;
451 if (sscanf(monotonic, "%llu", &l) > 0)
452 s->timestamp.monotonic = l;
453 }
a185c5aa 454
6d33772f
DH
455 if (controller) {
456 if (bus_name_has_owner(s->manager->bus, controller, NULL) > 0)
457 session_set_controller(s, controller, false);
90a18413
DH
458 else
459 session_restore_vt(s);
6d33772f
DH
460 }
461
a185c5aa 462 return r;
20263082
LP
463}
464
465int session_activate(Session *s) {
d7bd01b5
DH
466 unsigned int num_pending;
467
20263082 468 assert(s);
9444b1f2 469 assert(s->user);
20263082 470
20263082 471 if (!s->seat)
15411c0c 472 return -EOPNOTSUPP;
20263082
LP
473
474 if (s->seat->active == s)
475 return 0;
476
d7bd01b5
DH
477 /* on seats with VTs, we let VTs manage session-switching */
478 if (seat_has_vts(s->seat)) {
92bd5ff3 479 if (!s->vtnr)
15411c0c 480 return -EOPNOTSUPP;
d7bd01b5
DH
481
482 return chvt(s->vtnr);
483 }
484
485 /* On seats without VTs, we implement session-switching in logind. We
486 * try to pause all session-devices and wait until the session
487 * controller acknowledged them. Once all devices are asleep, we simply
488 * switch the active session and be done.
489 * We save the session we want to switch to in seat->pending_switch and
490 * seat_complete_switch() will perform the final switch. */
491
492 s->seat->pending_switch = s;
493
494 /* if no devices are running, immediately perform the session switch */
495 num_pending = session_device_try_pause_all(s);
496 if (!num_pending)
497 seat_complete_switch(s->seat);
20263082 498
d7bd01b5 499 return 0;
20263082
LP
500}
501
fb6becb4 502static int session_start_scope(Session *s) {
98a28fef
LP
503 int r;
504
505 assert(s);
9444b1f2 506 assert(s->user);
fb6becb4 507 assert(s->user->slice);
98a28fef 508
fb6becb4 509 if (!s->scope) {
cc377381 510 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
d0af76e6 511 _cleanup_free_ char *description = NULL;
39883f62 512 char *scope, *job = NULL;
d0af76e6 513
405e0255
LP
514 description = strjoin("Session ", s->id, " of user ", s->user->name, NULL);
515 if (!description)
516 return log_oom();
517
d0af76e6
LP
518 scope = strjoin("session-", s->id, ".scope", NULL);
519 if (!scope)
ae018d9b
LP
520 return log_oom();
521
646e392e 522 r = manager_start_scope(s->manager, scope, s->leader, s->user->slice, description, "systemd-logind.service", "systemd-user-sessions.service", &error, &job);
d0af76e6 523 if (r < 0) {
042f5988 524 log_error("Failed to start session scope %s: %s %s",
cc377381 525 scope, bus_error_message(&error, r), error.name);
d0af76e6 526 free(scope);
f2d4f98d 527 return r;
d0af76e6
LP
528 } else {
529 s->scope = scope;
530
531 free(s->scope_job);
532 s->scope_job = job;
533 }
20263082
LP
534 }
535
d0af76e6
LP
536 if (s->scope)
537 hashmap_put(s->manager->session_units, s->scope, s);
538
20263082
LP
539 return 0;
540}
541
542int session_start(Session *s) {
543 int r;
544
545 assert(s);
9444b1f2
LP
546
547 if (!s->user)
548 return -ESTALE;
20263082 549
9418f147
LP
550 if (s->started)
551 return 0;
552
ed18b08b
LP
553 r = user_start(s->user);
554 if (r < 0)
555 return r;
556
fb6becb4
LP
557 /* Create cgroup */
558 r = session_start_scope(s);
559 if (r < 0)
560 return r;
561
d9eb81f9 562 log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
e2cc6eca 563 LOG_MESSAGE_ID(SD_MESSAGE_SESSION_START),
877d54e9
LP
564 "SESSION_ID=%s", s->id,
565 "USER_ID=%s", s->user->name,
de0671ee 566 "LEADER="PID_FMT, s->leader,
e2cc6eca 567 LOG_MESSAGE("New session %s of user %s.", s->id, s->user->name),
877d54e9 568 NULL);
98a28fef 569
9444b1f2
LP
570 if (!dual_timestamp_is_set(&s->timestamp))
571 dual_timestamp_get(&s->timestamp);
14c3baca 572
e9816c48
LP
573 if (s->seat)
574 seat_read_active_vt(s->seat);
575
9418f147
LP
576 s->started = true;
577
952d3260
LP
578 user_elect_display(s->user);
579
5f41d1f1 580 /* Save data */
e9816c48 581 session_save(s);
7f7bb946 582 user_save(s->user);
5f41d1f1
LP
583 if (s->seat)
584 seat_save(s->seat);
e9816c48 585
5f41d1f1 586 /* Send signals */
da119395 587 session_send_signal(s, true);
952d3260 588 user_send_changed(s->user, "Sessions", "Display", NULL);
9418f147
LP
589 if (s->seat) {
590 if (s->seat->active == s)
cc377381 591 seat_send_changed(s->seat, "Sessions", "ActiveSession", NULL);
9418f147 592 else
cc377381 593 seat_send_changed(s->seat, "Sessions", NULL);
9418f147
LP
594 }
595
20263082
LP
596 return 0;
597}
598
9bb69af4 599static int session_stop_scope(Session *s, bool force) {
cc377381 600 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
39883f62 601 char *job = NULL;
20263082 602 int r;
20263082
LP
603
604 assert(s);
605
fb6becb4
LP
606 if (!s->scope)
607 return 0;
9b221b63 608
9bb69af4 609 if (force || manager_shall_kill(s->manager, s->user->name)) {
5f41d1f1
LP
610 r = manager_stop_unit(s->manager, s->scope, &error, &job);
611 if (r < 0) {
612 log_error("Failed to stop session scope: %s", bus_error_message(&error, r));
613 return r;
614 }
20263082 615
5f41d1f1
LP
616 free(s->scope_job);
617 s->scope_job = job;
618 } else {
619 r = manager_abandon_scope(s->manager, s->scope, &error);
620 if (r < 0) {
621 log_error("Failed to abandon session scope: %s", bus_error_message(&error, r));
622 return r;
623 }
624 }
20263082 625
9b221b63 626 return 0;
20263082
LP
627}
628
9bb69af4 629int session_stop(Session *s, bool force) {
405e0255
LP
630 int r;
631
632 assert(s);
633
634 if (!s->user)
635 return -ESTALE;
636
5f41d1f1
LP
637 s->timer_event_source = sd_event_source_unref(s->timer_event_source);
638
10189fd6
DH
639 if (s->seat)
640 seat_evict_position(s->seat, s);
641
5f41d1f1
LP
642 /* We are going down, don't care about FIFOs anymore */
643 session_remove_fifo(s);
644
405e0255 645 /* Kill cgroup */
9bb69af4 646 r = session_stop_scope(s, force);
405e0255 647
5f41d1f1
LP
648 s->stopping = true;
649
952d3260
LP
650 user_elect_display(s->user);
651
405e0255 652 session_save(s);
cc377381 653 user_save(s->user);
405e0255
LP
654
655 return r;
656}
657
658int session_finalize(Session *s) {
659 int r = 0;
118ecf32 660 SessionDevice *sd;
20263082
LP
661
662 assert(s);
663
9444b1f2
LP
664 if (!s->user)
665 return -ESTALE;
666
ed18b08b 667 if (s->started)
d9eb81f9 668 log_struct(s->class == SESSION_BACKGROUND ? LOG_DEBUG : LOG_INFO,
e2cc6eca 669 LOG_MESSAGE_ID(SD_MESSAGE_SESSION_STOP),
877d54e9
LP
670 "SESSION_ID=%s", s->id,
671 "USER_ID=%s", s->user->name,
de0671ee 672 "LEADER="PID_FMT, s->leader,
e2cc6eca 673 LOG_MESSAGE("Removed session %s.", s->id),
877d54e9 674 NULL);
98a28fef 675
5f41d1f1
LP
676 s->timer_event_source = sd_event_source_unref(s->timer_event_source);
677
10189fd6
DH
678 if (s->seat)
679 seat_evict_position(s->seat, s);
680
118ecf32
DH
681 /* Kill session devices */
682 while ((sd = hashmap_first(s->devices)))
683 session_device_free(sd);
684
d2f92cdf
LP
685 unlink(s->state_file);
686 session_add_to_gc_queue(s);
ed18b08b 687 user_add_to_gc_queue(s->user);
14c3baca 688
405e0255 689 if (s->started) {
ed18b08b 690 session_send_signal(s, false);
405e0255
LP
691 s->started = false;
692 }
50fb9793 693
9418f147
LP
694 if (s->seat) {
695 if (s->seat->active == s)
696 seat_set_active(s->seat, NULL);
697
23bd3b62 698 seat_save(s->seat);
5f41d1f1 699 seat_send_changed(s->seat, "Sessions", NULL);
9418f147
LP
700 }
701
23bd3b62 702 user_save(s->user);
952d3260 703 user_send_changed(s->user, "Sessions", "Display", NULL);
9418f147 704
20263082
LP
705 return r;
706}
707
5f41d1f1
LP
708static int release_timeout_callback(sd_event_source *es, uint64_t usec, void *userdata) {
709 Session *s = userdata;
710
711 assert(es);
712 assert(s);
713
9bb69af4 714 session_stop(s, false);
5f41d1f1
LP
715 return 0;
716}
717
ad8780c9 718int session_release(Session *s) {
5f41d1f1
LP
719 assert(s);
720
721 if (!s->started || s->stopping)
ad8780c9
ZJS
722 return 0;
723
724 if (s->timer_event_source)
725 return 0;
726
727 return sd_event_add_time(s->manager->event,
728 &s->timer_event_source,
729 CLOCK_MONOTONIC,
730 now(CLOCK_MONOTONIC) + RELEASE_USEC, 0,
731 release_timeout_callback, s);
5f41d1f1
LP
732}
733
20263082
LP
734bool session_is_active(Session *s) {
735 assert(s);
736
737 if (!s->seat)
738 return true;
739
740 return s->seat->active == s;
741}
742
23406ce5
LP
743static int get_tty_atime(const char *tty, usec_t *atime) {
744 _cleanup_free_ char *p = NULL;
a185c5aa 745 struct stat st;
23406ce5
LP
746
747 assert(tty);
748 assert(atime);
749
750 if (!path_is_absolute(tty)) {
751 p = strappend("/dev/", tty);
752 if (!p)
753 return -ENOMEM;
754
755 tty = p;
756 } else if (!path_startswith(tty, "/dev/"))
757 return -ENOENT;
758
759 if (lstat(tty, &st) < 0)
760 return -errno;
761
762 *atime = timespec_load(&st.st_atim);
763 return 0;
764}
765
766static int get_process_ctty_atime(pid_t pid, usec_t *atime) {
767 _cleanup_free_ char *p = NULL;
768 int r;
769
770 assert(pid > 0);
771 assert(atime);
772
773 r = get_ctty(pid, NULL, &p);
774 if (r < 0)
775 return r;
776
777 return get_tty_atime(p, atime);
778}
779
780int session_get_idle_hint(Session *s, dual_timestamp *t) {
23406ce5
LP
781 usec_t atime = 0, n;
782 int r;
a185c5aa
LP
783
784 assert(s);
785
23406ce5 786 /* Explicit idle hint is set */
a185c5aa
LP
787 if (s->idle_hint) {
788 if (t)
789 *t = s->idle_hint_timestamp;
790
791 return s->idle_hint;
792 }
793
0762eaa3 794 /* Graphical sessions should really implement a real
23406ce5
LP
795 * idle hint logic */
796 if (s->display)
a185c5aa
LP
797 goto dont_know;
798
23406ce5
LP
799 /* For sessions with an explicitly configured tty, let's check
800 * its atime */
801 if (s->tty) {
802 r = get_tty_atime(s->tty, &atime);
803 if (r >= 0)
804 goto found_atime;
805 }
a185c5aa 806
23406ce5
LP
807 /* For sessions with a leader but no explicitly configured
808 * tty, let's check the controlling tty of the leader */
809 if (s->leader > 0) {
810 r = get_process_ctty_atime(s->leader, &atime);
811 if (r >= 0)
812 goto found_atime;
a185c5aa
LP
813 }
814
a185c5aa
LP
815dont_know:
816 if (t)
817 *t = s->idle_hint_timestamp;
818
819 return 0;
23406ce5
LP
820
821found_atime:
822 if (t)
823 dual_timestamp_from_realtime(t, atime);
824
825 n = now(CLOCK_REALTIME);
826
827 if (s->manager->idle_action_usec <= 0)
828 return 0;
829
830 return atime + s->manager->idle_action_usec <= n;
a185c5aa
LP
831}
832
bef422ae
LP
833void session_set_idle_hint(Session *s, bool b) {
834 assert(s);
835
836 if (s->idle_hint == b)
837 return;
838
839 s->idle_hint = b;
840 dual_timestamp_get(&s->idle_hint_timestamp);
9418f147 841
cc377381 842 session_send_changed(s, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
9418f147
LP
843
844 if (s->seat)
cc377381
LP
845 seat_send_changed(s->seat, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
846
847 user_send_changed(s->user, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
848 manager_send_changed(s->manager, "IdleHint", "IdleSinceHint", "IdleSinceHintMonotonic", NULL);
849}
850
851static int session_dispatch_fifo(sd_event_source *es, int fd, uint32_t revents, void *userdata) {
852 Session *s = userdata;
853
854 assert(s);
855 assert(s->fifo_fd == fd);
856
857 /* EOF on the FIFO means the session died abnormally. */
858
859 session_remove_fifo(s);
9bb69af4 860 session_stop(s, false);
cc377381
LP
861
862 return 1;
bef422ae
LP
863}
864
932e3ee7
LP
865int session_create_fifo(Session *s) {
866 int r;
867
31b79c2b
LP
868 assert(s);
869
b4f78aea 870 /* Create FIFO */
932e3ee7 871 if (!s->fifo_path) {
d2e54fae 872 r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
e6061ab2
LP
873 if (r < 0)
874 return r;
875
932e3ee7
LP
876 if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0)
877 return -ENOMEM;
31b79c2b 878
932e3ee7
LP
879 if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
880 return -errno;
881 }
31b79c2b 882
932e3ee7 883 /* Open reading side */
b4f78aea 884 if (s->fifo_fd < 0) {
b4f78aea
LP
885 s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
886 if (s->fifo_fd < 0)
887 return -errno;
888
cc377381
LP
889 }
890
891 if (!s->fifo_event_source) {
151b9b96 892 r = sd_event_add_io(s->manager->event, &s->fifo_event_source, s->fifo_fd, 0, session_dispatch_fifo, s);
b4f78aea
LP
893 if (r < 0)
894 return r;
895
718db961 896 r = sd_event_source_set_priority(s->fifo_event_source, SD_EVENT_PRIORITY_IDLE);
cc377381
LP
897 if (r < 0)
898 return r;
b4f78aea 899 }
932e3ee7
LP
900
901 /* Open writing side */
902 r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
903 if (r < 0)
904 return -errno;
31b79c2b 905
932e3ee7
LP
906 return r;
907}
908
5f41d1f1 909static void session_remove_fifo(Session *s) {
932e3ee7
LP
910 assert(s);
911
03e334a1
LP
912 s->fifo_event_source = sd_event_source_unref(s->fifo_event_source);
913 s->fifo_fd = safe_close(s->fifo_fd);
932e3ee7
LP
914
915 if (s->fifo_path) {
916 unlink(s->fifo_path);
917 free(s->fifo_path);
918 s->fifo_path = NULL;
919 }
31b79c2b
LP
920}
921
cc377381 922bool session_check_gc(Session *s, bool drop_not_started) {
20263082
LP
923 assert(s);
924
4a4b033f 925 if (drop_not_started && !s->started)
cc377381 926 return false;
932e3ee7 927
9444b1f2 928 if (!s->user)
cc377381 929 return false;
9444b1f2 930
932e3ee7 931 if (s->fifo_fd >= 0) {
5f41d1f1 932 if (pipe_eof(s->fifo_fd) <= 0)
cc377381 933 return true;
20263082
LP
934 }
935
cc377381
LP
936 if (s->scope_job && manager_job_is_active(s->manager, s->scope_job))
937 return true;
20263082 938
cc377381
LP
939 if (s->scope && manager_unit_is_active(s->manager, s->scope))
940 return true;
20263082 941
cc377381 942 return false;
20263082
LP
943}
944
14c3baca
LP
945void session_add_to_gc_queue(Session *s) {
946 assert(s);
947
948 if (s->in_gc_queue)
949 return;
950
71fda00f 951 LIST_PREPEND(gc_queue, s->manager->session_gc_queue, s);
14c3baca
LP
952 s->in_gc_queue = true;
953}
954
0604381b
LP
955SessionState session_get_state(Session *s) {
956 assert(s);
957
8fe63cd4 958 /* always check closing first */
5f41d1f1
LP
959 if (s->stopping || s->timer_event_source)
960 return SESSION_CLOSING;
961
8fe63cd4 962 if (s->scope_job || s->fifo_fd < 0)
405e0255 963 return SESSION_OPENING;
fb6becb4 964
0604381b
LP
965 if (session_is_active(s))
966 return SESSION_ACTIVE;
967
968 return SESSION_ONLINE;
969}
970
de07ab16 971int session_kill(Session *s, KillWho who, int signo) {
de07ab16
LP
972 assert(s);
973
fb6becb4 974 if (!s->scope)
de07ab16
LP
975 return -ESRCH;
976
fb6becb4 977 return manager_kill_unit(s->manager, s->scope, who, signo, NULL);
de07ab16
LP
978}
979
90a18413 980static int session_open_vt(Session *s) {
5f41d1f1 981 char path[sizeof("/dev/tty") + DECIMAL_STR_MAX(s->vtnr)];
90a18413 982
baccf3e4
OB
983 if (s->vtnr < 1)
984 return -ENODEV;
90a18413
DH
985
986 if (s->vtfd >= 0)
987 return s->vtfd;
988
92bd5ff3 989 sprintf(path, "/dev/tty%u", s->vtnr);
90a18413 990 s->vtfd = open(path, O_RDWR | O_CLOEXEC | O_NONBLOCK | O_NOCTTY);
4a62c710
MS
991 if (s->vtfd < 0)
992 return log_error_errno(errno, "cannot open VT %s of session %s: %m", path, s->id);
90a18413
DH
993
994 return s->vtfd;
995}
996
baccf3e4 997int session_prepare_vt(Session *s) {
90a18413
DH
998 int vt, r;
999 struct vt_mode mode = { 0 };
90a18413 1000
baccf3e4
OB
1001 if (s->vtnr < 1)
1002 return 0;
1003
90a18413
DH
1004 vt = session_open_vt(s);
1005 if (vt < 0)
baccf3e4 1006 return vt;
90a18413 1007
d6176c6c 1008 r = fchown(vt, s->user->uid, -1);
baccf3e4
OB
1009 if (r < 0) {
1010 r = -errno;
56f64d95 1011 log_error_errno(errno, "Cannot change owner of /dev/tty%u: %m", s->vtnr);
d6176c6c 1012 goto error;
baccf3e4 1013 }
d6176c6c 1014
90a18413 1015 r = ioctl(vt, KDSKBMODE, K_OFF);
baccf3e4
OB
1016 if (r < 0) {
1017 r = -errno;
56f64d95 1018 log_error_errno(errno, "Cannot set K_OFF on /dev/tty%u: %m", s->vtnr);
90a18413 1019 goto error;
baccf3e4 1020 }
90a18413
DH
1021
1022 r = ioctl(vt, KDSETMODE, KD_GRAPHICS);
baccf3e4
OB
1023 if (r < 0) {
1024 r = -errno;
56f64d95 1025 log_error_errno(errno, "Cannot set KD_GRAPHICS on /dev/tty%u: %m", s->vtnr);
90a18413 1026 goto error;
baccf3e4 1027 }
90a18413 1028
90a18413
DH
1029 /* Oh, thanks to the VT layer, VT_AUTO does not work with KD_GRAPHICS.
1030 * So we need a dummy handler here which just acknowledges *all* VT
1031 * switch requests. */
1032 mode.mode = VT_PROCESS;
92683ad2
DH
1033 mode.relsig = SIGRTMIN;
1034 mode.acqsig = SIGRTMIN + 1;
90a18413 1035 r = ioctl(vt, VT_SETMODE, &mode);
baccf3e4
OB
1036 if (r < 0) {
1037 r = -errno;
56f64d95 1038 log_error_errno(errno, "Cannot set VT_PROCESS on /dev/tty%u: %m", s->vtnr);
90a18413 1039 goto error;
baccf3e4 1040 }
90a18413 1041
baccf3e4 1042 return 0;
90a18413
DH
1043
1044error:
90a18413 1045 session_restore_vt(s);
baccf3e4 1046 return r;
90a18413
DH
1047}
1048
1049void session_restore_vt(Session *s) {
a0eb2a75 1050 _cleanup_free_ char *utf8 = NULL;
90a18413
DH
1051 int vt, kb = K_XLATE;
1052 struct vt_mode mode = { 0 };
1053
1054 vt = session_open_vt(s);
1055 if (vt < 0)
1056 return;
1057
2bf10523 1058 (void) ioctl(vt, KDSETMODE, KD_TEXT);
90a18413
DH
1059
1060 if (read_one_line_file("/sys/module/vt/parameters/default_utf8", &utf8) >= 0 && *utf8 == '1')
1061 kb = K_UNICODE;
03e334a1 1062
2bf10523 1063 (void) ioctl(vt, KDSKBMODE, kb);
90a18413
DH
1064
1065 mode.mode = VT_AUTO;
2bf10523 1066 (void) ioctl(vt, VT_SETMODE, &mode);
90a18413 1067
d6176c6c
RS
1068 fchown(vt, 0, -1);
1069
03e334a1 1070 s->vtfd = safe_close(s->vtfd);
90a18413
DH
1071}
1072
2ec3ff66 1073void session_leave_vt(Session *s) {
ce540a24
DH
1074 int r;
1075
2ec3ff66
DH
1076 assert(s);
1077
1078 /* This is called whenever we get a VT-switch signal from the kernel.
1079 * We acknowledge all of them unconditionally. Note that session are
1080 * free to overwrite those handlers and we only register them for
1081 * sessions with controllers. Legacy sessions are not affected.
1082 * However, if we switch from a non-legacy to a legacy session, we must
1083 * make sure to pause all device before acknowledging the switch. We
1084 * process the real switch only after we are notified via sysfs, so the
1085 * legacy session might have already started using the devices. If we
1086 * don't pause the devices before the switch, we might confuse the
1087 * session we switch to. */
1088
1089 if (s->vtfd < 0)
1090 return;
1091
1092 session_device_pause_all(s);
ce540a24
DH
1093 r = ioctl(s->vtfd, VT_RELDISP, 1);
1094 if (r < 0)
56f64d95 1095 log_debug_errno(errno, "Cannot release VT of session %s: %m", s->id);
2ec3ff66
DH
1096}
1097
cc377381 1098bool session_is_controller(Session *s, const char *sender) {
ae5e06bd
DH
1099 assert(s);
1100
1101 return streq_ptr(s->controller, sender);
1102}
1103
b12e5615
DH
1104static void session_release_controller(Session *s, bool notify) {
1105 _cleanup_free_ char *name = NULL;
6d33772f
DH
1106 SessionDevice *sd;
1107
b12e5615
DH
1108 if (!s->controller)
1109 return;
6d33772f 1110
b12e5615 1111 name = s->controller;
90a18413 1112
b12e5615
DH
1113 /* By resetting the controller before releasing the devices, we won't
1114 * send notification signals. This avoids sending useless notifications
1115 * if the controller is released on disconnects. */
1116 if (!notify)
1117 s->controller = NULL;
6d33772f 1118
b12e5615
DH
1119 while ((sd = hashmap_first(s->devices)))
1120 session_device_free(sd);
1121
1122 s->controller = NULL;
3cde9e8f
DM
1123 s->track = sd_bus_track_unref(s->track);
1124}
1125
1126static int on_bus_track(sd_bus_track *track, void *userdata) {
1127 Session *s = userdata;
1128
1129 assert(track);
1130 assert(s);
1131
1132 session_drop_controller(s);
1133
1134 return 0;
6d33772f
DH
1135}
1136
ae5e06bd 1137int session_set_controller(Session *s, const char *sender, bool force) {
b12e5615 1138 _cleanup_free_ char *name = NULL;
ae5e06bd
DH
1139 int r;
1140
1141 assert(s);
1142 assert(sender);
1143
1144 if (session_is_controller(s, sender))
1145 return 0;
1146 if (s->controller && !force)
1147 return -EBUSY;
1148
b12e5615
DH
1149 name = strdup(sender);
1150 if (!name)
ae5e06bd
DH
1151 return -ENOMEM;
1152
3cde9e8f
DM
1153 s->track = sd_bus_track_unref(s->track);
1154 r = sd_bus_track_new(s->manager->bus, &s->track, on_bus_track, s);
1155 if (r < 0)
1156 return r;
1157
1158 r = sd_bus_track_add_name(s->track, name);
1159 if (r < 0)
ae5e06bd 1160 return r;
ae5e06bd 1161
90a18413
DH
1162 /* When setting a session controller, we forcibly mute the VT and set
1163 * it into graphics-mode. Applications can override that by changing
1164 * VT state after calling TakeControl(). However, this serves as a good
1165 * default and well-behaving controllers can now ignore VTs entirely.
1166 * Note that we reset the VT on ReleaseControl() and if the controller
1167 * exits.
1168 * If logind crashes/restarts, we restore the controller during restart
1169 * or reset the VT in case it crashed/exited, too. */
baccf3e4 1170 r = session_prepare_vt(s);
13f493dc 1171 if (r < 0) {
3cde9e8f 1172 s->track = sd_bus_track_unref(s->track);
baccf3e4 1173 return r;
13f493dc 1174 }
baccf3e4 1175
b12e5615
DH
1176 session_release_controller(s, true);
1177 s->controller = name;
1178 name = NULL;
1179 session_save(s);
90a18413 1180
ae5e06bd
DH
1181 return 0;
1182}
1183
1184void session_drop_controller(Session *s) {
1185 assert(s);
1186
1187 if (!s->controller)
1188 return;
1189
3cde9e8f 1190 s->track = sd_bus_track_unref(s->track);
b12e5615
DH
1191 session_release_controller(s, false);
1192 session_save(s);
1193 session_restore_vt(s);
ae5e06bd
DH
1194}
1195
fb6becb4
LP
1196static const char* const session_state_table[_SESSION_STATE_MAX] = {
1197 [SESSION_OPENING] = "opening",
0604381b
LP
1198 [SESSION_ONLINE] = "online",
1199 [SESSION_ACTIVE] = "active",
1200 [SESSION_CLOSING] = "closing"
1201};
1202
1203DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState);
1204
20263082 1205static const char* const session_type_table[_SESSION_TYPE_MAX] = {
2c5859af 1206 [SESSION_UNSPECIFIED] = "unspecified",
3f49d45a 1207 [SESSION_TTY] = "tty",
98a28fef 1208 [SESSION_X11] = "x11",
d9eb81f9 1209 [SESSION_WAYLAND] = "wayland",
9541666b 1210 [SESSION_MIR] = "mir",
e9e74f28 1211 [SESSION_WEB] = "web",
20263082
LP
1212};
1213
1214DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
de07ab16 1215
55efac6c
LP
1216static const char* const session_class_table[_SESSION_CLASS_MAX] = {
1217 [SESSION_USER] = "user",
1218 [SESSION_GREETER] = "greeter",
e2acb67b
LP
1219 [SESSION_LOCK_SCREEN] = "lock-screen",
1220 [SESSION_BACKGROUND] = "background"
55efac6c
LP
1221};
1222
1223DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
1224
de07ab16
LP
1225static const char* const kill_who_table[_KILL_WHO_MAX] = {
1226 [KILL_LEADER] = "leader",
1227 [KILL_ALL] = "all"
1228};
1229
1230DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);