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