]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/shared/machine-pool.c
tree-wide: make use of wait_for_terminate_and_check() at various places
[thirdparty/systemd.git] / src / shared / machine-pool.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
432cea00
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2015 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
a8fbdf54
TA
21#include <errno.h>
22#include <fcntl.h>
23#include <linux/loop.h>
24#include <signal.h>
25#include <stdbool.h>
26#include <stdio.h>
27#include <stdlib.h>
e306723e 28#include <sys/file.h>
a8fbdf54 29#include <sys/ioctl.h>
07630cea 30#include <sys/mount.h>
432cea00 31#include <sys/prctl.h>
a8fbdf54
TA
32#include <sys/stat.h>
33#include <sys/statfs.h>
432cea00 34#include <sys/statvfs.h>
a8fbdf54
TA
35#include <unistd.h>
36
37#include "sd-bus-protocol.h"
38#include "sd-bus.h"
432cea00 39
b5efdb8a 40#include "alloc-util.h"
07630cea 41#include "btrfs-util.h"
3ffd4af2 42#include "fd-util.h"
0d39fa9c 43#include "fileio.h"
f4f15635 44#include "fs-util.h"
de2e28d8 45#include "label.h"
cd2eb9e9 46#include "lockfile-util.h"
a8fbdf54 47#include "log.h"
3ffd4af2 48#include "machine-pool.h"
a8fbdf54
TA
49#include "macro.h"
50#include "missing.h"
432cea00 51#include "mkdir.h"
f4f15635 52#include "mount-util.h"
6bedfcbb 53#include "parse-util.h"
432cea00 54#include "path-util.h"
07630cea 55#include "process-util.h"
24882e06 56#include "signal-util.h"
8fcde012 57#include "stat-util.h"
07630cea 58#include "string-util.h"
432cea00
LP
59
60#define VAR_LIB_MACHINES_SIZE_START (1024UL*1024UL*500UL)
61#define VAR_LIB_MACHINES_FREE_MIN (1024UL*1024UL*750UL)
62
63static int check_btrfs(void) {
64 struct statfs sfs;
65
66 if (statfs("/var/lib/machines", &sfs) < 0) {
67 if (errno != ENOENT)
68 return -errno;
69
70 if (statfs("/var/lib", &sfs) < 0)
71 return -errno;
72 }
73
74 return F_TYPE_EQUAL(sfs.f_type, BTRFS_SUPER_MAGIC);
75}
76
4cee5eed 77static int setup_machine_raw(uint64_t size, sd_bus_error *error) {
432cea00
LP
78 _cleanup_free_ char *tmp = NULL;
79 _cleanup_close_ int fd = -1;
80 struct statvfs ss;
81 pid_t pid = 0;
432cea00
LP
82 int r;
83
84 /* We want to be able to make use of btrfs-specific file
85 * system features, in particular subvolumes, reflinks and
86 * quota. Hence, if we detect that /var/lib/machines.raw is
87 * not located on btrfs, let's create a loopback file, place a
88 * btrfs file system into it, and mount it to
89 * /var/lib/machines. */
90
91 fd = open("/var/lib/machines.raw", O_RDWR|O_CLOEXEC|O_NONBLOCK|O_NOCTTY);
92 if (fd >= 0) {
93 r = fd;
94 fd = -1;
95 return r;
96 }
97
98 if (errno != ENOENT)
99 return sd_bus_error_set_errnof(error, errno, "Failed to open /var/lib/machines.raw: %m");
100
14bcf25c 101 r = tempfn_xxxxxx("/var/lib/machines.raw", NULL, &tmp);
432cea00
LP
102 if (r < 0)
103 return r;
104
105 (void) mkdir_p_label("/var/lib", 0755);
106 fd = open(tmp, O_RDWR|O_CREAT|O_EXCL|O_NOCTTY|O_CLOEXEC, 0600);
107 if (fd < 0)
108 return sd_bus_error_set_errnof(error, errno, "Failed to create /var/lib/machines.raw: %m");
109
110 if (fstatvfs(fd, &ss) < 0) {
111 r = sd_bus_error_set_errnof(error, errno, "Failed to determine free space on /var/lib/machines.raw: %m");
112 goto fail;
113 }
114
115 if (ss.f_bsize * ss.f_bavail < VAR_LIB_MACHINES_FREE_MIN) {
116 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Not enough free disk space to set up /var/lib/machines.");
117 goto fail;
118 }
119
4cee5eed 120 if (ftruncate(fd, size) < 0) {
432cea00
LP
121 r = sd_bus_error_set_errnof(error, errno, "Failed to enlarge /var/lib/machines.raw: %m");
122 goto fail;
123 }
124
4c253ed1
LP
125 r = safe_fork("(mkfs)", FORK_RESET_SIGNALS|FORK_DEATHSIG, &pid);
126 if (r < 0) {
127 sd_bus_error_set_errnof(error, r, "Failed to fork mkfs.btrfs: %m");
432cea00
LP
128 goto fail;
129 }
4c253ed1 130 if (r == 0) {
432cea00
LP
131
132 /* Child */
133
432cea00
LP
134 fd = safe_close(fd);
135
136 execlp("mkfs.btrfs", "-Lvar-lib-machines", tmp, NULL);
137 if (errno == ENOENT)
579afbea 138 _exit(99);
432cea00
LP
139
140 _exit(EXIT_FAILURE);
141 }
142
2e87a1fd 143 r = wait_for_terminate_and_check("mkfs", pid, 0);
432cea00
LP
144 pid = 0;
145
2e87a1fd
LP
146 if (r < 0) {
147 sd_bus_error_set_errnof(error, r, "Failed to wait for mkfs.btrfs: %m");
432cea00
LP
148 goto fail;
149 }
2e87a1fd 150 if (r == 99) {
432cea00
LP
151 r = sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
152 goto fail;
153 }
2e87a1fd
LP
154 if (r != EXIT_SUCCESS) {
155 r = sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "mkfs.btrfs failed with error code %i", r);
432cea00
LP
156 goto fail;
157 }
158
f85ef957
AC
159 r = rename_noreplace(AT_FDCWD, tmp, AT_FDCWD, "/var/lib/machines.raw");
160 if (r < 0) {
161 sd_bus_error_set_errnof(error, r, "Failed to move /var/lib/machines.raw into place: %m");
432cea00
LP
162 goto fail;
163 }
164
165 r = fd;
166 fd = -1;
167
168 return r;
169
170fail:
132764a2 171 unlink_noerrno(tmp);
432cea00
LP
172
173 if (pid > 1)
174 kill_and_sigcont(pid, SIGKILL);
175
176 return r;
177}
178
4cee5eed 179int setup_machine_directory(uint64_t size, sd_bus_error *error) {
403e5b32 180 _cleanup_release_lock_file_ LockFile lock_file = LOCK_FILE_INIT;
432cea00
LP
181 struct loop_info64 info = {
182 .lo_flags = LO_FLAGS_AUTOCLEAR,
183 };
184 _cleanup_close_ int fd = -1, control = -1, loop = -1;
185 _cleanup_free_ char* loopdev = NULL;
5bcd08db 186 char tmpdir[] = "/tmp/machine-pool.XXXXXX", *mntdir = NULL;
432cea00 187 bool tmpdir_made = false, mntdir_made = false, mntdir_mounted = false;
26166c88 188 char buf[FORMAT_BYTES_MAX];
432cea00
LP
189 int r, nr = -1;
190
4cee5eed
LP
191 /* btrfs cannot handle file systems < 16M, hence use this as minimum */
192 if (size == (uint64_t) -1)
193 size = VAR_LIB_MACHINES_SIZE_START;
194 else if (size < 16*1024*1024)
195 size = 16*1024*1024;
196
403e5b32
LP
197 /* Make sure we only set the directory up once at a time */
198 r = make_lock_file("/run/systemd/machines.lock", LOCK_EX, &lock_file);
199 if (r < 0)
200 return r;
201
432cea00
LP
202 r = check_btrfs();
203 if (r < 0)
204 return sd_bus_error_set_errnof(error, r, "Failed to determine whether /var/lib/machines is located on btrfs: %m");
205 if (r > 0) {
206 (void) btrfs_subvol_make_label("/var/lib/machines");
207
208 r = btrfs_quota_enable("/var/lib/machines", true);
209 if (r < 0)
5bcd08db 210 log_warning_errno(r, "Failed to enable quota for /var/lib/machines, ignoring: %m");
432cea00 211
5bcd08db
LP
212 r = btrfs_subvol_auto_qgroup("/var/lib/machines", 0, true);
213 if (r < 0)
214 log_warning_errno(r, "Failed to set up default quota hierarchy for /var/lib/machines, ignoring: %m");
215
216 return 1;
217 }
218
e1873695 219 if (path_is_mount_point("/var/lib/machines", NULL, AT_SYMLINK_FOLLOW) > 0) {
5bcd08db 220 log_debug("/var/lib/machines is already a mount point, not creating loopback file for it.");
432cea00
LP
221 return 0;
222 }
223
5bcd08db
LP
224 r = dir_is_populated("/var/lib/machines");
225 if (r < 0 && r != -ENOENT)
226 return r;
227 if (r > 0) {
228 log_debug("/var/log/machines is already populated, not creating loopback file for it.");
229 return 0;
230 }
231
232 r = mkfs_exists("btrfs");
c3b0e5ac
EV
233 if (r == 0)
234 return sd_bus_error_set_errnof(error, ENOENT, "Cannot set up /var/lib/machines, mkfs.btrfs is missing");
5bcd08db
LP
235 if (r < 0)
236 return r;
432cea00 237
4cee5eed 238 fd = setup_machine_raw(size, error);
432cea00
LP
239 if (fd < 0)
240 return fd;
241
242 control = open("/dev/loop-control", O_RDWR|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
243 if (control < 0)
244 return sd_bus_error_set_errnof(error, errno, "Failed to open /dev/loop-control: %m");
245
246 nr = ioctl(control, LOOP_CTL_GET_FREE);
247 if (nr < 0)
248 return sd_bus_error_set_errnof(error, errno, "Failed to allocate loop device: %m");
249
250 if (asprintf(&loopdev, "/dev/loop%i", nr) < 0) {
251 r = -ENOMEM;
252 goto fail;
253 }
254
255 loop = open(loopdev, O_CLOEXEC|O_RDWR|O_NOCTTY|O_NONBLOCK);
256 if (loop < 0) {
257 r = sd_bus_error_set_errnof(error, errno, "Failed to open loopback device: %m");
258 goto fail;
259 }
260
261 if (ioctl(loop, LOOP_SET_FD, fd) < 0) {
262 r = sd_bus_error_set_errnof(error, errno, "Failed to bind loopback device: %m");
263 goto fail;
264 }
265
266 if (ioctl(loop, LOOP_SET_STATUS64, &info) < 0) {
267 r = sd_bus_error_set_errnof(error, errno, "Failed to enable auto-clear for loopback device: %m");
268 goto fail;
269 }
270
271 /* We need to make sure the new /var/lib/machines directory
272 * has an access mode of 0700 at the time it is first made
273 * available. mkfs will create it with 0755 however. Hence,
274 * let's mount the directory into an inaccessible directory
275 * below /tmp first, fix the access mode, and move it to the
276 * public place then. */
277
278 if (!mkdtemp(tmpdir)) {
279 r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount parent directory: %m");
280 goto fail;
281 }
282 tmpdir_made = true;
283
284 mntdir = strjoina(tmpdir, "/mnt");
285 if (mkdir(mntdir, 0700) < 0) {
286 r = sd_bus_error_set_errnof(error, errno, "Failed to create temporary mount directory: %m");
287 goto fail;
288 }
289 mntdir_made = true;
290
291 if (mount(loopdev, mntdir, "btrfs", 0, NULL) < 0) {
292 r = sd_bus_error_set_errnof(error, errno, "Failed to mount loopback device: %m");
293 goto fail;
294 }
295 mntdir_mounted = true;
296
297 r = btrfs_quota_enable(mntdir, true);
298 if (r < 0)
299 log_warning_errno(r, "Failed to enable quota, ignoring: %m");
300
5bcd08db
LP
301 r = btrfs_subvol_auto_qgroup(mntdir, 0, true);
302 if (r < 0)
303 log_warning_errno(r, "Failed to set up default quota hierarchy, ignoring: %m");
304
432cea00
LP
305 if (chmod(mntdir, 0700) < 0) {
306 r = sd_bus_error_set_errnof(error, errno, "Failed to fix owner: %m");
307 goto fail;
308 }
309
310 (void) mkdir_p_label("/var/lib/machines", 0700);
311
312 if (mount(mntdir, "/var/lib/machines", NULL, MS_BIND, NULL) < 0) {
313 r = sd_bus_error_set_errnof(error, errno, "Failed to mount directory into right place: %m");
314 goto fail;
315 }
316
26166c88
LP
317 (void) syncfs(fd);
318
319 log_info("Set up /var/lib/machines as btrfs loopback file system of size %s mounted on /var/lib/machines.raw.", format_bytes(buf, sizeof(buf), size));
320
432cea00
LP
321 (void) umount2(mntdir, MNT_DETACH);
322 (void) rmdir(mntdir);
323 (void) rmdir(tmpdir);
324
5bcd08db 325 return 1;
432cea00
LP
326
327fail:
328 if (mntdir_mounted)
329 (void) umount2(mntdir, MNT_DETACH);
330
331 if (mntdir_made)
332 (void) rmdir(mntdir);
333 if (tmpdir_made)
334 (void) rmdir(tmpdir);
335
336 if (loop >= 0) {
337 (void) ioctl(loop, LOOP_CLR_FD);
338 loop = safe_close(loop);
339 }
340
341 if (control >= 0 && nr >= 0)
342 (void) ioctl(control, LOOP_CTL_REMOVE, nr);
343
344 return r;
345}
26166c88
LP
346
347static int sync_path(const char *p) {
348 _cleanup_close_ int fd = -1;
349
350 fd = open(p, O_RDONLY|O_CLOEXEC|O_NOCTTY);
351 if (fd < 0)
352 return -errno;
353
354 if (syncfs(fd) < 0)
355 return -errno;
356
357 return 0;
358}
359
360int grow_machine_directory(void) {
361 char buf[FORMAT_BYTES_MAX];
362 struct statvfs a, b;
363 uint64_t old_size, new_size, max_add;
364 int r;
365
366 /* Ensure the disk space data is accurate */
367 sync_path("/var/lib/machines");
368 sync_path("/var/lib/machines.raw");
369
370 if (statvfs("/var/lib/machines.raw", &a) < 0)
371 return -errno;
372
373 if (statvfs("/var/lib/machines", &b) < 0)
374 return -errno;
375
376 /* Don't grow if not enough disk space is available on the host */
377 if (((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) <= VAR_LIB_MACHINES_FREE_MIN)
378 return 0;
379
380 /* Don't grow if at least 1/3th of the fs is still free */
381 if (b.f_bavail > b.f_blocks / 3)
382 return 0;
383
a8eaaee7 384 /* Calculate how much we are willing to add at most */
26166c88
LP
385 max_add = ((uint64_t) a.f_bavail * (uint64_t) a.f_bsize) - VAR_LIB_MACHINES_FREE_MIN;
386
387 /* Calculate the old size */
388 old_size = (uint64_t) b.f_blocks * (uint64_t) b.f_bsize;
389
390 /* Calculate the new size as three times the size of what is used right now */
391 new_size = ((uint64_t) b.f_blocks - (uint64_t) b.f_bavail) * (uint64_t) b.f_bsize * 3;
392
393 /* Always, grow at least to the start size */
394 if (new_size < VAR_LIB_MACHINES_SIZE_START)
395 new_size = VAR_LIB_MACHINES_SIZE_START;
396
397 /* If the new size is smaller than the old size, don't grow */
398 if (new_size < old_size)
399 return 0;
400
401 /* Ensure we never add more than the maximum */
402 if (new_size > old_size + max_add)
403 new_size = old_size + max_add;
404
405 r = btrfs_resize_loopback("/var/lib/machines", new_size, true);
406 if (r <= 0)
407 return r;
408
5bcd08db
LP
409 /* Also bump the quota, of both the subvolume leaf qgroup, as
410 * well as of any subtree quota group by the same id but a
411 * higher level, if it exists. */
412 (void) btrfs_qgroup_set_limit("/var/lib/machines", 0, new_size);
413 (void) btrfs_subvol_set_subtree_quota_limit("/var/lib/machines", 0, new_size);
26166c88
LP
414
415 log_info("Grew /var/lib/machines btrfs loopback file system to %s.", format_bytes(buf, sizeof(buf), new_size));
416 return 1;
417}