1 /* 2 * Copyright (c) 2019 The DragonFly Project. All rights reserved. 3 * 4 * This code is derived from software contributed to The DragonFly Project 5 * by Matthew Dillon <dillon@backplane.com> 6 * 7 * This code uses concepts and configuration based on 'synth', by 8 * John R. Marino <draco@marino.st>, which was written in ada. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in 18 * the documentation and/or other materials provided with the 19 * distribution. 20 * 3. Neither the name of The DragonFly Project nor the names of its 21 * contributors may be used to endorse or promote products derived 22 * from this software without specific, prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 25 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 28 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 29 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 32 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 33 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 34 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 */ 37 38 #include "dsynth.h" 39 40 static void DoInit(void); 41 static void usage(int ecode) __dead2; 42 43 int YesOpt; 44 int DebugOpt; 45 int ColorOpt = 1; 46 int NullStdinOpt = 1; 47 int SlowStartOpt = 1; 48 long PkgDepMemoryTarget; 49 char *DSynthExecPath; 50 char *ProfileOverrideOpt; 51 52 int 53 main(int ac, char **av) 54 { 55 pkg_t *pkgs; 56 int isworker; 57 int c; 58 int sopt; 59 60 /* 61 * Get our exec path so we can self-exec clean WORKER 62 * processes. 63 */ 64 { 65 size_t len; 66 const int name[] = { 67 CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1, 68 }; 69 if (sysctl(name, 4, NULL, &len, NULL, 0) < 0) 70 dfatal_errno("Cannot get binary path"); 71 DSynthExecPath = malloc(len + 1); 72 if (sysctl(name, 4, DSynthExecPath, &len, NULL, 0) < 0) 73 dfatal_errno("Cannot get binary path"); 74 DSynthExecPath[len] = 0; 75 } 76 77 /* 78 * Override profile in dsynth.ini (can be further overridden 79 * with the -p profile option). 80 */ 81 ProfileOverrideOpt = getenv("DSYNTH_PROFILE"); 82 83 /* 84 * Process options and make sure the directive is present 85 */ 86 sopt = 0; 87 while ((c = getopt(ac, av, "dhm:p:vys:DPS")) != -1) { 88 switch(c) { 89 case 'y': 90 ++YesOpt; 91 break; 92 case 'D': 93 WorkerProcFlags |= WORKER_PROC_DEVELOPER; 94 break; 95 case 'P': 96 WorkerProcFlags |= WORKER_PROC_CHECK_PLIST; 97 break; 98 case 'S': 99 UseNCurses = 0; 100 if (++sopt == 2) 101 ColorOpt = 0; 102 break; 103 case 'd': 104 ++DebugOpt; 105 if (DebugOpt >= 2) 106 UseNCurses = 0; 107 break; 108 case 'h': 109 usage(0); 110 /* NOT REACHED */ 111 exit(0); 112 case 'v': 113 printf("dsynth %s\n", DSYNTH_VERSION); 114 exit(0); 115 case 's': 116 SlowStartOpt = strtol(optarg, NULL, 0); 117 break; 118 case 'm': 119 PkgDepMemoryTarget = strtoul(optarg, NULL, 0); 120 PkgDepMemoryTarget *= ONEGB; 121 break; 122 case 'p': 123 ProfileOverrideOpt = optarg; 124 break; 125 default: 126 fprintf(stderr, "Unknown option: %c\n", c); 127 usage(2); 128 /* NOT REACHED */ 129 break; 130 } 131 } 132 ac -= optind; 133 av += optind; 134 pkgs = NULL; 135 if (ac < 1) { 136 fprintf(stderr, "Missing directive\n"); 137 usage(2); 138 /* NOT REACHED */ 139 } 140 141 /* 142 * Directives which do not require a working configuration 143 */ 144 if (strcmp(av[0], "init") == 0) { 145 DoInit(); 146 exit(0); 147 /* NOT REACHED */ 148 } 149 if (strcmp(av[0], "help") == 0) { 150 usage(0); 151 exit(0); 152 /* NOT REACHED */ 153 } 154 if (strcmp(av[0], "version") == 0) { 155 printf("dsynth %s\n", DSYNTH_VERSION); 156 exit(0); 157 /* NOT REACHED */ 158 } 159 160 /* 161 * Preconfiguration. 162 */ 163 if (strcmp(av[0], "WORKER") == 0) { 164 isworker = 1; 165 } else { 166 isworker = 0; 167 } 168 169 signal(SIGPIPE, SIG_IGN); 170 ParseConfiguration(isworker); 171 172 /* 173 * Setup some environment for bulk operations (pkglist scan). 174 * These are not used by the builder (the builder will replicate 175 * all of these). 176 */ 177 addbuildenv("PORTSDIR", DPortsPath, 178 BENV_ENVIRONMENT | BENV_PKGLIST); 179 addbuildenv("BATCH", "yes", 180 BENV_ENVIRONMENT | BENV_PKGLIST); 181 addbuildenv("PKG_SUFX", UsePkgSufx, 182 BENV_ENVIRONMENT | BENV_PKGLIST); 183 addbuildenv("PACKAGE_BUILDING", "yes", 184 BENV_ENVIRONMENT | BENV_PKGLIST); 185 addbuildenv("ARCH", ArchitectureName, 186 BENV_ENVIRONMENT | BENV_PKGLIST); 187 188 #if 0 189 /* 190 * 191 */ 192 addbuildenv("OSTYPE", OperatingSystemName, 193 BENV_ENVIRONMENT | BENV_PKGLIST); 194 addbuildenv("MACHTYPE", MachineName, 195 BENV_ENVIRONMENT | BENV_PKGLIST); 196 #endif 197 198 /* 199 * Special directive for when dsynth execs itself to manage 200 * a worker chroot. 201 */ 202 if (isworker) { 203 WorkerProcess(ac, av); 204 exit(0); 205 } 206 207 /* 208 * Build initialization and directive handling 209 */ 210 DoInitBuild(-1); 211 212 /* 213 * Directives that use the configuration but are not interlocked 214 * against a running dsynth. 215 */ 216 if (strcmp(av[0], "monitor") == 0) { 217 char *spath; 218 char *lpath; 219 220 if (ac == 1) { 221 asprintf(&spath, "%s/%s", StatsBase, STATS_FILE); 222 asprintf(&lpath, "%s/%s", StatsBase, STATS_LOCKFILE); 223 MonitorDirective(spath, lpath); 224 free(spath); 225 free(lpath); 226 } else { 227 MonitorDirective(av[1], NULL); 228 } 229 exit(0); 230 /* NOT REACHED */ 231 } 232 233 /* 234 * Front-end exec (not a WORKER exec), normal startup. We have 235 * the configuration so the first thing we need to do is check 236 * the lock file. 237 */ 238 { 239 char *lkpath; 240 int fd; 241 242 asprintf(&lkpath, "%s/.lock", BuildBase); 243 fd = open(lkpath, O_RDWR | O_CREAT | O_CLOEXEC, 0644); 244 if (fd < 0) 245 dfatal_errno("Unable to create %s", lkpath); 246 if (flock(fd, LOCK_EX | LOCK_NB) < 0) { 247 dfatal("Another dsynth is using %s, exiting", 248 BuildBase); 249 } 250 /* leave descriptor open */ 251 } 252 253 if (strcmp(av[0], "debug") == 0) { 254 DoCleanBuild(1); 255 OptimizeEnv(); 256 pkgs = ParsePackageList(ac - 1, av + 1, 1); 257 RemovePackages(pkgs); 258 DoBuild(pkgs); 259 } else if (strcmp(av[0], "status") == 0) { 260 OptimizeEnv(); 261 if (ac - 1) 262 pkgs = ParsePackageList(ac - 1, av + 1, 0); 263 else 264 pkgs = GetLocalPackageList(); 265 DoStatus(pkgs); 266 } else if (strcmp(av[0], "cleanup") == 0) { 267 DoCleanBuild(0); 268 } else if (strcmp(av[0], "configure") == 0) { 269 DoCleanBuild(0); 270 DoConfigure(); 271 } else if (strcmp(av[0], "upgrade-system") == 0) { 272 DoCleanBuild(1); 273 OptimizeEnv(); 274 pkgs = GetLocalPackageList(); 275 DoBuild(pkgs); 276 DoRebuildRepo(0); 277 DoUpgradePkgs(pkgs, 0); 278 } else if (strcmp(av[0], "prepare-system") == 0) { 279 DoCleanBuild(1); 280 OptimizeEnv(); 281 pkgs = GetLocalPackageList(); 282 DoBuild(pkgs); 283 DoRebuildRepo(0); 284 } else if (strcmp(av[0], "rebuild-repository") == 0) { 285 OptimizeEnv(); 286 DoRebuildRepo(0); 287 } else if (strcmp(av[0], "purge-distfiles") == 0) { 288 OptimizeEnv(); 289 pkgs = GetFullPackageList(); 290 PurgeDistfiles(pkgs); 291 } else if (strcmp(av[0], "status-everything") == 0) { 292 OptimizeEnv(); 293 pkgs = GetFullPackageList(); 294 DoStatus(pkgs); 295 } else if (strcmp(av[0], "everything") == 0) { 296 if (WorkerProcFlags & WORKER_PROC_DEVELOPER) 297 WorkerProcFlags |= WORKER_PROC_CHECK_PLIST; 298 DoCleanBuild(1); 299 OptimizeEnv(); 300 pkgs = GetFullPackageList(); 301 DoBuild(pkgs); 302 DoRebuildRepo(1); 303 } else if (strcmp(av[0], "build") == 0) { 304 DoCleanBuild(1); 305 OptimizeEnv(); 306 pkgs = ParsePackageList(ac - 1, av + 1, 0); 307 DoBuild(pkgs); 308 DoRebuildRepo(1); 309 DoUpgradePkgs(pkgs, 1); 310 } else if (strcmp(av[0], "just-build") == 0) { 311 DoCleanBuild(1); 312 OptimizeEnv(); 313 pkgs = ParsePackageList(ac - 1, av + 1, 0); 314 DoBuild(pkgs); 315 } else if (strcmp(av[0], "install") == 0) { 316 DoCleanBuild(1); 317 OptimizeEnv(); 318 pkgs = ParsePackageList(ac - 1, av + 1, 0); 319 DoBuild(pkgs); 320 DoRebuildRepo(0); 321 DoUpgradePkgs(pkgs, 0); 322 } else if (strcmp(av[0], "force") == 0) { 323 DoCleanBuild(1); 324 OptimizeEnv(); 325 pkgs = ParsePackageList(ac - 1, av + 1, 0); 326 RemovePackages(pkgs); 327 DoBuild(pkgs); 328 DoRebuildRepo(1); 329 DoUpgradePkgs(pkgs, 1); 330 } else if (strcmp(av[0], "test") == 0) { 331 WorkerProcFlags |= WORKER_PROC_CHECK_PLIST; 332 DoCleanBuild(1); 333 OptimizeEnv(); 334 pkgs = ParsePackageList(ac - 1, av + 1, 0); 335 RemovePackages(pkgs); 336 WorkerProcFlags |= WORKER_PROC_DEVELOPER; 337 DoBuild(pkgs); 338 } else { 339 fprintf(stderr, "Unknown directive '%s'\n", av[0]); 340 usage(2); 341 } 342 return 0; 343 } 344 345 static void 346 DoInit(void) 347 { 348 struct stat st; 349 char *path; 350 FILE *fp; 351 352 if (stat(ConfigBase1, &st) == 0) { 353 dfatal("init will not overwrite %s", ConfigBase1); 354 } 355 if (stat(ConfigBase2, &st) == 0) { 356 dfatal("init will not create %s if %s exists", 357 ConfigBase2, ConfigBase1); 358 } 359 if (mkdir(ConfigBase1, 0755) < 0) 360 dfatal_errno("Unable to mkdir %s", ConfigBase1); 361 362 asprintf(&path, "%s/dsynth.ini", ConfigBase1); 363 fp = fopen(path, "w"); 364 dassert_errno(fp, "Unable to create %s", path); 365 fprintf(fp, "%s", 366 "; This Synth configuration file is automatically generated\n" 367 "; Take care when hand editing!\n" 368 "\n" 369 "[Global Configuration]\n" 370 "profile_selected= LiveSystem\n" 371 "\n" 372 "[LiveSystem]\n" 373 "Operating_system= DragonFly\n" 374 "Directory_packages= /build/synth/live_packages\n" 375 "Directory_repository= /build/synth/live_packages/All\n" 376 "Directory_portsdir= /build/synth/dports\n" 377 "Directory_options= /build/synth/options\n" 378 "Directory_distfiles= /build/synth/distfiles\n" 379 "Directory_buildbase= /build/synth/build\n" 380 "Directory_logs= /build/synth/logs\n" 381 "Directory_ccache= disabled\n" 382 "Directory_system= /\n" 383 "Package_suffix= .txz\n" 384 "Number_of_builders= 0\n" 385 "Max_jobs_per_builder= 0\n" 386 "Tmpfs_workdir= true\n" 387 "Tmpfs_localbase= true\n" 388 "Display_with_ncurses= true\n" 389 "leverage_prebuilt= false\n" 390 "\n"); 391 if (fclose(fp)) 392 dfatal_errno("Unable to write to %s\n", ConfigBase1); 393 free(path); 394 395 asprintf(&path, "%s/LiveSystem-make.conf", ConfigBase1); 396 fp = fopen(path, "w"); 397 dassert_errno(fp, "Unable to create %s", path); 398 fprintf(fp, "%s", 399 "#\n" 400 "# Various dports options that might be of interest\n" 401 "#\n" 402 "#LICENSES_ACCEPTED= NONE\n" 403 "#DISABLE_LICENSES= yes\n" 404 "#DEFAULT_VERSIONS= ssl=openssl\n" 405 "#FORCE_PACKAGE= yes\n" 406 "#DPORTS_BUILDER= yes\n" 407 "#\n" 408 "# Turn these on to generate debug binaries. However, these\n" 409 "# options will seriously bloat memory use and storage use,\n" 410 "# do not use lightly\n" 411 "#\n" 412 "#STRIP=\n" 413 "#WITH_DEBUG=yes\n" 414 ); 415 if (fclose(fp)) 416 dfatal_errno("Unable to write to %s\n", ConfigBase1); 417 free(path); 418 } 419 420 __dead2 static void 421 usage(int ecode) 422 { 423 if (ecode == 2) { 424 fprintf(stderr, "Run 'dsynth help' for usage\n"); 425 exit(1); 426 } 427 428 fprintf(stderr, 429 "dsynth [options] directive\n" 430 " -d - Debug verbosity (-dd disables ncurses)\n" 431 " -h - Display this screen and exit\n" 432 " -m gb - Load management based on pkgdep memory\n" 433 " -p profile - Override profile selected in dsynth.ini\n" 434 " -s n - Set initial DynamicMaxWorkers\n" 435 " -v - Print version info and exit\n" 436 " -y - Automatically answer yes to dsynth questions\n" 437 " -D - Enable DEVELOPER mode\n" 438 " -P - Include the check-plist stage\n" 439 " -S - Disable ncurses\n" 440 "\n" 441 " init - Initialize /etc/dsynth\n" 442 " status - Dry-run of 'upgrade-system'\n" 443 " cleanup - Clean-up mounts\n" 444 " configure - Bring up configuration menu\n" 445 " upgrade-system - Incremental build and upgrade using pkg list\n" 446 " from local system, then upgrade the local\n" 447 " system.\n" 448 " prepare-system - 'upgrade-system' but stops after building\n" 449 " rebuild-repository - Rebuild database files for current repository\n" 450 " purge-distfiles - Delete obsolete source distribution files\n" 451 " status-everything - Dry-run of 'everything'\n" 452 " everything - Build entire dports tree and repo database\n" 453 " (-D everything infers -P)\n" 454 " version - Print version info and exit\n" 455 " help - Display this screen and exit\n" 456 " status [ports] - Dry-run of 'build' with given list\n" 457 " build [ports] - Incrementally build dports based on the given\n" 458 " list, but asks before updating the repo\n" 459 " database and system\n" 460 " just-build [ports] - 'build' but skips post-build steps\n" 461 " install [ports] - 'build' but upgrades system without asking\n" 462 " force [ports] - 'build' but deletes existing packages first\n" 463 " test [ports] - 'build' w/DEVELOPER=yes and pre-deletes pkgs\n" 464 " (also infers -P)\n" 465 " debug [ports] - like 'test' but leaves mounts intact\n" 466 " monitor [datfile] - Monitor a running dsynth\n" 467 "\n" 468 " [ports] is a space-delimited list of origins, e.g. editors/joe. It\n" 469 " may also be a path to a file containing one origin per line.\n" 470 ); 471 472 exit(ecode); 473 } 474