2 * Copyright (c) 2019 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * This code uses concepts and configuration based on 'synth', by
8 * John R. Marino <draco@marino.st>, which was written in ada.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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
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.
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
39 worker_t WorkerAry[MAXWORKERS];
42 int DynamicMaxWorkers;
44 long RunningPkgDepSize;
45 pthread_mutex_t WorkerMutex;
46 pthread_cond_t WorkerCond;
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,
72 static void doHook(pkg_t *pkg, const char *id, const char *path, int waitfor);
73 static void childHookRun(bulk_t *bulk);
74 static void adjloadavg(double *dload);
76 static worker_t *SigWork;
77 static int MasterPtyFd = -1;
78 static int CopyFileFd = -1;
80 static const char *WorkerFlavorPrt = ""; /* "" or "@flavor" */
82 #define MPTY_FAILED -2
92 int BuildSuccessCount;
95 * Initialize the WorkerAry[]
98 DoInitBuild(int slot_override)
104 ddassert(slot_override < 0 || MaxWorkers == 1);
106 bzero(WorkerAry, MaxWorkers * sizeof(worker_t));
107 pthread_mutex_init(&WorkerMutex, NULL);
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);
119 * Create required sub-directories. The base directories must already
120 * exist as a dsynth configuration safety.
122 if (stat(RepositoryPath, &st) < 0) {
123 if (mkdir(RepositoryPath, 0755) < 0)
124 dfatal("Cannot mkdir %s\n", RepositoryPath);
127 BuildInitialized = 1;
130 * slow-start (increases at a rate of 1 per 5 seconds)
132 if (SlowStartOpt > MaxWorkers)
133 DynamicMaxWorkers = MaxWorkers;
134 else if (SlowStartOpt > 0)
135 DynamicMaxWorkers = SlowStartOpt;
137 DynamicMaxWorkers = MaxWorkers;
141 * Called by the frontend to clean-up any hanging mounts.
144 DoCleanBuild(int resetlogs)
148 ddassert(BuildInitialized);
152 for (i = 0; i < MaxWorkers; ++i) {
153 DoWorkerUnmounts(&WorkerAry[i]);
160 pkg_t *build_list = NULL;
161 pkg_t **build_tail = &build_list;
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.
177 initbulk(childHookRun, MaxBulk);
180 * Count up the packages, not counting dummies
182 for (scan = pkgs; scan; scan = scan->bnext) {
183 if ((scan->flags & PKGF_DUMMY) == 0)
187 doHook(NULL, "hook_run_start", HookRunStart, 1);
190 * The pkg and pkg-static binaries are needed. If already present
191 * then assume that the template is also valid, otherwise build
194 scan = GetPkgPkg(pkgs);
197 * Create our template. The template will be missing pkg
200 if ((scan->flags & (PKGF_SUCCESS | PKGF_PACKAGED)) == 0) {
201 /* force a fresh template */
202 newtemplate = DoCreateTemplate(1);
204 newtemplate = DoCreateTemplate(0);
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
213 pthread_mutex_lock(&WorkerMutex);
214 startTime = time(NULL);
219 * Build pkg/pkg-static.
221 if ((scan->flags & (PKGF_SUCCESS | PKGF_PACKAGED)) == 0) {
223 build_tail = &scan->build_next;
224 startbuild(&build_list, &build_tail);
225 while (RunningWorkers == 1)
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'");
238 * Install pkg/pkg-static into the template
246 "tar --exclude '+*' --exclude '*/man/*' "
247 "-xvzpf %s/%s > /dev/null 2>&1",
253 dfatal("Command failed: %s\n", buf);
258 * Calculate depi_depth, the longest chain of dependencies
259 * for who depends on me, weighted by powers of two.
261 for (scan = pkgs; scan; scan = scan->bnext) {
262 buildCalculateDepiDepth(scan);
266 * Nominal bulk build sequence
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;
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.
280 if (scan->flags & (PKGF_SUCCESS | PKGF_FAILURE |
283 ddprintf(0, "%s: already built\n",
288 build_find_leaves(NULL, scan, &build_tail,
289 &ap, &haswork, 0, first, 0);
290 ddprintf(0, "TOPLEVEL %s %08x\n",
293 scan->flags &= ~PKGF_BUILDLOOP;
294 build_clear_trav(scan);
298 startbuild(&build_list, &build_tail);
300 if (haswork == 0 && RunningWorkers) {
301 waitbuild(RunningWorkers, 1);
305 pthread_mutex_unlock(&WorkerMutex);
307 RunStatsUpdateTop(0);
308 RunStatsUpdateLogs();
312 doHook(NULL, "hook_run_end", HookRunEnd, 1);
314 while ((bulk = getbulk()) != NULL)
319 t = time(NULL) - startTime;
324 dlog(DLOG_ALL|DLOG_STDOUT,
326 "Initial queue size: %d\n"
327 " packages built: %d\n"
332 "Duration: %02d:%02d:%02d\n"
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.
347 * (pkg) itself is only added to the list if it is immediately buildable.
351 build_find_leaves(pkg_t *parent, pkg_t *pkg, pkg_t ***build_tailp,
352 int *app, int *hasworkp, int depth, int first,
366 * Already on build list, possibly in-progress, tell caller that
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);
380 PKGLIST_FOREACH(link, &pkg->idepon_list) {
388 ddprintf(ndepth, "check %s %08x\t", scan->portdir, scan->flags);
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.
395 * DUMMY nodes can be marked SUCCESS so the build skips past
396 * them, but this doesn't mean that their sub-nodes succeeded.
397 * We have to check, so recurse even if it is marked
400 * NOTE: The depth is not being for complex dependency type
401 * tests like it is in childInstallPkgDeps_recurse(),
402 * so we don't have to hicup it like we do in that
405 dfirst_one_only = (scan->flags & PKGF_DUMMY) ? 1 : 0;
410 * When accounting for a successful build, just bump
411 * idep_count by one. scan->idep_count will heavily
412 * overlap packages that we count down multiple branches.
414 * We must still recurse through PACKAGED packages as
415 * some of their dependencies might be missing.
417 if (scan->flags & PKGF_SUCCESS) {
418 ddprintf(0, "SUCCESS - OK\n");
426 * ERROR includes FAILURE, which is set in numerous situations
427 * including when NOBUILD state is processed. So check for
428 * NOBUILD state first.
430 * An ERROR in a sub-package causes a NOBUILD in packages
433 if (scan->flags & PKGF_NOBUILD) {
434 ddprintf(0, "NOBUILD - OK "
435 "(propagate failure upward)\n");
436 *app |= PKGF_NOBUILD_S;
441 if (scan->flags & PKGF_ERROR) {
442 ddprintf(0, "ERROR - OK (propagate failure upward)\n");
443 *app |= PKGF_NOBUILD_S;
450 * If already on build-list this dependency is not ready.
452 if (scan->flags & PKGF_BUILDLIST) {
453 ddprintf(0, " [BUILDLIST]");
454 *app |= PKGF_NOTREADY;
458 * If not packaged this dependency is not ready for
461 if ((scan->flags & PKGF_PACKAGED) == 0) {
462 ddprintf(0, " [NOT_PACKAGED]");
463 *app |= PKGF_NOTREADY;
467 * Reduce search complexity, if we have already processed
468 * scan in the traversal it will either already be on the
469 * build list or it will not be buildable. Either way
470 * the parent is not buildable.
472 if (scan->flags & PKGF_BUILDTRAV) {
473 ddprintf(0, " [BUILDTRAV]\n");
474 *app |= PKGF_NOTREADY;
482 * Assert on dependency loop
485 if (scan->flags & PKGF_BUILDLOOP) {
486 dfatal("pkg dependency loop %s -> %s",
487 parent->portdir, scan->portdir);
491 * NOTE: For debug tabbing purposes we use (ndepth + 1)
492 * here (i.e. depth + 2) in our iteration.
494 scan->flags |= PKGF_BUILDLOOP;
496 ddprintf(0, " SUBRECURSION {\n");
497 idep_count += build_find_leaves(pkg, scan, build_tailp,
501 scan->flags &= ~PKGF_BUILDLOOP;
503 if (apsub & PKGF_NOBUILD) {
504 ddprintf(ndepth, "} (sub-nobuild)\n");
505 } else if (apsub & PKGF_ERROR) {
506 ddprintf(ndepth, "} (sub-error)\n");
507 } else if (apsub & PKGF_NOTREADY) {
508 ddprintf(ndepth, "} (sub-notready)\n");
510 ddprintf(ndepth, "} (sub-ok)\n");
515 pkg->idep_count = idep_count;
516 pkg->flags |= PKGF_BUILDTRAV;
519 * Incorporate scan results into pkg state.
521 if ((pkg->flags & PKGF_NOBUILD) == 0 && (*app & PKGF_NOBUILD)) {
523 } else if ((pkg->flags & PKGF_ERROR) == 0 && (*app & PKGF_ERROR)) {
526 pkg->flags |= *app & ~PKGF_NOTREADY;
529 * Clear PACKAGED bit if sub-dependencies aren't clean
531 if ((pkg->flags & PKGF_PACKAGED) &&
532 (pkg->flags & (PKGF_NOTREADY|PKGF_ERROR|PKGF_NOBUILD))) {
533 pkg->flags &= ~PKGF_PACKAGED;
534 ddassert(pkg->pkgfile);
535 asprintf(&buf, "%s/%s", RepositoryPath, pkg->pkgfile);
536 if (remove(buf) < 0) {
538 "[XXX] %s DELETE-PACKAGE %s (failed)\n",
542 "[XXX] %s DELETE-PACKAGE %s "
543 "(due to dependencies)\n",
551 * Set PKGF_NOBUILD_I if there is IGNORE data
554 pkg->flags |= PKGF_NOBUILD_I;
557 * Handle propagated flags
559 if (pkg->flags & PKGF_ERROR) {
561 * This can only happen if the ERROR has already been
562 * processed and accounted for.
564 ddprintf(depth, "} (ERROR - %s)\n", pkg->portdir);
565 } else if (*app & PKGF_NOTREADY) {
567 * We don't set PKGF_NOTREADY in the pkg, it is strictly
568 * a transient flag propagated via build_find_leaves().
570 * Just don't add the package to the list.
572 * NOTE: Even if NOBUILD is set (meaning we could list it
573 * and let startbuild() finish it up as a skip, we
574 * don't process it to the list because we want to
575 * process all the dependencies, so someone doing a
576 * manual build can get more complete information and
577 * does not have to iterate each failed dependency one
581 } else if (pkg->flags & PKGF_SUCCESS) {
582 ddprintf(depth, "} (SUCCESS - %s)\n", pkg->portdir);
583 } else if (pkg->flags & PKGF_DUMMY) {
585 * Just mark dummy packages as successful when all of their
586 * sub-depends (flavors) complete successfully. Note that
587 * dummy packages are not counted in the total, so do not
588 * decrement BuildTotal.
590 * Do not propagate *app up for the dummy node. If there
591 * is a generic dependency (i.e. no flavor specified), the
592 * upper recursion detects PKGF_DUMMY and traverses through
593 * to the default flavor without checking error/nobuild
596 if (pkg->flags & PKGF_NOBUILD) {
597 ddprintf(depth, "} (DUMMY/META - IGNORED)\n");
599 ddprintf(depth, "} (DUMMY/META - SUCCESS)\n");
600 pkg->flags |= PKGF_SUCCESS;
603 dlog(DLOG_ALL | DLOG_FILTER,
604 "[XXX] %s META-ALREADY-BUILT\n",
607 dlog(DLOG_SUCC, "[XXX] %s meta-node complete\n",
609 RunStatsUpdateCompletion(NULL, DLOG_SUCC, pkg,
613 } else if (pkg->flags & PKGF_PACKAGED) {
615 * We can just mark the pkg successful. If this is
616 * the first pass, we count this as an initial pruning
617 * pass and reduce BuildTotal.
619 ddprintf(depth, "} (PACKAGED - SUCCESS)\n");
620 pkg->flags |= PKGF_SUCCESS;
623 dlog(DLOG_ALL | DLOG_FILTER,
624 "[XXX] %s ALREADY-BUILT\n",
630 * All dependencies are successful, queue new work
631 * and indicate not-ready to the parent (since our
632 * package has to be built).
634 * NOTE: The NOBUILD case propagates to here as well
635 * and is ultimately handled by startbuild().
638 if (pkg->flags & PKGF_NOBUILD_I)
639 ddprintf(depth, "} (ADDLIST(IGNORE/BROKEN) - %s)\n",
641 else if (pkg->flags & PKGF_NOBUILD)
642 ddprintf(depth, "} (ADDLIST(NOBUILD) - %s)\n",
645 ddprintf(depth, "} (ADDLIST - %s)\n", pkg->portdir);
646 pkg->flags |= PKGF_BUILDLIST;
648 *build_tailp = &pkg->build_next;
649 *app |= PKGF_NOTREADY;
657 build_clear_trav(pkg_t *pkg)
662 pkg->flags &= ~PKGF_BUILDTRAV;
663 PKGLIST_FOREACH(link, &pkg->idepon_list) {
665 if (scan && (scan->flags & PKGF_BUILDTRAV))
666 build_clear_trav(scan);
671 * Calculate the longest chain of packages that depend on me. The
672 * long the chain, the more important my package is to build earlier
676 buildCalculateDepiDepth(pkg_t *pkg)
684 return(pkg->depi_depth + 1);
685 pkg->flags |= PKGF_BUILDLOOP;
686 PKGLIST_FOREACH(link, &pkg->deponi_list) {
688 if (scan && (scan->flags & PKGF_BUILDLOOP) == 0) {
689 res = buildCalculateDepiDepth(scan);
690 if (best_depth < res)
694 pkg->flags &= ~PKGF_BUILDLOOP;
695 pkg->depi_depth = best_depth;
697 return (best_depth + 1);
701 * Take a list of pkg ready to go, sort it, and assign it to worker
702 * slots. This routine blocks in waitbuild() until it can dispose of
705 * WorkerMutex is held by the caller.
709 startbuild(pkg_t **build_listp, pkg_t ***build_tailp)
720 static int IterateWorker;
725 if (*build_listp == NULL)
732 for (pkg = *build_listp; pkg; pkg = pkg->build_next)
734 idep_ary = calloc(count, sizeof(pkg_t *));
735 depi_ary = calloc(count, sizeof(pkg_t *));
738 for (pkg = *build_listp; pkg; pkg = pkg->build_next) {
739 idep_ary[count] = pkg;
740 depi_ary[count] = pkg;
745 * idep_ary - sorted by #of dependencies this pkg has.
746 * depi_ary - sorted by #of other packages that depend on this pkg.
748 qsort(idep_ary, count, sizeof(pkg_t *), qsort_idep);
749 qsort(depi_ary, count, sizeof(pkg_t *), qsort_depi);
755 * Half the workers build based on the highest depi count,
756 * the other half build based on the highest idep count.
758 * This is an attempt to get projects which many other projects
759 * depend on built first, but to also try to build large projects
760 * (which tend to have a lot of dependencies) earlier rather than
761 * later so the end of the bulk run doesn't inefficiently build
762 * the last few huge projects.
764 * Loop until we manage to assign slots to everyone. We do not
765 * wait for build completion.
767 * This is the point where we handle DUMMY packages (these are
768 * dummy unflavored packages which 'cover' all the flavors for
769 * a package). These are not real packages are marked SUCCESS
770 * at this time because their dependencies (the flavors) have all
773 while (idep_index != count || depi_index != count) {
778 * Find candidate to start sorted by depi or idep.
781 while (idep_index < count) {
782 ipkg = idep_ary[idep_index];
784 (PKGF_SUCCESS | PKGF_FAILURE |
785 PKGF_ERROR | PKGF_RUNNING)) == 0) {
793 while (depi_index < count) {
794 pkgi = depi_ary[depi_index];
796 (PKGF_SUCCESS | PKGF_FAILURE |
797 PKGF_ERROR | PKGF_RUNNING)) == 0) {
805 * ipkg and pkgi must either both be NULL, or both
808 if (ipkg == NULL && pkgi == NULL)
810 ddassert(ipkg && pkgi);
813 * Handle the NOBUILD case right here, there's no point
814 * queueing it anywhere.
816 if (ipkg->flags & PKGF_NOBUILD) {
821 scount = buildskipcount_dueto(ipkg, 1);
822 buildskipcount_dueto(ipkg, 0);
824 snprintf(skipbuf, sizeof(skipbuf), " %d",
830 ipkg->flags |= PKGF_FAILURE;
831 ipkg->flags &= ~PKGF_BUILDLIST;
833 reason = buildskipreason(NULL, ipkg);
834 if (ipkg->flags & PKGF_NOBUILD_I) {
837 "[XXX] %s%s ignored due to %s\n",
838 ipkg->portdir, skipbuf, reason);
839 RunStatsUpdateCompletion(NULL, DLOG_IGN, ipkg,
841 doHook(ipkg, "hook_pkg_ignored",
846 "[XXX] %s%s skipped due to %s\n",
847 ipkg->portdir, skipbuf, reason);
848 RunStatsUpdateCompletion(NULL, DLOG_SKIP, ipkg,
850 doHook(ipkg, "hook_pkg_skipped",
857 if (pkgi->flags & PKGF_NOBUILD) {
862 scount = buildskipcount_dueto(pkgi, 1);
863 buildskipcount_dueto(pkgi, 0);
865 snprintf(skipbuf, sizeof(skipbuf), " %d",
871 pkgi->flags |= PKGF_FAILURE;
872 pkgi->flags &= ~PKGF_BUILDLIST;
874 reason = buildskipreason(NULL, pkgi);
875 if (pkgi->flags & PKGF_NOBUILD_I) {
877 dlog(DLOG_IGN, "[XXX] %s%s ignored due to %s\n",
878 pkgi->portdir, skipbuf, reason);
879 RunStatsUpdateCompletion(NULL, DLOG_IGN, pkgi,
881 doHook(pkgi, "hook_pkg_ignored",
886 "[XXX] %s%s skipped due to %s\n",
887 pkgi->portdir, skipbuf, reason);
888 RunStatsUpdateCompletion(NULL, DLOG_SKIP, pkgi,
890 doHook(pkgi, "hook_pkg_skipped",
899 * Block while no slots are available. waitbuild()
900 * will clean out any DONE states.
902 while (RunningWorkers >= DynamicMaxWorkers ||
903 RunningWorkers >= MaxWorkers - FailedWorkers) {
904 waitbuild(RunningWorkers, 1);
908 * Find an available worker slot, there should be at
911 for (i = 0; i < MaxWorkers; ++i) {
912 n = IterateWorker % MaxWorkers;
913 work = &WorkerAry[n];
915 if (work->state == WORKER_DONE ||
916 work->state == WORKER_FAILED) {
917 workercomplete(work);
919 if (work->state == WORKER_NONE ||
920 work->state == WORKER_IDLE) {
921 if (n <= MaxWorkers / 2) {
922 startworker(pkgi, work);
924 startworker(ipkg, work);
926 /*RunStatsUpdate(work);*/
931 ddassert(i != MaxWorkers);
936 * We disposed of the whole list
941 *build_tailp = build_listp;
944 typedef const pkg_t *pkg_tt;
947 qsort_idep(const void *pkg1_arg, const void *pkg2_arg)
949 const pkg_t *pkg1 = *(const pkg_tt *)pkg1_arg;
950 const pkg_t *pkg2 = *(const pkg_tt *)pkg2_arg;
952 return (pkg2->idep_count - pkg1->idep_count);
956 qsort_depi(const void *pkg1_arg, const void *pkg2_arg)
958 const pkg_t *pkg1 = *(const pkg_tt *)pkg1_arg;
959 const pkg_t *pkg2 = *(const pkg_tt *)pkg2_arg;
961 return ((pkg2->depi_count * pkg2->depi_depth) -
962 (pkg1->depi_count * pkg1->depi_depth));
966 * Frontend starts a pkg up on a worker
968 * WorkerMutex must be held.
971 startworker(pkg_t *pkg, worker_t *work)
973 switch(work->state) {
975 pthread_create(&work->td, NULL, childBuilderThread, work);
976 work->state = WORKER_IDLE;
980 childInstallPkgDeps_recurse(NULL, &pkg->idepon_list, 0, 1, 0);
981 childInstallPkgDeps_recurse(NULL, &pkg->idepon_list, 1, 1, 0);
982 RunningPkgDepSize += work->pkg_dep_size;
984 dlog(DLOG_ALL, "[%03d] START %s "
985 "##idep=%02d depi=%02d/%02d dep=%-4.2fG\n",
986 work->index, pkg->portdir,
987 pkg->idep_count, pkg->depi_count, pkg->depi_depth,
988 (double)work->pkg_dep_size / (double)ONEGB);
991 pkg->flags |= PKGF_RUNNING;
993 pthread_cond_signal(&work->cond);
995 /*RunStatsUpdate(work);*/
1002 case WORKER_EXITING:
1004 dfatal("startworker: [%03d] Unexpected state %d for worker %d",
1005 work->index, work->state, work->index);
1011 cleanworker(worker_t *work)
1013 work->state = WORKER_PENDING;
1015 work->accum_error = 0;
1016 work->start_time = time(NULL);
1020 * Frontend finishes up a completed pkg on a worker.
1022 * If the worker is in a FAILED state we clean the pkg out but (for now)
1023 * leave it in its failed state so we can debug. At this point
1024 * workercomplete() will be called every once in a while on the state
1025 * and we have to deal with the NULL pkg.
1027 * WorkerMutex must be held.
1030 workercomplete(worker_t *work)
1039 * Steady state FAILED case.
1041 if (work->state == WORKER_FAILED) {
1042 if (work->pkg == NULL)
1046 t = time(NULL) - work->start_time;
1052 * Reduce total dep size
1054 RunningPkgDepSize -= work->pkg_dep_size;
1055 RunningPkgDepSize -= work->memuse;
1056 work->pkg_dep_size = 0;
1060 * Process pkg out of the worker
1063 if (pkg->flags & (PKGF_ERROR|PKGF_NOBUILD)) {
1067 pkg->flags |= PKGF_FAILURE;
1069 scount = buildskipcount_dueto(pkg, 1);
1070 buildskipcount_dueto(pkg, 0);
1072 snprintf(skipbuf, sizeof(skipbuf), " %d",
1079 * This NOBUILD condition XXX can occur if the package is
1080 * not allowed to be built.
1082 if (pkg->flags & PKGF_NOBUILD) {
1085 reason = buildskipreason(NULL, pkg);
1086 if (pkg->flags & PKGF_NOBUILD_I) {
1088 dlog(DLOG_SKIP, "[%03d] IGNORD %s%s - %s\n",
1089 work->index, pkg->portdir,
1091 RunStatsUpdateCompletion(work, DLOG_SKIP, pkg,
1093 doHook(pkg, "hook_pkg_ignored",
1097 dlog(DLOG_SKIP, "[%03d] SKIPPD %s%s - %s\n",
1098 work->index, pkg->portdir,
1100 RunStatsUpdateCompletion(work, DLOG_SKIP, pkg,
1102 doHook(pkg, "hook_pkg_skipped",
1108 dlog(DLOG_FAIL | DLOG_RED,
1109 "[%03d] FAILURE %s%s ##%16.16s %02d:%02d:%02d\n",
1110 work->index, pkg->portdir, skipbuf,
1111 getphasestr(work->phase),
1113 RunStatsUpdateCompletion(work, DLOG_FAIL, pkg,
1115 doHook(pkg, "hook_pkg_failure", HookPkgFailure, 0);
1118 pkg->flags |= PKGF_SUCCESS;
1119 ++BuildSuccessCount;
1120 dlog(DLOG_SUCC | DLOG_GRN,
1121 "[%03d] SUCCESS %s ##%02d:%02d:%02d\n",
1122 work->index, pkg->portdir, h, m, s);
1123 RunStatsUpdateCompletion(work, DLOG_SUCC, pkg, "", "");
1124 doHook(pkg, "hook_pkg_success", HookPkgSuccess, 0);
1127 pkg->flags &= ~PKGF_BUILDLIST;
1128 pkg->flags &= ~PKGF_RUNNING;
1132 if (work->state == WORKER_FAILED) {
1133 dlog(DLOG_ALL, "[%03d] XXX/XXX WORKER IS IN A FAILED STATE\n",
1136 } else if (work->flags & WORKERF_FREEZE) {
1137 dlog(DLOG_ALL, "[%03d] FROZEN(DEBUG) %s\n",
1138 work->index, pkg->portdir);
1139 work->state = WORKER_FROZEN;
1141 work->state = WORKER_IDLE;
1146 * Wait for one or more workers to complete.
1148 * WorkerMutex must be held.
1151 waitbuild(int whilematch, int dynamicmax)
1153 static time_t wblast_time;
1154 static time_t dmlast_time;
1160 if (whilematch == 0)
1163 while (RunningWorkers == whilematch) {
1164 for (i = 0; i < MaxWorkers; ++i) {
1165 work = &WorkerAry[i];
1166 if (work->state == WORKER_DONE ||
1167 work->state == WORKER_FAILED) {
1168 workercomplete(work);
1170 pthread_cond_signal(&work->cond);
1172 RunStatsUpdate(work, NULL);
1174 RunStatsUpdateTop(1);
1175 RunStatsUpdateLogs();
1177 if (RunningWorkers == whilematch) {
1178 clock_gettime(CLOCK_REALTIME, &ts);
1181 pthread_cond_timedwait(&WorkerCond, &WorkerMutex, &ts);
1185 * Dynamically reduce MaxWorkers based on the load. When
1186 * the load exceeds 2 x ncpus we reduce the number of workers
1187 * up to 75% of MaxWorkers @ (5 x ncpus) load.
1189 * Dynamically reduce MaxWorkers based on swap use, starting
1190 * at 10% swap and up to 75% of MaxWorkers at 40% swap.
1192 * NOTE! Generally speaking this allows more workers to be
1193 * configured which helps in two ways. First it allows
1194 * a higher build rate for smaller packages. Second
1195 * it allows dsynth to ratchet-down the number of slots
1196 * when large packages are forcing the load up.
1198 * A high load doesn't hurt efficiency, but swap usage
1199 * due to loading the tmpfs in lots of worker slots up
1200 * with tons of pkg install's (pre-reqs for a build)
1201 * does. Reducing the number of worker slots has a
1202 * huge beneficial effect on reducing swap use / paging.
1205 if (dynamicmax && (wblast_time == 0 ||
1206 (unsigned)(t - wblast_time) >= 5)) {
1207 double min_load = 1.5 * NumCores;
1208 double max_load = 5.0 * NumCores;
1209 double min_swap = 0.10;
1210 double max_swap = 0.40;
1222 * Cap based on load. This is back-loaded.
1224 getloadavg(dload, 3);
1226 if (dload[0] < min_load) {
1228 } else if (dload[0] <= max_load) {
1231 (dload[0] - min_load) /
1232 (max_load - min_load);
1234 max1 = MaxWorkers * 25 / 100;
1238 * Cap based on swap use. This is back-loaded.
1240 dswap = getswappct(&noswap);
1241 if (dswap < min_swap) {
1243 } else if (dswap <= max_swap) {
1246 (dswap - min_swap) /
1247 (max_swap - min_swap);
1249 max2 = MaxWorkers * 25 / 100;
1253 * Cap based on aggregate pkg-dependency memory
1254 * use installed in worker slots. This is
1257 * Since it can take a while for workers to retire
1258 * (to reduce RunningPkgDepSize), just set our
1259 * target 1 below the current run count to allow
1260 * jobs to retire without being replaced with new
1263 * In addition, in order to avoid a paging 'shock',
1264 * We enforce a 30 second-per-increment slow-start
1265 * once RunningPkgDepSize exceeds 1/2 the target.
1267 if (RunningPkgDepSize > PkgDepMemoryTarget) {
1268 max3 = RunningWorkers - 1;
1269 } else if (RunningPkgDepSize > PkgDepMemoryTarget / 2) {
1270 if (dmlast_time == 0 ||
1271 (unsigned)(t - dmlast_time) >= 30) {
1273 max3 = RunningWorkers + 1;
1275 max3 = RunningWorkers;
1282 * Priority reduction, convert to DynamicMaxWorkers
1291 * Restrict to allowed range, and also handle
1296 if (max_sel > DynamicMaxWorkers + 1)
1297 max_sel = DynamicMaxWorkers + 1;
1298 if (max_sel > MaxWorkers)
1299 max_sel = MaxWorkers;
1302 * Stop waiting if DynamicMaxWorkers is going to
1305 if (DynamicMaxWorkers < max1)
1311 if (DynamicMaxWorkers != max1) {
1312 dlog(DLOG_ALL | DLOG_FILTER,
1313 "[XXX] Load=%-6.2f(%2d) "
1314 "Swap=%-3.2f%%(%2d) "
1316 "Adjust Workers %d->%d\n",
1318 dswap * 100.0, max2,
1319 RunningPkgDepSize / (double)ONEGB, max3,
1320 DynamicMaxWorkers, max_sel);
1321 DynamicMaxWorkers = max_sel;
1329 * Worker pthread (WorkerAry)
1331 * This thread belongs to the dsynth master process and handled a worker slot.
1332 * (this_thread) -> WORKER fork/exec (WorkerPocess) -> (pty) -> sub-processes.
1335 childBuilderThread(void *arg)
1337 char *envary[1] = { NULL };
1338 worker_t *work = arg;
1344 volatile int dowait;
1349 pthread_mutex_lock(&WorkerMutex);
1350 while (work->terminate == 0) {
1353 switch(work->state) {
1356 case WORKER_PENDING:
1358 * Fork the management process for the pkg operation
1359 * on this worker slot.
1361 * This process will set up the environment, do the
1362 * mounts, will become the reaper, and will process
1363 * pipe commands and handle chroot operations.
1365 * NOTE: If SOCK_CLOEXEC is not supported WorkerMutex
1366 * is sufficient to interlock F_SETFD FD_CLOEXEC
1369 ddassert(work->pkg);
1370 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC,
1371 PF_UNSPEC, work->fds)) {
1372 dfatal_errno("socketpair() during worker fork");
1374 snprintf(slotbuf, sizeof(slotbuf), "%d", work->index);
1375 snprintf(fdbuf, sizeof(fdbuf), "3");
1378 * Pass global flags and add-in the DEBUGSTOP if
1379 * the package is flagged for debugging.
1381 flags = WorkerProcFlags;
1382 if (work->pkg->flags & PKGF_DEBUGSTOP) {
1383 flags |= WORKER_PROC_DEBUGSTOP;
1385 flags &= ~WORKER_PROC_DEBUGSTOP;
1387 snprintf(flagsbuf, sizeof(flagsbuf), "%d", flags);
1393 * We pass the salve descriptor in fd 3 and close all
1394 * other descriptors for security.
1396 pthread_mutex_unlock(&WorkerMutex);
1399 close(work->fds[0]);
1400 dup2(work->fds[1], 3);
1402 fcntl(3, F_SETFD, 0);
1403 execle(DSynthExecPath, DSynthExecPath,
1405 "WORKER", slotbuf, fdbuf,
1406 work->pkg->portdir, work->pkg->pkgfile,
1409 write(2, "EXECLE FAILURE\n", 15);
1412 pthread_mutex_lock(&WorkerMutex);
1413 close(work->fds[1]);
1414 work->phase = PHASE_PENDING;
1418 work->state = WORKER_RUNNING;
1420 case WORKER_RUNNING:
1422 * Poll for status updates, if NULL is returned
1423 * and status is non-zero, the communications link
1424 * failed unexpectedly.
1427 pthread_mutex_unlock(&WorkerMutex);
1428 status = ipcreadmsg(work->fds[0], &wmsg);
1429 pthread_mutex_lock(&WorkerMutex);
1433 * Normal message, can include normal
1434 * termination which changes us over
1439 case WMSG_CMD_INSTALL_PKGS:
1440 wmsg.cmd = WMSG_RES_INSTALL_PKGS;
1441 wmsg.status = childInstallPkgDeps(work);
1442 pthread_mutex_unlock(&WorkerMutex);
1443 ipcwritemsg(work->fds[0], &wmsg);
1444 pthread_mutex_lock(&WorkerMutex);
1446 case WMSG_CMD_STATUS_UPDATE:
1447 work->phase = wmsg.phase;
1448 work->lines = wmsg.lines;
1449 if (work->memuse != wmsg.memuse) {
1450 RunningPkgDepSize +=
1451 wmsg.memuse - work->memuse;
1452 work->memuse = wmsg.memuse;
1455 case WMSG_CMD_SUCCESS:
1456 work->flags |= WORKERF_SUCCESS;
1458 case WMSG_CMD_FAILURE:
1459 work->flags |= WORKERF_FAILURE;
1461 case WMSG_CMD_FREEZEWORKER:
1462 work->flags |= WORKERF_FREEZE;
1467 RunStatsUpdate(work, NULL);
1470 close(work->fds[0]);
1471 pthread_mutex_unlock(&WorkerMutex);
1472 while (waitpid(work->pid, &status, 0) < 0 &&
1476 pthread_mutex_lock(&WorkerMutex);
1478 if (work->flags & WORKERF_SUCCESS) {
1479 pkg->flags |= PKGF_SUCCESS;
1480 work->state = WORKER_DONE;
1481 } else if (work->flags & WORKERF_FAILURE) {
1482 pkg->flags |= PKGF_FAILURE;
1483 work->state = WORKER_DONE;
1485 pkg->flags |= PKGF_FAILURE;
1486 work->state = WORKER_FAILED;
1488 work->flags |= WORKERF_STATUS_UPDATE;
1489 pthread_cond_signal(&WorkerCond);
1494 * pkg remains attached until frontend processes the
1495 * completion. The frontend will then set the state
1501 * A worker failure means that the worker did not
1502 * send us a WMSG_CMD_SUCCESS or WMSG_CMD_FAILURE
1503 * ipc before terminating.
1505 * We just sit in this state until the front-end
1506 * does something about it.
1511 * A worker getting frozen is debug-related. We
1512 * just sit in this state (likely forever).
1516 dfatal("worker: [%03d] Unexpected state %d "
1518 work->index, work->state, work->index);
1524 * The dsynth frontend will poll us approximately once
1525 * a second (its variable).
1528 pthread_cond_wait(&work->cond, &WorkerMutex);
1532 * Scrap the comm socket if running, this should cause the worker
1533 * process to kill its sub-programs and cleanup.
1535 if (work->state == WORKER_RUNNING) {
1536 pthread_mutex_unlock(&WorkerMutex);
1537 close(work->fds[0]);
1538 while (waitpid(work->pid, &status, 0) < 0 &&
1540 pthread_mutex_lock(&WorkerMutex);
1546 work->state = WORKER_EXITING;
1547 pthread_cond_signal(&WorkerCond);
1548 pthread_mutex_unlock(&WorkerMutex);
1554 * Install all the binary packages (we have already built them) that
1555 * the current work package depends on, without duplicates, in a script
1556 * which will be run from within the specified work jail.
1558 * Locked by WorkerMutex (global)
1561 childInstallPkgDeps(worker_t *work)
1566 if (PKGLIST_EMPTY(&work->pkg->idepon_list))
1569 asprintf(&buf, "%s/tmp/dsynth_install_pkgs", work->basedir);
1570 fp = fopen(buf, "w");
1571 ddassert(fp != NULL);
1572 fprintf(fp, "#!/bin/sh\n");
1574 fchmod(fileno(fp), 0755);
1576 childInstallPkgDeps_recurse(fp, &work->pkg->idepon_list, 0, 1, 0);
1577 childInstallPkgDeps_recurse(fp, &work->pkg->idepon_list, 1, 1, 0);
1578 fprintf(fp, "\nexit 0\n");
1586 * Recursive child install dependencies.
1588 * first_one_only is only specified if the pkg the list comes from
1589 * is a generic unflavored package that has flavors, telling us to
1590 * dive the first flavor only.
1592 * However, in nearly all cases this flag will now be zero because
1593 * this code now dives the first flavor when encountering a dummy node
1594 * and clears nfirst on success. Hence if you are asking why 'nfirst'
1595 * is set to 1, and then zero, instead of just being removed entirely,
1596 * it is because there might still be an edge case here.
1599 childInstallPkgDeps_recurse(FILE *fp, pkglink_t *list, int undoit,
1600 int depth, int first_one_only)
1608 PKGLIST_FOREACH(link, list) {
1612 * We don't want to mess up our depth test just below if
1613 * a DUMMY node had to be inserted. The nodes under the
1616 * The elements under a dummy node represent all the flabor,
1617 * a dependency that directly references a dummy node only
1618 * uses the first flavor (first_one_only / nfirst).
1620 ndepth = (pkg->flags & PKGF_DUMMY) ? depth : depth + 1;
1621 nfirst = (pkg->flags & PKGF_DUMMY) ? 1 : 0;
1624 * We only need all packages for the top-level dependencies.
1625 * The deeper ones only need DEP_TYPE_LIB and DEP_TYPE_RUN
1626 * (types greater than DEP_TYPE_BUILD) since they are already
1629 if (depth > 1 && link->dep_type <= DEP_TYPE_BUILD) {
1636 * If this is a dummy node with no package, the originator
1637 * is requesting a flavored package. We select the default
1638 * flavor which we presume is the first one.
1640 if (pkg->pkgfile == NULL && (pkg->flags & PKGF_DUMMY)) {
1641 pkg_t *spkg = pkg->idepon_list.next->pkg;
1646 "echo 'UNFLAVORED %s -> use "
1656 "echo 'CANNOT FIND DEFAULT "
1664 if (pkg->dsynth_install_flg == 1) {
1665 pkg->dsynth_install_flg = 0;
1666 tot += childInstallPkgDeps_recurse(fp,
1676 if (pkg->dsynth_install_flg) {
1677 if (DebugOpt >= 2 && pkg->pkgfile && fp) {
1678 fprintf(fp, "echo 'AlreadyHave %s'\n",
1686 tot += childInstallPkgDeps_recurse(fp, &pkg->idepon_list,
1687 undoit, ndepth, nfirst);
1688 if (pkg->dsynth_install_flg) {
1693 pkg->dsynth_install_flg = 1;
1696 * Generate package installation command
1698 if (fp && pkg->pkgfile) {
1699 fprintf(fp, "echo 'Installing /packages/All/%s'\n",
1701 fprintf(fp, "pkg install -q -U -y /packages/All/%s "
1705 fprintf(fp, "echo 'CANNOT FIND PKG FOR %s'\n",
1714 asprintf(&path, "%s/%s", RepositoryPath, pkg->pkgfile);
1715 ptr = strrchr(pkg->pkgfile, '.');
1716 if (stat(path, &st) == 0) {
1717 if (strcmp(ptr, ".tar") == 0)
1719 else if (strcmp(ptr, ".tgz") == 0)
1720 tot += st.st_size * 3;
1721 else if (strcmp(ptr, ".txz") == 0)
1722 tot += st.st_size * 5;
1723 else if (strcmp(ptr, ".tbz") == 0)
1724 tot += st.st_size * 3;
1726 tot += st.st_size * 2;
1737 * Worker process interactions.
1739 * The worker process is responsible for managing the build of a single
1740 * package. It is exec'd by the master dsynth and only loads the
1743 * This process does not run in the chroot. It will become the reaper for
1744 * all sub-processes and it will enter the chroot to execute various phases.
1745 * It catches SIGINTR, SIGHUP, and SIGPIPE and will iterate, terminate, and
1746 * reap all sub-process upon kill or exit.
1748 * The command line forwarded to this function is:
1750 * WORKER slot# socketfd portdir/subdir
1756 * SSL_NO_VERIFY_PEER=1
1757 * USE_PACKAGE_DEPENDS_ONLY=1
1759 * PORT_DBDIR=/options For ports options
1760 * PACKAGE_BUILDING=yes Don't build packages that aren't legally
1761 * buildable for a binary repo.
1762 * PKG_DBDIR=/var/db/pkg
1763 * PKG_CACHEDIR=/var/cache/pkg
1764 * PKG_CREATE_VERBOSE=yes Ensure periodic output during packaging
1765 * (custom environment)
1766 * PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin
1767 * UNAME_s=DragonFly (example)
1768 * UNAME_v=DragonFly 5.7-SYNTH (example)
1769 * UNAME_p=x86_64 (example)
1770 * UNAME_m=x86_64 (example)
1771 * UNAME_r=5.7-SYNTH (example)
1772 * NO_DEPENDS=yes (conditional based on phase)
1773 * DISTDIR=/distfiles
1774 * WRKDIRPREFIX=/construction
1776 * MAKE_JOBS_NUMBER=n
1780 * /usr/local/sbin/pkg-static install /packages/All/<the pkg pkg>
1781 * /usr/local/sbin/pkg-static install /packages/All/<pkg>
1782 * (for all dependencies)
1784 * PHASES: make -C path FLAVOR=flavor <phase>
1801 * check-plist ('dsynth test blahblah' or 'dsynth -D everything' only)
1802 * package e.g. /construction/lang/perl5.28/pkg/perl5-5.28.2.txz
1803 * install-mtree (skipped)
1805 * deinstall (skipped)
1808 WorkerProcess(int ac, char **av)
1817 int do_install_phase;
1832 dlog(DLOG_ALL, "WORKER PROCESS %d- bad arguments\n", getpid());
1835 slot = strtol(av[1], NULL, 0);
1836 fd = strtol(av[2], NULL, 0); /* master<->slave messaging */
1839 flavor = strchr(portdir, '@');
1842 asprintf(&buf, "@%s", flavor);
1843 WorkerFlavorPrt = buf;
1844 buf = NULL; /* safety */
1846 WorkerProcFlags = strtol(av[5], NULL, 0);
1848 bzero(&wmsg, sizeof(wmsg));
1850 setproctitle("[%02d] WORKER STARTUP %s%s",
1851 slot, portdir, WorkerFlavorPrt);
1853 if (strcmp(portdir, "ports-mgmt/pkg") == 0)
1856 signal(SIGTERM, phaseTerminateSignal);
1857 signal(SIGINT, phaseTerminateSignal);
1858 signal(SIGHUP, phaseTerminateSignal);
1861 * Set up the environment
1863 setenv("TERM", "dumb", 1);
1864 setenv("USER", "root", 1);
1865 setenv("HOME", "/root", 1);
1866 setenv("LANG", "C", 1);
1867 setenv("SSL_NO_VERIFY_PEER", "1", 1);
1869 addbuildenv("USE_PACKAGE_DEPENDS_ONLY", "yes", BENV_MAKECONF);
1870 addbuildenv("PORTSDIR", "/xports", BENV_MAKECONF);
1871 addbuildenv("PORT_DBDIR", "/options", BENV_MAKECONF);
1872 addbuildenv("PKG_DBDIR", "/var/db/pkg", BENV_MAKECONF);
1873 addbuildenv("PKG_CACHEDIR", "/var/cache/pkg", BENV_MAKECONF);
1874 addbuildenv("PKG_SUFX", UsePkgSufx, BENV_MAKECONF);
1875 if (WorkerProcFlags & WORKER_PROC_DEVELOPER)
1876 addbuildenv("DEVELOPER", "1", BENV_MAKECONF);
1879 * CCache is a horrible unreliable hack but... leave the
1880 * mechanism in-place in case someone has a death wish.
1883 addbuildenv("WITH_CCACHE_BUILD", "yes", BENV_MAKECONF);
1884 addbuildenv("CCACHE_DIR", "/ccache", BENV_MAKECONF);
1887 addbuildenv("UID", "0", BENV_MAKECONF);
1888 addbuildenv("ARCH", ArchitectureName, BENV_MAKECONF);
1890 #ifdef __DragonFly__
1891 addbuildenv("OPSYS", "DragonFly", BENV_MAKECONF);
1892 addbuildenv("DFLYVERSION", VersionFromParamHeader, BENV_MAKECONF);
1893 addbuildenv("OSVERSION", "9999999", BENV_MAKECONF);
1895 #error "Need OS-specific data to generate make.conf"
1898 addbuildenv("OSREL", ReleaseName, BENV_MAKECONF);
1899 addbuildenv("_OSRELEASE", VersionOnlyName, BENV_MAKECONF);
1902 "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin",
1905 setenv("UNAME_s", OperatingSystemName, 1);
1906 setenv("UNAME_v", VersionName, 1);
1907 setenv("UNAME_p", ArchitectureName, 1);
1908 setenv("UNAME_m", MachineName, 1);
1909 setenv("UNAME_r", ReleaseName, 1);
1911 addbuildenv("DISTDIR", "/distfiles", BENV_MAKECONF);
1912 addbuildenv("WRKDIRPREFIX", "/construction", BENV_MAKECONF);
1913 addbuildenv("BATCH", "yes", BENV_MAKECONF);
1916 * Special consideration
1918 * PACKAGE_BUILDING - Disallow packaging ports which do not allow
1919 * for binary distribution.
1921 * PKG_CREATE_VERBOSE - Ensure periodic output during the packaging
1922 * process to avoid a watchdog timeout.
1925 addbuildenv("PACKAGE_BUILDING", "yes", BENV_MAKECONF);
1926 addbuildenv("PKG_CREATE_VERBOSE", "yes", BENV_MAKECONF);
1927 asprintf(&buf, "%d", MaxJobs);
1928 addbuildenv("MAKE_JOBS_NUMBER", buf, BENV_MAKECONF);
1932 setenv("FLAVOR", flavor, 1);
1937 if (procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) < 0)
1938 dfatal_errno("procctl() - Cannot become reaper");
1941 * Initialize a worker structure
1945 bzero(&pkg, sizeof(pkg));
1946 pkg.portdir = portdir; /* sans flavor */
1947 pkg.pkgfile = pkgfile;
1948 if (strchr(portdir, '/'))
1949 len = strchr(portdir, '/') - portdir;
1956 asprintf(&pkg.logfile,
1957 "%s/%*.*s___%s%s%s.log",
1958 LogsPath, len, len, portdir,
1959 ((portdir[len] == '/') ? portdir + len + 1 : portdir + len),
1960 (flavor ? "@" : ""),
1961 (flavor ? flavor : ""));
1962 tmpfd = open(pkg.logfile, O_RDWR|O_CREAT|O_TRUNC, 0666);
1964 if (DebugOpt >= 2) {
1965 dlog(DLOG_ALL, "[%03d] %s LOGFILE %s\n",
1966 slot, pkg.portdir, pkg.logfile);
1970 dlog(DLOG_ALL, "[%03d] LOGFILE %s (create failed)\n",
1975 * Setup the work structure. Because this is an exec'd sub-process,
1976 * there is only one work structure.
1978 work = &WorkerAry[0];
1979 work->flavor = flavor;
1982 work->start_time = time(NULL);
1988 setproctitle("[%02d] WORKER MOUNTS %s%s",
1989 slot, portdir, WorkerFlavorPrt);
1990 DoWorkerMounts(work);
1993 * Generate an /etc/make.conf in the build base
1995 asprintf(&buf, "%s/etc/make.conf", work->basedir);
1996 fp = fopen(buf, "w");
1997 dassert_errno(fp, "Unable to create %s\n", buf);
1998 for (benv = BuildEnv; benv; benv = benv->next) {
1999 if (benv->type & BENV_PKGLIST)
2001 if ((benv->type & BENV_CMDMASK) == BENV_MAKECONF) {
2002 if (DebugOpt >= 2) {
2003 dlog(DLOG_ALL, "[%03d] ENV %s=%s\n",
2004 slot, benv->label, benv->data);
2006 fprintf(fp, "%s=%s\n", benv->label, benv->data);
2016 initbulk(childHookRun, MaxBulk);
2021 wmsg.cmd = WMSG_CMD_INSTALL_PKGS;
2022 ipcwritemsg(fd, &wmsg);
2023 status = ipcreadmsg(fd, &wmsg);
2024 if (status < 0 || wmsg.cmd != WMSG_RES_INSTALL_PKGS)
2025 dfatal("pkg installation handshake failed");
2026 do_install_phase = wmsg.status;
2028 wmsg.cmd = WMSG_CMD_STATUS_UPDATE;
2029 wmsg.phase = PHASE_INSTALL_PKGS;
2032 status = ipcwritemsg(fd, &wmsg);
2035 dophase(work, &wmsg,
2036 WDOG5, PHASE_PACKAGE, "package");
2038 if (do_install_phase) {
2039 dophase(work, &wmsg,
2040 WDOG4, PHASE_INSTALL_PKGS, "setup");
2042 dophase(work, &wmsg,
2043 WDOG2, PHASE_CHECK_SANITY, "check-sanity");
2044 dophase(work, &wmsg,
2045 WDOG2, PHASE_PKG_DEPENDS, "pkg-depends");
2046 dophase(work, &wmsg,
2047 WDOG7, PHASE_FETCH_DEPENDS, "fetch-depends");
2048 dophase(work, &wmsg,
2049 WDOG7, PHASE_FETCH, "fetch");
2050 dophase(work, &wmsg,
2051 WDOG2, PHASE_CHECKSUM, "checksum");
2052 dophase(work, &wmsg,
2053 WDOG3, PHASE_EXTRACT_DEPENDS, "extract-depends");
2054 dophase(work, &wmsg,
2055 WDOG3, PHASE_EXTRACT, "extract");
2056 dophase(work, &wmsg,
2057 WDOG2, PHASE_PATCH_DEPENDS, "patch-depends");
2058 dophase(work, &wmsg,
2059 WDOG2, PHASE_PATCH, "patch");
2060 dophase(work, &wmsg,
2061 WDOG5, PHASE_BUILD_DEPENDS, "build-depends");
2062 dophase(work, &wmsg,
2063 WDOG5, PHASE_LIB_DEPENDS, "lib-depends");
2064 dophase(work, &wmsg,
2065 WDOG3, PHASE_CONFIGURE, "configure");
2066 dophase(work, &wmsg,
2067 WDOG9, PHASE_BUILD, "build");
2068 dophase(work, &wmsg,
2069 WDOG5, PHASE_RUN_DEPENDS, "run-depends");
2070 dophase(work, &wmsg,
2071 WDOG5, PHASE_STAGE, "stage");
2073 dophase(work, &wmsg,
2074 WDOG5, PHASE_TEST, "test");
2076 if (WorkerProcFlags & WORKER_PROC_CHECK_PLIST) {
2077 dophase(work, &wmsg,
2078 WDOG1, PHASE_CHECK_PLIST, "check-plist");
2080 dophase(work, &wmsg,
2081 WDOG5, PHASE_PACKAGE, "package");
2083 dophase(work, &wmsg,
2084 WDOG5, PHASE_INSTALL_MTREE, "install-mtree");
2085 dophase(work, &wmsg,
2086 WDOG5, PHASE_INSTALL, "install");
2087 dophase(work, &wmsg,
2088 WDOG5, PHASE_DEINSTALL, "deinstall");
2092 if (MasterPtyFd >= 0) {
2097 setproctitle("[%02d] WORKER CLEANUP %s%s",
2098 slot, portdir, WorkerFlavorPrt);
2101 * Copy the package to the repo.
2103 if (work->accum_error == 0) {
2107 asprintf(&b1, "%s/construction/%s/pkg/%s",
2108 work->basedir, pkg.portdir, pkg.pkgfile);
2109 asprintf(&b2, "%s/%s", RepositoryPath, pkg.pkgfile);
2110 if (copyfile(b1, b2)) {
2111 ++work->accum_error;
2112 dlog(DLOG_ALL, "[%03d] %s Unable to copy %s to %s\n",
2113 work->index, pkg.portdir, b1, b2);
2120 * Unmount, unless we are in DebugStopMode.
2122 if ((WorkerProcFlags & WORKER_PROC_DEBUGSTOP) == 0)
2123 DoWorkerUnmounts(work);
2126 * Send completion status to master dsynth worker thread.
2128 if (work->accum_error) {
2129 wmsg.cmd = WMSG_CMD_FAILURE;
2131 wmsg.cmd = WMSG_CMD_SUCCESS;
2133 ipcwritemsg(fd, &wmsg);
2134 if (WorkerProcFlags & WORKER_PROC_DEBUGSTOP) {
2135 wmsg.cmd = WMSG_CMD_FREEZEWORKER;
2136 ipcwritemsg(fd, &wmsg);
2139 while ((bulk = getbulk()) != NULL)
2146 dophase(worker_t *work, wmsg_t *wmsg, int wdog, int phaseid, const char *phase)
2148 pkg_t *pkg = work->pkg;
2162 if (work->accum_error)
2164 setproctitle("[%02d] WORKER %-8.8s %s%s",
2165 work->index, phase, pkg->portdir, WorkerFlavorPrt);
2166 wmsg->phase = phaseid;
2167 if (ipcwritemsg(work->fds[0], wmsg) < 0) {
2168 dlog(DLOG_ALL, "[%03d] %s Lost Communication with dsynth, "
2169 "aborting worker\n",
2170 work->index, pkg->portdir);
2171 ++work->accum_error;
2176 * Execute the port make command in chroot on a pty.
2180 if (MasterPtyFd >= 0) {
2184 * NOTE: We can't open the slave in the child because the
2185 * master may race a disconnection test. If we open
2186 * it in the parent our close() will flush any pending
2187 * output not read by the master (which is the same
2188 * parent process) and deadlock.
2190 * Solve this by hand-shaking the slave tty to give
2191 * the master time to close its slavefd (after this
2194 * Leave the tty defaults intact, which also likely
2195 * means it will be in line-buffered mode, so handshake
2198 * TODO: Our handshake probably echos back to the master pty
2199 * due to tty echo, and ends up in the log, so just
2200 * pass through a newline.
2202 slavefd = open(ptsname(MasterPtyFd), O_RDWR);
2203 dassert_errno(slavefd >= 0, "Cannot open slave pty");
2211 /* login_tty() closes slavefd */
2217 * Initial MasterPtyFd for the slot, just use forkpty().
2219 pid = forkpty(&MasterPtyFd, NULL, NULL, NULL);
2223 * The slave must make sure the master has time to close slavefd
2224 * in the re-use case before going its merry way. The master needs
2225 * to set terminal modes and the window as well.
2229 * Slave nices itself and waits for handshake
2234 * Self-nice to be nice (ignore any error)
2237 setpriority(PRIO_PROCESS, 0, NiceOpt);
2242 * We are going through a pty, so set the tty modes to
2243 * Set tty modes so we do not get ^M's in the log files.
2245 * This isn't fatal if it doesn't work. Remember that
2246 * our output goes through the pty to the management
2247 * process which will log it.
2252 if (tcgetattr(MasterPtyFd, &tio) == 0) {
2253 tio.c_oflag |= OPOST | ONOCR;
2254 tio.c_oflag &= ~(OCRNL | ONLCR);
2255 tio.c_iflag |= ICRNL;
2256 tio.c_iflag &= ~(INLCR | IGNCR);
2257 if (tcsetattr(MasterPtyFd, TCSANOW, &tio)) {
2258 printf("tcsetattr failed: %s\n",
2263 * Give the tty a non-zero columns field.
2264 * This fixes at least one port (textproc/po4a)
2266 if (ioctl(MasterPtyFd, TIOCGWINSZ, &win) == 0) {
2268 ioctl(MasterPtyFd, TIOCSWINSZ, &win);
2270 printf("TIOCGWINSZ failed: %s\n",
2275 printf("tcgetattr failed: %s\n", strerror(errno));
2279 * Master issues handshake
2281 write(MasterPtyFd, "\n", 1);
2286 * Additional phase-specific environment variables
2288 * - Do not try to process missing depends outside of the
2289 * depends phases. Also relies on USE_PACKAGE_DEPENDS_ONLY
2293 case PHASE_CHECK_SANITY:
2295 case PHASE_CHECKSUM:
2298 case PHASE_CONFIGURE:
2301 case PHASE_CHECK_PLIST:
2302 case PHASE_INSTALL_MTREE:
2304 case PHASE_DEINSTALL:
2306 case PHASE_PKG_DEPENDS:
2307 case PHASE_FETCH_DEPENDS:
2308 case PHASE_EXTRACT_DEPENDS:
2309 case PHASE_PATCH_DEPENDS:
2310 case PHASE_BUILD_DEPENDS:
2311 case PHASE_LIB_DEPENDS:
2312 case PHASE_RUN_DEPENDS:
2315 setenv("NO_DEPENDS", "1", 1);
2320 * Clean-up, chdir, and chroot.
2323 if (chdir(work->basedir) < 0)
2324 dfatal_errno("chdir in phase initialization");
2325 if (chroot(work->basedir) < 0)
2326 dfatal_errno("chroot in phase initialization");
2329 * We have a choice here on how to handle stdin (fd 0).
2330 * We can leave it connected to the pty in which case
2331 * the build will just block if it tries to ask a
2332 * question (and the watchdog will kill it, eventually),
2333 * or we can try to EOF the pty, or we can attach /dev/null
2339 fd = open("/dev/null", O_RDWR);
2340 dassert_errno(fd >= 0, "cannot open /dev/null");
2348 * Execute the appropriate command.
2351 case PHASE_INSTALL_PKGS:
2352 snprintf(buf, sizeof(buf), "/tmp/dsynth_install_pkgs");
2353 execl(buf, buf, NULL);
2356 snprintf(buf, sizeof(buf), "/xports/%s", pkg->portdir);
2357 execl(MAKE_BINARY, MAKE_BINARY, "-C", buf, phase, NULL);
2362 fcntl(MasterPtyFd, F_SETFL, O_NONBLOCK);
2365 dlog(DLOG_ALL, "[%03d] %s Fork Failed: %s\n",
2366 work->index, pkg->logfile, strerror(errno));
2367 ++work->accum_error;
2373 fdlog = open(pkg->logfile, O_RDWR|O_CREAT|O_APPEND, 0644);
2375 dlog(DLOG_ALL, "[%03d] %s Cannot open logfile '%s': %s\n",
2376 work->index, pkg->portdir,
2377 pkg->logfile, strerror(errno));
2380 snprintf(buf, sizeof(buf),
2381 "----------------------------------------"
2382 "---------------------------------------\n"
2384 "----------------------------------------"
2385 "---------------------------------------\n",
2387 write(fdlog, buf, strlen(buf));
2389 start_time = time(NULL);
2390 last_time = start_time;
2391 wdog_time = start_time;
2396 ms = mptylogpoll(MasterPtyFd, fdlog, wmsg, &wdog_time);
2397 if (ms == MPTY_FAILED) {
2399 "[%03d] %s lost pty in phase %s, terminating\n",
2400 work->index, pkg->portdir, phase);
2407 * Generally speaking update status once a second.
2408 * This also allows us to detect if the management
2409 * dsynth process has gone away.
2411 next_time = time(NULL);
2412 if (next_time != last_time) {
2418 * Send status update to the worker management thread
2419 * in the master dsynth process. Remember, *WE* are
2420 * the worker management process sub-fork.
2422 if (ipcwritemsg(work->fds[0], wmsg) < 0)
2424 last_time = next_time;
2429 getloadavg(dload, 3);
2431 dv = dload[2] / NumCores;
2432 if (dv < (double)NumCores) {
2435 if (dv > 4.0 * NumCores)
2436 dv = 4.0 * NumCores;
2437 wdog_scaled = wdog * dv / NumCores;
2443 if (next_time - wdog_time >= wdog_scaled * 60) {
2444 snprintf(buf, sizeof(buf),
2446 "WATCHDOG TIMEOUT FOR %s in %s "
2447 "after %d minutes\n"
2450 pkg->portdir, phase, wdog_scaled, pid);
2452 write(fdlog, buf, strlen(buf));
2454 "[%03d] %s WATCHDOG TIMEOUT in %s "
2455 "after %d minutes (%d min scaled)\n",
2456 work->index, pkg->portdir, phase,
2459 ++work->accum_error;
2465 * Check process exit. Normally the pty will EOF
2466 * but if background processes remain we need to
2467 * check here to see if our primary exec is done,
2468 * so we can break out and reap those processes.
2470 * Generally reap any other processes we have inherited
2471 * while we are here.
2474 wpid = wait3(&status, WNOHANG, NULL);
2475 } while (wpid > 0 && wpid != pid);
2476 if (wpid == pid && WIFEXITED(status)) {
2482 next_time = time(NULL);
2484 setproctitle("[%02d] WORKER EXITREAP %s%s",
2485 work->index, pkg->portdir, WorkerFlavorPrt);
2488 * We usually get here due to a mpty EOF, but not always as there
2489 * could be persistent processes still holding the slave. Finish
2490 * up getting the exit status for the main process we are waiting
2491 * on and clean out any data left on the MasterPtyFd (as it could
2492 * be blocking the exit).
2494 while (wpid_reaped == 0) {
2495 (void)mptylogpoll(MasterPtyFd, fdlog, wmsg, &wdog_time);
2496 wpid = waitpid(pid, &status, WNOHANG);
2497 if (wpid == pid && WIFEXITED(status)) {
2501 if (wpid < 0 && errno != EINTR) {
2506 * Safety. The normal phase waits until the fork/exec'd
2507 * pid finishes, causing a pty EOF on exit (the slave
2508 * descriptor is closed by the kernel on exit so the
2509 * process should already have exited).
2511 * However, it is also possible to get here if the pty fails
2512 * for some reason. In this case, make sure that the process
2519 * Clean out anything left on the pty but don't wait around
2520 * because there could be background processes preventing the
2521 * slave side from closing.
2523 while (mptylogpoll(MasterPtyFd, fdlog, wmsg, &wdog_time) == MPTY_DATA)
2527 * Report on the exit condition. If the pid was somehow lost
2528 * (probably due to someone gdb'ing the process), assume an error.
2531 if (WEXITSTATUS(status)) {
2532 dlog(DLOG_ALL | DLOG_FILTER,
2533 "[%03d] %s Build phase '%s' failed exit %d\n",
2534 work->index, pkg->portdir, phase,
2535 WEXITSTATUS(status));
2536 ++work->accum_error;
2539 dlog(DLOG_ALL, "[%03d] %s Build phase '%s' failed - lost pid\n",
2540 work->index, pkg->portdir, phase);
2541 ++work->accum_error;
2545 * Kill any processes still running (sometimes processes end up in
2546 * the background during a dports build), and clean up any other
2547 * children that we have inherited.
2552 * After the extraction phase add the space used by /construction
2553 * to the memory use. This helps us reduce the amount of paging
2554 * we do due to extremely large package extractions (languages,
2557 * (dsynth already estimated the space used by the package deps
2558 * up front, but this will help us further).
2560 if (work->accum_error == 0 && phaseid == PHASE_EXTRACT) {
2564 asprintf(&b1, "%s/construction", work->basedir);
2565 if (statfs(b1, &sfs) == 0) {
2566 wmsg->memuse = (sfs.f_blocks - sfs.f_bfree) *
2568 ipcwritemsg(work->fds[0], wmsg);
2581 last_time = next_time - start_time;
2583 m = last_time / 60 % 60;
2584 h = last_time / 3600;
2586 fp = fdopen(fdlog, "a");
2588 dlog(DLOG_ALL, "[%03d] %s Cannot fdopen fdlog: %s %d\n",
2589 work->index, pkg->portdir,
2590 strerror(errno), fstat(fdlog, &st));
2596 if (work->accum_error) {
2597 fprintf(fp, "FAILED %02d:%02d:%02d\n", h, m, s);
2599 if (phaseid == PHASE_EXTRACT && wmsg->memuse) {
2600 fprintf(fp, "Extracted Memory Use: %6.2fM\n",
2601 wmsg->memuse / (1024.0 * 1024.0));
2603 fprintf(fp, "SUCCEEDED %02d:%02d:%02d\n", h, m, s);
2605 last_time = next_time - work->start_time;
2607 m = last_time / 60 % 60;
2608 h = last_time / 3600;
2609 if (phaseid == PHASE_PACKAGE) {
2610 fprintf(fp, "TOTAL TIME %02d:%02d:%02d\n", h, m, s);
2623 struct reaper_status rs;
2626 while (procctl(P_PID, getpid(), PROC_REAP_STATUS, &rs) == 0) {
2627 if ((rs.flags & PROC_REAP_ACQUIRE) == 0)
2629 if (rs.pid_head < 0)
2631 if (kill(rs.pid_head, SIGKILL) == 0) {
2632 while (waitpid(rs.pid_head, &status, 0) < 0)
2636 while (wait3(&status, 0, NULL) > 0)
2641 phaseTerminateSignal(int sig __unused)
2643 if (CopyFileFd >= 0)
2645 if (MasterPtyFd >= 0)
2648 kill(SigPid, SIGKILL);
2651 DoWorkerUnmounts(SigWork);
2657 buildskipreason(pkglink_t *parent, pkg_t *pkg)
2661 char *reason = NULL;
2667 if ((pkg->flags & PKGF_NOBUILD_I) && pkg->ignore)
2668 asprintf(&reason, "%s ", pkg->ignore);
2671 PKGLIST_FOREACH(link, &pkg->idepon_list) {
2673 if (link->dep_type > DEP_TYPE_BUILD)
2679 if ((scan->flags & (PKGF_ERROR | PKGF_NOBUILD)) == 0)
2681 if (scan->flags & PKGF_NOBUILD) {
2683 stack.next = parent;
2684 ptr = buildskipreason(&stack, scan);
2685 len = strlen(scan->portdir) + strlen(ptr) + 8;
2686 reason = realloc(reason, tot + len);
2687 snprintf(reason + tot, len, "%s->%s",
2688 scan->portdir, ptr);
2691 len = strlen(scan->portdir) + 8;
2692 reason = realloc(reason, tot + len);
2693 snprintf(reason + tot, len, "%s", scan->portdir);
2697 * Don't try to print the entire graph
2701 tot += strlen(reason + tot);
2702 reason[tot++] = ' ';
2709 * Count number of packages that would be skipped due to the
2710 * specified package having failed.
2712 * Call with mode 1 to count, and mode 0 to clear the
2713 * cumulative rscan flag (used to de-duplicate the count).
2715 * Must be serialized.
2718 buildskipcount_dueto(pkg_t *pkg, int mode)
2725 PKGLIST_FOREACH(link, &pkg->deponi_list) {
2727 if (scan == NULL || scan->rscan == mode)
2731 total += buildskipcount_dueto(scan, mode);
2737 * The master ptyfd is in non-blocking mode. Drain up to 1024 bytes
2738 * and update wmsg->lines and *wdog_timep as appropriate.
2740 * This function will poll, stalling up to 1 second.
2743 mptylogpoll(int ptyfd, int fdlog, wmsg_t *wmsg, time_t *wdog_timep)
2750 pfd.events = POLLIN;
2753 poll(&pfd, 1, 1000);
2755 r = read(ptyfd, buf, sizeof(buf));
2757 *wdog_timep = time(NULL);
2758 if (r > 0 && fdlog >= 0)
2759 write(fdlog, buf, r);
2766 if (errno != EINTR && errno != EAGAIN)
2769 } else if (r == 0) {
2777 * Copy a (package) file from (src) to (dst), use an intermediate file and
2778 * rename to ensure that interruption does not leave us with a corrupt
2781 * This is called by the WORKER process.
2783 * (dsynth management thread -> WORKER process -> sub-processes)
2785 #define COPYBLKSIZE 32768
2788 copyfile(char *src, char *dst)
2798 asprintf(&tmp, "%s.new", dst);
2799 buf = malloc(COPYBLKSIZE);
2801 mask = sigsetmask(sigmask(SIGTERM)|sigmask(SIGINT)|sigmask(SIGHUP));
2802 fd1 = open(src, O_RDONLY|O_CLOEXEC);
2803 fd2 = open(tmp, O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0644);
2806 while ((r = read(fd1, buf, COPYBLKSIZE)) > 0) {
2807 if (write(fd2, buf, r) != r)
2812 mask = sigsetmask(sigmask(SIGTERM)|sigmask(SIGINT)|sigmask(SIGHUP));
2820 if (rename(tmp, dst)) {
2835 * primary process (threaded) - run_start, run_end, pkg_ignored, pkg_skipped
2836 * worker process (threaded) - pkg_sucess, pkg_failure
2838 * If waitfor is non-zero this hook will be serialized.
2841 doHook(pkg_t *pkg, const char *id, const char *path, int waitfor)
2845 while (waitfor && getbulk() != NULL)
2848 queuebulk(pkg->portdir, id, path, pkg->pkgfile);
2850 queuebulk(NULL, id, path, NULL);
2851 while (waitfor && getbulk() != NULL)
2856 * Execute hook (backend)
2861 * s4 - pkgfile (if applicable)
2864 childHookRun(bulk_t *bulk)
2866 const char *cav[MAXCAC];
2867 buildenv_t benv[MAXCAC];
2881 bzero(benv, sizeof(benv));
2883 cav[cac++] = bulk->s3;
2885 benv[bi].label = "PROFILE";
2886 benv[bi].data = Profile;
2889 benv[bi].label = "DIR_PACKAGES";
2890 benv[bi].data = PackagesPath;
2893 benv[bi].label = "DIR_REPOSITORY";
2894 benv[bi].data = RepositoryPath;
2897 benv[bi].label = "DIR_PORTS";
2898 benv[bi].data = DPortsPath;
2901 benv[bi].label = "DIR_OPTIONS";
2902 benv[bi].data = OptionsPath;
2905 benv[bi].label = "DIR_DISTFILES";
2906 benv[bi].data = DistFilesPath;
2909 benv[bi].label = "DIR_LOGS";
2910 benv[bi].data = LogsPath;
2913 benv[bi].label = "DIR_BUILDBASE";
2914 benv[bi].data = BuildBase;
2917 if (strcmp(bulk->s2, "hook_run_start") == 0) {
2918 snprintf(buf1, sizeof(buf1), "%d", BuildTotal);
2919 benv[bi].label = "PORTS_QUEUED";
2920 benv[bi].data = buf1;
2922 } else if (strcmp(bulk->s2, "hook_run_end") == 0) {
2923 snprintf(buf1, sizeof(buf1), "%d", BuildSuccessCount);
2924 benv[bi].label = "PORTS_BUILT";
2925 benv[bi].data = buf1;
2927 snprintf(buf2, sizeof(buf2), "%d", BuildFailCount);
2928 benv[bi].label = "PORTS_FAILED";
2929 benv[bi].data = buf2;
2931 snprintf(buf3, sizeof(buf3), "%d", BuildIgnoreCount);
2932 benv[bi].label = "PORTS_IGNORED";
2933 benv[bi].data = buf3;
2935 snprintf(buf4, sizeof(buf4), "%d", BuildSkipCount);
2936 benv[bi].label = "PORTS_SKIPPED";
2937 benv[bi].data = buf4;
2941 * success, failure, ignored, skipped
2943 benv[bi].label = "RESULT";
2944 if (strcmp(bulk->s2, "hook_pkg_success") == 0) {
2945 benv[bi].data = "success";
2946 } else if (strcmp(bulk->s2, "hook_pkg_failure") == 0) {
2947 benv[bi].data = "failure";
2948 } else if (strcmp(bulk->s2, "hook_pkg_ignored") == 0) {
2949 benv[bi].data = "ignored";
2950 } else if (strcmp(bulk->s2, "hook_pkg_skipped") == 0) {
2951 benv[bi].data = "skipped";
2953 dfatal("Unknown hook id: %s", bulk->s2);
2959 * For compatibility with synth:
2961 * ORIGIN does not include any @flavor, thus it is suitable
2962 * for finding the actual port directory/subdirectory.
2964 * FLAVOR is set to ORIGIN if there is no flavor, otherwise
2965 * it is set to only the flavor sans the '@'.
2967 if ((ptr = strchr(bulk->s1, '@')) != NULL) {
2968 snprintf(buf1, sizeof(buf1), "%*.*s",
2969 (int)(ptr - bulk->s1),
2970 (int)(ptr - bulk->s1),
2972 benv[bi].label = "ORIGIN";
2973 benv[bi].data = buf1;
2975 benv[bi].label = "FLAVOR";
2976 benv[bi].data = ptr + 1;
2979 benv[bi].label = "ORIGIN";
2980 benv[bi].data = bulk->s1;
2982 benv[bi].label = "FLAVOR";
2983 benv[bi].data = bulk->s1;
2986 benv[bi].label = "PKGNAME";
2987 benv[bi].data = bulk->s4;
2991 benv[bi].label = NULL;
2992 benv[bi].data = NULL;
2994 fp = dexec_open(cav, cac, &pid, benv, 0, 0);
2995 while ((ptr = fgetln(fp, &len)) != NULL)
2998 if (dexec_close(fp, pid)) {
3000 "[XXX] %s SCRIPT %s (%s)\n",
3001 bulk->s1, bulk->s2, bulk->s3);
3006 * Adjusts dload[0] by adding in t_pw (processes waiting on page-fault).
3007 * We don't want load reductions due to e.g. thrashing to cause dsynth
3008 * to increase the dynamic limit because it thinks the load is low.
3010 * This has a desirable property. If the system pager cannot keep up
3011 * with process demand t_pw will spike while loadavg will only drop
3012 * slowly, resulting in a high adjusted load calculation that causes
3013 * dsynth to quickly clamp-down the limit. If the condition alleviates,
3014 * the limit will then rise slowly again, possibly even before existing
3015 * jobs are retired to meet the clamp-down from the original spike.
3018 adjloadavg(double *dload)
3020 #if defined(__DragonFly__)
3021 struct vmtotal total;
3024 size = sizeof(total);
3025 if (sysctlbyname("vm.vmtotal", &total, &size, NULL, 0) == 0) {
3026 dload[0] += (double)total.t_pw;
3029 dload[0] += 0.0; /* just avoid compiler 'unused' warnings */