]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/login/logind-user.c
util: allow strappenda to take any number of args
[thirdparty/systemd.git] / src / login / logind-user.c
CommitLineData
20263082
LP
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
5430f7f2
LP
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
20263082
LP
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
5430f7f2 16 Lesser General Public License for more details.
20263082 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
20263082
LP
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20***/
21
1c231f56 22#include <sys/mount.h>
20263082
LP
23#include <string.h>
24#include <unistd.h>
25#include <errno.h>
26
20263082 27#include "util.h"
49e942b2 28#include "mkdir.h"
20263082
LP
29#include "hashmap.h"
30#include "strv.h"
a5c32cff 31#include "fileio.h"
1c231f56 32#include "path-util.h"
9444b1f2 33#include "special.h"
fb6becb4 34#include "unit-name.h"
cc377381
LP
35#include "bus-util.h"
36#include "bus-error.h"
1c231f56 37#include "conf-parser.h"
66cdd0f2 38#include "clean-ipc.h"
fb6becb4 39#include "logind-user.h"
20263082
LP
40
41User* user_new(Manager *m, uid_t uid, gid_t gid, const char *name) {
42 User *u;
43
44 assert(m);
45 assert(name);
46
14c3baca 47 u = new0(User, 1);
20263082
LP
48 if (!u)
49 return NULL;
50
51 u->name = strdup(name);
9444b1f2
LP
52 if (!u->name)
53 goto fail;
20263082 54
de0671ee 55 if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0)
9444b1f2 56 goto fail;
20263082 57
9444b1f2
LP
58 if (hashmap_put(m->users, ULONG_TO_PTR((unsigned long) uid), u) < 0)
59 goto fail;
20263082
LP
60
61 u->manager = m;
62 u->uid = uid;
63 u->gid = gid;
64
65 return u;
9444b1f2
LP
66
67fail:
68 free(u->state_file);
69 free(u->name);
70 free(u);
71
72 return NULL;
20263082
LP
73}
74
75void user_free(User *u) {
76 assert(u);
77
14c3baca 78 if (u->in_gc_queue)
71fda00f 79 LIST_REMOVE(gc_queue, u->manager->user_gc_queue, u);
14c3baca 80
20263082
LP
81 while (u->sessions)
82 session_free(u->sessions);
83
fb6becb4
LP
84 if (u->slice) {
85 hashmap_remove(u->manager->user_units, u->slice);
86 free(u->slice);
9444b1f2 87 }
20263082 88
fb6becb4
LP
89 if (u->service) {
90 hashmap_remove(u->manager->user_units, u->service);
91 free(u->service);
92 }
93
94 free(u->slice_job);
95 free(u->service_job);
96
20263082
LP
97 free(u->runtime_path);
98
99 hashmap_remove(u->manager->users, ULONG_TO_PTR((unsigned long) u->uid));
100
101 free(u->name);
d2f92cdf 102 free(u->state_file);
20263082
LP
103 free(u);
104}
105
106int user_save(User *u) {
9444b1f2
LP
107 _cleanup_free_ char *temp_path = NULL;
108 _cleanup_fclose_ FILE *f = NULL;
20263082
LP
109 int r;
110
111 assert(u);
112 assert(u->state_file);
113
accaeded
LP
114 if (!u->started)
115 return 0;
116
d2e54fae 117 r = mkdir_safe_label("/run/systemd/users", 0755, 0, 0);
20263082 118 if (r < 0)
14c3baca 119 goto finish;
20263082 120
14c3baca
LP
121 r = fopen_temporary(u->state_file, &f, &temp_path);
122 if (r < 0)
123 goto finish;
124
125 fchmod(fileno(f), 0644);
20263082
LP
126
127 fprintf(f,
14c3baca 128 "# This is private data. Do not parse.\n"
20263082
LP
129 "NAME=%s\n"
130 "STATE=%s\n",
131 u->name,
132 user_state_to_string(user_get_state(u)));
133
20263082 134 if (u->runtime_path)
9444b1f2 135 fprintf(f, "RUNTIME=%s\n", u->runtime_path);
20263082
LP
136
137 if (u->service)
9444b1f2 138 fprintf(f, "SERVICE=%s\n", u->service);
fb6becb4
LP
139 if (u->service_job)
140 fprintf(f, "SERVICE_JOB=%s\n", u->service_job);
9444b1f2
LP
141
142 if (u->slice)
143 fprintf(f, "SLICE=%s\n", u->slice);
fb6becb4
LP
144 if (u->slice_job)
145 fprintf(f, "SLICE_JOB=%s\n", u->slice_job);
20263082
LP
146
147 if (u->display)
9444b1f2
LP
148 fprintf(f, "DISPLAY=%s\n", u->display->id);
149
150 if (dual_timestamp_is_set(&u->timestamp))
20263082 151 fprintf(f,
90b2de37
ZJS
152 "REALTIME="USEC_FMT"\n"
153 "MONOTONIC="USEC_FMT"\n",
154 u->timestamp.realtime,
155 u->timestamp.monotonic);
20263082 156
034a2a52
LP
157 if (u->sessions) {
158 Session *i;
9b958eff 159 bool first;
034a2a52
LP
160
161 fputs("SESSIONS=", f);
9b958eff 162 first = true;
034a2a52 163 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
164 if (first)
165 first = false;
166 else
167 fputc(' ', f);
168
169 fputs(i->id, f);
034a2a52
LP
170 }
171
9b958eff
LP
172 fputs("\nSEATS=", f);
173 first = true;
034a2a52 174 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
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);
034a2a52
LP
184 }
185
9b958eff
LP
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 }
034a2a52 199
2dc8f41a
CG
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
9b958eff
LP
214 fputs("\nACTIVE_SEATS=", f);
215 first = true;
034a2a52 216 LIST_FOREACH(sessions_by_user, i, u->sessions) {
9b958eff
LP
217 if (!session_is_active(i) || !i->seat)
218 continue;
219
220 if (first)
221 first = false;
222 else
47acb2f1
CG
223 fputc(' ', f);
224
225 fputs(i->seat->id, f);
034a2a52 226 }
2dc8f41a
CG
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 }
9b958eff 241 fputc('\n', f);
034a2a52
LP
242 }
243
20263082 244 fflush(f);
14c3baca
LP
245
246 if (ferror(f) || rename(temp_path, u->state_file) < 0) {
20263082
LP
247 r = -errno;
248 unlink(u->state_file);
14c3baca 249 unlink(temp_path);
20263082
LP
250 }
251
14c3baca
LP
252finish:
253 if (r < 0)
90b2de37 254 log_error("Failed to save user data %s: %s", u->state_file, strerror(-r));
14c3baca 255
20263082
LP
256 return r;
257}
258
259int user_load(User *u) {
9444b1f2 260 _cleanup_free_ char *display = NULL, *realtime = NULL, *monotonic = NULL;
98a28fef 261 Session *s = NULL;
9444b1f2 262 int r;
20263082
LP
263
264 assert(u);
265
a185c5aa 266 r = parse_env_file(u->state_file, NEWLINE,
fb6becb4
LP
267 "RUNTIME", &u->runtime_path,
268 "SERVICE", &u->service,
269 "SERVICE_JOB", &u->service_job,
270 "SLICE", &u->slice,
271 "SLICE_JOB", &u->slice_job,
272 "DISPLAY", &display,
273 "REALTIME", &realtime,
274 "MONOTONIC", &monotonic,
20263082
LP
275 NULL);
276 if (r < 0) {
20263082
LP
277 if (r == -ENOENT)
278 return 0;
279
280 log_error("Failed to read %s: %s", u->state_file, strerror(-r));
281 return r;
282 }
283
9444b1f2 284 if (display)
98a28fef 285 s = hashmap_get(u->manager->sessions, display);
20263082 286
4d6d6518 287 if (s && s->display && display_is_local(s->display))
20263082
LP
288 u->display = s;
289
9444b1f2
LP
290 if (realtime) {
291 unsigned long long l;
292 if (sscanf(realtime, "%llu", &l) > 0)
293 u->timestamp.realtime = l;
294 }
295
296 if (monotonic) {
297 unsigned long long l;
298 if (sscanf(monotonic, "%llu", &l) > 0)
299 u->timestamp.monotonic = l;
300 }
301
20263082
LP
302 return r;
303}
304
305static int user_mkdir_runtime_path(User *u) {
306 char *p;
307 int r;
308
309 assert(u);
310
d2e54fae 311 r = mkdir_safe_label("/run/user", 0755, 0, 0);
20263082
LP
312 if (r < 0) {
313 log_error("Failed to create /run/user: %s", strerror(-r));
314 return r;
315 }
316
317 if (!u->runtime_path) {
1c231f56 318 if (asprintf(&p, "/run/user/" UID_FMT, u->uid) < 0)
0d0f0c50 319 return log_oom();
20263082
LP
320 } else
321 p = u->runtime_path;
322
1c231f56
LP
323 if (path_is_mount_point(p, false) <= 0) {
324 _cleanup_free_ char *t = NULL;
325
326 mkdir(p, 0700);
327
328 if (asprintf(&t, "mode=0700,uid=" UID_FMT ",gid=" GID_FMT ",size=%zu", u->uid, u->gid, u->manager->runtime_dir_size) < 0) {
329 r = log_oom();
330 goto fail;
331 }
332
333 r = mount("tmpfs", p, "tmpfs", MS_NODEV|MS_NOSUID, t);
334 if (r < 0) {
335 log_error("Failed to mount per-user tmpfs directory %s: %s", p, strerror(-r));
336 goto fail;
337 }
20263082
LP
338 }
339
340 u->runtime_path = p;
341 return 0;
1c231f56
LP
342
343fail:
344 free(p);
345 u->runtime_path = NULL;
346 return r;
20263082
LP
347}
348
fb6becb4 349static int user_start_slice(User *u) {
fb6becb4 350 char *job;
20263082
LP
351 int r;
352
353 assert(u);
354
fb6becb4 355 if (!u->slice) {
cc377381 356 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
de0671ee
ZJS
357 char lu[DECIMAL_STR_MAX(uid_t) + 1], *slice;
358 sprintf(lu, UID_FMT, u->uid);
ae018d9b 359
d0af76e6 360 r = build_subslice(SPECIAL_USER_SLICE, lu, &slice);
9444b1f2
LP
361 if (r < 0)
362 return r;
363
d0af76e6
LP
364 r = manager_start_unit(u->manager, slice, &error, &job);
365 if (r < 0) {
cc377381 366 log_error("Failed to start user slice: %s", bus_error_message(&error, r));
d0af76e6
LP
367 free(slice);
368 } else {
369 u->slice = slice;
370
371 free(u->slice_job);
372 u->slice_job = job;
373 }
20263082
LP
374 }
375
d0af76e6
LP
376 if (u->slice)
377 hashmap_put(u->manager->user_units, u->slice, u);
378
20263082
LP
379 return 0;
380}
381
382static int user_start_service(User *u) {
cc377381 383 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4
LP
384 char *job;
385 int r;
386
20263082
LP
387 assert(u);
388
fb6becb4 389 if (!u->service) {
de0671ee
ZJS
390 char lu[DECIMAL_STR_MAX(uid_t) + 1], *service;
391 sprintf(lu, UID_FMT, u->uid);
fb6becb4 392
d0af76e6
LP
393 service = unit_name_build("user", lu, ".service");
394 if (!service)
fb6becb4
LP
395 return log_oom();
396
d0af76e6
LP
397 r = manager_start_unit(u->manager, service, &error, &job);
398 if (r < 0) {
cc377381 399 log_error("Failed to start user service: %s", bus_error_message(&error, r));
d0af76e6
LP
400 free(service);
401 } else {
402 u->service = service;
403
404 free(u->service_job);
405 u->service_job = job;
406 }
fb6becb4
LP
407 }
408
d0af76e6
LP
409 if (u->service)
410 hashmap_put(u->manager->user_units, u->service, u);
411
20263082
LP
412 return 0;
413}
414
415int user_start(User *u) {
416 int r;
417
418 assert(u);
419
9418f147
LP
420 if (u->started)
421 return 0;
422
1637a8be 423 log_debug("New user %s logged in.", u->name);
ed18b08b 424
20263082
LP
425 /* Make XDG_RUNTIME_DIR */
426 r = user_mkdir_runtime_path(u);
427 if (r < 0)
428 return r;
429
430 /* Create cgroup */
fb6becb4 431 r = user_start_slice(u);
20263082
LP
432 if (r < 0)
433 return r;
434
435 /* Spawn user systemd */
436 r = user_start_service(u);
437 if (r < 0)
438 return r;
439
9444b1f2
LP
440 if (!dual_timestamp_is_set(&u->timestamp))
441 dual_timestamp_get(&u->timestamp);
20263082 442
9418f147
LP
443 u->started = true;
444
7f7bb946
LP
445 /* Save new user data */
446 user_save(u);
447
da119395
LP
448 user_send_signal(u, true);
449
20263082
LP
450 return 0;
451}
452
fb6becb4 453static int user_stop_slice(User *u) {
cc377381 454 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4
LP
455 char *job;
456 int r;
20263082 457
20263082
LP
458 assert(u);
459
fb6becb4
LP
460 if (!u->slice)
461 return 0;
462
463 r = manager_stop_unit(u->manager, u->slice, &error, &job);
464 if (r < 0) {
cc377381 465 log_error("Failed to stop user slice: %s", bus_error_message(&error, r));
fb6becb4
LP
466 return r;
467 }
ed18b08b 468
fb6becb4
LP
469 free(u->slice_job);
470 u->slice_job = job;
ed18b08b 471
fb6becb4 472 return r;
20263082
LP
473}
474
fb6becb4 475static int user_stop_service(User *u) {
cc377381 476 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
fb6becb4 477 char *job;
20263082 478 int r;
20263082
LP
479
480 assert(u);
481
fb6becb4 482 if (!u->service)
20263082
LP
483 return 0;
484
fb6becb4
LP
485 r = manager_stop_unit(u->manager, u->service, &error, &job);
486 if (r < 0) {
cc377381 487 log_error("Failed to stop user service: %s", bus_error_message(&error, r));
fb6becb4
LP
488 return r;
489 }
20263082 490
fb6becb4
LP
491 free(u->service_job);
492 u->service_job = job;
20263082 493
fb6becb4
LP
494 return r;
495}
20263082 496
20263082
LP
497static int user_remove_runtime_path(User *u) {
498 int r;
499
500 assert(u);
501
502 if (!u->runtime_path)
503 return 0;
504
1c231f56
LP
505 r = rm_rf(u->runtime_path, false, false, false);
506 if (r < 0)
507 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
508
509 if (umount2(u->runtime_path, MNT_DETACH) < 0)
510 log_error("Failed to unmount user runtime directory %s: %m", u->runtime_path);
511
ad293f5a 512 r = rm_rf(u->runtime_path, false, true, false);
20263082
LP
513 if (r < 0)
514 log_error("Failed to remove runtime directory %s: %s", u->runtime_path, strerror(-r));
515
516 free(u->runtime_path);
517 u->runtime_path = NULL;
518
519 return r;
520}
521
9bb69af4 522int user_stop(User *u, bool force) {
20263082
LP
523 Session *s;
524 int r = 0, k;
525 assert(u);
526
b58b227a
DH
527 /* Stop jobs have already been queued */
528 if (u->stopping) {
529 user_save(u);
530 return r;
531 }
532
20263082 533 LIST_FOREACH(sessions_by_user, s, u->sessions) {
9bb69af4 534 k = session_stop(s, force);
20263082
LP
535 if (k < 0)
536 r = k;
537 }
538
539 /* Kill systemd */
540 k = user_stop_service(u);
541 if (k < 0)
542 r = k;
543
544 /* Kill cgroup */
fb6becb4 545 k = user_stop_slice(u);
20263082
LP
546 if (k < 0)
547 r = k;
548
5f41d1f1
LP
549 u->stopping = true;
550
405e0255
LP
551 user_save(u);
552
553 return r;
554}
555
556int user_finalize(User *u) {
557 Session *s;
558 int r = 0, k;
559
560 assert(u);
561
562 if (u->started)
563 log_debug("User %s logged out.", u->name);
564
565 LIST_FOREACH(sessions_by_user, s, u->sessions) {
566 k = session_finalize(s);
567 if (k < 0)
568 r = k;
569 }
570
20263082
LP
571 /* Kill XDG_RUNTIME_DIR */
572 k = user_remove_runtime_path(u);
573 if (k < 0)
574 r = k;
575
66cdd0f2
LP
576 /* Clean SysV + POSIX IPC objects */
577 if (u->manager->remove_ipc) {
578 k = clean_ipc(u->uid);
579 if (k < 0)
580 r = k;
581 }
582
d2f92cdf
LP
583 unlink(u->state_file);
584 user_add_to_gc_queue(u);
585
405e0255 586 if (u->started) {
ed18b08b 587 user_send_signal(u, false);
405e0255
LP
588 u->started = false;
589 }
9418f147 590
20263082
LP
591 return r;
592}
593
a185c5aa
LP
594int user_get_idle_hint(User *u, dual_timestamp *t) {
595 Session *s;
596 bool idle_hint = true;
597 dual_timestamp ts = { 0, 0 };
598
599 assert(u);
600
601 LIST_FOREACH(sessions_by_user, s, u->sessions) {
602 dual_timestamp k;
603 int ih;
604
605 ih = session_get_idle_hint(s, &k);
606 if (ih < 0)
607 return ih;
608
609 if (!ih) {
610 if (!idle_hint) {
611 if (k.monotonic < ts.monotonic)
612 ts = k;
613 } else {
614 idle_hint = false;
615 ts = k;
616 }
617 } else if (idle_hint) {
618
619 if (k.monotonic > ts.monotonic)
620 ts = k;
621 }
622 }
623
624 if (t)
625 *t = ts;
626
627 return idle_hint;
628}
629
3a9f7a30 630int user_check_linger_file(User *u) {
cc377381
LP
631 _cleanup_free_ char *cc = NULL;
632 char *p = NULL;
e96cd586 633
cc377381
LP
634 cc = cescape(u->name);
635 if (!cc)
e96cd586
LP
636 return -ENOMEM;
637
cc377381 638 p = strappenda("/var/lib/systemd/linger/", cc);
e96cd586 639
cc377381 640 return access(p, F_OK) >= 0;
e96cd586
LP
641}
642
cc377381 643bool user_check_gc(User *u, bool drop_not_started) {
20263082
LP
644 assert(u);
645
4a4b033f 646 if (drop_not_started && !u->started)
cc377381 647 return false;
932e3ee7 648
20263082 649 if (u->sessions)
cc377381 650 return true;
20263082 651
e96cd586 652 if (user_check_linger_file(u) > 0)
cc377381 653 return true;
20263082 654
cc377381
LP
655 if (u->slice_job && manager_job_is_active(u->manager, u->slice_job))
656 return true;
657
658 if (u->service_job && manager_job_is_active(u->manager, u->service_job))
659 return true;
405e0255 660
cc377381 661 return false;
20263082
LP
662}
663
14c3baca
LP
664void user_add_to_gc_queue(User *u) {
665 assert(u);
666
667 if (u->in_gc_queue)
668 return;
669
71fda00f 670 LIST_PREPEND(gc_queue, u->manager->user_gc_queue, u);
14c3baca
LP
671 u->in_gc_queue = true;
672}
673
20263082
LP
674UserState user_get_state(User *u) {
675 Session *i;
676
677 assert(u);
678
5f41d1f1
LP
679 if (u->stopping)
680 return USER_CLOSING;
681
fb6becb4 682 if (u->slice_job || u->service_job)
405e0255 683 return USER_OPENING;
c9caad80 684
5f41d1f1
LP
685 if (u->sessions) {
686 bool all_closing = true;
687
688 LIST_FOREACH(sessions_by_user, i, u->sessions) {
00555a2e
DH
689 SessionState state;
690
691 state = session_get_state(i);
692 if (state == SESSION_ACTIVE)
5f41d1f1 693 return USER_ACTIVE;
00555a2e 694 if (state != SESSION_CLOSING)
5f41d1f1
LP
695 all_closing = false;
696 }
20263082 697
c9caad80 698 return all_closing ? USER_CLOSING : USER_ONLINE;
5f41d1f1 699 }
e96cd586
LP
700
701 if (user_check_linger_file(u) > 0)
702 return USER_LINGERING;
703
704 return USER_CLOSING;
20263082
LP
705}
706
de07ab16 707int user_kill(User *u, int signo) {
de07ab16
LP
708 assert(u);
709
fb6becb4 710 if (!u->slice)
de07ab16
LP
711 return -ESRCH;
712
fb6becb4 713 return manager_kill_unit(u->manager, u->slice, KILL_ALL, signo, NULL);
de07ab16
LP
714}
715
952d3260
LP
716void user_elect_display(User *u) {
717 Session *graphical = NULL, *text = NULL, *s;
718
719 assert(u);
720
721 /* This elects a primary session for each user, which we call
722 * the "display". We try to keep the assignment stable, but we
723 * "upgrade" to better choices. */
724
725 LIST_FOREACH(sessions_by_user, s, u->sessions) {
726
727 if (s->class != SESSION_USER)
728 continue;
729
730 if (s->stopping)
731 continue;
732
733 if (SESSION_TYPE_IS_GRAPHICAL(s->type))
734 graphical = s;
735 else
736 text = s;
737 }
738
739 if (graphical &&
740 (!u->display ||
741 u->display->class != SESSION_USER ||
742 u->display->stopping ||
743 !SESSION_TYPE_IS_GRAPHICAL(u->display->type)))
744 u->display = graphical;
745
746 if (text &&
747 (!u->display ||
748 u->display->class != SESSION_USER ||
749 u->display->stopping))
750 u->display = text;
751}
752
20263082
LP
753static const char* const user_state_table[_USER_STATE_MAX] = {
754 [USER_OFFLINE] = "offline",
fb6becb4 755 [USER_OPENING] = "opening",
20263082
LP
756 [USER_LINGERING] = "lingering",
757 [USER_ONLINE] = "online",
e96cd586
LP
758 [USER_ACTIVE] = "active",
759 [USER_CLOSING] = "closing"
20263082
LP
760};
761
762DEFINE_STRING_TABLE_LOOKUP(user_state, UserState);
1c231f56
LP
763
764int config_parse_tmpfs_size(
765 const char* unit,
766 const char *filename,
767 unsigned line,
768 const char *section,
769 unsigned section_line,
770 const char *lvalue,
771 int ltype,
772 const char *rvalue,
773 void *data,
774 void *userdata) {
775
776 size_t *sz = data;
777 const char *e;
778 int r;
779
780 assert(filename);
781 assert(lvalue);
782 assert(rvalue);
783 assert(data);
784
785 e = endswith(rvalue, "%");
786 if (e) {
787 unsigned long ul;
788 char *f;
789
790 errno = 0;
791 ul = strtoul(rvalue, &f, 10);
792 if (errno != 0 || f != e) {
793 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Failed to parse percentage value, ignoring: %s", rvalue);
794 return 0;
795 }
796
797 if (ul <= 0 || ul >= 100) {
798 log_syntax(unit, LOG_ERR, filename, line, errno ? errno : EINVAL, "Percentage value out of range, ignoring: %s", rvalue);
799 return 0;
800 }
801
802 *sz = PAGE_ALIGN((size_t) ((physical_memory() * (uint64_t) ul) / (uint64_t) 100));
803 } else {
804 off_t o;
805
806 r = parse_size(rvalue, 1024, &o);
807 if (r < 0 || (off_t) (size_t) o != o) {
808 log_syntax(unit, LOG_ERR, filename, line, r < 0 ? -r : ERANGE, "Failed to parse size value, ignoring: %s", rvalue);
809 return 0;
810 }
811
812 *sz = PAGE_ALIGN((size_t) o);
813 }
814
815 return 0;
816}