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