]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/logind-session.c
logind: remove a session when its cgroup is gone
[thirdparty/systemd.git] / src / logind-session.c
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
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
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
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
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>
25
26 #include "logind-session.h"
27 #include "strv.h"
28 #include "util.h"
29 #include "cgroup-util.h"
30
31 #define IDLE_THRESHOLD_USEC (5*USEC_PER_MINUTE)
32
33 Session* session_new(Manager *m, User *u, const char *id) {
34 Session *s;
35
36 assert(m);
37 assert(id);
38
39 s = new0(Session, 1);
40 if (!s)
41 return NULL;
42
43 s->state_file = strappend("/run/systemd/sessions/", id);
44 if (!s->state_file) {
45 free(s);
46 return NULL;
47 }
48
49 s->id = file_name_from_path(s->state_file);
50
51 if (hashmap_put(m->sessions, s->id, s) < 0) {
52 free(s->id);
53 free(s);
54 return NULL;
55 }
56
57 s->manager = m;
58 s->pipe_fd = -1;
59 s->user = u;
60
61 LIST_PREPEND(Session, sessions_by_user, u->sessions, s);
62
63 return s;
64 }
65
66 void session_free(Session *s) {
67 assert(s);
68
69 if (s->in_gc_queue)
70 LIST_REMOVE(Session, gc_queue, s->manager->session_gc_queue, s);
71
72 if (s->user) {
73 LIST_REMOVE(Session, sessions_by_user, s->user->sessions, s);
74
75 if (s->user->display == s)
76 s->user->display = NULL;
77 }
78
79 if (s->seat) {
80 if (s->seat->active == s)
81 s->seat->active = NULL;
82
83 LIST_REMOVE(Session, sessions_by_seat, s->seat->sessions, s);
84 }
85
86 if (s->cgroup_path)
87 hashmap_remove(s->manager->cgroups, s->cgroup_path);
88
89 free(s->cgroup_path);
90 strv_free(s->controllers);
91
92 free(s->tty);
93 free(s->display);
94 free(s->remote_host);
95 free(s->remote_user);
96 free(s->service);
97
98 hashmap_remove(s->manager->sessions, s->id);
99
100 if (s->pipe_fd >= 0)
101 close_nointr_nofail(s->pipe_fd);
102
103 free(s->state_file);
104 free(s);
105 }
106
107 int session_save(Session *s) {
108 FILE *f;
109 int r = 0;
110 char *temp_path;
111
112 assert(s);
113
114 r = safe_mkdir("/run/systemd/sessions", 0755, 0, 0);
115 if (r < 0)
116 goto finish;
117
118 r = fopen_temporary(s->state_file, &f, &temp_path);
119 if (r < 0)
120 goto finish;
121
122 assert(s->user);
123
124 fchmod(fileno(f), 0644);
125
126 fprintf(f,
127 "# This is private data. Do not parse.\n"
128 "UID=%lu\n"
129 "USER=%s\n"
130 "ACTIVE=%i\n"
131 "REMOTE=%i\n"
132 "KILL_PROCESSES=%i\n",
133 (unsigned long) s->user->uid,
134 s->user->name,
135 session_is_active(s),
136 s->remote,
137 s->kill_processes);
138
139 if (s->cgroup_path)
140 fprintf(f,
141 "CGROUP=%s\n",
142 s->cgroup_path);
143
144 if (s->seat)
145 fprintf(f,
146 "SEAT=%s\n",
147 s->seat->id);
148
149 if (s->tty)
150 fprintf(f,
151 "TTY=%s\n",
152 s->tty);
153
154 if (s->display)
155 fprintf(f,
156 "DISPLAY=%s\n",
157 s->display);
158
159 if (s->remote_host)
160 fprintf(f,
161 "REMOTE_HOST=%s\n",
162 s->remote_host);
163
164 if (s->remote_user)
165 fprintf(f,
166 "REMOTE_USER=%s\n",
167 s->remote_user);
168
169 if (s->service)
170 fprintf(f,
171 "SERVICE=%s\n",
172 s->service);
173
174 if (s->seat && seat_is_vtconsole(s->seat))
175 fprintf(f,
176 "VTNR=%i\n",
177 s->vtnr);
178
179 if (s->leader > 0)
180 fprintf(f,
181 "LEADER=%lu\n",
182 (unsigned long) s->leader);
183
184 if (s->audit_id > 0)
185 fprintf(f,
186 "AUDIT=%llu\n",
187 (unsigned long long) s->audit_id);
188
189 fflush(f);
190
191 if (ferror(f) || rename(temp_path, s->state_file) < 0) {
192 r = -errno;
193 unlink(s->state_file);
194 unlink(temp_path);
195 }
196
197 fclose(f);
198 free(temp_path);
199
200 finish:
201 if (r < 0)
202 log_error("Failed to save session data for %s: %s", s->id, strerror(-r));
203
204 return r;
205 }
206
207 int session_load(Session *s) {
208 char *remote = NULL,
209 *kill_processes = NULL,
210 *seat = NULL,
211 *vtnr = NULL,
212 *leader = NULL,
213 *audit_id = NULL;
214
215 int k, r;
216
217 assert(s);
218
219 r = parse_env_file(s->state_file, NEWLINE,
220 "REMOTE", &remote,
221 "KILL_PROCESSES", &kill_processes,
222 "CGROUP", &s->cgroup_path,
223 "SEAT", &seat,
224 "TTY", &s->tty,
225 "DISPLAY", &s->display,
226 "REMOTE_HOST", &s->remote_host,
227 "REMOTE_USER", &s->remote_user,
228 "SERVICE", &s->service,
229 "VTNR", &vtnr,
230 "LEADER", &leader,
231 NULL);
232
233 if (r < 0)
234 goto finish;
235
236 if (remote) {
237 k = parse_boolean(remote);
238 if (k >= 0)
239 s->remote = k;
240 }
241
242 if (kill_processes) {
243 k = parse_boolean(kill_processes);
244 if (k >= 0)
245 s->kill_processes = k;
246 }
247
248 if (seat && !s->seat) {
249 Seat *o;
250
251 o = hashmap_get(s->manager->seats, seat);
252 if (o)
253 seat_attach_session(o, s);
254 }
255
256 if (vtnr && s->seat && seat_is_vtconsole(s->seat)) {
257 int v;
258
259 k = safe_atoi(vtnr, &v);
260 if (k >= 0 && v >= 1)
261 s->vtnr = v;
262 }
263
264 if (leader) {
265 pid_t pid;
266
267 k = parse_pid(leader, &pid);
268 if (k >= 0 && pid >= 1) {
269 s->leader = pid;
270
271 audit_session_from_pid(pid, &s->audit_id);
272 }
273 }
274
275 finish:
276 free(remote);
277 free(kill_processes);
278 free(seat);
279 free(vtnr);
280 free(leader);
281 free(audit_id);
282
283 return r;
284 }
285
286 int session_activate(Session *s) {
287 int r;
288 Session *old_active;
289
290 assert(s);
291
292 if (s->vtnr < 0)
293 return -ENOTSUP;
294
295 if (!s->seat)
296 return -ENOTSUP;
297
298 if (s->seat->active == s)
299 return 0;
300
301 assert(seat_is_vtconsole(s->seat));
302
303 r = chvt(s->vtnr);
304 if (r < 0)
305 return r;
306
307 old_active = s->seat->active;
308 s->seat->active = s;
309
310 return seat_apply_acls(s->seat, old_active);
311 }
312
313 bool x11_display_is_local(const char *display) {
314 assert(display);
315
316 return
317 display[0] == ':' &&
318 display[1] >= '0' &&
319 display[1] <= '9';
320 }
321
322 static int session_link_x11_socket(Session *s) {
323 char *t, *f, *c;
324 size_t k;
325
326 assert(s);
327 assert(s->user);
328 assert(s->user->runtime_path);
329
330 if (s->user->display)
331 return 0;
332
333 if (!s->display || !x11_display_is_local(s->display))
334 return 0;
335
336 k = strspn(s->display+1, "0123456789");
337 f = new(char, sizeof("/tmp/.X11-unix/X") + k);
338 if (!f) {
339 log_error("Out of memory");
340 return -ENOMEM;
341 }
342
343 c = stpcpy(f, "/tmp/.X11-unix/X");
344 memcpy(c, s->display+1, k);
345 c[k] = 0;
346
347 if (access(f, F_OK) < 0) {
348 log_warning("Session %s has display %s with nonexisting socket %s.", s->id, s->display, f);
349 free(f);
350 return -ENOENT;
351 }
352
353 t = strappend(s->user->runtime_path, "/display");
354 if (!t) {
355 log_error("Out of memory");
356 free(f);
357 return -ENOMEM;
358 }
359
360 if (link(f, t) < 0) {
361 if (errno == EEXIST) {
362 unlink(t);
363
364 if (link(f, t) >= 0)
365 goto done;
366 }
367
368 if (symlink(f, t) < 0) {
369
370 if (errno == EEXIST) {
371 unlink(t);
372
373 if (symlink(f, t) >= 0)
374 goto done;
375 }
376
377 log_error("Failed to link %s to %s: %m", f, t);
378 free(f);
379 free(t);
380 return -errno;
381 }
382 }
383
384 done:
385 log_info("Linked %s to %s.", f, t);
386 free(f);
387 free(t);
388
389 s->user->display = s;
390
391 return 0;
392 }
393
394 static int session_create_one_group(Session *s, const char *controller, const char *path) {
395 int r;
396
397 assert(s);
398 assert(controller);
399 assert(path);
400
401 if (s->leader > 0)
402 r = cg_create_and_attach(controller, path, s->leader);
403 else
404 r = cg_create(controller, path);
405
406 if (r < 0)
407 return r;
408
409 r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid);
410 if (r >= 0)
411 r = cg_set_group_access(controller, path, 0755, s->user->uid, s->user->gid);
412
413 return r;
414 }
415
416 static int session_create_cgroup(Session *s) {
417 char **k;
418 char *p;
419 int r;
420
421 assert(s);
422 assert(s->user);
423 assert(s->user->cgroup_path);
424
425 if (!s->cgroup_path) {
426 if (asprintf(&p, "%s/%s", s->user->cgroup_path, s->id) < 0) {
427 log_error("Out of memory");
428 return -ENOMEM;
429 }
430 } else
431 p = s->cgroup_path;
432
433 r = session_create_one_group(s, SYSTEMD_CGROUP_CONTROLLER, p);
434 if (r < 0) {
435 free(p);
436 s->cgroup_path = NULL;
437 log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
438 return r;
439 }
440
441 s->cgroup_path = p;
442
443 STRV_FOREACH(k, s->controllers) {
444
445 if (strv_contains(s->reset_controllers, *k))
446 continue;
447
448 r = session_create_one_group(s, *k, p);
449 if (r < 0)
450 log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
451 }
452
453 STRV_FOREACH(k, s->manager->controllers) {
454
455 if (strv_contains(s->reset_controllers, *k) ||
456 strv_contains(s->controllers, *k))
457 continue;
458
459 r = session_create_one_group(s, *k, p);
460 if (r < 0)
461 log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
462 }
463
464 if (s->leader > 0) {
465
466 STRV_FOREACH(k, s->reset_controllers) {
467 r = cg_attach(*k, "/", s->leader);
468 if (r < 0)
469 log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
470
471 }
472 }
473
474 hashmap_put(s->manager->cgroups, s->cgroup_path, s);
475
476 return 0;
477 }
478
479 int session_start(Session *s) {
480 int r;
481
482 assert(s);
483 assert(s->user);
484
485 if (s->started)
486 return 0;
487
488 r = user_start(s->user);
489 if (r < 0)
490 return r;
491
492 log_info("New session %s of user %s.", s->id, s->user->name);
493
494 /* Create cgroup */
495 r = session_create_cgroup(s);
496 if (r < 0)
497 return r;
498
499 /* Create X11 symlink */
500 session_link_x11_socket(s);
501
502 /* Save session data */
503 session_save(s);
504
505 dual_timestamp_get(&s->timestamp);
506
507 s->started = true;
508
509 session_send_signal(s, true);
510
511 if (s->seat) {
512 if (s->seat->active == s)
513 seat_send_changed(s->seat, "Sessions\0ActiveSession\0");
514 else
515 seat_send_changed(s->seat, "Sessions\0");
516 }
517
518 user_send_changed(s->user, "Sessions\0");
519
520 return 0;
521 }
522
523 static bool session_shall_kill(Session *s) {
524 assert(s);
525
526 if (!s->kill_processes)
527 return false;
528
529 if (strv_contains(s->manager->kill_exclude_users, s->user->name))
530 return false;
531
532 if (strv_isempty(s->manager->kill_only_users))
533 return true;
534
535 return strv_contains(s->manager->kill_only_users, s->user->name);
536 }
537
538 static int session_kill_cgroup(Session *s) {
539 int r;
540 char **k;
541
542 assert(s);
543
544 if (!s->cgroup_path)
545 return 0;
546
547 cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
548
549 if (session_shall_kill(s)) {
550
551 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
552 if (r < 0)
553 log_error("Failed to kill session cgroup: %s", strerror(-r));
554
555 } else {
556 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
557 if (r < 0)
558 log_error("Failed to check session cgroup: %s", strerror(-r));
559 else if (r > 0) {
560 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path);
561 if (r < 0)
562 log_error("Failed to delete session cgroup: %s", strerror(-r));
563 } else
564 r = -EBUSY;
565 }
566
567 STRV_FOREACH(k, s->user->manager->controllers)
568 cg_trim(*k, s->cgroup_path, true);
569
570 hashmap_remove(s->manager->cgroups, s->cgroup_path);
571
572 free(s->cgroup_path);
573 s->cgroup_path = NULL;
574
575 return r;
576 }
577
578 static int session_unlink_x11_socket(Session *s) {
579 char *t;
580 int r;
581
582 assert(s);
583 assert(s->user);
584
585 if (s->user->display != s)
586 return 0;
587
588 s->user->display = NULL;
589
590 t = strappend(s->user->runtime_path, "/display");
591 if (!t) {
592 log_error("Out of memory");
593 return -ENOMEM;
594 }
595
596 r = unlink(t);
597 free(t);
598
599 return r < 0 ? -errno : 0;
600 }
601
602 int session_stop(Session *s) {
603 int r = 0, k;
604
605 assert(s);
606
607 if (s->started)
608 log_info("Removed session %s.", s->id);
609
610 /* Kill cgroup */
611 k = session_kill_cgroup(s);
612 if (k < 0)
613 r = k;
614
615 /* Remove X11 symlink */
616 session_unlink_x11_socket(s);
617
618 unlink(s->state_file);
619 session_add_to_gc_queue(s);
620 user_add_to_gc_queue(s->user);
621
622 if (s->started)
623 session_send_signal(s, false);
624
625 if (s->seat) {
626 if (s->seat->active == s)
627 seat_set_active(s->seat, NULL);
628
629 seat_send_changed(s->seat, "Sessions\0");
630 }
631
632 user_send_changed(s->user, "Sessions\0");
633
634 s->started = false;
635
636 return r;
637 }
638
639 bool session_is_active(Session *s) {
640 assert(s);
641
642 if (!s->seat)
643 return true;
644
645 return s->seat->active == s;
646 }
647
648 int session_get_idle_hint(Session *s, dual_timestamp *t) {
649 char *p;
650 struct stat st;
651 usec_t u, n;
652 bool b;
653 int k;
654
655 assert(s);
656
657 if (s->idle_hint) {
658 if (t)
659 *t = s->idle_hint_timestamp;
660
661 return s->idle_hint;
662 }
663
664 if (isempty(s->tty))
665 goto dont_know;
666
667 if (s->tty[0] != '/') {
668 p = strappend("/dev/", s->tty);
669 if (!p)
670 return -ENOMEM;
671 } else
672 p = NULL;
673
674 if (!startswith(p ? p : s->tty, "/dev/")) {
675 free(p);
676 goto dont_know;
677 }
678
679 k = lstat(p ? p : s->tty, &st);
680 free(p);
681
682 if (k < 0)
683 goto dont_know;
684
685 u = timespec_load(&st.st_atim);
686 n = now(CLOCK_REALTIME);
687 b = u + IDLE_THRESHOLD_USEC < n;
688
689 if (t)
690 dual_timestamp_from_realtime(t, u + b ? IDLE_THRESHOLD_USEC : 0);
691
692 return b;
693
694 dont_know:
695 if (t)
696 *t = s->idle_hint_timestamp;
697
698 return 0;
699 }
700
701 void session_set_idle_hint(Session *s, bool b) {
702 assert(s);
703
704 if (s->idle_hint == b)
705 return;
706
707 s->idle_hint = b;
708 dual_timestamp_get(&s->idle_hint_timestamp);
709
710 session_send_changed(s,
711 "IdleHint\0"
712 "IdleSinceHint\0"
713 "IdleSinceHintMonotonic\0");
714
715 if (s->seat)
716 seat_send_changed(s->seat,
717 "IdleHint\0"
718 "IdleSinceHint\0"
719 "IdleSinceHintMonotonic\0");
720
721 user_send_changed(s->user,
722 "IdleHint\0"
723 "IdleSinceHint\0"
724 "IdleSinceHintMonotonic\0");
725
726 manager_send_changed(s->manager,
727 "IdleHint\0"
728 "IdleSinceHint\0"
729 "IdleSinceHintMonotonic\0");
730 }
731
732 int session_check_gc(Session *s) {
733 int r;
734
735 assert(s);
736
737 if (s->pipe_fd >= 0) {
738
739 r = pipe_eof(s->pipe_fd);
740 if (r < 0)
741 return r;
742
743 if (r == 0)
744 return 1;
745 }
746
747 if (s->cgroup_path) {
748
749 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
750 if (r < 0)
751 return r;
752
753 if (r <= 0)
754 return 1;
755 }
756
757 return 0;
758 }
759
760 void session_add_to_gc_queue(Session *s) {
761 assert(s);
762
763 if (s->in_gc_queue)
764 return;
765
766 LIST_PREPEND(Session, gc_queue, s->manager->session_gc_queue, s);
767 s->in_gc_queue = true;
768 }
769
770 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
771 [SESSION_TTY] = "tty",
772 [SESSION_X11] = "x11",
773 [SESSION_OTHER] = "other"
774 };
775
776 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);