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