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