chiark / gitweb /
logind: various clean-ups
[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         r = user_start(s->user);
484         if (r < 0)
485                 return r;
486
487         log_info("New session %s of user %s.", s->id, s->user->name);
488
489         /* Create cgroup */
490         r = session_create_cgroup(s);
491         if (r < 0)
492                 return r;
493
494         /* Create X11 symlink */
495         session_link_x11_socket(s);
496
497         /* Save session data */
498         session_save(s);
499
500         dual_timestamp_get(&s->timestamp);
501
502         s->started = true;
503
504         session_send_signal(s, true);
505
506         if (s->seat) {
507                 if (s->seat->active == s)
508                         seat_send_changed(s->seat, "Sessions\0ActiveSession\0");
509                 else
510                         seat_send_changed(s->seat, "Sessions\0");
511         }
512
513         user_send_changed(s->user, "Sessions\0");
514
515         return 0;
516 }
517
518 static bool session_shall_kill(Session *s) {
519         assert(s);
520
521         if (!s->kill_processes)
522                 return false;
523
524         if (strv_contains(s->manager->kill_exclude_users, s->user->name))
525                 return false;
526
527         if (strv_isempty(s->manager->kill_only_users))
528                 return true;
529
530         return strv_contains(s->manager->kill_only_users, s->user->name);
531 }
532
533 static int session_kill_cgroup(Session *s) {
534         int r;
535         char **k;
536
537         assert(s);
538
539         if (!s->cgroup_path)
540                 return 0;
541
542         cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
543
544         if (session_shall_kill(s)) {
545
546                 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
547                 if (r < 0)
548                         log_error("Failed to kill session cgroup: %s", strerror(-r));
549
550         } else {
551                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
552                 if (r < 0)
553                         log_error("Failed to check session cgroup: %s", strerror(-r));
554                 else if (r > 0) {
555                         r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path);
556                         if (r < 0)
557                                 log_error("Failed to delete session cgroup: %s", strerror(-r));
558                 } else
559                         r = -EBUSY;
560         }
561
562         STRV_FOREACH(k, s->user->manager->controllers)
563                 cg_trim(*k, s->cgroup_path, true);
564
565         free(s->cgroup_path);
566         s->cgroup_path = NULL;
567
568         return r;
569 }
570
571 static int session_unlink_x11_socket(Session *s) {
572         char *t;
573         int r;
574
575         assert(s);
576         assert(s->user);
577
578         if (s->user->display != s)
579                 return 0;
580
581         s->user->display = NULL;
582
583         t = strappend(s->user->runtime_path, "/display");
584         if (!t) {
585                 log_error("Out of memory");
586                 return -ENOMEM;
587         }
588
589         r = unlink(t);
590         free(t);
591
592         return r < 0 ? -errno : 0;
593 }
594
595 int session_stop(Session *s) {
596         int r = 0, k;
597
598         assert(s);
599
600         if (s->started)
601                 log_info("Removed session %s.", s->id);
602
603         /* Kill cgroup */
604         k = session_kill_cgroup(s);
605         if (k < 0)
606                 r = k;
607
608         /* Remove X11 symlink */
609         session_unlink_x11_socket(s);
610
611         unlink(s->state_file);
612         session_add_to_gc_queue(s);
613         user_add_to_gc_queue(s->user);
614
615         if (s->started)
616                 session_send_signal(s, false);
617
618         if (s->seat) {
619                 if (s->seat->active == s)
620                         seat_set_active(s->seat, NULL);
621
622                 seat_send_changed(s->seat, "Sessions\0");
623         }
624
625         user_send_changed(s->user, "Sessions\0");
626
627         s->started = false;
628
629         return r;
630 }
631
632 bool session_is_active(Session *s) {
633         assert(s);
634
635         if (!s->seat)
636                 return true;
637
638         return s->seat->active == s;
639 }
640
641 int session_get_idle_hint(Session *s, dual_timestamp *t) {
642         char *p;
643         struct stat st;
644         usec_t u, n;
645         bool b;
646         int k;
647
648         assert(s);
649
650         if (s->idle_hint) {
651                 if (t)
652                         *t = s->idle_hint_timestamp;
653
654                 return s->idle_hint;
655         }
656
657         if (isempty(s->tty))
658                 goto dont_know;
659
660         if (s->tty[0] != '/') {
661                 p = strappend("/dev/", s->tty);
662                 if (!p)
663                         return -ENOMEM;
664         } else
665                 p = NULL;
666
667         if (!startswith(p ? p : s->tty, "/dev/")) {
668                 free(p);
669                 goto dont_know;
670         }
671
672         k = lstat(p ? p : s->tty, &st);
673         free(p);
674
675         if (k < 0)
676                 goto dont_know;
677
678         u = timespec_load(&st.st_atim);
679         n = now(CLOCK_REALTIME);
680         b = u + IDLE_THRESHOLD_USEC < n;
681
682         if (t)
683                 dual_timestamp_from_realtime(t, u + b ? IDLE_THRESHOLD_USEC : 0);
684
685         return b;
686
687 dont_know:
688         if (t)
689                 *t = s->idle_hint_timestamp;
690
691         return 0;
692 }
693
694 void session_set_idle_hint(Session *s, bool b) {
695         assert(s);
696
697         if (s->idle_hint == b)
698                 return;
699
700         s->idle_hint = b;
701         dual_timestamp_get(&s->idle_hint_timestamp);
702
703         session_send_changed(s,
704                              "IdleHint\0"
705                              "IdleSinceHint\0"
706                              "IdleSinceHintMonotonic\0");
707
708         if (s->seat)
709                 seat_send_changed(s->seat,
710                                   "IdleHint\0"
711                                   "IdleSinceHint\0"
712                                   "IdleSinceHintMonotonic\0");
713
714         user_send_changed(s->user,
715                           "IdleHint\0"
716                           "IdleSinceHint\0"
717                           "IdleSinceHintMonotonic\0");
718
719         manager_send_changed(s->manager,
720                              "IdleHint\0"
721                              "IdleSinceHint\0"
722                              "IdleSinceHintMonotonic\0");
723 }
724
725 int session_check_gc(Session *s) {
726         int r;
727
728         assert(s);
729
730         if (s->pipe_fd >= 0) {
731
732                 r = pipe_eof(s->pipe_fd);
733                 if (r < 0)
734                         return r;
735
736                 if (r == 0)
737                         return 1;
738         }
739
740         if (s->cgroup_path) {
741
742                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
743                 if (r < 0)
744                         return r;
745
746                 if (r <= 0)
747                         return 1;
748         }
749
750         return 0;
751 }
752
753 void session_add_to_gc_queue(Session *s) {
754         assert(s);
755
756         if (s->in_gc_queue)
757                 return;
758
759         LIST_PREPEND(Session, gc_queue, s->manager->session_gc_queue, s);
760         s->in_gc_queue = true;
761 }
762
763 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
764         [SESSION_TTY] = "tty",
765         [SESSION_X11] = "x11",
766         [SESSION_OTHER] = "other"
767 };
768
769 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);