]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/logind-session.c
logind: fix generation of seat state file
[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
331static int session_link_x11_socket(Session *s) {
332 char *t, *f, *c;
333 size_t k;
334
335 assert(s);
336 assert(s->user);
337 assert(s->user->runtime_path);
338
339 if (s->user->display)
340 return 0;
341
4d6d6518 342 if (!s->display || !display_is_local(s->display))
20263082
LP
343 return 0;
344
345 k = strspn(s->display+1, "0123456789");
346 f = new(char, sizeof("/tmp/.X11-unix/X") + k);
347 if (!f) {
348 log_error("Out of memory");
349 return -ENOMEM;
350 }
351
352 c = stpcpy(f, "/tmp/.X11-unix/X");
353 memcpy(c, s->display+1, k);
354 c[k] = 0;
355
356 if (access(f, F_OK) < 0) {
357 log_warning("Session %s has display %s with nonexisting socket %s.", s->id, s->display, f);
358 free(f);
359 return -ENOENT;
360 }
361
362 t = strappend(s->user->runtime_path, "/display");
363 if (!t) {
364 log_error("Out of memory");
365 free(f);
366 return -ENOMEM;
367 }
368
369 if (link(f, t) < 0) {
370 if (errno == EEXIST) {
371 unlink(t);
372
373 if (link(f, t) >= 0)
374 goto done;
375 }
376
377 if (symlink(f, t) < 0) {
378
379 if (errno == EEXIST) {
380 unlink(t);
381
382 if (symlink(f, t) >= 0)
383 goto done;
384 }
385
386 log_error("Failed to link %s to %s: %m", f, t);
387 free(f);
388 free(t);
389 return -errno;
390 }
391 }
392
393done:
394 log_info("Linked %s to %s.", f, t);
395 free(f);
396 free(t);
397
398 s->user->display = s;
399
400 return 0;
401}
402
98a28fef
LP
403static int session_create_one_group(Session *s, const char *controller, const char *path) {
404 int r;
405
406 assert(s);
407 assert(controller);
408 assert(path);
409
b6f68af1 410 if (s->leader > 0) {
98a28fef 411 r = cg_create_and_attach(controller, path, s->leader);
b6f68af1
LP
412 if (r < 0)
413 r = cg_create(controller, path);
414 } else
98a28fef
LP
415 r = cg_create(controller, path);
416
417 if (r < 0)
418 return r;
419
420 r = cg_set_task_access(controller, path, 0644, s->user->uid, s->user->gid);
421 if (r >= 0)
422 r = cg_set_group_access(controller, path, 0755, s->user->uid, s->user->gid);
423
424 return r;
425}
426
20263082
LP
427static int session_create_cgroup(Session *s) {
428 char **k;
429 char *p;
430 int r;
431
432 assert(s);
433 assert(s->user);
434 assert(s->user->cgroup_path);
435
436 if (!s->cgroup_path) {
437 if (asprintf(&p, "%s/%s", s->user->cgroup_path, s->id) < 0) {
438 log_error("Out of memory");
439 return -ENOMEM;
440 }
441 } else
442 p = s->cgroup_path;
443
98a28fef 444 r = session_create_one_group(s, SYSTEMD_CGROUP_CONTROLLER, p);
20263082 445 if (r < 0) {
4d6d6518 446 log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
20263082
LP
447 free(p);
448 s->cgroup_path = NULL;
20263082
LP
449 return r;
450 }
451
452 s->cgroup_path = p;
453
98a28fef
LP
454 STRV_FOREACH(k, s->controllers) {
455
456 if (strv_contains(s->reset_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
20263082 464 STRV_FOREACH(k, s->manager->controllers) {
20263082 465
98a28fef
LP
466 if (strv_contains(s->reset_controllers, *k) ||
467 strv_contains(s->controllers, *k))
468 continue;
469
470 r = session_create_one_group(s, *k, p);
20263082 471 if (r < 0)
98a28fef
LP
472 log_warning("Failed to create %s:%s: %s", *k, p, strerror(-r));
473 }
474
475 if (s->leader > 0) {
476
477 STRV_FOREACH(k, s->reset_controllers) {
478 r = cg_attach(*k, "/", s->leader);
479 if (r < 0)
480 log_warning("Failed to reset controller %s: %s", *k, strerror(-r));
481
482 }
20263082
LP
483 }
484
1713813d
LP
485 hashmap_put(s->manager->cgroups, s->cgroup_path, s);
486
20263082
LP
487 return 0;
488}
489
490int session_start(Session *s) {
491 int r;
492
493 assert(s);
494 assert(s->user);
495
9418f147
LP
496 if (s->started)
497 return 0;
498
ed18b08b
LP
499 r = user_start(s->user);
500 if (r < 0)
501 return r;
502
98a28fef
LP
503 log_info("New session %s of user %s.", s->id, s->user->name);
504
20263082
LP
505 /* Create cgroup */
506 r = session_create_cgroup(s);
507 if (r < 0)
508 return r;
509
510 /* Create X11 symlink */
511 session_link_x11_socket(s);
14c3baca 512
14c3baca
LP
513 dual_timestamp_get(&s->timestamp);
514
e9816c48
LP
515 if (s->seat)
516 seat_read_active_vt(s->seat);
517
9418f147
LP
518 s->started = true;
519
e9816c48
LP
520 /* Save session data */
521 session_save(s);
7f7bb946 522 user_save(s->user);
e9816c48 523
da119395
LP
524 session_send_signal(s, true);
525
9418f147 526 if (s->seat) {
7f7bb946
LP
527 seat_save(s->seat);
528
9418f147
LP
529 if (s->seat->active == s)
530 seat_send_changed(s->seat, "Sessions\0ActiveSession\0");
531 else
532 seat_send_changed(s->seat, "Sessions\0");
533 }
534
535 user_send_changed(s->user, "Sessions\0");
536
20263082
LP
537 return 0;
538}
539
540static bool session_shall_kill(Session *s) {
541 assert(s);
542
ed18b08b
LP
543 if (!s->kill_processes)
544 return false;
545
546 if (strv_contains(s->manager->kill_exclude_users, s->user->name))
547 return false;
548
549 if (strv_isempty(s->manager->kill_only_users))
550 return true;
551
552 return strv_contains(s->manager->kill_only_users, s->user->name);
20263082
LP
553}
554
555static int session_kill_cgroup(Session *s) {
556 int r;
557 char **k;
558
559 assert(s);
560
561 if (!s->cgroup_path)
562 return 0;
563
564 cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
565
566 if (session_shall_kill(s)) {
567
568 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
569 if (r < 0)
570 log_error("Failed to kill session cgroup: %s", strerror(-r));
571
572 } else {
573 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
574 if (r < 0)
575 log_error("Failed to check session cgroup: %s", strerror(-r));
576 else if (r > 0) {
577 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path);
578 if (r < 0)
579 log_error("Failed to delete session cgroup: %s", strerror(-r));
580 } else
581 r = -EBUSY;
582 }
583
584 STRV_FOREACH(k, s->user->manager->controllers)
585 cg_trim(*k, s->cgroup_path, true);
586
1713813d
LP
587 hashmap_remove(s->manager->cgroups, s->cgroup_path);
588
20263082
LP
589 free(s->cgroup_path);
590 s->cgroup_path = NULL;
591
592 return r;
593}
594
595static int session_unlink_x11_socket(Session *s) {
596 char *t;
597 int r;
598
599 assert(s);
600 assert(s->user);
601
602 if (s->user->display != s)
603 return 0;
604
605 s->user->display = NULL;
606
607 t = strappend(s->user->runtime_path, "/display");
608 if (!t) {
609 log_error("Out of memory");
610 return -ENOMEM;
611 }
612
613 r = unlink(t);
614 free(t);
615
616 return r < 0 ? -errno : 0;
617}
618
619int session_stop(Session *s) {
620 int r = 0, k;
621
622 assert(s);
623
ed18b08b
LP
624 if (s->started)
625 log_info("Removed session %s.", s->id);
98a28fef 626
20263082
LP
627 /* Kill cgroup */
628 k = session_kill_cgroup(s);
629 if (k < 0)
630 r = k;
631
632 /* Remove X11 symlink */
633 session_unlink_x11_socket(s);
634
d2f92cdf
LP
635 unlink(s->state_file);
636 session_add_to_gc_queue(s);
ed18b08b 637 user_add_to_gc_queue(s->user);
14c3baca 638
ed18b08b
LP
639 if (s->started)
640 session_send_signal(s, false);
9418f147
LP
641
642 if (s->seat) {
643 if (s->seat->active == s)
644 seat_set_active(s->seat, NULL);
645
646 seat_send_changed(s->seat, "Sessions\0");
647 }
648
649 user_send_changed(s->user, "Sessions\0");
650
651 s->started = false;
652
20263082
LP
653 return r;
654}
655
656bool session_is_active(Session *s) {
657 assert(s);
658
659 if (!s->seat)
660 return true;
661
662 return s->seat->active == s;
663}
664
a185c5aa
LP
665int session_get_idle_hint(Session *s, dual_timestamp *t) {
666 char *p;
667 struct stat st;
668 usec_t u, n;
669 bool b;
670 int k;
671
672 assert(s);
673
674 if (s->idle_hint) {
675 if (t)
676 *t = s->idle_hint_timestamp;
677
678 return s->idle_hint;
679 }
680
681 if (isempty(s->tty))
682 goto dont_know;
683
684 if (s->tty[0] != '/') {
685 p = strappend("/dev/", s->tty);
686 if (!p)
687 return -ENOMEM;
688 } else
689 p = NULL;
690
691 if (!startswith(p ? p : s->tty, "/dev/")) {
692 free(p);
693 goto dont_know;
694 }
695
696 k = lstat(p ? p : s->tty, &st);
697 free(p);
698
699 if (k < 0)
700 goto dont_know;
701
702 u = timespec_load(&st.st_atim);
703 n = now(CLOCK_REALTIME);
704 b = u + IDLE_THRESHOLD_USEC < n;
705
706 if (t)
707 dual_timestamp_from_realtime(t, u + b ? IDLE_THRESHOLD_USEC : 0);
708
709 return b;
710
711dont_know:
712 if (t)
713 *t = s->idle_hint_timestamp;
714
715 return 0;
716}
717
bef422ae
LP
718void session_set_idle_hint(Session *s, bool b) {
719 assert(s);
720
721 if (s->idle_hint == b)
722 return;
723
724 s->idle_hint = b;
725 dual_timestamp_get(&s->idle_hint_timestamp);
9418f147
LP
726
727 session_send_changed(s,
728 "IdleHint\0"
729 "IdleSinceHint\0"
730 "IdleSinceHintMonotonic\0");
731
732 if (s->seat)
733 seat_send_changed(s->seat,
734 "IdleHint\0"
735 "IdleSinceHint\0"
736 "IdleSinceHintMonotonic\0");
737
738 user_send_changed(s->user,
739 "IdleHint\0"
740 "IdleSinceHint\0"
741 "IdleSinceHintMonotonic\0");
742
743 manager_send_changed(s->manager,
744 "IdleHint\0"
745 "IdleSinceHint\0"
746 "IdleSinceHintMonotonic\0");
bef422ae
LP
747}
748
31b79c2b
LP
749int session_set_pipe_fd(Session *s, int fd) {
750 struct epoll_event ev;
751 int r;
752
753 assert(s);
754 assert(fd >= 0);
755 assert(s->pipe_fd < 0);
756
757 r = hashmap_put(s->manager->pipe_fds, INT_TO_PTR(fd + 1), s);
758 if (r < 0)
759 return r;
760
761 zero(ev);
762 ev.events = 0;
763 ev.data.u32 = FD_PIPE_BASE + fd;
764
765 if (epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
766 assert_se(hashmap_remove(s->manager->pipe_fds, INT_TO_PTR(fd + 1)) == s);
767 return -errno;
768 }
769
770 s->pipe_fd = fd;
771 return 0;
772}
773
774void session_unset_pipe_fd(Session *s) {
775 assert(s);
776
777 if (s->pipe_fd < 0)
778 return;
779
780 assert_se(hashmap_remove(s->manager->pipe_fds, INT_TO_PTR(s->pipe_fd + 1)) == s);
781
782 assert_se(epoll_ctl(s->manager->epoll_fd, EPOLL_CTL_DEL, s->pipe_fd, NULL) == 0);
783
784 close_nointr_nofail(s->pipe_fd);
785 s->pipe_fd = -1;
786}
787
20263082
LP
788int session_check_gc(Session *s) {
789 int r;
790
791 assert(s);
792
793 if (s->pipe_fd >= 0) {
794
795 r = pipe_eof(s->pipe_fd);
796 if (r < 0)
797 return r;
798
14c3baca 799 if (r == 0)
20263082
LP
800 return 1;
801 }
802
803 if (s->cgroup_path) {
804
805 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
806 if (r < 0)
807 return r;
808
809 if (r <= 0)
810 return 1;
811 }
812
813 return 0;
814}
815
14c3baca
LP
816void session_add_to_gc_queue(Session *s) {
817 assert(s);
818
819 if (s->in_gc_queue)
820 return;
821
822 LIST_PREPEND(Session, gc_queue, s->manager->session_gc_queue, s);
823 s->in_gc_queue = true;
824}
825
20263082 826static const char* const session_type_table[_SESSION_TYPE_MAX] = {
3f49d45a 827 [SESSION_TTY] = "tty",
98a28fef 828 [SESSION_X11] = "x11",
a91e4e53 829 [SESSION_UNSPECIFIED] = "unspecified"
20263082
LP
830};
831
832DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);