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