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