xref: /dragonfly/usr.sbin/config/main.c (revision 71126e33)
1 /*
2  * Copyright (c) 1980, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1980, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)main.c	8.1 (Berkeley) 6/6/93
35  * $FreeBSD: src/usr.sbin/config/main.c,v 1.37.2.3 2001/06/13 00:25:53 cg Exp $
36  * $DragonFly: src/usr.sbin/config/main.c,v 1.12 2004/03/08 03:24:27 dillon Exp $
37  */
38 
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <sys/file.h>
42 #include <sys/mman.h>
43 #include <sys/param.h>
44 #include <ctype.h>
45 #include <err.h>
46 #include <stdio.h>
47 #include <sysexits.h>
48 #include <unistd.h>
49 #include "y.tab.h"
50 #include "config.h"
51 
52 #ifndef TRUE
53 #define TRUE	(1)
54 #endif
55 
56 #ifndef FALSE
57 #define FALSE	(0)
58 #endif
59 
60 #define	CDIR	"../../compile/"
61 
62 char *	PREFIX;
63 char 	destdir[MAXPATHLEN];
64 char 	srcdir[MAXPATHLEN];
65 
66 static int no_config_clobber = TRUE;
67 int	debugging;
68 int	profiling;
69 
70 static void configfile(void);
71 static void get_srcdir(void);
72 static void usage(void);
73 
74 /*
75  * Config builds a set of files for building a UNIX
76  * system given a description of the desired system.
77  */
78 int
79 main(int argc, char *argv[])
80 {
81 	struct stat buf;
82 	int ch, len, i;
83 	char *p;
84 	char linksrc[64], linkdest[MAXPATHLEN];
85 	static char *emus[] = { "linux", "svr4" };
86 
87 	while ((ch = getopt(argc, argv, "d:gprn")) != -1)
88 		switch (ch) {
89 		case 'd':
90 			if (*destdir == '\0')
91 				strlcpy(destdir, optarg, sizeof(destdir));
92 			else
93 				errx(2, "directory already set");
94 			break;
95 		case 'g':
96 			debugging++;
97 			break;
98 		case 'p':
99 			profiling++;
100 			break;
101 		case 'n':
102 			/* no_config_clobber is now true by default, no-op */
103 			fprintf(stderr,
104 				"*** Using obsolete config option '-n' ***\n");
105 			break;
106 		case 'r':
107 			no_config_clobber = FALSE;
108 			break;
109 		case '?':
110 		default:
111 			usage();
112 		}
113 	argc -= optind;
114 	argv += optind;
115 
116 	if (argc != 1)
117 		usage();
118 
119 	if (freopen(PREFIX = *argv, "r", stdin) == NULL)
120 		err(2, "%s", PREFIX);
121 
122 	if (*destdir != '\0') {
123 		len = strlen(destdir);
124 		while (len > 1 && destdir[len - 1] == '/')
125 			destdir[--len] = '\0';
126 		get_srcdir();
127 	} else {
128 		strlcpy(destdir, CDIR, sizeof(destdir));
129 		strlcat(destdir, PREFIX, sizeof(destdir));
130 	}
131 
132 	p = path((char *)NULL);
133 	if (stat(p, &buf)) {
134 		if (mkdir(p, 0777))
135 			err(2, "%s", p);
136 	}
137 	else if ((buf.st_mode & S_IFMT) != S_IFDIR) {
138 		errx(2, "%s isn't a directory", p);
139 	}
140 	else if (!no_config_clobber) {
141 		char tmp[strlen(p) + 8];
142 
143 		fprintf(stderr, "Removing old directory %s:  ", p);
144 		fflush(stderr);
145 		snprintf(tmp, sizeof(tmp), "rm -rf %s", p);
146 		if (system(tmp)) {
147 			fprintf(stderr, "Failed!\n");
148 			err(2, "%s", tmp);
149 		}
150 		fprintf(stderr, "Done.\n");
151 		if (mkdir(p, 0777))
152 			err(2, "%s", p);
153 	}
154 
155 	dtab = NULL;
156 	if (yyparse())
157 		exit(3);
158 	if (machinename == NULL) {
159 		printf("Specify machine type, e.g. ``machine i386''\n");
160 		exit(1);
161 	}
162 	newbus_ioconf();
163 
164 	/*
165 	 * make symbolic links in compilation directory
166 	 * for "sys" (to make genassym.c work along with #include <sys/xxx>)
167 	 * and similarly for "machine".
168 	 */
169 	if (*srcdir == '\0')
170 		snprintf(linkdest, sizeof(linkdest), "../../%s/include",
171 		    machinename);
172 	else
173 		snprintf(linkdest, sizeof(linkdest), "%s/%s/include",
174 		    srcdir, machinename);
175 	symlink(linkdest, path("machine"));
176 
177 	/*
178 	 * XXX check directory structure for architecture subdirectories and
179 	 * create the symlinks automatically XXX
180 	 */
181 	if (*srcdir == '\0')
182 		snprintf(linkdest, sizeof(linkdest),
183 		    "../../../../../net/i4b/include/%s",
184 		    machinename);
185 	else
186 		snprintf(linkdest, sizeof(linkdest), "%s/net/i4b/include/%s",
187 		    srcdir, machinename);
188 	mkdir(path("net"), 0755);
189 	mkdir(path("net/i4b"), 0755);
190 	mkdir(path("net/i4b/include"), 0755);
191 	symlink(linkdest, path("net/i4b/include/machine"));
192 
193 	for (i = 0; i < sizeof(emus) / sizeof(emus[0]); ++i) {
194 		if (*srcdir == 0)  {
195 			snprintf(linkdest, sizeof(linkdest),
196 			    "../../emulation/%s/%s",
197 			    emus[i], machinename);
198 		} else {
199 			snprintf(linkdest, sizeof(linkdest),
200 			    "%s/emulation/%s/%s",
201 			    srcdir, emus[i], machinename);
202 		}
203 		snprintf(linksrc, sizeof(linksrc), "arch_%s", emus[i]);
204 		symlink(linkdest, path(linksrc));
205 	}
206 
207 	options();			/* make options .h files */
208 	makefile();			/* build Makefile */
209 	headers();			/* make a lot of .h files */
210 	configfile();			/* put config file into kernel*/
211 	printf("Kernel build directory is %s\n", p);
212 	exit(EX_OK);
213 }
214 
215 /*
216  * get_srcdir
217  *	determine the root of the kernel source tree
218  *	and save that in srcdir.
219  */
220 static void
221 get_srcdir(void)
222 {
223 
224 	if (realpath("../..", srcdir) == NULL)
225 		errx(2, "Unable to find root of source tree");
226 }
227 
228 static void
229 usage(void)
230 {
231 
232 	fprintf(stderr, "usage: config [-gpr] [-d destdir] sysname\n");
233 	exit(1);
234 }
235 
236 /*
237  * get_word
238  *	returns EOF on end of file
239  *	NULL on end of line
240  *	pointer to the word otherwise
241  */
242 char *
243 get_word(FILE *fp)
244 {
245 	static char line[80];
246 	int ch;
247 	char *cp;
248 	int escaped_nl = 0;
249 
250 begin:
251 	while ((ch = getc(fp)) != EOF)
252 		if (ch != ' ' && ch != '\t')
253 			break;
254 	if (ch == EOF)
255 		return((char *)EOF);
256 	if (ch == '\\') {
257 		escaped_nl = 1;
258 		goto begin;
259 	}
260 	if (ch == '\n') {
261 		if (escaped_nl) {
262 			escaped_nl = 0;
263 			goto begin;
264 		}
265 		else
266 			return(NULL);
267 	}
268 	cp = line;
269 	*cp++ = ch;
270 	while ((ch = getc(fp)) != EOF) {
271 		if (isspace(ch))
272 			break;
273 		*cp++ = ch;
274 	}
275 	*cp = 0;
276 	if (ch == EOF)
277 		return((char *)EOF);
278 	ungetc(ch, fp);
279 	return(line);
280 }
281 
282 /*
283  * get_quoted_word
284  *	like get_word but will accept something in double or single quotes
285  *	(to allow embedded spaces).
286  */
287 char *
288 get_quoted_word(FILE *fp)
289 {
290 	static char line[256];
291 	int ch;
292 	char *cp;
293 	int escaped_nl = 0;
294 
295 begin:
296 	while ((ch = getc(fp)) != EOF)
297 		if (ch != ' ' && ch != '\t')
298 			break;
299 	if (ch == EOF)
300 		return((char *)EOF);
301 	if (ch == '\\') {
302 		escaped_nl = 1;
303 		goto begin;
304 	}
305 	if (ch == '\n') {
306 		if (escaped_nl) {
307 			escaped_nl = 0;
308 			goto begin;
309 		}
310 		else
311 			return(NULL);
312 	}
313 	cp = line;
314 	if (ch == '"' || ch == '\'') {
315 		int quote = ch;
316 
317 		while ((ch = getc(fp)) != EOF) {
318 			if (ch == quote)
319 				break;
320 			if (ch == '\n') {
321 				*cp = 0;
322 				printf("config: missing quote reading `%s'\n",
323 					line);
324 				exit(2);
325 			}
326 			*cp++ = ch;
327 		}
328 	} else {
329 		*cp++ = ch;
330 		while ((ch = getc(fp)) != EOF) {
331 			if (isspace(ch))
332 				break;
333 			*cp++ = ch;
334 		}
335 		if (ch != EOF)
336 			ungetc(ch, fp);
337 	}
338 	*cp = 0;
339 	if (ch == EOF)
340 		return((char *)EOF);
341 	return(line);
342 }
343 
344 /*
345  * prepend the path to a filename
346  */
347 char *
348 path(char *file)
349 {
350 	char *cp;
351 
352 	cp = malloc((size_t)(strlen(destdir) + (file ? strlen(file) : 0) + 2));
353 	strcpy(cp, destdir);
354 	if (file != NULL) {
355 		strcat(cp, "/");
356 		strcat(cp, file);
357 	}
358 	return(cp);
359 }
360 
361 static void
362 configfile(void)
363 {
364 	FILE *fi, *fo;
365 	char *p;
366 	int i;
367 
368 	fi = fopen(PREFIX, "r");
369 	if (fi == NULL)
370 		err(2, "%s", PREFIX);
371 	fo = fopen(p = path("config.c.new"), "w");
372 	if (fo == NULL)
373 		err(2, "%s", p);
374 	fprintf(fo, "#include \"opt_config.h\"\n");
375 	fprintf(fo, "#ifdef INCLUDE_CONFIG_FILE \n");
376 	fprintf(fo, "static const char config[] = \"\\\n");
377 	fprintf(fo, "START CONFIG FILE %s\\n\\\n___", PREFIX);
378 	while (EOF != (i = getc(fi))) {
379 		if (i == '\n') {
380 			fprintf(fo, "\\n\\\n___");
381 		} else if (i == '\"') {
382 			fprintf(fo, "\\\"");
383 		} else if (i == '\\') {
384 			fprintf(fo, "\\\\");
385 		} else {
386 			putc(i, fo);
387 		}
388 	}
389 	fprintf(fo, "\\n\\\nEND CONFIG FILE %s\\n\\\n", PREFIX);
390 	fprintf(fo, "\";\n");
391 	fprintf(fo, "\n#endif /* INCLUDE_CONFIG_FILE */\n");
392 	fclose(fi);
393 	fclose(fo);
394 	moveifchanged(path("config.c.new"), path("config.c"));
395 }
396 
397 /*
398  * moveifchanged --
399  *	compare two files; rename if changed.
400  */
401 void
402 moveifchanged(const char *from_name, const char *to_name)
403 {
404 	char *p, *q;
405 	int changed;
406 	size_t tsize;
407 	struct stat from_sb, to_sb;
408 	int from_fd, to_fd;
409 
410 	changed = 0;
411 
412 	if ((from_fd = open(from_name, O_RDONLY)) < 0)
413 		err(EX_OSERR, "moveifchanged open(%s)", from_name);
414 
415 	if ((to_fd = open(to_name, O_RDONLY)) < 0)
416 		changed++;
417 
418 	if (!changed && fstat(from_fd, &from_sb) < 0)
419 		err(EX_OSERR, "moveifchanged fstat(%s)", from_name);
420 
421 	if (!changed && fstat(to_fd, &to_sb) < 0)
422 		err(EX_OSERR, "moveifchanged fstat(%s)", to_name);
423 
424 	if (!changed && from_sb.st_size != to_sb.st_size)
425 		changed++;
426 
427 	tsize = (size_t)from_sb.st_size;
428 
429 	if (!changed) {
430 		p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
431 #ifndef MAP_FAILED
432 #define MAP_FAILED ((caddr_t)-1)
433 #endif
434 		if (p == MAP_FAILED)
435 			err(EX_OSERR, "mmap %s", from_name);
436 		q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
437 		if (q == MAP_FAILED)
438 			err(EX_OSERR, "mmap %s", to_name);
439 
440 		changed = memcmp(p, q, tsize);
441 		munmap(p, tsize);
442 		munmap(q, tsize);
443 	}
444 	if (changed) {
445 		if (rename(from_name, to_name) < 0)
446 			err(EX_OSERR, "rename(%s, %s)", from_name, to_name);
447 	} else {
448 		if (unlink(from_name) < 0)
449 			err(EX_OSERR, "unlink(%s)", from_name);
450 	}
451 }
452