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