chiark / gitweb /
logind: split up logind.h
[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
178         assert(s);
179
180         if (s->vtnr < 0)
181                 return -ENOTSUP;
182
183         if (!s->seat)
184                 return -ENOTSUP;
185
186         if (s->seat->active == s)
187                 return 0;
188
189         assert(s->manager->vtconsole == s->seat);
190
191         r = chvt(s->vtnr);
192         if (r < 0)
193                 return r;
194
195         s->seat->active = s;
196
197         return seat_apply_acls(s->seat);
198 }
199
200 bool x11_display_is_local(const char *display) {
201         assert(display);
202
203         return
204                 display[0] == ':' &&
205                 display[1] >= '0' &&
206                 display[1] <= '9';
207 }
208
209 static int session_link_x11_socket(Session *s) {
210         char *t, *f, *c;
211         size_t k;
212
213         assert(s);
214         assert(s->user);
215         assert(s->user->runtime_path);
216
217         if (s->user->display)
218                 return 0;
219
220         if (!s->display || !x11_display_is_local(s->display))
221                 return 0;
222
223         k = strspn(s->display+1, "0123456789");
224         f = new(char, sizeof("/tmp/.X11-unix/X") + k);
225         if (!f) {
226                 log_error("Out of memory");
227                 return -ENOMEM;
228         }
229
230         c = stpcpy(f, "/tmp/.X11-unix/X");
231         memcpy(c, s->display+1, k);
232         c[k] = 0;
233
234         if (access(f, F_OK) < 0) {
235                 log_warning("Session %s has display %s with nonexisting socket %s.", s->id, s->display, f);
236                 free(f);
237                 return -ENOENT;
238         }
239
240         t = strappend(s->user->runtime_path, "/display");
241         if (!t) {
242                 log_error("Out of memory");
243                 free(f);
244                 return -ENOMEM;
245         }
246
247         if (link(f, t) < 0) {
248                 if (errno == EEXIST) {
249                         unlink(t);
250
251                         if (link(f, t) >= 0)
252                                 goto done;
253                 }
254
255                 if (symlink(f, t) < 0) {
256
257                         if (errno == EEXIST) {
258                                 unlink(t);
259
260                                 if (symlink(f, t) >= 0)
261                                         goto done;
262                         }
263
264                         log_error("Failed to link %s to %s: %m", f, t);
265                         free(f);
266                         free(t);
267                         return -errno;
268                 }
269         }
270
271 done:
272         log_info("Linked %s to %s.", f, t);
273         free(f);
274         free(t);
275
276         s->user->display = s;
277
278         return 0;
279 }
280
281 static int session_create_cgroup(Session *s) {
282         char **k;
283         char *p;
284         int r;
285
286         assert(s);
287         assert(s->user);
288         assert(s->user->cgroup_path);
289
290         if (!s->cgroup_path) {
291                 if (asprintf(&p, "%s/%s", s->user->cgroup_path, s->id) < 0) {
292                         log_error("Out of memory");
293                         return -ENOMEM;
294                 }
295         } else
296                 p = s->cgroup_path;
297
298         if (s->leader > 0)
299                 r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, p, s->leader);
300         else
301                 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
302
303         if (r < 0) {
304                 free(p);
305                 s->cgroup_path = NULL;
306                 log_error("Failed to create "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
307                 return r;
308         }
309
310         s->cgroup_path = p;
311
312         STRV_FOREACH(k, s->manager->controllers) {
313                 if (s->leader > 0)
314                         r = cg_create_and_attach(*k, p, s->leader);
315                 else
316                         r = cg_create(*k, p);
317
318                 if (r < 0)
319                         log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
320         }
321
322         return 0;
323 }
324
325 int session_start(Session *s) {
326         int r;
327
328         assert(s);
329         assert(s->user);
330
331         /* Create user first */
332         r = user_start(s->user);
333         if (r < 0)
334                 return r;
335
336         /* Create cgroup */
337         r = session_create_cgroup(s);
338         if (r < 0)
339                 return r;
340
341         /* Create X11 symlink */
342         session_link_x11_socket(s);
343         return 0;
344 }
345
346 static bool session_shall_kill(Session *s) {
347         assert(s);
348
349         return s->kill_processes;
350 }
351
352 static int session_kill_cgroup(Session *s) {
353         int r;
354         char **k;
355
356         assert(s);
357
358         if (!s->cgroup_path)
359                 return 0;
360
361         cg_trim(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
362
363         if (session_shall_kill(s)) {
364
365                 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
366                 if (r < 0)
367                         log_error("Failed to kill session cgroup: %s", strerror(-r));
368
369         } else {
370                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, true);
371                 if (r < 0)
372                         log_error("Failed to check session cgroup: %s", strerror(-r));
373                 else if (r > 0) {
374                         r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path);
375                         if (r < 0)
376                                 log_error("Failed to delete session cgroup: %s", strerror(-r));
377                 } else
378                         r = -EBUSY;
379         }
380
381         STRV_FOREACH(k, s->user->manager->controllers)
382                 cg_trim(*k, s->cgroup_path, true);
383
384         free(s->cgroup_path);
385         s->cgroup_path = NULL;
386
387         return r;
388 }
389
390 static int session_unlink_x11_socket(Session *s) {
391         char *t;
392         int r;
393
394         assert(s);
395         assert(s->user);
396
397         if (s->user->display != s)
398                 return 0;
399
400         s->user->display = NULL;
401
402         t = strappend(s->user->runtime_path, "/display");
403         if (!t) {
404                 log_error("Out of memory");
405                 return -ENOMEM;
406         }
407
408         r = unlink(t);
409         free(t);
410
411         return r < 0 ? -errno : 0;
412 }
413
414 int session_stop(Session *s) {
415         int r = 0, k;
416
417         assert(s);
418
419         /* Kill cgroup */
420         k = session_kill_cgroup(s);
421         if (k < 0)
422                 r = k;
423
424         /* Remove X11 symlink */
425         session_unlink_x11_socket(s);
426
427         return r;
428 }
429
430 bool session_is_active(Session *s) {
431         assert(s);
432
433         if (!s->seat)
434                 return true;
435
436         return s->seat->active == s;
437 }
438
439 int session_check_gc(Session *s) {
440         int r;
441
442         assert(s);
443
444         if (s->pipe_fd >= 0) {
445
446                 r = pipe_eof(s->pipe_fd);
447                 if (r < 0)
448                         return r;
449
450                 if (r <= 0)
451                         return 1;
452         }
453
454         if (s->cgroup_path) {
455
456                 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_path, false);
457                 if (r < 0)
458                         return r;
459
460                 if (r <= 0)
461                         return 1;
462         }
463
464         return 0;
465 }
466
467 static const char* const session_type_table[_SESSION_TYPE_MAX] = {
468         [SESSION_TERMINAL] = "terminal",
469         [SESSION_X11] = "x11"
470 };
471
472 DEFINE_STRING_TABLE_LOOKUP(session_type, SessionType);