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