]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/login/logind-user.c
logind: simplify one conditional
[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, false);
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, false);
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("New user %s logged in.", 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(u->service_job);
531 u->service_job = job;
532
533 return r;
534 }
535
536 static int user_remove_runtime_path(User *u) {
537 int r;
538
539 assert(u);
540
541 r = rm_rf(u->runtime_path, 0);
542 if (r < 0)
543 log_error_errno(r, "Failed to remove runtime directory %s (before unmounting): %m", u->runtime_path);
544
545 /* Ignore cases where the directory isn't mounted, as that's
546 * quite possible, if we lacked the permissions to mount
547 * something */
548 r = umount2(u->runtime_path, MNT_DETACH);
549 if (r < 0 && !IN_SET(errno, EINVAL, ENOENT))
550 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
551
552 r = rm_rf(u->runtime_path, REMOVE_ROOT);
553 if (r < 0)
554 log_error_errno(r, "Failed to remove runtime directory %s (after unmounting): %m", u->runtime_path);
555
556 return r;
557 }
558
559 int user_stop(User *u, bool force) {
560 Session *s;
561 int r = 0, k;
562 assert(u);
563
564 /* Stop jobs have already been queued */
565 if (u->stopping) {
566 user_save(u);
567 return r;
568 }
569
570 LIST_FOREACH(sessions_by_user, s, u->sessions) {
571 k = session_stop(s, force);
572 if (k < 0)
573 r = k;
574 }
575
576 /* Kill systemd */
577 k = user_stop_service(u);
578 if (k < 0)
579 r = k;
580
581 /* Kill cgroup */
582 k = user_stop_slice(u);
583 if (k < 0)
584 r = k;
585
586 u->stopping = true;
587
588 user_save(u);
589
590 return r;
591 }
592
593 int user_finalize(User *u) {
594 Session *s;
595 int r = 0, k;
596
597 assert(u);
598
599 if (u->started)
600 log_debug("User %s logged out.", u->name);
601
602 LIST_FOREACH(sessions_by_user, s, u->sessions) {
603 k = session_finalize(s);
604 if (k < 0)
605 r = k;
606 }
607
608 /* Kill XDG_RUNTIME_DIR */
609 k = user_remove_runtime_path(u);
610 if (k < 0)
611 r = k;
612
613 /* Clean SysV + POSIX IPC objects, but only if this is not a system user. Background: in many setups cronjobs
614 * are run in full PAM and thus logind sessions, even if the code run doesn't belong to actual users but to
615 * system components. Since enable RemoveIPC= globally for all users, we need to be a bit careful with such
616 * cases, as we shouldn't accidentally remove a system service's IPC objects while it is running, just because
617 * a cronjob running as the same user just finished. Hence: exclude system users generally from IPC clean-up,
618 * and do it only for normal users. */
619 if (u->manager->remove_ipc && !uid_is_system(u->uid)) {
620 k = clean_ipc_by_uid(u->uid);
621 if (k < 0)
622 r = k;
623 }
624
625 unlink(u->state_file);
626 user_add_to_gc_queue(u);
627
628 if (u->started) {
629 user_send_signal(u, false);
630 u->started = false;
631 }
632
633 return r;
634 }
635
636 int user_get_idle_hint(User *u, dual_timestamp *t) {
637 Session *s;
638 bool idle_hint = true;
639 dual_timestamp ts = DUAL_TIMESTAMP_NULL;
640
641 assert(u);
642
643 LIST_FOREACH(sessions_by_user, s, u->sessions) {
644 dual_timestamp k;
645 int ih;
646
647 ih = session_get_idle_hint(s, &k);
648 if (ih < 0)
649 return ih;
650
651 if (!ih) {
652 if (!idle_hint) {
653 if (k.monotonic < ts.monotonic)
654 ts = k;
655 } else {
656 idle_hint = false;
657 ts = k;
658 }
659 } else if (idle_hint) {
660
661 if (k.monotonic > ts.monotonic)
662 ts = k;
663 }
664 }
665
666 if (t)
667 *t = ts;
668
669 return idle_hint;
670 }
671
672 int user_check_linger_file(User *u) {
673 _cleanup_free_ char *cc = NULL;
674 char *p = NULL;
675
676 cc = cescape(u->name);
677 if (!cc)
678 return -ENOMEM;
679
680 p = strjoina("/var/lib/systemd/linger/", cc);
681
682 return access(p, F_OK) >= 0;
683 }
684
685 bool user_check_gc(User *u, bool drop_not_started) {
686 assert(u);
687
688 if (drop_not_started && !u->started)
689 return false;
690
691 if (u->sessions)
692 return true;
693
694 if (user_check_linger_file(u) > 0)
695 return true;
696
697 if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
698 return true;
699
700 if (u->service_job && manager_job_is_active(u->manager, u->service_job))
701 return true;
702
703 return false;
704 }
705
706 void user_add_to_gc_queue(User *u) {
707 assert(u);
708
709 if (u->in_gc_queue)
710 return;
711
712 LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
713 u->in_gc_queue = true;
714 }
715
716 UserState user_get_state(User *u) {
717 Session *i;
718
719 assert(u);
720
721 if (u->stopping)
722 return USER_CLOSING;
723
724 if (!u->started || u->slice_job || u->service_job)
725 return USER_OPENING;
726
727 if (u->sessions) {
728 bool all_closing = true;
729
730 LIST_FOREACH(sessions_by_user, i, u->sessions) {
731 SessionState state;
732
733 state = session_get_state(i);
734 if (state == SESSION_ACTIVE)
735 return USER_ACTIVE;
736 if (state != SESSION_CLOSING)
737 all_closing = false;
738 }
739
740 return all_closing ? USER_CLOSING : USER_ONLINE;
741 }
742
743 if (user_check_linger_file(u) > 0)
744 return USER_LINGERING;
745
746 return USER_CLOSING;
747 }
748
749 int user_kill(User *u, int signo) {
750 assert(u);
751
752 return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
753 }
754
755 static bool elect_display_filter(Session *s) {
756 /* Return true if the session is a candidate for the user’s ‘primary
757 * session’ or ‘display’. */
758 assert(s);
759
760 return (s->class == SESSION_USER && !s->stopping);
761 }
762
763 static int elect_display_compare(Session *s1, Session *s2) {
764 /* Indexed by SessionType. Lower numbers mean more preferred. */
765 const int type_ranks[_SESSION_TYPE_MAX] = {
766 [SESSION_UNSPECIFIED] = 0,
767 [SESSION_TTY] = -2,
768 [SESSION_X11] = -3,
769 [SESSION_WAYLAND] = -3,
770 [SESSION_MIR] = -3,
771 [SESSION_WEB] = -1,
772 };
773
774 /* Calculate the partial order relationship between s1 and s2,
775 * returning < 0 if s1 is preferred as the user’s ‘primary session’,
776 * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
777 * is preferred.
778 *
779 * s1 or s2 may be NULL. */
780 if (!s1 && !s2)
781 return 0;
782
783 if ((s1 == NULL) != (s2 == NULL))
784 return (s1 == NULL) - (s2 == NULL);
785
786 if (s1->stopping != s2->stopping)
787 return s1->stopping - s2->stopping;
788
789 if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
790 return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
791
792 if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
793 return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
794
795 if (s1->type != s2->type)
796 return type_ranks[s1->type] - type_ranks[s2->type];
797
798 return 0;
799 }
800
801 void user_elect_display(User *u) {
802 Session *s;
803
804 assert(u);
805
806 /* This elects a primary session for each user, which we call
807 * the "display". We try to keep the assignment stable, but we
808 * "upgrade" to better choices. */
809 log_debug("Electing new display for user %s", u->name);
810
811 LIST_FOREACH(sessions_by_user, s, u->sessions) {
812 if (!elect_display_filter(s)) {
813 log_debug("Ignoring session %s", s->id);
814 continue;
815 }
816
817 if (elect_display_compare(s, u->display) < 0) {
818 log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
819 u->display = s;
820 }
821 }
822 }
823
824 static const char* const user_state_table[_USER_STATE_MAX] = {
825 [USER_OFFLINE] = "offline",
826 [USER_OPENING] = "opening",
827 [USER_LINGERING] = "lingering",
828 [USER_ONLINE] = "online",
829 [USER_ACTIVE] = "active",
830 [USER_CLOSING] = "closing"
831 };
832
833 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
834
835 int config_parse_tmpfs_size(
836 const char* unit,
837 const char *filename,
838 unsigned line,
839 const char *section,
840 unsigned section_line,
841 const char *lvalue,
842 int ltype,
843 const char *rvalue,
844 void *data,
845 void *userdata) {
846
847 size_t *sz = data;
848 int r;
849
850 assert(filename);
851 assert(lvalue);
852 assert(rvalue);
853 assert(data);
854
855 /* First, try to parse as percentage */
856 r = parse_percent(rvalue);
857 if (r > 0 && r < 100)
858 *sz = physical_memory_scale(r, 100U);
859 else {
860 uint64_t k;
861
862 /* If the passed argument was not a percentage, or out of range, parse as byte size */
863
864 r = parse_size(rvalue, 1024, &k);
865 if (r < 0 || k <= 0 || (uint64_t) (size_t) k != k) {
866 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse size value, ignoring: %s", rvalue);
867 return 0;
868 }
869
870 *sz = PAGE_ALIGN((size_t) k);
871 }
872
873 return 0;
874 }
875
876 int config_parse_user_tasks_max(
877 const char* unit,
878 const char *filename,
879 unsigned line,
880 const char *section,
881 unsigned section_line,
882 const char *lvalue,
883 int ltype,
884 const char *rvalue,
885 void *data,
886 void *userdata) {
887
888 uint64_t *m = data;
889 uint64_t k;
890 int r;
891
892 assert(filename);
893 assert(lvalue);
894 assert(rvalue);
895 assert(data);
896
897 if (isempty(rvalue)) {
898 *m = system_tasks_max_scale(DEFAULT_USER_TASKS_MAX_PERCENTAGE, 100U);
899 return 0;
900 }
901
902 if (streq(rvalue, "infinity")) {
903 *m = CGROUP_LIMIT_MAX;
904 return 0;
905 }
906
907 /* Try to parse as percentage */
908 r = parse_percent(rvalue);
909 if (r >= 0)
910 k = system_tasks_max_scale(r, 100U);
911 else {
912
913 /* If the passed argument was not a percentage, or out of range, parse as byte size */
914
915 r = safe_atou64(rvalue, &k);
916 if (r < 0) {
917 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse tasks maximum, ignoring: %s", rvalue);
918 return 0;
919 }
920 }
921
922 if (k <= 0 || k >= UINT64_MAX) {
923 log_syntax(unit, LOG_ERR, filename, line, 0, "Tasks maximum out of range, ignoring: %s", rvalue);
924 return 0;
925 }
926
927 *m = k;
928 return 0;
929 }