]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-session.c
nspawn: introduce the new /machine/ tree in the cgroup tree and move containers there
[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>
23#include <string.h>
24#include <unistd.h>
31b79c2b 25#include <sys/epoll.h>
932e3ee7 26#include <fcntl.h>
20263082 27
877d54e9
LP
28#include "systemd/sd-id128.h"
29#include "systemd/sd-messages.h"
20263082
LP
30#include "strv.h"
31#include "util.h"
49e942b2 32#include "mkdir.h"
9eb977db 33#include "path-util.h"
20263082 34#include "cgroup-util.h"
f8e2fb7b 35#include "logind-session.h"
a5c32cff 36#include "fileio.h"
20263082
LP
37
38Session* session_new(Manager *m, User *u, const char *id) {
39 Session *s;
40
41 assert(m);
42 assert(id);
43
14c3baca 44 s = new0(Session, 1);
20263082
LP
45 if (!s)
46 return NULL;
47
98a28fef 48 s->state_file = strappend("/run/systemd/sessions/", id);
20263082
LP
49 if (!s->state_file) {
50 free(s);
51 return NULL;
52 }
53
9eb977db 54 s->id = path_get_file_name(s->state_file);
20263082
LP
55
56 if (hashmap_put(m->sessions, s->id, s) < 0) {
f8e2fb7b 57 free(s->state_file);
20263082
LP
58 free(s);
59 return NULL;
60 }
61
62 s->manager = m;
932e3ee7 63 s->fifo_fd = -1;
20263082
LP
64 s->user = u;
65
14c3baca 66 LIST_PREPEND(Session, sessions_by_user, u->sessions, s);
20263082
LP
67
68 return s;
69}
70
71void session_free(Session *s) {
72 assert(s);
73
14c3baca
LP
74 if (s->in_gc_queue)
75 LIST_REMOVE(Session, gc_queue, s->manager->session_gc_queue, s);
76
20263082
LP
77 if (s->user) {
78 LIST_REMOVE(Session, sessions_by_user, s->user->sessions, s);
79
80 if (s->user->display == s)
81 s->user->display = NULL;
82 }
83
9418f147
LP
84 if (s->seat) {
85 if (s->seat->active == s)
86 s->seat->active = NULL;
87
20263082 88 LIST_REMOVE(Session, sessions_by_seat, s->seat->sessions, s);
9418f147 89 }
20263082 90
1713813d 91 if (s->cgroup_path)
8c8c4351 92 hashmap_remove(s->manager->session_cgroups, s->cgroup_path);
1713813d 93
20263082
LP
94 free(s->cgroup_path);
95 strv_free(s->controllers);
96
97 free(s->tty);
98 free(s->display);
99 free(s->remote_host);
3f49d45a 100 free(s->remote_user);
98a28fef 101 free(s->service);
20263082
LP
102
103 hashmap_remove(s->manager->sessions, s->id);
932e3ee7 104 session_remove_fifo(s);
98a28fef 105
d2f92cdf 106 free(s->state_file);
20263082
LP
107 free(s);
108}
109
110int session_save(Session *s) {
111 FILE *f;
112 int r = 0;
14c3baca 113 char *temp_path;
20263082
LP
114
115 assert(s);
116
accaeded
LP
117 if (!s->started)
118 return 0;
119
d2e54fae 120 r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
20263082 121 if (r < 0)
14c3baca 122 goto finish;
20263082 123
14c3baca
LP
124 r = fopen_temporary(s->state_file, &f, &temp_path);
125 if (r < 0)
126 goto finish;
20263082
LP
127
128 assert(s->user);
129
14c3baca
LP
130 fchmod(fileno(f), 0644);
131
20263082
LP
132 fprintf(f,
133 "# This is private data. Do not parse.\n"
134 "UID=%lu\n"
135 "USER=%s\n"
136 "ACTIVE=%i\n"
0604381b 137 "STATE=%s\n"
20263082
LP
138 "REMOTE=%i\n"
139 "KILL_PROCESSES=%i\n",
140 (unsigned long) s->user->uid,
141 s->user->name,
142 session_is_active(s),
0604381b 143 session_state_to_string(session_get_state(s)),
20263082
LP
144 s->remote,
145 s->kill_processes);
146
a91e4e53
LP
147 if (s->type >= 0)
148 fprintf(f,
149 "TYPE=%s\n",
150 session_type_to_string(s->type));
151
55efac6c
LP
152 if (s->class >= 0)
153 fprintf(f,
154 "CLASS=%s\n",
155 session_class_to_string(s->class));
156
20263082
LP
157 if (s->cgroup_path)
158 fprintf(f,
159 "CGROUP=%s\n",
160 s->cgroup_path);
161
932e3ee7
LP
162 if (s->fifo_path)
163 fprintf(f,
164 "FIFO=%s\n",
165 s->fifo_path);
166
20263082
LP
167 if (s->seat)
168 fprintf(f,
169 "SEAT=%s\n",
170 s->seat->id);
171
172 if (s->tty)
173 fprintf(f,
174 "TTY=%s\n",
175 s->tty);
176
177 if (s->display)
178 fprintf(f,
179 "DISPLAY=%s\n",
180 s->display);
181
182 if (s->remote_host)
183 fprintf(f,
184 "REMOTE_HOST=%s\n",
185 s->remote_host);
186
3f49d45a
LP
187 if (s->remote_user)
188 fprintf(f,
189 "REMOTE_USER=%s\n",
190 s->remote_user);
191
98a28fef
LP
192 if (s->service)
193 fprintf(f,
194 "SERVICE=%s\n",
195 s->service);
196
addedec4 197 if (s->seat && seat_can_multi_session(s->seat))
20263082
LP
198 fprintf(f,
199 "VTNR=%i\n",
200 s->vtnr);
201
202 if (s->leader > 0)
203 fprintf(f,
204 "LEADER=%lu\n",
205 (unsigned long) s->leader);
206
207 if (s->audit_id > 0)
208 fprintf(f,
209 "AUDIT=%llu\n",
210 (unsigned long long) s->audit_id);
211
212 fflush(f);
14c3baca
LP
213
214 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
20263082
LP
215 r = -errno;
216 unlink(s->state_file);
14c3baca 217 unlink(temp_path);
20263082
LP
218 }
219
220 fclose(f);
14c3baca
LP
221 free(temp_path);
222
223finish:
224 if (r < 0)
225 log_error("Failed to save session data for %s: %s", s->id, strerror(-r));
226
20263082
LP
227 return r;
228}
229
230int session_load(Session *s) {
a185c5aa
LP
231 char *remote = NULL,
232 *kill_processes = NULL,
233 *seat = NULL,
234 *vtnr = NULL,
235 *leader = NULL,
a91e4e53 236 *audit_id = NULL,
55efac6c
LP
237 *type = NULL,
238 *class = NULL;
a185c5aa
LP
239
240 int k, r;
241
20263082
LP
242 assert(s);
243
a185c5aa
LP
244 r = parse_env_file(s->state_file, NEWLINE,
245 "REMOTE", &remote,
246 "KILL_PROCESSES", &kill_processes,
247 "CGROUP", &s->cgroup_path,
932e3ee7 248 "FIFO", &s->fifo_path,
a185c5aa
LP
249 "SEAT", &seat,
250 "TTY", &s->tty,
251 "DISPLAY", &s->display,
252 "REMOTE_HOST", &s->remote_host,
253 "REMOTE_USER", &s->remote_user,
98a28fef 254 "SERVICE", &s->service,
a185c5aa
LP
255 "VTNR", &vtnr,
256 "LEADER", &leader,
a91e4e53 257 "TYPE", &type,
55efac6c 258 "CLASS", &class,
a185c5aa
LP
259 NULL);
260
261 if (r < 0)
262 goto finish;
263
264 if (remote) {
265 k = parse_boolean(remote);
266 if (k >= 0)
267 s->remote = k;
268 }
269
270 if (kill_processes) {
271 k = parse_boolean(kill_processes);
272 if (k >= 0)
273 s->kill_processes = k;
274 }
275
9418f147 276 if (seat && !s->seat) {
a185c5aa
LP
277 Seat *o;
278
279 o = hashmap_get(s->manager->seats, seat);
280 if (o)
281 seat_attach_session(o, s);
282 }
283
addedec4 284 if (vtnr && s->seat && seat_can_multi_session(s->seat)) {
a185c5aa
LP
285 int v;
286
287 k = safe_atoi(vtnr, &v);
288 if (k >= 0 && v >= 1)
289 s->vtnr = v;
290 }
291
292 if (leader) {
f8e2fb7b
LP
293 k = parse_pid(leader, &s->leader);
294 if (k >= 0)
295 audit_session_from_pid(s->leader, &s->audit_id);
a185c5aa
LP
296 }
297
a91e4e53
LP
298 if (type) {
299 SessionType t;
300
301 t = session_type_from_string(type);
302 if (t >= 0)
303 s->type = t;
304 }
305
55efac6c
LP
306 if (class) {
307 SessionClass c;
308
309 c = session_class_from_string(class);
310 if (c >= 0)
311 s->class = c;
312 }
313
b4f78aea
LP
314 if (s->fifo_path) {
315 int fd;
316
317 /* If we open an unopened pipe for reading we will not
318 get an EOF. to trigger an EOF we hence open it for
319 reading, but close it right-away which then will
320 trigger the EOF. */
321
322 fd = session_create_fifo(s);
323 if (fd >= 0)
324 close_nointr_nofail(fd);
325 }
326
a185c5aa
LP
327finish:
328 free(remote);
329 free(kill_processes);
330 free(seat);
331 free(vtnr);
332 free(leader);
333 free(audit_id);
b80efeb9 334 free(class);
a185c5aa
LP
335
336 return r;
20263082
LP
337}
338
339int session_activate(Session *s) {
340 int r;
341
342 assert(s);
343
344 if (s->vtnr < 0)
345 return -ENOTSUP;
346
347 if (!s->seat)
348 return -ENOTSUP;
349
350 if (s->seat->active == s)
351 return 0;
352
a185c5aa 353 assert(seat_is_vtconsole(s->seat));
20263082
LP
354
355 r = chvt(s->vtnr);
356 if (r < 0)
357 return r;
358
129eebe0 359 return seat_set_active(s->seat, s);
20263082
LP
360}
361
20263082
LP
362static int session_link_x11_socket(Session *s) {
363 char *t, *f, *c;
364 size_t k;
365
366 assert(s);
367 assert(s->user);
368 assert(s->user->runtime_path);
369
370 if (s->user->display)
371 return 0;
372
4d6d6518 373 if (!s->display || !display_is_local(s->display))
20263082
LP
374 return 0;
375
376 k = strspn(s->display+1, "0123456789");
377 f = new(char, sizeof("/tmp/.X11-unix/X") + k);
0d0f0c50
SL
378 if (!f)
379 return log_oom();
20263082
LP
380
381 c = stpcpy(f, "/tmp/.X11-unix/X");
382 memcpy(c, s->display+1, k);
383 c[k] = 0;
384
385 if (access(f, F_OK) < 0) {
6ccf7562 386 log_warning("Session %s has display %s with non-existing socket %s.", s->id, s->display, f);
20263082
LP
387 free(f);
388 return -ENOENT;
389 }
390
c9d8629b
LP
391 /* Note that this cannot be in a subdir to avoid
392 * vulnerabilities since we are privileged but the runtime
393 * path is owned by the user */
394
fc3c1c6e 395 t = strappend(s->user->runtime_path, "/X11-display");
20263082 396 if (!t) {
20263082 397 free(f);
0d0f0c50 398 return log_oom();
20263082
LP
399 }
400
401 if (link(f, t) < 0) {
402 if (errno == EEXIST) {
403 unlink(t);
404
405 if (link(f, t) >= 0)
406 goto done;
407 }
408
409 if (symlink(f, t) < 0) {
410
411 if (errno == EEXIST) {
412 unlink(t);
413
414 if (symlink(f, t) >= 0)
415 goto done;
416 }
417
418 log_error("Failed to link %s to %s: %m", f, t);
419 free(f);
420 free(t);
421 return -errno;
422 }
423 }
424
425done:
426 log_info("Linked %s to %s.", f, t);
427 free(f);
428 free(t);
429
430 s->user->display = s;
431
432 return 0;
433}
434
98a28fef
LP
435static int session_create_one_group(Session *s, const char *controller, const char *path) {
436 int r;
437
438 assert(s);
98a28fef
LP
439 assert(path);
440
b6f68af1 441 if (s->leader > 0) {
98a28fef 442 r = cg_create_and_attach(controller, path, s->leader);
b6f68af1 443 if (r < 0)
a32360f1 444 r = cg_create(controller, path, NULL);
b6f68af1 445 } else
a32360f1 446 r = cg_create(controller, path, NULL);
98a28fef
LP
447
448 if (r < 0)
449 return r;
450
8d53b453 451 r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid, -1);
98a28fef
LP
452 if (r >= 0)
453 r = cg_set_group_access(controller, path, 0755, s->user->uid, s->user->gid);
454
455 return r;
456}
457
20263082
LP
458static int session_create_cgroup(Session *s) {
459 char **k;
460 char *p;
461 int r;
462
463 assert(s);
464 assert(s->user);
465 assert(s->user->cgroup_path);
466
467 if (!s->cgroup_path) {
0d0f0c50
SL
468 if (asprintf(&p, "%s/%s", s->user->cgroup_path, s->id) < 0)
469 return log_oom();
20263082
LP
470 } else
471 p = s->cgroup_path;
472
98a28fef 473 r = session_create_one_group(s, SYSTEMD_CGROUP_CONTROLLER, p);
20263082 474 if (r < 0) {
4d6d6518 475 log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
20263082
LP
476 free(p);
477 s->cgroup_path = NULL;
20263082
LP
478 return r;
479 }
480
481 s->cgroup_path = p;
482
98a28fef
LP
483 STRV_FOREACH(k, s->controllers) {
484
485 if (strv_contains(s->reset_controllers, *k))
486 continue;
487
488 r = session_create_one_group(s, *k, p);
489 if (r < 0)
490 log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
491 }
492
20263082 493 STRV_FOREACH(k, s->manager->controllers) {
20263082 494
98a28fef 495 if (strv_contains(s->reset_controllers, *k) ||
193197e8 496 strv_contains(s->manager->reset_controllers, *k) ||
98a28fef
LP
497 strv_contains(s->controllers, *k))
498 continue;
499
500 r = session_create_one_group(s, *k, p);
20263082 501 if (r < 0)
98a28fef
LP
502 log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
503 }
504
505 if (s->leader > 0) {
506
507 STRV_FOREACH(k, s->reset_controllers) {
508 r = cg_attach(*k, "/", s->leader);
509 if (r < 0)
510 log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
511
512 }
193197e8
LP
513
514 STRV_FOREACH(k, s->manager->reset_controllers) {
515
516 if (strv_contains(s->reset_controllers, *k) ||
517 strv_contains(s->controllers, *k))
518 continue;
519
520 r = cg_attach(*k, "/", s->leader);
521 if (r < 0)
522 log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
523
524 }
20263082
LP
525 }
526
5a165aa6
VP
527 r = hashmap_put(s->manager->session_cgroups, s->cgroup_path, s);
528 if (r < 0)
529 log_warning("Failed to create mapping between cgroup and session");
1713813d 530
20263082
LP
531 return 0;
532}
533
534int session_start(Session *s) {
535 int r;
536
537 assert(s);
538 assert(s->user);
539
9418f147
LP
540 if (s->started)
541 return 0;
542
ed18b08b
LP
543 r = user_start(s->user);
544 if (r < 0)
545 return r;
546
877d54e9 547 log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
1ca6783f 548 MESSAGE_ID(SD_MESSAGE_SESSION_START),
877d54e9
LP
549 "SESSION_ID=%s", s->id,
550 "USER_ID=%s", s->user->name,
551 "LEADER=%lu", (unsigned long) s->leader,
552 "MESSAGE=New session %s of user %s.", s->id, s->user->name,
553 NULL);
98a28fef 554
20263082
LP
555 /* Create cgroup */
556 r = session_create_cgroup(s);
557 if (r < 0)
558 return r;
559
560 /* Create X11 symlink */
561 session_link_x11_socket(s);
14c3baca 562
14c3baca
LP
563 dual_timestamp_get(&s->timestamp);
564
e9816c48
LP
565 if (s->seat)
566 seat_read_active_vt(s->seat);
567
9418f147
LP
568 s->started = true;
569
e9816c48
LP
570 /* Save session data */
571 session_save(s);
7f7bb946 572 user_save(s->user);
e9816c48 573
da119395
LP
574 session_send_signal(s, true);
575
9418f147 576 if (s->seat) {
7f7bb946
LP
577 seat_save(s->seat);
578
9418f147
LP
579 if (s->seat->active == s)
580 seat_send_changed(s->seat, "Sessions\0ActiveSession\0");
581 else
582 seat_send_changed(s->seat, "Sessions\0");
583 }
584
585 user_send_changed(s->user, "Sessions\0");
586
20263082
LP
587 return 0;
588}
589
590static bool session_shall_kill(Session *s) {
591 assert(s);
592
ed18b08b
LP
593 if (!s->kill_processes)
594 return false;
595
596 if (strv_contains(s->manager->kill_exclude_users, s->user->name))
597 return false;
598
599 if (strv_isempty(s->manager->kill_only_users))
600 return true;
601
602 return strv_contains(s->manager->kill_only_users, s->user->name);
20263082
LP
603}
604
de07ab16 605static int session_terminate_cgroup(Session *s) {
20263082
LP
606 int r;
607 char **k;
608
609 assert(s);
610
611 if (!s->cgroup_path)
612 return 0;
613
614 cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
615
616 if (session_shall_kill(s)) {
617
618 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
619 if (r < 0)
620 log_error("Failed to kill session cgroup: %s", strerror(-r));
621
622 } else {
9b221b63
LP
623 if (s->leader > 0) {
624 Session *t;
625
626 /* We still send a HUP to the leader process,
627 * even if we are not supposed to kill the
628 * whole cgroup. But let's first check the
629 * leader still exists and belongs to our
630 * session... */
631
632 r = manager_get_session_by_pid(s->manager, s->leader, &t);
633 if (r > 0 && t == s) {
634 kill(s->leader, SIGTERM); /* for normal processes */
635 kill(s->leader, SIGHUP); /* for shells */
636 kill(s->leader, SIGCONT); /* in case they are stopped */
637 }
638 }
639
20263082
LP
640 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
641 if (r < 0)
642 log_error("Failed to check session cgroup: %s", strerror(-r));
643 else if (r > 0) {
644 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path);
645 if (r < 0)
646 log_error("Failed to delete session cgroup: %s", strerror(-r));
9b221b63 647 }
20263082
LP
648 }
649
650 STRV_FOREACH(k, s->user->manager->controllers)
651 cg_trim(*k, s->cgroup_path, true);
652
8c8c4351 653 hashmap_remove(s->manager->session_cgroups, s->cgroup_path);
1713813d 654
20263082
LP
655 free(s->cgroup_path);
656 s->cgroup_path = NULL;
657
9b221b63 658 return 0;
20263082
LP
659}
660
661static int session_unlink_x11_socket(Session *s) {
662 char *t;
663 int r;
664
665 assert(s);
666 assert(s->user);
667
668 if (s->user->display != s)
669 return 0;
670
671 s->user->display = NULL;
672
fc3c1c6e 673 t = strappend(s->user->runtime_path, "/X11-display");
0d0f0c50
SL
674 if (!t)
675 return log_oom();
20263082
LP
676
677 r = unlink(t);
678 free(t);
679
680 return r < 0 ? -errno : 0;
681}
682
683int session_stop(Session *s) {
684 int r = 0, k;
685
686 assert(s);
687
ed18b08b 688 if (s->started)
877d54e9 689 log_struct(s->type == SESSION_TTY || s->type == SESSION_X11 ? LOG_INFO : LOG_DEBUG,
1ca6783f 690 MESSAGE_ID(SD_MESSAGE_SESSION_STOP),
877d54e9
LP
691 "SESSION_ID=%s", s->id,
692 "USER_ID=%s", s->user->name,
693 "LEADER=%lu", (unsigned long) s->leader,
694 "MESSAGE=Removed session %s.", s->id,
695 NULL);
98a28fef 696
20263082 697 /* Kill cgroup */
de07ab16 698 k = session_terminate_cgroup(s);
20263082
LP
699 if (k < 0)
700 r = k;
701
702 /* Remove X11 symlink */
703 session_unlink_x11_socket(s);
704
d2f92cdf
LP
705 unlink(s->state_file);
706 session_add_to_gc_queue(s);
ed18b08b 707 user_add_to_gc_queue(s->user);
14c3baca 708
ed18b08b
LP
709 if (s->started)
710 session_send_signal(s, false);
9418f147 711
50fb9793
F
712 s->started = false;
713
9418f147
LP
714 if (s->seat) {
715 if (s->seat->active == s)
716 seat_set_active(s->seat, NULL);
717
718 seat_send_changed(s->seat, "Sessions\0");
23bd3b62 719 seat_save(s->seat);
9418f147
LP
720 }
721
722 user_send_changed(s->user, "Sessions\0");
23bd3b62 723 user_save(s->user);
9418f147 724
20263082
LP
725 return r;
726}
727
728bool session_is_active(Session *s) {
729 assert(s);
730
731 if (!s->seat)
732 return true;
733
734 return s->seat->active == s;
735}
736
23406ce5
LP
737static int get_tty_atime(const char *tty, usec_t *atime) {
738 _cleanup_free_ char *p = NULL;
a185c5aa 739 struct stat st;
23406ce5
LP
740
741 assert(tty);
742 assert(atime);
743
744 if (!path_is_absolute(tty)) {
745 p = strappend("/dev/", tty);
746 if (!p)
747 return -ENOMEM;
748
749 tty = p;
750 } else if (!path_startswith(tty, "/dev/"))
751 return -ENOENT;
752
753 if (lstat(tty, &st) < 0)
754 return -errno;
755
756 *atime = timespec_load(&st.st_atim);
757 return 0;
758}
759
760static int get_process_ctty_atime(pid_t pid, usec_t *atime) {
761 _cleanup_free_ char *p = NULL;
762 int r;
763
764 assert(pid > 0);
765 assert(atime);
766
767 r = get_ctty(pid, NULL, &p);
768 if (r < 0)
769 return r;
770
771 return get_tty_atime(p, atime);
772}
773
774int session_get_idle_hint(Session *s, dual_timestamp *t) {
23406ce5
LP
775 usec_t atime = 0, n;
776 int r;
a185c5aa
LP
777
778 assert(s);
779
23406ce5 780 /* Explicit idle hint is set */
a185c5aa
LP
781 if (s->idle_hint) {
782 if (t)
783 *t = s->idle_hint_timestamp;
784
785 return s->idle_hint;
786 }
787
0762eaa3 788 /* Graphical sessions should really implement a real
23406ce5
LP
789 * idle hint logic */
790 if (s->display)
a185c5aa
LP
791 goto dont_know;
792
23406ce5
LP
793 /* For sessions with an explicitly configured tty, let's check
794 * its atime */
795 if (s->tty) {
796 r = get_tty_atime(s->tty, &atime);
797 if (r >= 0)
798 goto found_atime;
799 }
a185c5aa 800
23406ce5
LP
801 /* For sessions with a leader but no explicitly configured
802 * tty, let's check the controlling tty of the leader */
803 if (s->leader > 0) {
804 r = get_process_ctty_atime(s->leader, &atime);
805 if (r >= 0)
806 goto found_atime;
a185c5aa
LP
807 }
808
23406ce5
LP
809 /* For other TTY sessions, let's find the most recent atime of
810 * the ttys of any of the processes of the session */
811 if (s->cgroup_path) {
812 _cleanup_fclose_ FILE *f = NULL;
a185c5aa 813
23406ce5
LP
814 if (cg_enumerate_processes(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, &f) >= 0) {
815 pid_t pid;
a185c5aa 816
23406ce5
LP
817 atime = 0;
818 while (cg_read_pid(f, &pid) > 0) {
819 usec_t a;
a185c5aa 820
23406ce5
LP
821 if (get_process_ctty_atime(pid, &a) >= 0)
822 if (atime == 0 || atime < a)
823 atime = a;
824 }
a185c5aa 825
23406ce5
LP
826 if (atime != 0)
827 goto found_atime;
828 }
829 }
a185c5aa
LP
830
831dont_know:
832 if (t)
833 *t = s->idle_hint_timestamp;
834
835 return 0;
23406ce5
LP
836
837found_atime:
838 if (t)
839 dual_timestamp_from_realtime(t, atime);
840
841 n = now(CLOCK_REALTIME);
842
843 if (s->manager->idle_action_usec <= 0)
844 return 0;
845
846 return atime + s->manager->idle_action_usec <= n;
a185c5aa
LP
847}
848
bef422ae
LP
849void session_set_idle_hint(Session *s, bool b) {
850 assert(s);
851
852 if (s->idle_hint == b)
853 return;
854
855 s->idle_hint = b;
856 dual_timestamp_get(&s->idle_hint_timestamp);
9418f147
LP
857
858 session_send_changed(s,
859 "IdleHint\0"
860 "IdleSinceHint\0"
861 "IdleSinceHintMonotonic\0");
862
863 if (s->seat)
864 seat_send_changed(s->seat,
865 "IdleHint\0"
866 "IdleSinceHint\0"
867 "IdleSinceHintMonotonic\0");
868
869 user_send_changed(s->user,
870 "IdleHint\0"
871 "IdleSinceHint\0"
872 "IdleSinceHintMonotonic\0");
873
874 manager_send_changed(s->manager,
875 "IdleHint\0"
876 "IdleSinceHint\0"
877 "IdleSinceHintMonotonic\0");
bef422ae
LP
878}
879
932e3ee7
LP
880int session_create_fifo(Session *s) {
881 int r;
882
31b79c2b
LP
883 assert(s);
884
b4f78aea 885 /* Create FIFO */
932e3ee7 886 if (!s->fifo_path) {
d2e54fae 887 r = mkdir_safe_label("/run/systemd/sessions", 0755, 0, 0);
e6061ab2
LP
888 if (r < 0)
889 return r;
890
932e3ee7
LP
891 if (asprintf(&s->fifo_path, "/run/systemd/sessions/%s.ref", s->id) < 0)
892 return -ENOMEM;
31b79c2b 893
932e3ee7
LP
894 if (mkfifo(s->fifo_path, 0600) < 0 && errno != EEXIST)
895 return -errno;
896 }
31b79c2b 897
932e3ee7 898 /* Open reading side */
b4f78aea 899 if (s->fifo_fd < 0) {
b92bea5d 900 struct epoll_event ev = {};
b4f78aea
LP
901
902 s->fifo_fd = open(s->fifo_path, O_RDONLY|O_CLOEXEC|O_NDELAY);
903 if (s->fifo_fd < 0)
904 return -errno;
905
f8e2fb7b 906 r = hashmap_put(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1), s);
b4f78aea
LP
907 if (r < 0)
908 return r;
909
b4f78aea 910 ev.events = 0;
069cfc85 911 ev.data.u32 = FD_OTHER_BASE + s->fifo_fd;
b4f78aea
LP
912
913 if (epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_ADD, s->fifo_fd, &ev) < 0)
914 return -errno;
915 }
932e3ee7
LP
916
917 /* Open writing side */
918 r = open(s->fifo_path, O_WRONLY|O_CLOEXEC|O_NDELAY);
919 if (r < 0)
920 return -errno;
31b79c2b 921
932e3ee7
LP
922 return r;
923}
924
925void session_remove_fifo(Session *s) {
926 assert(s);
927
928 if (s->fifo_fd >= 0) {
f8e2fb7b 929 assert_se(hashmap_remove(s->manager->session_fds, INT_TO_PTR(s->fifo_fd + 1)) == s);
932e3ee7
LP
930 assert_se(epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_DEL, s->fifo_fd, NULL) == 0);
931 close_nointr_nofail(s->fifo_fd);
932 s->fifo_fd = -1;
23bd3b62
CG
933
934 session_save(s);
935 user_save(s->user);
932e3ee7
LP
936 }
937
938 if (s->fifo_path) {
939 unlink(s->fifo_path);
940 free(s->fifo_path);
941 s->fifo_path = NULL;
942 }
31b79c2b
LP
943}
944
4a4b033f 945int session_check_gc(Session *s, bool drop_not_started) {
20263082
LP
946 int r;
947
948 assert(s);
949
4a4b033f 950 if (drop_not_started && !s->started)
932e3ee7
LP
951 return 0;
952
953 if (s->fifo_fd >= 0) {
20263082 954
932e3ee7 955 r = pipe_eof(s->fifo_fd);
20263082
LP
956 if (r < 0)
957 return r;
958
14c3baca 959 if (r == 0)
20263082
LP
960 return 1;
961 }
962
963 if (s->cgroup_path) {
964
965 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
966 if (r < 0)
967 return r;
968
969 if (r <= 0)
970 return 1;
971 }
972
973 return 0;
974}
975
14c3baca
LP
976void session_add_to_gc_queue(Session *s) {
977 assert(s);
978
979 if (s->in_gc_queue)
980 return;
981
982 LIST_PREPEND(Session, gc_queue, s->manager->session_gc_queue, s);
983 s->in_gc_queue = true;
984}
985
0604381b
LP
986SessionState session_get_state(Session *s) {
987 assert(s);
988
989 if (s->fifo_fd < 0)
990 return SESSION_CLOSING;
991
992 if (session_is_active(s))
993 return SESSION_ACTIVE;
994
995 return SESSION_ONLINE;
996}
997
de07ab16
LP
998int session_kill(Session *s, KillWho who, int signo) {
999 int r = 0;
1000 Set *pid_set = NULL;
1001
1002 assert(s);
1003
1004 if (!s->cgroup_path)
1005 return -ESRCH;
1006
1007 if (s->leader <= 0 && who == KILL_LEADER)
1008 return -ESRCH;
1009
1010 if (s->leader > 0)
1011 if (kill(s->leader, signo) < 0)
1012 r = -errno;
1013
1014 if (who == KILL_ALL) {
1015 int q;
1016
1017 pid_set = set_new(trivial_hash_func, trivial_compare_func);
1018 if (!pid_set)
1019 return -ENOMEM;
1020
1021 if (s->leader > 0) {
1022 q = set_put(pid_set, LONG_TO_PTR(s->leader));
1023 if (q < 0)
1024 r = q;
1025 }
1026
1027 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, signo, false, true, false, pid_set);
1028 if (q < 0)
1029 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
1030 r = q;
1031 }
1032
de07ab16
LP
1033 if (pid_set)
1034 set_free(pid_set);
1035
1036 return r;
1037}
1038
0604381b
LP
1039static const char* const session_state_table[_SESSION_TYPE_MAX] = {
1040 [SESSION_ONLINE] = "online",
1041 [SESSION_ACTIVE] = "active",
1042 [SESSION_CLOSING] = "closing"
1043};
1044
1045DEFINE_STRING_TABLE_LOOKUP(session_state, SessionState);
1046
20263082 1047static const char* const session_type_table[_SESSION_TYPE_MAX] = {
3f49d45a 1048 [SESSION_TTY] = "tty",
98a28fef 1049 [SESSION_X11] = "x11",
a91e4e53 1050 [SESSION_UNSPECIFIED] = "unspecified"
20263082
LP
1051};
1052
1053DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);
de07ab16 1054
55efac6c
LP
1055static const char* const session_class_table[_SESSION_CLASS_MAX] = {
1056 [SESSION_USER] = "user",
1057 [SESSION_GREETER] = "greeter",
e2acb67b
LP
1058 [SESSION_LOCK_SCREEN] = "lock-screen",
1059 [SESSION_BACKGROUND] = "background"
55efac6c
LP
1060};
1061
1062DEFINE_STRING_TABLE_LOOKUP(session_class, SessionClass);
1063
de07ab16
LP
1064static const char* const kill_who_table[_KILL_WHO_MAX] = {
1065 [KILL_LEADER] = "leader",
1066 [KILL_ALL] = "all"
1067};
1068
1069DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);