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