chiark / gitweb /
sd-bus: introduce new sd_bus_flush_close_unref() call
[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         /* Create cgroup */
463         r = user_start_slice(u);
464         if (r < 0)
465                 return r;
466
467         /* Save the user data so far, because pam_systemd will read the
468          * XDG_RUNTIME_DIR out of it while starting up systemd --user.
469          * We need to do user_save_internal() because we have not
470          * "officially" started yet. */
471         user_save_internal(u);
472
473         /* Spawn user systemd */
474         r = user_start_service(u);
475         if (r < 0)
476                 return r;
477
478         if (!dual_timestamp_is_set(&u->timestamp))
479                 dual_timestamp_get(&u->timestamp);
480
481         u->started = true;
482
483         /* Save new user data */
484         user_save(u);
485
486         user_send_signal(u, true);
487
488         return 0;
489 }
490
491 static int user_stop_slice(User *u) {
492         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
493         char *job;
494         int r;
495
496         assert(u);
497
498         if (!u->slice)
499                 return 0;
500
501         r = manager_stop_unit(u->manager, u->slice, &error, &job);
502         if (r < 0) {
503                 log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
504                 return r;
505         }
506
507         free(u->slice_job);
508         u->slice_job = job;
509
510         return r;
511 }
512
513 static int user_stop_service(User *u) {
514         _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
515         char *job;
516         int r;
517
518         assert(u);
519
520         if (!u->service)
521                 return 0;
522
523         r = manager_stop_unit(u->manager, u->service, &error, &job);
524         if (r < 0) {
525                 log_error("Failed to stop user service: %s", bus_error_message(&error, r));
526                 return r;
527         }
528
529         free(u->service_job);
530         u->service_job = job;
531
532         return r;
533 }
534
535 static int user_remove_runtime_path(User *u) {
536         int r;
537
538         assert(u);
539
540         if (!u->runtime_path)
541                 return 0;
542
543         r = rm_rf(u->runtime_path, 0);
544         if (r < 0)
545                 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
546
547         /* Ignore cases where the directory isn't mounted, as that's
548          * quite possible, if we lacked the permissions to mount
549          * something */
550         r = umount2(u->runtime_path, MNT_DETACH);
551         if (r < 0 && errno != EINVAL && errno != ENOENT)
552                 log_error_errno(errno, "Failed to unmount user runtime directory %s: %m", u->runtime_path);
553
554         r = rm_rf(u->runtime_path, REMOVE_ROOT);
555         if (r < 0)
556                 log_error_errno(r, "Failed to remove runtime directory %s: %m", u->runtime_path);
557
558         free(u->runtime_path);
559         u->runtime_path = NULL;
560
561         return r;
562 }
563
564 int user_stop(User *u, bool force) {
565         Session *s;
566         int r = 0, k;
567         assert(u);
568
569         /* Stop jobs have already been queued */
570         if (u->stopping) {
571                 user_save(u);
572                 return r;
573         }
574
575         LIST_FOREACH(sessions_by_user, s, u->sessions) {
576                 k = session_stop(s, force);
577                 if (k < 0)
578                         r = k;
579         }
580
581         /* Kill systemd */
582         k = user_stop_service(u);
583         if (k < 0)
584                 r = k;
585
586         /* Kill cgroup */
587         k = user_stop_slice(u);
588         if (k < 0)
589                 r = k;
590
591         u->stopping = true;
592
593         user_save(u);
594
595         return r;
596 }
597
598 int user_finalize(User *u) {
599         Session *s;
600         int r = 0, k;
601
602         assert(u);
603
604         if (u->started)
605                 log_debug("User %s logged out.", u->name);
606
607         LIST_FOREACH(sessions_by_user, s, u->sessions) {
608                 k = session_finalize(s);
609                 if (k < 0)
610                         r = k;
611         }
612
613         /* Kill XDG_RUNTIME_DIR */
614         k = user_remove_runtime_path(u);
615         if (k < 0)
616                 r = k;
617
618         /* Clean SysV + POSIX IPC objects */
619         if (u->manager->remove_ipc) {
620                 k = clean_ipc(u->uid);
621                 if (k < 0)
622                         r = k;
623         }
624
625         unlink(u->state_file);
626         user_add_to_gc_queue(u);
627
628         if (u->started) {
629                 user_send_signal(u, false);
630                 u->started = false;
631         }
632
633         return r;
634 }
635
636 int user_get_idle_hint(User *u, dual_timestamp *t) {
637         Session *s;
638         bool idle_hint = true;
639         dual_timestamp ts = DUAL_TIMESTAMP_NULL;
640
641         assert(u);
642
643         LIST_FOREACH(sessions_by_user, s, u->sessions) {
644                 dual_timestamp k;
645                 int ih;
646
647                 ih = session_get_idle_hint(s, &k);
648                 if (ih < 0)
649                         return ih;
650
651                 if (!ih) {
652                         if (!idle_hint) {
653                                 if (k.monotonic < ts.monotonic)
654                                         ts = k;
655                         } else {
656                                 idle_hint = false;
657                                 ts = k;
658                         }
659                 } else if (idle_hint) {
660
661                         if (k.monotonic > ts.monotonic)
662                                 ts = k;
663                 }
664         }
665
666         if (t)
667                 *t = ts;
668
669         return idle_hint;
670 }
671
672 int user_check_linger_file(User *u) {
673         _cleanup_free_ char *cc = NULL;
674         char *p = NULL;
675
676         cc = cescape(u->name);
677         if (!cc)
678                 return -ENOMEM;
679
680         p = strjoina("/var/lib/systemd/linger/", cc);
681
682         return access(p, F_OK) >= 0;
683 }
684
685 bool user_check_gc(User *u, bool drop_not_started) {
686         assert(u);
687
688         if (drop_not_started && !u->started)
689                 return false;
690
691         if (u->sessions)
692                 return true;
693
694         if (user_check_linger_file(u) > 0)
695                 return true;
696
697         if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
698                 return true;
699
700         if (u->service_job && manager_job_is_active(u->manager, u->service_job))
701                 return true;
702
703         return false;
704 }
705
706 void user_add_to_gc_queue(User *u) {
707         assert(u);
708
709         if (u->in_gc_queue)
710                 return;
711
712         LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
713         u->in_gc_queue = true;
714 }
715
716 UserState user_get_state(User *u) {
717         Session *i;
718
719         assert(u);
720
721         if (u->stopping)
722                 return USER_CLOSING;
723
724         if (!u->started || u->slice_job || u->service_job)
725                 return USER_OPENING;
726
727         if (u->sessions) {
728                 bool all_closing = true;
729
730                 LIST_FOREACH(sessions_by_user, i, u->sessions) {
731                         SessionState state;
732
733                         state = session_get_state(i);
734                         if (state == SESSION_ACTIVE)
735                                 return USER_ACTIVE;
736                         if (state != SESSION_CLOSING)
737                                 all_closing = false;
738                 }
739
740                 return all_closing ? USER_CLOSING : USER_ONLINE;
741         }
742
743         if (user_check_linger_file(u) > 0)
744                 return USER_LINGERING;
745
746         return USER_CLOSING;
747 }
748
749 int user_kill(User *u, int signo) {
750         assert(u);
751
752         if (!u->slice)
753                 return -ESRCH;
754
755         return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
756 }
757
758 static bool elect_display_filter(Session *s) {
759         /* Return true if the session is a candidate for the user’s ‘primary
760          * session’ or ‘display’. */
761         assert(s);
762
763         return (s->class == SESSION_USER && !s->stopping);
764 }
765
766 static int elect_display_compare(Session *s1, Session *s2) {
767         /* Indexed by SessionType. Lower numbers mean more preferred. */
768         const int type_ranks[_SESSION_TYPE_MAX] = {
769                 [SESSION_UNSPECIFIED] = 0,
770                 [SESSION_TTY] = -2,
771                 [SESSION_X11] = -3,
772                 [SESSION_WAYLAND] = -3,
773                 [SESSION_MIR] = -3,
774                 [SESSION_WEB] = -1,
775         };
776
777         /* Calculate the partial order relationship between s1 and s2,
778          * returning < 0 if s1 is preferred as the user’s ‘primary session’,
779          * 0 if s1 and s2 are equally preferred or incomparable, or > 0 if s2
780          * is preferred.
781          *
782          * s1 or s2 may be NULL. */
783         if (!s1 && !s2)
784                 return 0;
785
786         if ((s1 == NULL) != (s2 == NULL))
787                 return (s1 == NULL) - (s2 == NULL);
788
789         if (s1->stopping != s2->stopping)
790                 return s1->stopping - s2->stopping;
791
792         if ((s1->class != SESSION_USER) != (s2->class != SESSION_USER))
793                 return (s1->class != SESSION_USER) - (s2->class != SESSION_USER);
794
795         if ((s1->type == _SESSION_TYPE_INVALID) != (s2->type == _SESSION_TYPE_INVALID))
796                 return (s1->type == _SESSION_TYPE_INVALID) - (s2->type == _SESSION_TYPE_INVALID);
797
798         if (s1->type != s2->type)
799                 return type_ranks[s1->type] - type_ranks[s2->type];
800
801         return 0;
802 }
803
804 void user_elect_display(User *u) {
805         Session *s;
806
807         assert(u);
808
809         /* This elects a primary session for each user, which we call
810          * the "display". We try to keep the assignment stable, but we
811          * "upgrade" to better choices. */
812         log_debug("Electing new display for user %s", u->name);
813
814         LIST_FOREACH(sessions_by_user, s, u->sessions) {
815                 if (!elect_display_filter(s)) {
816                         log_debug("Ignoring session %s", s->id);
817                         continue;
818                 }
819
820                 if (elect_display_compare(s, u->display) < 0) {
821                         log_debug("Choosing session %s in preference to %s", s->id, u->display ? u->display->id : "-");
822                         u->display = s;
823                 }
824         }
825 }
826
827 static const char* const user_state_table[_USER_STATE_MAX] = {
828         [USER_OFFLINE] = "offline",
829         [USER_OPENING] = "opening",
830         [USER_LINGERING] = "lingering",
831         [USER_ONLINE] = "online",
832         [USER_ACTIVE] = "active",
833         [USER_CLOSING] = "closing"
834 };
835
836 DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
837
838 int config_parse_tmpfs_size(
839                 const char* unit,
840                 const char *filename,
841                 unsigned line,
842                 const char *section,
843                 unsigned section_line,
844                 const char *lvalue,
845                 int ltype,
846                 const char *rvalue,
847                 void *data,
848                 void *userdata) {
849
850         size_t *sz = data;
851         const char *e;
852         int r;
853
854         assert(filename);
855         assert(lvalue);
856         assert(rvalue);
857         assert(data);
858
859         e = endswith(rvalue, "%");
860         if (e) {
861                 unsigned long ul;
862                 char *f;
863
864                 errno = 0;
865                 ul = strtoul(rvalue, &f, 10);
866                 if (errno != 0 || f != e) {
867                         log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
868                         return 0;
869                 }
870
871                 if (ul <= 0 || ul >= 100) {
872                         log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
873                         return 0;
874                 }
875
876                 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
877         } else {
878                 off_t o;
879
880                 r = parse_size(rvalue, 1024, &o);
881                 if (r < 0 || (off_t) (size_t) o != o) {
882                         log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
883                         return 0;
884                 }
885
886                 *sz = PAGE_ALIGN((size_t) o);
887         }
888
889         return 0;
890 }