xref: /dragonfly/usr.bin/dsynth/dsynth.c (revision ab4c55c707dde5384d8a233485cc87b3ea249687)
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