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