1 /* 2 * Copyright (c) 2019 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * This code uses concepts and configuration based on 'synth', by 8 * John R. Marino <draco@marino.st>, which was written in ada. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 3. Neither the name of The DragonFly Project nor the names of its 21 * contributors may be used to endorse or promote products derived 22 * from this software without specific, prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 #include "dsynth.h" 38 39 static void domount(worker_t *work, int type, 40 const char *spath, const char *dpath, 41 const char *discretefmt); 42 static void dounmount(worker_t *work, const char *rpath); 43 static void makeDiscreteCopies(const char *spath, const char *discretefmt); 44 45 /* 46 * Called by the frontend to create a template which will be cpdup'd 47 * into fresh workers. 48 * 49 * Template must have been previously destroyed. Errors are fatal 50 */ 51 int 52 DoCreateTemplate(int force) 53 { 54 struct stat st; 55 char *goodbuf; 56 char *buf; 57 int rc; 58 int fd; 59 60 rc = 0; 61 asprintf(&goodbuf, "%s/.template.good", BuildBase); 62 63 /* 64 * Conditionally create the template and discrete copies of certain 65 * directories if we think we are missing things. 66 */ 67 if (force == 0) { 68 asprintf(&buf, "%s/Template", BuildBase); 69 if (stat(buf, &st) < 0) 70 force = 1; 71 free(buf); 72 73 if (stat(goodbuf, &st) < 0) 74 force = 1; 75 } 76 77 dlog(DLOG_ALL, "Check Template: %s\n", 78 (force ? "Must-Create" : "Good")); 79 80 /* 81 * Create the template 82 */ 83 if (force) { 84 remove(goodbuf); /* ignore exit code */ 85 86 rc = 0; 87 asprintf(&buf, "%s/mktemplate %s %s/Template", 88 SCRIPTPATH(SCRIPTDIR), SystemPath, BuildBase); 89 rc = system(buf); 90 if (rc) 91 dfatal("Command failed: %s\n", buf); 92 dlog(DLOG_ALL | DLOG_FILTER, 93 "Template - rc=%d running %s\n", rc, buf); 94 free(buf); 95 96 /* 97 * Make discrete copies of certain extremely heavily used 98 * but small directories. 99 */ 100 if (force) { 101 makeDiscreteCopies("$/bin", "/bin.%03d"); 102 makeDiscreteCopies("$/lib", "/lib.%03d"); 103 makeDiscreteCopies("$/libexec", "/libexec.%03d"); 104 makeDiscreteCopies("$/usr/bin", "/usr.bin.%03d"); 105 } 106 107 /* 108 * Mark the template good... ah, do a sync() to really 109 * be sure that it can't get corrupted. 110 */ 111 sync(); 112 fd = open(goodbuf, O_RDWR|O_CREAT|O_TRUNC, 0644); 113 dassert_errno(fd >= 0, "could not create %s", goodbuf); 114 close(fd); 115 116 dlog(DLOG_ALL | DLOG_FILTER, "Template - done\n"); 117 } 118 free(goodbuf); 119 120 return force; 121 } 122 123 void 124 DoDestroyTemplate(void) 125 { 126 struct stat st; 127 char *path; 128 char *buf; 129 int rc; 130 131 /* 132 * NOTE: rm -rf safety, use a fixed name 'Template' to ensure we 133 * do not accidently blow something up. 134 */ 135 asprintf(&path, "%s/Template", BuildBase); 136 if (stat(path, &st) == 0) { 137 asprintf(&buf, "chflags -R noschg %s; /bin/rm -rf %s", 138 path, path); 139 rc = system(buf); 140 if (rc) 141 dfatal("Command failed: %s (ignored)\n", buf); 142 free(buf); 143 } 144 free(path); 145 } 146 147 /* 148 * Called by the worker support thread to install a new worker 149 * filesystem topology. 150 */ 151 void 152 DoWorkerMounts(worker_t *work) 153 { 154 char *buf; 155 int rc; 156 157 /* 158 * Generate required mounts, domount() will mkdir() the target 159 * directory if necessary and prefix spath with SystemPath if 160 * it starts with $/ 161 */ 162 domount(work, TMPFS_RW, "dummy", "", NULL); 163 asprintf(&buf, "%s/usr", work->basedir); 164 if (mkdir(buf, 0755) != 0) { 165 fprintf(stderr, "Command failed: mkdir %s\n", buf); 166 ++work->mount_error; 167 } 168 domount(work, NULLFS_RO, "$/boot", "/boot", NULL); 169 domount(work, TMPFS_RW, "dummy", "/boot/modules.local", NULL); 170 domount(work, DEVFS_RW, "dummy", "/dev", NULL); 171 domount(work, PROCFS_RO, "dummy", "/proc", NULL); 172 domount(work, NULLFS_RO, "$/bin", "/bin", "/bin.%03d"); 173 domount(work, NULLFS_RO, "$/sbin", "/sbin", NULL); 174 domount(work, NULLFS_RO, "$/lib", "/lib", "/lib.%03d"); 175 domount(work, NULLFS_RO, "$/libexec", "/libexec", "/libexec.%03d"); 176 domount(work, NULLFS_RO, "$/usr/bin", "/usr/bin", "/usr.bin.%03d"); 177 domount(work, NULLFS_RO, "$/usr/include", "/usr/include", NULL); 178 domount(work, NULLFS_RO, "$/usr/lib", "/usr/lib", NULL); 179 domount(work, NULLFS_RO, "$/usr/libdata", "/usr/libdata", NULL); 180 domount(work, NULLFS_RO, "$/usr/libexec", "/usr/libexec", NULL); 181 domount(work, NULLFS_RO, "$/usr/sbin", "/usr/sbin", NULL); 182 domount(work, NULLFS_RO, "$/usr/share", "/usr/share", NULL); 183 domount(work, TMPFS_RW, "dummy", "/usr/local", NULL); 184 domount(work, NULLFS_RO, "$/usr/games", "/usr/games", NULL); 185 if (UseUsrSrc) 186 domount(work, NULLFS_RO, "$/usr/src", "/usr/src", NULL); 187 domount(work, NULLFS_RO, DPortsPath, "/xports", NULL); 188 domount(work, NULLFS_RW, OptionsPath, "/options", NULL); 189 domount(work, NULLFS_RW, PackagesPath, "/packages", NULL); 190 domount(work, NULLFS_RW, DistFilesPath, "/distfiles", NULL); 191 domount(work, TMPFS_RW_BIG, "dummy", "/construction", NULL); 192 if (UseCCache) 193 domount(work, NULLFS_RW, CCachePath, "/ccache", NULL); 194 195 /* 196 * NOTE: Uses blah/. to prevent cp from creating 'Template' under 197 * work->basedir. We want to start with the content. 198 */ 199 asprintf(&buf, "cp -Rp %s/Template/. %s", BuildBase, work->basedir); 200 rc = system(buf); 201 if (rc) { 202 fprintf(stderr, "Command failed: %s\n", buf); 203 ++work->accum_error; 204 snprintf(work->status, sizeof(work->status), 205 "Template copy failed"); 206 } 207 free(buf); 208 } 209 210 /* 211 * Called by the worker support thread to remove a worker 212 * filesystem topology. 213 * 214 * NOTE: No need to conditionalize UseUsrSrc, it doesn't hurt to 215 * issue the umount() if it isn't mounted and it ensures that 216 * everything is unmounted properly on cleanup if the state 217 * changes. 218 */ 219 void 220 DoWorkerUnmounts(worker_t *work) 221 { 222 int retries; 223 224 work->mount_error = 0; 225 for (retries = 0; retries < 10; ++retries) { 226 dounmount(work, "/proc"); 227 dounmount(work, "/dev"); 228 dounmount(work, "/usr/src"); 229 dounmount(work, "/usr/games"); 230 dounmount(work, "/boot/modules.local"); 231 dounmount(work, "/boot"); 232 dounmount(work, "/usr/local"); 233 dounmount(work, "/construction"); 234 dounmount(work, "/ccache"); /* in case of config change */ 235 dounmount(work, "/distfiles"); 236 dounmount(work, "/packages"); 237 dounmount(work, "/options"); 238 dounmount(work, "/xports"); 239 dounmount(work, "/usr/share"); 240 dounmount(work, "/usr/sbin"); 241 dounmount(work, "/usr/libexec"); 242 dounmount(work, "/usr/libdata"); 243 dounmount(work, "/usr/lib"); 244 dounmount(work, "/usr/include"); 245 dounmount(work, "/usr/bin"); 246 dounmount(work, "/libexec"); 247 dounmount(work, "/lib"); 248 dounmount(work, "/sbin"); 249 dounmount(work, "/bin"); 250 dounmount(work, ""); 251 if (work->mount_error == 0) 252 break; 253 sleep(5); 254 work->mount_error = 0; 255 } 256 if (work->mount_error) { 257 ++work->accum_error; 258 snprintf(work->status, sizeof(work->status), 259 "Unable to unmount slot"); 260 } 261 } 262 263 static 264 void 265 domount(worker_t *work, int type, const char *spath, const char *dpath, 266 const char *discretefmt) 267 { 268 const char *prog; 269 const char *sbase; 270 const char *rwstr; 271 const char *optstr; 272 struct stat st; 273 char *buf; 274 char *tmp; 275 int rc; 276 277 /* 278 * Make target directory if necessary. This must occur in-order 279 * since directories may have to be created under prior mounts 280 * in the sequence. 281 */ 282 asprintf(&buf, "%s%s", work->basedir, dpath); 283 if (stat(buf, &st) != 0) { 284 if (mkdir(buf, 0755) != 0) { 285 fprintf(stderr, "Command failed: mkdir %s\n", buf); 286 ++work->mount_error; 287 } 288 } 289 free(buf); 290 291 /* 292 * Setup for mount arguments 293 */ 294 rwstr = (type & MOUNT_TYPE_RW) ? "rw" : "ro"; 295 optstr = ""; 296 297 switch(type & MOUNT_TYPE_MASK) { 298 case MOUNT_TYPE_TMPFS: 299 prog = MOUNT_TMPFS_BINARY; 300 if (type & MOUNT_TYPE_BIG) 301 optstr = " -s 64g"; 302 else 303 optstr = " -s 16g"; 304 break; 305 case MOUNT_TYPE_NULLFS: 306 prog = MOUNT_NULLFS_BINARY; 307 break; 308 case MOUNT_TYPE_DEVFS: 309 prog = MOUNT_DEVFS_BINARY; 310 break; 311 case MOUNT_TYPE_PROCFS: 312 prog = MOUNT_PROCFS_BINARY; 313 break; 314 default: 315 dfatal("Illegal mount type: %08x", type); 316 /* NOT REACHED */ 317 prog = "/bin/hell"; 318 break; 319 } 320 321 /* 322 * Prefix spath 323 */ 324 if (discretefmt) { 325 sbase = BuildBase; 326 asprintf(&tmp, discretefmt, work->index); 327 spath = tmp; 328 } else { 329 if (spath[0] == '$') { 330 ++spath; 331 sbase = SystemPath; 332 if (strcmp(sbase, "/") == 0) 333 ++sbase; 334 } else { 335 sbase = ""; 336 } 337 tmp = NULL; 338 } 339 asprintf(&buf, "%s%s -o %s %s%s %s%s", 340 prog, optstr, rwstr, 341 sbase, spath, work->basedir, dpath); 342 rc = system(buf); 343 if (rc) { 344 fprintf(stderr, "Command failed: %s\n", buf); 345 ++work->mount_error; 346 } 347 free(buf); 348 if (tmp) 349 free(tmp); 350 } 351 352 static 353 void 354 dounmount(worker_t *work, const char *rpath) 355 { 356 char *buf; 357 358 asprintf(&buf, "%s%s", work->basedir, rpath); 359 if (unmount(buf, 0) < 0) { 360 switch(errno) { 361 case EPERM: /* This is probably fatal later on in mount */ 362 case ENOENT: /* Expected if mount already gone */ 363 case EINVAL: /* Expected if mount already gone (maybe) */ 364 break; 365 default: 366 fprintf(stderr, "Cannot umount %s (%s)\n", 367 buf, strerror(errno)); 368 ++work->mount_error; 369 break; 370 } 371 } 372 free(buf); 373 } 374 375 static 376 void 377 makeDiscreteCopies(const char *spath, const char *discretefmt) 378 { 379 char *src; 380 char *dst; 381 char *buf; 382 struct stat st; 383 int i; 384 int rc; 385 386 for (i = 0; i < MaxWorkers; ++i) { 387 if (spath[0] == '$') { 388 if (strcmp(SystemPath, "/") == 0) 389 asprintf(&src, "%s%s", 390 SystemPath + 1, spath + 1); 391 else 392 asprintf(&src, "%s%s", 393 SystemPath, spath + 1); 394 } else { 395 src = strdup(spath); 396 } 397 asprintf(&buf, discretefmt, i); 398 asprintf(&dst, "%s%s", BuildBase, buf); 399 free(buf); 400 401 if (stat(dst, &st) < 0) { 402 if (mkdir(dst, 0555) < 0) { 403 dlog(DLOG_ALL, "Template - mkdir %s failed\n", 404 dst); 405 dfatal_errno("Cannot mkdir %s:", dst); 406 } 407 } 408 asprintf(&buf, "chflags -R noschg %s; " 409 "rm -rf %s; " 410 "cp -Rp %s/. %s", 411 dst, dst, src, dst); 412 rc = system(buf); 413 dlog(DLOG_ALL | DLOG_FILTER, 414 "Template - rc=%d running %s\n", rc, buf); 415 if (rc) 416 dfatal("Command failed: %s", buf); 417 free(buf); 418 free(src); 419 free(dst); 420 } 421 } 422