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