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