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 "(propagate 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 (propagate 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 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 (conditional based on 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("DISTDIR", "/distfiles", BENV_MAKECONF); 1813 addbuildenv("WRKDIRPREFIX", "/construction", BENV_MAKECONF); 1814 addbuildenv("BATCH", "yes", BENV_MAKECONF); 1815 1816 /* 1817 * Special consideration 1818 * 1819 * PACKAGE_BUILDING - Disallow packaging ports which do not allow 1820 * for binary distribution. 1821 * 1822 * PKG_CREATE_VERBOSE - Ensure periodic output during the packaging 1823 * process to avoid a watchdog timeout. 1824 * 1825 */ 1826 addbuildenv("PACKAGE_BUILDING", "yes", BENV_MAKECONF); 1827 addbuildenv("PKG_CREATE_VERBOSE", "yes", BENV_MAKECONF); 1828 asprintf(&buf, "%d", MaxJobs); 1829 addbuildenv("MAKE_JOBS_NUMBER", buf, BENV_MAKECONF); 1830 freestrp(&buf); 1831 1832 if (flavor) 1833 setenv("FLAVOR", flavor, 1); 1834 1835 /* 1836 * Become the reaper 1837 */ 1838 if (procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) < 0) 1839 dfatal_errno("procctl() - Cannot become reaper"); 1840 1841 /* 1842 * Initialize a worker structure 1843 */ 1844 DoInitBuild(slot); 1845 1846 bzero(&pkg, sizeof(pkg)); 1847 pkg.portdir = portdir; /* sans flavor */ 1848 pkg.pkgfile = pkgfile; 1849 if (strchr(portdir, '/')) 1850 len = strchr(portdir, '/') - portdir; 1851 else 1852 len = 0; 1853 1854 /* 1855 * Setup the logfile 1856 */ 1857 asprintf(&pkg.logfile, 1858 "%s/%*.*s___%s%s%s.log", 1859 LogsPath, len, len, portdir, 1860 ((portdir[len] == '/') ? portdir + len + 1 : portdir + len), 1861 (flavor ? "@" : ""), 1862 (flavor ? flavor : "")); 1863 tmpfd = open(pkg.logfile, O_RDWR|O_CREAT|O_TRUNC, 0666); 1864 if (tmpfd >= 0) { 1865 if (DebugOpt >= 2) { 1866 dlog(DLOG_ALL, "[%03d] %s LOGFILE %s\n", 1867 slot, pkg.portdir, pkg.logfile); 1868 } 1869 close(tmpfd); 1870 } else { 1871 dlog(DLOG_ALL, "[%03d] LOGFILE %s (create failed)\n", 1872 slot, pkg.logfile); 1873 } 1874 1875 /* 1876 * Setup the work structure. Because this is an exec'd sub-process, 1877 * there is only one work structure. 1878 */ 1879 work = &WorkerAry[0]; 1880 work->flavor = flavor; 1881 work->fds[0] = fd; 1882 work->pkg = &pkg; 1883 work->start_time = time(NULL); 1884 1885 /* 1886 * Do mounts 1887 */ 1888 SigWork = work; 1889 setproctitle("[%02d] WORKER MOUNTS %s%s", 1890 slot, portdir, WorkerFlavorPrt); 1891 DoWorkerMounts(work); 1892 1893 /* 1894 * Generate an /etc/make.conf in the build base 1895 */ 1896 asprintf(&buf, "%s/etc/make.conf", work->basedir); 1897 fp = fopen(buf, "w"); 1898 dassert_errno(fp, "Unable to create %s\n", buf); 1899 for (benv = BuildEnv; benv; benv = benv->next) { 1900 if (benv->type & BENV_PKGLIST) 1901 continue; 1902 if ((benv->type & BENV_CMDMASK) == BENV_MAKECONF) { 1903 if (DebugOpt >= 2) { 1904 dlog(DLOG_ALL, "[%03d] ENV %s=%s\n", 1905 slot, benv->label, benv->data); 1906 } 1907 fprintf(fp, "%s=%s\n", benv->label, benv->data); 1908 } 1909 } 1910 fclose(fp); 1911 freestrp(&buf); 1912 1913 /* 1914 * Set up our hooks 1915 */ 1916 if (UsingHooks) 1917 initbulk(childHookRun, MaxBulk); 1918 1919 /* 1920 * Start phases 1921 */ 1922 wmsg.cmd = WMSG_CMD_INSTALL_PKGS; 1923 ipcwritemsg(fd, &wmsg); 1924 status = ipcreadmsg(fd, &wmsg); 1925 if (status < 0 || wmsg.cmd != WMSG_RES_INSTALL_PKGS) 1926 dfatal("pkg installation handshake failed"); 1927 do_install_phase = wmsg.status; 1928 1929 wmsg.cmd = WMSG_CMD_STATUS_UPDATE; 1930 wmsg.phase = PHASE_INSTALL_PKGS; 1931 wmsg.lines = 0; 1932 1933 status = ipcwritemsg(fd, &wmsg); 1934 1935 if (pkgpkg) { 1936 dophase(work, &wmsg, 1937 WDOG5, PHASE_PACKAGE, "package"); 1938 } else { 1939 if (do_install_phase) { 1940 dophase(work, &wmsg, 1941 WDOG4, PHASE_INSTALL_PKGS, "setup"); 1942 } 1943 dophase(work, &wmsg, 1944 WDOG2, PHASE_CHECK_SANITY, "check-sanity"); 1945 dophase(work, &wmsg, 1946 WDOG2, PHASE_PKG_DEPENDS, "pkg-depends"); 1947 dophase(work, &wmsg, 1948 WDOG7, PHASE_FETCH_DEPENDS, "fetch-depends"); 1949 dophase(work, &wmsg, 1950 WDOG7, PHASE_FETCH, "fetch"); 1951 dophase(work, &wmsg, 1952 WDOG2, PHASE_CHECKSUM, "checksum"); 1953 dophase(work, &wmsg, 1954 WDOG3, PHASE_EXTRACT_DEPENDS, "extract-depends"); 1955 dophase(work, &wmsg, 1956 WDOG3, PHASE_EXTRACT, "extract"); 1957 dophase(work, &wmsg, 1958 WDOG2, PHASE_PATCH_DEPENDS, "patch-depends"); 1959 dophase(work, &wmsg, 1960 WDOG2, PHASE_PATCH, "patch"); 1961 dophase(work, &wmsg, 1962 WDOG5, PHASE_BUILD_DEPENDS, "build-depends"); 1963 dophase(work, &wmsg, 1964 WDOG5, PHASE_LIB_DEPENDS, "lib-depends"); 1965 dophase(work, &wmsg, 1966 WDOG3, PHASE_CONFIGURE, "configure"); 1967 dophase(work, &wmsg, 1968 WDOG9, PHASE_BUILD, "build"); 1969 dophase(work, &wmsg, 1970 WDOG5, PHASE_RUN_DEPENDS, "run-depends"); 1971 dophase(work, &wmsg, 1972 WDOG5, PHASE_STAGE, "stage"); 1973 #if 0 1974 dophase(work, &wmsg, 1975 WDOG5, PHASE_TEST, "test"); 1976 #endif 1977 if (WorkerProcFlags & WORKER_PROC_CHECK_PLIST) { 1978 dophase(work, &wmsg, 1979 WDOG1, PHASE_CHECK_PLIST, "check-plist"); 1980 } 1981 dophase(work, &wmsg, 1982 WDOG5, PHASE_PACKAGE, "package"); 1983 #if 0 1984 dophase(work, &wmsg, 1985 WDOG5, PHASE_INSTALL_MTREE, "install-mtree"); 1986 dophase(work, &wmsg, 1987 WDOG5, PHASE_INSTALL, "install"); 1988 dophase(work, &wmsg, 1989 WDOG5, PHASE_DEINSTALL, "deinstall"); 1990 #endif 1991 } 1992 1993 if (MasterPtyFd >= 0) { 1994 close(MasterPtyFd); 1995 MasterPtyFd = -1; 1996 } 1997 1998 setproctitle("[%02d] WORKER CLEANUP %s%s", 1999 slot, portdir, WorkerFlavorPrt); 2000 2001 /* 2002 * Copy the package to the repo. 2003 */ 2004 if (work->accum_error == 0) { 2005 char *b1; 2006 char *b2; 2007 2008 asprintf(&b1, "%s/construction/%s/pkg/%s", 2009 work->basedir, pkg.portdir, pkg.pkgfile); 2010 asprintf(&b2, "%s/%s", RepositoryPath, pkg.pkgfile); 2011 if (copyfile(b1, b2)) { 2012 ++work->accum_error; 2013 dlog(DLOG_ALL, "[%03d] %s Unable to copy %s to %s\n", 2014 work->index, pkg.portdir, b1, b2); 2015 } 2016 free(b1); 2017 free(b2); 2018 } 2019 2020 /* 2021 * Unmount, unless we are in DebugStopMode. 2022 */ 2023 if ((WorkerProcFlags & WORKER_PROC_DEBUGSTOP) == 0) 2024 DoWorkerUnmounts(work); 2025 2026 /* 2027 * Send completion status to master dsynth worker thread. 2028 */ 2029 if (work->accum_error) { 2030 wmsg.cmd = WMSG_CMD_FAILURE; 2031 } else { 2032 wmsg.cmd = WMSG_CMD_SUCCESS; 2033 } 2034 ipcwritemsg(fd, &wmsg); 2035 if (WorkerProcFlags & WORKER_PROC_DEBUGSTOP) { 2036 wmsg.cmd = WMSG_CMD_FREEZEWORKER; 2037 ipcwritemsg(fd, &wmsg); 2038 } 2039 if (UsingHooks) { 2040 while ((bulk = getbulk()) != NULL) 2041 freebulk(bulk); 2042 donebulk(); 2043 } 2044 } 2045 2046 static void 2047 dophase(worker_t *work, wmsg_t *wmsg, int wdog, int phaseid, const char *phase) 2048 { 2049 pkg_t *pkg = work->pkg; 2050 char buf[1024]; 2051 pid_t pid; 2052 int status; 2053 int ms; 2054 pid_t wpid; 2055 int wpid_reaped; 2056 int fdlog; 2057 time_t start_time; 2058 time_t last_time; 2059 time_t next_time; 2060 time_t wdog_time; 2061 FILE *fp; 2062 2063 if (work->accum_error) 2064 return; 2065 setproctitle("[%02d] WORKER %-8.8s %s%s", 2066 work->index, phase, pkg->portdir, WorkerFlavorPrt); 2067 wmsg->phase = phaseid; 2068 if (ipcwritemsg(work->fds[0], wmsg) < 0) { 2069 dlog(DLOG_ALL, "[%03d] %s Lost Communication with dsynth, " 2070 "aborting worker\n", 2071 work->index, pkg->portdir); 2072 ++work->accum_error; 2073 return; 2074 } 2075 2076 /* 2077 * Execute the port make command in chroot on a pty. 2078 */ 2079 fflush(stdout); 2080 fflush(stderr); 2081 if (MasterPtyFd >= 0) { 2082 int slavefd; 2083 char ttybuf[2]; 2084 2085 /* 2086 * NOTE: We can't open the slave in the child because the 2087 * master may race a disconnection test. If we open 2088 * it in the parent our close() will flush any pending 2089 * output not read by the master (which is the same 2090 * parent process) and deadlock. 2091 * 2092 * Solve this by hand-shaking the slave tty to give 2093 * the master time to close its slavefd. 2094 * 2095 * Leave the tty defaults intact, which also likely 2096 * means it will be in line-buffered mode, so handshake 2097 * with a full line. 2098 * 2099 * TODO: Our handshake probably echos back to the master pty 2100 * due to tty echo, and ends up in the log, so just 2101 * pass through a newline. 2102 */ 2103 slavefd = open(ptsname(MasterPtyFd), O_RDWR); 2104 dassert_errno(slavefd >= 0, "Cannot open slave pty"); 2105 pid = fork(); 2106 if (pid == 0) { 2107 login_tty(slavefd); 2108 /* login_tty() closes slavefd */ 2109 read(0, ttybuf, 1); 2110 } else { 2111 close(slavefd); 2112 write(MasterPtyFd, "\n", 1); 2113 } 2114 } else { 2115 pid = forkpty(&MasterPtyFd, NULL, NULL, NULL); 2116 } 2117 2118 if (pid == 0) { 2119 struct termios tio; 2120 2121 /* 2122 * We are going through a pty, so set the tty modes to 2123 * Set tty modes so we do not get ^M's in the log files. 2124 * 2125 * This isn't fatal if it doesn't work. Remember that 2126 * our output goes through the pty to the management 2127 * process which will log it. 2128 */ 2129 if (tcgetattr(1, &tio) == 0) { 2130 tio.c_oflag |= OPOST | ONOCR; 2131 tio.c_oflag &= ~(OCRNL | ONLCR); 2132 tio.c_iflag |= ICRNL; 2133 tio.c_iflag &= ~(INLCR|IGNCR); 2134 if (tcsetattr(1, TCSANOW, &tio)) { 2135 printf("tcsetattr failed: %s\n", 2136 strerror(errno)); 2137 } 2138 } else { 2139 printf("tcgetattr failed: %s\n", strerror(errno)); 2140 } 2141 2142 /* 2143 * Additional phase-specific environment variables 2144 * 2145 * - Do not try to process missing depends outside of the 2146 * depends phases. Also relies on USE_PACKAGE_DEPENDS_ONLY 2147 * in the make.conf. 2148 */ 2149 switch(phaseid) { 2150 case PHASE_CHECK_SANITY: 2151 case PHASE_FETCH: 2152 case PHASE_CHECKSUM: 2153 case PHASE_EXTRACT: 2154 case PHASE_PATCH: 2155 case PHASE_CONFIGURE: 2156 case PHASE_STAGE: 2157 case PHASE_TEST: 2158 case PHASE_CHECK_PLIST: 2159 case PHASE_INSTALL_MTREE: 2160 case PHASE_INSTALL: 2161 case PHASE_DEINSTALL: 2162 break; 2163 case PHASE_PKG_DEPENDS: 2164 case PHASE_FETCH_DEPENDS: 2165 case PHASE_EXTRACT_DEPENDS: 2166 case PHASE_PATCH_DEPENDS: 2167 case PHASE_BUILD_DEPENDS: 2168 case PHASE_LIB_DEPENDS: 2169 case PHASE_RUN_DEPENDS: 2170 break; 2171 default: 2172 setenv("NO_DEPENDS", "1", 1); 2173 break; 2174 } 2175 2176 /* 2177 * Clean-up, chdir, and chroot. 2178 */ 2179 closefrom(3); 2180 if (chdir(work->basedir) < 0) 2181 dfatal_errno("chdir in phase initialization"); 2182 if (chroot(work->basedir) < 0) 2183 dfatal_errno("chroot in phase initialization"); 2184 2185 /* 2186 * We have a choice here on how to handle stdin (fd 0). 2187 * We can leave it connected to the pty in which case 2188 * the build will just block if it tries to ask a 2189 * question (and the watchdog will kill it, eventually), 2190 * or we can try to EOF the pty, or we can attach /dev/null 2191 * to descriptor 0. 2192 */ 2193 if (NullStdinOpt) { 2194 int fd; 2195 2196 fd = open("/dev/null", O_RDWR); 2197 dassert_errno(fd >= 0, "cannot open /dev/null"); 2198 if (fd != 0) { 2199 dup2(fd, 0); 2200 close(fd); 2201 } 2202 } 2203 2204 /* 2205 * Execute the appropriate command. 2206 */ 2207 switch(phaseid) { 2208 case PHASE_INSTALL_PKGS: 2209 snprintf(buf, sizeof(buf), "/tmp/dsynth_install_pkgs"); 2210 execl(buf, buf, NULL); 2211 break; 2212 default: 2213 snprintf(buf, sizeof(buf), "/xports/%s", pkg->portdir); 2214 execl(MAKE_BINARY, MAKE_BINARY, "-C", buf, phase, NULL); 2215 break; 2216 } 2217 _exit(1); 2218 } 2219 fcntl(MasterPtyFd, F_SETFL, O_NONBLOCK); 2220 2221 if (pid < 0) { 2222 dlog(DLOG_ALL, "[%03d] %s Fork Failed: %s\n", 2223 work->index, pkg->logfile, strerror(errno)); 2224 ++work->accum_error; 2225 return; 2226 } 2227 2228 SigPid = pid; 2229 2230 fdlog = open(pkg->logfile, O_RDWR|O_CREAT|O_APPEND, 0644); 2231 if (fdlog < 0) { 2232 dlog(DLOG_ALL, "[%03d] %s Cannot open logfile '%s': %s\n", 2233 work->index, pkg->portdir, 2234 pkg->logfile, strerror(errno)); 2235 } 2236 2237 snprintf(buf, sizeof(buf), 2238 "----------------------------------------" 2239 "---------------------------------------\n" 2240 "-- Phase: %s\n" 2241 "----------------------------------------" 2242 "---------------------------------------\n", 2243 phase); 2244 write(fdlog, buf, strlen(buf)); 2245 2246 start_time = time(NULL); 2247 last_time = start_time; 2248 wdog_time = start_time; 2249 wpid_reaped = 0; 2250 2251 status = 0; 2252 for (;;) { 2253 ms = mptylogpoll(MasterPtyFd, fdlog, wmsg, &wdog_time); 2254 if (ms == MPTY_FAILED) { 2255 dlog(DLOG_ALL, 2256 "[%03d] %s lost pty in phase %s, terminating\n", 2257 work->index, pkg->portdir, phase); 2258 break; 2259 } 2260 if (ms == MPTY_EOF) 2261 break; 2262 2263 /* 2264 * Generally speaking update status once a second. 2265 * This also allows us to detect if the management 2266 * dsynth process has gone away. 2267 */ 2268 next_time = time(NULL); 2269 if (next_time != last_time) { 2270 double dload[3]; 2271 double dv; 2272 int wdog_scaled; 2273 2274 /* 2275 * Send status update to the worker management thread 2276 * in the master dsynth process. Remember, *WE* are 2277 * the worker management process sub-fork. 2278 */ 2279 if (ipcwritemsg(work->fds[0], wmsg) < 0) 2280 break; 2281 last_time = next_time; 2282 2283 /* 2284 * Watchdog scaling 2285 */ 2286 getloadavg(dload, 3); 2287 dv = dload[2] / NumCores; 2288 if (dv < (double)NumCores) { 2289 wdog_scaled = wdog; 2290 } else { 2291 if (dv > 4.0 * NumCores) 2292 dv = 4.0 * NumCores; 2293 wdog_scaled = wdog * dv / NumCores; 2294 } 2295 2296 /* 2297 * Watchdog 2298 */ 2299 if (next_time - wdog_time >= wdog_scaled * 60) { 2300 snprintf(buf, sizeof(buf), 2301 "\n--------\n" 2302 "WATCHDOG TIMEOUT FOR %s in %s " 2303 "after %d minutes\n" 2304 "Killing pid %d\n" 2305 "--------\n", 2306 pkg->portdir, phase, wdog_scaled, pid); 2307 if (fdlog >= 0) 2308 write(fdlog, buf, strlen(buf)); 2309 dlog(DLOG_ALL, 2310 "[%03d] %s WATCHDOG TIMEOUT in %s " 2311 "after %d minutes (%d min scaled)\n", 2312 work->index, pkg->portdir, phase, 2313 wdog, wdog_scaled); 2314 kill(pid, SIGKILL); 2315 ++work->accum_error; 2316 break; 2317 } 2318 } 2319 2320 /* 2321 * Check process exit. Normally the pty will EOF 2322 * but if background processes remain we need to 2323 * check here to see if our primary exec is done, 2324 * so we can break out and reap those processes. 2325 * 2326 * Generally reap any other processes we have inherited 2327 * while we are here. 2328 */ 2329 do { 2330 wpid = wait3(&status, WNOHANG, NULL); 2331 } while (wpid > 0 && wpid != pid); 2332 if (wpid == pid && WIFEXITED(status)) { 2333 wpid_reaped = 1; 2334 break; 2335 } 2336 } 2337 2338 next_time = time(NULL); 2339 2340 setproctitle("[%02d] WORKER EXITREAP %s%s", 2341 work->index, pkg->portdir, WorkerFlavorPrt); 2342 2343 /* 2344 * We usually get here due to a mpty EOF, but not always as there 2345 * could be persistent processes still holding the slave. Finish 2346 * up getting the exit status for the main process we are waiting 2347 * on and clean out any data left on the MasterPtyFd (as it could 2348 * be blocking the exit). 2349 */ 2350 while (wpid_reaped == 0) { 2351 (void)mptylogpoll(MasterPtyFd, fdlog, wmsg, &wdog_time); 2352 wpid = waitpid(pid, &status, WNOHANG); 2353 if (wpid == pid && WIFEXITED(status)) { 2354 wpid_reaped = 1; 2355 break; 2356 } 2357 if (wpid < 0 && errno != EINTR) { 2358 break; 2359 } 2360 2361 /* 2362 * Safety. The normal phase waits until the fork/exec'd 2363 * pid finishes, causing a pty EOF on exit (the slave 2364 * descriptor is closed by the kernel on exit so the 2365 * process should already have exited). 2366 * 2367 * However, it is also possible to get here if the pty fails 2368 * for some reason. In this case, make sure that the process 2369 * is killed. 2370 */ 2371 kill(pid, SIGKILL); 2372 } 2373 2374 /* 2375 * Clean out anything left on the pty but don't wait around 2376 * because there could be background processes preventing the 2377 * slave side from closing. 2378 */ 2379 while (mptylogpoll(MasterPtyFd, fdlog, wmsg, &wdog_time) == MPTY_DATA) 2380 ; 2381 2382 /* 2383 * Report on the exit condition. If the pid was somehow lost 2384 * (probably due to someone gdb'ing the process), assume an error. 2385 */ 2386 if (wpid_reaped) { 2387 if (WEXITSTATUS(status)) { 2388 dlog(DLOG_ALL | DLOG_FILTER, 2389 "[%03d] %s Build phase '%s' failed exit %d\n", 2390 work->index, pkg->portdir, phase, 2391 WEXITSTATUS(status)); 2392 ++work->accum_error; 2393 } 2394 } else { 2395 dlog(DLOG_ALL, "[%03d] %s Build phase '%s' failed - lost pid\n", 2396 work->index, pkg->portdir, phase); 2397 ++work->accum_error; 2398 } 2399 2400 /* 2401 * Kill any processes still running (sometimes processes end up in 2402 * the background during a dports build), and clean up any other 2403 * children that we have inherited. 2404 */ 2405 phaseReapAll(); 2406 2407 /* 2408 * After the extraction phase add the space used by /construction 2409 * to the memory use. This helps us reduce the amount of paging 2410 * we do due to extremely large package extractions (languages, 2411 * chromium, etc). 2412 * 2413 * (dsynth already estimated the space used by the package deps 2414 * up front, but this will help us further). 2415 */ 2416 if (work->accum_error == 0 && phaseid == PHASE_EXTRACT) { 2417 struct statfs sfs; 2418 char *b1; 2419 2420 asprintf(&b1, "%s/construction", work->basedir); 2421 if (statfs(b1, &sfs) == 0) { 2422 wmsg->memuse = (sfs.f_blocks - sfs.f_bfree) * 2423 sfs.f_bsize; 2424 ipcwritemsg(work->fds[0], wmsg); 2425 } 2426 } 2427 2428 /* 2429 * Update log 2430 */ 2431 if (fdlog >= 0) { 2432 struct stat st; 2433 int h; 2434 int m; 2435 int s; 2436 2437 last_time = next_time - start_time; 2438 s = last_time % 60; 2439 m = last_time / 60 % 60; 2440 h = last_time / 3600; 2441 2442 fp = fdopen(fdlog, "a"); 2443 if (fp == NULL) { 2444 dlog(DLOG_ALL, "[%03d] %s Cannot fdopen fdlog: %s %d\n", 2445 work->index, pkg->portdir, 2446 strerror(errno), fstat(fdlog, &st)); 2447 close(fdlog); 2448 goto skip; 2449 } 2450 2451 fprintf(fp, "\n"); 2452 if (work->accum_error) { 2453 fprintf(fp, "FAILED %02d:%02d:%02d\n", h, m, s); 2454 } else { 2455 if (phaseid == PHASE_EXTRACT && wmsg->memuse) { 2456 fprintf(fp, "Extracted Memory Use: %6.2fM\n", 2457 wmsg->memuse / (1024.0 * 1024.0)); 2458 } 2459 fprintf(fp, "SUCCEEDED %02d:%02d:%02d\n", h, m, s); 2460 } 2461 last_time = next_time - work->start_time; 2462 s = last_time % 60; 2463 m = last_time / 60 % 60; 2464 h = last_time / 3600; 2465 if (phaseid == PHASE_PACKAGE) { 2466 fprintf(fp, "TOTAL TIME %02d:%02d:%02d\n", h, m, s); 2467 } 2468 fprintf(fp, "\n"); 2469 fclose(fp); 2470 skip: 2471 ; 2472 } 2473 2474 } 2475 2476 static void 2477 phaseReapAll(void) 2478 { 2479 struct reaper_status rs; 2480 int status; 2481 2482 while (procctl(P_PID, getpid(), PROC_REAP_STATUS, &rs) == 0) { 2483 if ((rs.flags & PROC_REAP_ACQUIRE) == 0) 2484 break; 2485 if (rs.pid_head < 0) 2486 break; 2487 if (kill(rs.pid_head, SIGKILL) == 0) { 2488 while (waitpid(rs.pid_head, &status, 0) < 0) 2489 ; 2490 } 2491 } 2492 while (wait3(&status, 0, NULL) > 0) 2493 ; 2494 } 2495 2496 static void 2497 phaseTerminateSignal(int sig __unused) 2498 { 2499 if (CopyFileFd >= 0) 2500 close(CopyFileFd); 2501 if (MasterPtyFd >= 0) 2502 close(MasterPtyFd); 2503 if (SigPid > 1) 2504 kill(SigPid, SIGKILL); 2505 phaseReapAll(); 2506 if (SigWork) 2507 DoWorkerUnmounts(SigWork); 2508 exit(1); 2509 } 2510 2511 static 2512 char * 2513 buildskipreason(pkglink_t *parent, pkg_t *pkg) 2514 { 2515 pkglink_t *link; 2516 pkg_t *scan; 2517 char *reason = NULL; 2518 char *ptr; 2519 size_t tot; 2520 size_t len; 2521 pkglink_t stack; 2522 2523 if ((pkg->flags & PKGF_NOBUILD_I) && pkg->ignore) 2524 asprintf(&reason, "%s ", pkg->ignore); 2525 2526 tot = 0; 2527 PKGLIST_FOREACH(link, &pkg->idepon_list) { 2528 #if 0 2529 if (link->dep_type > DEP_TYPE_BUILD) 2530 continue; 2531 #endif 2532 scan = link->pkg; 2533 if (scan == NULL) 2534 continue; 2535 if ((scan->flags & (PKGF_ERROR | PKGF_NOBUILD)) == 0) 2536 continue; 2537 if (scan->flags & PKGF_NOBUILD) { 2538 stack.pkg = scan; 2539 stack.next = parent; 2540 ptr = buildskipreason(&stack, scan); 2541 len = strlen(scan->portdir) + strlen(ptr) + 8; 2542 reason = realloc(reason, tot + len); 2543 snprintf(reason + tot, len, "%s->%s", 2544 scan->portdir, ptr); 2545 free(ptr); 2546 } else { 2547 len = strlen(scan->portdir) + 8; 2548 reason = realloc(reason, tot + len); 2549 snprintf(reason + tot, len, "%s", scan->portdir); 2550 } 2551 2552 /* 2553 * Don't try to print the entire graph 2554 */ 2555 if (parent) 2556 break; 2557 tot += strlen(reason + tot); 2558 reason[tot++] = ' '; 2559 reason[tot] = 0; 2560 } 2561 return (reason); 2562 } 2563 2564 /* 2565 * Count number of packages that would be skipped due to the 2566 * specified package having failed. 2567 * 2568 * Call with mode 1 to count, and mode 0 to clear the 2569 * cumulative rscan flag (used to de-duplicate the count). 2570 * 2571 * Must be serialized. 2572 */ 2573 static int 2574 buildskipcount_dueto(pkg_t *pkg, int mode) 2575 { 2576 pkglink_t *link; 2577 pkg_t *scan; 2578 int total; 2579 2580 total = 0; 2581 PKGLIST_FOREACH(link, &pkg->deponi_list) { 2582 scan = link->pkg; 2583 if (scan == NULL || scan->rscan == mode) 2584 continue; 2585 scan->rscan = mode; 2586 ++total; 2587 total += buildskipcount_dueto(scan, mode); 2588 } 2589 return total; 2590 } 2591 2592 /* 2593 * The master ptyfd is in non-blocking mode. Drain up to 1024 bytes 2594 * and update wmsg->lines and *wdog_timep as appropriate. 2595 * 2596 * This function will poll, stalling up to 1 second. 2597 */ 2598 static int 2599 mptylogpoll(int ptyfd, int fdlog, wmsg_t *wmsg, time_t *wdog_timep) 2600 { 2601 struct pollfd pfd; 2602 char buf[1024]; 2603 ssize_t r; 2604 2605 pfd.fd = ptyfd; 2606 pfd.events = POLLIN; 2607 pfd.revents = 0; 2608 2609 poll(&pfd, 1, 1000); 2610 if (pfd.revents) { 2611 r = read(ptyfd, buf, sizeof(buf)); 2612 if (r > 0) { 2613 *wdog_timep = time(NULL); 2614 if (r > 0 && fdlog >= 0) 2615 write(fdlog, buf, r); 2616 while (--r >= 0) { 2617 if (buf[r] == '\n') 2618 ++wmsg->lines; 2619 } 2620 return MPTY_DATA; 2621 } else if (r < 0) { 2622 if (errno != EINTR && errno != EAGAIN) 2623 return MPTY_FAILED; 2624 return MPTY_AGAIN; 2625 } else if (r == 0) { 2626 return MPTY_EOF; 2627 } 2628 } 2629 return MPTY_AGAIN; 2630 } 2631 2632 /* 2633 * Copy a (package) file from (src) to (dst), use an intermediate file and 2634 * rename to ensure that interruption does not leave us with a corrupt 2635 * package file. 2636 * 2637 * This is called by the WORKER process. 2638 * 2639 * (dsynth management thread -> WORKER process -> sub-processes) 2640 */ 2641 #define COPYBLKSIZE 32768 2642 2643 static int 2644 copyfile(char *src, char *dst) 2645 { 2646 char *tmp; 2647 char *buf; 2648 int fd1; 2649 int fd2; 2650 int error = 0; 2651 int mask; 2652 ssize_t r; 2653 2654 asprintf(&tmp, "%s.new", dst); 2655 buf = malloc(COPYBLKSIZE); 2656 2657 mask = sigsetmask(sigmask(SIGTERM)|sigmask(SIGINT)|sigmask(SIGHUP)); 2658 fd1 = open(src, O_RDONLY|O_CLOEXEC); 2659 fd2 = open(tmp, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0644); 2660 CopyFileFd = fd1; 2661 sigsetmask(mask); 2662 while ((r = read(fd1, buf, COPYBLKSIZE)) > 0) { 2663 if (write(fd2, buf, r) != r) 2664 error = 1; 2665 } 2666 if (r < 0) 2667 error = 1; 2668 mask = sigsetmask(sigmask(SIGTERM)|sigmask(SIGINT)|sigmask(SIGHUP)); 2669 CopyFileFd = -1; 2670 close(fd1); 2671 close(fd2); 2672 sigsetmask(mask); 2673 if (error) { 2674 remove(tmp); 2675 } else { 2676 if (rename(tmp, dst)) { 2677 error = 1; 2678 remove(tmp); 2679 } 2680 } 2681 2682 freestrp(&buf); 2683 freestrp(&tmp); 2684 2685 return error; 2686 } 2687 2688 /* 2689 * doHook() 2690 * 2691 * primary process (threaded) - run_start, run_end, pkg_ignored, pkg_skipped 2692 * worker process (threaded) - pkg_sucess, pkg_failure 2693 * 2694 * If waitfor is non-zero this hook will be serialized. 2695 */ 2696 static void 2697 doHook(pkg_t *pkg, const char *id, const char *path, int waitfor) 2698 { 2699 if (path == NULL) 2700 return; 2701 while (waitfor && getbulk() != NULL) 2702 ; 2703 if (pkg) 2704 queuebulk(pkg->portdir, id, path, pkg->pkgfile); 2705 else 2706 queuebulk(NULL, id, path, NULL); 2707 while (waitfor && getbulk() != NULL) 2708 ; 2709 } 2710 2711 /* 2712 * Execute hook (backend) 2713 * 2714 * s1 - portdir 2715 * s2 - id 2716 * s3 - script path 2717 * s4 - pkgfile (if applicable) 2718 */ 2719 static void 2720 childHookRun(bulk_t *bulk) 2721 { 2722 const char *cav[MAXCAC]; 2723 buildenv_t benv[MAXCAC]; 2724 char buf1[128]; 2725 char buf2[128]; 2726 char buf3[128]; 2727 char buf4[128]; 2728 FILE *fp; 2729 char *ptr; 2730 size_t len; 2731 pid_t pid; 2732 int cac; 2733 int bi; 2734 2735 cac = 0; 2736 bi = 0; 2737 bzero(benv, sizeof(benv)); 2738 2739 cav[cac++] = bulk->s3; 2740 2741 benv[bi].label = "PROFILE"; 2742 benv[bi].data = Profile; 2743 ++bi; 2744 2745 benv[bi].label = "DIR_PACKAGES"; 2746 benv[bi].data = PackagesPath; 2747 ++bi; 2748 2749 benv[bi].label = "DIR_REPOSITORY"; 2750 benv[bi].data = RepositoryPath; 2751 ++bi; 2752 2753 benv[bi].label = "DIR_PORTS"; 2754 benv[bi].data = DPortsPath; 2755 ++bi; 2756 2757 benv[bi].label = "DIR_OPTIONS"; 2758 benv[bi].data = OptionsPath; 2759 ++bi; 2760 2761 benv[bi].label = "DIR_DISTFILES"; 2762 benv[bi].data = DistFilesPath; 2763 ++bi; 2764 2765 benv[bi].label = "DIR_LOGS"; 2766 benv[bi].data = LogsPath; 2767 ++bi; 2768 2769 benv[bi].label = "DIR_BUILDBASE"; 2770 benv[bi].data = BuildBase; 2771 ++bi; 2772 2773 if (strcmp(bulk->s2, "hook_run_start") == 0) { 2774 snprintf(buf1, sizeof(buf1), "%d", BuildTotal); 2775 benv[bi].label = "PORTS_QUEUED"; 2776 benv[bi].data = buf1; 2777 ++bi; 2778 } else if (strcmp(bulk->s2, "hook_run_end") == 0) { 2779 snprintf(buf1, sizeof(buf1), "%d", BuildSuccessCount); 2780 benv[bi].label = "PORTS_BUILT"; 2781 benv[bi].data = buf1; 2782 ++bi; 2783 snprintf(buf2, sizeof(buf2), "%d", BuildFailCount); 2784 benv[bi].label = "PORTS_FAILED"; 2785 benv[bi].data = buf2; 2786 ++bi; 2787 snprintf(buf3, sizeof(buf3), "%d", BuildIgnoreCount); 2788 benv[bi].label = "PORTS_IGNORED"; 2789 benv[bi].data = buf3; 2790 ++bi; 2791 snprintf(buf4, sizeof(buf4), "%d", BuildSkipCount); 2792 benv[bi].label = "PORTS_SKIPPED"; 2793 benv[bi].data = buf4; 2794 ++bi; 2795 } else { 2796 /* 2797 * success, failure, ignored, skipped 2798 */ 2799 benv[bi].label = "RESULT"; 2800 if (strcmp(bulk->s2, "hook_pkg_success") == 0) { 2801 benv[bi].data = "success"; 2802 } else if (strcmp(bulk->s2, "hook_pkg_failure") == 0) { 2803 benv[bi].data = "failure"; 2804 } else if (strcmp(bulk->s2, "hook_pkg_ignored") == 0) { 2805 benv[bi].data = "ignored"; 2806 } else if (strcmp(bulk->s2, "hook_pkg_skipped") == 0) { 2807 benv[bi].data = "skipped"; 2808 } else { 2809 dfatal("Unknown hook id: %s", bulk->s2); 2810 /* NOT REACHED */ 2811 } 2812 ++bi; 2813 2814 /* 2815 * For compatibility with synth: 2816 * 2817 * ORIGIN does not include any @flavor, thus it is suitable 2818 * for finding the actual port directory/subdirectory. 2819 * 2820 * FLAVOR is set to ORIGIN if there is no flavor, otherwise 2821 * it is set to only the flavor sans the '@'. 2822 */ 2823 if ((ptr = strchr(bulk->s1, '@')) != NULL) { 2824 snprintf(buf1, sizeof(buf1), "%*.*s", 2825 (int)(ptr - bulk->s1), 2826 (int)(ptr - bulk->s1), 2827 bulk->s1); 2828 benv[bi].label = "ORIGIN"; 2829 benv[bi].data = buf1; 2830 ++bi; 2831 benv[bi].label = "FLAVOR"; 2832 benv[bi].data = ptr + 1; 2833 ++bi; 2834 } else { 2835 benv[bi].label = "ORIGIN"; 2836 benv[bi].data = bulk->s1; 2837 ++bi; 2838 benv[bi].label = "FLAVOR"; 2839 benv[bi].data = bulk->s1; 2840 ++bi; 2841 } 2842 benv[bi].label = "PKGNAME"; 2843 benv[bi].data = bulk->s4; 2844 ++bi; 2845 } 2846 2847 benv[bi].label = NULL; 2848 benv[bi].data = NULL; 2849 2850 fp = dexec_open(cav, cac, &pid, benv, 0, 0); 2851 while ((ptr = fgetln(fp, &len)) != NULL) 2852 ; 2853 2854 if (dexec_close(fp, pid)) { 2855 dlog(DLOG_ALL, 2856 "[XXX] %s SCRIPT %s (%s)\n", 2857 bulk->s1, bulk->s2, bulk->s3); 2858 } 2859 } 2860