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