]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-user.c
util: rework rm_rf() logic
[thirdparty/systemd.git] / src / login / logind-user.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 Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/mount.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <errno.h>
26
27 #include "util.h"
28 #include "mkdir.h"
29 #include "rm-rf.h"
30 #include "hashmap.h"
31 #include "fileio.h"
32 #include "path-util.h"
33 #include "special.h"
34 #include "unit-name.h"
35 #include "bus-util.h"
36 #include "bus-error.h"
37 #include "conf-parser.h"
38 #include "clean-ipc.h"
39 #include "logind-user.h"
40 #include "smack-util.h"
41
42 User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
43 User *u;
44
45 assert(m);
46 assert(name);
47
48 u = new0(User, 1);
49 if (!u)
50 return NULL;
51
52 u->name = strdup(name);
53 if (!u->name)
54 goto fail;
55
56 if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
57 goto fail;
58
59 if (hashmap_put(m->users, UID_TO_PTR(uid), u) < 0)
60 goto fail;
61
62 u->manager = m;
63 u->uid = uid;
64 u->gid = gid;
65
66 return u;
67
68 fail:
69 free(u->state_file);
70 free(u->name);
71 free(u);
72
73 return NULL;
74 }
75
76 void user_free(User *u) {
77 assert(u);
78
79 if (u->in_gc_queue)
80 LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u);
81
82 while (u->sessions)
83 session_free(u->sessions);
84
85 if (u->slice) {
86 hashmap_remove(u->manager->user_units, u->slice);
87 free(u->slice);
88 }
89
90 if (u->service) {
91 hashmap_remove(u->manager->user_units, u->service);
92 free(u->service);
93 }
94
95 free(u->slice_job);
96 free(u->service_job);
97
98 free(u->runtime_path);
99
100 hashmap_remove(u->manager->users, UID_TO_PTR(u->uid));
101
102 free(u->name);
103 free(u->state_file);
104 free(u);
105 }
106
107 int user_save(User *u) {
108 _cleanup_free_ char *temp_path = NULL;
109 _cleanup_fclose_ FILE *f = NULL;
110 int r;
111
112 assert(u);
113 assert(u->state_file);
114
115 if (!u->started)
116 return 0;
117
118 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
119 if (r < 0)
120 goto finish;
121
122 r = fopen_temporary(u->state_file, &f, &temp_path);
123 if (r < 0)
124 goto finish;
125
126 fchmod(fileno(f), 0644);
127
128 fprintf(f,
129 "# This is private data. Do not parse.\n"
130 "NAME=%s\n"
131 "STATE=%s\n",
132 u->name,
133 user_state_to_string(user_get_state(u)));
134
135 if (u->runtime_path)
136 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
137
138 if (u->service)
139 fprintf(f, "SERVICE=%s\n", u->service);
140 if (u->service_job)
141 fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
142
143 if (u->slice)
144 fprintf(f, "SLICE=%s\n", u->slice);
145 if (u->slice_job)
146 fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
147
148 if (u->display)
149 fprintf(f, "DISPLAY=%s\n", u->display->id);
150
151 if (dual_timestamp_is_set(&u->timestamp))
152 fprintf(f,
153 "REALTIME="USEC_FMT"\n"
154 "MONOTONIC="USEC_FMT"\n",
155 u->timestamp.realtime,
156 u->timestamp.monotonic);
157
158 if (u->sessions) {
159 Session *i;
160 bool first;
161
162 fputs("SESSIONS=", f);
163 first = true;
164 LIST_FOREACH(sessions_by_user, i, u->sessions) {
165 if (first)
166 first = false;
167 else
168 fputc(' ', f);
169
170 fputs(i->id, f);
171 }
172
173 fputs("\nSEATS=", f);
174 first = true;
175 LIST_FOREACH(sessions_by_user, i, u->sessions) {
176 if (!i->seat)
177 continue;
178
179 if (first)
180 first = false;
181 else
182 fputc(' ', f);
183
184 fputs(i->seat->id, f);
185 }
186
187 fputs("\nACTIVE_SESSIONS=", f);
188 first = true;
189 LIST_FOREACH(sessions_by_user, i, u->sessions) {
190 if (!session_is_active(i))
191 continue;
192
193 if (first)
194 first = false;
195 else
196 fputc(' ', f);
197
198 fputs(i->id, f);
199 }
200
201 fputs("\nONLINE_SESSIONS=", f);
202 first = true;
203 LIST_FOREACH(sessions_by_user, i, u->sessions) {
204 if (session_get_state(i) == SESSION_CLOSING)
205 continue;
206
207 if (first)
208 first = false;
209 else
210 fputc(' ', f);
211
212 fputs(i->id, f);
213 }
214
215 fputs("\nACTIVE_SEATS=", f);
216 first = true;
217 LIST_FOREACH(sessions_by_user, i, u->sessions) {
218 if (!session_is_active(i) || !i->seat)
219 continue;
220
221 if (first)
222 first = false;
223 else
224 fputc(' ', f);
225
226 fputs(i->seat->id, f);
227 }
228
229 fputs("\nONLINE_SEATS=", f);
230 first = true;
231 LIST_FOREACH(sessions_by_user, i, u->sessions) {
232 if (session_get_state(i) == SESSION_CLOSING || !i->seat)
233 continue;
234
235 if (first)
236 first = false;
237 else
238 fputc(' ', f);
239
240 fputs(i->seat->id, f);
241 }
242 fputc('\n', f);
243 }
244
245 fflush(f);
246
247 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
248 r = -errno;
249 unlink(u->state_file);
250 unlink(temp_path);
251 }
252
253 finish:
254 if (r < 0)
255 log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
256
257 return r;
258 }
259
260 int user_load(User *u) {
261 _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
262 Session *s = NULL;
263 int r;
264
265 assert(u);
266
267 r = parse_env_file(u->state_file, NEWLINE,
268 "RUNTIME", &u->runtime_path,
269 "SERVICE", &u->service,
270 "SERVICE_JOB", &u->service_job,
271 "SLICE", &u->slice,
272 "SLICE_JOB", &u->slice_job,
273 "DISPLAY", &display,
274 "REALTIME", &realtime,
275 "MONOTONIC", &monotonic,
276 NULL);
277 if (r < 0) {
278 if (r == -ENOENT)
279 return 0;
280
281 log_error_errno(r, "Failed to read %s: %m", u->state_file);
282 return r;
283 }
284
285 if (display)
286 s = hashmap_get(u->manager->sessions, display);
287
288 if (s && s->display && display_is_local(s->display))
289 u->display = s;
290
291 if (realtime) {
292 unsigned long long l;
293 if (sscanf(realtime, "%llu", &l) > 0)
294 u->timestamp.realtime = l;
295 }
296
297 if (monotonic) {
298 unsigned long long l;
299 if (sscanf(monotonic, "%llu", &l) > 0)
300 u->timestamp.monotonic = l;
301 }
302
303 return r;
304 }
305
306 static int user_mkdir_runtime_path(User *u) {
307 char *p;
308 int r;
309
310 assert(u);
311
312 r = mkdir_safe_label("/run/user", 0755, 0, 0);
313 if (r < 0)
314 return log_error_errno(r, "Failed to create /run/user: %m");
315
316 if (!u->runtime_path) {
317 if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
318 return log_oom();
319 } else
320 p = u->runtime_path;
321
322 if (path_is_mount_point(p, false) <= 0) {
323 _cleanup_free_ char *t = NULL;
324
325 (void) mkdir(p, 0700);
326
327 if (mac_smack_use())
328 r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
329 else
330 r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
331 if (r < 0) {
332 r = log_oom();
333 goto fail;
334 }
335
336 r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
337 if (r < 0) {
338 if (errno != EPERM) {
339 r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", p);
340 goto fail;
341 }
342
343 /* Lacking permissions, maybe
344 * CAP_SYS_ADMIN-less container? In this case,
345 * just use a normal directory. */
346
347 r = chmod_and_chown(p, 0700, u->uid, u->gid);
348 if (r < 0) {
349 log_error_errno(r, "Failed to change runtime directory ownership and mode: %m");
350 goto fail;
351 }
352 }
353 }
354
355 u->runtime_path = p;
356 return 0;
357
358 fail:
359 if (p) {
360 /* Try to clean up, but ignore errors */
361 (void) rmdir(p);
362 free(p);
363 }
364
365 u->runtime_path = NULL;
366 return r;
367 }
368
369 static int user_start_slice(User *u) {
370 char *job;
371 int r;
372
373 assert(u);
374
375 if (!u->slice) {
376 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
377 char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice;
378 sprintf(lu, UID_FMT, u->uid);
379
380 r = build_subslice(SPECIAL_USER_SLICE, lu, &slice);
381 if (r < 0)
382 return r;
383
384 r = manager_start_unit(u->manager, slice, &error, &job);
385 if (r < 0) {
386 log_error("Failed to start user slice: %s", bus_error_message(&error, r));
387 free(slice);
388 } else {
389 u->slice = slice;
390
391 free(u->slice_job);
392 u->slice_job = job;
393 }
394 }
395
396 if (u->slice)
397 hashmap_put(u->manager->user_units, u->slice, u);
398
399 return 0;
400 }
401
402 static int user_start_service(User *u) {
403 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
404 char *job;
405 int r;
406
407 assert(u);
408
409 if (!u->service) {
410 char lu[DECIMAL_STR_MAX(uid_t) + 1], *service;
411 sprintf(lu, UID_FMT, u->uid);
412
413 service = unit_name_build("user", lu, ".service");
414 if (!service)
415 return log_oom();
416
417 r = manager_start_unit(u->manager, service, &error, &job);
418 if (r < 0) {
419 log_error("Failed to start user service: %s", bus_error_message(&error, r));
420 free(service);
421 } else {
422 u->service = service;
423
424 free(u->service_job);
425 u->service_job = job;
426 }
427 }
428
429 if (u->service)
430 hashmap_put(u->manager->user_units, u->service, u);
431
432 return 0;
433 }
434
435 int user_start(User *u) {
436 int r;
437
438 assert(u);
439
440 if (u->started)
441 return 0;
442
443 log_debug("New user %s logged in.", u->name);
444
445 /* Make XDG_RUNTIME_DIR */
446 r = user_mkdir_runtime_path(u);
447 if (r < 0)
448 return r;
449
450 /* Create cgroup */
451 r = user_start_slice(u);
452 if (r < 0)
453 return r;
454
455 /* Spawn user systemd */
456 r = user_start_service(u);
457 if (r < 0)
458 return r;
459
460 if (!dual_timestamp_is_set(&u->timestamp))
461 dual_timestamp_get(&u->timestamp);
462
463 u->started = true;
464
465 /* Save new user data */
466 user_save(u);
467
468 user_send_signal(u, true);
469
470 return 0;
471 }
472
473 static int user_stop_slice(User *u) {
474 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
475 char *job;
476 int r;
477
478 assert(u);
479
480 if (!u->slice)
481 return 0;
482
483 r = manager_stop_unit(u->manager, u->slice, &error, &job);
484 if (r < 0) {
485 log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
486 return r;
487 }
488
489 free(u->slice_job);
490 u->slice_job = job;
491
492 return r;
493 }
494
495 static int user_stop_service(User *u) {
496 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
497 char *job;
498 int r;
499
500 assert(u);
501
502 if (!u->service)
503 return 0;
504
505 r = manager_stop_unit(u->manager, u->service, &error, &job);
506 if (r < 0) {
507 log_error("Failed to stop user service: %s", bus_error_message(&error, r));
508 return r;
509 }
510
511 free(u->service_job);
512 u->service_job = job;
513
514 return r;
515 }
516
517 static int user_remove_runtime_path(User *u) {
518 int r;
519
520 assert(u);
521
522 if (!u->runtime_path)
523 return 0;
524
525 r = rm_rf(u->runtime_path, 0);
526 if (r < 0)
527 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
528
529 /* Ignore cases where the directory isn't mounted, as that's
530 * quite possible, if we lacked the permissions to mount
531 * something */
532 r = umount2(u->runtime_path, MNT_DETACH);
533 if (r < 0 && errno != EINVAL && errno != ENOENT)
534 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
535
536 r = rm_rf(u->runtime_path, REMOVE_ROOT);
537 if (r < 0)
538 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
539
540 free(u->runtime_path);
541 u->runtime_path = NULL;
542
543 return r;
544 }
545
546 int user_stop(User *u, bool force) {
547 Session *s;
548 int r = 0, k;
549 assert(u);
550
551 /* Stop jobs have already been queued */
552 if (u->stopping) {
553 user_save(u);
554 return r;
555 }
556
557 LIST_FOREACH(sessions_by_user, s, u->sessions) {
558 k = session_stop(s, force);
559 if (k < 0)
560 r = k;
561 }
562
563 /* Kill systemd */
564 k = user_stop_service(u);
565 if (k < 0)
566 r = k;
567
568 /* Kill cgroup */
569 k = user_stop_slice(u);
570 if (k < 0)
571 r = k;
572
573 u->stopping = true;
574
575 user_save(u);
576
577 return r;
578 }
579
580 int user_finalize(User *u) {
581 Session *s;
582 int r = 0, k;
583
584 assert(u);
585
586 if (u->started)
587 log_debug("User %s logged out.", u->name);
588
589 LIST_FOREACH(sessions_by_user, s, u->sessions) {
590 k = session_finalize(s);
591 if (k < 0)
592 r = k;
593 }
594
595 /* Kill XDG_RUNTIME_DIR */
596 k = user_remove_runtime_path(u);
597 if (k < 0)
598 r = k;
599
600 /* Clean SysV + POSIX IPC objects */
601 if (u->manager->remove_ipc) {
602 k = clean_ipc(u->uid);
603 if (k < 0)
604 r = k;
605 }
606
607 unlink(u->state_file);
608 user_add_to_gc_queue(u);
609
610 if (u->started) {
611 user_send_signal(u, false);
612 u->started = false;
613 }
614
615 return r;
616 }
617
618 int user_get_idle_hint(User *u, dual_timestamp *t) {
619 Session *s;
620 bool idle_hint = true;
621 dual_timestamp ts = { 0, 0 };
622
623 assert(u);
624
625 LIST_FOREACH(sessions_by_user, s, u->sessions) {
626 dual_timestamp k;
627 int ih;
628
629 ih = session_get_idle_hint(s, &k);
630 if (ih < 0)
631 return ih;
632
633 if (!ih) {
634 if (!idle_hint) {
635 if (k.monotonic < ts.monotonic)
636 ts = k;
637 } else {
638 idle_hint = false;
639 ts = k;
640 }
641 } else if (idle_hint) {
642
643 if (k.monotonic > ts.monotonic)
644 ts = k;
645 }
646 }
647
648 if (t)
649 *t = ts;
650
651 return idle_hint;
652 }
653
654 int user_check_linger_file(User *u) {
655 _cleanup_free_ char *cc = NULL;
656 char *p = NULL;
657
658 cc = cescape(u->name);
659 if (!cc)
660 return -ENOMEM;
661
662 p = strjoina("/var/lib/systemd/linger/", cc);
663
664 return access(p, F_OK) >= 0;
665 }
666
667 bool user_check_gc(User *u, bool drop_not_started) {
668 assert(u);
669
670 if (drop_not_started && !u->started)
671 return false;
672
673 if (u->sessions)
674 return true;
675
676 if (user_check_linger_file(u) > 0)
677 return true;
678
679 if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
680 return true;
681
682 if (u->service_job && manager_job_is_active(u->manager, u->service_job))
683 return true;
684
685 return false;
686 }
687
688 void user_add_to_gc_queue(User *u) {
689 assert(u);
690
691 if (u->in_gc_queue)
692 return;
693
694 LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
695 u->in_gc_queue = true;
696 }
697
698 UserState user_get_state(User *u) {
699 Session *i;
700
701 assert(u);
702
703 if (u->stopping)
704 return USER_CLOSING;
705
706 if (u->slice_job || u->service_job)
707 return USER_OPENING;
708
709 if (u->sessions) {
710 bool all_closing = true;
711
712 LIST_FOREACH(sessions_by_user, i, u->sessions) {
713 SessionState state;
714
715 state = session_get_state(i);
716 if (state == SESSION_ACTIVE)
717 return USER_ACTIVE;
718 if (state != SESSION_CLOSING)
719 all_closing = false;
720 }
721
722 return all_closing ? USER_CLOSING : USER_ONLINE;
723 }
724
725 if (user_check_linger_file(u) > 0)
726 return USER_LINGERING;
727
728 return USER_CLOSING;
729 }
730
731 int user_kill(User *u, int signo) {
732 assert(u);
733
734 if (!u->slice)
735 return -ESRCH;
736
737 return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
738 }
739
740 void user_elect_display(User *u) {
741 Session *graphical = NULL, *text = NULL, *other = NULL, *s;
742
743 assert(u);
744
745 /* This elects a primary session for each user, which we call
746 * the "display". We try to keep the assignment stable, but we
747 * "upgrade" to better choices. */
748
749 LIST_FOREACH(sessions_by_user, s, u->sessions) {
750
751 if (s->class != SESSION_USER)
752 continue;
753
754 if (s->stopping)
755 continue;
756
757 if (SESSION_TYPE_IS_GRAPHICAL(s->type))
758 graphical = s;
759 else if (s->type == SESSION_TTY)
760 text = s;
761 else
762 other = s;
763 }
764
765 if (graphical &&
766 (!u->display ||
767 u->display->class != SESSION_USER ||
768 u->display->stopping ||
769 !SESSION_TYPE_IS_GRAPHICAL(u->display->type))) {
770 u->display = graphical;
771 return;
772 }
773
774 if (text &&
775 (!u->display ||
776 u->display->class != SESSION_USER ||
777 u->display->stopping ||
778 u->display->type != SESSION_TTY)) {
779 u->display = text;
780 return;
781 }
782
783 if (other &&
784 (!u->display ||
785 u->display->class != SESSION_USER ||
786 u->display->stopping))
787 u->display = other;
788 }
789
790 static const char* const user_state_table[_USER_STATE_MAX] = {
791 [USER_OFFLINE] = "offline",
792 [USER_OPENING] = "opening",
793 [USER_LINGERING] = "lingering",
794 [USER_ONLINE] = "online",
795 [USER_ACTIVE] = "active",
796 [USER_CLOSING] = "closing"
797 };
798
799 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
800
801 int config_parse_tmpfs_size(
802 const char* unit,
803 const char *filename,
804 unsigned line,
805 const char *section,
806 unsigned section_line,
807 const char *lvalue,
808 int ltype,
809 const char *rvalue,
810 void *data,
811 void *userdata) {
812
813 size_t *sz = data;
814 const char *e;
815 int r;
816
817 assert(filename);
818 assert(lvalue);
819 assert(rvalue);
820 assert(data);
821
822 e = endswith(rvalue, "%");
823 if (e) {
824 unsigned long ul;
825 char *f;
826
827 errno = 0;
828 ul = strtoul(rvalue, &f, 10);
829 if (errno != 0 || f != e) {
830 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
831 return 0;
832 }
833
834 if (ul <= 0 || ul >= 100) {
835 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
836 return 0;
837 }
838
839 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
840 } else {
841 off_t o;
842
843 r = parse_size(rvalue, 1024, &o);
844 if (r < 0 || (off_t) (size_t) o != o) {
845 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
846 return 0;
847 }
848
849 *sz = PAGE_ALIGN((size_t) o);
850 }
851
852 return 0;
853 }