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 int n; 60 61 rc = 0; 62 asprintf(&goodbuf, "%s/.template.good", BuildBase); 63 64 /* 65 * Conditionally create the template and discrete copies of certain 66 * directories if we think we are missing things. 67 */ 68 if (force == 0) { 69 time_t ls_mtime; 70 71 asprintf(&buf, "%s/Template", BuildBase); 72 if (stat(buf, &st) < 0) 73 force = 1; 74 free(buf); 75 76 /* 77 * Check to see if the worker count changed and some 78 * template dirs are missing or out of date, and also if 79 * a new world was installed (via /bin/ls mtime). 80 */ 81 asprintf(&buf, "%s/bin/ls", SystemPath); 82 if (stat(buf, &st) < 0) 83 dfatal_errno("Unable to locate %s", buf); 84 free(buf); 85 ls_mtime = st.st_mtime; 86 87 for (n = 0; n < MaxWorkers; ++n) { 88 asprintf(&buf, "%s/bin.%03d/ls", BuildBase, n); 89 if (stat(buf, &st) < 0 || st.st_mtime != ls_mtime) 90 force = 1; 91 free(buf); 92 } 93 94 if (stat(goodbuf, &st) < 0) 95 force = 1; 96 } 97 98 dlog(DLOG_ALL, "Check Template: %s\n", 99 (force ? "Must-Create" : "Good")); 100 101 /* 102 * Create the template 103 */ 104 if (force) { 105 remove(goodbuf); /* ignore exit code */ 106 107 rc = 0; 108 asprintf(&buf, "%s/mktemplate %s %s/Template", 109 SCRIPTPATH(SCRIPTDIR), SystemPath, BuildBase); 110 rc = system(buf); 111 if (rc) 112 dfatal("Command failed: %s\n", buf); 113 dlog(DLOG_ALL | DLOG_FILTER, 114 "Template - rc=%d running %s\n", rc, buf); 115 free(buf); 116 117 /* 118 * Make discrete copies of certain extremely heavily used 119 * but small directories. 120 */ 121 makeDiscreteCopies("$/bin", "/bin.%03d"); 122 makeDiscreteCopies("$/lib", "/lib.%03d"); 123 makeDiscreteCopies("$/libexec", "/libexec.%03d"); 124 makeDiscreteCopies("$/usr/bin", "/usr.bin.%03d"); 125 126 /* 127 * Mark the template good... ah, do a sync() to really 128 * be sure that it can't get corrupted. 129 */ 130 sync(); 131 fd = open(goodbuf, O_RDWR|O_CREAT|O_TRUNC, 0644); 132 dassert_errno(fd >= 0, "could not create %s", goodbuf); 133 close(fd); 134 135 dlog(DLOG_ALL | DLOG_FILTER, "Template - done\n"); 136 } 137 free(goodbuf); 138 139 return force; 140 } 141 142 void 143 DoDestroyTemplate(void) 144 { 145 struct stat st; 146 char *path; 147 char *buf; 148 int rc; 149 150 /* 151 * NOTE: rm -rf safety, use a fixed name 'Template' to ensure we 152 * do not accidently blow something up. 153 */ 154 asprintf(&path, "%s/Template", BuildBase); 155 if (stat(path, &st) == 0) { 156 asprintf(&buf, "chflags -R noschg %s; /bin/rm -rf %s", 157 path, path); 158 rc = system(buf); 159 if (rc) 160 dfatal("Command failed: %s (ignored)\n", buf); 161 free(buf); 162 } 163 free(path); 164 } 165 166 /* 167 * Called by the worker support thread to install a new worker 168 * filesystem topology. 169 */ 170 void 171 DoWorkerMounts(worker_t *work) 172 { 173 char *buf; 174 int rc; 175 176 /* 177 * Generate required mounts, domount() will mkdir() the target 178 * directory if necessary and prefix spath with SystemPath if 179 * it starts with $/ 180 */ 181 domount(work, TMPFS_RW, "dummy", "", NULL); 182 asprintf(&buf, "%s/usr", work->basedir); 183 if (mkdir(buf, 0755) != 0) { 184 fprintf(stderr, "Command failed: mkdir %s\n", buf); 185 ++work->mount_error; 186 } 187 domount(work, NULLFS_RO, "$/boot", "/boot", NULL); 188 domount(work, TMPFS_RW, "dummy", "/boot/modules.local", NULL); 189 domount(work, DEVFS_RW, "dummy", "/dev", NULL); 190 domount(work, PROCFS_RO, "dummy", "/proc", NULL); 191 domount(work, NULLFS_RO, "$/bin", "/bin", "/bin.%03d"); 192 domount(work, NULLFS_RO, "$/sbin", "/sbin", NULL); 193 domount(work, NULLFS_RO, "$/lib", "/lib", "/lib.%03d"); 194 domount(work, NULLFS_RO, "$/libexec", "/libexec", "/libexec.%03d"); 195 domount(work, NULLFS_RO, "$/usr/bin", "/usr/bin", "/usr.bin.%03d"); 196 domount(work, NULLFS_RO, "$/usr/include", "/usr/include", NULL); 197 domount(work, NULLFS_RO, "$/usr/lib", "/usr/lib", NULL); 198 domount(work, NULLFS_RO, "$/usr/libdata", "/usr/libdata", NULL); 199 domount(work, NULLFS_RO, "$/usr/libexec", "/usr/libexec", NULL); 200 domount(work, NULLFS_RO, "$/usr/sbin", "/usr/sbin", NULL); 201 domount(work, NULLFS_RO, "$/usr/share", "/usr/share", NULL); 202 domount(work, TMPFS_RW, "dummy", "/usr/local", NULL); 203 domount(work, NULLFS_RO, "$/usr/games", "/usr/games", NULL); 204 if (UseUsrSrc) 205 domount(work, NULLFS_RO, "$/usr/src", "/usr/src", NULL); 206 domount(work, NULLFS_RO, DPortsPath, "/xports", NULL); 207 domount(work, NULLFS_RW, OptionsPath, "/options", NULL); 208 domount(work, NULLFS_RW, PackagesPath, "/packages", NULL); 209 domount(work, NULLFS_RW, DistFilesPath, "/distfiles", NULL); 210 domount(work, TMPFS_RW_BIG, "dummy", "/construction", NULL); 211 if (UseCCache) 212 domount(work, NULLFS_RW, CCachePath, "/ccache", NULL); 213 214 /* 215 * NOTE: Uses blah/. to prevent cp from creating 'Template' under 216 * work->basedir. We want to start with the content. 217 */ 218 asprintf(&buf, "cp -Rp %s/Template/. %s", BuildBase, work->basedir); 219 rc = system(buf); 220 if (rc) { 221 fprintf(stderr, "Command failed: %s\n", buf); 222 ++work->accum_error; 223 snprintf(work->status, sizeof(work->status), 224 "Template copy failed"); 225 } 226 free(buf); 227 } 228 229 /* 230 * Called by the worker support thread to remove a worker 231 * filesystem topology. 232 * 233 * NOTE: No need to conditionalize UseUsrSrc, it doesn't hurt to 234 * issue the umount() if it isn't mounted and it ensures that 235 * everything is unmounted properly on cleanup if the state 236 * changes. 237 */ 238 void 239 DoWorkerUnmounts(worker_t *work) 240 { 241 int retries; 242 243 work->mount_error = 0; 244 for (retries = 0; retries < 10; ++retries) { 245 dounmount(work, "/proc"); 246 dounmount(work, "/dev"); 247 dounmount(work, "/usr/src"); 248 dounmount(work, "/usr/games"); 249 dounmount(work, "/boot/modules.local"); 250 dounmount(work, "/boot"); 251 dounmount(work, "/usr/local"); 252 dounmount(work, "/construction"); 253 dounmount(work, "/ccache"); /* in case of config change */ 254 dounmount(work, "/distfiles"); 255 dounmount(work, "/packages"); 256 dounmount(work, "/options"); 257 dounmount(work, "/xports"); 258 dounmount(work, "/usr/share"); 259 dounmount(work, "/usr/sbin"); 260 dounmount(work, "/usr/libexec"); 261 dounmount(work, "/usr/libdata"); 262 dounmount(work, "/usr/lib"); 263 dounmount(work, "/usr/include"); 264 dounmount(work, "/usr/bin"); 265 dounmount(work, "/libexec"); 266 dounmount(work, "/lib"); 267 dounmount(work, "/sbin"); 268 dounmount(work, "/bin"); 269 dounmount(work, ""); 270 if (work->mount_error == 0) 271 break; 272 sleep(5); 273 work->mount_error = 0; 274 } 275 if (work->mount_error) { 276 ++work->accum_error; 277 snprintf(work->status, sizeof(work->status), 278 "Unable to unmount slot"); 279 } 280 } 281 282 static 283 void 284 domount(worker_t *work, int type, const char *spath, const char *dpath, 285 const char *discretefmt) 286 { 287 const char *prog; 288 const char *sbase; 289 const char *rwstr; 290 const char *optstr; 291 struct stat st; 292 char *buf; 293 char *tmp; 294 int rc; 295 296 /* 297 * Make target directory if necessary. This must occur in-order 298 * since directories may have to be created under prior mounts 299 * in the sequence. 300 */ 301 asprintf(&buf, "%s%s", work->basedir, dpath); 302 if (stat(buf, &st) != 0) { 303 if (mkdir(buf, 0755) != 0) { 304 fprintf(stderr, "Command failed: mkdir %s\n", buf); 305 ++work->mount_error; 306 } 307 } 308 free(buf); 309 310 /* 311 * Setup for mount arguments 312 */ 313 rwstr = (type & MOUNT_TYPE_RW) ? "rw" : "ro"; 314 optstr = ""; 315 316 switch(type & MOUNT_TYPE_MASK) { 317 case MOUNT_TYPE_TMPFS: 318 prog = MOUNT_TMPFS_BINARY; 319 if (type & MOUNT_TYPE_BIG) 320 optstr = " -s 64g"; 321 else 322 optstr = " -s 16g"; 323 break; 324 case MOUNT_TYPE_NULLFS: 325 prog = MOUNT_NULLFS_BINARY; 326 break; 327 case MOUNT_TYPE_DEVFS: 328 prog = MOUNT_DEVFS_BINARY; 329 break; 330 case MOUNT_TYPE_PROCFS: 331 prog = MOUNT_PROCFS_BINARY; 332 break; 333 default: 334 dfatal("Illegal mount type: %08x", type); 335 /* NOT REACHED */ 336 prog = "/bin/hell"; 337 break; 338 } 339 340 /* 341 * Prefix spath 342 */ 343 if (discretefmt) { 344 sbase = BuildBase; 345 asprintf(&tmp, discretefmt, work->index); 346 spath = tmp; 347 } else { 348 if (spath[0] == '$') { 349 ++spath; 350 sbase = SystemPath; 351 if (strcmp(sbase, "/") == 0) 352 ++sbase; 353 } else { 354 sbase = ""; 355 } 356 tmp = NULL; 357 } 358 asprintf(&buf, "%s%s -o %s %s%s %s%s", 359 prog, optstr, rwstr, 360 sbase, spath, work->basedir, dpath); 361 rc = system(buf); 362 if (rc) { 363 fprintf(stderr, "Command failed: %s\n", buf); 364 ++work->mount_error; 365 } 366 free(buf); 367 if (tmp) 368 free(tmp); 369 } 370 371 static 372 void 373 dounmount(worker_t *work, const char *rpath) 374 { 375 char *buf; 376 377 asprintf(&buf, "%s%s", work->basedir, rpath); 378 if (unmount(buf, 0) < 0) { 379 switch(errno) { 380 case EPERM: /* This is probably fatal later on in mount */ 381 case ENOENT: /* Expected if mount already gone */ 382 case EINVAL: /* Expected if mount already gone (maybe) */ 383 break; 384 default: 385 fprintf(stderr, "Cannot umount %s (%s)\n", 386 buf, strerror(errno)); 387 ++work->mount_error; 388 break; 389 } 390 } 391 free(buf); 392 } 393 394 static 395 void 396 makeDiscreteCopies(const char *spath, const char *discretefmt) 397 { 398 char *src; 399 char *dst; 400 char *buf; 401 struct stat st; 402 int i; 403 int rc; 404 405 for (i = 0; i < MaxWorkers; ++i) { 406 if (spath[0] == '$') { 407 if (strcmp(SystemPath, "/") == 0) 408 asprintf(&src, "%s%s", 409 SystemPath + 1, spath + 1); 410 else 411 asprintf(&src, "%s%s", 412 SystemPath, spath + 1); 413 } else { 414 src = strdup(spath); 415 } 416 asprintf(&buf, discretefmt, i); 417 asprintf(&dst, "%s%s", BuildBase, buf); 418 free(buf); 419 420 if (stat(dst, &st) < 0) { 421 if (mkdir(dst, 0555) < 0) { 422 dlog(DLOG_ALL, "Template - mkdir %s failed\n", 423 dst); 424 dfatal_errno("Cannot mkdir %s:", dst); 425 } 426 } 427 asprintf(&buf, "chflags -R noschg %s; " 428 "rm -rf %s; " 429 "cp -Rp %s/. %s", 430 dst, dst, src, dst); 431 rc = system(buf); 432 dlog(DLOG_ALL | DLOG_FILTER, 433 "Template - rc=%d running %s\n", rc, buf); 434 if (rc) 435 dfatal("Command failed: %s", buf); 436 free(buf); 437 free(src); 438 free(dst); 439 } 440 } 441