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