chiark / gitweb /
logind: unlink state files when stopping
[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 = 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);