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 mptylogpoll(int ptyfd, int fdlog, wmsg_t *wmsg,
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);
75 static worker_t *SigWork;
76 static int MasterPtyFd = -1;
77 static int CopyFileFd = -1;
79 static const char *WorkerFlavorPrt = ""; /* "" or "@flavor" */
81 #define MPTY_FAILED -2
91 int BuildSuccessCount;
94 * Initialize the WorkerAry[]
97 DoInitBuild(int slot_override)
103 ddassert(slot_override < 0 || MaxWorkers == 1);
105 bzero(WorkerAry, MaxWorkers * sizeof(worker_t));
106 pthread_mutex_init(&WorkerMutex, NULL);
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);
118 * Create required sub-directories. The base directories must already
119 * exist as a dsynth configuration safety.
121 if (stat(RepositoryPath, &st) < 0) {
122 if (mkdir(RepositoryPath, 0755) < 0)
123 dfatal("Cannot mkdir %s\n", RepositoryPath);
126 BuildInitialized = 1;
129 * slow-start (increases at a rate of 1 per 5 seconds)
131 if (SlowStartOpt > MaxWorkers)
132 DynamicMaxWorkers = MaxWorkers;
133 else if (SlowStartOpt > 0)
134 DynamicMaxWorkers = SlowStartOpt;
136 DynamicMaxWorkers = MaxWorkers;
140 * Called by the frontend to clean-up any hanging mounts.
143 DoCleanBuild(int resetlogs)
147 ddassert(BuildInitialized);
151 for (i = 0; i < MaxWorkers; ++i) {
152 DoWorkerUnmounts(&WorkerAry[i]);
159 pkg_t *build_list = NULL;
160 pkg_t **build_tail = &build_list;
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.
176 initbulk(childHookRun, MaxBulk);
179 * Count up the packages, not counting dummies
181 for (scan = pkgs; scan; scan = scan->bnext) {
182 if ((scan->flags & PKGF_DUMMY) == 0)
186 doHook(NULL, "hook_run_start", HookRunStart, 1);
189 * The pkg and pkg-static binaries are needed. If already present
190 * then assume that the template is also valid, otherwise build
193 scan = GetPkgPkg(pkgs);
196 * Create our template. The template will be missing pkg
199 if ((scan->flags & (PKGF_SUCCESS | PKGF_PACKAGED)) == 0) {
200 /* force a fresh template */
201 newtemplate = DoCreateTemplate(1);
203 newtemplate = DoCreateTemplate(0);
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
212 pthread_mutex_lock(&WorkerMutex);
213 startTime = time(NULL);
218 * Build pkg/pkg-static.
220 if ((scan->flags & (PKGF_SUCCESS | PKGF_PACKAGED)) == 0) {
222 build_tail = &scan->build_next;
223 startbuild(&build_list, &build_tail);
224 while (RunningWorkers == 1)
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'");
237 * Install pkg/pkg-static into the template
245 "tar --exclude '+*' --exclude '*/man/*' "
246 "-xvzpf %s/%s > /dev/null 2>&1",
252 dfatal("Command failed: %s\n", buf);
257 * Calculate depi_depth, the longest chain of dependencies
258 * for who depends on me, weighted by powers of two.
260 for (scan = pkgs; scan; scan = scan->bnext) {
261 buildCalculateDepiDepth(scan);
265 * Nominal bulk build sequence
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;
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.
279 if (scan->flags & (PKGF_SUCCESS | PKGF_FAILURE |
282 ddprintf(0, "%s: already built\n",
287 build_find_leaves(NULL, scan, &build_tail,
288 &ap, &haswork, 0, first, 0);
289 ddprintf(0, "TOPLEVEL %s %08x\n",
292 scan->flags &= ~PKGF_BUILDLOOP;
293 build_clear_trav(scan);
297 startbuild(&build_list, &build_tail);
299 if (haswork == 0 && RunningWorkers) {
300 waitbuild(RunningWorkers, 1);
304 pthread_mutex_unlock(&WorkerMutex);
307 RunStatsUpdateLogs();
311 doHook(NULL, "hook_run_end", HookRunEnd, 1);
313 while ((bulk = getbulk()) != NULL)
318 t = time(NULL) - startTime;
323 dlog(DLOG_ALL|DLOG_STDOUT,
325 "Initial queue size: %d\n"
326 " packages built: %d\n"
331 "Duration: %02d:%02d:%02d\n"
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.
346 * (pkg) itself is only added to the list if it is immediately buildable.
350 build_find_leaves(pkg_t *parent, pkg_t *pkg, pkg_t ***build_tailp,
351 int *app, int *hasworkp, int depth, int first,
365 * Already on build list, possibly in-progress, tell caller that
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);
379 PKGLIST_FOREACH(link, &pkg->idepon_list) {
387 ddprintf(ndepth, "check %s %08x\t", scan->portdir, scan->flags);
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.
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
399 dfirst_one_only = (scan->flags & PKGF_DUMMY) ? 1 : 0;
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.
406 * We must still recurse through PACKAGED packages as
407 * some of their dependencies might be missing.
409 if (scan->flags & PKGF_SUCCESS) {
410 ddprintf(0, "SUCCESS - OK\n");
418 * ERROR includes FAILURE, which is set in numerous situations
419 * including when NOBUILD state is processed. So check for
420 * NOBUILD state first.
422 * An ERROR in a sub-package causes a NOBUILD in packages
425 if (scan->flags & PKGF_NOBUILD) {
426 ddprintf(0, "NOBUILD - OK "
427 "(propogate failure upward)\n");
428 *app |= PKGF_NOBUILD_S;
433 if (scan->flags & PKGF_ERROR) {
434 ddprintf(0, "ERROR - OK (propogate failure upward)\n");
435 *app |= PKGF_NOBUILD_S;
442 * If already on build-list this dependency is not ready.
444 if (scan->flags & PKGF_BUILDLIST) {
445 ddprintf(0, " [BUILDLIST]");
446 *app |= PKGF_NOTREADY;
450 * If not packaged this dependency is not ready for
453 if ((scan->flags & PKGF_PACKAGED) == 0) {
454 ddprintf(0, " [NOT_PACKAGED]");
455 *app |= PKGF_NOTREADY;
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.
464 if (scan->flags & PKGF_BUILDTRAV) {
465 ddprintf(0, " [BUILDTRAV]\n");
466 *app |= PKGF_NOTREADY;
473 * Assert on dependency loop
476 if (scan->flags & PKGF_BUILDLOOP) {
477 dfatal("pkg dependency loop %s -> %s",
478 parent->portdir, scan->portdir);
482 * NOTE: For debug tabbing purposes we use (ndepth + 1)
483 * here (i.e. depth + 2) in our iteration.
485 scan->flags |= PKGF_BUILDLOOP;
487 ddprintf(0, " SUBRECURSION {\n");
488 idep_count += build_find_leaves(pkg, scan, build_tailp,
492 scan->flags &= ~PKGF_BUILDLOOP;
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");
501 ddprintf(ndepth, "} (sub-ok)\n");
506 pkg->idep_count = idep_count;
507 pkg->flags |= PKGF_BUILDTRAV;
510 * Incorporate scan results into pkg state.
512 if ((pkg->flags & PKGF_NOBUILD) == 0 && (*app & PKGF_NOBUILD)) {
514 } else if ((pkg->flags & PKGF_ERROR) == 0 && (*app & PKGF_ERROR)) {
517 pkg->flags |= *app & ~PKGF_NOTREADY;
520 * Clear PACKAGED bit if sub-dependencies aren't clean
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) {
529 "[XXX] %s DELETE-PACKAGE %s (failed)\n",
533 "[XXX] %s DELETE-PACKAGE %s "
534 "(due to dependencies)\n",
542 * Set PKGF_NOBUILD_I if there is IGNORE data
545 pkg->flags |= PKGF_NOBUILD_I;
548 * Handle propagated flags
550 if (pkg->flags & PKGF_ERROR) {
552 * This can only happen if the ERROR has already been
553 * processed and accounted for.
555 ddprintf(depth, "} (ERROR - %s)\n", pkg->portdir);
556 } else if (*app & PKGF_NOTREADY) {
558 * We don't set PKGF_NOTREADY in the pkg, it is strictly
559 * a transient flag propagated via build_find_leaves().
561 * Just don't add the package to the list.
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
572 } else if (pkg->flags & PKGF_SUCCESS) {
573 ddprintf(depth, "} (SUCCESS - %s)\n", pkg->portdir);
574 } else if (pkg->flags & PKGF_DUMMY) {
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.
581 ddprintf(depth, "} (DUMMY/META - SUCCESS)\n");
582 pkg->flags |= PKGF_SUCCESS;
585 dlog(DLOG_ALL | DLOG_FILTER,
586 "[XXX] %s META-ALREADY-BUILT\n",
589 dlog(DLOG_SUCC, "[XXX] %s meta-node complete\n",
592 } else if (pkg->flags & PKGF_PACKAGED) {
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.
598 ddprintf(depth, "} (PACKAGED - SUCCESS)\n");
599 pkg->flags |= PKGF_SUCCESS;
602 dlog(DLOG_ALL | DLOG_FILTER,
603 "[XXX] %s ALREADY-BUILT\n",
609 * All dependencies are successful, queue new work
610 * and indicate not-ready to the parent (since our
611 * package has to be built).
613 * NOTE: The NOBUILD case propagates to here as well
614 * and is ultimately handled by startbuild().
617 if (pkg->flags & PKGF_NOBUILD_I)
618 ddprintf(depth, "} (ADDLIST(IGNORE/BROKEN) - %s)\n",
620 else if (pkg->flags & PKGF_NOBUILD)
621 ddprintf(depth, "} (ADDLIST(NOBUILD) - %s)\n",
624 ddprintf(depth, "} (ADDLIST - %s)\n", pkg->portdir);
625 pkg->flags |= PKGF_BUILDLIST;
627 *build_tailp = &pkg->build_next;
628 *app |= PKGF_NOTREADY;
636 build_clear_trav(pkg_t *pkg)
641 pkg->flags &= ~PKGF_BUILDTRAV;
642 PKGLIST_FOREACH(link, &pkg->idepon_list) {
644 if (scan && (scan->flags & PKGF_BUILDTRAV))
645 build_clear_trav(scan);
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
655 buildCalculateDepiDepth(pkg_t *pkg)
663 return(pkg->depi_depth + 1);
664 pkg->flags |= PKGF_BUILDLOOP;
665 PKGLIST_FOREACH(link, &pkg->deponi_list) {
667 if (scan && (scan->flags & PKGF_BUILDLOOP) == 0) {
668 res = buildCalculateDepiDepth(scan);
669 if (best_depth < res)
673 pkg->flags &= ~PKGF_BUILDLOOP;
674 pkg->depi_depth = best_depth;
676 return (best_depth + 1);
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
684 * WorkerMutex is held by the caller.
688 startbuild(pkg_t **build_listp, pkg_t ***build_tailp)
699 static int IterateWorker;
704 if (*build_listp == NULL)
711 for (pkg = *build_listp; pkg; pkg = pkg->build_next)
713 idep_ary = calloc(count, sizeof(pkg_t *));
714 depi_ary = calloc(count, sizeof(pkg_t *));
717 for (pkg = *build_listp; pkg; pkg = pkg->build_next) {
718 idep_ary[count] = pkg;
719 depi_ary[count] = pkg;
724 * idep_ary - sorted by #of dependencies this pkg has.
725 * depi_ary - sorted by #of other packages that depend on this pkg.
727 qsort(idep_ary, count, sizeof(pkg_t *), qsort_idep);
728 qsort(depi_ary, count, sizeof(pkg_t *), qsort_depi);
734 * Half the workers build based on the highest depi count,
735 * the other half build based on the highest idep count.
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.
743 * Loop until we manage to assign slots to everyone. We do not
744 * wait for build completion.
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
752 while (idep_index != count || depi_index != count) {
757 * Find candidate to start sorted by depi or idep.
760 while (idep_index < count) {
761 ipkg = idep_ary[idep_index];
763 (PKGF_SUCCESS | PKGF_FAILURE |
764 PKGF_ERROR | PKGF_RUNNING)) == 0) {
772 while (depi_index < count) {
773 pkgi = depi_ary[depi_index];
775 (PKGF_SUCCESS | PKGF_FAILURE |
776 PKGF_ERROR | PKGF_RUNNING)) == 0) {
784 * ipkg and pkgi must either both be NULL, or both
787 if (ipkg == NULL && pkgi == NULL)
789 ddassert(ipkg && pkgi);
792 * Handle the NOBUILD case right here, there's no point
793 * queueing it anywhere.
795 if (ipkg->flags & PKGF_NOBUILD) {
798 ipkg->flags |= PKGF_FAILURE;
799 ipkg->flags &= ~PKGF_BUILDLIST;
801 reason = buildskipreason(NULL, ipkg);
802 if (ipkg->flags & PKGF_NOBUILD_I) {
804 dlog(DLOG_IGN, "[XXX] %s ignored due to %s\n",
805 ipkg->portdir, reason);
806 doHook(ipkg, "hook_pkg_ignored",
810 dlog(DLOG_SKIP, "[XXX] %s skipped due to %s\n",
811 ipkg->portdir, reason);
812 doHook(ipkg, "hook_pkg_skipped",
819 if (pkgi->flags & PKGF_NOBUILD) {
822 pkgi->flags |= PKGF_FAILURE;
823 pkgi->flags &= ~PKGF_BUILDLIST;
825 reason = buildskipreason(NULL, pkgi);
826 if (pkgi->flags & PKGF_NOBUILD_I) {
828 dlog(DLOG_IGN, "[XXX] %s ignored due to %s\n",
829 pkgi->portdir, reason);
830 doHook(pkgi, "hook_pkg_ignored",
834 dlog(DLOG_SKIP, "[XXX] %s skipped due to %s\n",
835 pkgi->portdir, reason);
836 doHook(pkgi, "hook_pkg_skipped",
845 * Block while no slots are available. waitbuild()
846 * will clean out any DONE states.
848 while (RunningWorkers >= DynamicMaxWorkers ||
849 RunningWorkers >= MaxWorkers - FailedWorkers) {
850 waitbuild(RunningWorkers, 1);
854 * Find an available worker slot, there should be at
857 for (i = 0; i < MaxWorkers; ++i) {
858 n = IterateWorker % MaxWorkers;
859 work = &WorkerAry[n];
861 if (work->state == WORKER_DONE ||
862 work->state == WORKER_FAILED) {
863 workercomplete(work);
865 if (work->state == WORKER_NONE ||
866 work->state == WORKER_IDLE) {
867 if (n <= MaxWorkers / 2) {
868 startworker(pkgi, work);
870 startworker(ipkg, work);
872 /*RunStatsUpdate(work);*/
877 ddassert(i != MaxWorkers);
882 * We disposed of the whole list
887 *build_tailp = build_listp;
890 typedef const pkg_t *pkg_tt;
893 qsort_idep(const void *pkg1_arg, const void *pkg2_arg)
895 const pkg_t *pkg1 = *(const pkg_tt *)pkg1_arg;
896 const pkg_t *pkg2 = *(const pkg_tt *)pkg2_arg;
898 return (pkg2->idep_count - pkg1->idep_count);
902 qsort_depi(const void *pkg1_arg, const void *pkg2_arg)
904 const pkg_t *pkg1 = *(const pkg_tt *)pkg1_arg;
905 const pkg_t *pkg2 = *(const pkg_tt *)pkg2_arg;
907 return ((pkg2->depi_count * pkg2->depi_depth) -
908 (pkg1->depi_count * pkg1->depi_depth));
912 * Frontend starts a pkg up on a worker
914 * WorkerMutex must be held.
917 startworker(pkg_t *pkg, worker_t *work)
919 switch(work->state) {
921 pthread_create(&work->td, NULL, childBuilderThread, work);
922 work->state = WORKER_IDLE;
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;
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);
937 pkg->flags |= PKGF_RUNNING;
939 pthread_cond_signal(&work->cond);
941 /*RunStatsUpdate(work);*/
950 dfatal("startworker: [%03d] Unexpected state %d for worker %d",
951 work->index, work->state, work->index);
957 cleanworker(worker_t *work)
959 work->state = WORKER_PENDING;
961 work->accum_error = 0;
962 work->start_time = time(NULL);
966 * Frontend finishes up a completed pkg on a worker.
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.
973 * WorkerMutex must be held.
976 workercomplete(worker_t *work)
985 * Steady state FAILED case.
987 if (work->state == WORKER_FAILED) {
988 if (work->pkg == NULL)
992 t = time(NULL) - work->start_time;
998 * Reduce total dep size
1000 RunningPkgDepSize -= work->pkg_dep_size;
1001 RunningPkgDepSize -= work->memuse;
1002 work->pkg_dep_size = 0;
1006 * Process pkg out of the worker
1009 if (pkg->flags & (PKGF_ERROR|PKGF_NOBUILD)) {
1010 pkg->flags |= PKGF_FAILURE;
1013 * This NOBUILD condition XXX can occur if the package is
1014 * not allowed to be built.
1016 if (pkg->flags & PKGF_NOBUILD) {
1019 reason = buildskipreason(NULL, pkg);
1020 if (pkg->flags & PKGF_NOBUILD_I) {
1022 dlog(DLOG_SKIP, "[%03d] IGNORD %s - %s\n",
1023 work->index, pkg->portdir, reason);
1024 doHook(pkg, "hook_pkg_ignored",
1028 dlog(DLOG_SKIP, "[%03d] SKIPPD %s - %s\n",
1029 work->index, pkg->portdir, reason);
1030 doHook(pkg, "hook_pkg_skipped",
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),
1041 doHook(pkg, "hook_pkg_failure", HookPkgFailure, 0);
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);
1052 pkg->flags &= ~PKGF_BUILDLIST;
1053 pkg->flags &= ~PKGF_RUNNING;
1057 if (work->state == WORKER_FAILED) {
1058 dlog(DLOG_ALL, "[%03d] XXX/XXX WORKER IS IN A FAILED STATE\n",
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;
1066 work->state = WORKER_IDLE;
1071 * Wait for one or more workers to complete.
1073 * WorkerMutex must be held.
1076 waitbuild(int whilematch, int dynamicmax)
1078 static time_t wblast_time;
1079 static time_t dmlast_time;
1085 if (whilematch == 0)
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);
1095 pthread_cond_signal(&work->cond);
1097 RunStatsUpdate(work, NULL);
1099 RunStatsUpdateTop();
1100 RunStatsUpdateLogs();
1102 if (RunningWorkers == whilematch) {
1103 clock_gettime(CLOCK_REALTIME, &ts);
1106 pthread_cond_timedwait(&WorkerCond, &WorkerMutex, &ts);
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.
1114 * Dynamically reduce MaxWorkers based on swap use, starting
1115 * at 10% swap and up to 75% of MaxWorkers at 40% swap.
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.
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.
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;
1147 * Cap based on load. This is back-loaded.
1149 getloadavg(dload, 3);
1150 if (dload[0] < min_load) {
1152 } else if (dload[0] <= max_load) {
1155 (dload[0] - min_load) /
1156 (max_load - min_load);
1158 max1 = MaxWorkers * 25 / 100;
1162 * Cap based on swap use. This is back-loaded.
1164 dswap = getswappct(&noswap);
1165 if (dswap < min_swap) {
1167 } else if (dswap <= max_swap) {
1170 (dswap - min_swap) /
1171 (max_swap - min_swap);
1173 max2 = MaxWorkers * 25 / 100;
1177 * Cap based on aggregate pkg-dependency memory
1178 * use installed in worker slots. This is
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
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.
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) {
1197 max3 = RunningWorkers + 1;
1199 max3 = RunningWorkers;
1206 * Priority reduction, convert to DynamicMaxWorkers
1215 * Restrict to allowed range, and also handle
1220 if (max_sel > DynamicMaxWorkers + 1)
1221 max_sel = DynamicMaxWorkers + 1;
1222 if (max_sel > MaxWorkers)
1223 max_sel = MaxWorkers;
1226 * Stop waiting if DynamicMaxWorkers is going to
1229 if (DynamicMaxWorkers < max1)
1235 if (DynamicMaxWorkers != max1) {
1236 dlog(DLOG_ALL | DLOG_FILTER,
1237 "[XXX] Load=%-6.2f(%2d) "
1238 "Swap=%-3.2f%%(%2d) "
1240 "Adjust Workers %d->%d\n",
1242 dswap * 100.0, max2,
1243 RunningPkgDepSize / (double)ONEGB, max3,
1244 DynamicMaxWorkers, max_sel);
1245 DynamicMaxWorkers = max_sel;
1253 * Worker pthread (WorkerAry)
1255 * This thread belongs to the dsynth master process and handled a worker slot.
1256 * (this_thread) -> WORKER fork/exec (WorkerPocess) -> (pty) -> sub-processes.
1259 childBuilderThread(void *arg)
1261 char *envary[1] = { NULL };
1262 worker_t *work = arg;
1267 volatile int dowait;
1272 pthread_mutex_lock(&WorkerMutex);
1273 while (work->terminate == 0) {
1276 switch(work->state) {
1279 case WORKER_PENDING:
1281 * Fork the management process for the pkg operation
1282 * on this worker slot.
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.
1288 * NOTE: If SOCK_CLOEXEC is not supported WorkerMutex
1289 * is sufficient to interlock F_SETFD FD_CLOEXEC
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");
1297 snprintf(slotbuf, sizeof(slotbuf),
1299 snprintf(fdbuf, sizeof(fdbuf),
1301 snprintf(flagsbuf, sizeof(flagsbuf),
1302 "%d", WorkerProcFlags);
1308 * We pass the salve descriptor in fd 3 and close all
1309 * other descriptors for security.
1311 pthread_mutex_unlock(&WorkerMutex);
1314 close(work->fds[0]);
1315 dup2(work->fds[1], 3);
1317 fcntl(3, F_SETFD, 0);
1318 execle(DSynthExecPath, DSynthExecPath,
1319 "WORKER", slotbuf, fdbuf,
1320 work->pkg->portdir, work->pkg->pkgfile,
1323 write(2, "EXECLE FAILURE\n", 15);
1326 pthread_mutex_lock(&WorkerMutex);
1327 close(work->fds[1]);
1328 work->phase = PHASE_PENDING;
1332 work->state = WORKER_RUNNING;
1334 case WORKER_RUNNING:
1336 * Poll for status updates, if NULL is returned
1337 * and status is non-zero, the communications link
1338 * failed unexpectedly.
1341 pthread_mutex_unlock(&WorkerMutex);
1342 status = ipcreadmsg(work->fds[0], &wmsg);
1343 pthread_mutex_lock(&WorkerMutex);
1347 * Normal message, can include normal
1348 * termination which changes us over
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);
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;
1369 case WMSG_CMD_SUCCESS:
1370 work->flags |= WORKERF_SUCCESS;
1372 case WMSG_CMD_FAILURE:
1373 work->flags |= WORKERF_FAILURE;
1375 case WMSG_CMD_FREEZEWORKER:
1376 work->flags |= WORKERF_FREEZE;
1381 RunStatsUpdate(work, NULL);
1384 close(work->fds[0]);
1385 pthread_mutex_unlock(&WorkerMutex);
1386 while (waitpid(work->pid, &status, 0) < 0 &&
1390 pthread_mutex_lock(&WorkerMutex);
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;
1399 pkg->flags |= PKGF_FAILURE;
1400 work->state = WORKER_FAILED;
1402 work->flags |= WORKERF_STATUS_UPDATE;
1403 pthread_cond_signal(&WorkerCond);
1408 * pkg remains attached until frontend processes the
1409 * completion. The frontend will then set the state
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.
1419 * We just sit in this state until the front-end
1420 * does something about it.
1424 dfatal("worker: [%03d] Unexpected state %d for worker %d",
1425 work->index, work->state, work->index);
1431 * The dsynth frontend will poll us approximately once
1432 * a second (its variable).
1435 pthread_cond_wait(&work->cond, &WorkerMutex);
1439 * Scrap the comm socket if running, this should cause the worker
1440 * process to kill its sub-programs and cleanup.
1442 if (work->state == WORKER_RUNNING) {
1443 pthread_mutex_unlock(&WorkerMutex);
1444 close(work->fds[0]);
1445 while (waitpid(work->pid, &status, 0) < 0 &&
1447 pthread_mutex_lock(&WorkerMutex);
1453 work->state = WORKER_EXITING;
1454 pthread_cond_signal(&WorkerCond);
1455 pthread_mutex_unlock(&WorkerMutex);
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.
1465 * Locked by WorkerMutex (global)
1468 childInstallPkgDeps(worker_t *work)
1473 if (PKGLIST_EMPTY(&work->pkg->idepon_list))
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");
1481 fchmod(fileno(fp), 0755);
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");
1493 childInstallPkgDeps_recurse(FILE *fp, pkglink_t *list, int undoit,
1494 int depth, int first_one_only)
1502 PKGLIST_FOREACH(link, list) {
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
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).
1514 ndepth = (pkg->flags & PKGF_DUMMY) ? depth : depth + 1;
1515 nfirst = (pkg->flags & PKGF_DUMMY) ? 1 : 0;
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
1523 if (depth > 1 && link->dep_type <= DEP_TYPE_BUILD) {
1530 if (pkg->dsynth_install_flg == 1) {
1531 pkg->dsynth_install_flg = 0;
1532 tot += childInstallPkgDeps_recurse(fp,
1541 if (pkg->dsynth_install_flg) {
1542 if (DebugOpt >= 2 && pkg->pkgfile && fp) {
1543 fprintf(fp, "echo 'AlreadyHave %s'\n",
1551 tot += childInstallPkgDeps_recurse(fp, &pkg->idepon_list,
1552 undoit, ndepth, nfirst);
1553 if (pkg->dsynth_install_flg) {
1558 pkg->dsynth_install_flg = 1;
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.
1565 if (pkg->pkgfile == NULL && (pkg->flags & PKGF_DUMMY)) {
1566 pkg_t *spkg = pkg->idepon_list.next->pkg;
1572 "echo 'DUMMY use %s (%p)'\n",
1573 pkg->portdir, pkg->pkgfile);
1578 "echo 'CANNOT FIND DEFAULT "
1586 * Generate package installation command
1588 if (fp && pkg->pkgfile) {
1589 fprintf(fp, "echo 'Installing /packages/All/%s'\n",
1591 fprintf(fp, "pkg install -q -y /packages/All/%s "
1595 fprintf(fp, "echo 'CANNOT FIND PKG FOR %s'\n",
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)
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;
1616 tot += st.st_size * 2;
1627 * Worker process interactions.
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
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.
1638 * The command line forwarded to this function is:
1640 * WORKER slot# socketfd portdir/subdir
1646 * SSL_NO_VERIFY_PEER=1
1647 * USE_PACKAGE_DEPENDS_ONLY=1 (exec_phase_depends)
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
1666 * MAKE_JOBS_NUMBER=n
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)
1674 * PHASES: make -C path FLAVOR=flavor <phase>
1692 * package e.g. /construction/lang/perl5.28/pkg/perl5-5.28.2.txz
1693 * install-mtree (skipped)
1695 * deinstall (skipped)
1698 WorkerProcess(int ac, char **av)
1707 int do_install_phase;
1722 dlog(DLOG_ALL, "WORKER PROCESS %d- bad arguments\n", getpid());
1725 slot = strtol(av[1], NULL, 0);
1726 fd = strtol(av[2], NULL, 0); /* master<->slave messaging */
1729 flavor = strchr(portdir, '@');
1732 asprintf(&buf, "@%s", flavor);
1733 WorkerFlavorPrt = buf;
1734 buf = NULL; /* safety */
1736 WorkerProcFlags = strtol(av[5], NULL, 0);
1738 bzero(&wmsg, sizeof(wmsg));
1740 setproctitle("[%02d] WORKER STARTUP %s%s",
1741 slot, portdir, WorkerFlavorPrt);
1743 if (strcmp(portdir, "ports-mgmt/pkg") == 0)
1746 signal(SIGTERM, phaseTerminateSignal);
1747 signal(SIGINT, phaseTerminateSignal);
1748 signal(SIGHUP, phaseTerminateSignal);
1751 * Set up the environment
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);
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);
1769 * CCache is a horrible unreliable hack but... leave the
1770 * mechanism in-place in case someone has a death wish.
1773 addbuildenv("WITH_CCACHE_BUILD", "yes", BENV_MAKECONF);
1774 addbuildenv("CCACHE_DIR", "/ccache", BENV_MAKECONF);
1777 addbuildenv("UID", "0", BENV_MAKECONF);
1778 addbuildenv("ARCH", ArchitectureName, BENV_MAKECONF);
1780 #ifdef __DragonFly__
1781 addbuildenv("OPSYS", "DragonFly", BENV_MAKECONF);
1782 addbuildenv("DFLYVERSION", VersionFromParamHeader, BENV_MAKECONF);
1783 addbuildenv("OSVERSION", "9999999", BENV_MAKECONF);
1785 #error "Need OS-specific data to generate make.conf"
1788 addbuildenv("OSREL", ReleaseName, BENV_MAKECONF);
1789 addbuildenv("_OSRELEASE", VersionOnlyName, BENV_MAKECONF);
1792 "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin",
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);
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);
1807 * Special consideration
1809 * PACKAGE_BUILDING - Disallow packaging ports which do not allow
1810 * for binary distribution.
1812 * PKG_CREATE_VERBOSE - Ensure periodic output during the packaging
1813 * process to avoid a watchdog timeout.
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);
1823 setenv("FLAVOR", flavor, 1);
1828 if (procctl(P_PID, getpid(), PROC_REAP_ACQUIRE, NULL) < 0)
1829 dfatal_errno("procctl() - Cannot become reaper");
1832 * Initialize a worker structure
1836 bzero(&pkg, sizeof(pkg));
1837 pkg.portdir = portdir; /* sans flavor */
1838 pkg.pkgfile = pkgfile;
1839 if (strchr(portdir, '/'))
1840 len = strchr(portdir, '/') - portdir;
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);
1855 if (DebugOpt >= 2) {
1856 dlog(DLOG_ALL, "[%03d] %s LOGFILE %s\n",
1857 slot, pkg.portdir, pkg.logfile);
1861 dlog(DLOG_ALL, "[%03d] LOGFILE %s (create failed)\n",
1866 * Setup the work structure. Because this is an exec'd sub-process,
1867 * there is only one work structure.
1869 work = &WorkerAry[0];
1870 work->flavor = flavor;
1873 work->start_time = time(NULL);
1879 setproctitle("[%02d] WORKER MOUNTS %s%s",
1880 slot, portdir, WorkerFlavorPrt);
1881 DoWorkerMounts(work);
1884 * Generate an /etc/make.conf in the build base
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)
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);
1897 fprintf(fp, "%s=%s\n", benv->label, benv->data);
1907 initbulk(childHookRun, MaxBulk);
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;
1919 wmsg.cmd = WMSG_CMD_STATUS_UPDATE;
1920 wmsg.phase = PHASE_INSTALL_PKGS;
1923 status = ipcwritemsg(fd, &wmsg);
1926 dophase(work, &wmsg,
1927 WDOG5, PHASE_PACKAGE, "package");
1929 if (do_install_phase) {
1930 dophase(work, &wmsg,
1931 WDOG4, PHASE_INSTALL_PKGS, "setup");
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");
1964 dophase(work, &wmsg,
1965 WDOG5, PHASE_TEST, "test");
1967 dophase(work, &wmsg,
1968 WDOG1, PHASE_CHECK_PLIST, "check-plist");
1969 dophase(work, &wmsg,
1970 WDOG5, PHASE_PACKAGE, "package");
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");
1981 if (MasterPtyFd >= 0) {
1986 setproctitle("[%02d] WORKER CLEANUP %s%s",
1987 slot, portdir, WorkerFlavorPrt);
1990 * Copy the package to the repo.
1992 if (work->accum_error == 0) {
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);
2009 * Unmount, unless we are in DebugStopMode.
2011 if ((WorkerProcFlags & WORKER_PROC_DEBUGSTOP) == 0)
2012 DoWorkerUnmounts(work);
2015 * Send completion status to master dsynth worker thread.
2017 if (work->accum_error) {
2018 wmsg.cmd = WMSG_CMD_FAILURE;
2020 wmsg.cmd = WMSG_CMD_SUCCESS;
2022 ipcwritemsg(fd, &wmsg);
2023 if (WorkerProcFlags & WORKER_PROC_DEBUGSTOP) {
2024 wmsg.cmd = WMSG_CMD_FREEZEWORKER;
2025 ipcwritemsg(fd, &wmsg);
2028 while ((bulk = getbulk()) != NULL)
2035 dophase(worker_t *work, wmsg_t *wmsg, int wdog, int phaseid, const char *phase)
2037 pkg_t *pkg = work->pkg;
2051 if (work->accum_error)
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;
2065 * Execute the port make command in chroot on a pty.
2069 if (MasterPtyFd >= 0) {
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.
2080 * Solve this by hand-shaking the slave tty to give
2081 * the master time to close its slavefd.
2083 * Leave the tty defaults intact, which also likely
2084 * means it will be in line-buffered mode, so handshake
2087 * TODO: Our handshake probably echos back to the master pty
2088 * due to tty echo, and ends up in the log, fix me.
2090 slavefd = open(ptsname(MasterPtyFd), O_RDWR);
2091 dassert_errno(slavefd >= 0, "Cannot open slave pty");
2095 /* login_tty() closes slavefd */
2099 write(MasterPtyFd, "x\n", 2);
2102 pid = forkpty(&MasterPtyFd, NULL, NULL, NULL);
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.
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.
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",
2126 printf("tcgetattr failed: %s\n", strerror(errno));
2130 * Clean-up, chdir, and chroot.
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");
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
2149 fd = open("/dev/null", O_RDWR);
2150 dassert_errno(fd >= 0, "cannot open /dev/null");
2158 * Execute the appropriate command.
2161 case PHASE_INSTALL_PKGS:
2162 snprintf(buf, sizeof(buf), "/tmp/dsynth_install_pkgs");
2163 execl(buf, buf, NULL);
2166 snprintf(buf, sizeof(buf), "/xports/%s", pkg->portdir);
2167 execl(MAKE_BINARY, MAKE_BINARY, "-C", buf, phase, NULL);
2172 fcntl(MasterPtyFd, F_SETFL, O_NONBLOCK);
2175 dlog(DLOG_ALL, "[%03d] %s Fork Failed: %s\n",
2176 work->index, pkg->logfile, strerror(errno));
2177 ++work->accum_error;
2183 fdlog = open(pkg->logfile, O_RDWR|O_CREAT|O_APPEND, 0644);
2185 dlog(DLOG_ALL, "[%03d] %s Cannot open logfile '%s': %s\n",
2186 work->index, pkg->portdir,
2187 pkg->logfile, strerror(errno));
2190 snprintf(buf, sizeof(buf),
2191 "----------------------------------------"
2192 "---------------------------------------\n"
2194 "----------------------------------------"
2195 "---------------------------------------\n",
2197 write(fdlog, buf, strlen(buf));
2199 start_time = time(NULL);
2200 last_time = start_time;
2201 wdog_time = start_time;
2206 ms = mptylogpoll(MasterPtyFd, fdlog, wmsg, &wdog_time);
2207 if (ms == MPTY_FAILED) {
2209 "[%03d] %s lost pty in phase %s, terminating\n",
2210 work->index, pkg->portdir, phase);
2217 * Generally speaking update status once a second.
2218 * This also allows us to detect if the management
2219 * dsynth process has gone away.
2221 next_time = time(NULL);
2222 if (next_time != last_time) {
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.
2232 if (ipcwritemsg(work->fds[0], wmsg) < 0)
2234 last_time = next_time;
2239 getloadavg(dload, 3);
2240 dv = dload[2] / NumCores;
2241 if (dv < (double)NumCores) {
2244 if (dv > 4.0 * NumCores)
2245 dv = 4.0 * NumCores;
2246 wdog_scaled = wdog * dv / NumCores;
2252 if (next_time - wdog_time >= wdog_scaled * 60) {
2253 snprintf(buf, sizeof(buf),
2255 "WATCHDOG TIMEOUT FOR %s in %s "
2256 "after %d minutes\n"
2259 pkg->portdir, phase, wdog_scaled, pid);
2261 write(fdlog, buf, strlen(buf));
2263 "[%03d] %s WATCHDOG TIMEOUT in %s "
2264 "after %d minutes (%d min scaled)\n",
2265 work->index, pkg->portdir, phase,
2268 ++work->accum_error;
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.
2279 * Generally reap any other processes we have inherited
2280 * while we are here.
2283 wpid = wait3(&status, WNOHANG, NULL);
2284 } while (wpid > 0 && wpid != pid);
2285 if (wpid == pid && WIFEXITED(status)) {
2291 next_time = time(NULL);
2293 setproctitle("[%02d] WORKER EXITREAP %s%s",
2294 work->index, pkg->portdir, WorkerFlavorPrt);
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).
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)) {
2310 if (wpid < 0 && errno != EINTR) {
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).
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
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.
2332 while (mptylogpoll(MasterPtyFd, fdlog, wmsg, &wdog_time) == MPTY_DATA)
2336 * Report on the exit condition. If the pid was somehow lost
2337 * (probably due to someone gdb'ing the process), assume an error.
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;
2348 dlog(DLOG_ALL, "[%03d] %s Build phase '%s' failed - lost pid\n",
2349 work->index, pkg->portdir, phase);
2350 ++work->accum_error;
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.
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,
2366 * (dsynth already estimated the space used by the package deps
2367 * up front, but this will help us further).
2369 if (work->accum_error == 0 && phaseid == PHASE_EXTRACT) {
2373 asprintf(&b1, "%s/construction", work->basedir);
2374 if (statfs(b1, &sfs) == 0) {
2375 wmsg->memuse = (sfs.f_blocks - sfs.f_bfree) *
2377 ipcwritemsg(work->fds[0], wmsg);
2390 last_time = next_time - start_time;
2392 m = last_time / 60 % 60;
2393 h = last_time / 3600;
2395 fp = fdopen(fdlog, "a");
2397 dlog(DLOG_ALL, "[%03d] %s Cannot fdopen fdlog: %s %d\n",
2398 work->index, pkg->portdir,
2399 strerror(errno), fstat(fdlog, &st));
2405 if (work->accum_error) {
2406 fprintf(fp, "FAILED %02d:%02d:%02d\n", h, m, s);
2408 if (phaseid == PHASE_EXTRACT && wmsg->memuse) {
2409 fprintf(fp, "Extracted Memory Use: %6.2fM\n",
2410 wmsg->memuse / (1024.0 * 1024.0));
2412 fprintf(fp, "SUCCEEDED %02d:%02d:%02d\n", h, m, s);
2414 last_time = next_time - work->start_time;
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);
2432 struct reaper_status rs;
2435 while (procctl(P_PID, getpid(), PROC_REAP_STATUS, &rs) == 0) {
2436 if ((rs.flags & PROC_REAP_ACQUIRE) == 0)
2438 if (rs.pid_head < 0)
2440 if (kill(rs.pid_head, SIGKILL) == 0) {
2441 while (waitpid(rs.pid_head, &status, 0) < 0)
2445 while (wait3(&status, 0, NULL) > 0)
2450 phaseTerminateSignal(int sig __unused)
2452 if (CopyFileFd >= 0)
2454 if (MasterPtyFd >= 0)
2457 kill(SigPid, SIGKILL);
2460 DoWorkerUnmounts(SigWork);
2466 buildskipreason(pkglink_t *parent, pkg_t *pkg)
2470 char *reason = NULL;
2476 if ((pkg->flags & PKGF_NOBUILD_I) && pkg->ignore)
2477 asprintf(&reason, "%s ", pkg->ignore);
2480 PKGLIST_FOREACH(link, &pkg->idepon_list) {
2482 if (link->dep_type > DEP_TYPE_BUILD)
2488 if ((scan->flags & (PKGF_ERROR | PKGF_NOBUILD)) == 0)
2490 if (scan->flags & PKGF_NOBUILD) {
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);
2500 len = strlen(scan->portdir) + 8;
2501 reason = realloc(reason, tot + len);
2502 snprintf(reason + tot, len, "%s", scan->portdir);
2506 * Don't try to print the entire graph
2510 tot += strlen(reason + tot);
2511 reason[tot++] = ' ';
2518 * The master ptyfd is in non-blocking mode. Drain up to 1024 bytes
2519 * and update wmsg->lines and *wdog_timep as appropriate.
2521 * This function will poll, stalling up to 1 second.
2524 mptylogpoll(int ptyfd, int fdlog, wmsg_t *wmsg, time_t *wdog_timep)
2531 pfd.events = POLLIN;
2534 poll(&pfd, 1, 1000);
2536 r = read(ptyfd, buf, sizeof(buf));
2538 *wdog_timep = time(NULL);
2539 if (r > 0 && fdlog >= 0)
2540 write(fdlog, buf, r);
2547 if (errno != EINTR && errno != EAGAIN)
2550 } else if (r == 0) {
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
2562 * This is called by the WORKER process.
2564 * (dsynth management thread -> WORKER process -> sub-processes)
2566 #define COPYBLKSIZE 32768
2569 copyfile(char *src, char *dst)
2579 asprintf(&tmp, "%s.new", dst);
2580 buf = malloc(COPYBLKSIZE);
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);
2587 while ((r = read(fd1, buf, COPYBLKSIZE)) > 0) {
2588 if (write(fd2, buf, r) != r)
2593 mask = sigsetmask(sigmask(SIGTERM)|sigmask(SIGINT)|sigmask(SIGHUP));
2601 if (rename(tmp, dst)) {
2616 * primary process (threaded) - run_start, run_end, pkg_ignored, pkg_skipped
2617 * worker process (threaded) - pkg_sucess, pkg_failure
2619 * If waitfor is non-zero this hook will be serialized.
2622 doHook(pkg_t *pkg, const char *id, const char *path, int waitfor)
2626 while (waitfor && getbulk() != NULL)
2629 queuebulk(pkg->portdir, id, path, pkg->pkgfile);
2631 queuebulk(NULL, id, path, NULL);
2632 while (waitfor && getbulk() != NULL)
2637 * Execute hook (backend)
2642 * s4 - pkgfile (if applicable)
2645 childHookRun(bulk_t *bulk)
2647 const char *cav[MAXCAC];
2648 buildenv_t benv[MAXCAC];
2662 bzero(benv, sizeof(benv));
2664 cav[cac++] = bulk->s3;
2666 benv[bi].label = "PROFILE";
2667 benv[bi].data = Profile;
2670 benv[bi].label = "DIR_PACKAGES";
2671 benv[bi].data = PackagesPath;
2674 benv[bi].label = "DIR_REPOSITORY";
2675 benv[bi].data = RepositoryPath;
2678 benv[bi].label = "DIR_PORTS";
2679 benv[bi].data = DPortsPath;
2682 benv[bi].label = "DIR_OPTIONS";
2683 benv[bi].data = OptionsPath;
2686 benv[bi].label = "DIR_DISTFILES";
2687 benv[bi].data = DistFilesPath;
2690 benv[bi].label = "DIR_LOGS";
2691 benv[bi].data = LogsPath;
2694 benv[bi].label = "DIR_BUILDBASE";
2695 benv[bi].data = BuildBase;
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;
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;
2708 snprintf(buf2, sizeof(buf2), "%d", BuildFailCount);
2709 benv[bi].label = "PORTS_FAILED";
2710 benv[bi].data = buf2;
2712 snprintf(buf3, sizeof(buf3), "%d", BuildIgnoreCount);
2713 benv[bi].label = "PORTS_IGNORED";
2714 benv[bi].data = buf3;
2716 snprintf(buf4, sizeof(buf4), "%d", BuildSkipCount);
2717 benv[bi].label = "PORTS_SKIPPED";
2718 benv[bi].data = buf4;
2722 * success, failure, ignored, skipped
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";
2734 dfatal("Unknown hook id: %s", bulk->s2);
2740 * For compatibility with synth:
2742 * ORIGIN does not include any @flavor, thus it is suitable
2743 * for finding the actual port directory/subdirectory.
2745 * FLAVOR is set to ORIGIN if there is no flavor, otherwise
2746 * it is set to only the flavor sans the '@'.
2748 if ((ptr = strchr(bulk->s1, '@')) != NULL) {
2749 snprintf(buf1, sizeof(buf1), "%*.*s",
2750 (int)(ptr - bulk->s1),
2751 (int)(ptr - bulk->s1),
2753 benv[bi].label = "ORIGIN";
2754 benv[bi].data = buf1;
2756 benv[bi].label = "FLAVOR";
2757 benv[bi].data = ptr + 1;
2760 benv[bi].label = "ORIGIN";
2761 benv[bi].data = bulk->s1;
2763 benv[bi].label = "FLAVOR";
2764 benv[bi].data = bulk->s1;
2767 benv[bi].label = "PKGNAME";
2768 benv[bi].data = bulk->s4;
2772 benv[bi].label = NULL;
2773 benv[bi].data = NULL;
2775 fp = dexec_open(cav, cac, &pid, benv, 0, 0);
2776 while ((ptr = fgetln(fp, &len)) != NULL)
2779 if (dexec_close(fp, pid)) {
2781 "[XXX] %s SCRIPT %s (%s)\n",
2782 bulk->s1, bulk->s2, bulk->s3);