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