2 * Copyright (c) 2019-2022 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
38 #include <sys/types.h>
41 #include <sys/sysctl.h>
42 #include <sys/socket.h>
44 #include <sys/mount.h>
45 #include <sys/procctl.h>
46 #include <sys/resource.h> /* setpriority() */
48 #if defined(__DragonFly__)
49 #include <sys/vmmeter.h>
65 #include <pthread_np.h>
72 * More esoteric headers
74 #include <libutil.h> /* forkpty() */
75 #include <arpa/inet.h> /* ntohl() */
76 #include <elf.h> /* try to get elf info */
80 #define DSYNTH_VERSION "1.0.2"
81 #define MAXWORKERS 1024
82 #define MAXLOGLINES 1024
83 #define MAXJOBS 8192 /* just used for -j sanity */
84 #define MAXBULK MAXWORKERS
86 #define MAKE_BINARY "/usr/bin/make"
87 #define PKG_BINARY "/usr/local/sbin/pkg"
88 #define MOUNT_BINARY "/sbin/mount"
89 #define UMOUNT_BINARY "/sbin/umount"
91 #define STATS_FILE "monitor.dat" /* under LogsPath */
92 #define STATS_LOCKFILE "monitor.lk" /* under LogsPath */
94 #define ONEGB (1024L * 1024 * 1024)
95 #define DISABLED_STR "disabled"
98 * This can be ".tar", ".tgz", ".txz", ".tbz", "tzst".
100 * .tar - very fast but you'll need 1TB+ of storage just for the package files.
101 * .txz - very compact but decompression speed is horrible.
102 * .tgz - reasonable compression, extremely fast decompression. Roughly
103 * 1.1x to 2.0x the size of a .txz, but decompresses 10x faster.
104 * .tbz - worse than .tgz generally
105 * .tzst - slightly worse compression ratio to .txz, decompresses 13x faster.
107 * NOTE: Decompression speed does effect bulk builds since each slot has
108 * to install pre-reqs before building any particular package. Set
109 * the default to .txz to remain close to synth's default.
111 * NOTE: As of version 1.17 of pkg, all package files use the .pkg suffix
112 * regardless of the compression format.
114 #define USE_PKG_SUFX ".txz"
119 typedef struct pkglink
{
120 struct pkglink
*next
;
121 struct pkglink
*prev
;
126 #define DEP_TYPE_FETCH 1
127 #define DEP_TYPE_EXT 2
128 #define DEP_TYPE_PATCH 3
129 #define DEP_TYPE_BUILD 4
130 #define DEP_TYPE_LIB 5
131 #define DEP_TYPE_RUN 6
134 * Describes a [flavored] package
137 struct pkg
*build_next
; /* topology inversion build list */
138 struct pkg
*bnext
; /* linked list from bulk return */
139 struct pkg
*hnext1
; /* hash based on portdir */
140 struct pkg
*hnext2
; /* hash based on pkgfile */
141 pkglink_t idepon_list
; /* I need these pkgs */
142 pkglink_t deponi_list
; /* pkgs which depend on me */
143 char *portdir
; /* origin name e.g. www/chromium[@flavor] */
144 char *logfile
; /* relative logfile path */
145 char *version
; /* PKGVERSION - e.g. 3.5.0_1 */
146 char *pkgfile
; /* PKGFILE - e.g. flav-blah-3.5.0_1.txz */
147 char *distfiles
; /* DISTFILES - e.g. blah-68.0.source.tar.xz */
148 char *distsubdir
; /* DIST_SUBDIR- e.g. cabal */
149 char *ignore
; /* IGNORE (also covers BROKEN) */
150 char *fetch_deps
; /* FETCH_DEPENDS */
151 char *ext_deps
; /* EXTRACT_DEPENDS */
152 char *patch_deps
; /* PATCH_DEPENDS */
153 char *build_deps
; /* BUILD_DEPENDS */
154 char *lib_deps
; /* LIB_DEPENDS */
155 char *run_deps
; /* RUN_DEPENDS */
156 char *pos_options
; /* SELECTED_OPTIONS */
157 char *neg_options
; /* DESELECTED_OPTIONS */
158 char *flavors
; /* FLAVORS - e.g. py36 py27 */
159 char *uses
; /* USES (metaport test) */
160 int make_jobs_number
; /* MAKE_JOBS_NUMBER */
161 int use_linux
; /* USE_LINUX */
162 int idep_count
; /* count recursive idepon build deps */
163 int depi_count
; /* count recursive deponi build deps */
164 int depi_depth
; /* tree depth who depends on me */
165 int dsynth_install_flg
; /* locked with WorkerMutex */
167 int rscan
; /* recursive scan flag (serialized use) */
168 uint32_t crc32
; /* crc of port directory tree */
169 size_t pkgfile_size
; /* size of pkgfile */
172 #define PKGF_PACKAGED 0x00000001 /* has a repo package */
173 #define PKGF_DUMMY 0x00000002 /* generic root for flavors */
174 #define PKGF_NOTFOUND 0x00000004 /* dport not found */
175 #define PKGF_CORRUPT 0x00000008 /* dport corrupt */
176 #define PKGF_PLACEHOLD 0x00000010 /* pre-entered */
177 #define PKGF_BUILDLIST 0x00000020 /* on build_list */
178 #define PKGF_BUILDLOOP 0x00000040 /* traversal loop test */
179 #define PKGF_BUILDTRAV 0x00000080 /* traversal optimization */
180 #define PKGF_NOBUILD_D 0x00000100 /* can't build - dependency problem */
181 #define PKGF_NOBUILD_S 0x00000200 /* can't build - skipped */
182 #define PKGF_NOBUILD_F 0x00000400 /* can't build - failed */
183 #define PKGF_NOBUILD_I 0x00000800 /* can't build - ignored or broken */
184 #define PKGF_SUCCESS 0x00001000 /* build complete */
185 #define PKGF_FAILURE 0x00002000 /* build complete */
186 #define PKGF_RUNNING 0x00004000 /* build complete */
187 #define PKGF_PKGPKG 0x00008000 /* pkg/pkg-static special */
188 #define PKGF_NOTREADY 0x00010000 /* build_find_leaves() only */
189 #define PKGF_MANUALSEL 0x00020000 /* manually specified */
190 #define PKGF_META 0x00040000 /* USES contains 'metaport' */
191 #define PKGF_DEBUGSTOP 0x00080000 /* freeze slot on completion */
193 #define PKGF_ERROR (PKGF_PLACEHOLD | PKGF_CORRUPT | PKGF_NOTFOUND | \
195 #define PKGF_NOBUILD (PKGF_NOBUILD_D | PKGF_NOBUILD_S | PKGF_NOBUILD_F | \
198 #define PKGLIST_EMPTY(pkglink) ((pkglink)->next == (pkglink))
199 #define PKGLIST_FOREACH(var, head) \
200 for (var = (head)->next; var != (head); var = (var)->next)
202 typedef struct bulk
{
207 enum { UNLISTED
, ONSUBMIT
, ONRUN
, ISRUNNING
, ONRESPONSE
} state
;
216 pkg_t
*list
; /* pkgs linked by bnext */
220 * Worker state (up to MAXWORKERS). Each worker operates within a
221 * chroot or jail. A system mirror is setup and the template
226 * /sbin - nullfs (ro)
228 * /libexec - nullfs (ro)
229 * /usr/bin - nullfs (ro)
230 * /usr/include - nullfs (ro)
231 * /usr/lib - nullfs (ro)
232 * /usr/libdata - nullfs (ro)
233 * /usr/libexec - nullfs (ro)
234 * /usr/sbin - nullfs (ro)
235 * /usr/share - nullfs (ro)
236 * /xports - nullfs (ro)
237 * /options - nullfs (ro)
238 * /packages - nullfs (ro)
239 * /distfiles - nullfs (ro)
240 * construction - tmpfs
242 * /boot - nullfs (ro)
243 * /boot/modules.local - tmpfs
244 * /usr/games - nullfs (ro)
245 * /usr/src - nullfs (ro)
248 enum worker_state
{ WORKER_NONE
, WORKER_IDLE
, WORKER_PENDING
,
249 WORKER_RUNNING
, WORKER_DONE
, WORKER_FAILED
,
250 WORKER_FROZEN
, WORKER_EXITING
};
251 typedef enum worker_state worker_state_t
;
253 enum worker_phase
{ PHASE_PENDING
,
260 PHASE_EXTRACT_DEPENDS
,
281 typedef enum worker_phase worker_phase_t
;
284 * Watchdog timeouts, in minutes, baseline, scales up with load/ncpus but
285 * does not scale down.
292 #define WDOG6 (60 + 30)
293 #define WDOG7 (60 * 2)
294 #define WDOG8 (60 * 2 + 30)
295 #define WDOG9 (60 * 3)
297 typedef struct worker
{
298 int index
; /* worker number 0..N-1 */
300 int accum_error
; /* cumulative error */
301 int mount_error
; /* mount and unmount error */
302 int terminate
: 1; /* request sub-thread to terminate */
303 char *basedir
; /* base directory including id */
305 pthread_t td
; /* pthread */
306 pthread_cond_t cond
; /* interlock cond (w/ WorkerMutex) */
308 worker_state_t state
; /* general worker state */
309 worker_phase_t phase
; /* phase control in childBuilderThread */
314 int fds
[2]; /* forked environment process */
316 size_t pkg_dep_size
; /* pkg dependency size(s) */
319 #define WORKERF_STATUS_UPDATE 0x0001 /* display update */
320 #define WORKERF_SUCCESS 0x0002 /* completion flag */
321 #define WORKERF_FAILURE 0x0004 /* completion flag */
322 #define WORKERF_FREEZE 0x0008 /* freeze the worker */
324 #define MOUNT_TYPE_MASK 0x000F
325 #define MOUNT_TYPE_TMPFS 0x0001
326 #define MOUNT_TYPE_NULLFS 0x0002
327 #define MOUNT_TYPE_DEVFS 0x0003
328 #define MOUNT_TYPE_PROCFS 0x0004
329 #define MOUNT_TYPE_RW 0x0010
330 #define MOUNT_TYPE_BIG 0x0020
331 #define MOUNT_TYPE_TMP 0x0040
332 #define MOUNT_TYPE_MED 0x0080
334 #define NULLFS_RO (MOUNT_TYPE_NULLFS)
335 #define NULLFS_RW (MOUNT_TYPE_NULLFS | MOUNT_TYPE_RW)
336 #define PROCFS_RO (MOUNT_TYPE_PROCFS)
337 #define TMPFS_RW (MOUNT_TYPE_TMPFS | MOUNT_TYPE_RW)
338 #define TMPFS_RW_BIG (MOUNT_TYPE_TMPFS | MOUNT_TYPE_RW | \
340 #define TMPFS_RW_MED (MOUNT_TYPE_TMPFS | MOUNT_TYPE_RW | \
342 #define DEVFS_RW (MOUNT_TYPE_DEVFS | MOUNT_TYPE_RW)
345 * IPC messages between the worker support thread and the worker process.
347 typedef struct wmsg
{
352 worker_phase_t phase
;
355 #define WMSG_CMD_STATUS_UPDATE 0x0001
356 #define WMSG_CMD_SUCCESS 0x0002
357 #define WMSG_CMD_FAILURE 0x0003
358 #define WMSG_CMD_INSTALL_PKGS 0x0004
359 #define WMSG_RES_INSTALL_PKGS 0x0005
360 #define WMSG_CMD_FREEZEWORKER 0x0006
363 * Make variables and build environment
365 typedef struct buildenv
{
366 struct buildenv
*next
;
369 char *a1
; /* allocations */
370 char *a2
; /* allocations */
375 * Operating systems recognized by dsynth
378 OS_UNKNOWN
, OS_DRAGONFLY
, OS_FREEBSD
, OS_NETBSD
, OS_LINUX
381 typedef enum os_id os_id_t
;
386 #define DLOG_ALL 0 /* Usually stdout when curses disabled */
387 #define DLOG_SUCC 1 /* success_list.log */
388 #define DLOG_FAIL 2 /* failure_list.log */
389 #define DLOG_IGN 3 /* ignored_list.log */
390 #define DLOG_SKIP 4 /* skipped_list.log */
391 #define DLOG_ABN 5 /* abnormal_command_output */
392 #define DLOG_OBS 6 /* obsolete_packages.log */
393 #define DLOG_DEBUG 7 /* debug.log */
394 #define DLOG_COUNT 8 /* total number of DLOGs */
395 #define DLOG_MASK 0x0FF
397 #define DLOG_FILTER 0x100 /* Filter out of stdout in non-curses mode */
398 #define DLOG_RED 0x200 /* Print in color */
399 #define DLOG_GRN 0x400 /* Print in color */
400 #define DLOG_STDOUT 0x800 /* And stdout */
402 #define dassert(exp, fmt, ...) \
403 if (!(exp)) dpanic(fmt, ## __VA_ARGS__)
405 #define ddassert(exp) \
406 dassert((exp), "\"%s\" line %d", __FILE__, __LINE__)
408 #define dassert_errno(exp, fmt, ...) \
409 if (!(exp)) dpanic_errno(fmt, ## __VA_ARGS__)
411 #define dlog_tab(which, tab, fmt, ...) \
412 _dlog(which, "%*.*s" fmt, (int)tab, (int)tab, "", ## __VA_ARGS__)
414 #define dlog(which, fmt, ...) \
415 _dlog(which, fmt, ## __VA_ARGS__)
417 #define dlog_tsnl(which, fmt, ...) \
418 _dlog(which, fmt, ## __VA_ARGS__)
420 #define dfatal(fmt, ...) \
421 _dfatal(__FILE__, __LINE__, __func__, 0, fmt, ## __VA_ARGS__)
423 #define dpanic(fmt, ...) \
424 _dfatal(__FILE__, __LINE__, __func__, 2, fmt, ## __VA_ARGS__)
426 #define dfatal_errno(fmt, ...) \
427 _dfatal(__FILE__, __LINE__, __func__, 1, fmt, ## __VA_ARGS__)
429 #define dpanic_errno(fmt, ...) \
430 _dfatal(__FILE__, __LINE__, __func__, 3, fmt, ## __VA_ARGS__)
432 #define ddprintf(tab, fmt, ...) \
434 if (DebugOpt == 1) dlog_tab(DLOG_DEBUG, tab, fmt, ## __VA_ARGS__); \
435 if (DebugOpt > 1) _ddprintf(tab, fmt, ## __VA_ARGS__); \
439 * addbuildenv() types
441 #define BENV_ENVIRONMENT 1
442 #define BENV_MAKECONF 2
443 #define BENV_CMDMASK 0x000F
445 #define BENV_PKGLIST 0x0010
448 * WORKER process flags
450 #define WORKER_PROC_DEBUGSTOP 0x0001
451 #define WORKER_PROC_DEVELOPER 0x0002
452 #define WORKER_PROC_CHECK_PLIST 0x0004
453 #define WORKER_PROC_INSTALL 0x0008
454 #define WORKER_PROC_DEINSTALL 0x0010
455 #define WORKER_PROC_PKGV17 0x0020
456 #define WORKER_PROC_FETCHONLY 0x0040
461 #define DOSTRING(label) #label
462 #define SCRIPTPATH(x) DOSTRING(x)
466 * RunStats satellite modules
468 typedef struct topinfo
{
488 typedef struct runstats
{
489 struct runstats
*next
;
493 void (*update
)(worker_t
*work
, const char *portdir
);
494 void (*updateTop
)(topinfo_t
*info
);
495 void (*updateLogs
)(void);
496 void (*updateCompletion
)(worker_t
*work
, int dlogid
, pkg_t
*pkg
,
497 const char *reason
, const char *skipbuf
);
501 typedef struct monitorlog
{
507 int buf_discard_mode
;
511 extern runstats_t NCursesRunStats
;
512 extern runstats_t MonitorRunStats
;
513 extern runstats_t HtmlRunStats
;
515 extern int BuildCount
;
516 extern int BuildTotal
;
517 extern int BuildFailCount
;
518 extern int BuildSkipCount
;
519 extern int BuildIgnoreCount
;
520 extern int BuildSuccessCount
;
521 extern int BuildMissingCount
;
522 extern int BuildMetaCount
;
523 extern int DynamicMaxWorkers
;
525 extern buildenv_t
*BuildEnv
;
526 extern int WorkerProcFlags
;
529 extern int CapabilityRestrictions
;
530 extern int MaskProbeAbort
;
532 extern int SlowStartOpt
;
534 extern int OverridePkgDeleteOpt
;
535 extern int FetchOnlyOpt
;
537 extern int NullStdinOpt
;
538 extern int NumaSetSize
;
539 extern int DeleteObsoletePkgs
;
540 extern int UseCCache
;
541 extern int UseUsrSrc
;
545 extern long PkgDepMemoryTarget
;
546 extern long PkgDepScaleTarget
;
548 extern int MaxWorkers
;
550 extern int UseTmpfsWork
;
551 extern int UseTmpfsBase
;
552 extern int UseNCurses
;
553 extern int LeveragePrebuilt
;
554 extern char *DSynthExecPath
;
555 extern char *ProfileOverrideOpt
;
557 extern const char *OperatingSystemName
;
558 extern const char *ArchitectureName
;
559 extern const char *MachineName
;
560 extern const char *ReleaseName
;
561 extern const char *VersionName
;
562 extern const char *VersionOnlyName
;
563 extern const char *VersionFromParamHeader
;
565 extern const char *ConfigBase1
;
566 extern const char *ConfigBase2
;
567 extern const char *ConfigBase
;
568 extern const char *DPortsPath
;
569 extern const char *CCachePath
;
570 extern const char *PackagesPath
;
571 extern const char *RepositoryPath
;
572 extern const char *OptionsPath
;
573 extern const char *DistFilesPath
;
574 extern const char *BuildBase
;
575 extern const char *LogsPath
;
576 extern const char *SystemPath
;
577 extern const char *UsePkgSufx
;
578 extern const char *Profile
;
579 extern int MetaVersion
;
580 extern char *StatsBase
;
581 extern char *StatsFilePath
;
582 extern char *StatsLockPath
;
584 extern int UsingHooks
;
585 extern const char *HookRunStart
;
586 extern const char *HookRunEnd
;
587 extern const char *HookPkgSuccess
;
588 extern const char *HookPkgFailure
;
589 extern const char *HookPkgIgnored
;
590 extern const char *HookPkgSkipped
;
592 void _dfatal(const char *file
, int line
, const char *func
, int do_errno
,
593 const char *fmt
, ...);
594 void _ddprintf(int tab
, const char *fmt
, ...);
595 void _dlog(int which
, const char *fmt
, ...);
596 char *strdup_or_null(char *str
);
597 void dlogreset(void);
599 void addbuildenv(const char *label
, const char *data
, int type
);
600 void delbuildenv(const char *label
);
601 const char *getbuildenv(const char *label
);
602 int readlogline(monitorlog_t
*log
, char **bufp
);
603 uint32_t crcDirTree(const char *path
);
605 void initbulk(void (*func
)(bulk_t
*bulk
), int jobs
);
606 void queuebulk(const char *s1
, const char *s2
, const char *s3
,
608 bulk_t
*getbulk(void);
610 void freebulk(bulk_t
*bulk
);
611 void freestrp(char **strp
);
612 void dupstrp(char **strp
);
613 int askyn(const char *ctl
, ...);
614 double getswappct(int *noswapp
);
615 FILE *dexec_open(const char *logid
, const char **cav
, int cac
,
616 pid_t
*pidp
, buildenv_t
*xenv
,
617 int with_env
, int with_mvars
);
618 int dexec_close(FILE *fp
, pid_t pid
);
619 const char *getphasestr(worker_phase_t phase
);
620 void set_capability_restrictions(void);
622 void ParseConfiguration(int isworker
);
623 pkg_t
*ParsePackageList(int ac
, char **av
, int debugstop
);
624 void FreePackageList(pkg_t
*pkgs
);
625 pkg_t
*GetLocalPackageList(void);
626 pkg_t
*GetFullPackageList(void);
627 pkg_t
*GetPkgPkg(pkg_t
**listp
);
629 void DoConfigure(void);
630 void DoStatus(pkg_t
*pkgs
);
631 void DoBuild(pkg_t
*pkgs
);
632 void DoInitBuild(int slot_override
);
633 void DoCleanBuild(int resetlogs
);
634 void OptimizeEnv(void);
635 void WorkerProcess(int ac
, char **av
);
637 int DoCreateTemplate(int force
);
638 void DoDestroyTemplate(void);
639 void DoWorkerMounts(worker_t
*work
);
640 void DoWorkerUnmounts(worker_t
*work
);
641 void DoRebuildRepo(int ask
);
642 void DoUpgradePkgs(pkg_t
*pkgs
, int ask
);
643 void RemovePackages(pkg_t
*pkgs
);
644 void PurgeDistfiles(pkg_t
*pkgs
);
646 void RunStatsInit(void);
647 void RunStatsDone(void);
648 void RunStatsReset(void);
649 void RunStatsUpdate(worker_t
*work
, const char *portdir
);
650 void RunStatsUpdateTop(int active
);
651 void RunStatsUpdateLogs(void);
652 void RunStatsSync(void);
653 void RunStatsUpdateCompletion(worker_t
*work
, int logid
, pkg_t
*pkg
,
654 const char *reason
, const char *skipbuf
);
656 void setNumaDomain(int slot
);
658 int copyfile(char *src
, char *dst
);
659 int ipcreadmsg(int fd
, wmsg_t
*msg
);
660 int ipcwritemsg(int fd
, wmsg_t
*msg
);
661 extern void MonitorDirective(const char *datfile
, const char *lkfile
);
663 uint32_t iscsi_crc32(const void *buf
, size_t size
);
664 uint32_t iscsi_crc32_ext(const void *buf
, size_t size
, uint32_t ocrc
);