]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-user.c
Merge pull request #8554 from poettering/chase-trail-slash
[thirdparty/systemd.git] / src / login / logind-user.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2011 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <errno.h>
22 #include <string.h>
23 #include <sys/mount.h>
24 #include <unistd.h>
25 #include <stdio_ext.h>
26
27 #include "alloc-util.h"
28 #include "bus-common-errors.h"
29 #include "bus-error.h"
30 #include "bus-util.h"
31 #include "cgroup-util.h"
32 #include "clean-ipc.h"
33 #include "conf-parser.h"
34 #include "escape.h"
35 #include "fd-util.h"
36 #include "fileio.h"
37 #include "format-util.h"
38 #include "fs-util.h"
39 #include "hashmap.h"
40 #include "label.h"
41 #include "logind-user.h"
42 #include "mkdir.h"
43 #include "mount-util.h"
44 #include "parse-util.h"
45 #include "path-util.h"
46 #include "rm-rf.h"
47 #include "smack-util.h"
48 #include "special.h"
49 #include "stdio-util.h"
50 #include "string-table.h"
51 #include "unit-name.h"
52 #include "user-util.h"
53 #include "util.h"
54
55 int user_new(User **out, Manager *m, uid_t uid, gid_t gid, const char *name) {
56 _cleanup_(user_freep) User *u = NULL;
57 char lu[DECIMAL_STR_MAX(uid_t) + 1];
58 int r;
59
60 assert(out);
61 assert(m);
62 assert(name);
63
64 u = new0(User, 1);
65 if (!u)
66 return -ENOMEM;
67
68 u->manager = m;
69 u->uid = uid;
70 u->gid = gid;
71 xsprintf(lu, UID_FMT, uid);
72
73 u->name = strdup(name);
74 if (!u->name)
75 return -ENOMEM;
76
77 if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
78 return -ENOMEM;
79
80 if (asprintf(&u->runtime_path, "/run/user/"UID_FMT, uid) < 0)
81 return -ENOMEM;
82
83 r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &u->slice);
84 if (r < 0)
85 return r;
86
87 r = unit_name_build("user", lu, ".service", &u->service);
88 if (r < 0)
89 return r;
90
91 r = hashmap_put(m->users, UID_TO_PTR(uid), u);
92 if (r < 0)
93 return r;
94
95 r = hashmap_put(m->user_units, u->slice, u);
96 if (r < 0)
97 return r;
98
99 r = hashmap_put(m->user_units, u->service, u);
100 if (r < 0)
101 return r;
102
103 *out = u;
104 u = NULL;
105 return 0;
106 }
107
108 User *user_free(User *u) {
109 if (!u)
110 return NULL;
111
112 if (u->in_gc_queue)
113 LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u);
114
115 while (u->sessions)
116 session_free(u->sessions);
117
118 if (u->service)
119 hashmap_remove_value(u->manager->user_units, u->service, u);
120
121 if (u->slice)
122 hashmap_remove_value(u->manager->user_units, u->slice, u);
123
124 hashmap_remove_value(u->manager->users, UID_TO_PTR(u->uid), u);
125
126 u->slice_job = mfree(u->slice_job);
127 u->service_job = mfree(u->service_job);
128
129 u->service = mfree(u->service);
130 u->slice = mfree(u->slice);
131 u->runtime_path = mfree(u->runtime_path);
132 u->state_file = mfree(u->state_file);
133 u->name = mfree(u->name);
134
135 return mfree(u);
136 }
137
138 static int user_save_internal(User *u) {
139 _cleanup_free_ char *temp_path = NULL;
140 _cleanup_fclose_ FILE *f = NULL;
141 int r;
142
143 assert(u);
144 assert(u->state_file);
145
146 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0, MKDIR_WARN_MODE);
147 if (r < 0)
148 goto fail;
149
150 r = fopen_temporary(u->state_file, &f, &temp_path);
151 if (r < 0)
152 goto fail;
153
154 (void) __fsetlocking(f, FSETLOCKING_BYCALLER);
155 (void) fchmod(fileno(f), 0644);
156
157 fprintf(f,
158 "# This is private data. Do not parse.\n"
159 "NAME=%s\n"
160 "STATE=%s\n",
161 u->name,
162 user_state_to_string(user_get_state(u)));
163
164 /* LEGACY: no-one reads RUNTIME= anymore, drop it at some point */
165 if (u->runtime_path)
166 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
167
168 if (u->service_job)
169 fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
170
171 if (u->slice_job)
172 fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
173
174 if (u->display)
175 fprintf(f, "DISPLAY=%s\n", u->display->id);
176
177 if (dual_timestamp_is_set(&u->timestamp))
178 fprintf(f,
179 "REALTIME="USEC_FMT"\n"
180 "MONOTONIC="USEC_FMT"\n",
181 u->timestamp.realtime,
182 u->timestamp.monotonic);
183
184 if (u->sessions) {
185 Session *i;
186 bool first;
187
188 fputs("SESSIONS=", f);
189 first = true;
190 LIST_FOREACH(sessions_by_user, i, u->sessions) {
191 if (first)
192 first = false;
193 else
194 fputc(' ', f);
195
196 fputs(i->id, f);
197 }
198
199 fputs("\nSEATS=", f);
200 first = true;
201 LIST_FOREACH(sessions_by_user, i, u->sessions) {
202 if (!i->seat)
203 continue;
204
205 if (first)
206 first = false;
207 else
208 fputc(' ', f);
209
210 fputs(i->seat->id, f);
211 }
212
213 fputs("\nACTIVE_SESSIONS=", f);
214 first = true;
215 LIST_FOREACH(sessions_by_user, i, u->sessions) {
216 if (!session_is_active(i))
217 continue;
218
219 if (first)
220 first = false;
221 else
222 fputc(' ', f);
223
224 fputs(i->id, f);
225 }
226
227 fputs("\nONLINE_SESSIONS=", f);
228 first = true;
229 LIST_FOREACH(sessions_by_user, i, u->sessions) {
230 if (session_get_state(i) == SESSION_CLOSING)
231 continue;
232
233 if (first)
234 first = false;
235 else
236 fputc(' ', f);
237
238 fputs(i->id, f);
239 }
240
241 fputs("\nACTIVE_SEATS=", f);
242 first = true;
243 LIST_FOREACH(sessions_by_user, i, u->sessions) {
244 if (!session_is_active(i) || !i->seat)
245 continue;
246
247 if (first)
248 first = false;
249 else
250 fputc(' ', f);
251
252 fputs(i->seat->id, f);
253 }
254
255 fputs("\nONLINE_SEATS=", f);
256 first = true;
257 LIST_FOREACH(sessions_by_user, i, u->sessions) {
258 if (session_get_state(i) == SESSION_CLOSING || !i->seat)
259 continue;
260
261 if (first)
262 first = false;
263 else
264 fputc(' ', f);
265
266 fputs(i->seat->id, f);
267 }
268 fputc('\n', f);
269 }
270
271 r = fflush_and_check(f);
272 if (r < 0)
273 goto fail;
274
275 if (rename(temp_path, u->state_file) < 0) {
276 r = -errno;
277 goto fail;
278 }
279
280 return 0;
281
282 fail:
283 (void) unlink(u->state_file);
284
285 if (temp_path)
286 (void) unlink(temp_path);
287
288 return log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
289 }
290
291 int user_save(User *u) {
292 assert(u);
293
294 if (!u->started)
295 return 0;
296
297 return user_save_internal (u);
298 }
299
300 int user_load(User *u) {
301 _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
302 Session *s = NULL;
303 int r;
304
305 assert(u);
306
307 r = parse_env_file(u->state_file, NEWLINE,
308 "SERVICE_JOB", &u->service_job,
309 "SLICE_JOB", &u->slice_job,
310 "DISPLAY", &display,
311 "REALTIME", &realtime,
312 "MONOTONIC", &monotonic,
313 NULL);
314 if (r < 0) {
315 if (r == -ENOENT)
316 return 0;
317
318 return log_error_errno(r, "Failed to read %s: %m", u->state_file);
319 }
320
321 if (display)
322 s = hashmap_get(u->manager->sessions, display);
323
324 if (s && s->display && display_is_local(s->display))
325 u->display = s;
326
327 if (realtime)
328 timestamp_deserialize(realtime, &u->timestamp.realtime);
329 if (monotonic)
330 timestamp_deserialize(monotonic, &u->timestamp.monotonic);
331
332 return r;
333 }
334
335 static int user_mkdir_runtime_path(User *u) {
336 int r;
337
338 assert(u);
339
340 r = mkdir_safe_label("/run/user", 0755, 0, 0, MKDIR_WARN_MODE);
341 if (r < 0)
342 return log_error_errno(r, "Failed to create /run/user: %m");
343
344 if (path_is_mount_point(u->runtime_path, NULL, 0) <= 0) {
345 _cleanup_free_ char *t = NULL;
346
347 r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu%s",
348 u->uid, u->gid, u->manager->runtime_dir_size,
349 mac_smack_use() ? ",smackfsroot=*" : "");
350 if (r < 0)
351 return log_oom();
352
353 (void) mkdir_label(u->runtime_path, 0700);
354
355 r = mount("tmpfs", u->runtime_path, "tmpfs", MS_NODEV|MS_NOSUID, t);
356 if (r < 0) {
357 if (!IN_SET(errno, EPERM, EACCES)) {
358 r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", u->runtime_path);
359 goto fail;
360 }
361
362 log_debug_errno(errno, "Failed to mount per-user tmpfs directory %s, assuming containerized execution, ignoring: %m", u->runtime_path);
363
364 r = chmod_and_chown(u->runtime_path, 0700, u->uid, u->gid);
365 if (r < 0) {
366 log_error_errno(r, "Failed to change runtime directory ownership and mode: %m");
367 goto fail;
368 }
369 }
370
371 r = label_fix(u->runtime_path, false, false);
372 if (r < 0)
373 log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", u->runtime_path);
374 }
375
376 return 0;
377
378 fail:
379 /* Try to clean up, but ignore errors */
380 (void) rmdir(u->runtime_path);
381 return r;
382 }
383
384 static int user_start_slice(User *u) {
385 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
386 const char *description;
387 char *job;
388 int r;
389
390 assert(u);
391
392 u->slice_job = mfree(u->slice_job);
393 description = strjoina("User Slice of ", u->name);
394
395 r = manager_start_slice(
396 u->manager,
397 u->slice,
398 description,
399 "systemd-logind.service",
400 "systemd-user-sessions.service",
401 u->manager->user_tasks_max,
402 &error,
403 &job);
404 if (r >= 0)
405 u->slice_job = job;
406 else if (!sd_bus_error_has_name(&error, BUS_ERROR_UNIT_EXISTS))
407 /* we don't fail due to this, let's try to continue */
408 log_error_errno(r, "Failed to start user slice %s, ignoring: %s (%s)",
409 u->slice, bus_error_message(&error, r), error.name);
410
411 return 0;
412 }
413
414 static int user_start_service(User *u) {
415 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
416 char *job;
417 int r;
418
419 assert(u);
420
421 u->service_job = mfree(u->service_job);
422
423 r = manager_start_unit(
424 u->manager,
425 u->service,
426 &error,
427 &job);
428 if (r < 0)
429 /* we don't fail due to this, let's try to continue */
430 log_error_errno(r, "Failed to start user service, ignoring: %s", bus_error_message(&error, r));
431 else
432 u->service_job = job;
433
434 return 0;
435 }
436
437 int user_start(User *u) {
438 int r;
439
440 assert(u);
441
442 if (u->started && !u->stopping)
443 return 0;
444
445 /*
446 * If u->stopping is set, the user is marked for removal and the slice
447 * and service stop-jobs are queued. We have to clear that flag before
448 * queing the start-jobs again. If they succeed, the user object can be
449 * re-used just fine (pid1 takes care of job-ordering and proper
450 * restart), but if they fail, we want to force another user_stop() so
451 * possibly pending units are stopped.
452 * Note that we don't clear u->started, as we have no clue what state
453 * the user is in on failure here. Hence, we pretend the user is
454 * running so it will be properly taken down by GC. However, we clearly
455 * return an error from user_start() in that case, so no further
456 * reference to the user is taken.
457 */
458 u->stopping = false;
459
460 if (!u->started) {
461 log_debug("Starting services for new user %s.", u->name);
462
463 /* Make XDG_RUNTIME_DIR */
464 r = user_mkdir_runtime_path(u);
465 if (r < 0)
466 return r;
467 }
468
469 /* Create cgroup */
470 r = user_start_slice(u);
471 if (r < 0)
472 return r;
473
474 /* Save the user data so far, because pam_systemd will read the
475 * XDG_RUNTIME_DIR out of it while starting up systemd --user.
476 * We need to do user_save_internal() because we have not
477 * "officially" started yet. */
478 user_save_internal(u);
479
480 /* Spawn user systemd */
481 r = user_start_service(u);
482 if (r < 0)
483 return r;
484
485 if (!u->started) {
486 if (!dual_timestamp_is_set(&u->timestamp))
487 dual_timestamp_get(&u->timestamp);
488 user_send_signal(u, true);
489 u->started = true;
490 }
491
492 /* Save new user data */
493 user_save(u);
494
495 return 0;
496 }
497
498 static int user_stop_slice(User *u) {
499 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
500 char *job;
501 int r;
502
503 assert(u);
504
505 r = manager_stop_unit(u->manager, u->slice, &error, &job);
506 if (r < 0) {
507 log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
508 return r;
509 }
510
511 free(u->slice_job);
512 u->slice_job = job;
513
514 return r;
515 }
516
517 static int user_stop_service(User *u) {
518 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
519 char *job;
520 int r;
521
522 assert(u);
523
524 r = manager_stop_unit(u->manager, u->service, &error, &job);
525 if (r < 0) {
526 log_error("Failed to stop user service: %s", bus_error_message(&error, r));
527 return r;
528 }
529
530 free_and_replace(u->service_job, job);
531 return r;
532 }
533
534 static int user_remove_runtime_path(User *u) {
535 int r;
536
537 assert(u);
538
539 r = rm_rf(u->runtime_path, 0);
540 if (r < 0)
541 log_error_errno(r, "Failed to remove runtime directory %s (before unmounting): %m", u->runtime_path);
542
543 /* Ignore cases where the directory isn't mounted, as that's
544 * quite possible, if we lacked the permissions to mount
545 * something */
546 r = umount2(u->runtime_path, MNT_DETACH);
547 if (r < 0 && !IN_SET(errno, EINVAL, ENOENT))
548 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
549
550 r = rm_rf(u->runtime_path, REMOVE_ROOT);
551 if (r < 0)
552 log_error_errno(r, "Failed to remove runtime directory %s (after unmounting): %m", u->runtime_path);
553
554 return r;
555 }
556
557 int user_stop(User *u, bool force) {
558 Session *s;
559 int r = 0, k;
560 assert(u);
561
562 /* Stop jobs have already been queued */
563 if (u->stopping) {
564 user_save(u);
565 return r;
566 }
567
568 LIST_FOREACH(sessions_by_user, s, u->sessions) {
569 k = session_stop(s, force);
570 if (k < 0)
571 r = k;
572 }
573
574 /* Kill systemd */
575 k = user_stop_service(u);
576 if (k < 0)
577 r = k;
578
579 /* Kill cgroup */
580 k = user_stop_slice(u);
581 if (k < 0)
582 r = k;
583
584 u->stopping = true;
585
586 user_save(u);
587
588 return r;
589 }
590
591 int user_finalize(User *u) {
592 Session *s;
593 int r = 0, k;
594
595 assert(u);
596
597 if (u->started)
598 log_debug("User %s logged out.", u->name);
599
600 LIST_FOREACH(sessions_by_user, s, u->sessions) {
601 k = session_finalize(s);
602 if (k < 0)
603 r = k;
604 }
605
606 /* Kill XDG_RUNTIME_DIR */
607 k = user_remove_runtime_path(u);
608 if (k < 0)
609 r = k;
610
611 /* Clean SysV + POSIX IPC objects, but only if this is not a system user. Background: in many setups cronjobs
612 * are run in full PAM and thus logind sessions, even if the code run doesn't belong to actual users but to
613 * system components. Since enable RemoveIPC= globally for all users, we need to be a bit careful with such
614 * cases, as we shouldn't accidentally remove a system service's IPC objects while it is running, just because
615 * a cronjob running as the same user just finished. Hence: exclude system users generally from IPC clean-up,
616 * and do it only for normal users. */
617 if (u->manager->remove_ipc && !uid_is_system(u->uid)) {
618 k = clean_ipc_by_uid(u->uid);
619 if (k < 0)
620 r = k;
621 }
622
623 unlink(u->state_file);
624 user_add_to_gc_queue(u);
625
626 if (u->started) {
627 user_send_signal(u, false);
628 u->started = false;
629 }
630
631 return r;
632 }
633
634 int user_get_idle_hint(User *u, dual_timestamp *t) {
635 Session *s;
636 bool idle_hint = true;
637 dual_timestamp ts = DUAL_TIMESTAMP_NULL;
638
639 assert(u);
640
641 LIST_FOREACH(sessions_by_user, s, u->sessions) {
642 dual_timestamp k;
643 int ih;
644
645 ih = session_get_idle_hint(s, &k);
646 if (ih < 0)
647 return ih;
648
649 if (!ih) {
650 if (!idle_hint) {
651 if (k.monotonic < ts.monotonic)
652 ts = k;
653 } else {
654 idle_hint = false;
655 ts = k;
656 }
657 } else if (idle_hint) {
658
659 if (k.monotonic > ts.monotonic)
660 ts = k;
661 }
662 }
663
664 if (t)
665 *t = ts;
666
667 return idle_hint;
668 }
669
670 int user_check_linger_file(User *u) {
671 _cleanup_free_ char *cc = NULL;
672 char *p = NULL;
673
674 cc = cescape(u->name);
675 if (!cc)
676 return -ENOMEM;
677
678 p = strjoina("/var/lib/systemd/linger/", cc);
679
680 return access(p, F_OK) >= 0;
681 }
682
683 bool user_may_gc(User *u, bool drop_not_started) {
684 assert(u);
685
686 if (drop_not_started && !u->started)
687 return true;
688
689 if (u->sessions)
690 return false;
691
692 if (user_check_linger_file(u) > 0)
693 return false;
694
695 if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
696 return false;
697
698 if (u->service_job && manager_job_is_active(u->manager, u->service_job))
699 return false;
700
701 return true;
702 }
703
704 void user_add_to_gc_queue(User *u) {
705 assert(u);
706
707 if (u->in_gc_queue)
708 return;
709
710 LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
711 u->in_gc_queue = true;
712 }
713
714 UserState user_get_state(User *u) {
715 Session *i;
716
717 assert(u);
718
719 if (u->stopping)
720 return USER_CLOSING;
721
722 if (!u->started || u->slice_job || u->service_job)
723 return USER_OPENING;
724
725 if (u->sessions) {
726 bool all_closing = true;
727
728 LIST_FOREACH(sessions_by_user, i, u->sessions) {
729 SessionState state;
730
731 state = session_get_state(i);
732 if (state == SESSION_ACTIVE)
733 return USER_ACTIVE;
734 if (state != SESSION_CLOSING)
735 all_closing = false;
736 }
737
738 return all_closing ? USER_CLOSING : USER_ONLINE;
739 }
740
741 if (user_check_linger_file(u) > 0)
742 return USER_LINGERING;
743
744 return USER_CLOSING;
745 }
746
747 int user_kill(User *u, int signo) {
748 assert(u);
749
750 return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
751 }
752
753 static bool elect_display_filter(Session *s) {
754 /* Return true if the session is a candidate for the user’s ‘primary
755 * session’ or ‘display’. */
756 assert(s);
757
758 return (s->class == SESSION_USER && !s->stopping);
759 }
760
761 static int elect_display_compare(Session *s1, Session *s2) {
762 /* Indexed by SessionType. Lower numbers mean more preferred. */
763 const int type_ranks[_SESSION_TYPE_MAX] = {
764 [SESSION_UNSPECIFIED] = 0,
765 [SESSION_TTY] = -2,
766 [SESSION_X11] = -3,
767 [SESSION_WAYLAND] = -3,
768 [SESSION_MIR] = -3,
769 [SESSION_WEB] = -1,
770 };
771
772 /* Calculate the partial order relationship between s1 and s2,
773 * returning < 0 if s1 is preferred as the user’s ‘primary session’,
774 * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
775 * is preferred.
776 *
777 * s1 or s2 may be NULL. */
778 if (!s1 && !s2)
779 return 0;
780
781 if ((s1 == NULL) != (s2 == NULL))
782 return (s1 == NULL) - (s2 == NULL);
783
784 if (s1->stopping != s2->stopping)
785 return s1->stopping - s2->stopping;
786
787 if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
788 return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
789
790 if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
791 return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
792
793 if (s1->type != s2->type)
794 return type_ranks[s1->type] - type_ranks[s2->type];
795
796 return 0;
797 }
798
799 void user_elect_display(User *u) {
800 Session *s;
801
802 assert(u);
803
804 /* This elects a primary session for each user, which we call
805 * the "display". We try to keep the assignment stable, but we
806 * "upgrade" to better choices. */
807 log_debug("Electing new display for user %s", u->name);
808
809 LIST_FOREACH(sessions_by_user, s, u->sessions) {
810 if (!elect_display_filter(s)) {
811 log_debug("Ignoring session %s", s->id);
812 continue;
813 }
814
815 if (elect_display_compare(s, u->display) < 0) {
816 log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
817 u->display = s;
818 }
819 }
820 }
821
822 static const char* const user_state_table[_USER_STATE_MAX] = {
823 [USER_OFFLINE] = "offline",
824 [USER_OPENING] = "opening",
825 [USER_LINGERING] = "lingering",
826 [USER_ONLINE] = "online",
827 [USER_ACTIVE] = "active",
828 [USER_CLOSING] = "closing"
829 };
830
831 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
832
833 int config_parse_tmpfs_size(
834 const char* unit,
835 const char *filename,
836 unsigned line,
837 const char *section,
838 unsigned section_line,
839 const char *lvalue,
840 int ltype,
841 const char *rvalue,
842 void *data,
843 void *userdata) {
844
845 size_t *sz = data;
846 int r;
847
848 assert(filename);
849 assert(lvalue);
850 assert(rvalue);
851 assert(data);
852
853 /* First, try to parse as percentage */
854 r = parse_percent(rvalue);
855 if (r > 0 && r < 100)
856 *sz = physical_memory_scale(r, 100U);
857 else {
858 uint64_t k;
859
860 /* If the passed argument was not a percentage, or out of range, parse as byte size */
861
862 r = parse_size(rvalue, 1024, &k);
863 if (r < 0 || k <= 0 || (uint64_t) (size_t) k != k) {
864 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
865 return 0;
866 }
867
868 *sz = PAGE_ALIGN((size_t) k);
869 }
870
871 return 0;
872 }
873
874 int config_parse_user_tasks_max(
875 const char* unit,
876 const char *filename,
877 unsigned line,
878 const char *section,
879 unsigned section_line,
880 const char *lvalue,
881 int ltype,
882 const char *rvalue,
883 void *data,
884 void *userdata) {
885
886 uint64_t *m = data;
887 uint64_t k;
888 int r;
889
890 assert(filename);
891 assert(lvalue);
892 assert(rvalue);
893 assert(data);
894
895 if (isempty(rvalue)) {
896 *m = system_tasks_max_scale(DEFAULT_USER_TASKS_MAX_PERCENTAGE, 100U);
897 return 0;
898 }
899
900 if (streq(rvalue, "infinity")) {
901 *m = CGROUP_LIMIT_MAX;
902 return 0;
903 }
904
905 /* Try to parse as percentage */
906 r = parse_percent(rvalue);
907 if (r >= 0)
908 k = system_tasks_max_scale(r, 100U);
909 else {
910
911 /* If the passed argument was not a percentage, or out of range, parse as byte size */
912
913 r = safe_atou64(rvalue, &k);
914 if (r < 0) {
915 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse tasks maximum, ignoring: %s", rvalue);
916 return 0;
917 }
918 }
919
920 if (k <= 0 || k >= UINT64_MAX) {
921 log_syntax(unit, LOG_ERR, filename, line, 0, "Tasks maximum out of range, ignoring: %s", rvalue);
922 return 0;
923 }
924
925 *m = k;
926 return 0;
927 }