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
main(int ac,char ** av)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
CheckAddReExec(int lkfd)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
DoAddReExec(int lkfd,int ac,char ** oldav)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
DoInit(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
usage(int ecode)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 " add [ports] - Add listed dports to a new build queue that\n"
703 " will be built after the current run finishes.\n"
704 " build [ports] - Incrementally build dports based on the given\n"
705 " list, but asks before updating the repo\n"
706 " database and system\n"
707 " just-build [ports] - 'build' but skips post-build steps\n"
708 " install [ports] - 'build' but upgrades system without asking\n"
709 " force [ports] - 'build' but deletes existing packages first\n"
710 " test [ports] - 'build' w/DEVELOPER=yes and pre-deletes pkgs\n"
711 " (also infers -P)\n"
712 " debug [ports] - like 'test' but leaves mounts intact\n"
713 " fetch-only [ports] - Fetch src dists only ('everything' ok)\n"
714 " monitor [datfile] - Monitor a running dsynth\n"
715 "\n"
716 " [ports] is a space-delimited list of origins, e.g. editors/joe. It\n"
717 " may also be a path to a file containing one origin per line.\n"
718 );
719
720 exit(ecode);
721 }
722