chiark / gitweb /
logind: implement ACL management
[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 Session* session_new(Manager *m, User *u, const char *id) {
32         Session *s;
33
34         assert(m);
35         assert(id);
36
37         s = new(Session, 1);
38         if (!s)
39                 return NULL;
40
41         s->state_file = strappend("/run/systemd/session/", id);
42         if (!s->state_file) {
43                 free(s);
44                 return NULL;
45         }
46
47         s->id = file_name_from_path(s->state_file);
48
49         if (hashmap_put(m->sessions, s->id, s) < 0) {
50                 free(s->id);
51                 free(s);
52                 return NULL;
53         }
54
55         s->manager = m;
56         s->pipe_fd = -1;
57         s->user = u;
58
59         dual_timestamp_get(&s->timestamp);
60
61         return s;
62 }
63
64 void session_free(Session *s) {
65         assert(s);
66
67         if (s->user) {
68                 LIST_REMOVE(Session, sessions_by_user, s->user->sessions, s);
69
70                 if (s->user->display == s)
71                         s->user->display = NULL;
72         }
73
74         if (s->seat)
75                 LIST_REMOVE(Session, sessions_by_seat, s->seat->sessions, s);
76
77         free(s->cgroup_path);
78         strv_free(s->controllers);
79
80         free(s->tty);
81         free(s->display);
82         free(s->remote_host);
83
84         hashmap_remove(s->manager->sessions, s->id);
85
86         free(s->state_file);
87         free(s);
88 }
89
90 int session_save(Session *s) {
91         FILE *f;
92         int r = 0;
93
94         assert(s);
95
96         r = safe_mkdir("/run/systemd/session", 0755, 0, 0);
97         if (r < 0)
98                 return r;
99
100         f = fopen(s->state_file, "we");
101         if (!f)
102                 return -errno;
103
104         assert(s->user);
105
106         fprintf(f,
107                 "# This is private data. Do not parse.\n"
108                 "UID=%lu\n"
109                 "USER=%s\n"
110                 "ACTIVE=%i\n"
111                 "REMOTE=%i\n"
112                 "KILL_PROCESSES=%i\n",
113                 (unsigned long) s->user->uid,
114                 s->user->name,
115                 session_is_active(s),
116                 s->remote,
117                 s->kill_processes);
118
119         if (s->cgroup_path)
120                 fprintf(f,
121                         "CGROUP=%s\n",
122                         s->cgroup_path);
123
124         if (s->seat)
125                 fprintf(f,
126                         "SEAT=%s\n",
127                         s->seat->id);
128
129         if (s->tty)
130                 fprintf(f,
131                         "TTY=%s\n",
132                         s->tty);
133
134         if (s->display)
135                 fprintf(f,
136                         "DISPLAY=%s\n",
137                         s->display);
138
139         if (s->remote_host)
140                 fprintf(f,
141                         "REMOTE_HOST=%s\n",
142                         s->remote_host);
143
144         if (s->seat && s->seat->manager->vtconsole == s->seat)
145                 fprintf(f,
146                         "VTNR=%i\n",
147                         s->vtnr);
148
149         if (s->leader > 0)
150                 fprintf(f,
151                         "LEADER=%lu\n",
152                         (unsigned long) s->leader);
153
154         if (s->audit_id > 0)
155                 fprintf(f,
156                         "AUDIT=%llu\n",
157                         (unsigned long long) s->audit_id);
158
159         fflush(f);
160         if (ferror(f)) {
161                 r = -errno;
162                 unlink(s->state_file);
163         }
164
165         fclose(f);
166         return r;
167 }
168
169 int session_load(Session *s) {
170         assert(s);
171
172         return 0;
173 }
174
175 int session_activate(Session *s) {
176         int r;
177         Session *old_active;
178
179         assert(s);
180
181         if (s->vtnr < 0)
182                 return -ENOTSUP;
183
184         if (!s->seat)
185                 return -ENOTSUP;
186
187         if (s->seat->active == s)
188                 return 0;
189
190         assert(s->manager->vtconsole == s->seat);
191
192         r = chvt(s->vtnr);
193         if (r < 0)
194                 return r;
195
196         old_active = s->seat->active;
197         s->seat->active = s;
198
199         seat_apply_acls(s->seat, old_active);
200         manager_spawn_autovt(s->manager, s->vtnr);
201
202         return 0;
203 }
204
205 bool x11_display_is_local(const char *display) {
206         assert(display);
207
208         return
209                 display[0] == ':' &&
210                 display[1] >= '0' &&
211                 display[1] <= '9';
212 }
213
214 static int session_link_x11_socket(Session *s) {
215         char *t, *f, *c;
216         size_t k;
217
218         assert(s);
219         assert(s->user);
220         assert(s->user->runtime_path);
221
222         if (s->user->display)
223                 return 0;
224
225         if (!s->display || !x11_display_is_local(s->display))
226                 return 0;
227
228         k = strspn(s->display+1, "0123456789");
229         f = new(char, sizeof("/tmp/.X11-unix/X") + k);
230         if (!f) {
231                 log_error("Out of memory");
232                 return -ENOMEM;
233         }
234
235         c = stpcpy(f, "/tmp/.X11-unix/X");
236         memcpy(c, s->display+1, k);
237         c[k] = 0;
238
239         if (access(f, F_OK) < 0) {
240                 log_warning("Session %s has display %s with nonexisting socket %s.", s->id, s->display, f);
241                 free(f);
242                 return -ENOENT;
243         }
244
245         t = strappend(s->user->runtime_path, "/display");
246         if (!t) {
247                 log_error("Out of memory");
248                 free(f);
249                 return -ENOMEM;
250         }
251
252         if (link(f, t) < 0) {
253                 if (errno == EEXIST) {
254                         unlink(t);
255
256                         if (link(f, t) >= 0)
257                                 goto done;
258                 }
259
260                 if (symlink(f, t) < 0) {
261
262                         if (errno == EEXIST) {
263                                 unlink(t);
264
265                                 if (symlink(f, t) >= 0)
266                                         goto done;
267                         }
268
269                         log_error("Failed to link %s to %s: %m", f, t);
270                         free(f);
271                         free(t);
272                         return -errno;
273                 }
274         }
275
276 done:
277         log_info("Linked %s to %s.", f, t);
278         free(f);
279         free(t);
280
281         s->user->display = s;
282
283         return 0;
284 }
285
286 static int session_create_cgroup(Session *s) {
287         char **k;
288         char *p;
289         int r;
290
291         assert(s);
292         assert(s->user);
293         assert(s->user->cgroup_path);
294
295         if (!s->cgroup_path) {
296                 if (asprintf(&p, "%s/%s", s->user->cgroup_path, s->id) < 0) {
297                         log_error("Out of memory");
298                         return -ENOMEM;
299                 }
300         } else
301                 p = s->cgroup_path;
302
303         if (s->leader > 0)
304                 r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, p, s->leader);
305         else
306                 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
307
308         if (r < 0) {
309                 free(p);
310                 s->cgroup_path = NULL;
311                 log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
312                 return r;
313         }
314
315         s->cgroup_path = p;
316
317         STRV_FOREACH(k, s->manager->controllers) {
318                 if (s->leader > 0)
319                         r = cg_create_and_attach(*k, p, s->leader);
320                 else
321                         r = cg_create(*k, p);
322
323                 if (r < 0)
324                         log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
325         }
326
327         return 0;
328 }
329
330 int session_start(Session *s) {
331         int r;
332
333         assert(s);
334         assert(s->user);
335
336         /* Create user first */
337         r = user_start(s->user);
338         if (r < 0)
339                 return r;
340
341         /* Create cgroup */
342         r = session_create_cgroup(s);
343         if (r < 0)
344                 return r;
345
346         /* Create X11 symlink */
347         session_link_x11_socket(s);
348         return 0;
349 }
350
351 static bool session_shall_kill(Session *s) {
352         assert(s);
353
354         return s->kill_processes;
355 }
356
357 static int session_kill_cgroup(Session *s) {
358         int r;
359         char **k;
360
361         assert(s);
362
363         if (!s->cgroup_path)
364                 return 0;
365
366         cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
367
368         if (session_shall_kill(s)) {
369
370                 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
371                 if (r < 0)
372                         log_error("Failed to kill session cgroup: %s", strerror(-r));
373
374         } else {
375                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
376                 if (r < 0)
377                         log_error("Failed to check session cgroup: %s", strerror(-r));
378                 else if (r > 0) {
379                         r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path);
380                         if (r < 0)
381                                 log_error("Failed to delete session cgroup: %s", strerror(-r));
382                 } else
383                         r = -EBUSY;
384         }
385
386         STRV_FOREACH(k, s->user->manager->controllers)
387                 cg_trim(*k, s->cgroup_path, true);
388
389         free(s->cgroup_path);
390         s->cgroup_path = NULL;
391
392         return r;
393 }
394
395 static int session_unlink_x11_socket(Session *s) {
396         char *t;
397         int r;
398
399         assert(s);
400         assert(s->user);
401
402         if (s->user->display != s)
403                 return 0;
404
405         s->user->display = NULL;
406
407         t = strappend(s->user->runtime_path, "/display");
408         if (!t) {
409                 log_error("Out of memory");
410                 return -ENOMEM;
411         }
412
413         r = unlink(t);
414         free(t);
415
416         return r < 0 ? -errno : 0;
417 }
418
419 int session_stop(Session *s) {
420         int r = 0, k;
421
422         assert(s);
423
424         /* Kill cgroup */
425         k = session_kill_cgroup(s);
426         if (k < 0)
427                 r = k;
428
429         /* Remove X11 symlink */
430         session_unlink_x11_socket(s);
431
432         return r;
433 }
434
435 bool session_is_active(Session *s) {
436         assert(s);
437
438         if (!s->seat)
439                 return true;
440
441         return s->seat->active == s;
442 }
443
444 int session_check_gc(Session *s) {
445         int r;
446
447         assert(s);
448
449         if (s->pipe_fd >= 0) {
450
451                 r = pipe_eof(s->pipe_fd);
452                 if (r < 0)
453                         return r;
454
455                 if (r <= 0)
456                         return 1;
457         }
458
459         if (s->cgroup_path) {
460
461                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
462                 if (r < 0)
463                         return r;
464
465                 if (r <= 0)
466                         return 1;
467         }
468
469         return 0;
470 }
471
472 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
473         [SESSION_TERMINAL] = "terminal",
474         [SESSION_X11] = "x11"
475 };
476
477 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);