]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-user.c
logind: properly clean up user cgroups when they run empty
[thirdparty/systemd.git] / src / login / logind-user.c
CommitLineData
20263082
LP
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
5430f7f2
LP
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
20263082
LP
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
5430f7f2 16 Lesser General Public License for more details.
20263082 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
20263082
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
22#include <string.h>
23#include <unistd.h>
24#include <errno.h>
25
90821c93 26#include "logind-user.h"
20263082 27#include "util.h"
49e942b2 28#include "mkdir.h"
20263082
LP
29#include "cgroup-util.h"
30#include "hashmap.h"
31#include "strv.h"
32
33User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
34 User *u;
35
36 assert(m);
37 assert(name);
38
14c3baca 39 u = new0(User, 1);
20263082
LP
40 if (!u)
41 return NULL;
42
43 u->name = strdup(name);
44 if (!u->name) {
45 free(u);
46 return NULL;
47 }
48
98a28fef 49 if (asprintf(&u->state_file, "/run/systemd/users/%lu", (unsigned long) uid) < 0) {
20263082
LP
50 free(u->name);
51 free(u);
52 return NULL;
53 }
54
55 if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0) {
56 free(u->state_file);
57 free(u->name);
58 free(u);
59 return NULL;
60 }
61
62 u->manager = m;
63 u->uid = uid;
64 u->gid = gid;
65
66 return u;
67}
68
69void user_free(User *u) {
70 assert(u);
71
14c3baca
LP
72 if (u->in_gc_queue)
73 LIST_REMOVE(User, gc_queue, u->manager->user_gc_queue, u);
74
20263082
LP
75 while (u->sessions)
76 session_free(u->sessions);
77
8c8c4351
LP
78 if (u->cgroup_path)
79 hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
20263082
LP
80 free(u->cgroup_path);
81
82 free(u->service);
83 free(u->runtime_path);
84
85 hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
86
87 free(u->name);
d2f92cdf 88 free(u->state_file);
20263082
LP
89 free(u);
90}
91
92int user_save(User *u) {
93 FILE *f;
94 int r;
14c3baca 95 char *temp_path;
20263082
LP
96
97 assert(u);
98 assert(u->state_file);
99
accaeded
LP
100 if (!u->started)
101 return 0;
102
d2e54fae 103 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
20263082 104 if (r < 0)
14c3baca 105 goto finish;
20263082 106
14c3baca
LP
107 r = fopen_temporary(u->state_file, &f, &temp_path);
108 if (r < 0)
109 goto finish;
110
111 fchmod(fileno(f), 0644);
20263082
LP
112
113 fprintf(f,
14c3baca 114 "# This is private data. Do not parse.\n"
20263082
LP
115 "NAME=%s\n"
116 "STATE=%s\n",
117 u->name,
118 user_state_to_string(user_get_state(u)));
119
120 if (u->cgroup_path)
121 fprintf(f,
122 "CGROUP=%s\n",
123 u->cgroup_path);
124
125 if (u->runtime_path)
126 fprintf(f,
127 "RUNTIME=%s\n",
128 u->runtime_path);
129
130 if (u->service)
131 fprintf(f,
132 "SERVICE=%s\n",
133 u->service);
134
135 if (u->display)
136 fprintf(f,
137 "DISPLAY=%s\n",
138 u->display->id);
139
034a2a52
LP
140 if (u->sessions) {
141 Session *i;
9b958eff 142 bool first;
034a2a52
LP
143
144 fputs("SESSIONS=", f);
9b958eff 145 first = true;
034a2a52 146 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
147 if (first)
148 first = false;
149 else
150 fputc(' ', f);
151
152 fputs(i->id, f);
034a2a52
LP
153 }
154
9b958eff
LP
155 fputs("\nSEATS=", f);
156 first = true;
034a2a52 157 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
158 if (!i->seat)
159 continue;
160
161 if (first)
162 first = false;
163 else
164 fputc(' ', f);
165
166 fputs(i->seat->id, f);
034a2a52
LP
167 }
168
9b958eff
LP
169 fputs("\nACTIVE_SESSIONS=", f);
170 first = true;
171 LIST_FOREACH(sessions_by_user, i, u->sessions) {
172 if (!session_is_active(i))
173 continue;
174
175 if (first)
176 first = false;
177 else
178 fputc(' ', f);
179
180 fputs(i->id, f);
181 }
034a2a52 182
9b958eff
LP
183 fputs("\nACTIVE_SEATS=", f);
184 first = true;
034a2a52 185 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
186 if (!session_is_active(i) || !i->seat)
187 continue;
188
189 if (first)
190 first = false;
191 else
192 fputs(i->seat->id, f);
034a2a52 193 }
9b958eff 194 fputc('\n', f);
034a2a52
LP
195 }
196
20263082 197 fflush(f);
14c3baca
LP
198
199 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
20263082
LP
200 r = -errno;
201 unlink(u->state_file);
14c3baca 202 unlink(temp_path);
20263082
LP
203 }
204
205 fclose(f);
14c3baca
LP
206 free(temp_path);
207
208finish:
209 if (r < 0)
210 log_error("Failed to save user data for %s: %s", u->name, strerror(-r));
211
20263082
LP
212 return r;
213}
214
215int user_load(User *u) {
216 int r;
217 char *display = NULL;
98a28fef 218 Session *s = NULL;
20263082
LP
219
220 assert(u);
221
a185c5aa 222 r = parse_env_file(u->state_file, NEWLINE,
20263082
LP
223 "CGROUP", &u->cgroup_path,
224 "RUNTIME", &u->runtime_path,
225 "SERVICE", &u->service,
226 "DISPLAY", &display,
227 NULL);
228 if (r < 0) {
229 free(display);
230
231 if (r == -ENOENT)
232 return 0;
233
234 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
235 return r;
236 }
237
98a28fef
LP
238 if (display) {
239 s = hashmap_get(u->manager->sessions, display);
240 free(display);
241 }
20263082 242
4d6d6518 243 if (s && s->display && display_is_local(s->display))
20263082
LP
244 u->display = s;
245
246 return r;
247}
248
249static int user_mkdir_runtime_path(User *u) {
250 char *p;
251 int r;
252
253 assert(u);
254
d2e54fae 255 r = mkdir_safe_label("/run/user", 0755, 0, 0);
20263082
LP
256 if (r < 0) {
257 log_error("Failed to create /run/user: %s", strerror(-r));
258 return r;
259 }
260
261 if (!u->runtime_path) {
262 p = strappend("/run/user/", u->name);
263
264 if (!p) {
265 log_error("Out of memory");
266 return -ENOMEM;
267 }
268 } else
269 p = u->runtime_path;
270
d2e54fae 271 r = mkdir_safe_label(p, 0700, u->uid, u->gid);
20263082
LP
272 if (r < 0) {
273 log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
274 free(p);
275 u->runtime_path = NULL;
276 return r;
277 }
278
279 u->runtime_path = p;
280 return 0;
281}
282
283static int user_create_cgroup(User *u) {
284 char **k;
285 char *p;
286 int r;
287
288 assert(u);
289
290 if (!u->cgroup_path) {
291 if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0) {
292 log_error("Out of memory");
293 return -ENOMEM;
294 }
295 } else
296 p = u->cgroup_path;
297
298 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
299 if (r < 0) {
4d6d6518 300 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
20263082
LP
301 free(p);
302 u->cgroup_path = NULL;
20263082
LP
303 return r;
304 }
305
306 u->cgroup_path = p;
307
308 STRV_FOREACH(k, u->manager->controllers) {
193197e8
LP
309
310 if (strv_contains(u->manager->reset_controllers, *k))
311 continue;
312
20263082
LP
313 r = cg_create(*k, p);
314 if (r < 0)
315 log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
316 }
317
8c8c4351
LP
318 hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
319
20263082
LP
320 return 0;
321}
322
323static int user_start_service(User *u) {
324 assert(u);
325
129eebe0
LP
326 /* FIXME: Fill me in later ... */
327
20263082
LP
328 return 0;
329}
330
331int user_start(User *u) {
332 int r;
333
334 assert(u);
335
9418f147
LP
336 if (u->started)
337 return 0;
338
1637a8be 339 log_debug("New user %s logged in.", u->name);
ed18b08b 340
20263082
LP
341 /* Make XDG_RUNTIME_DIR */
342 r = user_mkdir_runtime_path(u);
343 if (r < 0)
344 return r;
345
346 /* Create cgroup */
347 r = user_create_cgroup(u);
348 if (r < 0)
349 return r;
350
351 /* Spawn user systemd */
352 r = user_start_service(u);
353 if (r < 0)
354 return r;
355
356 dual_timestamp_get(&u->timestamp);
357
9418f147
LP
358 u->started = true;
359
7f7bb946
LP
360 /* Save new user data */
361 user_save(u);
362
da119395
LP
363 user_send_signal(u, true);
364
20263082
LP
365 return 0;
366}
367
368static int user_stop_service(User *u) {
369 assert(u);
370
371 if (!u->service)
372 return 0;
373
374 return 0;
375}
376
377static int user_shall_kill(User *u) {
378 assert(u);
379
ed18b08b
LP
380 if (!u->manager->kill_user_processes)
381 return false;
382
383 if (strv_contains(u->manager->kill_exclude_users, u->name))
384 return false;
385
386 if (strv_isempty(u->manager->kill_only_users))
387 return true;
388
389 return strv_contains(u->manager->kill_only_users, u->name);
20263082
LP
390}
391
de07ab16 392static int user_terminate_cgroup(User *u) {
20263082
LP
393 int r;
394 char **k;
395
396 assert(u);
397
398 if (!u->cgroup_path)
399 return 0;
400
401 cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
402
403 if (user_shall_kill(u)) {
404
405 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
406 if (r < 0)
407 log_error("Failed to kill user cgroup: %s", strerror(-r));
408 } else {
409
410 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
411 if (r < 0)
412 log_error("Failed to check user cgroup: %s", strerror(-r));
413 else if (r > 0) {
414 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
415 if (r < 0)
416 log_error("Failed to delete user cgroup: %s", strerror(-r));
417 } else
418 r = -EBUSY;
419 }
420
421 STRV_FOREACH(k, u->manager->controllers)
422 cg_trim(*k, u->cgroup_path, true);
423
8c8c4351
LP
424 hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
425
20263082
LP
426 free(u->cgroup_path);
427 u->cgroup_path = NULL;
428
429 return r;
430}
431
432static int user_remove_runtime_path(User *u) {
433 int r;
434
435 assert(u);
436
437 if (!u->runtime_path)
438 return 0;
439
ad293f5a 440 r = rm_rf(u->runtime_path, false, true, false);
20263082
LP
441 if (r < 0)
442 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
443
444 free(u->runtime_path);
445 u->runtime_path = NULL;
446
447 return r;
448}
449
450int user_stop(User *u) {
451 Session *s;
452 int r = 0, k;
453 assert(u);
454
ed18b08b 455 if (u->started)
1637a8be 456 log_debug("User %s logged out.", u->name);
9418f147 457
20263082
LP
458 LIST_FOREACH(sessions_by_user, s, u->sessions) {
459 k = session_stop(s);
460 if (k < 0)
461 r = k;
462 }
463
464 /* Kill systemd */
465 k = user_stop_service(u);
466 if (k < 0)
467 r = k;
468
469 /* Kill cgroup */
de07ab16 470 k = user_terminate_cgroup(u);
20263082
LP
471 if (k < 0)
472 r = k;
473
474 /* Kill XDG_RUNTIME_DIR */
475 k = user_remove_runtime_path(u);
476 if (k < 0)
477 r = k;
478
d2f92cdf
LP
479 unlink(u->state_file);
480 user_add_to_gc_queue(u);
481
ed18b08b
LP
482 if (u->started)
483 user_send_signal(u, false);
484
9418f147
LP
485 u->started = false;
486
20263082
LP
487 return r;
488}
489
a185c5aa
LP
490int user_get_idle_hint(User *u, dual_timestamp *t) {
491 Session *s;
492 bool idle_hint = true;
493 dual_timestamp ts = { 0, 0 };
494
495 assert(u);
496
497 LIST_FOREACH(sessions_by_user, s, u->sessions) {
498 dual_timestamp k;
499 int ih;
500
501 ih = session_get_idle_hint(s, &k);
502 if (ih < 0)
503 return ih;
504
505 if (!ih) {
506 if (!idle_hint) {
507 if (k.monotonic < ts.monotonic)
508 ts = k;
509 } else {
510 idle_hint = false;
511 ts = k;
512 }
513 } else if (idle_hint) {
514
515 if (k.monotonic > ts.monotonic)
516 ts = k;
517 }
518 }
519
520 if (t)
521 *t = ts;
522
523 return idle_hint;
524}
525
4a4b033f 526int user_check_gc(User *u, bool drop_not_started) {
20263082
LP
527 int r;
528 char *p;
529
530 assert(u);
531
4a4b033f 532 if (drop_not_started && !u->started)
932e3ee7
LP
533 return 0;
534
20263082
LP
535 if (u->sessions)
536 return 1;
537
538 if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
539 return -ENOMEM;
540
541 r = access(p, F_OK) >= 0;
542 free(p);
543
544 if (r > 0)
545 return 1;
546
547 if (u->cgroup_path) {
548 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
549 if (r < 0)
550 return r;
551
552 if (r <= 0)
553 return 1;
554 }
555
556 return 0;
557}
558
14c3baca
LP
559void user_add_to_gc_queue(User *u) {
560 assert(u);
561
562 if (u->in_gc_queue)
563 return;
564
565 LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
566 u->in_gc_queue = true;
567}
568
20263082
LP
569UserState user_get_state(User *u) {
570 Session *i;
571
572 assert(u);
573
574 if (!u->sessions)
575 return USER_LINGERING;
576
577 LIST_FOREACH(sessions_by_user, i, u->sessions)
578 if (session_is_active(i))
579 return USER_ACTIVE;
580
581 return USER_ONLINE;
582}
583
de07ab16
LP
584int user_kill(User *u, int signo) {
585 int r = 0, q;
586 Set *pid_set = NULL;
587
588 assert(u);
589
590 if (!u->cgroup_path)
591 return -ESRCH;
592
593 pid_set = set_new(trivial_hash_func, trivial_compare_func);
594 if (!pid_set)
595 return -ENOMEM;
596
597 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
598 if (q < 0)
599 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
600 r = q;
601
de07ab16
LP
602 if (pid_set)
603 set_free(pid_set);
604
605 return r;
606}
607
20263082
LP
608static const char* const user_state_table[_USER_STATE_MAX] = {
609 [USER_OFFLINE] = "offline",
610 [USER_LINGERING] = "lingering",
611 [USER_ONLINE] = "online",
612 [USER_ACTIVE] = "active"
613};
614
615DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);