#include "dsynth.h"
static int CheckAddReExec(int lkfd);
static void DoAddReExec(int lkfd, int ac, char **oldav);
static void DoInit(void);
static void usage(int ecode) __dead2;
int ForceOpt;
int OverridePkgDeleteOpt;
int DFlyPortsBaseOpt;
int FetchOnlyOpt;
int YesOpt;
int DebugOpt;
int GlobalExitCode;
int MaskProbeAbort;
int ColorOpt = 1;
int NullStdinOpt = 1;
int SlowStartOpt = -1;
int CapabilityRestrictions;
long PkgDepMemoryTarget;
long PkgDepScaleTarget = 100;
char *DSynthExecPath;
char *ProfileOverrideOpt;
int NiceOpt = 10;
int
main(int ac, char **av)
{
char *lkpath;
pkg_t *pkgs;
int lkfd;
int isworker;
int c;
int sopt;
int doadds;
#if defined(__DragonFly__)
{
size_t len = sizeof(CapabilityRestrictions);
sysctlbyname("kern.caps_available",
&CapabilityRestrictions, &len, NULL, 0);
if (CapabilityRestrictions == 0)
fprintf(stderr, "caps restrictions unavailable\n");
}
#endif
{
size_t len;
const int name[] = {
CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1,
};
if (sysctl(name, 4, NULL, &len, NULL, 0) < 0)
dfatal_errno("Cannot get binary path");
DSynthExecPath = malloc(len + 1);
if (sysctl(name, 4, DSynthExecPath, &len, NULL, 0) < 0)
dfatal_errno("Cannot get binary path");
DSynthExecPath[len] = 0;
}
ProfileOverrideOpt = getenv("DSYNTH_PROFILE");
sopt = 0;
while ((c = getopt(ac, av, "dfghm:p:vxys:C:DPM:NS")) != -1) {
switch(c) {
case 'f':
++ForceOpt;
break;
case 'g':
++DFlyPortsBaseOpt;
WorkerProcFlags |= WORKER_PROC_PORTSBASEOPT;
break;
case 'x':
++OverridePkgDeleteOpt;
break;
case 'y':
++YesOpt;
break;
case 'C':
ConfigBase1 = optarg;
ConfigBase2 = NULL;
break;
case 'D':
WorkerProcFlags |= WORKER_PROC_DEVELOPER;
break;
case 'P':
WorkerProcFlags |= WORKER_PROC_CHECK_PLIST;
break;
case 'S':
UseNCurses = 0;
if (++sopt == 2)
ColorOpt = 0;
break;
case 'N':
NiceOpt = 0;
break;
case 'd':
++DebugOpt;
if (DebugOpt >= 2)
UseNCurses = 0;
break;
case 'h':
usage(0);
exit(0);
case 'v':
printf("dsynth %s\n", DSYNTH_VERSION);
exit(0);
case 's':
SlowStartOpt = strtol(optarg, NULL, 0);
break;
case 'm':
PkgDepMemoryTarget = strtoul(optarg, NULL, 0);
PkgDepMemoryTarget *= ONEGB;
break;
case 'M':
PkgDepScaleTarget = strtod(optarg, NULL) * 100;
if (PkgDepScaleTarget < 1)
PkgDepScaleTarget = 1;
if (PkgDepScaleTarget > 9900)
PkgDepScaleTarget = 9900;
break;
case 'p':
ProfileOverrideOpt = optarg;
break;
default:
fprintf(stderr, "Unknown option: %c\n", c);
usage(2);
break;
}
}
ac -= optind;
av += optind;
pkgs = NULL;
if (ac < 1) {
fprintf(stderr, "Missing directive\n");
usage(2);
}
if (strcmp(av[0], "init") == 0) {
DoInit();
exit(0);
}
if (strcmp(av[0], "help") == 0) {
usage(0);
exit(0);
}
if (strcmp(av[0], "version") == 0) {
printf("dsynth %s\n", DSYNTH_VERSION);
exit(0);
}
if (strcmp(av[0], "WORKER") == 0) {
isworker = 1;
} else {
isworker = 0;
}
signal(SIGPIPE, SIG_IGN);
ParseConfiguration(isworker);
asprintf(&lkpath, "%s/.lock", BuildBase);
addbuildenv("PORTSDIR", DPortsPath,
BENV_ENVIRONMENT | BENV_PKGLIST);
addbuildenv("BATCH", "yes",
BENV_ENVIRONMENT | BENV_PKGLIST);
addbuildenv("PKG_COMPRESSION_FORMAT", UsePkgSufx,
BENV_ENVIRONMENT | BENV_PKGLIST);
addbuildenv("PKG_SUFX", UsePkgSufx,
BENV_ENVIRONMENT | BENV_PKGLIST);
addbuildenv("PACKAGE_BUILDING", "yes",
BENV_ENVIRONMENT | BENV_PKGLIST);
addbuildenv("ARCH", ArchitectureName,
BENV_ENVIRONMENT | BENV_PKGLIST);
#if 0
addbuildenv("OSTYPE", OperatingSystemName,
BENV_ENVIRONMENT | BENV_PKGLIST);
addbuildenv("MACHTYPE", MachineName,
BENV_ENVIRONMENT | BENV_PKGLIST);
#endif
if (SlowStartOpt > MaxWorkers)
SlowStartOpt = MaxWorkers;
if (SlowStartOpt < 0) {
if (MaxWorkers < 16)
SlowStartOpt = 1;
else
SlowStartOpt = MaxWorkers / 4;
}
if (isworker) {
WorkerProcess(ac, av);
exit(0);
}
DoInitBuild(-1);
if (strcmp(av[0], "monitor") == 0) {
char *spath;
char *lpath;
if (ac == 1) {
asprintf(&spath, "%s/%s", StatsBase, STATS_FILE);
asprintf(&lpath, "%s/%s", StatsBase, STATS_LOCKFILE);
MonitorDirective(spath, lpath);
free(spath);
free(lpath);
} else {
MonitorDirective(av[1], NULL);
}
exit(0);
} else if (strcmp(av[0], "add") == 0) {
char *buf;
int fd;
int i;
fd = open(lkpath, O_RDWR | O_CREAT | O_APPEND, 0644);
if (flock(fd, LOCK_EX | LOCK_NB) == 0) {
dfatal("No dsynth running to add ports to");
flock(fd, LOCK_UN);
}
for (i = 1; i < ac; ++i) {
asprintf(&buf, "%s\n", av[i]);
write(fd, buf, strlen(buf));
printf("added to run: %s\n", av[i]);
}
close(fd);
exit(0);
}
lkfd = open(lkpath, O_RDWR | O_CREAT | O_CLOEXEC, 0644);
if (lkfd < 0)
dfatal_errno("Unable to create %s", lkpath);
if (flock(lkfd, LOCK_EX | LOCK_NB) < 0) {
dfatal("Another dsynth is using %s, exiting",
BuildBase);
}
ftruncate(lkfd, 0);
doadds = 0;
if (strcmp(av[0], "debug") == 0) {
DoCleanBuild(1);
OptimizeEnv();
pkgs = ParsePackageList(ac - 1, av + 1, 1);
RemovePackages(pkgs);
DoBuild(pkgs);
doadds = 1;
} else if (strcmp(av[0], "status") == 0) {
OptimizeEnv();
if (ac - 1)
pkgs = ParsePackageList(ac - 1, av + 1, 0);
else
pkgs = GetLocalPackageList();
DoStatus(pkgs);
} else if (strcmp(av[0], "cleanup") == 0) {
DoCleanBuild(0);
} else if (strcmp(av[0], "configure") == 0) {
DoCleanBuild(0);
DoConfigure();
} else if (strcmp(av[0], "fetch-only") == 0) {
if (SlowStartOpt == -1)
SlowStartOpt = 999;
if (PkgDepScaleTarget == 100)
PkgDepScaleTarget = 999;
++FetchOnlyOpt;
++YesOpt;
WorkerProcFlags |= WORKER_PROC_FETCHONLY;
DoCleanBuild(1);
OptimizeEnv();
if (ac == 2 && strcmp(av[1], "everything") == 0) {
MaskProbeAbort = 1;
pkgs = GetFullPackageList();
} else {
pkgs = ParsePackageList(ac - 1, av + 1, 0);
}
DoBuild(pkgs);
doadds = 1;
} else if (strcmp(av[0], "list-only") == 0) {
FILE *fp;
++YesOpt;
DoCleanBuild(1);
OptimizeEnv();
if (ac == 2 && strcmp(av[1], "everything") == 0) {
MaskProbeAbort = 1;
pkgs = GetFullPackageList();
} else {
pkgs = ParsePackageList(ac - 1, av + 1, 0);
}
printf("Writing build.txt file\n");
if ((fp = fopen("build.txt", "w")) != NULL) {
while (pkgs) {
fprintf(fp, "%s\t%s\n",
pkgs->portdir, pkgs->pkgfile);
pkgs = pkgs->bnext;
}
fclose(fp);
}
} else if (strcmp(av[0], "list-system") == 0) {
FILE *fp;
DoCleanBuild(1);
OptimizeEnv();
pkgs = GetLocalPackageList();
if ((fp = fopen("build.txt", "w")) != NULL) {
while (pkgs) {
fprintf(fp, "%s\n", pkgs->portdir);
pkgs = pkgs->bnext;
}
fclose(fp);
printf("list written to build.txt\n");
} else {
fprintf(stderr, "Cannot create 'build.txt'\n");
exit(1);
}
} else if (strcmp(av[0], "upgrade-system") == 0) {
DoCleanBuild(1);
OptimizeEnv();
pkgs = GetLocalPackageList();
DoBuild(pkgs);
DoRebuildRepo(0);
DoUpgradePkgs(pkgs, 0);
dfatal("NOTE: you have to pkg upgrade manually");
} else if (strcmp(av[0], "prepare-system") == 0) {
DeleteObsoletePkgs = 1;
DoCleanBuild(1);
OptimizeEnv();
pkgs = GetLocalPackageList();
DoBuild(pkgs);
DoRebuildRepo(0);
} else if (strcmp(av[0], "rebuild-repository") == 0) {
OptimizeEnv();
DoRebuildRepo(0);
} else if (strcmp(av[0], "purge-distfiles") == 0) {
OptimizeEnv();
pkgs = GetFullPackageList();
PurgeDistfiles(pkgs);
} else if (strcmp(av[0], "reset-db") == 0) {
char *dbmpath;
asprintf(&dbmpath, "%s/ports_crc.db", BuildBase);
remove(dbmpath);
printf("%s reset, will be regenerated on next build\n",
dbmpath);
free(dbmpath);
} else if (strcmp(av[0], "status-everything") == 0) {
OptimizeEnv();
pkgs = GetFullPackageList();
DoStatus(pkgs);
} else if (strcmp(av[0], "everything") == 0) {
if (WorkerProcFlags & WORKER_PROC_DEVELOPER)
WorkerProcFlags |= WORKER_PROC_CHECK_PLIST;
MaskProbeAbort = 1;
DeleteObsoletePkgs = 1;
DoCleanBuild(1);
OptimizeEnv();
pkgs = GetFullPackageList();
DoBuild(pkgs);
DoRebuildRepo(!CheckAddReExec(lkfd));
} else if (strcmp(av[0], "build") == 0) {
DoCleanBuild(1);
OptimizeEnv();
pkgs = ParsePackageList(ac - 1, av + 1, 0);
DoBuild(pkgs);
DoRebuildRepo(!CheckAddReExec(lkfd));
doadds = 1;
} else if (strcmp(av[0], "just-build") == 0) {
DoCleanBuild(1);
OptimizeEnv();
pkgs = ParsePackageList(ac - 1, av + 1, 0);
DoBuild(pkgs);
doadds = 1;
} else if (strcmp(av[0], "install") == 0) {
DoCleanBuild(1);
OptimizeEnv();
pkgs = ParsePackageList(ac - 1, av + 1, 0);
DoBuild(pkgs);
DoRebuildRepo(0);
DoUpgradePkgs(pkgs, 0);
doadds = 1;
} else if (strcmp(av[0], "force") == 0) {
DoCleanBuild(1);
OptimizeEnv();
pkgs = ParsePackageList(ac - 1, av + 1, 0);
RemovePackages(pkgs);
DoBuild(pkgs);
DoRebuildRepo(!CheckAddReExec(lkfd));
doadds = 1;
} else if (strcmp(av[0], "test") == 0) {
WorkerProcFlags |= WORKER_PROC_CHECK_PLIST |
WORKER_PROC_INSTALL |
WORKER_PROC_DEINSTALL;
DoCleanBuild(1);
OptimizeEnv();
pkgs = ParsePackageList(ac - 1, av + 1, 0);
RemovePackages(pkgs);
WorkerProcFlags |= WORKER_PROC_DEVELOPER;
DoBuild(pkgs);
doadds = 1;
} else {
fprintf(stderr, "Unknown directive '%s'\n", av[0]);
usage(2);
}
if (doadds && CheckAddReExec(lkfd))
DoAddReExec(lkfd, optind + 1, av - optind);
return GlobalExitCode;
}
static int
CheckAddReExec(int lkfd)
{
struct stat st;
if (fstat(lkfd, &st) < 0 || st.st_size == 0)
return 0;
return 1;
}
static void
DoAddReExec(int lkfd, int ac, char **oldav)
{
struct stat st;
char *buf;
char **av;
size_t bi;
size_t i;
int nadd;
int n;
if (fstat(lkfd, &st) < 0 || st.st_size == 0)
return;
buf = malloc(st.st_size + 1);
if (read(lkfd, buf, st.st_size) != st.st_size) {
free(buf);
return;
}
buf[st.st_size] = 0;
nadd = 0;
for (i = 0; i < (size_t)st.st_size; ++i) {
if (buf[i] == '\n' || buf[i] == 0) {
buf[i] = 0;
++nadd;
}
}
av = calloc(ac + nadd + 1, sizeof(char *));
for (n = 0; n < ac; ++n)
av[n] = oldav[n];
nadd = 0;
bi = 0;
for (i = 0; i < (size_t)st.st_size; ++i) {
if (buf[i] == 0) {
av[ac + nadd] = buf + bi;
bi = i + 1;
++nadd;
}
}
printf("dsynth re-exec'ing additionally added packages\n");
for (n = 0; n < ac + nadd; ++n)
printf(" %s", av[n]);
printf("\n");
fflush(stdout);
sleep(2);
execv(DSynthExecPath, av);
}
static void
DoInit(void)
{
struct stat st;
char *path;
FILE *fp;
if (stat(ConfigBase1, &st) == 0) {
dfatal("init will not overwrite %s", ConfigBase1);
}
if (ConfigBase2 && stat(ConfigBase2, &st) == 0) {
dfatal("init will not create %s if %s exists",
ConfigBase2, ConfigBase1);
}
if (mkdir(ConfigBase1, 0755) < 0)
dfatal_errno("Unable to mkdir %s", ConfigBase1);
asprintf(&path, "%s/dsynth.ini", ConfigBase1);
fp = fopen(path, "w");
dassert_errno(fp, "Unable to create %s", path);
fprintf(fp, "%s",
"; This Synth configuration file is automatically generated\n"
"; Take care when hand editing!\n"
"\n"
"[Global Configuration]\n"
"profile_selected= LiveSystem\n"
"\n"
"[LiveSystem]\n"
"Operating_system= DragonFly\n"
"Directory_packages= /build/synth/live_packages\n"
"Directory_repository= /build/synth/live_packages/All\n"
"Directory_portsdir= /build/synth/dports\n"
"Directory_options= /build/synth/options\n"
"Directory_distfiles= /build/synth/distfiles\n"
"Directory_buildbase= /build/synth/build\n"
"Directory_logs= /build/synth/logs\n"
"Directory_ccache= disabled\n"
"Directory_system= /\n"
"Package_suffix= .txz\n"
"Number_of_builders= 0\n"
"Max_jobs_per_builder= 0\n"
"Tmpfs_workdir= true\n"
"Tmpfs_localbase= true\n"
"Display_with_ncurses= true\n"
"leverage_prebuilt= false\n"
"; Meta_version= 2\n"
"; Check_plist= false\n"
"; Numa_setsize= 2\n"
"\n");
if (fclose(fp))
dfatal_errno("Unable to write to %s\n", ConfigBase1);
free(path);
asprintf(&path, "%s/LiveSystem-make.conf", ConfigBase1);
fp = fopen(path, "w");
dassert_errno(fp, "Unable to create %s", path);
fprintf(fp, "%s",
"#\n"
"# Various dports options that might be of interest\n"
"#\n"
"#LICENSES_ACCEPTED= NONE\n"
"#DISABLE_LICENSES= yes\n"
"#DEFAULT_VERSIONS= ssl=openssl\n"
"#FORCE_PACKAGE= yes\n"
"#DPORTS_BUILDER= yes\n"
"#\n"
"# Turn these on to generate debug binaries. However, these\n"
"# options will seriously bloat memory use and storage use,\n"
"# do not use lightly\n"
"#\n"
"#STRIP=\n"
"#WITH_DEBUG=yes\n"
);
if (fclose(fp))
dfatal_errno("Unable to write to %s\n", ConfigBase1);
free(path);
}
__dead2 static void
usage(int ecode)
{
if (ecode == 2) {
fprintf(stderr, "Run 'dsynth help' for usage\n");
exit(1);
}
fprintf(stderr,
"dsynth [options] directive\n"
" -d - Debug verbosity (-dd disables ncurses)\n"
" -f - Force (for purge-distfiles)\n"
" -h - Display this screen and exit\n"
" -m gb - Load management based on pkgdep memory\n"
" -p profile - Override profile selected in dsynth.ini\n"
" -s n - Set initial DynamicMaxWorkers\n"
" -v - Print version info and exit\n"
" -x - Do not rebuild packages with dependencies\n"
" which require rebuilding\n"
" -xx - Do not rebuild packages whos dports trees\n"
" change\n"
" -y - Automatically answer yes to dsynth questions\n"
" -C configbase - Config base directory (replaces /etc/dsynth)\n"
" -D - Enable DEVELOPER mode\n"
" -P - Include the check-plist stage\n"
" -S - Disable ncurses\n"
" -N - Do not nice-up sub-processes (else nice +10)\n"
"\n"
" init - Initialize /etc/dsynth\n"
" status - Dry-run of 'upgrade-system'\n"
" cleanup - Clean-up mounts\n"
" configure - Bring up configuration menu\n"
" list-system - Just generate the build list to build.txt\n"
" upgrade-system - Incremental build and upgrade using pkg list\n"
" from local system, then upgrade the local\n"
" system.\n"
" prepare-system - 'upgrade-system' but stops after building\n"
" rebuild-repository - Rebuild database files for current repository\n"
" purge-distfiles - Delete obsolete source distribution files\n"
" reset-db - Delete ports_crc.db, regenerate next build\n"
" status-everything - Dry-run of 'everything'\n"
" everything - Build entire dports tree and repo database\n"
" (-D everything infers -P)\n"
" version - Print version info and exit\n"
" help - Display this screen and exit\n"
" status [ports] - Dry-run of 'build' with given list\n"
" add [ports] - Add listed dports to a new build queue that\n"
" will be built after the current run finishes.\n"
" build [ports] - Incrementally build dports based on the given\n"
" list, but asks before updating the repo\n"
" database and system\n"
" just-build [ports] - 'build' but skips post-build steps\n"
" install [ports] - 'build' but upgrades system without asking\n"
" force [ports] - 'build' but deletes existing packages first\n"
" test [ports] - 'build' w/DEVELOPER=yes and pre-deletes pkgs\n"
" (also infers -P)\n"
" debug [ports] - like 'test' but leaves mounts intact\n"
" fetch-only [ports] - Fetch src dists only ('everything' ok)\n"
" list-only [ports] - Dump deps (port & pkgs) to build.txt ('everything' ok)\n"
" monitor [datfile] - Monitor a running dsynth\n"
"\n"
" [ports] is a space-delimited list of origins, e.g. editors/joe. It\n"
" may also be a path to a file containing one origin per line.\n"
);
exit(ecode);
}