chiark / gitweb /
logind: hook up PAM module with logind
[elogind.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         free(s->cgroup_path);
87         strv_free(s->controllers);
88
89         free(s->tty);
90         free(s->display);
91         free(s->remote_host);
92         free(s->remote_user);
93         free(s->service);
94
95         hashmap_remove(s->manager->sessions, s->id);
96
97         if (s->pipe_fd >= 0)
98                 close_nointr_nofail(s->pipe_fd);
99
100         free(s->state_file);
101         free(s);
102 }
103
104 int session_save(Session *s) {
105         FILE *f;
106         int r = 0;
107         char *temp_path;
108
109         assert(s);
110
111         r = safe_mkdir("/run/systemd/sessions", 0755, 0, 0);
112         if (r < 0)
113                 goto finish;
114
115         r = fopen_temporary(s->state_file, &f, &temp_path);
116         if (r < 0)
117                 goto finish;
118
119         assert(s->user);
120
121         fchmod(fileno(f), 0644);
122
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
161         if (s->remote_user)
162                 fprintf(f,
163                         "REMOTE_USER=%s\n",
164                         s->remote_user);
165
166         if (s->service)
167                 fprintf(f,
168                         "SERVICE=%s\n",
169                         s->service);
170
171         if (s->seat && seat_is_vtconsole(s->seat))
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);
187
188         if (ferror(f) || rename(temp_path, s->state_file) < 0) {
189                 r = -errno;
190                 unlink(s->state_file);
191                 unlink(temp_path);
192         }
193
194         fclose(f);
195         free(temp_path);
196
197 finish:
198         if (r < 0)
199                 log_error("Failed to save session data for %s: %s", s->id, strerror(-r));
200
201         return r;
202 }
203
204 int session_load(Session *s) {
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
214         assert(s);
215
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,
225                            "SERVICE",        &s->service,
226                            "VTNR",           &vtnr,
227                            "LEADER",         &leader,
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
245         if (seat && !s->seat) {
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);
265                 if (k >= 0 && pid >= 1) {
266                         s->leader = pid;
267
268                         audit_session_from_pid(pid, &s->audit_id);
269                 }
270         }
271
272 finish:
273         free(remote);
274         free(kill_processes);
275         free(seat);
276         free(vtnr);
277         free(leader);
278         free(audit_id);
279
280         return r;
281 }
282
283 int session_activate(Session *s) {
284         int r;
285         Session *old_active;
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
298         assert(seat_is_vtconsole(s->seat));
299
300         r = chvt(s->vtnr);
301         if (r < 0)
302                 return r;
303
304         old_active = s->seat->active;
305         s->seat->active = s;
306
307         return seat_apply_acls(s->seat, old_active);
308 }
309
310 bool 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
319 static 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
381 done:
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
391 static 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
413 static 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
430         r = session_create_one_group(s, SYSTEMD_CGROUP_CONTROLLER, p);
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
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
450         STRV_FOREACH(k, s->manager->controllers) {
451
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);
457                 if (r < 0)
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                 }
469         }
470
471         return 0;
472 }
473
474 int session_start(Session *s) {
475         int r;
476
477         assert(s);
478         assert(s->user);
479
480         if (s->started)
481                 return 0;
482
483         log_info("New session %s of user %s.", s->id, s->user->name);
484
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);
492
493         /* Save session data */
494         session_save(s);
495
496         dual_timestamp_get(&s->timestamp);
497
498         s->started = true;
499
500         session_send_signal(s, true);
501
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
511         return 0;
512 }
513
514 static bool session_shall_kill(Session *s) {
515         assert(s);
516
517         return s->kill_processes;
518 }
519
520 static 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
558 static 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
582 int session_stop(Session *s) {
583         int r = 0, k;
584
585         assert(s);
586
587         if (!s->started)
588                 return 0;
589
590         log_info("Removed session %s.", s->id);
591
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
600         unlink(s->state_file);
601         session_add_to_gc_queue(s);
602
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
616         return r;
617 }
618
619 bool 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
628 int 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
674 dont_know:
675         if (t)
676                 *t = s->idle_hint_timestamp;
677
678         return 0;
679 }
680
681 void 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);
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");
710 }
711
712 int 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
723                 if (r == 0)
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
740 void 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
750 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
751         [SESSION_TTY] = "tty",
752         [SESSION_X11] = "x11",
753         [SESSION_OTHER] = "other"
754 };
755
756 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);