]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-user.c
use "Out of memory." consistantly (or with "\n")
[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) {
2138e969 262 if (asprintf(&p, "/run/user/%lu", (unsigned long) u->uid) < 0) {
669241a0 263 log_error("Out of memory.");
20263082
LP
264 return -ENOMEM;
265 }
266 } else
267 p = u->runtime_path;
268
d2e54fae 269 r = mkdir_safe_label(p, 0700, u->uid, u->gid);
20263082
LP
270 if (r < 0) {
271 log_error("Failed to create runtime directory %s: %s", p, strerror(-r));
272 free(p);
273 u->runtime_path = NULL;
274 return r;
275 }
276
277 u->runtime_path = p;
278 return 0;
279}
280
281static int user_create_cgroup(User *u) {
282 char **k;
283 char *p;
284 int r;
285
286 assert(u);
287
288 if (!u->cgroup_path) {
289 if (asprintf(&p, "%s/%s", u->manager->cgroup_path, u->name) < 0) {
669241a0 290 log_error("Out of memory.");
20263082
LP
291 return -ENOMEM;
292 }
293 } else
294 p = u->cgroup_path;
295
296 r = cg_create(SYSTEMD_CGROUP_CONTROLLER, p);
297 if (r < 0) {
4d6d6518 298 log_error("Failed to create cgroup "SYSTEMD_CGROUP_CONTROLLER":%s: %s", p, strerror(-r));
20263082
LP
299 free(p);
300 u->cgroup_path = NULL;
20263082
LP
301 return r;
302 }
303
304 u->cgroup_path = p;
305
306 STRV_FOREACH(k, u->manager->controllers) {
193197e8
LP
307
308 if (strv_contains(u->manager->reset_controllers, *k))
309 continue;
310
20263082
LP
311 r = cg_create(*k, p);
312 if (r < 0)
313 log_warning("Failed to create cgroup %s:%s: %s", *k, p, strerror(-r));
314 }
315
8c8c4351
LP
316 hashmap_put(u->manager->user_cgroups, u->cgroup_path, u);
317
20263082
LP
318 return 0;
319}
320
321static int user_start_service(User *u) {
322 assert(u);
323
129eebe0
LP
324 /* FIXME: Fill me in later ... */
325
20263082
LP
326 return 0;
327}
328
329int user_start(User *u) {
330 int r;
331
332 assert(u);
333
9418f147
LP
334 if (u->started)
335 return 0;
336
1637a8be 337 log_debug("New user %s logged in.", u->name);
ed18b08b 338
20263082
LP
339 /* Make XDG_RUNTIME_DIR */
340 r = user_mkdir_runtime_path(u);
341 if (r < 0)
342 return r;
343
344 /* Create cgroup */
345 r = user_create_cgroup(u);
346 if (r < 0)
347 return r;
348
349 /* Spawn user systemd */
350 r = user_start_service(u);
351 if (r < 0)
352 return r;
353
354 dual_timestamp_get(&u->timestamp);
355
9418f147
LP
356 u->started = true;
357
7f7bb946
LP
358 /* Save new user data */
359 user_save(u);
360
da119395
LP
361 user_send_signal(u, true);
362
20263082
LP
363 return 0;
364}
365
366static int user_stop_service(User *u) {
367 assert(u);
368
369 if (!u->service)
370 return 0;
371
372 return 0;
373}
374
375static int user_shall_kill(User *u) {
376 assert(u);
377
ed18b08b
LP
378 if (!u->manager->kill_user_processes)
379 return false;
380
381 if (strv_contains(u->manager->kill_exclude_users, u->name))
382 return false;
383
384 if (strv_isempty(u->manager->kill_only_users))
385 return true;
386
387 return strv_contains(u->manager->kill_only_users, u->name);
20263082
LP
388}
389
de07ab16 390static int user_terminate_cgroup(User *u) {
20263082
LP
391 int r;
392 char **k;
393
394 assert(u);
395
396 if (!u->cgroup_path)
397 return 0;
398
399 cg_trim(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
400
401 if (user_shall_kill(u)) {
402
403 r = cg_kill_recursive_and_wait(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
404 if (r < 0)
405 log_error("Failed to kill user cgroup: %s", strerror(-r));
406 } else {
407
408 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, true);
409 if (r < 0)
410 log_error("Failed to check user cgroup: %s", strerror(-r));
411 else if (r > 0) {
412 r = cg_delete(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
413 if (r < 0)
414 log_error("Failed to delete user cgroup: %s", strerror(-r));
415 } else
416 r = -EBUSY;
417 }
418
419 STRV_FOREACH(k, u->manager->controllers)
420 cg_trim(*k, u->cgroup_path, true);
421
8c8c4351
LP
422 hashmap_remove(u->manager->user_cgroups, u->cgroup_path);
423
20263082
LP
424 free(u->cgroup_path);
425 u->cgroup_path = NULL;
426
427 return r;
428}
429
430static int user_remove_runtime_path(User *u) {
431 int r;
432
433 assert(u);
434
435 if (!u->runtime_path)
436 return 0;
437
ad293f5a 438 r = rm_rf(u->runtime_path, false, true, false);
20263082
LP
439 if (r < 0)
440 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
441
442 free(u->runtime_path);
443 u->runtime_path = NULL;
444
445 return r;
446}
447
448int user_stop(User *u) {
449 Session *s;
450 int r = 0, k;
451 assert(u);
452
ed18b08b 453 if (u->started)
1637a8be 454 log_debug("User %s logged out.", u->name);
9418f147 455
20263082
LP
456 LIST_FOREACH(sessions_by_user, s, u->sessions) {
457 k = session_stop(s);
458 if (k < 0)
459 r = k;
460 }
461
462 /* Kill systemd */
463 k = user_stop_service(u);
464 if (k < 0)
465 r = k;
466
467 /* Kill cgroup */
de07ab16 468 k = user_terminate_cgroup(u);
20263082
LP
469 if (k < 0)
470 r = k;
471
472 /* Kill XDG_RUNTIME_DIR */
473 k = user_remove_runtime_path(u);
474 if (k < 0)
475 r = k;
476
d2f92cdf
LP
477 unlink(u->state_file);
478 user_add_to_gc_queue(u);
479
ed18b08b
LP
480 if (u->started)
481 user_send_signal(u, false);
482
9418f147
LP
483 u->started = false;
484
20263082
LP
485 return r;
486}
487
a185c5aa
LP
488int user_get_idle_hint(User *u, dual_timestamp *t) {
489 Session *s;
490 bool idle_hint = true;
491 dual_timestamp ts = { 0, 0 };
492
493 assert(u);
494
495 LIST_FOREACH(sessions_by_user, s, u->sessions) {
496 dual_timestamp k;
497 int ih;
498
499 ih = session_get_idle_hint(s, &k);
500 if (ih < 0)
501 return ih;
502
503 if (!ih) {
504 if (!idle_hint) {
505 if (k.monotonic < ts.monotonic)
506 ts = k;
507 } else {
508 idle_hint = false;
509 ts = k;
510 }
511 } else if (idle_hint) {
512
513 if (k.monotonic > ts.monotonic)
514 ts = k;
515 }
516 }
517
518 if (t)
519 *t = ts;
520
521 return idle_hint;
522}
523
e96cd586
LP
524static int user_check_linger_file(User *u) {
525 char *p;
526 int r;
527
528 if (asprintf(&p, "/var/lib/systemd/linger/%s", u->name) < 0)
529 return -ENOMEM;
530
531 r = access(p, F_OK) >= 0;
532 free(p);
533
534 return r;
535}
536
4a4b033f 537int user_check_gc(User *u, bool drop_not_started) {
20263082 538 int r;
20263082
LP
539
540 assert(u);
541
4a4b033f 542 if (drop_not_started && !u->started)
932e3ee7
LP
543 return 0;
544
20263082
LP
545 if (u->sessions)
546 return 1;
547
e96cd586 548 if (user_check_linger_file(u) > 0)
20263082
LP
549 return 1;
550
551 if (u->cgroup_path) {
552 r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, false);
553 if (r < 0)
554 return r;
555
556 if (r <= 0)
557 return 1;
558 }
559
560 return 0;
561}
562
14c3baca
LP
563void user_add_to_gc_queue(User *u) {
564 assert(u);
565
566 if (u->in_gc_queue)
567 return;
568
569 LIST_PREPEND(User, gc_queue, u->manager->user_gc_queue, u);
570 u->in_gc_queue = true;
571}
572
20263082
LP
573UserState user_get_state(User *u) {
574 Session *i;
575
576 assert(u);
577
20263082
LP
578 LIST_FOREACH(sessions_by_user, i, u->sessions)
579 if (session_is_active(i))
580 return USER_ACTIVE;
581
e96cd586
LP
582 if (u->sessions)
583 return USER_ONLINE;
584
585 if (user_check_linger_file(u) > 0)
586 return USER_LINGERING;
587
588 return USER_CLOSING;
20263082
LP
589}
590
de07ab16
LP
591int user_kill(User *u, int signo) {
592 int r = 0, q;
593 Set *pid_set = NULL;
594
595 assert(u);
596
597 if (!u->cgroup_path)
598 return -ESRCH;
599
600 pid_set = set_new(trivial_hash_func, trivial_compare_func);
601 if (!pid_set)
602 return -ENOMEM;
603
604 q = cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, signo, false, true, false, pid_set);
605 if (q < 0)
606 if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
607 r = q;
608
de07ab16
LP
609 if (pid_set)
610 set_free(pid_set);
611
612 return r;
613}
614
20263082
LP
615static const char* const user_state_table[_USER_STATE_MAX] = {
616 [USER_OFFLINE] = "offline",
617 [USER_LINGERING] = "lingering",
618 [USER_ONLINE] = "online",
e96cd586
LP
619 [USER_ACTIVE] = "active",
620 [USER_CLOSING] = "closing"
20263082
LP
621};
622
623DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);