xref: /dragonfly/usr.bin/dsynth/dsynth.c (revision 03517d4e)
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 int CheckAddReExec(int lkfd);
41 static void DoAddReExec(int lkfd, int ac, char **oldav);
42 static void DoInit(void);
43 static void usage(int ecode) __dead2;
44 
45 int ForceOpt;
46 int OverridePkgDeleteOpt;
47 int FetchOnlyOpt;
48 int YesOpt;
49 int DebugOpt;
50 int MaskProbeAbort;
51 int ColorOpt = 1;
52 int NullStdinOpt = 1;
53 int SlowStartOpt = -1;
54 int CapabilityRestrictions;
55 long PkgDepMemoryTarget;
56 long PkgDepScaleTarget = 100;	/* 1.00 */
57 char *DSynthExecPath;
58 char *ProfileOverrideOpt;
59 int NiceOpt = 10;
60 
61 int
62 main(int ac, char **av)
63 {
64 	char *lkpath;
65 	pkg_t *pkgs;
66 	int lkfd;
67 	int isworker;
68 	int c;
69 	int sopt;
70 	int doadds;
71 
72 #if defined(__DragonFly__)
73 	/*
74 	 * The system is expected to have capabilities
75 	 */
76 	{
77 		size_t len = sizeof(CapabilityRestrictions);
78 		sysctlbyname("kern.caps_available",
79 			     &CapabilityRestrictions, &len, NULL, 0);
80 		if (CapabilityRestrictions == 0)
81 			fprintf(stderr, "caps restrictions unavailable\n");
82 	}
83 #endif
84 
85 	/*
86 	 * Get our exec path so we can self-exec clean WORKER
87 	 * processes.
88 	 */
89 	{
90 		size_t len;
91 		const int name[] = {
92 			CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1,
93 		};
94 		if (sysctl(name, 4, NULL, &len, NULL, 0) < 0)
95 			dfatal_errno("Cannot get binary path");
96 		DSynthExecPath = malloc(len + 1);
97 		if (sysctl(name, 4, DSynthExecPath, &len, NULL, 0) < 0)
98 			dfatal_errno("Cannot get binary path");
99 		DSynthExecPath[len] = 0;
100 	}
101 
102 	/*
103 	 * Override profile in dsynth.ini (can be further overridden
104 	 * with the -p profile option).
105 	 */
106 	ProfileOverrideOpt = getenv("DSYNTH_PROFILE");
107 
108 	/*
109 	 * Process options and make sure the directive is present
110 	 */
111 	sopt = 0;
112 	while ((c = getopt(ac, av, "dfhm:p:vxys:C:DPM:NS")) != -1) {
113 		switch(c) {
114 		case 'f':
115 			++ForceOpt;
116 			break;
117 		case 'x':
118 			++OverridePkgDeleteOpt;
119 			break;
120 		case 'y':
121 			++YesOpt;
122 			break;
123 		case 'C':
124 			ConfigBase1 = optarg;
125 			ConfigBase2 = NULL;
126 			break;
127 		case 'D':
128 			WorkerProcFlags |= WORKER_PROC_DEVELOPER;
129 			break;
130 		case 'P':
131 			WorkerProcFlags |= WORKER_PROC_CHECK_PLIST;
132 			break;
133 		case 'S':
134 			UseNCurses = 0;
135 			if (++sopt == 2)
136 				ColorOpt = 0;
137 			break;
138 		case 'N':
139 			NiceOpt = 0;
140 			break;
141 		case 'd':
142 			++DebugOpt;
143 			if (DebugOpt >= 2)
144 				UseNCurses = 0;
145 			break;
146 		case 'h':
147 			usage(0);
148 			/* NOT REACHED */
149 			exit(0);
150 		case 'v':
151 			printf("dsynth %s\n", DSYNTH_VERSION);
152 			exit(0);
153 		case 's':
154 			/*
155 			 * Start with N jobs, increasing to the configured
156 			 * maximum slowly.  0 to disable (starts with the
157 			 * full count).
158 			 */
159 			SlowStartOpt = strtol(optarg, NULL, 0);
160 			break;
161 		case 'm':
162 			PkgDepMemoryTarget = strtoul(optarg, NULL, 0);
163 			PkgDepMemoryTarget *= ONEGB;
164 			break;
165 		case 'M':
166 			PkgDepScaleTarget = strtod(optarg, NULL) * 100;
167 			if (PkgDepScaleTarget < 1)
168 				PkgDepScaleTarget = 1;
169 			if (PkgDepScaleTarget > 9900)
170 				PkgDepScaleTarget = 9900;
171 			break;
172 		case 'p':
173 			ProfileOverrideOpt = optarg;
174 			break;
175 		default:
176 			fprintf(stderr, "Unknown option: %c\n", c);
177 			usage(2);
178 			/* NOT REACHED */
179 			break;
180 		}
181 	}
182 	ac -= optind;
183 	av += optind;
184 	pkgs = NULL;
185 	if (ac < 1) {
186 		fprintf(stderr, "Missing directive\n");
187 		usage(2);
188 		/* NOT REACHED */
189 	}
190 
191 	/*
192 	 * Directives which do not require a working configuration
193 	 */
194 	if (strcmp(av[0], "init") == 0) {
195 		DoInit();
196 		exit(0);
197 		/* NOT REACHED */
198 	}
199 	if (strcmp(av[0], "help") == 0) {
200 		usage(0);
201 		exit(0);
202 		/* NOT REACHED */
203 	}
204 	if (strcmp(av[0], "version") == 0) {
205 		printf("dsynth %s\n", DSYNTH_VERSION);
206 		exit(0);
207 		/* NOT REACHED */
208 	}
209 
210 	/*
211 	 * Preconfiguration.
212 	 */
213 	if (strcmp(av[0], "WORKER") == 0) {
214 		isworker = 1;
215 	} else {
216 		isworker = 0;
217 	}
218 
219 	signal(SIGPIPE, SIG_IGN);
220 	ParseConfiguration(isworker);
221 
222 	/*
223 	 * Lock file path (also contains any 'add' directives thrown in
224 	 * during a build).
225 	 */
226 	asprintf(&lkpath, "%s/.lock", BuildBase);
227 
228 	/*
229 	 * Setup some environment for bulk operations (pkglist scan).
230 	 * These are not used by the builder (the builder will replicate
231 	 * all of these).
232 	 *
233 	 * NOTE: PKG_SUFX - pkg versions older than 1.17
234 	 *	 PKG_COMPRESSION_FORMAT - pkg versions >= 1.17
235 	 */
236 	addbuildenv("PORTSDIR", DPortsPath,
237 		    BENV_ENVIRONMENT | BENV_PKGLIST);
238 	addbuildenv("BATCH", "yes",
239 		    BENV_ENVIRONMENT | BENV_PKGLIST);
240 	addbuildenv("PKG_COMPRESSION_FORMAT", UsePkgSufx,
241 		    BENV_ENVIRONMENT | BENV_PKGLIST);
242 	addbuildenv("PKG_SUFX", UsePkgSufx,
243 		    BENV_ENVIRONMENT | BENV_PKGLIST);
244 	addbuildenv("PACKAGE_BUILDING", "yes",
245 		    BENV_ENVIRONMENT | BENV_PKGLIST);
246 	addbuildenv("ARCH", ArchitectureName,
247 		    BENV_ENVIRONMENT | BENV_PKGLIST);
248 
249 #if 0
250 	/*
251 	 *
252 	 */
253 	addbuildenv("OSTYPE", OperatingSystemName,
254 		    BENV_ENVIRONMENT | BENV_PKGLIST);
255 	addbuildenv("MACHTYPE", MachineName,
256 		    BENV_ENVIRONMENT | BENV_PKGLIST);
257 #endif
258 	/*
259 	 * SlowStart auto adjust.  We nominally start with 1 job and increase
260 	 * it to the maximum every 5 seconds to give various dynamic management
261 	 * parameters time to stabilize.
262 	 *
263 	 * This can take a while on a many-core box with a high jobs setting,
264 	 * so increase the initial jobs in such cases.
265 	 */
266 	if (SlowStartOpt > MaxWorkers)
267 		SlowStartOpt = MaxWorkers;
268 	if (SlowStartOpt < 0) {
269 		if (MaxWorkers < 16)
270 			SlowStartOpt = 1;
271 		else
272 			SlowStartOpt = MaxWorkers / 4;
273 	}
274 
275 	/*
276 	 * Special directive for when dsynth execs itself to manage
277 	 * a worker chroot.
278 	 */
279 	if (isworker) {
280 		WorkerProcess(ac, av);
281 		exit(0);
282 	}
283 
284 	/*
285 	 * Build initialization and directive handling
286 	 */
287 	DoInitBuild(-1);
288 
289 	/*
290 	 * Directives that use the configuration but are not interlocked
291 	 * against a running dsynth.
292 	 */
293 	if (strcmp(av[0], "monitor") == 0) {
294 		char *spath;
295 		char *lpath;
296 
297 		if (ac == 1) {
298 			asprintf(&spath, "%s/%s", StatsBase, STATS_FILE);
299 			asprintf(&lpath, "%s/%s", StatsBase, STATS_LOCKFILE);
300 			MonitorDirective(spath, lpath);
301 			free(spath);
302 			free(lpath);
303 		} else {
304 			MonitorDirective(av[1], NULL);
305 		}
306 		exit(0);
307 		/* NOT REACHED */
308 	} else if (strcmp(av[0], "add") == 0) {
309 		char *buf;
310 		int fd;
311 		int i;
312 
313 		/*
314 		 * The lock check is a bit racey XXX
315 		 */
316 		fd = open(lkpath, O_RDWR | O_CREAT | O_APPEND, 0644);
317 		if (flock(fd, LOCK_EX | LOCK_NB) == 0) {
318 			dfatal("No dsynth running to add ports to");
319 			flock(fd, LOCK_UN);
320 		}
321 		for (i = 1; i < ac; ++i) {
322 			asprintf(&buf, "%s\n", av[i]);
323 			write(fd, buf, strlen(buf));
324 			printf("added to run: %s\n", av[i]);
325 		}
326 		close(fd);
327 		exit(0);
328 	}
329 
330 	/*
331 	 * Front-end exec (not a WORKER exec), normal startup.  We have
332 	 * the configuration so the first thing we need to do is check
333 	 * the lock file.
334 	 */
335 	lkfd = open(lkpath, O_RDWR | O_CREAT | O_CLOEXEC, 0644);
336 	if (lkfd < 0)
337 		dfatal_errno("Unable to create %s", lkpath);
338 	if (flock(lkfd, LOCK_EX | LOCK_NB) < 0) {
339 		dfatal("Another dsynth is using %s, exiting",
340 		       BuildBase);
341 	}
342 
343 	/*
344 	 * Starting a new run cleans out any prior add directives
345 	 * that may have been pending.
346 	 */
347 	ftruncate(lkfd, 0);
348 	/* leave descriptor open */
349 
350 	doadds = 0;
351 
352 	if (strcmp(av[0], "debug") == 0) {
353 		DoCleanBuild(1);
354 		OptimizeEnv();
355 		pkgs = ParsePackageList(ac - 1, av + 1, 1);
356 		RemovePackages(pkgs);
357 		DoBuild(pkgs);
358 		doadds = 1;
359 	} else if (strcmp(av[0], "status") == 0) {
360 		OptimizeEnv();
361 		if (ac - 1)
362 			pkgs = ParsePackageList(ac - 1, av + 1, 0);
363 		else
364 			pkgs = GetLocalPackageList();
365 		DoStatus(pkgs);
366 	} else if (strcmp(av[0], "cleanup") == 0) {
367 		DoCleanBuild(0);
368 	} else if (strcmp(av[0], "configure") == 0) {
369 		DoCleanBuild(0);
370 		DoConfigure();
371 	} else if (strcmp(av[0], "fetch-only") == 0) {
372 		if (SlowStartOpt == -1)
373 			SlowStartOpt = 999;
374 		if (PkgDepScaleTarget == 100)
375 			PkgDepScaleTarget = 999;
376 		++FetchOnlyOpt;
377 		++YesOpt;
378 		WorkerProcFlags |= WORKER_PROC_FETCHONLY;
379 		DoCleanBuild(1);
380 		OptimizeEnv();
381 		if (ac == 2 && strcmp(av[1], "everything") == 0) {
382 			MaskProbeAbort = 1;
383 			pkgs = GetFullPackageList();
384 		} else {
385 			pkgs = ParsePackageList(ac - 1, av + 1, 0);
386 		}
387 		DoBuild(pkgs);
388 		doadds = 1;
389 	} else if (strcmp(av[0], "list-system") == 0) {
390 		FILE *fp;
391 
392 		DoCleanBuild(1);
393 		OptimizeEnv();
394 		pkgs = GetLocalPackageList();
395 		if ((fp = fopen("build.txt", "w")) != NULL) {
396 			while (pkgs) {
397 				fprintf(fp, "%s\n", pkgs->portdir);
398 				pkgs = pkgs->bnext;
399 			}
400 			fclose(fp);
401 			printf("list written to build.txt\n");
402 		} else {
403 			fprintf(stderr, "Cannot create 'build.txt'\n");
404 			exit(1);
405 		}
406 	} else if (strcmp(av[0], "upgrade-system") == 0) {
407 		DoCleanBuild(1);
408 		OptimizeEnv();
409 		pkgs = GetLocalPackageList();
410 		DoBuild(pkgs);
411 		DoRebuildRepo(0);
412 		DoUpgradePkgs(pkgs, 0);
413 		dfatal("NOTE: you have to pkg upgrade manually");
414 	} else if (strcmp(av[0], "prepare-system") == 0) {
415 		DeleteObsoletePkgs = 1;
416 		DoCleanBuild(1);
417 		OptimizeEnv();
418 		pkgs = GetLocalPackageList();
419 		DoBuild(pkgs);
420 		DoRebuildRepo(0);
421 	} else if (strcmp(av[0], "rebuild-repository") == 0) {
422 		OptimizeEnv();
423 		DoRebuildRepo(0);
424 	} else if (strcmp(av[0], "purge-distfiles") == 0) {
425 		OptimizeEnv();
426 		pkgs = GetFullPackageList();
427 		PurgeDistfiles(pkgs);
428 	} else if (strcmp(av[0], "reset-db") == 0) {
429 		char *dbmpath;
430 
431 		asprintf(&dbmpath, "%s/ports_crc.db", BuildBase);
432 		remove(dbmpath);
433 		printf("%s reset, will be regenerated on next build\n",
434 		       dbmpath);
435 		free(dbmpath);
436 	} else if (strcmp(av[0], "status-everything") == 0) {
437 		OptimizeEnv();
438 		pkgs = GetFullPackageList();
439 		DoStatus(pkgs);
440 	} else if (strcmp(av[0], "everything") == 0) {
441 		if (WorkerProcFlags & WORKER_PROC_DEVELOPER)
442 			WorkerProcFlags |= WORKER_PROC_CHECK_PLIST;
443 		MaskProbeAbort = 1;
444 		DeleteObsoletePkgs = 1;
445 		DoCleanBuild(1);
446 		OptimizeEnv();
447 		pkgs = GetFullPackageList();
448 		DoBuild(pkgs);
449 		DoRebuildRepo(!CheckAddReExec(lkfd));
450 	} else if (strcmp(av[0], "build") == 0) {
451 		DoCleanBuild(1);
452 		OptimizeEnv();
453 		pkgs = ParsePackageList(ac - 1, av + 1, 0);
454 		DoBuild(pkgs);
455 		DoRebuildRepo(!CheckAddReExec(lkfd));
456 		DoUpgradePkgs(pkgs, 1);
457 		doadds = 1;
458 	} else if (strcmp(av[0], "just-build") == 0) {
459 		DoCleanBuild(1);
460 		OptimizeEnv();
461 		pkgs = ParsePackageList(ac - 1, av + 1, 0);
462 		DoBuild(pkgs);
463 		doadds = 1;
464 	} else if (strcmp(av[0], "install") == 0) {
465 		DoCleanBuild(1);
466 		OptimizeEnv();
467 		pkgs = ParsePackageList(ac - 1, av + 1, 0);
468 		DoBuild(pkgs);
469 		DoRebuildRepo(0);
470 		DoUpgradePkgs(pkgs, 0);
471 		doadds = 1;
472 	} else if (strcmp(av[0], "force") == 0) {
473 		DoCleanBuild(1);
474 		OptimizeEnv();
475 		pkgs = ParsePackageList(ac - 1, av + 1, 0);
476 		RemovePackages(pkgs);
477 		DoBuild(pkgs);
478 		DoRebuildRepo(!CheckAddReExec(lkfd));
479 		DoUpgradePkgs(pkgs, 1);
480 		doadds = 1;
481 	} else if (strcmp(av[0], "test") == 0) {
482 		WorkerProcFlags |= WORKER_PROC_CHECK_PLIST |
483 				   WORKER_PROC_INSTALL |
484 				   WORKER_PROC_DEINSTALL;
485 		DoCleanBuild(1);
486 		OptimizeEnv();
487 		pkgs = ParsePackageList(ac - 1, av + 1, 0);
488 		RemovePackages(pkgs);
489 		WorkerProcFlags |= WORKER_PROC_DEVELOPER;
490 		DoBuild(pkgs);
491 		doadds = 1;
492 	} else {
493 		fprintf(stderr, "Unknown directive '%s'\n", av[0]);
494 		usage(2);
495 	}
496 
497 	/*
498 	 * For directives that support the 'add' directive, check for
499 	 * additions and re-exec.
500 	 *
501 	 * Note that the lockfile is O_CLOEXEC and will be remade on exec.
502 	 *
503 	 * XXX a bit racey vs adds done just as we are finishing
504 	 */
505 	if (doadds && CheckAddReExec(lkfd))
506 		DoAddReExec(lkfd, optind + 1, av - optind);
507 
508 	return 0;
509 }
510 
511 /*
512  * If the 'add' directive was issued while a dsynth build was in
513  * progress, we re-exec dsynth with its original options and
514  * directive along with the added ports.
515  */
516 static int
517 CheckAddReExec(int lkfd)
518 {
519 	struct stat st;
520 
521 	if (fstat(lkfd, &st) < 0 || st.st_size == 0)
522 		return 0;
523 	return 1;
524 }
525 
526 static void
527 DoAddReExec(int lkfd, int ac, char **oldav)
528 {
529 	struct stat st;
530 	char *buf;
531 	char **av;
532 	size_t bi;
533 	size_t i;
534 	int nadd;
535 	int n;
536 
537 	if (fstat(lkfd, &st) < 0 || st.st_size == 0)
538 		return;
539 	buf = malloc(st.st_size + 1);
540 	if (read(lkfd, buf, st.st_size) != st.st_size) {
541 		free(buf);
542 		return;
543 	}
544 	buf[st.st_size] = 0;
545 
546 	nadd = 0;
547 	for (i = 0; i < (size_t)st.st_size; ++i) {
548 		if (buf[i] == '\n' || buf[i] == 0) {
549 			buf[i] = 0;
550 			++nadd;
551 		}
552 	}
553 
554 	av = calloc(ac + nadd + 1, sizeof(char *));
555 
556 	for (n = 0; n < ac; ++n)
557 		av[n] = oldav[n];
558 
559 	nadd = 0;
560 	bi = 0;
561 	for (i = 0; i < (size_t)st.st_size; ++i) {
562 		if (buf[i] == 0) {
563 			av[ac + nadd] = buf + bi;
564 			bi = i + 1;
565 			++nadd;
566 		}
567 	}
568 
569 	printf("dsynth re-exec'ing additionally added packages\n");
570 	for (n = 0; n < ac + nadd; ++n)
571 		printf(" %s", av[n]);
572 	printf("\n");
573 	fflush(stdout);
574 	sleep(2);
575 	execv(DSynthExecPath, av);
576 }
577 
578 static void
579 DoInit(void)
580 {
581 	struct stat st;
582 	char *path;
583 	FILE *fp;
584 
585 	if (stat(ConfigBase1, &st) == 0) {
586 		dfatal("init will not overwrite %s", ConfigBase1);
587 	}
588 	if (ConfigBase2 && stat(ConfigBase2, &st) == 0) {
589 		dfatal("init will not create %s if %s exists",
590 		       ConfigBase2, ConfigBase1);
591 	}
592 	if (mkdir(ConfigBase1, 0755) < 0)
593 		dfatal_errno("Unable to mkdir %s", ConfigBase1);
594 
595 	asprintf(&path, "%s/dsynth.ini", ConfigBase1);
596 	fp = fopen(path, "w");
597 	dassert_errno(fp, "Unable to create %s", path);
598 	fprintf(fp, "%s",
599 	    "; This Synth configuration file is automatically generated\n"
600 	    "; Take care when hand editing!\n"
601 	    "\n"
602 	    "[Global Configuration]\n"
603 	    "profile_selected= LiveSystem\n"
604 	    "\n"
605 	    "[LiveSystem]\n"
606 	    "Operating_system= DragonFly\n"
607 	    "Directory_packages= /build/synth/live_packages\n"
608 	    "Directory_repository= /build/synth/live_packages/All\n"
609 	    "Directory_portsdir= /build/synth/dports\n"
610 	    "Directory_options= /build/synth/options\n"
611 	    "Directory_distfiles= /build/synth/distfiles\n"
612 	    "Directory_buildbase= /build/synth/build\n"
613 	    "Directory_logs= /build/synth/logs\n"
614 	    "Directory_ccache= disabled\n"
615 	    "Directory_system= /\n"
616 	    "Package_suffix= .txz\n"
617 	    "Number_of_builders= 0\n"
618 	    "Max_jobs_per_builder= 0\n"
619 	    "Tmpfs_workdir= true\n"
620 	    "Tmpfs_localbase= true\n"
621 	    "Display_with_ncurses= true\n"
622 	    "leverage_prebuilt= false\n"
623 	    "; Meta_version= 2\n"
624 	    "; Check_plist= false\n"
625 	    "; Numa_setsize= 2\n"
626 	    "\n");
627 	if (fclose(fp))
628 		dfatal_errno("Unable to write to %s\n", ConfigBase1);
629 	free(path);
630 
631 	asprintf(&path, "%s/LiveSystem-make.conf", ConfigBase1);
632 	fp = fopen(path, "w");
633 	dassert_errno(fp, "Unable to create %s", path);
634 	fprintf(fp, "%s",
635 	    "#\n"
636 	    "# Various dports options that might be of interest\n"
637 	    "#\n"
638 	    "#LICENSES_ACCEPTED=      NONE\n"
639 	    "#DISABLE_LICENSES=       yes\n"
640 	    "#DEFAULT_VERSIONS=       ssl=openssl\n"
641 	    "#FORCE_PACKAGE=          yes\n"
642 	    "#DPORTS_BUILDER=         yes\n"
643 	    "#\n"
644 	    "# Turn these on to generate debug binaries.  However, these\n"
645 	    "# options will seriously bloat memory use and storage use,\n"
646 	    "# do not use lightly\n"
647 	    "#\n"
648 	    "#STRIP=\n"
649 	    "#WITH_DEBUG=yes\n"
650 	);
651 	if (fclose(fp))
652 		dfatal_errno("Unable to write to %s\n", ConfigBase1);
653 	free(path);
654 }
655 
656 __dead2 static void
657 usage(int ecode)
658 {
659 	if (ecode == 2) {
660 		fprintf(stderr, "Run 'dsynth help' for usage\n");
661 		exit(1);
662 	}
663 
664 	fprintf(stderr,
665     "dsynth [options] directive\n"
666     "    -d                   - Debug verbosity (-dd disables ncurses)\n"
667     "    -f                   - Force (for purge-distfiles)\n"
668     "    -h                   - Display this screen and exit\n"
669     "    -m gb                - Load management based on pkgdep memory\n"
670     "    -p profile           - Override profile selected in dsynth.ini\n"
671     "    -s n                 - Set initial DynamicMaxWorkers\n"
672     "    -v                   - Print version info and exit\n"
673     "    -x                   - Do not rebuild packages with dependencies\n"
674     "                           which require rebuilding\n"
675     "    -xx                  - Do not rebuild packages whos dports trees\n"
676     "                           change\n"
677     "    -y                   - Automatically answer yes to dsynth questions\n"
678     "    -C configbase        - Config base directory (replaces /etc/dsynth)\n"
679     "    -D                   - Enable DEVELOPER mode\n"
680     "    -P                   - Include the check-plist stage\n"
681     "    -S                   - Disable ncurses\n"
682     "    -N                   - Do not nice-up sub-processes (else nice +10)\n"
683     "\n"
684     "    init                 - Initialize /etc/dsynth\n"
685     "    status               - Dry-run of 'upgrade-system'\n"
686     "    cleanup              - Clean-up mounts\n"
687     "    configure            - Bring up configuration menu\n"
688     "    list-system          - Just generate the build list to build.txt\n"
689     "    upgrade-system       - Incremental build and upgrade using pkg list\n"
690     "                           from local system, then upgrade the local\n"
691     "                           system.\n"
692     "    prepare-system       - 'upgrade-system' but stops after building\n"
693     "    rebuild-repository   - Rebuild database files for current repository\n"
694     "    purge-distfiles      - Delete obsolete source distribution files\n"
695     "    reset-db             - Delete ports_crc.db, regenerate next build\n"
696     "    status-everything    - Dry-run of 'everything'\n"
697     "    everything           - Build entire dports tree and repo database\n"
698     "				(-D everything infers -P)\n"
699     "    version              - Print version info and exit\n"
700     "    help                 - Display this screen and exit\n"
701     "    status     [ports]   - Dry-run of 'build' with given list\n"
702     "    build      [ports]   - Incrementally build dports based on the given\n"
703     "                           list, but asks before updating the repo\n"
704     "                           database and system\n"
705     "    just-build [ports]   - 'build' but skips post-build steps\n"
706     "    install    [ports]   - 'build' but upgrades system without asking\n"
707     "    force      [ports]   - 'build' but deletes existing packages first\n"
708     "    test       [ports]   - 'build' w/DEVELOPER=yes and pre-deletes pkgs\n"
709     "				(also infers -P)\n"
710     "    debug      [ports]   - like 'test' but leaves mounts intact\n"
711     "    fetch-only [ports]   - Fetch src dists only ('everything' ok)\n"
712     "    monitor    [datfile] - Monitor a running dsynth\n"
713     "\n"
714     "    [ports] is a space-delimited list of origins, e.g. editors/joe.  It\n"
715     "            may also be a path to a file containing one origin per line.\n"
716 	);
717 
718 	exit(ecode);
719 }
720