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