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 worker_t WorkerAry[MAXWORKERS]; 40 int BuildInitialized; 41 int RunningWorkers; 42 int DynamicMaxWorkers; 43 int FailedWorkers; 44 long RunningPkgDepSize; 45 pthread_mutex_t WorkerMutex; 46 pthread_cond_t WorkerCond; 47 48 static int build_find_leaves(pkg_t *parent, pkg_t *pkg, 49 pkg_t ***build_tailp, int *app, int *hasworkp, 50 int depth, int first, int first_one_only); 51 static int buildCalculateDepiDepth(pkg_t *pkg); 52 static void build_clear_trav(pkg_t *pkg); 53 static void startbuild(pkg_t **build_listp, pkg_t ***build_tailp); 54 static int qsort_depi(const void *pkg1, const void *pkg2); 55 static int qsort_idep(const void *pkg1, const void *pkg2); 56 static void startworker(pkg_t *pkg, worker_t *work); 57 static void cleanworker(worker_t *work); 58 static void waitbuild(int whilematch, int dynamicmax); 59 static void workercomplete(worker_t *work); 60 static void *childBuilderThread(void *arg); 61 static int childInstallPkgDeps(worker_t *work); 62 static size_t childInstallPkgDeps_recurse(FILE *fp, pkglink_t *list, 63 int undoit, int depth, int first_one_only); 64 static void dophase(worker_t *work, wmsg_t *wmsg, 65 int wdog, int phaseid, const char *phase); 66 static void phaseReapAll(void); 67 static void phaseTerminateSignal(int sig); 68 static char *buildskipreason(pkglink_t *parent, pkg_t *pkg); 69 static int buildskipcount_dueto(pkg_t *pkg, int mode); 70 static int mptylogpoll(int ptyfd, int fdlog, wmsg_t *wmsg, 71 time_t *wdog_timep); 72 static int copyfile(char *src, char *dst); 73 static void doHook(pkg_t *pkg, const char *id, const char *path, int waitfor); 74 static void childHookRun(bulk_t *bulk); 75 76 static worker_t *SigWork; 77 static int MasterPtyFd = -1; 78 static int CopyFileFd = -1; 79 static pid_t SigPid; 80 static const char *WorkerFlavorPrt = ""; /* "" or "@flavor" */ 81 82 #define MPTY_FAILED -2 83 #define MPTY_AGAIN -1 84 #define MPTY_EOF 0 85 #define MPTY_DATA 1 86 87 int BuildTotal; 88 int BuildCount; 89 int BuildSkipCount; 90 int BuildIgnoreCount; 91 int BuildFailCount; 92 int BuildSuccessCount; 93 94 /* 95 * Initialize the WorkerAry[] 96 */ 97 void 98 DoInitBuild(int slot_override) 99 { 100 worker_t *work; 101 struct stat st; 102 int i; 103 104 ddassert(slot_override < 0 || MaxWorkers == 1); 105 106 bzero(WorkerAry, MaxWorkers * sizeof(worker_t)); 107 pthread_mutex_init(&WorkerMutex, NULL); 108 109 for (i = 0; i < MaxWorkers; ++i) { 110 work = &WorkerAry[i]; 111 work->index = (slot_override >= 0) ? slot_override : i; 112 work->state = WORKER_NONE; 113 asprintf(&work->basedir, "%s/SL%02d", BuildBase, work->index); 114 pthread_cond_init(&work->cond, NULL); 115 } 116 BuildCount = 0; 117 118 /* 119 * Create required sub-directories. The base directories must already 120 * exist as a dsynth configuration safety. 121 */ 122 if (stat(RepositoryPath, &st) < 0) { 123 if (mkdir(RepositoryPath, 0755) < 0) 124 dfatal("Cannot mkdir %s\n", RepositoryPath); 125 } 126 127 BuildInitialized = 1; 128 129 /* 130 * slow-start (increases at a rate of 1 per 5 seconds) 131 */ 132 if (SlowStartOpt > MaxWorkers) 133 DynamicMaxWorkers = MaxWorkers; 134 else if (SlowStartOpt > 0) 135 DynamicMaxWorkers = SlowStartOpt; 136 else 137 DynamicMaxWorkers = MaxWorkers; 138 } 139 140 /* 141 * Called by the frontend to clean-up any hanging mounts. 142 */ 143 void 144 DoCleanBuild(int resetlogs) 145 { 146 int i; 147 148 ddassert(BuildInitialized); 149 150 if (resetlogs) 151 dlogreset(); 152 for (i = 0; i < MaxWorkers; ++i) { 153 DoWorkerUnmounts(&WorkerAry[i]); 154 } 155 } 156 157 void 158 DoBuild(pkg_t *pkgs) 159 { 160 pkg_t *build_list = NULL; 161 pkg_t **build_tail = &build_list; 162 pkg_t *scan; 163 bulk_t *bulk; 164 int haswork = 1; 165 int first = 1; 166 int newtemplate; 167 time_t startTime; 168 time_t t; 169 int h, m, s; 170 171 /* 172 * We use our bulk system to run hooks. This will be for 173 * Skipped and Ignored. Success and Failure are handled by 174 * WorkerProcess() which is a separately-exec'd dsynth. 175 */ 176 if (UsingHooks) 177 initbulk(childHookRun, MaxBulk); 178 179 /* 180 * Count up the packages, not counting dummies 181 */ 182 for (scan = pkgs; scan; scan = scan->bnext) { 183 if ((scan->flags & PKGF_DUMMY) == 0) 184 ++BuildTotal; 185 } 186 187 doHook(NULL, "hook_run_start", HookRunStart, 1); 188 189 /* 190 * The pkg and pkg-static binaries are needed. If already present 191 * then assume that the template is also valid, otherwise build 192 * both. 193 */ 194 scan = GetPkgPkg(pkgs); 195 196 /* 197 * Create our template. The template will be missing pkg 198 * and pkg-static. 199 */ 200 if ((scan->flags & (PKGF_SUCCESS | PKGF_PACKAGED)) == 0) { 201 /* force a fresh template */ 202 newtemplate = DoCreateTemplate(1); 203 } else { 204 newtemplate = DoCreateTemplate(0); 205 } 206 207 /* 208 * This will clear the screen and set-up our gui, so sleep 209 * a little first in case the user wants to see what was 210 * printed before. 211 */ 212 sleep(2); 213 pthread_mutex_lock(&WorkerMutex); 214 startTime = time(NULL); 215 RunStatsInit(); 216 RunStatsReset(); 217 218 /* 219 * Build pkg/pkg-static. 220 */ 221 if ((scan->flags & (PKGF_SUCCESS | PKGF_PACKAGED)) == 0) { 222 build_list = scan; 223 build_tail = &scan->build_next; 224 startbuild(&build_list, &build_tail); 225 while (RunningWorkers == 1) 226 waitbuild(1, 0); 227 228 if (scan->flags & PKGF_NOBUILD) 229 dfatal("Unable to build 'pkg'"); 230 if (scan->flags & PKGF_ERROR) 231 dfatal("Error building 'pkg'"); 232 if ((scan->flags & PKGF_SUCCESS) == 0) 233 dfatal("Error building 'pkg'"); 234 newtemplate = 1; 235 } 236 237 /* 238 * Install pkg/pkg-static into the template 239 */ 240 if (newtemplate) { 241 char *buf; 242 int rc; 243 244 asprintf(&buf, 245 "cd %s/Template; " 246 "tar --exclude '+*' --exclude '*/man/*' " 247 "-xvzpf %s/%s > /dev/null 2>&1", 248 BuildBase, 249 RepositoryPath, 250 scan->pkgfile); 251 rc = system(buf); 252 if (rc) 253 dfatal("Command failed: %s\n", buf); 254 freestrp(&buf); 255 } 256 257 /* 258 * Calculate depi_depth, the longest chain of dependencies 259 * for who depends on me, weighted by powers of two. 260 */ 261 for (scan = pkgs; scan; scan = scan->bnext) { 262 buildCalculateDepiDepth(scan); 263 } 264 265 /* 266 * Nominal bulk build sequence 267 */ 268 while (haswork) { 269 haswork = 0; 270 fflush(stdout); 271 for (scan = pkgs; scan; scan = scan->bnext) { 272 ddprintf(0, "SCANLEAVES %08x %s\n", 273 scan->flags, scan->portdir); 274 scan->flags |= PKGF_BUILDLOOP; 275 /* 276 * NOTE: We must still find dependencies if PACKAGED 277 * to fill in the gaps, as some of them may 278 * need to be rebuilt. 279 */ 280 if (scan->flags & (PKGF_SUCCESS | PKGF_FAILURE | 281 PKGF_ERROR)) { 282 #if 0 283 ddprintf(0, "%s: already built\n", 284 scan->portdir); 285 #endif 286 } else { 287 int ap = 0; 288 build_find_leaves(NULL, scan, &build_tail, 289 &ap, &haswork, 0, first, 0); 290 ddprintf(0, "TOPLEVEL %s %08x\n", 291 scan->portdir, ap); 292 } 293 scan->flags &= ~PKGF_BUILDLOOP; 294 build_clear_trav(scan); 295 } 296 first = 0; 297 fflush(stdout); 298 startbuild(&build_list, &build_tail); 299 300 if (haswork == 0 && RunningWorkers) { 301 waitbuild(RunningWorkers, 1); 302 haswork = 1; 303 } 304 } 305 pthread_mutex_unlock(&WorkerMutex); 306 307 RunStatsUpdateTop(); 308 RunStatsUpdateLogs(); 309 RunStatsSync(); 310 RunStatsDone(); 311 312 doHook(NULL, "hook_run_end", HookRunEnd, 1); 313 if (UsingHooks) { 314 while ((bulk = getbulk()) != NULL) 315 freebulk(bulk); 316 donebulk(); 317 } 318 319 t = time(NULL) - startTime; 320 h = t / 3600; 321 m = t / 60 % 60; 322 s = t % 60; 323 324 dlog(DLOG_ALL|DLOG_STDOUT, 325 "\n" 326 "Initial queue size: %d\n" 327 " packages built: %d\n" 328 " ignored: %d\n" 329 " skipped: %d\n" 330 " failed: %d\n" 331 "\n" 332 "Duration: %02d:%02d:%02d\n" 333 "\n", 334 BuildTotal, 335 BuildSuccessCount, 336 BuildIgnoreCount, 337 BuildSkipCount, 338 BuildFailCount, 339 h, m, s); 340 } 341 342 /* 343 * Traverse the packages (pkg) depends on recursively until we find 344 * a leaf to build or report as unbuildable. Calculates and assigns a 345 * dependency count. Returns all parallel-buildable packages. 346 * 347 * (pkg) itself is only added to the list if it is immediately buildable. 348 */ 349 static 350 int 351 build_find_leaves(pkg_t *parent, pkg_t *pkg, pkg_t ***build_tailp, 352 int *app, int *hasworkp, int depth, int first, 353 int first_one_only) 354 { 355 pkglink_t *link; 356 pkg_t *scan; 357 int idep_count = 0; 358 int apsub; 359 int dfirst_one_only; 360 int ndepth; 361 char *buf; 362 363 ndepth = depth + 1; 364 365 /* 366 * Already on build list, possibly in-progress, tell caller that 367 * it is not ready. 368 */ 369 ddprintf(ndepth, "sbuild_find_leaves %d %s %08x {\n", 370 depth, pkg->portdir, pkg->flags); 371 if (pkg->flags & PKGF_BUILDLIST) { 372 ddprintf(ndepth, "} (already on build list)\n"); 373 *app |= PKGF_NOTREADY; 374 return (pkg->idep_count); 375 } 376 377 /* 378 * Check dependencies 379 */ 380 PKGLIST_FOREACH(link, &pkg->idepon_list) { 381 scan = link->pkg; 382 383 if (scan == NULL) { 384 if (first_one_only) 385 break; 386 continue; 387 } 388 ddprintf(ndepth, "check %s %08x\t", scan->portdir, scan->flags); 389 390 /* 391 * If this dependency is to a DUMMY node it is a dependency 392 * only on the default flavor which is only the first node 393 * under this one, not all of them. 394 * 395 * NOTE: The depth is not being for complex dependency type 396 * tests like it is in childInstallPkgDeps_recurse(), 397 * so we don't have to hicup it like we do in that 398 * procedure. 399 */ 400 dfirst_one_only = (scan->flags & PKGF_DUMMY) ? 1 : 0; 401 402 /* 403 * When accounting for a successful build, just bump 404 * idep_count by one. scan->idep_count will heavily 405 * overlap packages that we count down multiple branches. 406 * 407 * We must still recurse through PACKAGED packages as 408 * some of their dependencies might be missing. 409 */ 410 if (scan->flags & PKGF_SUCCESS) { 411 ddprintf(0, "SUCCESS - OK\n"); 412 ++idep_count; 413 if (first_one_only) 414 break; 415 continue; 416 } 417 418 /* 419 * ERROR includes FAILURE, which is set in numerous situations 420 * including when NOBUILD state is processed. So check for 421 * NOBUILD state first. 422 * 423 * An ERROR in a sub-package causes a NOBUILD in packages 424 * that depend on it. 425 */ 426 if (scan->flags & PKGF_NOBUILD) { 427 ddprintf(0, "NOBUILD - OK " 428 "(propogate failure upward)\n"); 429 *app |= PKGF_NOBUILD_S; 430 if (first_one_only) 431 break; 432 continue; 433 } 434 if (scan->flags & PKGF_ERROR) { 435 ddprintf(0, "ERROR - OK (propogate failure upward)\n"); 436 *app |= PKGF_NOBUILD_S; 437 if (first_one_only) 438 break; 439 continue; 440 } 441 442 /* 443 * If already on build-list this dependency is not ready. 444 */ 445 if (scan->flags & PKGF_BUILDLIST) { 446 ddprintf(0, " [BUILDLIST]"); 447 *app |= PKGF_NOTREADY; 448 } 449 450 /* 451 * If not packaged this dependency is not ready for 452 * the caller. 453 */ 454 if ((scan->flags & PKGF_PACKAGED) == 0) { 455 ddprintf(0, " [NOT_PACKAGED]"); 456 *app |= PKGF_NOTREADY; 457 } 458 459 /* 460 * Reduce search complexity, if we have already processed 461 * scan in the traversal it will either already be on the 462 * build list or it will not be buildable. Either way 463 * the parent is not buildable. 464 */ 465 if (scan->flags & PKGF_BUILDTRAV) { 466 ddprintf(0, " [BUILDTRAV]\n"); 467 *app |= PKGF_NOTREADY; 468 if (first_one_only) 469 break; 470 continue; 471 } 472 473 /* 474 * Assert on dependency loop 475 */ 476 ++idep_count; 477 if (scan->flags & PKGF_BUILDLOOP) { 478 dfatal("pkg dependency loop %s -> %s", 479 parent->portdir, scan->portdir); 480 } 481 482 /* 483 * NOTE: For debug tabbing purposes we use (ndepth + 1) 484 * here (i.e. depth + 2) in our iteration. 485 */ 486 scan->flags |= PKGF_BUILDLOOP; 487 apsub = 0; 488 ddprintf(0, " SUBRECURSION {\n"); 489 idep_count += build_find_leaves(pkg, scan, build_tailp, 490 &apsub, hasworkp, 491 ndepth + 1, first, 492 dfirst_one_only); 493 scan->flags &= ~PKGF_BUILDLOOP; 494 *app |= apsub; 495 if (apsub & PKGF_NOBUILD) { 496 ddprintf(ndepth, "} (sub-nobuild)\n"); 497 } else if (apsub & PKGF_ERROR) { 498 ddprintf(ndepth, "} (sub-error)\n"); 499 } else if (apsub & PKGF_NOTREADY) { 500 ddprintf(ndepth, "} (sub-notready)\n"); 501 } else { 502 ddprintf(ndepth, "} (sub-ok)\n"); 503 } 504 if (first_one_only) 505 break; 506 } 507 pkg->idep_count = idep_count; 508 pkg->flags |= PKGF_BUILDTRAV; 509 510 /* 511 * Incorporate scan results into pkg state. 512 */ 513 if ((pkg->flags & PKGF_NOBUILD) == 0 && (*app & PKGF_NOBUILD)) { 514 *hasworkp = 1; 515 } else if ((pkg->flags & PKGF_ERROR) == 0 && (*app & PKGF_ERROR)) { 516 *hasworkp = 1; 517 } 518 pkg->flags |= *app & ~PKGF_NOTREADY; 519 520 /* 521 * Clear PACKAGED bit if sub-dependencies aren't clean 522 */ 523 if ((pkg->flags & PKGF_PACKAGED) && 524 (pkg->flags & (PKGF_NOTREADY|PKGF_ERROR|PKGF_NOBUILD))) { 525 pkg->flags &= ~PKGF_PACKAGED; 526 ddassert(pkg->pkgfile); 527 asprintf(&buf, "%s/%s", RepositoryPath, pkg->pkgfile); 528 if (remove(buf) < 0) { 529 dlog(DLOG_ALL, 530 "[XXX] %s DELETE-PACKAGE %s (failed)\n", 531 pkg->portdir, buf); 532 } else { 533 dlog(DLOG_ALL, 534 "[XXX] %s DELETE-PACKAGE %s " 535 "(due to dependencies)\n", 536 pkg->portdir, buf); 537 } 538 freestrp(&buf); 539 *hasworkp = 1; 540 } 541 542 /* 543 * Set PKGF_NOBUILD_I if there is IGNORE data 544 */ 545 if (pkg->ignore) 546 pkg->flags |= PKGF_NOBUILD_I; 547 548 /* 549 * Handle propagated flags 550 */ 551 if (pkg->flags & PKGF_ERROR) { 552 /* 553 * This can only happen if the ERROR has already been 554 * processed and accounted for. 555 */ 556 ddprintf(depth, "} (ERROR - %s)\n", pkg->portdir); 557 } else if (*app & PKGF_NOTREADY) { 558 /* 559 * We don't set PKGF_NOTREADY in the pkg, it is strictly 560 * a transient flag propagated via build_find_leaves(). 561 * 562 * Just don't add the package to the list. 563 * 564 * NOTE: Even if NOBUILD is set (meaning we could list it 565 * and let startbuild() finish it up as a skip, we 566 * don't process it to the list because we want to 567 * process all the dependencies, so someone doing a 568 * manual build can get more complete information and 569 * does not have to iterate each failed dependency one 570 * at a time. 571 */ 572 ; 573 } else if (pkg->flags & PKGF_SUCCESS) { 574 ddprintf(depth, "} (SUCCESS - %s)\n", pkg->portdir); 575 } else if (pkg->flags & PKGF_DUMMY) { 576 /* 577 * Just mark dummy packages as successful when all of their 578 * sub-depends (flavors) complete successfully. Note that 579 * dummy packages are not counted in the total, so do not 580 * decrement BuildTotal. 581 */ 582 ddprintf(depth, "} (DUMMY/META - SUCCESS)\n"); 583 pkg->flags |= PKGF_SUCCESS; 584 *hasworkp = 1; 585 if (first) { 586 dlog(DLOG_ALL | DLOG_FILTER, 587 "[XXX] %s META-ALREADY-BUILT\n", 588 pkg->portdir); 589 } else { 590 dlog(DLOG_SUCC, "[XXX] %s meta-node complete\n", 591 pkg->portdir); 592 } 593 } else if (pkg->flags & PKGF_PACKAGED) { 594 /* 595 * We can just mark the pkg successful. If this is 596 * the first pass, we count this as an initial pruning 597 * pass and reduce BuildTotal. 598 */ 599 ddprintf(depth, "} (PACKAGED - SUCCESS)\n"); 600 pkg->flags |= PKGF_SUCCESS; 601 *hasworkp = 1; 602 if (first) { 603 dlog(DLOG_ALL | DLOG_FILTER, 604 "[XXX] %s ALREADY-BUILT\n", 605 pkg->portdir); 606 --BuildTotal; 607 } 608 } else { 609 /* 610 * All dependencies are successful, queue new work 611 * and indicate not-ready to the parent (since our 612 * package has to be built). 613 * 614 * NOTE: The NOBUILD case propagates to here as well 615 * and is ultimately handled by startbuild(). 616 */ 617 *hasworkp = 1; 618 if (pkg->flags & PKGF_NOBUILD_I) 619 ddprintf(depth, "} (ADDLIST(IGNORE/BROKEN) - %s)\n", 620 pkg->portdir); 621 else if (pkg->flags & PKGF_NOBUILD) 622 ddprintf(depth, "} (ADDLIST(NOBUILD) - %s)\n", 623 pkg->portdir); 624 else 625 ddprintf(depth, "} (ADDLIST - %s)\n", pkg->portdir); 626 pkg->flags |= PKGF_BUILDLIST; 627 **build_tailp = pkg; 628 *build_tailp = &pkg->build_next; 629 *app |= PKGF_NOTREADY; 630 } 631 632 return idep_count; 633 } 634 635 static 636 void 637 build_clear_trav(pkg_t *pkg) 638 { 639 pkglink_t *link; 640 pkg_t *scan; 641 642 pkg->flags &= ~PKGF_BUILDTRAV; 643 PKGLIST_FOREACH(link, &pkg->idepon_list) { 644 scan = link->pkg; 645 if (scan && (scan->flags & PKGF_BUILDTRAV)) 646 build_clear_trav(scan); 647 } 648 } 649 650 /* 651 * Calculate the longest chain of packages that depend on me. The 652 * long the chain, the more important my package is to build earlier 653 * rather than later. 654 */ 655 static int 656 buildCalculateDepiDepth(pkg_t *pkg) 657 { 658 pkglink_t *link; 659 pkg_t *scan; 660 int best_depth = 0; 661 int res; 662 663 if (pkg->depi_depth) 664 return(pkg->depi_depth + 1); 665 pkg->flags |= PKGF_BUILDLOOP; 666 PKGLIST_FOREACH(link, &pkg->deponi_list) { 667 scan = link->pkg; 668 if (scan && (scan->flags & PKGF_BUILDLOOP) == 0) { 669 res = buildCalculateDepiDepth(scan); 670 if (best_depth < res) 671 best_depth = res; 672 } 673 } 674 pkg->flags &= ~PKGF_BUILDLOOP; 675 pkg->depi_depth = best_depth; 676 677 return (best_depth + 1); 678 } 679 680 /* 681 * Take a list of pkg ready to go, sort it, and assign it to worker 682 * slots. This routine blocks in waitbuild() until it can dispose of 683 * the entire list. 684 * 685 * WorkerMutex is held by the caller. 686 */ 687 static 688 void 689 startbuild(pkg_t **build_listp, pkg_t ***build_tailp) 690 { 691 pkg_t *pkg; 692 pkg_t **idep_ary; 693 pkg_t **depi_ary; 694 int count; 695 int idep_index; 696 int depi_index; 697 int i; 698 int n; 699 worker_t *work; 700 static int IterateWorker; 701 702 /* 703 * Nothing to do 704 */ 705 if (*build_listp == NULL) 706 return; 707 708 /* 709 * Sort 710 */ 711 count = 0; 712 for (pkg = *build_listp; pkg; pkg = pkg->build_next) 713 ++count; 714 idep_ary = calloc(count, sizeof(pkg_t *)); 715 depi_ary = calloc(count, sizeof(pkg_t *)); 716 717 count = 0; 718 for (pkg = *build_listp; pkg; pkg = pkg->build_next) { 719 idep_ary[count] = pkg; 720 depi_ary[count] = pkg; 721 ++count; 722 } 723 724 /* 725 * idep_ary - sorted by #of dependencies this pkg has. 726 * depi_ary - sorted by #of other packages that depend on this pkg. 727 */ 728 qsort(idep_ary, count, sizeof(pkg_t *), qsort_idep); 729 qsort(depi_ary, count, sizeof(pkg_t *), qsort_depi); 730 731 idep_index = 0; 732 depi_index = 0; 733 734 /* 735 * Half the workers build based on the highest depi count, 736 * the other half build based on the highest idep count. 737 * 738 * This is an attempt to get projects which many other projects 739 * depend on built first, but to also try to build large projects 740 * (which tend to have a lot of dependencies) earlier rather than 741 * later so the end of the bulk run doesn't inefficiently build 742 * the last few huge projects. 743 * 744 * Loop until we manage to assign slots to everyone. We do not 745 * wait for build completion. 746 * 747 * This is the point where we handle DUMMY packages (these are 748 * dummy unflavored packages which 'cover' all the flavors for 749 * a package). These are not real packages are marked SUCCESS 750 * at this time because their dependencies (the flavors) have all 751 * been built. 752 */ 753 while (idep_index != count || depi_index != count) { 754 pkg_t *pkgi; 755 pkg_t *ipkg; 756 757 /* 758 * Find candidate to start sorted by depi or idep. 759 */ 760 ipkg = NULL; 761 while (idep_index < count) { 762 ipkg = idep_ary[idep_index]; 763 if ((ipkg->flags & 764 (PKGF_SUCCESS | PKGF_FAILURE | 765 PKGF_ERROR | PKGF_RUNNING)) == 0) { 766 break; 767 } 768 ipkg = NULL; 769 ++idep_index; 770 } 771 772 pkgi = NULL; 773 while (depi_index < count) { 774 pkgi = depi_ary[depi_index]; 775 if ((pkgi->flags & 776 (PKGF_SUCCESS | PKGF_FAILURE | 777 PKGF_ERROR | PKGF_RUNNING)) == 0) { 778 break; 779 } 780 pkgi = NULL; 781 ++depi_index; 782 } 783 784 /* 785 * ipkg and pkgi must either both be NULL, or both 786 * be non-NULL. 787 */ 788 if (ipkg == NULL && pkgi == NULL) 789 break; 790 ddassert(ipkg && pkgi); 791 792 /* 793 * Handle the NOBUILD case right here, there's no point 794 * queueing it anywhere. 795 */ 796 if (ipkg->flags & PKGF_NOBUILD) { 797 char *reason; 798 799 ipkg->flags |= PKGF_FAILURE; 800 ipkg->flags &= ~PKGF_BUILDLIST; 801 802 reason = buildskipreason(NULL, ipkg); 803 if (ipkg->flags & PKGF_NOBUILD_I) { 804 ++BuildIgnoreCount; 805 dlog(DLOG_IGN, "[XXX] %s ignored due to %s\n", 806 ipkg->portdir, reason); 807 doHook(ipkg, "hook_pkg_ignored", 808 HookPkgIgnored, 0); 809 } else { 810 ++BuildSkipCount; 811 dlog(DLOG_SKIP, "[XXX] %s skipped due to %s\n", 812 ipkg->portdir, reason); 813 doHook(ipkg, "hook_pkg_skipped", 814 HookPkgSkipped, 0); 815 } 816 free(reason); 817 ++BuildCount; 818 continue; 819 } 820 if (pkgi->flags & PKGF_NOBUILD) { 821 char *reason; 822 823 pkgi->flags |= PKGF_FAILURE; 824 pkgi->flags &= ~PKGF_BUILDLIST; 825 826 reason = buildskipreason(NULL, pkgi); 827 if (pkgi->flags & PKGF_NOBUILD_I) { 828 ++BuildIgnoreCount; 829 dlog(DLOG_IGN, "[XXX] %s ignored due to %s\n", 830 pkgi->portdir, reason); 831 doHook(pkgi, "hook_pkg_ignored", 832 HookPkgIgnored, 0); 833 } else { 834 ++BuildSkipCount; 835 dlog(DLOG_SKIP, "[XXX] %s skipped due to %s\n", 836 pkgi->portdir, reason); 837 doHook(pkgi, "hook_pkg_skipped", 838 HookPkgSkipped, 0); 839 } 840 free(reason); 841 ++BuildCount; 842 continue; 843 } 844 845 /* 846 * Block while no slots are available. waitbuild() 847 * will clean out any DONE states. 848 */ 849 while (RunningWorkers >= DynamicMaxWorkers || 850 RunningWorkers >= MaxWorkers - FailedWorkers) { 851 waitbuild(RunningWorkers, 1); 852 } 853 854 /* 855 * Find an available worker slot, there should be at 856 * least one. 857 */ 858 for (i = 0; i < MaxWorkers; ++i) { 859 n = IterateWorker % MaxWorkers; 860 work = &WorkerAry[n]; 861 862 if (work->state == WORKER_DONE || 863 work->state == WORKER_FAILED) { 864 workercomplete(work); 865 } 866 if (work->state == WORKER_NONE || 867 work->state == WORKER_IDLE) { 868 if (n <= MaxWorkers / 2) { 869 startworker(pkgi, work); 870 } else { 871 startworker(ipkg, work); 872 } 873 /*RunStatsUpdate(work);*/ 874 break; 875 } 876 ++IterateWorker; 877 } 878 ddassert(i != MaxWorkers); 879 } 880 RunStatsSync(); 881 882 /* 883 * We disposed of the whole list 884 */ 885 free(idep_ary); 886 free(depi_ary); 887 *build_listp = NULL; 888 *build_tailp = build_listp; 889 } 890 891 typedef const pkg_t *pkg_tt; 892 893 static int 894 qsort_idep(const void *pkg1_arg, const void *pkg2_arg) 895 { 896 const pkg_t *pkg1 = *(const pkg_tt *)pkg1_arg; 897 const pkg_t *pkg2 = *(const pkg_tt *)pkg2_arg; 898 899 return (pkg2->idep_count - pkg1->idep_count); 900 } 901 902 static int 903 qsort_depi(const void *pkg1_arg, const void *pkg2_arg) 904 { 905 const pkg_t *pkg1 = *(const pkg_tt *)pkg1_arg; 906 const pkg_t *pkg2 = *(const pkg_tt *)pkg2_arg; 907 908 return ((pkg2->depi_count * pkg2->depi_depth) - 909 (pkg1->depi_count * pkg1->depi_depth)); 910 } 911 912 /* 913 * Frontend starts a pkg up on a worker 914 * 915 * WorkerMutex must be held. 916 */ 917 static void 918 startworker(pkg_t *pkg, worker_t *work) 919 { 920 switch(work->state) { 921 case WORKER_NONE: 922 pthread_create(&work->td, NULL, childBuilderThread, work); 923 work->state = WORKER_IDLE; 924 /* fall through */ 925 case WORKER_IDLE: 926 work->pkg_dep_size = 927 childInstallPkgDeps_recurse(NULL, &pkg->idepon_list, 0, 1, 0); 928 childInstallPkgDeps_recurse(NULL, &pkg->idepon_list, 1, 1, 0); 929 RunningPkgDepSize += work->pkg_dep_size; 930 931 dlog(DLOG_ALL, "[%03d] START %s " 932 "##idep=%02d depi=%02d/%02d dep=%-4.2fG\n", 933 work->index, pkg->portdir, 934 pkg->idep_count, pkg->depi_count, pkg->depi_depth, 935 (double)work->pkg_dep_size / (double)ONEGB); 936 937 cleanworker(work); 938 pkg->flags |= PKGF_RUNNING; 939 work->pkg = pkg; 940 pthread_cond_signal(&work->cond); 941 ++RunningWorkers; 942 /*RunStatsUpdate(work);*/ 943 break; 944 case WORKER_PENDING: 945 case WORKER_RUNNING: 946 case WORKER_DONE: 947 case WORKER_FAILED: 948 case WORKER_FROZEN: 949 case WORKER_EXITING: 950 default: 951 dfatal("startworker: [%03d] Unexpected state %d for worker %d", 952 work->index, work->state, work->index); 953 break; 954 } 955 } 956 957 static void 958 cleanworker(worker_t *work) 959 { 960 work->state = WORKER_PENDING; 961 work->flags = 0; 962 work->accum_error = 0; 963 work->start_time = time(NULL); 964 } 965 966 /* 967 * Frontend finishes up a completed pkg on a worker. 968 * 969 * If the worker is in a FAILED state we clean the pkg out but (for now) 970 * leave it in its failed state so we can debug. At this point 971 * workercomplete() will be called every once in a while on the state 972 * and we have to deal with the NULL pkg. 973 * 974 * WorkerMutex must be held. 975 */ 976 static void 977 workercomplete(worker_t *work) 978 { 979 pkg_t *pkg; 980 time_t t; 981 int h; 982 int m; 983 int s; 984 985 /* 986 * Steady state FAILED case. 987 */ 988 if (work->state == WORKER_FAILED) { 989 if (work->pkg == NULL) 990 return; 991 } 992 993 t = time(NULL) - work->start_time; 994 h = t / 3600; 995 m = t / 60 % 60; 996 s = t % 60; 997 998 /* 999 * Reduce total dep size 1000 */ 1001 RunningPkgDepSize -= work->pkg_dep_size; 1002 RunningPkgDepSize -= work->memuse; 1003 work->pkg_dep_size = 0; 1004 work->memuse = 0; 1005 1006 /* 1007 * Process pkg out of the worker 1008 */ 1009 pkg = work->pkg; 1010 if (pkg->flags & (PKGF_ERROR|PKGF_NOBUILD)) { 1011 pkg->flags |= PKGF_FAILURE; 1012 1013 /* 1014 * This NOBUILD condition XXX can occur if the package is 1015 * not allowed to be built. 1016 */ 1017 if (pkg->flags & PKGF_NOBUILD) { 1018 char *reason; 1019 1020 reason = buildskipreason(NULL, pkg); 1021 if (pkg->flags & PKGF_NOBUILD_I) { 1022 ++BuildIgnoreCount; 1023 dlog(DLOG_SKIP, "[%03d] IGNORD %s - %s\n", 1024 work->index, pkg->portdir, reason); 1025 doHook(pkg, "hook_pkg_ignored", 1026 HookPkgIgnored, 0); 1027 } else { 1028 ++BuildSkipCount; 1029 dlog(DLOG_SKIP, "[%03d] SKIPPD %s - %s\n", 1030 work->index, pkg->portdir, reason); 1031 doHook(pkg, "hook_pkg_skipped", 1032 HookPkgSkipped, 0); 1033 } 1034 free(reason); 1035 } else { 1036 char skipbuf[16]; 1037 int scount; 1038 1039 scount = buildskipcount_dueto(pkg, 1); 1040 buildskipcount_dueto(pkg, 0); 1041 if (scount) 1042 snprintf(skipbuf, sizeof(skipbuf), " (%d)", scount); 1043 else 1044 skipbuf[0] = 0; 1045 1046 ++BuildFailCount; 1047 dlog(DLOG_FAIL | DLOG_RED, 1048 "[%03d] FAILURE %s%s ##%16.16s %02d:%02d:%02d\n", 1049 work->index, pkg->portdir, skipbuf, 1050 getphasestr(work->phase), 1051 h, m, s); 1052 doHook(pkg, "hook_pkg_failure", HookPkgFailure, 0); 1053 } 1054 } else { 1055 pkg->flags |= PKGF_SUCCESS; 1056 ++BuildSuccessCount; 1057 dlog(DLOG_SUCC | DLOG_GRN, 1058 "[%03d] SUCCESS %s ##%02d:%02d:%02d\n", 1059 work->index, pkg->portdir, h, m, s); 1060 doHook(pkg, "hook_pkg_success", HookPkgSuccess, 0); 1061 } 1062 ++BuildCount; 1063 pkg->flags &= ~PKGF_BUILDLIST; 1064 pkg->flags &= ~PKGF_RUNNING; 1065 work->pkg = NULL; 1066 --RunningWorkers; 1067 1068 if (work->state == WORKER_FAILED) { 1069 dlog(DLOG_ALL, "[%03d] XXX/XXX WORKER IS IN A FAILED STATE\n", 1070 work->index); 1071 ++FailedWorkers; 1072 } else if (work->flags & WORKERF_FREEZE) { 1073 dlog(DLOG_ALL, "[%03d] FROZEN(DEBUG) %s\n", 1074 work->index, pkg->portdir); 1075 work->state = WORKER_FROZEN; 1076 } else { 1077 work->state = WORKER_IDLE; 1078 } 1079 } 1080 1081 /* 1082 * Wait for one or more workers to complete. 1083 * 1084 * WorkerMutex must be held. 1085 */ 1086 static void 1087 waitbuild(int whilematch, int dynamicmax) 1088 { 1089 static time_t wblast_time; 1090 static time_t dmlast_time; 1091 struct timespec ts; 1092 worker_t *work; 1093 time_t t; 1094 int i; 1095 1096 if (whilematch == 0) 1097 whilematch = 1; 1098 1099 while (RunningWorkers == whilematch) { 1100 for (i = 0; i < MaxWorkers; ++i) { 1101 work = &WorkerAry[i]; 1102 if (work->state == WORKER_DONE || 1103 work->state == WORKER_FAILED) { 1104 workercomplete(work); 1105 } else { 1106 pthread_cond_signal(&work->cond); 1107 } 1108 RunStatsUpdate(work, NULL); 1109 } 1110 RunStatsUpdateTop(); 1111 RunStatsUpdateLogs(); 1112 RunStatsSync(); 1113 if (RunningWorkers == whilematch) { 1114 clock_gettime(CLOCK_REALTIME, &ts); 1115 ts.tv_sec += 1; 1116 ts.tv_nsec = 0; 1117 pthread_cond_timedwait(&WorkerCond, &WorkerMutex, &ts); 1118 } 1119 1120 /* 1121 * Dynamically reduce MaxWorkers based on the load. When 1122 * the load exceeds 2 x ncpus we reduce the number of workers 1123 * up to 75% of MaxWorkers @ (5 x ncpus) load. 1124 * 1125 * Dynamically reduce MaxWorkers based on swap use, starting 1126 * at 10% swap and up to 75% of MaxWorkers at 40% swap. 1127 * 1128 * NOTE! Generally speaking this allows more workers to be 1129 * configured which helps in two ways. First it allows 1130 * a higher build rate for smaller packages. Second 1131 * it allows dsynth to ratchet-down the number of slots 1132 * when large packages are forcing the load up. 1133 * 1134 * A high load doesn't hurt efficiency, but swap usage 1135 * due to loading the tmpfs in lots of worker slots up 1136 * with tons of pkg install's (pre-reqs for a build) 1137 * does. Reducing the number of worker slots has a 1138 * huge beneficial effect on reducing swap use / paging. 1139 */ 1140 t = time(NULL); 1141 if (dynamicmax && (wblast_time == 0 || 1142 (unsigned)(t - wblast_time) >= 5)) { 1143 double min_load = 1.5 * NumCores; 1144 double max_load = 5.0 * NumCores; 1145 double min_swap = 0.10; 1146 double max_swap = 0.40; 1147 double dload[3]; 1148 double dswap; 1149 int max1; 1150 int max2; 1151 int max3; 1152 int max_sel; 1153 int noswap; 1154 1155 wblast_time = t; 1156 1157 /* 1158 * Cap based on load. This is back-loaded. 1159 */ 1160 getloadavg(dload, 3); 1161 if (dload[0] < min_load) { 1162 max1 = MaxWorkers; 1163 } else if (dload[0] <= max_load) { 1164 max1 = MaxWorkers - 1165 MaxWorkers * 0.75 * 1166 (dload[0] - min_load) / 1167 (max_load - min_load); 1168 } else { 1169 max1 = MaxWorkers * 25 / 100; 1170 } 1171 1172 /* 1173 * Cap based on swap use. This is back-loaded. 1174 */ 1175 dswap = getswappct(&noswap); 1176 if (dswap < min_swap) { 1177 max2 = MaxWorkers; 1178 } else if (dswap <= max_swap) { 1179 max2 = MaxWorkers - 1180 MaxWorkers * 0.75 * 1181 (dswap - min_swap) / 1182 (max_swap - min_swap); 1183 } else { 1184 max2 = MaxWorkers * 25 / 100; 1185 } 1186 1187 /* 1188 * Cap based on aggregate pkg-dependency memory 1189 * use installed in worker slots. This is 1190 * front-loaded. 1191 * 1192 * Since it can take a while for workers to retire 1193 * (to reduce RunningPkgDepSize), just set our 1194 * target 1 below the current run count to allow 1195 * jobs to retire without being replaced with new 1196 * jobs. 1197 * 1198 * In addition, in order to avoid a paging 'shock', 1199 * We enforce a 30 second-per-increment slow-start 1200 * once RunningPkgDepSize exceeds 1/2 the target. 1201 */ 1202 if (RunningPkgDepSize > PkgDepMemoryTarget) { 1203 max3 = RunningWorkers - 1; 1204 } else if (RunningPkgDepSize > PkgDepMemoryTarget / 2) { 1205 if (dmlast_time == 0 || 1206 (unsigned)(t - dmlast_time) >= 30) { 1207 dmlast_time = t; 1208 max3 = RunningWorkers + 1; 1209 } else { 1210 max3 = RunningWorkers; 1211 } 1212 } else { 1213 max3 = MaxWorkers; 1214 } 1215 1216 /* 1217 * Priority reduction, convert to DynamicMaxWorkers 1218 */ 1219 max_sel = max1; 1220 if (max_sel > max2) 1221 max_sel = max2; 1222 if (max_sel > max3) 1223 max_sel = max3; 1224 1225 /* 1226 * Restrict to allowed range, and also handle 1227 * slow-start. 1228 */ 1229 if (max_sel < 1) 1230 max_sel = 1; 1231 if (max_sel > DynamicMaxWorkers + 1) 1232 max_sel = DynamicMaxWorkers + 1; 1233 if (max_sel > MaxWorkers) 1234 max_sel = MaxWorkers; 1235 1236 /* 1237 * Stop waiting if DynamicMaxWorkers is going to 1238 * increase. 1239 */ 1240 if (DynamicMaxWorkers < max1) 1241 whilematch = -1; 1242 1243 /* 1244 * And adjust 1245 */ 1246 if (DynamicMaxWorkers != max1) { 1247 dlog(DLOG_ALL | DLOG_FILTER, 1248 "[XXX] Load=%-6.2f(%2d) " 1249 "Swap=%-3.2f%%(%2d) " 1250 "Mem=%3.2fG(%2d) " 1251 "Adjust Workers %d->%d\n", 1252 dload[0], max1, 1253 dswap * 100.0, max2, 1254 RunningPkgDepSize / (double)ONEGB, max3, 1255 DynamicMaxWorkers, max_sel); 1256 DynamicMaxWorkers = max_sel; 1257 } 1258 } 1259 } 1260 } 1261 1262 1263 /* 1264 * Worker pthread (WorkerAry) 1265 * 1266 * This thread belongs to the dsynth master process and handled a worker slot. 1267 * (this_thread) -> WORKER fork/exec (WorkerPocess) -> (pty) -> sub-processes. 1268 */ 1269 static void * 1270 childBuilderThread(void *arg) 1271 { 1272 char *envary[1] = { NULL }; 1273 worker_t *work = arg; 1274 wmsg_t wmsg; 1275 pkg_t *pkg; 1276 pid_t pid; 1277 int status; 1278 volatile int dowait; 1279 char slotbuf[8]; 1280 char fdbuf[8]; 1281 char flagsbuf[16]; 1282 1283 pthread_mutex_lock(&WorkerMutex); 1284 while (work->terminate == 0) { 1285 dowait = 1; 1286 1287 switch(work->state) { 1288 case WORKER_IDLE: 1289 break; 1290 case WORKER_PENDING: 1291 /* 1292 * Fork the management process for the pkg operation 1293 * on this worker slot. 1294 * 1295 * This process will set up the environment, do the 1296 * mounts, will become the reaper, and will process 1297 * pipe commands and handle chroot operations. 1298 * 1299 * NOTE: If SOCK_CLOEXEC is not supported WorkerMutex 1300 * is sufficient to interlock F_SETFD FD_CLOEXEC 1301 * operations. 1302 */ 1303 ddassert(work->pkg); 1304 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 1305 PF_UNSPEC, work->fds)) { 1306 dfatal_errno("socketpair() during worker fork"); 1307 } 1308 snprintf(slotbuf, sizeof(slotbuf), 1309 "%d", work->index); 1310 snprintf(fdbuf, sizeof(fdbuf), 1311 "3"); 1312 snprintf(flagsbuf, sizeof(flagsbuf), 1313 "%d", WorkerProcFlags); 1314 1315 /* 1316 * fds[0] - master 1317 * fds[1] - slave 1318 * 1319 * We pass the salve descriptor in fd 3 and close all 1320 * other descriptors for security. 1321 */ 1322 pthread_mutex_unlock(&WorkerMutex); 1323 pid = vfork(); 1324 if (pid == 0) { 1325 close(work->fds[0]); 1326 dup2(work->fds[1], 3); 1327 closefrom(4); 1328 fcntl(3, F_SETFD, 0); 1329 execle(DSynthExecPath, DSynthExecPath, 1330 "WORKER", slotbuf, fdbuf, 1331 work->pkg->portdir, work->pkg->pkgfile, 1332 flagsbuf, 1333 NULL, envary); 1334 write(2, "EXECLE FAILURE\n", 15); 1335 _exit(1); 1336 } 1337 pthread_mutex_lock(&WorkerMutex); 1338 close(work->fds[1]); 1339 work->phase = PHASE_PENDING; 1340 work->lines = 0; 1341 work->memuse = 0; 1342 work->pid = pid; 1343 work->state = WORKER_RUNNING; 1344 /* fall through */ 1345 case WORKER_RUNNING: 1346 /* 1347 * Poll for status updates, if NULL is returned 1348 * and status is non-zero, the communications link 1349 * failed unexpectedly. 1350 */ 1351 pkg = work->pkg; 1352 pthread_mutex_unlock(&WorkerMutex); 1353 status = ipcreadmsg(work->fds[0], &wmsg); 1354 pthread_mutex_lock(&WorkerMutex); 1355 1356 if (status == 0) { 1357 /* 1358 * Normal message, can include normal 1359 * termination which changes us over 1360 * to another state. 1361 */ 1362 dowait = 0; 1363 switch(wmsg.cmd) { 1364 case WMSG_CMD_INSTALL_PKGS: 1365 wmsg.cmd = WMSG_RES_INSTALL_PKGS; 1366 wmsg.status = childInstallPkgDeps(work); 1367 pthread_mutex_unlock(&WorkerMutex); 1368 ipcwritemsg(work->fds[0], &wmsg); 1369 pthread_mutex_lock(&WorkerMutex); 1370 break; 1371 case WMSG_CMD_STATUS_UPDATE: 1372 work->phase = wmsg.phase; 1373 work->lines = wmsg.lines; 1374 if (work->memuse != wmsg.memuse) { 1375 RunningPkgDepSize += 1376 wmsg.memuse - work->memuse; 1377 work->memuse = wmsg.memuse; 1378 } 1379 break; 1380 case WMSG_CMD_SUCCESS: 1381 work->flags |= WORKERF_SUCCESS; 1382 break; 1383 case WMSG_CMD_FAILURE: 1384 work->flags |= WORKERF_FAILURE; 1385 break; 1386 case WMSG_CMD_FREEZEWORKER: 1387 work->flags |= WORKERF_FREEZE; 1388 break; 1389 default: 1390 break; 1391 } 1392 RunStatsUpdate(work, NULL); 1393 RunStatsSync(); 1394 } else { 1395 close(work->fds[0]); 1396 pthread_mutex_unlock(&WorkerMutex); 1397 while (waitpid(work->pid, &status, 0) < 0 && 1398 errno == EINTR) { 1399 ; 1400 } 1401 pthread_mutex_lock(&WorkerMutex); 1402 1403 if (work->flags & WORKERF_SUCCESS) { 1404 pkg->flags |= PKGF_SUCCESS; 1405 work->state = WORKER_DONE; 1406 } else if (work->flags & WORKERF_FAILURE) { 1407 pkg->flags |= PKGF_FAILURE; 1408 work->state = WORKER_DONE; 1409 } else { 1410 pkg->flags |= PKGF_FAILURE; 1411 work->state = WORKER_FAILED; 1412 } 1413 work->flags |= WORKERF_STATUS_UPDATE; 1414 pthread_cond_signal(&WorkerCond); 1415 } 1416 break; 1417 case WORKER_DONE: 1418 /* 1419 * pkg remains attached until frontend processes the 1420 * completion. The frontend will then set the state 1421 * back to idle. 1422 */ 1423 break; 1424 case WORKER_FAILED: 1425 /* 1426 * A worker failure means that the worker did not 1427 * send us a WMSG_CMD_SUCCESS or WMSG_CMD_FAILURE 1428 * ipc before terminating. 1429 * 1430 * We just sit in this state until the front-end 1431 * does something about it. 1432 */ 1433 break; 1434 default: 1435 dfatal("worker: [%03d] Unexpected state %d for worker %d", 1436 work->index, work->state, work->index); 1437 /* NOT REACHED */ 1438 break; 1439 } 1440 1441 /* 1442 * The dsynth frontend will poll us approximately once 1443 * a second (its variable). 1444 */ 1445 if (dowait) 1446 pthread_cond_wait(&work->cond, &WorkerMutex); 1447 } 1448 1449 /* 1450 * Scrap the comm socket if running, this should cause the worker 1451 * process to kill its sub-programs and cleanup. 1452 */ 1453 if (work->state == WORKER_RUNNING) { 1454 pthread_mutex_unlock(&WorkerMutex); 1455 close(work->fds[0]); 1456 while (waitpid(work->pid, &status, 0) < 0 && 1457 errno == EINTR); 1458 pthread_mutex_lock(&WorkerMutex); 1459 } 1460 1461 /* 1462 * Final handshake 1463 */ 1464 work->state = WORKER_EXITING; 1465 pthread_cond_signal(&WorkerCond); 1466 pthread_mutex_unlock(&WorkerMutex); 1467 1468 return NULL; 1469 } 1470 1471 /* 1472 * Install all the binary packages (we have already built them) that 1473 * the current work package depends on, without duplicates, in a script 1474 * which will be run from within the specified work jail. 1475 * 1476 * Locked by WorkerMutex (global) 1477 */ 1478 static int 1479 childInstallPkgDeps(worker_t *work) 1480 { 1481 char *buf; 1482 FILE *fp; 1483 1484 if (PKGLIST_EMPTY(&work->pkg->idepon_list)) 1485 return 0; 1486 1487 asprintf(&buf, "%s/tmp/dsynth_install_pkgs", work->basedir); 1488 fp = fopen(buf, "w"); 1489 ddassert(fp != NULL); 1490 fprintf(fp, "#!/bin/sh\n"); 1491 fprintf(fp, "#\n"); 1492 fchmod(fileno(fp), 0755); 1493 1494 childInstallPkgDeps_recurse(fp, &work->pkg->idepon_list, 0, 1, 0); 1495 childInstallPkgDeps_recurse(fp, &work->pkg->idepon_list, 1, 1, 0); 1496 fprintf(fp, "\nexit 0\n"); 1497 fclose(fp); 1498 freestrp(&buf); 1499 1500 return 1; 1501 } 1502 1503 static size_t 1504 childInstallPkgDeps_recurse(FILE *fp, pkglink_t *list, int undoit, 1505 int depth, int first_one_only) 1506 { 1507 pkglink_t *link; 1508 pkg_t *pkg; 1509 size_t tot = 0; 1510 int ndepth; 1511 int nfirst; 1512 1513 PKGLIST_FOREACH(link, list) { 1514 pkg = link->pkg; 1515 1516 /* 1517 * We don't want to mess up our depth test just below if 1518 * a DUMMY node had to be inserted. The nodes under the 1519 * dummy node. 1520 * 1521 * The elements under a dummy node represent all the flabor, 1522 * a dependency that directly references a dummy node only 1523 * uses the first flavor (first_one_only / nfirst). 1524 */ 1525 ndepth = (pkg->flags & PKGF_DUMMY) ? depth : depth + 1; 1526 nfirst = (pkg->flags & PKGF_DUMMY) ? 1 : 0; 1527 1528 /* 1529 * We only need all packages for the top-level dependencies. 1530 * The deeper ones only need DEP_TYPE_LIB and DEP_TYPE_RUN 1531 * (types greater than DEP_TYPE_BUILD) since they are already 1532 * built. 1533 */ 1534 if (depth > 1 && link->dep_type <= DEP_TYPE_BUILD) { 1535 if (first_one_only) 1536 break; 1537 continue; 1538 } 1539 1540 if (undoit) { 1541 if (pkg->dsynth_install_flg == 1) { 1542 pkg->dsynth_install_flg = 0; 1543 tot += childInstallPkgDeps_recurse(fp, 1544 &pkg->idepon_list, 1545 undoit, 1546 ndepth, nfirst); 1547 } 1548 if (first_one_only) 1549 break; 1550 continue; 1551 } 1552 if (pkg->dsynth_install_flg) { 1553 if (DebugOpt >= 2 && pkg->pkgfile && fp) { 1554 fprintf(fp, "echo 'AlreadyHave %s'\n", 1555 pkg->pkgfile); 1556 } 1557 if (first_one_only) 1558 break; 1559 continue; 1560 } 1561 1562 tot += childInstallPkgDeps_recurse(fp, &pkg->idepon_list, 1563 undoit, ndepth, nfirst); 1564 if (pkg->dsynth_install_flg) { 1565 if (first_one_only) 1566 break; 1567 continue; 1568 } 1569 pkg->dsynth_install_flg = 1; 1570 1571 /* 1572 * If this is a dummy node with no package, the originator 1573 * is requesting a flavored package. We select the default 1574 * flavor which we presume is the first one. 1575 */ 1576 if (pkg->pkgfile == NULL && (pkg->flags & PKGF_DUMMY)) { 1577 pkg_t *spkg = pkg->idepon_list.next->pkg; 1578 1579 if (spkg) { 1580 pkg = spkg; 1581 if (fp) { 1582 fprintf(fp, 1583 "echo 'DUMMY use %s (%p)'\n", 1584 pkg->portdir, pkg->pkgfile); 1585 } 1586 } else { 1587 if (fp) { 1588 fprintf(fp, 1589 "echo 'CANNOT FIND DEFAULT " 1590 "FLAVOR FOR %s'\n", 1591 pkg->portdir); 1592 } 1593 } 1594 } 1595 1596 /* 1597 * Generate package installation command 1598 */ 1599 if (fp && pkg->pkgfile) { 1600 fprintf(fp, "echo 'Installing /packages/All/%s'\n", 1601 pkg->pkgfile); 1602 fprintf(fp, "pkg install -q -y /packages/All/%s " 1603 "|| exit 1\n", 1604 pkg->pkgfile); 1605 } else if (fp) { 1606 fprintf(fp, "echo 'CANNOT FIND PKG FOR %s'\n", 1607 pkg->portdir); 1608 } 1609 1610 if (pkg->pkgfile) { 1611 struct stat st; 1612 char *path; 1613 char *ptr; 1614 1615 asprintf(&path, "%s/%s", RepositoryPath, pkg->pkgfile); 1616 ptr = strrchr(pkg->pkgfile, '.'); 1617 if (stat(path, &st) == 0) { 1618 if (strcmp(ptr, ".tar") == 0) 1619 tot += st.st_size; 1620 else if (strcmp(ptr, ".tgz") == 0) 1621 tot += st.st_size * 3; 1622 else if (strcmp(ptr, ".txz") == 0) 1623 tot += st.st_size * 5; 1624 else if (strcmp(ptr, ".tbz") == 0) 1625 tot += st.st_size * 3; 1626 else 1627 tot += st.st_size * 2; 1628 } 1629 free(path); 1630 } 1631 if (first_one_only) 1632 break; 1633 } 1634 return tot; 1635 } 1636 1637 /* 1638 * Worker process interactions. 1639 * 1640 * The worker process is responsible for managing the build of a single 1641 * package. It is exec'd by the master dsynth and only loads the 1642 * configuration. 1643 * 1644 * This process does not run in the chroot. It will become the reaper for 1645 * all sub-processes and it will enter the chroot to execute various phases. 1646 * It catches SIGINTR, SIGHUP, and SIGPIPE and will iterate, terminate, and 1647 * reap all sub-process upon kill or exit. 1648 * 1649 * The command line forwarded to this function is: 1650 * 1651 * WORKER slot# socketfd portdir/subdir 1652 * 1653 * TERM=dumb 1654 * USER=root 1655 * HOME=/root 1656 * LANG=C 1657 * SSL_NO_VERIFY_PEER=1 1658 * USE_PACKAGE_DEPENDS_ONLY=1 (exec_phase_depends) 1659 * PORTSDIR=/xports 1660 * PORT_DBDIR=/options For ports options 1661 * PACKAGE_BUILDING=yes Don't build packages that aren't legally 1662 * buildable for a binary repo. 1663 * PKG_DBDIR=/var/db/pkg 1664 * PKG_CACHEDIR=/var/cache/pkg 1665 * PKG_CREATE_VERBOSE=yes Ensure periodic output during packaging 1666 * (custom environment) 1667 * PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin 1668 * UNAME_s=DragonFly (example) 1669 * UNAME_v=DragonFly 5.7-SYNTH (example) 1670 * UNAME_p=x86_64 (example) 1671 * UNAME_m=x86_64 (example) 1672 * UNAME_r=5.7-SYNTH (example) 1673 * NO_DEPENDS=yes (exec_phase) 1674 * DISTDIR=/distfiles 1675 * WRKDIRPREFIX=/construction 1676 * BATCH=yes 1677 * MAKE_JOBS_NUMBER=n 1678 * 1679 * SETUP: 1680 * ldconfig -R 1681 * /usr/local/sbin/pkg-static install /packages/All/<the pkg pkg> 1682 * /usr/local/sbin/pkg-static install /packages/All/<pkg> 1683 * (for all dependencies) 1684 * 1685 * PHASES: make -C path FLAVOR=flavor <phase> 1686 * check-sanity 1687 * pkg-depends 1688 * fetch-depends 1689 * fetch 1690 * checksum 1691 * extract-depends 1692 * extract 1693 * patch-depends 1694 * patch 1695 * build-depends 1696 * lib-depends 1697 * configure 1698 * build 1699 * run-depends 1700 * stage 1701 * test (skipped) 1702 * check-plist ('dsynth test blahblah' or 'dsynth -D everything' only) 1703 * package e.g. /construction/lang/perl5.28/pkg/perl5-5.28.2.txz 1704 * install-mtree (skipped) 1705 * install (skipped) 1706 * deinstall (skipped) 1707 */ 1708 void 1709 WorkerProcess(int ac, char **av) 1710 { 1711 wmsg_t wmsg; 1712 int fd; 1713 int slot; 1714 int tmpfd; 1715 int pkgpkg = 0; 1716 int status; 1717 int len; 1718 int do_install_phase; 1719 char *portdir; 1720 char *pkgfile; 1721 char *flavor; 1722 char *buf; 1723 worker_t *work; 1724 bulk_t *bulk; 1725 pkg_t pkg; 1726 buildenv_t *benv; 1727 FILE *fp; 1728 1729 /* 1730 * Parse arguments 1731 */ 1732 if (ac != 6) { 1733 dlog(DLOG_ALL, "WORKER PROCESS %d- bad arguments\n", getpid()); 1734 exit(1); 1735 } 1736 slot = strtol(av[1], NULL, 0); 1737 fd = strtol(av[2], NULL, 0); /* master<->slave messaging */ 1738 portdir = av[3]; 1739 pkgfile = av[4]; 1740 flavor = strchr(portdir, '@'); 1741 if (flavor) { 1742 *flavor++ = 0; 1743 asprintf(&buf, "@%s", flavor); 1744 WorkerFlavorPrt = buf; 1745 buf = NULL; /* safety */ 1746 } 1747 WorkerProcFlags = strtol(av[5], NULL, 0); 1748 1749 bzero(&wmsg, sizeof(wmsg)); 1750 1751 setproctitle("[%02d] WORKER STARTUP %s%s", 1752 slot, portdir, WorkerFlavorPrt); 1753 1754 if (strcmp(portdir, "ports-mgmt/pkg") == 0) 1755 pkgpkg = 1; 1756 1757 signal(SIGTERM, phaseTerminateSignal); 1758 signal(SIGINT, phaseTerminateSignal); 1759 signal(SIGHUP, phaseTerminateSignal); 1760 1761 /* 1762 * Set up the environment 1763 */ 1764 setenv("TERM", "dumb", 1); 1765 setenv("USER", "root", 1); 1766 setenv("HOME", "/root", 1); 1767 setenv("LANG", "C", 1); 1768 setenv("SSL_NO_VERIFY_PEER", "1", 1); 1769 1770 addbuildenv("USE_PACKAGE_DEPENDS_ONLY", "yes", BENV_MAKECONF); 1771 addbuildenv("PORTSDIR", "/xports", BENV_MAKECONF); 1772 addbuildenv("PORT_DBDIR", "/options", BENV_MAKECONF); 1773 addbuildenv("PKG_DBDIR", "/var/db/pkg", BENV_MAKECONF); 1774 addbuildenv("PKG_CACHEDIR", "/var/cache/pkg", BENV_MAKECONF); 1775 addbuildenv("PKG_SUFX", UsePkgSufx, BENV_MAKECONF); 1776 if (WorkerProcFlags & WORKER_PROC_DEVELOPER) 1777 addbuildenv("DEVELOPER", "1", BENV_MAKECONF); 1778 1779 /* 1780 * CCache is a horrible unreliable hack but... leave the 1781 * mechanism in-place in case someone has a death wish. 1782 */ 1783 if (UseCCache) { 1784 addbuildenv("WITH_CCACHE_BUILD", "yes", BENV_MAKECONF); 1785 addbuildenv("CCACHE_DIR", "/ccache", BENV_MAKECONF); 1786 } 1787 1788 addbuildenv("UID", "0", BENV_MAKECONF); 1789 addbuildenv("ARCH", ArchitectureName, BENV_MAKECONF); 1790 1791 #ifdef __DragonFly__ 1792 addbuildenv("OPSYS", "DragonFly", BENV_MAKECONF); 1793 addbuildenv("DFLYVERSION", VersionFromParamHeader, BENV_MAKECONF); 1794 addbuildenv("OSVERSION", "9999999", BENV_MAKECONF); 1795 #else 1796 #error "Need OS-specific data to generate make.conf" 1797 #endif 1798 1799 addbuildenv("OSREL", ReleaseName, BENV_MAKECONF); 1800 addbuildenv("_OSRELEASE", VersionOnlyName, BENV_MAKECONF); 1801 1802 setenv("PATH", 1803 "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin", 1804 1); 1805 1806 setenv("UNAME_s", OperatingSystemName, 1); 1807 setenv("UNAME_v", VersionName, 1); 1808 setenv("UNAME_p", ArchitectureName, 1); 1809 setenv("UNAME_m", MachineName, 1); 1810 setenv("UNAME_r", ReleaseName, 1); 1811 1812 addbuildenv("NO_DEPENDS", "yes", BENV_MAKECONF); 1813 addbuildenv("DISTDIR", "/distfiles", BENV_MAKECONF); 1814 addbuildenv("WRKDIRPREFIX", "/construction", BENV_MAKECONF); 1815 addbuildenv("BATCH", "yes", BENV_MAKECONF); 1816 1817 /* 1818 * Special consideration 1819 * 1820 * PACKAGE_BUILDING - Disallow packaging ports which do not allow 1821 * for binary distribution. 1822 * 1823 * PKG_CREATE_VERBOSE - Ensure periodic output during the packaging 1824 * process to avoid a watchdog timeout. 1825 * 1826 */ 1827 addbuildenv("PACKAGE_BUILDING", "yes", BENV_MAKECONF); 1828 addbuildenv("PKG_CREATE_VERBOSE", "yes", BENV_MAKECONF); 1829 asprintf(&buf, "%d", MaxJobs); 1830 addbuildenv("MAKE_JOBS_NUMBER", buf, BENV_MAKECONF); 1831 freestrp(&buf); 1832 1833 if (flavor) 1834 setenv("FLAVOR", flavor, 1); 1835 1836 /* 1837 * Become the reaper 1838 */ 1839 if (procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) < 0) 1840 dfatal_errno("procctl() - Cannot become reaper"); 1841 1842 /* 1843 * Initialize a worker structure 1844 */ 1845 DoInitBuild(slot); 1846 1847 bzero(&pkg, sizeof(pkg)); 1848 pkg.portdir = portdir; /* sans flavor */ 1849 pkg.pkgfile = pkgfile; 1850 if (strchr(portdir, '/')) 1851 len = strchr(portdir, '/') - portdir; 1852 else 1853 len = 0; 1854 1855 /* 1856 * Setup the logfile 1857 */ 1858 asprintf(&pkg.logfile, 1859 "%s/%*.*s___%s%s%s.log", 1860 LogsPath, len, len, portdir, 1861 ((portdir[len] == '/') ? portdir + len + 1 : portdir + len), 1862 (flavor ? "@" : ""), 1863 (flavor ? flavor : "")); 1864 tmpfd = open(pkg.logfile, O_RDWR|O_CREAT|O_TRUNC, 0666); 1865 if (tmpfd >= 0) { 1866 if (DebugOpt >= 2) { 1867 dlog(DLOG_ALL, "[%03d] %s LOGFILE %s\n", 1868 slot, pkg.portdir, pkg.logfile); 1869 } 1870 close(tmpfd); 1871 } else { 1872 dlog(DLOG_ALL, "[%03d] LOGFILE %s (create failed)\n", 1873 slot, pkg.logfile); 1874 } 1875 1876 /* 1877 * Setup the work structure. Because this is an exec'd sub-process, 1878 * there is only one work structure. 1879 */ 1880 work = &WorkerAry[0]; 1881 work->flavor = flavor; 1882 work->fds[0] = fd; 1883 work->pkg = &pkg; 1884 work->start_time = time(NULL); 1885 1886 /* 1887 * Do mounts 1888 */ 1889 SigWork = work; 1890 setproctitle("[%02d] WORKER MOUNTS %s%s", 1891 slot, portdir, WorkerFlavorPrt); 1892 DoWorkerMounts(work); 1893 1894 /* 1895 * Generate an /etc/make.conf in the build base 1896 */ 1897 asprintf(&buf, "%s/etc/make.conf", work->basedir); 1898 fp = fopen(buf, "w"); 1899 dassert_errno(fp, "Unable to create %s\n", buf); 1900 for (benv = BuildEnv; benv; benv = benv->next) { 1901 if (benv->type & BENV_PKGLIST) 1902 continue; 1903 if ((benv->type & BENV_CMDMASK) == BENV_MAKECONF) { 1904 if (DebugOpt >= 2) { 1905 dlog(DLOG_ALL, "[%03d] ENV %s=%s\n", 1906 slot, benv->label, benv->data); 1907 } 1908 fprintf(fp, "%s=%s\n", benv->label, benv->data); 1909 } 1910 } 1911 fclose(fp); 1912 freestrp(&buf); 1913 1914 /* 1915 * Set up our hooks 1916 */ 1917 if (UsingHooks) 1918 initbulk(childHookRun, MaxBulk); 1919 1920 /* 1921 * Start phases 1922 */ 1923 wmsg.cmd = WMSG_CMD_INSTALL_PKGS; 1924 ipcwritemsg(fd, &wmsg); 1925 status = ipcreadmsg(fd, &wmsg); 1926 if (status < 0 || wmsg.cmd != WMSG_RES_INSTALL_PKGS) 1927 dfatal("pkg installation handshake failed"); 1928 do_install_phase = wmsg.status; 1929 1930 wmsg.cmd = WMSG_CMD_STATUS_UPDATE; 1931 wmsg.phase = PHASE_INSTALL_PKGS; 1932 wmsg.lines = 0; 1933 1934 status = ipcwritemsg(fd, &wmsg); 1935 1936 if (pkgpkg) { 1937 dophase(work, &wmsg, 1938 WDOG5, PHASE_PACKAGE, "package"); 1939 } else { 1940 if (do_install_phase) { 1941 dophase(work, &wmsg, 1942 WDOG4, PHASE_INSTALL_PKGS, "setup"); 1943 } 1944 dophase(work, &wmsg, 1945 WDOG2, PHASE_CHECK_SANITY, "check-sanity"); 1946 dophase(work, &wmsg, 1947 WDOG2, PHASE_PKG_DEPENDS, "pkg-depends"); 1948 dophase(work, &wmsg, 1949 WDOG7, PHASE_FETCH_DEPENDS, "fetch-depends"); 1950 dophase(work, &wmsg, 1951 WDOG7, PHASE_FETCH, "fetch"); 1952 dophase(work, &wmsg, 1953 WDOG2, PHASE_CHECKSUM, "checksum"); 1954 dophase(work, &wmsg, 1955 WDOG3, PHASE_EXTRACT_DEPENDS, "extract-depends"); 1956 dophase(work, &wmsg, 1957 WDOG3, PHASE_EXTRACT, "extract"); 1958 dophase(work, &wmsg, 1959 WDOG2, PHASE_PATCH_DEPENDS, "patch-depends"); 1960 dophase(work, &wmsg, 1961 WDOG2, PHASE_PATCH, "patch"); 1962 dophase(work, &wmsg, 1963 WDOG5, PHASE_BUILD_DEPENDS, "build-depends"); 1964 dophase(work, &wmsg, 1965 WDOG5, PHASE_LIB_DEPENDS, "lib-depends"); 1966 dophase(work, &wmsg, 1967 WDOG3, PHASE_CONFIGURE, "configure"); 1968 dophase(work, &wmsg, 1969 WDOG9, PHASE_BUILD, "build"); 1970 dophase(work, &wmsg, 1971 WDOG5, PHASE_RUN_DEPENDS, "run-depends"); 1972 dophase(work, &wmsg, 1973 WDOG5, PHASE_STAGE, "stage"); 1974 #if 0 1975 dophase(work, &wmsg, 1976 WDOG5, PHASE_TEST, "test"); 1977 #endif 1978 if (WorkerProcFlags & WORKER_PROC_CHECK_PLIST) { 1979 dophase(work, &wmsg, 1980 WDOG1, PHASE_CHECK_PLIST, "check-plist"); 1981 } 1982 dophase(work, &wmsg, 1983 WDOG5, PHASE_PACKAGE, "package"); 1984 #if 0 1985 dophase(work, &wmsg, 1986 WDOG5, PHASE_INSTALL_MTREE, "install-mtree"); 1987 dophase(work, &wmsg, 1988 WDOG5, PHASE_INSTALL, "install"); 1989 dophase(work, &wmsg, 1990 WDOG5, PHASE_DEINSTALL, "deinstall"); 1991 #endif 1992 } 1993 1994 if (MasterPtyFd >= 0) { 1995 close(MasterPtyFd); 1996 MasterPtyFd = -1; 1997 } 1998 1999 setproctitle("[%02d] WORKER CLEANUP %s%s", 2000 slot, portdir, WorkerFlavorPrt); 2001 2002 /* 2003 * Copy the package to the repo. 2004 */ 2005 if (work->accum_error == 0) { 2006 char *b1; 2007 char *b2; 2008 2009 asprintf(&b1, "%s/construction/%s/pkg/%s", 2010 work->basedir, pkg.portdir, pkg.pkgfile); 2011 asprintf(&b2, "%s/%s", RepositoryPath, pkg.pkgfile); 2012 if (copyfile(b1, b2)) { 2013 ++work->accum_error; 2014 dlog(DLOG_ALL, "[%03d] %s Unable to copy %s to %s\n", 2015 work->index, pkg.portdir, b1, b2); 2016 } 2017 free(b1); 2018 free(b2); 2019 } 2020 2021 /* 2022 * Unmount, unless we are in DebugStopMode. 2023 */ 2024 if ((WorkerProcFlags & WORKER_PROC_DEBUGSTOP) == 0) 2025 DoWorkerUnmounts(work); 2026 2027 /* 2028 * Send completion status to master dsynth worker thread. 2029 */ 2030 if (work->accum_error) { 2031 wmsg.cmd = WMSG_CMD_FAILURE; 2032 } else { 2033 wmsg.cmd = WMSG_CMD_SUCCESS; 2034 } 2035 ipcwritemsg(fd, &wmsg); 2036 if (WorkerProcFlags & WORKER_PROC_DEBUGSTOP) { 2037 wmsg.cmd = WMSG_CMD_FREEZEWORKER; 2038 ipcwritemsg(fd, &wmsg); 2039 } 2040 if (UsingHooks) { 2041 while ((bulk = getbulk()) != NULL) 2042 freebulk(bulk); 2043 donebulk(); 2044 } 2045 } 2046 2047 static void 2048 dophase(worker_t *work, wmsg_t *wmsg, int wdog, int phaseid, const char *phase) 2049 { 2050 pkg_t *pkg = work->pkg; 2051 char buf[1024]; 2052 pid_t pid; 2053 int status; 2054 int ms; 2055 pid_t wpid; 2056 int wpid_reaped; 2057 int fdlog; 2058 time_t start_time; 2059 time_t last_time; 2060 time_t next_time; 2061 time_t wdog_time; 2062 FILE *fp; 2063 2064 if (work->accum_error) 2065 return; 2066 setproctitle("[%02d] WORKER %-8.8s %s%s", 2067 work->index, phase, pkg->portdir, WorkerFlavorPrt); 2068 wmsg->phase = phaseid; 2069 if (ipcwritemsg(work->fds[0], wmsg) < 0) { 2070 dlog(DLOG_ALL, "[%03d] %s Lost Communication with dsynth, " 2071 "aborting worker\n", 2072 work->index, pkg->portdir); 2073 ++work->accum_error; 2074 return; 2075 } 2076 2077 /* 2078 * Execute the port make command in chroot on a pty. 2079 */ 2080 fflush(stdout); 2081 fflush(stderr); 2082 if (MasterPtyFd >= 0) { 2083 int slavefd; 2084 char ttybuf[2]; 2085 2086 /* 2087 * NOTE: We can't open the slave in the child because the 2088 * master may race a disconnection test. If we open 2089 * it in the parent our close() will flush any pending 2090 * output not read by the master (which is the same 2091 * parent process) and deadlock. 2092 * 2093 * Solve this by hand-shaking the slave tty to give 2094 * the master time to close its slavefd. 2095 * 2096 * Leave the tty defaults intact, which also likely 2097 * means it will be in line-buffered mode, so handshake 2098 * with a full line. 2099 * 2100 * TODO: Our handshake probably echos back to the master pty 2101 * due to tty echo, and ends up in the log, so just 2102 * pass through a newline. 2103 */ 2104 slavefd = open(ptsname(MasterPtyFd), O_RDWR); 2105 dassert_errno(slavefd >= 0, "Cannot open slave pty"); 2106 pid = fork(); 2107 if (pid == 0) { 2108 login_tty(slavefd); 2109 /* login_tty() closes slavefd */ 2110 read(0, ttybuf, 1); 2111 } else { 2112 close(slavefd); 2113 write(MasterPtyFd, "\n", 1); 2114 } 2115 } else { 2116 pid = forkpty(&MasterPtyFd, NULL, NULL, NULL); 2117 } 2118 2119 if (pid == 0) { 2120 struct termios tio; 2121 2122 /* 2123 * We are going through a pty, so set the tty modes to 2124 * Set tty modes so we do not get ^M's in the log files. 2125 * 2126 * This isn't fatal if it doesn't work. Remember that 2127 * our output goes through the pty to the management 2128 * process which will log it. 2129 */ 2130 if (tcgetattr(1, &tio) == 0) { 2131 tio.c_oflag |= OPOST | ONOCR; 2132 tio.c_oflag &= ~(OCRNL | ONLCR); 2133 tio.c_iflag |= ICRNL; 2134 tio.c_iflag &= ~(INLCR|IGNCR); 2135 if (tcsetattr(1, TCSANOW, &tio)) { 2136 printf("tcsetattr failed: %s\n", 2137 strerror(errno)); 2138 } 2139 } else { 2140 printf("tcgetattr failed: %s\n", strerror(errno)); 2141 } 2142 2143 /* 2144 * Clean-up, chdir, and chroot. 2145 */ 2146 closefrom(3); 2147 if (chdir(work->basedir) < 0) 2148 dfatal_errno("chdir in phase initialization"); 2149 if (chroot(work->basedir) < 0) 2150 dfatal_errno("chroot in phase initialization"); 2151 2152 /* 2153 * We have a choice here on how to handle stdin (fd 0). 2154 * We can leave it connected to the pty in which case 2155 * the build will just block if it tries to ask a 2156 * question (and the watchdog will kill it, eventually), 2157 * or we can try to EOF the pty, or we can attach /dev/null 2158 * to descriptor 0. 2159 */ 2160 if (NullStdinOpt) { 2161 int fd; 2162 2163 fd = open("/dev/null", O_RDWR); 2164 dassert_errno(fd >= 0, "cannot open /dev/null"); 2165 if (fd != 0) { 2166 dup2(fd, 0); 2167 close(fd); 2168 } 2169 } 2170 2171 /* 2172 * Execute the appropriate command. 2173 */ 2174 switch(phaseid) { 2175 case PHASE_INSTALL_PKGS: 2176 snprintf(buf, sizeof(buf), "/tmp/dsynth_install_pkgs"); 2177 execl(buf, buf, NULL); 2178 break; 2179 default: 2180 snprintf(buf, sizeof(buf), "/xports/%s", pkg->portdir); 2181 execl(MAKE_BINARY, MAKE_BINARY, "-C", buf, phase, NULL); 2182 break; 2183 } 2184 _exit(1); 2185 } 2186 fcntl(MasterPtyFd, F_SETFL, O_NONBLOCK); 2187 2188 if (pid < 0) { 2189 dlog(DLOG_ALL, "[%03d] %s Fork Failed: %s\n", 2190 work->index, pkg->logfile, strerror(errno)); 2191 ++work->accum_error; 2192 return; 2193 } 2194 2195 SigPid = pid; 2196 2197 fdlog = open(pkg->logfile, O_RDWR|O_CREAT|O_APPEND, 0644); 2198 if (fdlog < 0) { 2199 dlog(DLOG_ALL, "[%03d] %s Cannot open logfile '%s': %s\n", 2200 work->index, pkg->portdir, 2201 pkg->logfile, strerror(errno)); 2202 } 2203 2204 snprintf(buf, sizeof(buf), 2205 "----------------------------------------" 2206 "---------------------------------------\n" 2207 "-- Phase: %s\n" 2208 "----------------------------------------" 2209 "---------------------------------------\n", 2210 phase); 2211 write(fdlog, buf, strlen(buf)); 2212 2213 start_time = time(NULL); 2214 last_time = start_time; 2215 wdog_time = start_time; 2216 wpid_reaped = 0; 2217 2218 status = 0; 2219 for (;;) { 2220 ms = mptylogpoll(MasterPtyFd, fdlog, wmsg, &wdog_time); 2221 if (ms == MPTY_FAILED) { 2222 dlog(DLOG_ALL, 2223 "[%03d] %s lost pty in phase %s, terminating\n", 2224 work->index, pkg->portdir, phase); 2225 break; 2226 } 2227 if (ms == MPTY_EOF) 2228 break; 2229 2230 /* 2231 * Generally speaking update status once a second. 2232 * This also allows us to detect if the management 2233 * dsynth process has gone away. 2234 */ 2235 next_time = time(NULL); 2236 if (next_time != last_time) { 2237 double dload[3]; 2238 double dv; 2239 int wdog_scaled; 2240 2241 /* 2242 * Send status update to the worker management thread 2243 * in the master dsynth process. Remember, *WE* are 2244 * the worker management process sub-fork. 2245 */ 2246 if (ipcwritemsg(work->fds[0], wmsg) < 0) 2247 break; 2248 last_time = next_time; 2249 2250 /* 2251 * Watchdog scaling 2252 */ 2253 getloadavg(dload, 3); 2254 dv = dload[2] / NumCores; 2255 if (dv < (double)NumCores) { 2256 wdog_scaled = wdog; 2257 } else { 2258 if (dv > 4.0 * NumCores) 2259 dv = 4.0 * NumCores; 2260 wdog_scaled = wdog * dv / NumCores; 2261 } 2262 2263 /* 2264 * Watchdog 2265 */ 2266 if (next_time - wdog_time >= wdog_scaled * 60) { 2267 snprintf(buf, sizeof(buf), 2268 "\n--------\n" 2269 "WATCHDOG TIMEOUT FOR %s in %s " 2270 "after %d minutes\n" 2271 "Killing pid %d\n" 2272 "--------\n", 2273 pkg->portdir, phase, wdog_scaled, pid); 2274 if (fdlog >= 0) 2275 write(fdlog, buf, strlen(buf)); 2276 dlog(DLOG_ALL, 2277 "[%03d] %s WATCHDOG TIMEOUT in %s " 2278 "after %d minutes (%d min scaled)\n", 2279 work->index, pkg->portdir, phase, 2280 wdog, wdog_scaled); 2281 kill(pid, SIGKILL); 2282 ++work->accum_error; 2283 break; 2284 } 2285 } 2286 2287 /* 2288 * Check process exit. Normally the pty will EOF 2289 * but if background processes remain we need to 2290 * check here to see if our primary exec is done, 2291 * so we can break out and reap those processes. 2292 * 2293 * Generally reap any other processes we have inherited 2294 * while we are here. 2295 */ 2296 do { 2297 wpid = wait3(&status, WNOHANG, NULL); 2298 } while (wpid > 0 && wpid != pid); 2299 if (wpid == pid && WIFEXITED(status)) { 2300 wpid_reaped = 1; 2301 break; 2302 } 2303 } 2304 2305 next_time = time(NULL); 2306 2307 setproctitle("[%02d] WORKER EXITREAP %s%s", 2308 work->index, pkg->portdir, WorkerFlavorPrt); 2309 2310 /* 2311 * We usually get here due to a mpty EOF, but not always as there 2312 * could be persistent processes still holding the slave. Finish 2313 * up getting the exit status for the main process we are waiting 2314 * on and clean out any data left on the MasterPtyFd (as it could 2315 * be blocking the exit). 2316 */ 2317 while (wpid_reaped == 0) { 2318 (void)mptylogpoll(MasterPtyFd, fdlog, wmsg, &wdog_time); 2319 wpid = waitpid(pid, &status, WNOHANG); 2320 if (wpid == pid && WIFEXITED(status)) { 2321 wpid_reaped = 1; 2322 break; 2323 } 2324 if (wpid < 0 && errno != EINTR) { 2325 break; 2326 } 2327 2328 /* 2329 * Safety. The normal phase waits until the fork/exec'd 2330 * pid finishes, causing a pty EOF on exit (the slave 2331 * descriptor is closed by the kernel on exit so the 2332 * process should already have exited). 2333 * 2334 * However, it is also possible to get here if the pty fails 2335 * for some reason. In this case, make sure that the process 2336 * is killed. 2337 */ 2338 kill(pid, SIGKILL); 2339 } 2340 2341 /* 2342 * Clean out anything left on the pty but don't wait around 2343 * because there could be background processes preventing the 2344 * slave side from closing. 2345 */ 2346 while (mptylogpoll(MasterPtyFd, fdlog, wmsg, &wdog_time) == MPTY_DATA) 2347 ; 2348 2349 /* 2350 * Report on the exit condition. If the pid was somehow lost 2351 * (probably due to someone gdb'ing the process), assume an error. 2352 */ 2353 if (wpid_reaped) { 2354 if (WEXITSTATUS(status)) { 2355 dlog(DLOG_ALL | DLOG_FILTER, 2356 "[%03d] %s Build phase '%s' failed exit %d\n", 2357 work->index, pkg->portdir, phase, 2358 WEXITSTATUS(status)); 2359 ++work->accum_error; 2360 } 2361 } else { 2362 dlog(DLOG_ALL, "[%03d] %s Build phase '%s' failed - lost pid\n", 2363 work->index, pkg->portdir, phase); 2364 ++work->accum_error; 2365 } 2366 2367 /* 2368 * Kill any processes still running (sometimes processes end up in 2369 * the background during a dports build), and clean up any other 2370 * children that we have inherited. 2371 */ 2372 phaseReapAll(); 2373 2374 /* 2375 * After the extraction phase add the space used by /construction 2376 * to the memory use. This helps us reduce the amount of paging 2377 * we do due to extremely large package extractions (languages, 2378 * chromium, etc). 2379 * 2380 * (dsynth already estimated the space used by the package deps 2381 * up front, but this will help us further). 2382 */ 2383 if (work->accum_error == 0 && phaseid == PHASE_EXTRACT) { 2384 struct statfs sfs; 2385 char *b1; 2386 2387 asprintf(&b1, "%s/construction", work->basedir); 2388 if (statfs(b1, &sfs) == 0) { 2389 wmsg->memuse = (sfs.f_blocks - sfs.f_bfree) * 2390 sfs.f_bsize; 2391 ipcwritemsg(work->fds[0], wmsg); 2392 } 2393 } 2394 2395 /* 2396 * Update log 2397 */ 2398 if (fdlog >= 0) { 2399 struct stat st; 2400 int h; 2401 int m; 2402 int s; 2403 2404 last_time = next_time - start_time; 2405 s = last_time % 60; 2406 m = last_time / 60 % 60; 2407 h = last_time / 3600; 2408 2409 fp = fdopen(fdlog, "a"); 2410 if (fp == NULL) { 2411 dlog(DLOG_ALL, "[%03d] %s Cannot fdopen fdlog: %s %d\n", 2412 work->index, pkg->portdir, 2413 strerror(errno), fstat(fdlog, &st)); 2414 close(fdlog); 2415 goto skip; 2416 } 2417 2418 fprintf(fp, "\n"); 2419 if (work->accum_error) { 2420 fprintf(fp, "FAILED %02d:%02d:%02d\n", h, m, s); 2421 } else { 2422 if (phaseid == PHASE_EXTRACT && wmsg->memuse) { 2423 fprintf(fp, "Extracted Memory Use: %6.2fM\n", 2424 wmsg->memuse / (1024.0 * 1024.0)); 2425 } 2426 fprintf(fp, "SUCCEEDED %02d:%02d:%02d\n", h, m, s); 2427 } 2428 last_time = next_time - work->start_time; 2429 s = last_time % 60; 2430 m = last_time / 60 % 60; 2431 h = last_time / 3600; 2432 if (phaseid == PHASE_PACKAGE) { 2433 fprintf(fp, "TOTAL TIME %02d:%02d:%02d\n", h, m, s); 2434 } 2435 fprintf(fp, "\n"); 2436 fclose(fp); 2437 skip: 2438 ; 2439 } 2440 2441 } 2442 2443 static void 2444 phaseReapAll(void) 2445 { 2446 struct reaper_status rs; 2447 int status; 2448 2449 while (procctl(P_PID, getpid(), PROC_REAP_STATUS, &rs) == 0) { 2450 if ((rs.flags & PROC_REAP_ACQUIRE) == 0) 2451 break; 2452 if (rs.pid_head < 0) 2453 break; 2454 if (kill(rs.pid_head, SIGKILL) == 0) { 2455 while (waitpid(rs.pid_head, &status, 0) < 0) 2456 ; 2457 } 2458 } 2459 while (wait3(&status, 0, NULL) > 0) 2460 ; 2461 } 2462 2463 static void 2464 phaseTerminateSignal(int sig __unused) 2465 { 2466 if (CopyFileFd >= 0) 2467 close(CopyFileFd); 2468 if (MasterPtyFd >= 0) 2469 close(MasterPtyFd); 2470 if (SigPid > 1) 2471 kill(SigPid, SIGKILL); 2472 phaseReapAll(); 2473 if (SigWork) 2474 DoWorkerUnmounts(SigWork); 2475 exit(1); 2476 } 2477 2478 static 2479 char * 2480 buildskipreason(pkglink_t *parent, pkg_t *pkg) 2481 { 2482 pkglink_t *link; 2483 pkg_t *scan; 2484 char *reason = NULL; 2485 char *ptr; 2486 size_t tot; 2487 size_t len; 2488 pkglink_t stack; 2489 2490 if ((pkg->flags & PKGF_NOBUILD_I) && pkg->ignore) 2491 asprintf(&reason, "%s ", pkg->ignore); 2492 2493 tot = 0; 2494 PKGLIST_FOREACH(link, &pkg->idepon_list) { 2495 #if 0 2496 if (link->dep_type > DEP_TYPE_BUILD) 2497 continue; 2498 #endif 2499 scan = link->pkg; 2500 if (scan == NULL) 2501 continue; 2502 if ((scan->flags & (PKGF_ERROR | PKGF_NOBUILD)) == 0) 2503 continue; 2504 if (scan->flags & PKGF_NOBUILD) { 2505 stack.pkg = scan; 2506 stack.next = parent; 2507 ptr = buildskipreason(&stack, scan); 2508 len = strlen(scan->portdir) + strlen(ptr) + 8; 2509 reason = realloc(reason, tot + len); 2510 snprintf(reason + tot, len, "%s->%s", 2511 scan->portdir, ptr); 2512 free(ptr); 2513 } else { 2514 len = strlen(scan->portdir) + 8; 2515 reason = realloc(reason, tot + len); 2516 snprintf(reason + tot, len, "%s", scan->portdir); 2517 } 2518 2519 /* 2520 * Don't try to print the entire graph 2521 */ 2522 if (parent) 2523 break; 2524 tot += strlen(reason + tot); 2525 reason[tot++] = ' '; 2526 reason[tot] = 0; 2527 } 2528 return (reason); 2529 } 2530 2531 /* 2532 * Count number of packages that would be skipped due to the 2533 * specified package having failed. 2534 * 2535 * Call with mode 1 to count, and mode 0 to clear the 2536 * cumulative rscan flag (used to de-duplicate the count). 2537 * 2538 * Must be serialized. 2539 */ 2540 static int 2541 buildskipcount_dueto(pkg_t *pkg, int mode) 2542 { 2543 pkglink_t *link; 2544 pkg_t *scan; 2545 int total; 2546 2547 total = 0; 2548 PKGLIST_FOREACH(link, &pkg->deponi_list) { 2549 scan = link->pkg; 2550 if (scan == NULL || scan->rscan == mode) 2551 continue; 2552 scan->rscan = mode; 2553 ++total; 2554 total += buildskipcount_dueto(scan, mode); 2555 } 2556 return total; 2557 } 2558 2559 /* 2560 * The master ptyfd is in non-blocking mode. Drain up to 1024 bytes 2561 * and update wmsg->lines and *wdog_timep as appropriate. 2562 * 2563 * This function will poll, stalling up to 1 second. 2564 */ 2565 static int 2566 mptylogpoll(int ptyfd, int fdlog, wmsg_t *wmsg, time_t *wdog_timep) 2567 { 2568 struct pollfd pfd; 2569 char buf[1024]; 2570 ssize_t r; 2571 2572 pfd.fd = ptyfd; 2573 pfd.events = POLLIN; 2574 pfd.revents = 0; 2575 2576 poll(&pfd, 1, 1000); 2577 if (pfd.revents) { 2578 r = read(ptyfd, buf, sizeof(buf)); 2579 if (r > 0) { 2580 *wdog_timep = time(NULL); 2581 if (r > 0 && fdlog >= 0) 2582 write(fdlog, buf, r); 2583 while (--r >= 0) { 2584 if (buf[r] == '\n') 2585 ++wmsg->lines; 2586 } 2587 return MPTY_DATA; 2588 } else if (r < 0) { 2589 if (errno != EINTR && errno != EAGAIN) 2590 return MPTY_FAILED; 2591 return MPTY_AGAIN; 2592 } else if (r == 0) { 2593 return MPTY_EOF; 2594 } 2595 } 2596 return MPTY_AGAIN; 2597 } 2598 2599 /* 2600 * Copy a (package) file from (src) to (dst), use an intermediate file and 2601 * rename to ensure that interruption does not leave us with a corrupt 2602 * package file. 2603 * 2604 * This is called by the WORKER process. 2605 * 2606 * (dsynth management thread -> WORKER process -> sub-processes) 2607 */ 2608 #define COPYBLKSIZE 32768 2609 2610 static int 2611 copyfile(char *src, char *dst) 2612 { 2613 char *tmp; 2614 char *buf; 2615 int fd1; 2616 int fd2; 2617 int error = 0; 2618 int mask; 2619 ssize_t r; 2620 2621 asprintf(&tmp, "%s.new", dst); 2622 buf = malloc(COPYBLKSIZE); 2623 2624 mask = sigsetmask(sigmask(SIGTERM)|sigmask(SIGINT)|sigmask(SIGHUP)); 2625 fd1 = open(src, O_RDONLY|O_CLOEXEC); 2626 fd2 = open(tmp, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0644); 2627 CopyFileFd = fd1; 2628 sigsetmask(mask); 2629 while ((r = read(fd1, buf, COPYBLKSIZE)) > 0) { 2630 if (write(fd2, buf, r) != r) 2631 error = 1; 2632 } 2633 if (r < 0) 2634 error = 1; 2635 mask = sigsetmask(sigmask(SIGTERM)|sigmask(SIGINT)|sigmask(SIGHUP)); 2636 CopyFileFd = -1; 2637 close(fd1); 2638 close(fd2); 2639 sigsetmask(mask); 2640 if (error) { 2641 remove(tmp); 2642 } else { 2643 if (rename(tmp, dst)) { 2644 error = 1; 2645 remove(tmp); 2646 } 2647 } 2648 2649 freestrp(&buf); 2650 freestrp(&tmp); 2651 2652 return error; 2653 } 2654 2655 /* 2656 * doHook() 2657 * 2658 * primary process (threaded) - run_start, run_end, pkg_ignored, pkg_skipped 2659 * worker process (threaded) - pkg_sucess, pkg_failure 2660 * 2661 * If waitfor is non-zero this hook will be serialized. 2662 */ 2663 static void 2664 doHook(pkg_t *pkg, const char *id, const char *path, int waitfor) 2665 { 2666 if (path == NULL) 2667 return; 2668 while (waitfor && getbulk() != NULL) 2669 ; 2670 if (pkg) 2671 queuebulk(pkg->portdir, id, path, pkg->pkgfile); 2672 else 2673 queuebulk(NULL, id, path, NULL); 2674 while (waitfor && getbulk() != NULL) 2675 ; 2676 } 2677 2678 /* 2679 * Execute hook (backend) 2680 * 2681 * s1 - portdir 2682 * s2 - id 2683 * s3 - script path 2684 * s4 - pkgfile (if applicable) 2685 */ 2686 static void 2687 childHookRun(bulk_t *bulk) 2688 { 2689 const char *cav[MAXCAC]; 2690 buildenv_t benv[MAXCAC]; 2691 char buf1[128]; 2692 char buf2[128]; 2693 char buf3[128]; 2694 char buf4[128]; 2695 FILE *fp; 2696 char *ptr; 2697 size_t len; 2698 pid_t pid; 2699 int cac; 2700 int bi; 2701 2702 cac = 0; 2703 bi = 0; 2704 bzero(benv, sizeof(benv)); 2705 2706 cav[cac++] = bulk->s3; 2707 2708 benv[bi].label = "PROFILE"; 2709 benv[bi].data = Profile; 2710 ++bi; 2711 2712 benv[bi].label = "DIR_PACKAGES"; 2713 benv[bi].data = PackagesPath; 2714 ++bi; 2715 2716 benv[bi].label = "DIR_REPOSITORY"; 2717 benv[bi].data = RepositoryPath; 2718 ++bi; 2719 2720 benv[bi].label = "DIR_PORTS"; 2721 benv[bi].data = DPortsPath; 2722 ++bi; 2723 2724 benv[bi].label = "DIR_OPTIONS"; 2725 benv[bi].data = OptionsPath; 2726 ++bi; 2727 2728 benv[bi].label = "DIR_DISTFILES"; 2729 benv[bi].data = DistFilesPath; 2730 ++bi; 2731 2732 benv[bi].label = "DIR_LOGS"; 2733 benv[bi].data = LogsPath; 2734 ++bi; 2735 2736 benv[bi].label = "DIR_BUILDBASE"; 2737 benv[bi].data = BuildBase; 2738 ++bi; 2739 2740 if (strcmp(bulk->s2, "hook_run_start") == 0) { 2741 snprintf(buf1, sizeof(buf1), "%d", BuildTotal); 2742 benv[bi].label = "PORTS_QUEUED"; 2743 benv[bi].data = buf1; 2744 ++bi; 2745 } else if (strcmp(bulk->s2, "hook_run_end") == 0) { 2746 snprintf(buf1, sizeof(buf1), "%d", BuildSuccessCount); 2747 benv[bi].label = "PORTS_BUILT"; 2748 benv[bi].data = buf1; 2749 ++bi; 2750 snprintf(buf2, sizeof(buf2), "%d", BuildFailCount); 2751 benv[bi].label = "PORTS_FAILED"; 2752 benv[bi].data = buf2; 2753 ++bi; 2754 snprintf(buf3, sizeof(buf3), "%d", BuildIgnoreCount); 2755 benv[bi].label = "PORTS_IGNORED"; 2756 benv[bi].data = buf3; 2757 ++bi; 2758 snprintf(buf4, sizeof(buf4), "%d", BuildSkipCount); 2759 benv[bi].label = "PORTS_SKIPPED"; 2760 benv[bi].data = buf4; 2761 ++bi; 2762 } else { 2763 /* 2764 * success, failure, ignored, skipped 2765 */ 2766 benv[bi].label = "RESULT"; 2767 if (strcmp(bulk->s2, "hook_pkg_success") == 0) { 2768 benv[bi].data = "success"; 2769 } else if (strcmp(bulk->s2, "hook_pkg_failure") == 0) { 2770 benv[bi].data = "failure"; 2771 } else if (strcmp(bulk->s2, "hook_pkg_ignored") == 0) { 2772 benv[bi].data = "ignored"; 2773 } else if (strcmp(bulk->s2, "hook_pkg_skipped") == 0) { 2774 benv[bi].data = "skipped"; 2775 } else { 2776 dfatal("Unknown hook id: %s", bulk->s2); 2777 /* NOT REACHED */ 2778 } 2779 ++bi; 2780 2781 /* 2782 * For compatibility with synth: 2783 * 2784 * ORIGIN does not include any @flavor, thus it is suitable 2785 * for finding the actual port directory/subdirectory. 2786 * 2787 * FLAVOR is set to ORIGIN if there is no flavor, otherwise 2788 * it is set to only the flavor sans the '@'. 2789 */ 2790 if ((ptr = strchr(bulk->s1, '@')) != NULL) { 2791 snprintf(buf1, sizeof(buf1), "%*.*s", 2792 (int)(ptr - bulk->s1), 2793 (int)(ptr - bulk->s1), 2794 bulk->s1); 2795 benv[bi].label = "ORIGIN"; 2796 benv[bi].data = buf1; 2797 ++bi; 2798 benv[bi].label = "FLAVOR"; 2799 benv[bi].data = ptr + 1; 2800 ++bi; 2801 } else { 2802 benv[bi].label = "ORIGIN"; 2803 benv[bi].data = bulk->s1; 2804 ++bi; 2805 benv[bi].label = "FLAVOR"; 2806 benv[bi].data = bulk->s1; 2807 ++bi; 2808 } 2809 benv[bi].label = "PKGNAME"; 2810 benv[bi].data = bulk->s4; 2811 ++bi; 2812 } 2813 2814 benv[bi].label = NULL; 2815 benv[bi].data = NULL; 2816 2817 fp = dexec_open(cav, cac, &pid, benv, 0, 0); 2818 while ((ptr = fgetln(fp, &len)) != NULL) 2819 ; 2820 2821 if (dexec_close(fp, pid)) { 2822 dlog(DLOG_ALL, 2823 "[XXX] %s SCRIPT %s (%s)\n", 2824 bulk->s1, bulk->s2, bulk->s3); 2825 } 2826 } 2827