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