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 51 int 52 main(int ac, char **av) 53 { 54 pkg_t *pkgs; 55 int isworker; 56 int c; 57 int sopt; 58 59 /* 60 * Get our exec path so we can self-exec clean WORKER 61 * processes. 62 */ 63 { 64 size_t len; 65 const int name[] = { 66 CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1, 67 }; 68 if (sysctl(name, 4, NULL, &len, NULL, 0) < 0) 69 dfatal_errno("Cannot get binary path"); 70 DSynthExecPath = malloc(len + 1); 71 if (sysctl(name, 4, DSynthExecPath, &len, NULL, 0) < 0) 72 dfatal_errno("Cannot get binary path"); 73 DSynthExecPath[len] = 0; 74 } 75 76 /* 77 * Process options and make sure the directive is present 78 */ 79 sopt = 0; 80 while ((c = getopt(ac, av, "dhm:vys:DPS")) != -1) { 81 switch(c) { 82 case 'y': 83 ++YesOpt; 84 break; 85 case 'D': 86 WorkerProcFlags |= WORKER_PROC_DEVELOPER; 87 break; 88 case 'P': 89 WorkerProcFlags |= WORKER_PROC_CHECK_PLIST; 90 break; 91 case 'S': 92 UseNCurses = 0; 93 if (++sopt == 2) 94 ColorOpt = 0; 95 break; 96 case 'd': 97 ++DebugOpt; 98 if (DebugOpt >= 2) 99 UseNCurses = 0; 100 break; 101 case 'h': 102 usage(0); 103 /* NOT REACHED */ 104 exit(0); 105 case 'v': 106 printf("dsynth %s\n", DSYNTH_VERSION); 107 exit(0); 108 case 's': 109 SlowStartOpt = strtol(optarg, NULL, 0); 110 break; 111 case 'm': 112 PkgDepMemoryTarget = strtoul(optarg, NULL, 0); 113 PkgDepMemoryTarget *= ONEGB; 114 break; 115 default: 116 fprintf(stderr, "Unknown option: %c\n", c); 117 usage(2); 118 /* NOT REACHED */ 119 break; 120 } 121 } 122 ac -= optind; 123 av += optind; 124 pkgs = NULL; 125 if (ac < 1) { 126 fprintf(stderr, "Missing directive\n"); 127 usage(2); 128 /* NOT REACHED */ 129 } 130 131 if (strcmp(av[0], "init") == 0) { 132 DoInit(); 133 exit(0); 134 /* NOT REACHED */ 135 } 136 if (strcmp(av[0], "help") == 0) { 137 usage(0); 138 exit(0); 139 /* NOT REACHED */ 140 } 141 142 if (strcmp(av[0], "WORKER") == 0) { 143 isworker = 1; 144 } else { 145 isworker = 0; 146 } 147 148 /* 149 * Preconfiguration. 150 */ 151 signal(SIGPIPE, SIG_IGN); 152 ParseConfiguration(isworker); 153 154 /* 155 * Setup some environment for bulk operations (pkglist scan). 156 * These are not used by the builder (the builder will replicate 157 * all of these). 158 */ 159 addbuildenv("PORTSDIR", DPortsPath, 160 BENV_ENVIRONMENT | BENV_PKGLIST); 161 addbuildenv("BATCH", "yes", 162 BENV_ENVIRONMENT | BENV_PKGLIST); 163 addbuildenv("PKG_SUFX", UsePkgSufx, 164 BENV_ENVIRONMENT | BENV_PKGLIST); 165 addbuildenv("PACKAGE_BUILDING", "yes", 166 BENV_ENVIRONMENT | BENV_PKGLIST); 167 addbuildenv("ARCH", ArchitectureName, 168 BENV_ENVIRONMENT | BENV_PKGLIST); 169 170 #if 0 171 /* 172 * 173 */ 174 addbuildenv("OSTYPE", OperatingSystemName, 175 BENV_ENVIRONMENT | BENV_PKGLIST); 176 addbuildenv("MACHTYPE", MachineName, 177 BENV_ENVIRONMENT | BENV_PKGLIST); 178 #endif 179 180 /* 181 * Special directive for when dsynth execs itself to manage 182 * a worker chroot. 183 */ 184 if (isworker) { 185 WorkerProcess(ac, av); 186 exit(0); 187 } 188 189 DoInitBuild(-1); 190 191 if (strcmp(av[0], "debug") == 0) { 192 DoCleanBuild(1); 193 OptimizeEnv(); 194 pkgs = ParsePackageList(ac - 1, av + 1, 1); 195 RemovePackages(pkgs); 196 DoBuild(pkgs); 197 } else if (strcmp(av[0], "status") == 0) { 198 OptimizeEnv(); 199 if (ac - 1) 200 pkgs = ParsePackageList(ac - 1, av + 1, 0); 201 else 202 pkgs = GetLocalPackageList(); 203 DoStatus(pkgs); 204 } else if (strcmp(av[0], "monitor") == 0) { 205 char *spath; 206 char *lpath; 207 208 if (ac == 1) { 209 asprintf(&spath, "%s/%s", StatsBase, STATS_FILE); 210 asprintf(&lpath, "%s/%s", StatsBase, STATS_LOCKFILE); 211 MonitorDirective(spath, lpath); 212 free(spath); 213 free(lpath); 214 } else { 215 MonitorDirective(av[1], NULL); 216 } 217 } else if (strcmp(av[0], "cleanup") == 0) { 218 DoCleanBuild(0); 219 } else if (strcmp(av[0], "configure") == 0) { 220 DoCleanBuild(0); 221 DoConfigure(); 222 } else if (strcmp(av[0], "upgrade-system") == 0) { 223 DoCleanBuild(1); 224 OptimizeEnv(); 225 pkgs = GetLocalPackageList(); 226 DoBuild(pkgs); 227 DoRebuildRepo(0); 228 DoUpgradePkgs(pkgs, 0); 229 } else if (strcmp(av[0], "prepare-system") == 0) { 230 DoCleanBuild(1); 231 OptimizeEnv(); 232 pkgs = GetLocalPackageList(); 233 DoBuild(pkgs); 234 DoRebuildRepo(0); 235 } else if (strcmp(av[0], "rebuild-repository") == 0) { 236 OptimizeEnv(); 237 DoRebuildRepo(0); 238 } else if (strcmp(av[0], "purge-distfiles") == 0) { 239 OptimizeEnv(); 240 pkgs = GetFullPackageList(); 241 PurgeDistfiles(pkgs); 242 } else if (strcmp(av[0], "status-everything") == 0) { 243 OptimizeEnv(); 244 pkgs = GetFullPackageList(); 245 DoStatus(pkgs); 246 } else if (strcmp(av[0], "everything") == 0) { 247 if (WorkerProcFlags & WORKER_PROC_DEVELOPER) 248 WorkerProcFlags |= WORKER_PROC_CHECK_PLIST; 249 DoCleanBuild(1); 250 OptimizeEnv(); 251 pkgs = GetFullPackageList(); 252 DoBuild(pkgs); 253 DoRebuildRepo(1); 254 } else if (strcmp(av[0], "version") == 0) { 255 printf("dsynth %s\n", DSYNTH_VERSION); 256 exit(0); 257 } else if (strcmp(av[0], "build") == 0) { 258 DoCleanBuild(1); 259 OptimizeEnv(); 260 pkgs = ParsePackageList(ac - 1, av + 1, 0); 261 DoBuild(pkgs); 262 DoRebuildRepo(1); 263 DoUpgradePkgs(pkgs, 1); 264 } else if (strcmp(av[0], "just-build") == 0) { 265 DoCleanBuild(1); 266 OptimizeEnv(); 267 pkgs = ParsePackageList(ac - 1, av + 1, 0); 268 DoBuild(pkgs); 269 } else if (strcmp(av[0], "install") == 0) { 270 DoCleanBuild(1); 271 OptimizeEnv(); 272 pkgs = ParsePackageList(ac - 1, av + 1, 0); 273 DoBuild(pkgs); 274 DoRebuildRepo(0); 275 DoUpgradePkgs(pkgs, 0); 276 } else if (strcmp(av[0], "force") == 0) { 277 DoCleanBuild(1); 278 OptimizeEnv(); 279 pkgs = ParsePackageList(ac - 1, av + 1, 0); 280 RemovePackages(pkgs); 281 DoBuild(pkgs); 282 DoRebuildRepo(1); 283 DoUpgradePkgs(pkgs, 1); 284 } else if (strcmp(av[0], "test") == 0) { 285 WorkerProcFlags |= WORKER_PROC_CHECK_PLIST; 286 DoCleanBuild(1); 287 OptimizeEnv(); 288 pkgs = ParsePackageList(ac - 1, av + 1, 0); 289 RemovePackages(pkgs); 290 WorkerProcFlags |= WORKER_PROC_DEVELOPER; 291 DoBuild(pkgs); 292 } else { 293 fprintf(stderr, "Unknown directive '%s'\n", av[0]); 294 usage(2); 295 } 296 297 return 0; 298 } 299 300 static void 301 DoInit(void) 302 { 303 struct stat st; 304 char *path; 305 FILE *fp; 306 307 if (stat(ConfigBase1, &st) == 0) { 308 dfatal("init will not overwrite %s", ConfigBase1); 309 } 310 if (stat(ConfigBase2, &st) == 0) { 311 dfatal("init will not create %s if %s exists", 312 ConfigBase2, ConfigBase1); 313 } 314 if (mkdir(ConfigBase1, 0755) < 0) 315 dfatal_errno("Unable to mkdir %s", ConfigBase1); 316 317 asprintf(&path, "%s/dsynth.ini", ConfigBase1); 318 fp = fopen(path, "w"); 319 dassert_errno(fp, "Unable to create %s", path); 320 fprintf(fp, "%s", 321 "; This Synth configuration file is automatically generated\n" 322 "; Take care when hand editing!\n" 323 "\n" 324 "[Global Configuration]\n" 325 "profile_selected= LiveSystem\n" 326 "\n" 327 "[LiveSystem]\n" 328 "Operating_system= DragonFly\n" 329 "Directory_packages= /build/synth/live_packages\n" 330 "Directory_repository= /build/synth/live_packages/All\n" 331 "Directory_portsdir= /build/synth/dports\n" 332 "Directory_options= /build/synth/options\n" 333 "Directory_distfiles= /build/synth/distfiles\n" 334 "Directory_buildbase= /build/synth/build\n" 335 "Directory_logs= /build/synth/logs\n" 336 "Directory_ccache= disabled\n" 337 "Directory_system= /\n" 338 "Package_suffix= .txz\n" 339 "Number_of_builders= 0\n" 340 "Max_jobs_per_builder= 0\n" 341 "Tmpfs_workdir= true\n" 342 "Tmpfs_localbase= true\n" 343 "Display_with_ncurses= true\n" 344 "leverage_prebuilt= false\n" 345 "\n"); 346 if (fclose(fp)) 347 dfatal_errno("Unable to write to %s\n", ConfigBase1); 348 free(path); 349 350 asprintf(&path, "%s/LiveSystem-make.conf", ConfigBase1); 351 fp = fopen(path, "w"); 352 dassert_errno(fp, "Unable to create %s", path); 353 fprintf(fp, "%s", 354 "#\n" 355 "# Various dports options that might be of interest\n" 356 "#\n" 357 "#LICENSES_ACCEPTED= NONE\n" 358 "#DISABLE_LICENSES= yes\n" 359 "#DEFAULT_VERSIONS= ssl=openssl\n" 360 "#FORCE_PACKAGE= yes\n" 361 "#DPORTS_BUILDER= yes\n" 362 "#\n" 363 "# Turn these on to generate debug binaries. However, these\n" 364 "# options will seriously bloat memory use and storage use,\n" 365 "# do not use lightly\n" 366 "#\n" 367 "#STRIP=\n" 368 "#WITH_DEBUG=yes\n" 369 ); 370 if (fclose(fp)) 371 dfatal_errno("Unable to write to %s\n", ConfigBase1); 372 free(path); 373 } 374 375 __dead2 static void 376 usage(int ecode) 377 { 378 if (ecode == 2) { 379 fprintf(stderr, "Run 'dsynth help' for usage\n"); 380 exit(1); 381 } 382 383 fprintf(stderr, 384 "dsynth [options] directive\n" 385 " -d - Debug verbosity (-dd disables ncurses)\n" 386 " -h - Display this screen and exit\n" 387 " -m gb - Load management based on pkgdep memory\n" 388 " -v - Print version info and exit\n" 389 " -y - Automatically answer yes to dsynth questions\n" 390 " -s n - Set initial DynamicMaxWorkers\n" 391 " -D - Enable DEVELOPER mode\n" 392 " -P - Include the check-plist stage\n" 393 " -S - Disable ncurses\n" 394 "\n" 395 " init - Initialize /etc/dsynth\n" 396 " status - Dry-run of 'upgrade-system'\n" 397 " cleanup - Clean-up mounts\n" 398 " configure - Bring up configuration menu\n" 399 " upgrade-system - Incremental build and upgrade using pkg list\n" 400 " from local system, then upgrade the local\n" 401 " system.\n" 402 " prepare-system - 'upgrade-system' but stops after building\n" 403 " rebuild-repository - Rebuild database files for current repository\n" 404 " purge-distfiles - Delete obsolete source distribution files\n" 405 " status-everything - Dry-run of 'everything'\n" 406 " everything - Build entire dports tree and repo database\n" 407 " (-D everything infers -P)\n" 408 " version - Print version info and exit\n" 409 " help - Display this screen and exit\n" 410 " status [ports] - Dry-run of 'build' with given list\n" 411 " build [ports] - Incrementally build dports based on the given\n" 412 " list, but asks before updating the repo\n" 413 " database and system\n" 414 " just-build [ports] - 'build' but skips post-build steps\n" 415 " install [ports] - 'build' but upgrades system without asking\n" 416 " force [ports] - 'build' but deletes existing packages first\n" 417 " test [ports] - 'build' w/DEVELOPER=yes and pre-deletes pkgs\n" 418 " (also infers -P)\n" 419 " debug [ports] - like 'test' but leaves mounts intact\n" 420 " monitor [datfile] - Monitor a running dsynth\n" 421 "\n" 422 " [ports] is a space-delimited list of origins, e.g. editors/joe. It\n" 423 " may also be a path to a file containing one origin per line.\n" 424 ); 425 426 exit(ecode); 427 } 428