chiark / gitweb /
39fbb8d1b1638f57407c6cc69ea329d4a1260e00
[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 "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 finish;
120
121         r = fopen_temporary(u->state_file, &f, &temp_path);
122         if (r < 0)
123                 goto finish;
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         fflush(f);
245
246         if (ferror(f) || rename(temp_path, u->state_file) < 0) {
247                 r = -errno;
248                 unlink(u->state_file);
249                 unlink(temp_path);
250         }
251
252 finish:
253         if (r < 0)
254                 log_error_errno(r, "Failed to save user data %s: %m", u->state_file);
255
256         return r;
257 }
258
259 int user_save(User *u) {
260         assert(u);
261
262         if (!u->started)
263                 return 0;
264
265         return user_save_internal (u);
266 }
267
268 int user_load(User *u) {
269         _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
270         Session *s = NULL;
271         int r;
272
273         assert(u);
274
275         r = parse_env_file(u->state_file, NEWLINE,
276                            "RUNTIME",     &u->runtime_path,
277                            "SERVICE",     &u->service,
278                            "SERVICE_JOB", &u->service_job,
279                            "SLICE",       &u->slice,
280                            "SLICE_JOB",   &u->slice_job,
281                            "DISPLAY",     &display,
282                            "REALTIME",    &realtime,
283                            "MONOTONIC",   &monotonic,
284                            NULL);
285         if (r < 0) {
286                 if (r == -ENOENT)
287                         return 0;
288
289                 log_error_errno(r, "Failed to read %s: %m", u->state_file);
290                 return r;
291         }
292
293         if (display)
294                 s = hashmap_get(u->manager->sessions, display);
295
296         if (s && s->display && display_is_local(s->display))
297                 u->display = s;
298
299         if (realtime) {
300                 unsigned long long l;
301                 if (sscanf(realtime, "%llu", &l) > 0)
302                         u->timestamp.realtime = l;
303         }
304
305         if (monotonic) {
306                 unsigned long long l;
307                 if (sscanf(monotonic, "%llu", &l) > 0)
308                         u->timestamp.monotonic = l;
309         }
310
311         return r;
312 }
313
314 static int user_mkdir_runtime_path(User *u) {
315         char *p;
316         int r;
317
318         assert(u);
319
320         r = mkdir_safe_label("/run/user", 0755, 0, 0);
321         if (r < 0)
322                 return log_error_errno(r, "Failed to create /run/user: %m");
323
324         if (!u->runtime_path) {
325                 if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
326                         return log_oom();
327         } else
328                 p = u->runtime_path;
329
330         if (path_is_mount_point(p, 0) <= 0) {
331                 _cleanup_free_ char *t = NULL;
332
333                 (void) mkdir_label(p, 0700);
334
335                 if (mac_smack_use())
336                         r = asprintf(&t, "mode=0700,smackfsroot=*,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
337                 else
338                         r = asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size);
339                 if (r < 0) {
340                         r = log_oom();
341                         goto fail;
342                 }
343
344                 r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
345                 if (r < 0) {
346                         if (errno != EPERM) {
347                                 r = log_error_errno(errno, "Failed to mount per-user tmpfs directory %s: %m", p);
348                                 goto fail;
349                         }
350
351                         /* Lacking permissions, maybe
352                          * CAP_SYS_ADMIN-less container? In this case,
353                          * just use a normal directory. */
354
355                         r = chmod_and_chown(p, 0700, u->uid, u->gid);
356                         if (r < 0) {
357                                 log_error_errno(r, "Failed to change runtime directory ownership and mode: %m");
358                                 goto fail;
359                         }
360                 }
361
362                 r = label_fix(p, false, false);
363                 if (r < 0)
364                         log_warning_errno(r, "Failed to fix label of '%s', ignoring: %m", p);
365         }
366
367         u->runtime_path = p;
368         return 0;
369
370 fail:
371         if (p) {
372                 /* Try to clean up, but ignore errors */
373                 (void) rmdir(p);
374                 free(p);
375         }
376
377         u->runtime_path = NULL;
378         return r;
379 }
380
381 static int user_start_slice(User *u) {
382         char *job;
383         int r;
384
385         assert(u);
386
387         if (!u->slice) {
388                 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
389                 char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice;
390                 sprintf(lu, UID_FMT, u->uid);
391
392                 r = slice_build_subslice(SPECIAL_USER_SLICE, lu, &slice);
393                 if (r < 0)
394                         return r;
395
396                 r = manager_start_unit(u->manager, slice, &error, &job);
397                 if (r < 0) {
398                         log_error("Failed to start user slice: %s", bus_error_message(&error, r));
399                         free(slice);
400                 } else {
401                         u->slice = slice;
402
403                         free(u->slice_job);
404                         u->slice_job = job;
405                 }
406         }
407
408         if (u->slice)
409                 hashmap_put(u->manager->user_units, u->slice, u);
410
411         return 0;
412 }
413
414 static int user_start_service(User *u) {
415         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
416         char *job;
417         int r;
418
419         assert(u);
420
421         if (!u->service) {
422                 char lu[DECIMAL_STR_MAX(uid_t) + 1], *service;
423                 sprintf(lu, UID_FMT, u->uid);
424
425                 r = unit_name_build("user", lu, ".service", &service);
426                 if (r < 0)
427                         return log_error_errno(r, "Failed to build service name: %m");
428
429                 r = manager_start_unit(u->manager, service, &error, &job);
430                 if (r < 0) {
431                         log_error("Failed to start user service: %s", bus_error_message(&error, r));
432                         free(service);
433                 } else {
434                         u->service = service;
435
436                         free(u->service_job);
437                         u->service_job = job;
438                 }
439         }
440
441         if (u->service)
442                 hashmap_put(u->manager->user_units, u->service, u);
443
444         return 0;
445 }
446
447 int user_start(User *u) {
448         int r;
449
450         assert(u);
451
452         if (u->started)
453                 return 0;
454
455         log_debug("New user %s logged in.", u->name);
456
457         /* Make XDG_RUNTIME_DIR */
458         r = user_mkdir_runtime_path(u);
459         if (r < 0)
460                 return r;
461
462 #if 0
463         /* Create cgroup */
464         r = user_start_slice(u);
465         if (r < 0)
466                 return r;
467
468         /* Save the user data so far, because pam_systemd will read the
469          * XDG_RUNTIME_DIR out of it while starting up systemd --user.
470          * We need to do user_save_internal() because we have not
471          * "officially" started yet. */
472         user_save_internal(u);
473
474         /* Spawn user systemd */
475         r = user_start_service(u);
476         if (r < 0)
477                 return r;
478 #endif
479
480         if (!dual_timestamp_is_set(&u->timestamp))
481                 dual_timestamp_get(&u->timestamp);
482
483         u->started = true;
484
485         /* Save new user data */
486         user_save(u);
487
488         user_send_signal(u, true);
489
490         return 0;
491 }
492
493 static int user_stop_slice(User *u) {
494         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
495         char *job;
496         int r;
497
498         assert(u);
499
500         if (!u->slice)
501                 return 0;
502
503         r = manager_stop_unit(u->manager, u->slice, &error, &job);
504         if (r < 0) {
505                 log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
506                 return r;
507         }
508
509         free(u->slice_job);
510         u->slice_job = job;
511
512         return r;
513 }
514
515 static int user_stop_service(User *u) {
516         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
517         char *job;
518         int r;
519
520         assert(u);
521
522         if (!u->service)
523                 return 0;
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         if (!u->runtime_path)
543                 return 0;
544
545         r = rm_rf(u->runtime_path, 0);
546         if (r < 0)
547                 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
548
549         /* Ignore cases where the directory isn't mounted, as that's
550          * quite possible, if we lacked the permissions to mount
551          * something */
552         r = umount2(u->runtime_path, MNT_DETACH);
553         if (r < 0 && errno != EINVAL && errno != ENOENT)
554                 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
555
556         r = rm_rf(u->runtime_path, REMOVE_ROOT);
557         if (r < 0)
558                 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
559
560         free(u->runtime_path);
561         u->runtime_path = NULL;
562
563         return r;
564 }
565
566 int user_stop(User *u, bool force) {
567         Session *s;
568         int r = 0, k;
569         assert(u);
570
571         /* Stop jobs have already been queued */
572         if (u->stopping) {
573                 user_save(u);
574                 return r;
575         }
576
577         LIST_FOREACH(sessions_by_user, s, u->sessions) {
578                 k = session_stop(s, force);
579                 if (k < 0)
580                         r = k;
581         }
582
583         /* Kill systemd */
584         k = user_stop_service(u);
585         if (k < 0)
586                 r = k;
587
588         /* Kill cgroup */
589         k = user_stop_slice(u);
590         if (k < 0)
591                 r = k;
592
593         u->stopping = true;
594
595         user_save(u);
596
597         return r;
598 }
599
600 int user_finalize(User *u) {
601         Session *s;
602         int r = 0, k;
603
604         assert(u);
605
606         if (u->started)
607                 log_debug("User %s logged out.", u->name);
608
609         LIST_FOREACH(sessions_by_user, s, u->sessions) {
610                 k = session_finalize(s);
611                 if (k < 0)
612                         r = k;
613         }
614
615         /* Kill XDG_RUNTIME_DIR */
616         k = user_remove_runtime_path(u);
617         if (k < 0)
618                 r = k;
619
620         /* Clean SysV + POSIX IPC objects */
621         if (u->manager->remove_ipc) {
622                 k = clean_ipc(u->uid);
623                 if (k < 0)
624                         r = k;
625         }
626
627         unlink(u->state_file);
628         user_add_to_gc_queue(u);
629
630         if (u->started) {
631                 user_send_signal(u, false);
632                 u->started = false;
633         }
634
635         return r;
636 }
637
638 int user_get_idle_hint(User *u, dual_timestamp *t) {
639         Session *s;
640         bool idle_hint = true;
641         dual_timestamp ts = DUAL_TIMESTAMP_NULL;
642
643         assert(u);
644
645         LIST_FOREACH(sessions_by_user, s, u->sessions) {
646                 dual_timestamp k;
647                 int ih;
648
649                 ih = session_get_idle_hint(s, &k);
650                 if (ih < 0)
651                         return ih;
652
653                 if (!ih) {
654                         if (!idle_hint) {
655                                 if (k.monotonic < ts.monotonic)
656                                         ts = k;
657                         } else {
658                                 idle_hint = false;
659                                 ts = k;
660                         }
661                 } else if (idle_hint) {
662
663                         if (k.monotonic > ts.monotonic)
664                                 ts = k;
665                 }
666         }
667
668         if (t)
669                 *t = ts;
670
671         return idle_hint;
672 }
673
674 int user_check_linger_file(User *u) {
675         _cleanup_free_ char *cc = NULL;
676         char *p = NULL;
677
678         cc = cescape(u->name);
679         if (!cc)
680                 return -ENOMEM;
681
682         p = strjoina("/var/lib/systemd/linger/", cc);
683
684         return access(p, F_OK) >= 0;
685 }
686
687 bool user_check_gc(User *u, bool drop_not_started) {
688         assert(u);
689
690         if (drop_not_started && !u->started)
691                 return false;
692
693         if (u->sessions)
694                 return true;
695
696         if (user_check_linger_file(u) > 0)
697                 return true;
698
699         if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
700                 return true;
701
702         if (u->service_job && manager_job_is_active(u->manager, u->service_job))
703                 return true;
704
705         return false;
706 }
707
708 void user_add_to_gc_queue(User *u) {
709         assert(u);
710
711         if (u->in_gc_queue)
712                 return;
713
714         LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
715         u->in_gc_queue = true;
716 }
717
718 UserState user_get_state(User *u) {
719         Session *i;
720
721         assert(u);
722
723         if (u->stopping)
724                 return USER_CLOSING;
725
726         if (!u->started || u->slice_job || u->service_job)
727                 return USER_OPENING;
728
729         if (u->sessions) {
730                 bool all_closing = true;
731
732                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
733                         SessionState state;
734
735                         state = session_get_state(i);
736                         if (state == SESSION_ACTIVE)
737                                 return USER_ACTIVE;
738                         if (state != SESSION_CLOSING)
739                                 all_closing = false;
740                 }
741
742                 return all_closing ? USER_CLOSING : USER_ONLINE;
743         }
744
745         if (user_check_linger_file(u) > 0)
746                 return USER_LINGERING;
747
748         return USER_CLOSING;
749 }
750
751 int user_kill(User *u, int signo) {
752         assert(u);
753
754         if (!u->slice)
755                 return -ESRCH;
756
757         return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
758 }
759
760 static bool elect_display_filter(Session *s) {
761         /* Return true if the session is a candidate for the user’s ‘primary
762          * session’ or ‘display’. */
763         assert(s);
764
765         return (s->class == SESSION_USER && !s->stopping);
766 }
767
768 static int elect_display_compare(Session *s1, Session *s2) {
769         /* Indexed by SessionType. Lower numbers mean more preferred. */
770         const int type_ranks[_SESSION_TYPE_MAX] = {
771                 [SESSION_UNSPECIFIED] = 0,
772                 [SESSION_TTY] = -2,
773                 [SESSION_X11] = -3,
774                 [SESSION_WAYLAND] = -3,
775                 [SESSION_MIR] = -3,
776                 [SESSION_WEB] = -1,
777         };
778
779         /* Calculate the partial order relationship between s1 and s2,
780          * returning < 0 if s1 is preferred as the user’s ‘primary session’,
781          * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
782          * is preferred.
783          *
784          * s1 or s2 may be NULL. */
785         if (!s1 && !s2)
786                 return 0;
787
788         if ((s1 == NULL) != (s2 == NULL))
789                 return (s1 == NULL) - (s2 == NULL);
790
791         if (s1->stopping != s2->stopping)
792                 return s1->stopping - s2->stopping;
793
794         if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
795                 return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
796
797         if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
798                 return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
799
800         if (s1->type != s2->type)
801                 return type_ranks[s1->type] - type_ranks[s2->type];
802
803         return 0;
804 }
805
806 void user_elect_display(User *u) {
807         Session *s;
808
809         assert(u);
810
811         /* This elects a primary session for each user, which we call
812          * the "display". We try to keep the assignment stable, but we
813          * "upgrade" to better choices. */
814         log_debug("Electing new display for user %s", u->name);
815
816         LIST_FOREACH(sessions_by_user, s, u->sessions) {
817                 if (!elect_display_filter(s)) {
818                         log_debug("Ignoring session %s", s->id);
819                         continue;
820                 }
821
822                 if (elect_display_compare(s, u->display) < 0) {
823                         log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
824                         u->display = s;
825                 }
826         }
827 }
828
829 static const char* const user_state_table[_USER_STATE_MAX] = {
830         [USER_OFFLINE] = "offline",
831         [USER_OPENING] = "opening",
832         [USER_LINGERING] = "lingering",
833         [USER_ONLINE] = "online",
834         [USER_ACTIVE] = "active",
835         [USER_CLOSING] = "closing"
836 };
837
838 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
839
840 int config_parse_tmpfs_size(
841                 const char* unit,
842                 const char *filename,
843                 unsigned line,
844                 const char *section,
845                 unsigned section_line,
846                 const char *lvalue,
847                 int ltype,
848                 const char *rvalue,
849                 void *data,
850                 void *userdata) {
851
852         size_t *sz = data;
853         const char *e;
854         int r;
855
856         assert(filename);
857         assert(lvalue);
858         assert(rvalue);
859         assert(data);
860
861         e = endswith(rvalue, "%");
862         if (e) {
863                 unsigned long ul;
864                 char *f;
865
866                 errno = 0;
867                 ul = strtoul(rvalue, &f, 10);
868                 if (errno != 0 || f != e) {
869                         log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
870                         return 0;
871                 }
872
873                 if (ul <= 0 || ul >= 100) {
874                         log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
875                         return 0;
876                 }
877
878                 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
879         } else {
880                 off_t o;
881
882                 r = parse_size(rvalue, 1024, &o);
883                 if (r < 0 || (off_t) (size_t) o != o) {
884                         log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
885                         return 0;
886                 }
887
888                 *sz = PAGE_ALIGN((size_t) o);
889         }
890
891         return 0;
892 }