diff options
Diffstat (limited to 'usr.sbin/config')
| -rw-r--r-- | usr.sbin/config/Makefile | 23 | ||||
| -rw-r--r-- | usr.sbin/config/Makefile.depend | 19 | ||||
| -rw-r--r-- | usr.sbin/config/config.5 | 474 | ||||
| -rw-r--r-- | usr.sbin/config/config.8 | 277 | ||||
| -rw-r--r-- | usr.sbin/config/config.h | 278 | ||||
| -rw-r--r-- | usr.sbin/config/config.y | 503 | ||||
| -rw-r--r-- | usr.sbin/config/configvers.h | 52 | ||||
| -rw-r--r-- | usr.sbin/config/kernconf.tmpl | 15 | ||||
| -rw-r--r-- | usr.sbin/config/lang.l | 336 | ||||
| -rw-r--r-- | usr.sbin/config/main.cc | 819 | ||||
| -rw-r--r-- | usr.sbin/config/mkheaders.c | 60 | ||||
| -rw-r--r-- | usr.sbin/config/mkmakefile.cc | 848 | ||||
| -rw-r--r-- | usr.sbin/config/mkoptions.cc | 460 | 
13 files changed, 4164 insertions, 0 deletions
| diff --git a/usr.sbin/config/Makefile b/usr.sbin/config/Makefile new file mode 100644 index 000000000000..6c0320a07cc1 --- /dev/null +++ b/usr.sbin/config/Makefile @@ -0,0 +1,23 @@ +SRCDIR:=${.PARSEDIR:tA} + +PACKAGE= toolchain +PROG_CXX=	config +MAN=	config.5 config.8 +SRCS=	config.y main.cc lang.l mkmakefile.cc mkheaders.c \ +	mkoptions.cc y.tab.h kernconf.c + +FILE2C?=file2c + +kernconf.c: kernconf.tmpl +	${FILE2C} 'char kernconfstr[] = {' ',0};' < \ +	    ${SRCDIR}/kernconf.tmpl > kernconf.c + +CFLAGS+= -I. -I${SRCDIR} + +NO_WMISSING_VARIABLE_DECLARATIONS= + +CLEANFILES+=	kernconf.c + +mkmakefile.o: configvers.h + +.include <bsd.prog.mk> diff --git a/usr.sbin/config/Makefile.depend b/usr.sbin/config/Makefile.depend new file mode 100644 index 000000000000..4b9cd9863363 --- /dev/null +++ b/usr.sbin/config/Makefile.depend @@ -0,0 +1,19 @@ +# Autogenerated - do NOT edit! + +DIRDEPS = \ +	include \ +	include/xlocale \ +	lib/${CSU_DIR} \ +	lib/libc \ +	lib/libc++ \ +	lib/libcompiler_rt \ +	lib/libcxxrt \ +	lib/msun \ +	usr.bin/yacc.host \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/usr.sbin/config/config.5 b/usr.sbin/config/config.5 new file mode 100644 index 000000000000..d7bf96cf6e7f --- /dev/null +++ b/usr.sbin/config/config.5 @@ -0,0 +1,474 @@ +.\" Copyright (c) 2003 Joseph Koshy +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\"    notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\"    notice, this list of conditions and the following disclaimer in the +.\"    documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd June 13, 2025 +.Dt CONFIG 5 +.Os +.Sh NAME +.Nm config +.Nd kernel configuration file format +.Sh DESCRIPTION +A kernel configuration file specifies the configuration of a +.Fx +kernel. +It is processed by +.Xr config 8 +to create a build environment where a kernel may be built using +.Xr make 1 . +.Ss Lexical Structure +A kernel configuration file comprises a sequence of specification +directives. +.Pp +A specification directive starts with a keyword at the beginning +of the line and is followed by additional parameters. +.Pp +A specification directive may be terminated by a semicolon +.Ql \&; +or by a newline. +Long input lines may be broken into shorter lines by starting the +second and subsequent lines with a white space character. +.Pp +Case is significant, +.Dq Li machine +and +.Dq Li MACHINE +are different tokens. +.Pp +A double quote character +.Ql \[dq] +starts a quoted string. +All characters up to the next quote character form the value +of the quoted string. +A +.Ql \[dq] +character may be inserted into a quoted string by +using the sequence +.Ql \e\[dq] . +.Pp +Numbers are specified using +.Tn C Ns -style +syntax. +.Pp +A +.Ql # +character starts a comment; all characters from the +.Ql # +character till the end of the current line are ignored. +.Pp +Whitespace between tokens is ignored, except inside quoted strings. +Whitespace following a comment line is ignored. +.Ss Configuration Directives +Kernel configuration directives may appear in any order +in a kernel configuration file. +Directives are processed in order of appearance with subsequent +directive lines overriding the effect of prior ones. +.Pp +The list of keywords and their meanings are as follows: +.Pp +.Bl -tag -width indent -compact +.\" -------- CPU -------- +.It Ic cpu Ar cputype +Specify the CPU this kernel will run on. +There can be more than one +.Ic cpu +directive in a configuration file. +The allowed list of CPU names is architecture specific and is +defined in the file +.Pa sys/conf/options . Ns Aq Ar arch . +.\" -------- DEVICE -------- +.Pp +.It Ic device Ar name Op , Ar name Op ... +.It Ic devices Ar name Op , Ar name Op ... +Configures the specified devices +for inclusion into the kernel image. +Devices that are common to all architectures are +defined in the file +.Pa sys/conf/files . +Devices that are specific to architecture +.Ar arch +are defined in the file +.Pa sys/conf/files . Ns Aq Ar arch . +.\" -------- ENV -------- +.Pp +.It Ic env Ar filename +Specifies a filename containing a kernel environment definition. +.Pp +The kernel will augment this compiled-in environment with the environment +prepared for it at boot time by +.Xr loader 8 . +Environment variables specified in the +.Xr loader 8 +environment will take precedence over environment variables specified in +.Ar filename , +and environment variables specified in the dynamic environment take precedence +over both of these. +.Pp +.Va loader_env.disabled=1 +may be specified in the static environment to disable the +.Xr loader 8 +environment. +Disabling the +.Xr loader 8 +should be done with caution and due consideration for whether or not it supplies +environment variables needed for properly booting the system. +.Pp +.Va static_env.disabled=1 +may be specified in the +.Xr loader 8 +environment to disable use of the static environment. +This option has no effect if specified in any environment after the +.Xr loader 8 +environment is processed. +This option is not usable in conjunction with +.Va loader_env.disabled . +.Pp +This directive is useful for setting kernel tunables in +embedded environments that do not start from +.Xr loader 8 . +.Pp +All +.Ic env +and +.Ic envvar +directives will be processed and added to the static environment in reversed +order of appearance so that later specified variables properly override earlier +specified variables. +Note that within +.Ar filename , +the first appearance of a given variable will be the first one seen by the +kernel, effectively shadowing any later appearances of the same variable within +.Ar filename . +.\" -------- ENVVAR -------- +.Pp +.It Ic envvar Ar setting +Specifies an individual environment setting to be added to the kernel's +compiled-in environment. +.Ar setting +must be of the form +.Dq Va name=value . +Optional quotes are supported in both name and value. +.Pp +All +.Ic env +and +.Ic envvar +directives will be processed and added to the static environment in reversed +order of appearance so that later specified variables properly override earlier +specified variables. +.\" -------- FILES -------- +.Pp +.It Ic files Ar filename +Specifies a file containing a list of files specific to that kernel +configuration file (a la +.Pa files . Ns Aq Ar arch ) . +.\" -------- HINTS -------- +.Pp +.It Ic hints Ar filename +Specifies a file to load a static device configuration specification +from. +From +.Fx 5.0 +onwards, the kernel reads the system's device configuration at boot +time (see +.Xr device.hints 5 ) . +This directive configures the kernel to use the static device configuration +listed in +.Ar filename . +.Pp +Hints provided in this static device configuration will be overwritten in the +order in which they're encountered. +Hints in the compiled-in environment takes precedence over compiled-in hints, +and hints in the environment prepared for the kernel by +.Xr loader 8 +takes precedence over hints in the compiled-in environment. +.Pp +Once the dynamic environment becomes available, all compiled-in hints will be +added to the dynamic environment if they do not already have an override in +the dynamic environment. +The dynamic environment will then be used for all searches of hints. +.Pp +.Va static_hints.disabled=1 +may be specified in either a compiled-in environment or the +.Xr loader 8 +environment to disable use of these hints files. +This option has no effect if specified in any environment after the +.Xr loader 8 +environment is processed. +.Pp +The file +.Ar filename +must conform to the syntax specified by +.Xr device.hints 5 . +Multiple hints lines are allowed. +The resulting hints will be the files concatenated in reverse order of +appearance so that hints in later files properly override hints in earlier +files. +.\" -------- IDENT -------- +.Pp +.It Ic ident Ar name +Set the kernel name to +.Ar name . +At least one +.Ic ident +directive is required. +.\" -------- INCLUDE -------- +.Pp +.It Ic include Ar filename +Read subsequent text from file +.Ar filename +and return to the current file after +.Ar filename +is successfully processed. +.\" -------- INCLUDEOPTIONS -------- +.Pp +.It Ic includeoptions Ar filename +Specifies a file containing a list of additional options +specific to that kernel configuration file. +This is useful for build environments that need to add +custom options and is often used in conjunction with the +.Ic makeoption +directive. +.\" -------- MACHINE -------- +.Pp +.It Ic machine Ar arch Op Ar cpuarch +Specifies the architecture of the machine the kernel is being +compiled for. +Legal values for +.Ar arch +include: +.Pp +.Bl -tag -width ".Cm powerpc" -compact +.It Cm arm64 +The 64-bit ARM application architecture. +.It Cm arm +The ARM architecture +.It Cm amd64 +The AMD x86-64 architecture. +.It Cm i386 +The Intel x86 based PC architecture. +.It Cm powerpc +The IBM PowerPC architecture. +.It Cm riscv +The RISC-V architecture. +.El +.Pp +If argument +.Ar cpuarch +is specified, it points +.Xr config 8 +to the cpu architecture of the machine. +When +.Ar cpuarch +is not specified, it is assumed to be the same as +.Ar arch . +.Ar arch +corresponds to MACHINE. +.Ar cpuarch +corresponds to MACHINE_ARCH. +.Pp +A kernel configuration file may have only one +.Ic machine +directive, unless the second one matches the +machine argument in the first one exactly. +.\" -------- MAKEOPTION -------- +.Pp +.It Ic makeoption Ar options +.It Ic makeoptions Ar options +Add +.Ar options +to the generated makefile. +.Pp +The +.Ar options +argument is a comma separated list of one or more option +specifications. +Each option specification has the form +.Pp +.D1 Ar MakeVariableName Ns Op = Ns Ar Value +.D1 Ar MakeVariableName Ns += Ns Ar Value +.Pp +and results in the appropriate +.Xr make 1 +variable definition being inserted into the generated makefile. +If only the name of the +.Xr make 1 +variable is specified, +.Ar value +is assumed to be the empty string. +.Pp +Note that, as the common makefiles overwrite the +.Va CFLAGS +variable after having processed the configuration file, +customizing +.Va CFLAGS +directly via +.Ic makeoptions +is not possible. +Nonetheless, custom compiler flags can be specified using the +.Va CONF_CFLAGS +variable instead. +Its content is appended to +.Va CFLAGS +after the common makefiles have set the latter, allowing to override their +compilation flags. +.Pp +Example: +.Bd -literal -offset indent -compact +makeoptions MYMAKEOPTION="foo" +makeoptions MYMAKEOPTION+="bar" +makeoptions MYNULLMAKEOPTION +makeoptions CONF_CFLAGS+="-DSOME_CONTROLLING_MACRO" +.Ed +.\" -------- MAXUSERS -------- +.Pp +.It Ic maxusers Ar number +This optional directive is used to configure the size +of some kernel data structures. +The parameter +.Ar number +can be 0 (the default) or an integer greater than or equal to 2. +A value of 0 indicates that the kernel should configure +its data structures according to the size of available +physical memory. +If auto configuration is requested, the kernel will set +this tunable to a value between 32 and 384 for 32-bit systems, +or scale the value higher based on available memory for 64-bit +systems. +.Pp +As explained in +.Xr tuning 7 , +this tunable can also be set at boot time using +.Xr loader 8 . +.\" -------- NOCPU -------- +.Pp +.It Ic nocpu Ar cputype +Remove the specified CPU +from the list of previously selected CPUs. +This directive can be used to cancel the effect of +.Ic cpu +directives in files included using +.Ic include . +.\" -------- NODEVICE -------- +.Pp +.It Ic nodevice Ar name Op , Ar name Op ... +.It Ic nodevices Ar name Op , Ar name Op ... +Remove the specified devices +from the list of previously selected devices. +This directive can be used to cancel the effects of +.Ic device +or +.Ic devices +directives in files included using +.Ic include . +.\" -------- NOMAKEOPTION -------- +.Pp +.It Ic nomakeoption Ar name +.It Ic nomakeoptions Ar name +Removes previously defined +.Xr make 1 +option +.Ar name +from the kernel build. +This directive can be used to cancel the effects of +.Ic makeoption +directives in files included using +.Ic include . +.\" -------- NOOPTION -------- +.Pp +.It Ic nooption Ar name Op , Ar name Op ... +.It Ic nooptions Ar name Op , Ar name Op ... +Remove the specified kernel options +from the list of previously defined options. +This directive can be used to cancel the effects of +.Ic option +or +.Ic options +directives in files included using +.Ic include . +.\" -------- OPTIONS -------- +.Pp +.It Ic option Ar optionspec Op , Ar optionspec Op ... +.It Ic options Ar optionspec Op , Ar optionspec Op ... +Add compile time kernel options to the kernel build. +Each option specification has the form +.Pp +.D1 Ar name Ns Op = Ns Ar value +.Pp +If +.Ar value +is not specified, it is assumed to be +.Dv NULL . +Options common to all architectures are specified in +the file +.Pa sys/conf/options . +Options specific to architecture +.Ar arch +are specified in the file +.Pa sys/conf/options . Ns Aq Ar arch . +.El +.Sh FILES +.Bl -tag -width ".Pa sys/conf/Makefile. Ns Ar arch" -compact +.It Pa sys/compile/ Ns Ar NAME +Compile directory created from a kernel configuration. +.It Pa sys/conf/Makefile . Ns Ar arch +.Pa Makefile +fragments for architecture +.Ar arch . +.It Pa sys/conf/files +Devices common to all architectures. +.It Pa sys/conf/files . Ns Ar arch +Devices for architecture +.Ar arch . +.It Pa sys/conf/options +Options common to all architectures. +.It Pa sys/conf/options . Ns Ar arch +Options for architecture +.Ar arch . +.El +.Sh SEE ALSO +.Xr kenv 1 , +.Xr make 1 , +.Xr device.hints 5 , +.Xr loader.conf 5 , +.Xr config 8 , +.Xr kldload 8 , +.Xr loader 8 +.Rs +.%T "Building 4.4BSD Kernels with Config" +.%A "Samuel J. Leffler" +.%A "Michael J. Karels" +.Re +.Sh HISTORY +The +.Xr config 8 +utility first appeared in +.Bx 4.1 , +and was subsequently revised in +.Bx 4.4 . +.Pp +The kernel configuration mechanism changed further in +.Fx 4.0 +and +.Fx 5.0 , +moving toward an architecture supporting dynamic kernel +configuration. diff --git a/usr.sbin/config/config.8 b/usr.sbin/config/config.8 new file mode 100644 index 000000000000..e0912a4bc104 --- /dev/null +++ b/usr.sbin/config/config.8 @@ -0,0 +1,277 @@ +.\" Copyright (c) 1980, 1991, 1993 +.\"	The Regents of the University of California.  All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\"    notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\"    notice, this list of conditions and the following disclaimer in the +.\"    documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\"    may be used to endorse or promote products derived from this software +.\"    without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd April 9, 2021 +.Dt CONFIG 8 +.Os +.Sh NAME +.Nm config +.Nd build system configuration files +.Sh SYNOPSIS +.Nm +.Op Fl CVgpv +.Op Fl I Ar path +.Op Fl d Ar destdir +.Op Fl s Ar srcdir +.Ar SYSTEM_NAME +.Nm +.Op Fl x Ar kernel +.Sh DESCRIPTION +The +.Nm +utility builds a set of system configuration files from the file +.Ar SYSTEM_NAME +which describes +the system to configure. +A second file +tells +.Nm +what files are needed to generate a system and +can be augmented by configuration specific set of files +that give alternate files for a specific machine +(see the +.Sx FILES +section below). +.Pp +Available options and operands: +.Bl -tag -width "SYSTEM_NAME" +.It Fl V +Print the +.Nm +version number. +.It Fl C +If the INCLUDE_CONFIG_FILE is present in a configuration file, +kernel image will contain full configuration files included +literally (preserving comments). +This flag is kept for backward compatibility. +.It Fl I Ar path +Search in +.Ar path +for any file included by the +.Ic include +directive. +This option may be specified more than once. +.It Fl d Ar destdir +Use +.Ar destdir +as the output directory, instead of the default one. +Note that +.Nm +does not append +.Ar SYSTEM_NAME +to the directory given. +.It Fl s Ar srcdir +Use +.Ar srcdir +as the source directory, instead of the default one. +.It Fl m +Print the MACHINE and MACHINE_ARCH values for this +kernel and exit. +.It Fl g +Configure a system for debugging. +.It Fl x Ar kernel +Print kernel configuration file embedded into a kernel +file. +This option makes sense only if +.Cd "options INCLUDE_CONFIG_FILE" +entry was present in your configuration file. +.It Fl v +Turns on verbose output. +.It Ar SYSTEM_NAME +Specify the name of the system configuration file +containing device specifications, configuration options +and other system parameters for one system configuration. +.El +.Pp +The +.Nm +utility should be run from the +.Pa conf +subdirectory of the system source (usually +.Pa /sys/ Ns Va ARCH Ns Pa /conf ) , +where +.Va ARCH +represents one of the architectures supported by +.Fx . +The +.Nm +utility creates the directory +.Pa ../compile/ Ns Ar SYSTEM_NAME +or the one given with the +.Fl d +option +as necessary and places all output files there. +The output of +.Nm +consists of a number of files; for the +.Tn i386 , +they are: +.Pa Makefile , +used by +.Xr make 1 +in building the system; +header files, +definitions of +the number of various devices that will be compiled into the system. +.Pp +The +.Nm +utility looks for kernel sources in the directory +.Pa ../.. +or the one given with the +.Fl s +option. +.Pp +After running +.Nm , +it is necessary to run +.Dq Li make depend +in the directory where the new makefile +was created. +The +.Nm +utility prints a reminder of this when it completes. +.Pp +If any other error messages are produced by +.Nm , +the problems in the configuration file should be corrected and +.Nm +should be run again. +Attempts to compile a system that had configuration errors +are likely to fail. +.Sh DEBUG KERNELS +Traditional +.Bx +kernels are compiled without symbols due to the heavy load on the +system when compiling a +.Dq debug +kernel. +A debug kernel contains complete symbols for all the source files, and +enables an experienced kernel programmer to analyse the cause of a problem. +The +debuggers available prior to +.Bx 4.4 Lite +were able to find some information +from a normal kernel; +.Xr gdb 1 Pq Pa ports/devel/gdb +provides very little support for normal kernels, and a debug kernel is needed +for any meaningful analysis. +.Pp +For reasons of history, time and space, building a debug kernel is not the +default with +.Fx : +a debug kernel takes up to 30% longer to build and +requires about 30 MB of disk storage in the build directory, compared to about 6 +MB for a non-debug kernel. +A debug kernel is about 11 MB in size, compared to +about 2 MB for a non-debug kernel. +This space is used both in the root file +system and at run time in memory. +Use the +.Fl g +option to build a debug kernel. +With this option, +.Nm +causes two kernel files to be built in the kernel build directory: +.Bl -bullet +.It +.Pa kernel.debug +is the complete debug kernel. +.It +.Pa kernel +is a copy of the kernel with the debug symbols stripped off. +This is equivalent +to the normal non-debug kernel. +.El +.Pp +There is currently little sense in installing and booting from a debug kernel, +since the only tools available which use the symbols do not run on-line. +There +are therefore two options for installing a debug kernel: +.Bl -bullet +.It +.Dq Li "make install" +installs +.Pa kernel +in the root file system. +.It +.Dq Li "make install.debug" +installs +.Pa kernel.debug +in the root file system. +.El +.Sh FILES +.Bl -tag -width "/sys/ARCH/compile/SYSTEM_NAME" -compact +.It Pa /sys/conf/files +list of common files system is built from +.It Pa /sys/conf/Makefile . Ns Va ARCH +generic makefile for the +.Va ARCH +.It Pa /sys/conf/files . Ns Va ARCH +list of +.Va ARCH +specific files +.It Pa /sys/ Ns Va ARCH Ns Pa /compile/ Ns Ar SYSTEM_NAME +default kernel build directory for system +.Ar SYSTEM_NAME +on +.Va ARCH . +.El +.Sh SEE ALSO +.Xr config 5 +.Pp +The +.Sx SYNOPSIS +portion of each device in section 4. +.Rs +.\" 4.4BSD SMM:2 +.%A S. J. Leffler +.%A M. J. Karels +.%T "Building 4.3 BSD UNIX System with Config" +.%B 4.4BSD System Manager's Manual (SMM) +.Re +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.1 . +.Pp +Before support for +.Fl x +was introduced, +.Cd "options INCLUDE_CONFIG_FILE" +included entire configuration file that used to be embedded in +the new kernel. +This meant that +.Xr strings 1 +could be used to extract it from a kernel: +to extract the configuration information, you had to use +the command: +.Pp +.Dl "strings -n 3 kernel | sed -n 's/^___//p'" +.Sh BUGS +The line numbers reported in error messages are usually off by one. diff --git a/usr.sbin/config/config.h b/usr.sbin/config/config.h new file mode 100644 index 000000000000..6085c52d1b95 --- /dev/null +++ b/usr.sbin/config/config.h @@ -0,0 +1,278 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1980, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Config. + */ +#include <sys/types.h> +#include <sys/queue.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#ifdef __cplusplus +#include <string> + +class configword { +private: +	std::string	cw_word; +	bool		cw_eof; +	bool		cw_eol; +public: +	configword() : cw_word(""), cw_eof(false), cw_eol(false) {} +	configword(std::string &&word) : cw_word(word), cw_eof(false), cw_eol(false) {} + +	bool eof() const { +		return (cw_eof); +	} + +	bool eol() const { +		return (cw_eol); +	} + +	configword &eof(bool eof) { +		cw_eof = eof; +		return (*this); +	} + +	configword &eol(bool eol) { +		cw_eol = eol; +		return (*this); +	} + +	char operator[](int idx) { +		return (cw_word[idx]); +	} + +	operator const char*() const { +		return (cw_word.c_str()); +	} + +	const std::string &operator*() const { +		return (cw_word); +	} + +	const std::string *operator->() const { +		return (&cw_word); +	} +}; + +/* + * Is it ugly to limit these to C++ files? Yes. + */ +configword get_word(FILE *); +configword get_quoted_word(FILE *); +#endif + +__BEGIN_DECLS + +struct cfgfile { +	STAILQ_ENTRY(cfgfile)	cfg_next; +	char	*cfg_path; +}; +extern STAILQ_HEAD(cfgfile_head, cfgfile) cfgfiles; + +struct file_list { +	STAILQ_ENTRY(file_list) f_next; +	char	*f_fn;			/* the name */ +	int     f_type;                 /* type */ +	u_char	f_flags;		/* see below */ +	char	*f_compilewith;		/* special make rule if present */ +	char	*f_depends;		/* additional dependencies */ +	char	*f_clean;		/* File list to add to clean rule */ +	char	*f_warn;		/* warning message */ +	const char *f_objprefix;	/* prefix string for object name */ +	const char *f_srcprefix;	/* source prefix such as $S/ */ +}; + +struct files_name { +	char *f_name; +	STAILQ_ENTRY(files_name) f_next; +}; + +/* + * Types. + */ +#define NORMAL		1 +#define NODEPEND	4 +#define LOCAL		5 +#define DEVDONE		0x80000000 +#define TYPEMASK	0x7fffffff + +/* + * Attributes (flags). + */ +#define NO_IMPLCT_RULE	1 +#define NO_OBJ		2 +#define BEFORE_DEPEND	4 +#define NOWERROR	16 +#define NO_CTFCONVERT	32 + +struct device { +	int	d_done;			/* processed */ +	char	*d_name;		/* name of device (e.g. rk11) */ +	char	*yyfile;		/* name of the file that first include the device */ +#define	UNKNOWN -2	/* -2 means not set yet */ +	STAILQ_ENTRY(device) d_next;	/* Next one in list */ +}; + +struct config { +	char	*s_sysname; +}; + +/* + * Config has a global notion of which machine type is + * being used.  It uses the name of the machine in choosing + * files and directories.  Thus if the name of the machine is ``i386'', + * it will build from ``Makefile.i386'' and use ``../i386/inline'' + * in the makerules, etc.  machinearch is the global notion of the + * MACHINE_ARCH for this MACHINE. + */ +extern char	*machinename; +extern char	*machinearch; + +/* + * For each machine, a set of CPU's may be specified as supported. + * These and the options (below) are put in the C flags in the makefile. + */ +struct cputype { +	char	*cpu_name; +	SLIST_ENTRY(cputype) cpu_next; +}; + +extern SLIST_HEAD(cputype_head, cputype) cputype; + +/* + * A set of options may also be specified which are like CPU types, + * but which may also specify values for the options. + * A separate set of options may be defined for make-style options. + */ +struct opt { +	char	*op_name; +	char	*op_value; +	int	op_ownfile;	/* true = own file, false = makefile */ +	char	*yyfile;	/* name of the file that first include the option */ +	SLIST_ENTRY(opt) op_next; +	SLIST_ENTRY(opt) op_append; +}; + +extern SLIST_HEAD(opt_head, opt) opt, mkopt, rmopts; + +struct opt_list { +	char *o_name; +	char *o_file; +	int o_flags; +#define OL_ALIAS	1 +	SLIST_ENTRY(opt_list) o_next; +}; + +extern SLIST_HEAD(opt_list_head, opt_list) otab; + +struct envvar { +	char	*env_str; +	bool	env_is_file; +	STAILQ_ENTRY(envvar) envvar_next; +}; + +extern STAILQ_HEAD(envvar_head, envvar) envvars; + +struct hint { +	char	*hint_name; +	STAILQ_ENTRY(hint) hint_next; +}; + +extern STAILQ_HEAD(hint_head, hint) hints; + +struct includepath { +	char	*path; +	SLIST_ENTRY(includepath) path_next; +}; + +extern SLIST_HEAD(includepath_head, includepath) includepath; + +/* + * Tag present in the kernconf.tmpl template file. It's mandatory for those + * two strings to be the same. Otherwise you'll get into trouble. + */ +#define	KERNCONFTAG	"%%KERNCONFFILE%%" + +/* + * Faked option to note, that the configuration file has been taken from the + * kernel file and inclusion of DEFAULTS etc.. isn't nessesery, because we + * already have a list of all required devices. + */ +#define OPT_AUTOGEN	"CONFIG_AUTOGENERATED" + +extern char	*ident; +extern char	kernconfstr[]; +extern int	do_trace; +extern int	incignore; + +char	*path(const char *); +char	*raisestr(char *); +void	remember(const char *); +void	moveifchanged(const char *, const char *); +int	yylex(void); +int	yyparse(void); +void	options(void); +void	makefile(void); +void	makeenv(void); +void	makehints(void); +void	headers(void); +void	cfgfile_add(const char *); +void	cfgfile_removeall(void); +FILE	*open_makefile_template(void); + +extern STAILQ_HEAD(device_head, device) dtab; + +extern char	errbuf[80]; +extern int	yyline; +extern const	char *yyfile; + +extern STAILQ_HEAD(file_list_head, file_list) ftab; + +extern STAILQ_HEAD(files_name_head, files_name) fntab; +extern STAILQ_HEAD(options_files_name_head, files_name) optfntab; + +extern int	debugging; +extern int	found_defaults; +extern int	verbose; + +extern int	maxusers; +extern int	versreq; + +extern char *PREFIX;		/* Config file name - for error messages */ +extern char srcdir[];		/* root of the kernel source tree */ + +__END_DECLS; + +#define eq(a,b)	(!strcmp(a,b)) +#define ns(s)	strdup(s) diff --git a/usr.sbin/config/config.y b/usr.sbin/config/config.y new file mode 100644 index 000000000000..15139197dbb6 --- /dev/null +++ b/usr.sbin/config/config.y @@ -0,0 +1,503 @@ +%union { +	char	*str; +	int	val; +	struct	file_list *file; +} + +%token	ARCH +%token	COMMA +%token	CONFIG +%token	CPU +%token	NOCPU +%token	DEVICE +%token	NODEVICE +%token	ENV +%token	ENVVAR +%token	EQUALS +%token	PLUSEQUALS +%token	HINTS +%token	IDENT +%token	MAXUSERS +%token	OPTIONS +%token	NOOPTION +%token	MAKEOPTIONS +%token	NOMAKEOPTION  +%token	SEMICOLON +%token	INCLUDE +%token	INCLUDEOPTIONS +%token	FILES + +%token	<str>	ENVLINE +%token	<str>	ID +%token	<val>	NUMBER + +%type	<str>	Save_id +%type	<str>	Opt_value +%type	<str>	Dev +%token	<str>	PATH + +%{ + +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1988, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <string.h> + +#include "config.h" + +struct	device_head dtab; +char	*ident; +char	*env; +int	yyline; +const	char *yyfile; +struct  file_list_head ftab; +struct  files_name_head fntab; +struct	options_files_name_head optfntab; +char	errbuf[80]; +int	maxusers; + +#define ns(s)	strdup(s) +int include(const char *, int); +int yyerror(const char *s); +int yywrap(void); + +static void newdev(char *name); +static void newfile(char *name); +static void newoptionsfile(char *name); +static void newenvvar(char *name, bool is_file); +static void rmdev_schedule(struct device_head *dh, char *name); +static void newopt(struct opt_head *list, char *name, char *value, int append, int dupe); +static void rmopt_schedule(struct opt_head *list, char *name); + +static char * +devopt(char *dev) +{ +	char *ret = malloc(strlen(dev) + 5); +	 +	sprintf(ret, "DEV_%s", dev); +	raisestr(ret); +	return ret; +} + +%} +%% +Configuration: +	Many_specs +		; + +Many_specs: +	Many_specs Spec +		| +	/* lambda */ +		; + +Spec: +	Device_spec SEMICOLON +		| +	Config_spec SEMICOLON +		| +	INCLUDE PATH SEMICOLON { +		if (incignore == 0) +			include($2, 0); +		}; +		| +	INCLUDE ID SEMICOLON { +	          if (incignore == 0) +		  	include($2, 0); +		}; +		| +	INCLUDEOPTIONS PATH SEMICOLON { newoptionsfile($2); }; +		| +	INCLUDEOPTIONS ID SEMICOLON { newoptionsfile($2); }; +		| +	FILES ID SEMICOLON { newfile($2); }; +	        | +	SEMICOLON +		| +	error SEMICOLON +		; + +Config_spec: +	ARCH Save_id { +		if (machinename != NULL && !eq($2, machinename)) +		    errx(1, "%s:%d: only one machine directive is allowed", +			yyfile, yyline); +		machinename = $2; +		machinearch = $2; +	      } | +	ARCH Save_id Save_id { +		/* +		 * Allow the machinearch to change with a second machine directive, +		 * but still enforce no changes to the machinename. +		 */ +		if (machinename != NULL && !eq($2, machinename)) +		    errx(1, "%s:%d: only one machine directive is allowed", +			yyfile, yyline); +		machinename = $2; +		machinearch = $3; +	      } | +	CPU Save_id { +		struct cputype *cp = +		    (struct cputype *)calloc(1, sizeof (struct cputype)); +		if (cp == NULL) +			err(EXIT_FAILURE, "calloc"); +		cp->cpu_name = $2; +		SLIST_INSERT_HEAD(&cputype, cp, cpu_next); +	      } | +	NOCPU Save_id { +		struct cputype *cp, *cp2; +		SLIST_FOREACH_SAFE(cp, &cputype, cpu_next, cp2) { +			if (eq(cp->cpu_name, $2)) { +				SLIST_REMOVE(&cputype, cp, cputype, cpu_next); +				free(cp); +			} +		} +	      } | +	OPTIONS Opt_list +		| +	NOOPTION NoOpt_list | +	MAKEOPTIONS Mkopt_list +		| +	NOMAKEOPTION Save_id { rmopt_schedule(&mkopt, $2); } | +	IDENT ID { ident = $2; } | +	MAXUSERS NUMBER { maxusers = $2; } | +	ENV ID { newenvvar($2, true); } | +	ENVVAR ENVLINE { newenvvar($2, false); } | +	HINTS ID { +		struct hint *hint; + +		hint = (struct hint *)calloc(1, sizeof (struct hint)); +		if (hint == NULL) +			err(EXIT_FAILURE, "calloc");	 +		hint->hint_name = $2; +		STAILQ_INSERT_HEAD(&hints, hint, hint_next); +	        } + +System_id: +	Save_id { newopt(&mkopt, ns("KERNEL"), $1, 0, 0); }; + +System_parameter_list: +	  System_parameter_list ID +	| ID +	; + +Opt_list: +	Opt_list COMMA Option +		| +	Option +		; + +NoOpt_list: +	NoOpt_list COMMA NoOption +	  	| +	NoOption +		; +Option: +	Save_id { +		newopt(&opt, $1, NULL, 0, 1); +		if (strchr($1, '=') != NULL) +			errx(1, "%s:%d: The `=' in options should not be " +			    "quoted", yyfile, yyline); +	      } | +	Save_id EQUALS Opt_value { +		newopt(&opt, $1, $3, 0, 1); +	      } ; + +NoOption: +	Save_id { +		rmopt_schedule(&opt, $1); +		}; + +Opt_value: +	ID { $$ = $1; } | +	NUMBER { +			char buf[80]; + +			(void) snprintf(buf, sizeof(buf), "%d", $1); +			$$ = ns(buf); +		} ; + +Save_id: +	ID { $$ = $1; } +	; + +Mkopt_list: +	Mkopt_list COMMA Mkoption +		| +	Mkoption +		; + +Mkoption: +	Save_id { newopt(&mkopt, $1, ns(""), 0, 0); } | +	Save_id EQUALS { newopt(&mkopt, $1, ns(""), 0, 0); } | +	Save_id EQUALS Opt_value { newopt(&mkopt, $1, $3, 0, 0); } | +	Save_id PLUSEQUALS Opt_value { newopt(&mkopt, $1, $3, 1, 0); } ; + +Dev: +	ID { $$ = $1; } +	; + +Device_spec: +	DEVICE Dev_list +		| +	NODEVICE NoDev_list +		; + +Dev_list: +	Dev_list COMMA Device +		| +	Device +		; + +NoDev_list: +	NoDev_list COMMA NoDevice +		| +	NoDevice +		; + +Device: +	Dev { +		newopt(&opt, devopt($1), ns("1"), 0, 0); +		/* and the device part */ +		newdev($1); +		} + +NoDevice: +	Dev { +		char *s = devopt($1); + +		rmopt_schedule(&opt, s); +		free(s); +		/* and the device part */ +		rmdev_schedule(&dtab, $1); +		} ; + +%% + +int +yyerror(const char *s) +{ + +	errx(1, "%s:%d: %s", yyfile, yyline + 1, s); +} + +int +yywrap(void) +{ +	if (found_defaults) { +		if (freopen(PREFIX, "r", stdin) == NULL) +			err(2, "%s", PREFIX);		 +		yyfile = PREFIX; +		yyline = 0; +		found_defaults = 0; +		return 0; +	} +	return 1; +} + +/* + * Add a new file to the list of files. + */ +static void +newfile(char *name) +{ +	struct files_name *nl; +	 +	nl = (struct files_name *) calloc(1, sizeof *nl); +	if (nl == NULL) +		err(EXIT_FAILURE, "calloc"); +	nl->f_name = name; +	STAILQ_INSERT_TAIL(&fntab, nl, f_next); +} + +/* + * Add a new options file to the list of options files. + */ +static void +newoptionsfile(char *name) +{ +	struct files_name *nl; + +	nl = (struct files_name *) calloc(1, sizeof *nl); +	if (nl == NULL) +		err(EXIT_FAILURE, "calloc"); +	nl->f_name = name; +	STAILQ_INSERT_TAIL(&optfntab, nl, f_next); +} + +static void +newenvvar(char *name, bool is_file) +{ +	struct envvar *envvar; + +	envvar = (struct envvar *)calloc(1, sizeof (struct envvar)); +	if (envvar == NULL) +		err(EXIT_FAILURE, "calloc"); +	envvar->env_str = name; +	envvar->env_is_file = is_file; +	STAILQ_INSERT_HEAD(&envvars, envvar, envvar_next); +} + +/* + * Find a device in the list of devices. + */ +static struct device * +finddev(struct device_head *dlist, char *name) +{ +	struct device *dp; + +	STAILQ_FOREACH(dp, dlist, d_next) +		if (eq(dp->d_name, name)) +			return (dp); + +	return (NULL); +} +	 +/* + * Add a device to the list of devices. + */ +static void +newdev(char *name) +{ +	struct device *np, *dp; + +	if ((dp = finddev(&dtab, name)) != NULL) { +		if (strcmp(dp->yyfile, yyfile) == 0) +			fprintf(stderr, +			    "WARNING: duplicate device `%s' encountered in %s\n", +			    name, yyfile); +		return; +	} + +	np = (struct device *) calloc(1, sizeof *np); +	if (np == NULL) +		err(EXIT_FAILURE, "calloc"); +	np->d_name = name; +	np->yyfile = strdup(yyfile); +	STAILQ_INSERT_TAIL(&dtab, np, d_next); +} + +/* + * Schedule a device to removal. + */ +static void +rmdev_schedule(struct device_head *dh, char *name) +{ +	struct device *dp; + +	dp = finddev(dh, name); +	if (dp != NULL) { +		STAILQ_REMOVE(dh, dp, device, d_next); +		free(dp->yyfile); +		free(dp->d_name); +		free(dp); +	} +} + +/* + * Find an option in the list of options. + */ +static struct opt * +findopt(struct opt_head *list, char *name) +{ +	struct opt *op; + +	SLIST_FOREACH(op, list, op_next) +		if (eq(op->op_name, name)) +			return (op); + +	return (NULL); +} + +/* + * Add an option to the list of options. + */ +static void +newopt(struct opt_head *list, char *name, char *value, int append, int dupe) +{ +	struct opt *op, *op2; + +	/* +	 * Ignore inclusions listed explicitly for configuration files. +	 */ +	if (eq(name, OPT_AUTOGEN)) { +		incignore = 1; +		return; +	} + +	op2 = findopt(list, name); +	if (op2 != NULL && !append && !dupe) { +		if (strcmp(op2->yyfile, yyfile) == 0) +			fprintf(stderr, +			    "WARNING: duplicate option `%s' encountered.\n", name); +		return; +	} + +	op = (struct opt *)calloc(1, sizeof (struct opt)); +	if (op == NULL) +		err(EXIT_FAILURE, "calloc"); +	op->op_name = name; +	op->op_ownfile = 0; +	op->op_value = value; +	op->yyfile = strdup(yyfile); +	if (op2 != NULL) { +		if (append) { +			while (SLIST_NEXT(op2, op_append) != NULL) +				op2 = SLIST_NEXT(op2, op_append); +			SLIST_NEXT(op2, op_append) = op; +		} else { +			while (SLIST_NEXT(op2, op_next) != NULL) +				op2 = SLIST_NEXT(op2, op_next); +			SLIST_NEXT(op2, op_next) = op; +		} +	} else +		SLIST_INSERT_HEAD(list, op, op_next); +} + +/* + * Remove an option from the list of options. + */ +static void +rmopt_schedule(struct opt_head *list, char *name) +{ +	struct opt *op; + +	while ((op = findopt(list, name)) != NULL) { +		SLIST_REMOVE(list, op, opt, op_next); +		free(op->yyfile); +		free(op->op_name); +		free(op); +	} +} diff --git a/usr.sbin/config/configvers.h b/usr.sbin/config/configvers.h new file mode 100644 index 000000000000..5d73e1064eda --- /dev/null +++ b/usr.sbin/config/configvers.h @@ -0,0 +1,52 @@ +/*- + * This file is in the public domain + */ + +/* + * 6 digits of version.  The most significant are branch indicators at the + * time when the last incompatible change was made (which is why it is + * presently 6 on 7-current).  The least significant digits are incremented + * as described below.  The format is similar to the __FreeBSD_version, but + * not tied to it. + * + * DO NOT CASUALLY BUMP THIS NUMBER!  The rules are not the same as shared + * libs or param.h/osreldate. + * + * It is the version number of the protocol between config(8) and the + * sys/conf/ Makefiles (the kernel build system). + * + * It is now also used to trap certain problems that the syntax parser cannot + * detect. + * + * Unfortunately, there is no version number for user supplied config files. + * + * Once, config(8) used to silently report errors and continue anyway.  This + * was a huge problem for 'make buildkernel' which was run with the installed + * /usr/sbin/config, not a cross built one.  We started bumping the version + * number as a way to trap cases where the previous installworld was not + * compatible with the new buildkernel.  The buildtools phase and much more + * comprehensive error code returns solved this original problem. + * + * Most end-users will use buildkernel and the build tools from buildworld. + * The people that are inconvenienced by gratuitous bumps are developers + * who run config by hand.  However, developers shouldn't gratuitously be + * inconvenienced. + * + * One should bump the CONFIGVERS in the following ways: + * + * (1) If you change config such that it won't read old config files, + *     then bump the major number.  You shouldn't be doing this unless + *     you are overhauling config.  Do not casually bump this number + *     and by implication do not make changes that would force a bump + *     of this number casually.  You should limit major bumps to once + *     per branch. + * (2) For each new feature added, bump the minor version of this file. + *     When a new feature is actually used by the build system, update the + *     %VERSREQ field in the Makefile.$ARCH of all the affected makefiles + *     (typically all of them). + */ +#define	CONFIGVERS	600018 +#define	MAJOR_VERS(x)	((x) / 100000) + +/* Last config(8) version to require envmode/hintmode */ +#define	CONFIGVERS_ENVMODE_REQ	600015 diff --git a/usr.sbin/config/kernconf.tmpl b/usr.sbin/config/kernconf.tmpl new file mode 100644 index 000000000000..ce3437677092 --- /dev/null +++ b/usr.sbin/config/kernconf.tmpl @@ -0,0 +1,15 @@ +/* + * This file acts as a template for config.c that will be generated in the + * kernel build directory after config(8) has been successfully run. + */ +#include "opt_config.h" +#ifdef INCLUDE_CONFIG_FILE + +/* + * For !INCLUDE_CONFIG_FILE case, you should look at kern_mib.c. This is + * where kernconfstring is defined then. + */ +const char kernconfstring[] __attribute__ ((section("kern_conf"))) = +"%%KERNCONFFILE%%"; + +#endif /* INCLUDE_CONFIG_FILE */ diff --git a/usr.sbin/config/lang.l b/usr.sbin/config/lang.l new file mode 100644 index 000000000000..2052d98d1fdd --- /dev/null +++ b/usr.sbin/config/lang.l @@ -0,0 +1,336 @@ +%{ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1980, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <ctype.h> +#include <err.h> +#include <stdlib.h> +#include <string.h> +#include "y.tab.h" +#include "config.h" + +/* + * Data for returning to previous files from include files. + */ +struct incl { +	struct	incl *in_prev; 	/* previous includes in effect, if any */ +	YY_BUFFER_STATE in_buf;	/* previous lex state */ +	const	char *in_fname;	/* previous file name */ +	int	in_lineno;	/* previous line number */ +	int	in_ateof;	/* token to insert at EOF */ +}; +static struct	incl *inclp; +static const	char *lastfile; + +/* + * Key word table + */ + +struct kt { +	const char *kt_name; +	int kt_val; +} key_words[] = { +	{ "cpu",	CPU }, +	{ "nocpu",	NOCPU }, +	{ "device",	DEVICE }, +	{ "devices",	DEVICE }, +	{ "nodevice",	NODEVICE }, +	{ "nodevices",	NODEVICE }, +	{ "env",	ENV }, +	{ "envvar",	ENVVAR }, +	{ "hints",	HINTS }, +	{ "ident",	IDENT }, +	{ "machine",	ARCH }, /* MACHINE is defined in /sys/param.h */ +	{ "makeoption",	MAKEOPTIONS }, +	{ "makeoptions", MAKEOPTIONS }, +	{ "nomakeoption", NOMAKEOPTION }, +	{ "nomakeoptions", NOMAKEOPTION }, +	{ "maxusers",	MAXUSERS }, +	{ "option",	OPTIONS }, +	{ "options",	OPTIONS }, +	{ "nooption",	NOOPTION }, +	{ "nooptions",	NOOPTION }, +	{ "include",	INCLUDE }, +	{ "includeoptions", INCLUDEOPTIONS }, +	{ "files", 	FILES }, +	{ 0, 0 }, +}; + + +static int endinclude(void); +int include(const char *, int); +int kw_lookup(char *); +unsigned int octal(const char *); +unsigned int hex(const char *); +int yyerror(const char *); + +#define YY_DECL int yylex(void) +%} + +%option nounput +%option noinput + +ID	[A-Za-z_][-A-Za-z_0-9]* +PATH	[./][-/.%^A-Za-z_0-9]+ +%START TOEOL +%START ENVC +%% +<ENVC>[^#\n]*	{ +			BEGIN 0; +			yylval.str = strdup(yytext); +			return ENVLINE; +		} +{ID}		{ +			int i; + +			BEGIN 0; +			if ((i = kw_lookup(yytext)) == -1) +			{ +				yylval.str = strdup(yytext); +				return ID; +			} +			/* We'll glom onto the rest of an envvar line */ +			if (i == ENVVAR) +				BEGIN ENVC; +			return i; +		} +\\\"[^"]*\\\"	{ +			BEGIN 0; +			yytext[yyleng-2] = '"'; +			yytext[yyleng-1] = '\0'; +			yylval.str = strdup(yytext + 1); +			return ID; +		} +\"[^"]+\"	{ +			BEGIN 0; +			yytext[yyleng-1] = '\0'; +			yylval.str = strdup(yytext + 1); +			return ID; +		} +<TOEOL>[^# \t\n]*	{ +			BEGIN 0; +			yylval.str = strdup(yytext); +			return ID; +		} +0[0-7]*		{ +			yylval.val = octal(yytext); +			return NUMBER; +		} +0x[0-9a-fA-F]+	{ +			yylval.val = hex(yytext); +			return NUMBER; +		} +-?[1-9][0-9]*	{ +			yylval.val = atoi(yytext); +			return NUMBER; +		} +"?"		{ +			yylval.val = -1; +			return NUMBER; +		} +\n/[ \t]	{ +			yyline++; +		} +\n		{ +			yyline++; +			return SEMICOLON; +		} +#.*		{	/* Ignored (comment) */;	} +[ \t\f]*	{	/* Ignored (white space) */;	} +";"		{	return SEMICOLON;		} +","		{	return COMMA;			} +"="		{	BEGIN TOEOL; return EQUALS;	} +"+="		{	BEGIN TOEOL; return PLUSEQUALS;	} +<<EOF>>		{ +			int tok; + +			if (inclp == NULL) +				return YY_NULL; +			tok = endinclude(); +			if (tok != 0) +				return tok; +			/* otherwise continue scanning */ +		} +{PATH}		{ +			BEGIN 0; +			yylval.str = strdup(yytext); +			return PATH; +		} +.		{	return yytext[0];		} + +%% +/* + * kw_lookup + *	Look up a string in the keyword table.  Returns a -1 if the + *	string is not a keyword otherwise it returns the keyword number + */ + +int +kw_lookup(char *word) +{ +	struct kt *kp; + +	for (kp = key_words; kp->kt_name != 0; kp++) +		if (eq(word, kp->kt_name)) +			return kp->kt_val; +	return -1; +} + +/* + * Number conversion routines + */ + +unsigned int +octal(const char *str) +{ +	unsigned int num; + +	(void) sscanf(str, "%o", &num); +	return num; +} + +unsigned int +hex(const char *str) +{ +	unsigned int num; + +	(void) sscanf(str+2, "%x", &num); +	return num; +} + +void +cfgfile_add(const char *fname) +{ +	struct cfgfile *cf; + +	cf = calloc(1, sizeof(*cf)); +	if (cf == NULL) +		err(EXIT_FAILURE, "calloc"); +	assert(cf != NULL); +	asprintf(&cf->cfg_path, "%s", fname); +	STAILQ_INSERT_TAIL(&cfgfiles, cf, cfg_next); +} + +void +cfgfile_removeall(void) +{ +	struct cfgfile *cf; + +	while (!STAILQ_EMPTY(&cfgfiles)) { +		cf = STAILQ_FIRST(&cfgfiles); +		STAILQ_REMOVE_HEAD(&cfgfiles, cfg_next); +		if (cf->cfg_path != NULL) +			free(cf->cfg_path); +		free(cf); +	} +} + +/* + * Open the named file for inclusion at the current point.  Returns 0 on + * success (file opened and previous state pushed), nonzero on failure + * (fopen failed, complaint made).  The `ateof' parameter controls the + * token to be inserted at the end of the include file. If ateof == 0, + * then nothing is inserted. + */ +int +include(const char *fname, int ateof) +{ +	FILE *fp; +	struct incl *in; +	struct includepath* ipath; +	char *fnamebuf; + +	fnamebuf = NULL; +	fp = fopen(fname, "r"); +	if (fp == NULL && fname[0] != '.' && fname[0] != '/') { +		asprintf(&fnamebuf, "../../conf/%s", fname); +		if (fnamebuf != NULL) { +			fp = fopen(fnamebuf, "r"); +			if (fp == NULL) +				free(fnamebuf); +		} +	} +	if (fp == NULL) { +		SLIST_FOREACH(ipath, &includepath, path_next) { +			asprintf(&fnamebuf, "%s/%s", ipath->path, fname); +			if (fnamebuf != NULL) { +				fp = fopen(fnamebuf, "r"); +			} +			if (fp != NULL) +				break; +			free(fnamebuf); +		} +	} +	if (fp == NULL) { +		yyerror("cannot open included file"); +		return (-1); +	} +	cfgfile_add(fnamebuf == NULL ? fname : fnamebuf); +	free(fnamebuf); + +	in = malloc(sizeof(*in)); +	assert(in != NULL); +	in->in_prev = inclp; +	in->in_buf = YY_CURRENT_BUFFER; +	in->in_fname = yyfile; +	in->in_lineno = yyline; +	in->in_ateof = ateof; +	inclp = in; +	yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE)); +	yyfile = fname; +	yyline = 0; +	return (0); +} + +/* + * Terminate the most recent inclusion. + */ +static int +endinclude(void) +{ +	struct incl *in; +	int ateof; + +	in = inclp; +	assert(in != NULL); +	inclp = in->in_prev; +	lastfile = yyfile; +	yy_delete_buffer(YY_CURRENT_BUFFER); +	(void)fclose(yyin); +	yy_switch_to_buffer(in->in_buf); +	yyfile = in->in_fname; +	yyline = in->in_lineno; +	ateof  = in->in_ateof; +	free(in); + +	return (ateof); +} diff --git a/usr.sbin/config/main.cc b/usr.sbin/config/main.cc new file mode 100644 index 000000000000..1e89cbf3230a --- /dev/null +++ b/usr.sbin/config/main.cc @@ -0,0 +1,819 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1980, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/mman.h> +#include <sys/param.h> + +#include <assert.h> +#include <ctype.h> +#include <dirent.h> +#include <err.h> +#include <iostream> +#include <sstream> +#include <stdio.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> + +#include "y.tab.h" +#include "config.h" +#include "configvers.h" + +#ifndef TRUE +#define TRUE	(1) +#endif + +#ifndef FALSE +#define FALSE	(0) +#endif + +#define	CDIR	"../compile/" + +char	*machinename; +char	*machinearch; + +struct cfgfile_head	cfgfiles; +struct cputype_head	cputype; +struct opt_head		opt, mkopt, rmopts; +struct opt_list_head	otab; +struct envvar_head	envvars; +struct hint_head	hints; +struct includepath_head	includepath; + +char *	PREFIX; +char 	destdir[MAXPATHLEN]; +char 	srcdir[MAXPATHLEN]; + +int	debugging; +int	found_defaults; +int	incignore; +int	verbose; + +/* + * Preserve old behaviour in INCLUDE_CONFIG_FILE handling (files are included + * literally). + */ +int	filebased = 0; +int	versreq; + +static void configfile(void); +static void get_srcdir(void); +static void usage(void); +static void cleanheaders(char *); +static void kernconfdump(const char *); +static void badversion(void); +static void checkversion(void); + +struct hdr_list { +	const char *h_name; +	struct hdr_list *h_next; +} *htab; + +static std::stringstream line_buf; + +/* + * Config builds a set of files for building a UNIX + * system given a description of the desired system. + */ +int +main(int argc, char **argv) +{ + +	struct stat buf; +	int ch, len; +	char *p; +	char *kernfile; +	struct includepath* ipath; +	int printmachine; +	bool cust_dest = false; + +	printmachine = 0; +	kernfile = NULL; +	SLIST_INIT(&includepath); +	SLIST_INIT(&cputype); +	SLIST_INIT(&mkopt); +	SLIST_INIT(&opt); +	SLIST_INIT(&rmopts); +	STAILQ_INIT(&cfgfiles); +	STAILQ_INIT(&dtab); +	STAILQ_INIT(&fntab); +	STAILQ_INIT(&ftab); +	STAILQ_INIT(&hints); +	STAILQ_INIT(&envvars); +	STAILQ_INIT(&optfntab); +	while ((ch = getopt(argc, argv, "Cd:gI:mps:Vvx:")) != -1) +		switch (ch) { +		case 'C': +			filebased = 1; +			break; +		case 'd': +			if (*destdir == '\0') +				strlcpy(destdir, optarg, sizeof(destdir)); +			else +				errx(EXIT_FAILURE, "directory already set"); +			cust_dest = true; +			break; +		case 'g': +			debugging++; +			break; +		case 'I': +			ipath = (struct includepath *) \ +			    	calloc(1, sizeof (struct includepath)); +			if (ipath == NULL) +				err(EXIT_FAILURE, "calloc"); +			ipath->path = optarg; +			SLIST_INSERT_HEAD(&includepath, ipath, path_next); +			break; +		case 'm': +			printmachine = 1; +			break; +		case 's': +			if (*srcdir == '\0') +				strlcpy(srcdir, optarg, sizeof(srcdir)); +			else +				errx(EXIT_FAILURE, "src directory already set"); +			break; +		case 'V': +			printf("%d\n", CONFIGVERS); +			exit(0); +		case 'v': +			verbose++; +			break; +		case 'x': +			kernfile = optarg; +			break; +		case '?': +		default: +			usage(); +		} +	argc -= optind; +	argv += optind; + +	if (kernfile != NULL) { +		kernconfdump(kernfile); +		exit(EXIT_SUCCESS); +	} + +	if (argc != 1) +		usage(); + +	PREFIX = *argv; +	if (stat(PREFIX, &buf) != 0 || !S_ISREG(buf.st_mode)) +		err(2, "%s", PREFIX); +	if (freopen("DEFAULTS", "r", stdin) != NULL) { +		found_defaults = 1; +		yyfile = "DEFAULTS"; +	} else { +		if (freopen(PREFIX, "r", stdin) == NULL) +			err(2, "%s", PREFIX); +		yyfile = PREFIX; +	} +	if (*destdir != '\0') { +		len = strlen(destdir); +		while (len > 1 && destdir[len - 1] == '/') +			destdir[--len] = '\0'; +		if (*srcdir == '\0') +			get_srcdir(); +	} else { +		strlcpy(destdir, CDIR, sizeof(destdir)); +		strlcat(destdir, PREFIX, sizeof(destdir)); +	} + +	if (yyparse()) +		exit(3); + +	/* +	 * Ensure that required elements (machine, cpu, ident) are present. +	 */ +	if (machinename == NULL) { +		printf("Specify machine type, e.g. ``machine i386''\n"); +		exit(1); +	} +	if (ident == NULL) { +		printf("no ident line specified\n"); +		exit(1); +	} +	if (SLIST_EMPTY(&cputype)) { +		printf("cpu type must be specified\n"); +		exit(1); +	} +	checkversion(); + +	if (printmachine) { +		printf("%s\t%s\n",machinename,machinearch); +		exit(0); +	} + +	/* +	 * Make CDIR directory, if doing a default destination. Some version +	 * control systems delete empty directories and this seemlessly copes. +	 */ +	if (!cust_dest && stat(CDIR, &buf)) +		if (mkdir(CDIR, 0777)) +			err(2, "%s", CDIR); +	/* Create the compile directory */ +	p = path((char *)NULL); +	if (stat(p, &buf)) { +		if (mkdir(p, 0777)) +			err(2, "%s", p); +	} else if (!S_ISDIR(buf.st_mode)) +		errx(EXIT_FAILURE, "%s isn't a directory", p); + +	configfile();			/* put config file into kernel*/ +	options();			/* make options .h files */ +	makefile();			/* build Makefile */ +	makeenv();			/* build env.c */ +	makehints();			/* build hints.c */ +	headers();			/* make a lot of .h files */ +	cleanheaders(p); +	printf("Kernel build directory is %s\n", p); +	printf("Don't forget to do ``make cleandepend && make depend''\n"); +	exit(0); +} + +/* + * get_srcdir + *	determine the root of the kernel source tree + *	and save that in srcdir. + */ +static void +get_srcdir(void) +{ +	struct stat lg, phy; +	char *p, *pwd; +	int i; + +	if (realpath("../..", srcdir) == NULL) +		err(EXIT_FAILURE, "Unable to find root of source tree"); +	if ((pwd = getenv("PWD")) != NULL && *pwd == '/' && +	    (pwd = strdup(pwd)) != NULL) { +		/* Remove the last two path components. */ +		for (i = 0; i < 2; i++) { +			if ((p = strrchr(pwd, '/')) == NULL) { +				free(pwd); +				return; +			} +			*p = '\0'; +		} +		if (stat(pwd, &lg) != -1 && stat(srcdir, &phy) != -1 && +		    lg.st_dev == phy.st_dev && lg.st_ino == phy.st_ino) +			strlcpy(srcdir, pwd, MAXPATHLEN); +		free(pwd); +	} +} + +static void +usage(void) +{ + +	fprintf(stderr, +	    "usage: config [-CgmpV] [-d destdir] [-s srcdir] sysname\n"); +	fprintf(stderr, "       config -x kernel\n"); +	exit(EX_USAGE); +} + +static void +init_line_buf(void) +{ + +	line_buf.str(""); +} + +static std::string +get_line_buf(void) +{ + +	line_buf.flush(); +	if (!line_buf.good()) { +		errx(EXIT_FAILURE, "failed to generate line buffer, " +		    "partial line = %s", line_buf.str().c_str()); +	} + +	return line_buf.str(); +} + +/* + * get_word + *	returns EOF on end of file + *	NULL on end of line + *	pointer to the word otherwise + */ +configword +get_word(FILE *fp) +{ +	int ch; +	int escaped_nl = 0; + +	init_line_buf(); +begin: +	while ((ch = getc(fp)) != EOF) +		if (ch != ' ' && ch != '\t') +			break; +	if (ch == EOF) +		return (configword().eof(true)); +	if (ch == '\\'){ +		escaped_nl = 1; +		goto begin; +	} +	if (ch == '\n') { +		if (escaped_nl){ +			escaped_nl = 0; +			goto begin; +		} +		else +			return (configword().eol(true)); +	} +	line_buf << (char)ch; +	/* Negation operator is a word by itself. */ +	if (ch == '!') { +		return (configword(get_line_buf())); +	} +	while ((ch = getc(fp)) != EOF) { +		if (isspace(ch)) +			break; +		line_buf << (char)ch; +	} +	if (ch == EOF) +		return (configword().eof(true)); +	(void) ungetc(ch, fp); +	return (configword(get_line_buf())); +} + +/* + * get_quoted_word + *	like get_word but will accept something in double or single quotes + *	(to allow embedded spaces). + */ +configword +get_quoted_word(FILE *fp) +{ +	int ch; +	int escaped_nl = 0; + +	init_line_buf(); +begin: +	while ((ch = getc(fp)) != EOF) +		if (ch != ' ' && ch != '\t') +			break; +	if (ch == EOF) +		return (configword().eof(true)); +	if (ch == '\\'){ +		escaped_nl = 1; +		goto begin; +	} +	if (ch == '\n') { +		if (escaped_nl){ +			escaped_nl = 0; +			goto begin; +		} +		else +			return (configword().eol(true)); +	} +	if (ch == '"' || ch == '\'') { +		int quote = ch; + +		escaped_nl = 0; +		while ((ch = getc(fp)) != EOF) { +			if (ch == quote && !escaped_nl) +				break; +			if (ch == '\n' && !escaped_nl) { +				printf("config: missing quote reading `%s'\n", +					get_line_buf().c_str()); +				exit(2); +			} +			if (ch == '\\' && !escaped_nl) { +				escaped_nl = 1; +				continue; +			} +			if (ch != quote && escaped_nl) +				line_buf << "\\"; +			line_buf << (char)ch; +			escaped_nl = 0; +		} +	} else { +		line_buf << (char)ch; +		while ((ch = getc(fp)) != EOF) { +			if (isspace(ch)) +				break; +			line_buf << (char)ch; +		} +		if (ch != EOF) +			(void) ungetc(ch, fp); +	} +	if (ch == EOF) +		return (configword().eof(true)); +	return (configword(get_line_buf())); +} + +/* + * prepend the path to a filename + */ +char * +path(const char *file) +{ +	char *cp = NULL; + +	if (file) +		asprintf(&cp, "%s/%s", destdir, file); +	else +		cp = strdup(destdir); +	if (cp == NULL) +		err(EXIT_FAILURE, "malloc"); +	return (cp); +} + +/* + * Generate configuration file based on actual settings. With this mode, user + * will be able to obtain and build conifguration file with one command. + */ +static void +configfile_dynamic(std::ostringstream &cfg) +{ +	struct cputype *cput; +	struct device *d; +	struct opt *ol; +	char *lend; +	unsigned int i; + +	asprintf(&lend, "\\n\\\n"); +	assert(lend != NULL); +	cfg << "options\t" << OPT_AUTOGEN << lend; +	cfg << "ident\t" << ident << lend; +	cfg << "machine\t" << machinename << lend; +	SLIST_FOREACH(cput, &cputype, cpu_next) +		cfg << "cpu\t" << cput->cpu_name << lend; +	SLIST_FOREACH(ol, &mkopt, op_next) +		cfg << "makeoptions\t" << ol->op_name << '=' << +		    ol->op_value << lend; +	SLIST_FOREACH(ol, &opt, op_next) { +		if (strncmp(ol->op_name, "DEV_", 4) == 0) +			continue; +		cfg << "options\t" << ol->op_name; +		if (ol->op_value != NULL) { +			cfg << '='; +			for (i = 0; i < strlen(ol->op_value); i++) { +				if (ol->op_value[i] == '"') +					cfg << '\\' << ol->op_value[i]; +				else +					cfg << ol->op_value[i]; +			} +		} + +		cfg << lend; +	} +	/* +	 * Mark this file as containing everything we need. +	 */ +	STAILQ_FOREACH(d, &dtab, d_next) +		cfg << "device\t" << d->d_name << lend; +	free(lend); +} + +/* + * Generate file from the configuration files. + */ +static void +configfile_filebased(std::ostringstream &cfg) +{ +	FILE *cff; +	struct cfgfile *cf; +	int i; + +	/* +	 * Try to read all configuration files. Since those will be present as +	 * C string in the macro, we have to slash their ends then the line +	 * wraps. +	 */ +	STAILQ_FOREACH(cf, &cfgfiles, cfg_next) { +		cff = fopen(cf->cfg_path, "r"); +		if (cff == NULL) { +			warn("Couldn't open file %s", cf->cfg_path); +			continue; +		} +		while ((i = getc(cff)) != EOF) { +			if (i == '\n') +				cfg << "\\n\\\n"; +			else if (i == '"' || i == '\'') +				cfg << '\\' << i; +			else +				cfg << i; +		} +		fclose(cff); +	} +} + +static void +configfile(void) +{ +	FILE *fo; +	std::ostringstream cfg; +	char *p; + +	/* Add main configuration file to the list of files to be included */ +	cfgfile_add(PREFIX); +	p = path("config.c.new"); +	fo = fopen(p, "w"); +	if (!fo) +		err(2, "%s", p); +	free(p); + +	if (filebased) { +		/* Is needed, can be used for backward compatibility. */ +		configfile_filebased(cfg); +	} else { +		configfile_dynamic(cfg); +	} + +	cfg.flush(); +	/*  +	 * We print first part of the template, replace our tag with +	 * configuration files content and later continue writing our +	 * template. +	 */ +	p = strstr(kernconfstr, KERNCONFTAG); +	if (p == NULL) +		errx(EXIT_FAILURE, "Something went terribly wrong!"); +	*p = '\0'; +	fprintf(fo, "%s", kernconfstr); +	fprintf(fo, "%s", cfg.str().c_str()); +	p += strlen(KERNCONFTAG); +	fprintf(fo, "%s", p); +	fclose(fo); +	moveifchanged("config.c.new", "config.c"); +	cfgfile_removeall(); +} + +/* + * moveifchanged -- + *	compare two files; rename if changed. + */ +void +moveifchanged(const char *from_name, const char *to_name) +{ +	char *p, *q; +	char *from_path, *to_path; +	int changed; +	size_t tsize; +	struct stat from_sb, to_sb; +	int from_fd, to_fd; + +	changed = 0; + +	from_path = path(from_name); +	to_path = path(to_name); +	if ((from_fd = open(from_path, O_RDONLY)) < 0) +		err(EX_OSERR, "moveifchanged open(%s)", from_name); + +	if ((to_fd = open(to_path, O_RDONLY)) < 0) +		changed++; + +	if (!changed && fstat(from_fd, &from_sb) < 0) +		err(EX_OSERR, "moveifchanged fstat(%s)", from_path); + +	if (!changed && fstat(to_fd, &to_sb) < 0) +		err(EX_OSERR, "moveifchanged fstat(%s)", to_path); + +	if (!changed && from_sb.st_size != to_sb.st_size) +		changed++; + +	if (!changed) { +		tsize = (size_t)from_sb.st_size; + +		p = (char *)mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, +		    (off_t)0); +		if (p == MAP_FAILED) +			err(EX_OSERR, "mmap %s", from_path); +		q = (char *)mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, +		    (off_t)0); +		if (q == MAP_FAILED) +			err(EX_OSERR, "mmap %s", to_path); + +		changed = memcmp(p, q, tsize); +		munmap(p, tsize); +		munmap(q, tsize); +	} + +	if (changed) { +		if (rename(from_path, to_path) < 0) +			err(EX_OSERR, "rename(%s, %s)", from_path, to_path); +	} else { +		if (unlink(from_path) < 0) +			err(EX_OSERR, "unlink(%s)", from_path); +	} + +	close(from_fd); +	if (to_fd >= 0) +		close(to_fd); + +	free(from_path); +	free(to_path); +} + +static void +cleanheaders(char *p) +{ +	DIR *dirp; +	struct dirent *dp; +	struct file_list *fl; +	struct hdr_list *hl; +	size_t len; + +	remember("y.tab.h"); +	remember("setdefs.h"); +	STAILQ_FOREACH(fl, &ftab, f_next) +		remember(fl->f_fn); + +	/* +	 * Scan the build directory and clean out stuff that looks like +	 * it might have been a leftover NFOO header, etc. +	 */ +	if ((dirp = opendir(p)) == NULL) +		err(EX_OSERR, "opendir %s", p); +	while ((dp = readdir(dirp)) != NULL) { +		len = strlen(dp->d_name); +		/* Skip non-headers */ +		if (len < 2 || dp->d_name[len - 2] != '.' || +		    dp->d_name[len - 1] != 'h') +			continue; +		/* Skip special stuff, eg: bus_if.h, but check opt_*.h */ +		if (strchr(dp->d_name, '_') && +		    strncmp(dp->d_name, "opt_", 4) != 0) +			continue; +		/* Check if it is a target file */ +		for (hl = htab; hl != NULL; hl = hl->h_next) { +			if (eq(dp->d_name, hl->h_name)) { +				break; +			} +		} +		if (hl) +			continue; +		printf("Removing stale header: %s\n", dp->d_name); +		p = path(dp->d_name); +		if (unlink(p) == -1) +			warn("unlink %s", dp->d_name); +		free(p); +	} +	(void)closedir(dirp); +} + +void +remember(const char *file) +{ +	const char *s; +	struct hdr_list *hl; + +	if ((s = strrchr(file, '/')) != NULL) +		s = ns(s + 1); +	else +		s = ns(file); + +	if (strchr(s, '_') && strncmp(s, "opt_", 4) != 0) { +		free(__DECONST(char *, s)); +		return; +	} +	for (hl = htab; hl != NULL; hl = hl->h_next) { +		if (eq(s, hl->h_name)) { +			free(__DECONST(char *, s)); +			return; +		} +	} +	hl = (struct hdr_list *)calloc(1, sizeof(*hl)); +	if (hl == NULL) +		err(EXIT_FAILURE, "calloc"); +	hl->h_name = s; +	hl->h_next = htab; +	htab = hl; +} + +/* + * This one is quick hack. Will be probably moved to elf(3) interface. + * It takes kernel configuration file name, passes it as an argument to + * elfdump -a, which output is parsed by some UNIX tools... + */ +static void +kernconfdump(const char *file) +{ +	struct stat st; +	FILE *fp, *pp; +	int error, osz, r; +	size_t i, off, size, t1, t2, align; +	char *cmd, *o; + +	r = open(file, O_RDONLY); +	if (r == -1) +		err(EXIT_FAILURE, "Couldn't open file '%s'", file); +	error = fstat(r, &st); +	if (error == -1) +		err(EXIT_FAILURE, "fstat() failed"); +	if (S_ISDIR(st.st_mode)) +		errx(EXIT_FAILURE, "'%s' is a directory", file); +	fp = fdopen(r, "r"); +	if (fp == NULL) +		err(EXIT_FAILURE, "fdopen() failed"); +	osz = 1024; +	o = (char *)calloc(1, osz); +	if (o == NULL) +		err(EXIT_FAILURE, "Couldn't allocate memory"); +	/* ELF note section header. */ +	asprintf(&cmd, "/usr/bin/elfdump -c %s | grep -A 8 kern_conf" +	    "| tail -5 | cut -d ' ' -f 2 | paste - - - - -", file); +	if (cmd == NULL) +		errx(EXIT_FAILURE, "asprintf() failed"); +	pp = popen(cmd, "r"); +	if (pp == NULL) +		errx(EXIT_FAILURE, "popen() failed"); +	free(cmd); +	(void)fread(o, osz, 1, pp); +	pclose(pp); +	r = sscanf(o, "%zu%zu%zu%zu%zu", &off, &size, &t1, &t2, &align); +	free(o); +	if (size > SIZE_MAX - off || off + size > (size_t)st.st_size) +		errx(EXIT_FAILURE, "%s: incoherent ELF headers", file); +	if (r != 5) +		errx(EXIT_FAILURE, "File %s doesn't contain configuration " +		    "file. Either unsupported, or not compiled with " +		    "INCLUDE_CONFIG_FILE", file); +	r = fseek(fp, off, SEEK_CUR); +	if (r != 0) +		err(EXIT_FAILURE, "fseek() failed"); +	for (i = 0; i < size; i++) { +		r = fgetc(fp); +		if (r == EOF) +			break; +		if (r == '\0') { +			assert(i == size - 1 && +			    ("\\0 found in the middle of a file")); +			break; +		} +		fputc(r, stdout); +	} +	fclose(fp); +} + +static void +badversion(void) +{ +	fprintf(stderr, "ERROR: version of config(8) does not match kernel!\n"); +	fprintf(stderr, "config version = %d, ", CONFIGVERS); +	fprintf(stderr, "version required = %d\n\n", versreq); +	fprintf(stderr, "Make sure that /usr/src/usr.sbin/config is in sync\n"); +	fprintf(stderr, "with your /usr/src/sys and install a new config binary\n"); +	fprintf(stderr, "before trying this again.\n\n"); +	fprintf(stderr, "If running the new config fails check your config\n"); +	fprintf(stderr, "file against the GENERIC or LINT config files for\n"); +	fprintf(stderr, "changes in config syntax, or option/device naming\n"); +	fprintf(stderr, "conventions\n\n"); +	exit(1); +} + +static void +checkversion(void) +{ +	FILE *ifp; +	char line[BUFSIZ]; + +	ifp = open_makefile_template(); +	while (fgets(line, BUFSIZ, ifp) != 0) { +		if (*line != '%') +			continue; +		if (strncmp(line, "%VERSREQ=", 9) != 0) +			continue; +		versreq = atoi(line + 9); +		if (MAJOR_VERS(versreq) == MAJOR_VERS(CONFIGVERS) && +		    versreq <= CONFIGVERS) +			continue; +		badversion(); +	} +	fclose(ifp); +} diff --git a/usr.sbin/config/mkheaders.c b/usr.sbin/config/mkheaders.c new file mode 100644 index 000000000000..8ff81072c5d7 --- /dev/null +++ b/usr.sbin/config/mkheaders.c @@ -0,0 +1,60 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1980, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * This used to generate a bunch of headers files related to devices when + * device counters were supported. Support for that was removed in 2005. + * Since then, all we've done is to report unknown devices in this file. + * It's kept its historical name, despite no longer generating headers. + */ + +#include <err.h> +#include <stdio.h> +#include "config.h" +#include "y.tab.h" + +void +headers(void) +{ +	struct device *dp; +	int errors; + +	errors = 0; +	STAILQ_FOREACH(dp, &dtab, d_next) { +		if (!(dp->d_done & DEVDONE)) { +			warnx("Error: device \"%s\" is unknown", +			       dp->d_name); +			       errors++; +			} +	} +	if (errors) +		errx(1, "%d errors", errors); +} diff --git a/usr.sbin/config/mkmakefile.cc b/usr.sbin/config/mkmakefile.cc new file mode 100644 index 000000000000..6cffb64a5569 --- /dev/null +++ b/usr.sbin/config/mkmakefile.cc @@ -0,0 +1,848 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1980, 1990, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Build the makefile for the system, from + * the information in the files files and the + * additional files for the machine being compiled to. + */ +#include <sys/param.h> + +#include <cerrno> +#include <ctype.h> +#include <err.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdio.h> +#include <string.h> +#include <string> +#include <unordered_map> + +#include "y.tab.h" +#include "config.h" +#include "configvers.h" + +typedef std::unordered_map<std::string, std::string>	env_map; + +static char *tail(char *); +static void do_clean(FILE *); +static void do_rules(FILE *); +static void do_xxfiles(char *, FILE *); +static void do_objs(FILE *); +static void do_before_depend(FILE *); +static void read_files(void); +static void sanitize_envline(char *result, const char *src); +static bool preprocess(char *line, char *result); +static void process_into_file(char *line, FILE *ofp); +static int process_into_map(char *line, env_map &emap); +static void dump_map(env_map &emap, FILE *ofp); + +static void __printflike(1, 2) +errout(const char *fmt, ...) +{ +	va_list ap; + +	va_start(ap, fmt); +	vfprintf(stderr, fmt, ap); +	va_end(ap); +	exit(1); +} + +/* + * Lookup a file, by name. + */ +static struct file_list * +fl_lookup(char *file) +{ +	struct file_list *fp; + +	STAILQ_FOREACH(fp, &ftab, f_next) { +		if (eq(fp->f_fn, file)) +			return (fp); +	} +	return (0); +} + +/* + * Make a new file list entry + */ +static struct file_list * +new_fent(void) +{ +	struct file_list *fp; + +	fp = (struct file_list *) calloc(1, sizeof *fp); +	if (fp == NULL) +		err(EXIT_FAILURE, "calloc"); +	STAILQ_INSERT_TAIL(&ftab, fp, f_next); +	return (fp); +} + +/* + * Open the correct Makefile and return it, or error out. + */ +FILE * +open_makefile_template(void) +{ +	FILE *ifp; +	char line[BUFSIZ]; + +	snprintf(line, sizeof(line), "../../conf/Makefile.%s", machinename); +	ifp = fopen(line, "r"); +	if (ifp == NULL) { +		snprintf(line, sizeof(line), "Makefile.%s", machinename); +		ifp = fopen(line, "r"); +	} +	if (ifp == NULL) +		err(1, "%s", line); +	return (ifp); +} + +/* + * Build the makefile from the skeleton + */ +void +makefile(void) +{ +	FILE *ifp, *ofp; +	char line[BUFSIZ]; +	struct opt *op, *t; + +	read_files(); +	ifp = open_makefile_template(); +	ofp = fopen(path("Makefile.new"), "w"); +	if (ofp == NULL) +		err(1, "%s", path("Makefile.new")); +	fprintf(ofp, "KERN_IDENT=%s\n", ident); +	fprintf(ofp, "MACHINE=%s\n", machinename); +	fprintf(ofp, "MACHINE_ARCH=%s\n", machinearch); +	SLIST_FOREACH_SAFE(op, &mkopt, op_next, t) { +		fprintf(ofp, "%s=%s", op->op_name, op->op_value); +		while ((op = SLIST_NEXT(op, op_append)) != NULL) +			fprintf(ofp, " %s", op->op_value); +		fprintf(ofp, "\n"); +	} +	if (debugging) +		fprintf(ofp, "DEBUG=-g\n"); +	if (*srcdir != '\0') +		fprintf(ofp,"S=%s\n", srcdir); +	while (fgets(line, BUFSIZ, ifp) != NULL) { +		if (*line != '%') { +			fprintf(ofp, "%s", line); +			continue; +		} +		if (eq(line, "%BEFORE_DEPEND\n")) +			do_before_depend(ofp); +		else if (eq(line, "%OBJS\n")) +			do_objs(ofp); +		else if (strncmp(line, "%FILES.", 7) == 0) +			do_xxfiles(line, ofp); +		else if (eq(line, "%RULES\n")) +			do_rules(ofp); +		else if (eq(line, "%CLEAN\n")) +			do_clean(ofp); +		else if (strncmp(line, "%VERSREQ=", 9) == 0) +			line[0] = '\0'; /* handled elsewhere */ +		else +			fprintf(stderr, +			    "Unknown %% construct in generic makefile: %s", +			    line); +	} +	(void) fclose(ifp); +	(void) fclose(ofp); +	moveifchanged("Makefile.new", "Makefile"); +} + +static void +sanitize_envline(char *result, const char *src) +{ +	const char *eq; +	char c, *dst; +	bool leading; + +	/* If there is no '=' it's not a well-formed name=value line. */ +	if ((eq = strchr(src, '=')) == NULL) { +		*result = '\0'; +		return; +	} +	dst = result; + +	/* Copy chars before the '=', skipping any leading spaces/quotes. */ +	leading = true; +	while (src < eq) { +		c = *src++; +		if (leading && (isspace(c) || c == '"')) +			continue; +		*dst++ = c; +		leading = false; +	} + +	/* If it was all leading space, we don't have a well-formed line. */ +	if (leading) { +		*result = '\0'; +		return; +	} + +	/* Trim spaces/quotes immediately before the '=', then copy the '='. */ +	while (isspace(dst[-1]) || dst[-1] == '"') +		--dst; +	*dst++ = *src++; + +	/* Copy chars after the '=', skipping any leading whitespace. */ +	leading = true; +	while ((c = *src++) != '\0') { +		if (leading && (isspace(c) || c == '"')) +			continue; +		*dst++ = c; +		leading = false; +	} + +	/* If it was all leading space, it's a valid 'var=' (nil value). */ +	if (leading) { +		*dst = '\0'; +		return; +	} + +	/* Trim trailing whitespace and quotes. */ +	while (isspace(dst[-1]) || dst[-1] == '"') +		--dst; + +	*dst = '\0'; +} + +/* + * Returns true if the caller may use the string. + */ +static bool +preprocess(char *line, char *result) +{ +	char *s; + +	/* Strip any comments */ +	if ((s = strchr(line, '#')) != NULL) +		*s = '\0'; +	sanitize_envline(result, line); +	/* Return true if it's non-empty */ +	return (*result != '\0'); +} + +static void +process_into_file(char *line, FILE *ofp) +{ +	char result[BUFSIZ]; + +	if (preprocess(line, result)) +		fprintf(ofp, "\"%s\\0\"\n", result); +} + +static int +process_into_map(char *line, env_map &emap) +{ +	char result[BUFSIZ], *s; + +	if (preprocess(line, result)) { +		s = strchr(result, '='); +		if (s == NULL) +			return (EINVAL); +		*s = '\0'; +		emap[result] = s + 1; +	} + +	return (0); +} + +static void +dump_map(env_map &emap, FILE *ofp) +{ + +	for (auto iter : emap) { +		fprintf(ofp, "\"%s=%s\\0\"\n", iter.first.c_str(), +		    iter.second.c_str()); +	} +} + +/* + * Build hints.c from the skeleton + */ +void +makehints(void) +{ +	FILE *ifp, *ofp; +	env_map emap; +	char line[BUFSIZ]; +	struct hint *hint; + +	ofp = fopen(path("hints.c.new"), "w"); +	if (ofp == NULL) +		err(1, "%s", path("hints.c.new")); +	fprintf(ofp, "#include <sys/types.h>\n"); +	fprintf(ofp, "#include <sys/systm.h>\n"); +	fprintf(ofp, "\n"); +	/* +	 * Write out hintmode for older kernels. Remove when config(8) major +	 * version rolls over. +	 */ +	if (versreq <= CONFIGVERS_ENVMODE_REQ) +		fprintf(ofp, "int hintmode = %d;\n", +			!STAILQ_EMPTY(&hints) ? 1 : 0); +	fprintf(ofp, "char static_hints[] = {\n"); +	STAILQ_FOREACH(hint, &hints, hint_next) { +		ifp = fopen(hint->hint_name, "r"); +		if (ifp == NULL) +			err(1, "%s", hint->hint_name); +		while (fgets(line, BUFSIZ, ifp) != NULL) { +			if (process_into_map(line, emap) != 0) +				errout("%s: malformed line: %s\n", +				    hint->hint_name, line); +		} +		dump_map(emap, ofp); +		fclose(ifp); +	} +	fprintf(ofp, "\"\\0\"\n};\n"); +	fclose(ofp); +	moveifchanged("hints.c.new", "hints.c"); +} + +/* + * Build env.c from the skeleton + */ +void +makeenv(void) +{ +	FILE *ifp, *ofp; +	env_map emap; +	char line[BUFSIZ]; +	struct envvar *envvar; + +	ofp = fopen(path("env.c.new"), "w"); +	if (ofp == NULL) +		err(1, "%s", path("env.c.new")); +	fprintf(ofp, "#include <sys/types.h>\n"); +	fprintf(ofp, "#include <sys/systm.h>\n"); +	fprintf(ofp, "\n"); +	/* +	 * Write out envmode for older kernels. Remove when config(8) major +	 * version rolls over. +	 */ +	if (versreq <= CONFIGVERS_ENVMODE_REQ) +		fprintf(ofp, "int envmode = %d;\n", +			!STAILQ_EMPTY(&envvars) ? 1 : 0); +	fprintf(ofp, "char static_env[] = {\n"); +	STAILQ_FOREACH(envvar, &envvars, envvar_next) { +		if (envvar->env_is_file) { +			ifp = fopen(envvar->env_str, "r"); +			if (ifp == NULL) +				err(1, "%s", envvar->env_str); +			while (fgets(line, BUFSIZ, ifp) != NULL) { +				if (process_into_map(line, emap) != 0) +					errout("%s: malformed line: %s\n", +					    envvar->env_str, line); +			} +			dump_map(emap, ofp); +			fclose(ifp); +		} else +			process_into_file(envvar->env_str, ofp); +	} +	fprintf(ofp, "\"\\0\"\n};\n"); +	fclose(ofp); +	moveifchanged("env.c.new", "env.c"); +} + +static void +read_file(char *fname) +{ +	char ifname[MAXPATHLEN]; +	FILE *fp; +	struct file_list *tp; +	struct device *dp; +	struct opt *op; +	struct includepath *ipath; +	configword wd; +	char *rfile, *compilewith, *depends, *clean, *fnamebuf, *warning; +	const char *objprefix; +	int compile, match, nreqs, std, filetype, negate, +	    imp_rule, no_ctfconvert, no_obj, before_depend, nowerror; + +	fp = fopen(fname, "r"); +	if (fp == NULL) { +		SLIST_FOREACH(ipath, &includepath, path_next) { +			asprintf(&fnamebuf, "%s/%s", ipath->path, fname); +			if (fnamebuf != NULL) { +				fp = fopen(fnamebuf, "r"); +				if (fp != NULL) +					break; +				free(fnamebuf); +			} +		} +	} +	if (fp == NULL) +		err(1, "%s", fname); +next: +	/* +	 * include "filename" +	 * filename    [ standard | optional ] +	 *	[ dev* [ | dev* ... ] | [ no-obj ] +	 *	[ compile-with "compile rule" [no-implicit-rule] ] +	 *      [ dependency "dependency-list"] [ before-depend ] +	 *	[ clean "file-list"] [ warning "text warning" ] +	 *	[ obj-prefix "file prefix"] +	 *	[ nowerror ] [ local ] +	 */ +	wd = get_word(fp); +	if (wd.eof()) { +		(void) fclose(fp); +		return; +	}  +	if (wd.eol()) +		goto next; +	if (wd[0] == '#') +	{ +		while (!(wd = get_word(fp)).eof() && !wd.eol()) +			; +		goto next; +	} +	if (eq(wd, "include")) { +		wd = get_quoted_word(fp); +		if (wd.eof() || wd.eol()) +			errout("%s: missing include filename.\n", fname); +		(void) snprintf(ifname, sizeof(ifname), "../../%s", +		    wd->c_str()); +		read_file(ifname); +		while (!(wd = get_word(fp)).eof() && !wd.eol()) +			; +		goto next; +	} +	rfile = ns(wd); +	wd = get_word(fp); +	if (wd.eof()) +		return; +	if (wd.eol()) +		errout("%s: No type for %s.\n", fname, rfile); +	tp = fl_lookup(rfile); +	compile = 0; +	match = 1; +	nreqs = 0; +	compilewith = NULL; +	depends = NULL; +	clean = NULL; +	warning = NULL; +	std = 0; +	imp_rule = 0; +	no_ctfconvert = 0; +	no_obj = 0; +	before_depend = 0; +	nowerror = 0; +	negate = 0; +	filetype = NORMAL; +	objprefix = ""; +	if (eq(wd, "standard")) +		std = 1; +	else if (!eq(wd, "optional")) +		errout("%s: \"%s\" %s must be optional or standard\n", +		    fname, wd->c_str(), rfile); +	for (wd = get_word(fp); !wd.eol(); wd = get_word(fp)) { +		if (wd.eof()) +			return; +		if (eq(wd, "!")) { +			negate = 1; +			continue; +		} +		if (eq(wd, "|")) { +			if (nreqs == 0) +				errout("%s: syntax error describing %s\n", +				       fname, rfile); +			compile += match; +			match = 1; +			nreqs = 0; +			continue; +		} +		if (eq(wd, "no-ctfconvert")) { +			no_ctfconvert++; +			continue; +		} +		if (eq(wd, "no-obj")) { +			no_obj++; +			continue; +		} +		if (eq(wd, "no-implicit-rule")) { +			if (compilewith == NULL) +				errout("%s: alternate rule required when " +				       "\"no-implicit-rule\" is specified for" +				       " %s.\n", +				       fname, rfile); +			imp_rule++; +			continue; +		} +		if (eq(wd, "before-depend")) { +			before_depend++; +			continue; +		} +		if (eq(wd, "dependency")) { +			wd = get_quoted_word(fp); +			if (wd.eof() || wd.eol()) +				errout("%s: %s missing dependency string.\n", +				       fname, rfile); +			depends = ns(wd); +			continue; +		} +		if (eq(wd, "clean")) { +			wd = get_quoted_word(fp); +			if (wd.eof() || wd.eol()) +				errout("%s: %s missing clean file list.\n", +				       fname, rfile); +			clean = ns(wd); +			continue; +		} +		if (eq(wd, "compile-with")) { +			wd = get_quoted_word(fp); +			if (wd.eof() || wd.eol()) +				errout("%s: %s missing compile command string.\n", +				       fname, rfile); +			compilewith = ns(wd); +			continue; +		} +		if (eq(wd, "warning")) { +			wd = get_quoted_word(fp); +			if (wd.eof() || wd.eol()) +				errout("%s: %s missing warning text string.\n", +				       fname, rfile); +			warning = ns(wd); +			continue; +		} +		if (eq(wd, "obj-prefix")) { +			wd = get_quoted_word(fp); +			if (wd.eof() || wd.eol()) +				errout("%s: %s missing object prefix string.\n", +				       fname, rfile); +			objprefix = ns(wd); +			continue; +		} +		if (eq(wd, "nowerror")) { +			nowerror = 1; +			continue; +		} +		if (eq(wd, "local")) { +			filetype = LOCAL; +			continue; +		} +		if (eq(wd, "no-depend")) { +			filetype = NODEPEND; +			continue; +		} +		nreqs++; +		if (std) +			errout("standard entry %s has optional inclusion specifier %s!\n", +			       rfile, wd->c_str()); +		STAILQ_FOREACH(dp, &dtab, d_next) +			if (eq(dp->d_name, wd)) { +				if (negate) +					match = 0; +				else +					dp->d_done |= DEVDONE; +				goto nextparam; +			} +		SLIST_FOREACH(op, &opt, op_next) +			if (op->op_value == 0 && +			    strcasecmp(op->op_name, wd) == 0) { +				if (negate) +					match = 0; +				goto nextparam; +			} +		match &= negate; +nextparam: +		negate = 0; +	} +	compile += match; +	if (compile && tp == NULL) { +		if (std == 0 && nreqs == 0) +			errout("%s: what is %s optional on?\n", +			       fname, rfile); +		tp = new_fent(); +		tp->f_fn = rfile; +		tp->f_type = filetype; +		if (filetype == LOCAL) +			tp->f_srcprefix = ""; +		else +			tp->f_srcprefix = "$S/"; +		if (imp_rule) +			tp->f_flags |= NO_IMPLCT_RULE; +		if (no_ctfconvert) +			tp->f_flags |= NO_CTFCONVERT; +		if (no_obj) +			tp->f_flags |= NO_OBJ | NO_CTFCONVERT; +		if (before_depend) +			tp->f_flags |= BEFORE_DEPEND; +		if (nowerror) +			tp->f_flags |= NOWERROR; +		tp->f_compilewith = compilewith; +		tp->f_depends = depends; +		tp->f_clean = clean; +		tp->f_warn = warning; +		tp->f_objprefix = objprefix; +	} +	goto next; +} + +/* + * Read in the information about files used in making the system. + * Store it in the ftab linked list. + */ +static void +read_files(void) +{ +	char fname[MAXPATHLEN]; +	struct files_name *nl, *tnl; +	 +	(void) snprintf(fname, sizeof(fname), "../../conf/files"); +	read_file(fname); +	(void) snprintf(fname, sizeof(fname), +		       	"../../conf/files.%s", machinename); +	read_file(fname); +	for (nl = STAILQ_FIRST(&fntab); nl != NULL; nl = tnl) { +		read_file(nl->f_name); +		tnl = STAILQ_NEXT(nl, f_next); +		free(nl->f_name); +		free(nl); +	} +} + +static void +do_before_depend(FILE *fp) +{ +	struct file_list *tp; +	int lpos, len; + +	fputs("BEFORE_DEPEND=", fp); +	lpos = 15; +	STAILQ_FOREACH(tp, &ftab, f_next) +		if (tp->f_flags & BEFORE_DEPEND) { +			len = strlen(tp->f_fn) + strlen(tp->f_srcprefix); +			if (len + lpos > 72) { +				lpos = 8; +				fputs("\\\n\t", fp); +			} +			if (tp->f_flags & NO_IMPLCT_RULE) +				lpos += fprintf(fp, "%s ", tp->f_fn); +			else +				lpos += fprintf(fp, "%s%s ", tp->f_srcprefix, +				    tp->f_fn); +		} +	if (lpos != 8) +		putc('\n', fp); +} + +static void +do_objs(FILE *fp) +{ +	struct file_list *tp; +	int lpos, len; +	char *cp, och, *sp; + +	fprintf(fp, "OBJS="); +	lpos = 6; +	STAILQ_FOREACH(tp, &ftab, f_next) { +		if (tp->f_flags & NO_OBJ) +			continue; +		sp = tail(tp->f_fn); +		cp = sp + (len = strlen(sp)) - 1; +		och = *cp; +		*cp = 'o'; +		len += strlen(tp->f_objprefix); +		if (len + lpos > 72) { +			lpos = 8; +			fprintf(fp, "\\\n\t"); +		} +		fprintf(fp, "%s%s ", tp->f_objprefix, sp); +		lpos += len + 1; +		*cp = och; +	} +	if (lpos != 8) +		putc('\n', fp); +} + +static void +do_xxfiles(char *tag, FILE *fp) +{ +	struct file_list *tp; +	int lpos, len, slen; +	char *suff, *SUFF; + +	if (tag[strlen(tag) - 1] == '\n') +		tag[strlen(tag) - 1] = '\0'; + +	suff = ns(tag + 7); +	SUFF = ns(suff); +	raisestr(SUFF); +	slen = strlen(suff); + +	fprintf(fp, "%sFILES=", SUFF); +	free(SUFF); +	lpos = 8; +	STAILQ_FOREACH(tp, &ftab, f_next) +		if (tp->f_type != NODEPEND) { +			len = strlen(tp->f_fn); +			if (tp->f_fn[len - slen - 1] != '.') +				continue; +			if (strcasecmp(&tp->f_fn[len - slen], suff) != 0) +				continue; +			if (len + strlen(tp->f_srcprefix) + lpos > 72) { +				lpos = 8; +				fputs("\\\n\t", fp); +			} +			lpos += fprintf(fp, "%s%s ", tp->f_srcprefix, tp->f_fn); +		} +	free(suff); +	if (lpos != 8) +		putc('\n', fp); +} + +static char * +tail(char *fn) +{ +	char *cp; + +	cp = strrchr(fn, '/'); +	if (cp == NULL) +		return (fn); +	return (cp+1); +} + +/* + * Create the makerules for each file + * which is part of the system. + */ +static void +do_rules(FILE *f) +{ +	char *cp, *np, och; +	struct file_list *ftp; +	char *compilewith; +	char cmd[128]; + +	STAILQ_FOREACH(ftp, &ftab, f_next) { +		if (ftp->f_warn) +			fprintf(stderr, "WARNING: %s\n", ftp->f_warn); +		cp = (np = ftp->f_fn) + strlen(ftp->f_fn) - 1; +		och = *cp; +		if (ftp->f_flags & NO_IMPLCT_RULE) { +			if (ftp->f_depends) +				fprintf(f, "%s%s: %s\n", +					ftp->f_objprefix, np, ftp->f_depends); +			else +				fprintf(f, "%s%s: \n", ftp->f_objprefix, np); +		} +		else { +			*cp = '\0'; +			if (och == 'o') { +				fprintf(f, "%s%so:\n\t-cp %s%so .\n\n", +					ftp->f_objprefix, tail(np), +					ftp->f_srcprefix, np); +				continue; +			} +			if (ftp->f_depends) { +				fprintf(f, "%s%so: %s%s%c %s\n", +					ftp->f_objprefix, tail(np), +					ftp->f_srcprefix, np, och, +					ftp->f_depends); +			} +			else { +				fprintf(f, "%s%so: %s%s%c\n", +					ftp->f_objprefix, tail(np), +					ftp->f_srcprefix, np, och); +			} +		} +		compilewith = ftp->f_compilewith; +		if (compilewith == NULL) { +			const char *ftype = NULL; + +			switch (ftp->f_type) { +			case NORMAL: +				ftype = "NORMAL"; +				break; +			default: +				fprintf(stderr, +				    "config: don't know rules for %s\n", np); +				break; +			} +			snprintf(cmd, sizeof(cmd), +			    "${%s_%c%s}", ftype, +			    toupper(och), +			    ftp->f_flags & NOWERROR ? "_NOWERROR" : ""); +			compilewith = cmd; +		} +		*cp = och; +		if (strlen(ftp->f_objprefix)) +			fprintf(f, "\t%s %s%s\n", compilewith, +			    ftp->f_srcprefix, np); +		else +			fprintf(f, "\t%s\n", compilewith); + +		if (!(ftp->f_flags & NO_CTFCONVERT)) +			fprintf(f, "\t${NORMAL_CTFCONVERT}\n\n"); +		else +			fprintf(f, "\n"); +	} +} + +static void +do_clean(FILE *fp) +{ +	struct file_list *tp; +	int lpos, len; + +	fputs("CLEAN=", fp); +	lpos = 7; +	STAILQ_FOREACH(tp, &ftab, f_next) +		if (tp->f_clean) { +			len = strlen(tp->f_clean); +			if (len + lpos > 72) { +				lpos = 8; +				fputs("\\\n\t", fp); +			} +			fprintf(fp, "%s ", tp->f_clean); +			lpos += len + 1; +		} +	if (lpos != 8) +		putc('\n', fp); +} + +char * +raisestr(char *str) +{ +	char *cp = str; + +	while (*str) { +		if (islower(*str)) +			*str = toupper(*str); +		str++; +	} +	return (cp); +} diff --git a/usr.sbin/config/mkoptions.cc b/usr.sbin/config/mkoptions.cc new file mode 100644 index 000000000000..3b5ed17e455c --- /dev/null +++ b/usr.sbin/config/mkoptions.cc @@ -0,0 +1,460 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1995  Peter Wemm + * Copyright (c) 1980, 1993 + *	The Regents of the University of California.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Make all the .h files for the optional entries + */ + +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/param.h> +#include "config.h" +#include "y.tab.h" + +static	struct users { +	int	u_default; +	int	u_min; +	int	u_max; +} users = { 8, 2, 512 }; + +static char *lower(char *); +static void read_options(void); +static void do_option(char *); +static char *tooption(char *); + +void +options(void) +{ +	char buf[40]; +	struct cputype *cp; +	struct opt_list *ol; +	struct opt *op; + +	/* Fake the cpu types as options. */ +	SLIST_FOREACH(cp, &cputype, cpu_next) { +		op = (struct opt *)calloc(1, sizeof(*op)); +		if (op == NULL) +			err(EXIT_FAILURE, "calloc"); +		op->op_name = ns(cp->cpu_name); +		SLIST_INSERT_HEAD(&opt, op, op_next); +	}	 + +	if (maxusers == 0) { +		if (verbose) +			fprintf(stderr, +			    "maxusers not specified; will auto-size\n"); +	} else if (maxusers < users.u_min) { +		fprintf(stderr, "minimum of %d maxusers assumed\n", +		    users.u_min); +		maxusers = users.u_min; +	} else if (maxusers > users.u_max) +		fprintf(stderr, "warning: maxusers > %d (%d)\n", +		    users.u_max, maxusers); + +	/* Fake MAXUSERS as an option. */ +	op = (struct opt *)calloc(1, sizeof(*op)); +	if (op == NULL) +		err(EXIT_FAILURE, "calloc"); +	op->op_name = ns("MAXUSERS"); +	snprintf(buf, sizeof(buf), "%d", maxusers); +	op->op_value = ns(buf); +	SLIST_INSERT_HEAD(&opt, op, op_next); + +	read_options(); + +	/* Fake the value of MACHINE_ARCH as an option if necessary */ +	SLIST_FOREACH(ol, &otab, o_next) { +		if (strcasecmp(ol->o_name, machinearch) != 0) +			continue; + +		op = (struct opt *)calloc(1, sizeof(*op)); +		if (op == NULL) +			err(EXIT_FAILURE, "calloc"); +		op->op_name = ns(ol->o_name); +		SLIST_INSERT_HEAD(&opt, op, op_next); +		break; +	} + +	SLIST_FOREACH(op, &opt, op_next) { +		SLIST_FOREACH(ol, &otab, o_next) { +			if (eq(op->op_name, ol->o_name) && +			    (ol->o_flags & OL_ALIAS)) { +				fprintf(stderr, "Mapping option %s to %s.\n", +				    op->op_name, ol->o_file); +				op->op_name = ol->o_file; +				break; +			} +		} +	} +	SLIST_FOREACH(ol, &otab, o_next) +		do_option(ol->o_name); +	SLIST_FOREACH(op, &opt, op_next) { +		if (!op->op_ownfile && strncmp(op->op_name, "DEV_", 4)) { +			fprintf(stderr, "%s: unknown option \"%s\"\n", +			       PREFIX, op->op_name); +			exit(1); +		} +	} +} + +/* + * Generate an <options>.h file + */ + +static void +do_option(char *name) +{ +	char *file; +	const char *basefile; +	struct opt_list *ol; +	struct opt *op; +	struct opt_head op_head; +	FILE *inf, *outf; +	char *value; +	char *oldvalue; +	int seen; +	int tidy; + +	file = tooption(name); +	/* +	 * Check to see if the option was specified.. +	 */ +	value = NULL; +	SLIST_FOREACH(op, &opt, op_next) { +		if (eq(name, op->op_name)) { +			oldvalue = value; +			value = op->op_value; +			if (value == NULL) +				value = ns("1"); +			if (oldvalue != NULL && !eq(value, oldvalue)) +				fprintf(stderr, +			    "%s: option \"%s\" redefined from %s to %s\n", +				   PREFIX, op->op_name, oldvalue, +				   value); +			op->op_ownfile++; +		} +	} + +	remember(file); +	inf = fopen(file, "r"); +	if (inf == NULL) { +		outf = fopen(file, "w"); +		if (outf == NULL) +			err(1, "%s", file); + +		/* was the option in the config file? */ +		if (value) { +			fprintf(outf, "#define %s %s\n", name, value); +		} /* else empty file */ + +		(void)fclose(outf); +		return; +	} +	basefile = ""; +	SLIST_FOREACH(ol, &otab, o_next) +		if (eq(name, ol->o_name)) { +			basefile = ol->o_file; +			break; +		} +	oldvalue = NULL; +	SLIST_INIT(&op_head); +	seen = 0; +	tidy = 0; +	for (;;) { +		configword cp, inw; +		char *invalue; + +		/* get the #define */ +		if ((inw = get_word(inf)).eol() || inw.eof()) +			break; +		/* get the option name */ +		if ((inw = get_word(inf)).eol() || inw.eof()) +			break; +		/* get the option value */ +		if ((cp = get_word(inf)).eol() || cp.eof()) +			break; +		/* option value */ +		invalue = ns(cp); /* malloced */ +		if (eq(inw, name)) { +			oldvalue = invalue; +			invalue = value; +			seen++; +		} +		SLIST_FOREACH(ol, &otab, o_next) +			if (eq(inw, ol->o_name)) +				break; +		if (!eq(inw, name) && !ol) { +			fprintf(stderr, +			    "WARNING: unknown option `%s' removed from %s\n", +			    inw->c_str(), file); +			tidy++; +		} else if (ol != NULL && !eq(basefile, ol->o_file)) { +			fprintf(stderr, +			    "WARNING: option `%s' moved from %s to %s\n", +			    inw->c_str(), basefile, ol->o_file); +			tidy++; +		} else { +			op = (struct opt *) calloc(1, sizeof *op); +			if (op == NULL) +				err(EXIT_FAILURE, "calloc"); +			op->op_name = ns(inw); +			op->op_value = invalue; +			SLIST_INSERT_HEAD(&op_head, op, op_next); +		} + +		/* EOL? */ +		cp = get_word(inf); +		if (cp.eof()) +			break; +	} +	(void)fclose(inf); +	if (!tidy && ((value == NULL && oldvalue == NULL) || +	    (value && oldvalue && eq(value, oldvalue)))) {	 +		while (!SLIST_EMPTY(&op_head)) { +			op = SLIST_FIRST(&op_head); +			SLIST_REMOVE_HEAD(&op_head, op_next); +			free(op->op_name); +			free(op->op_value); +			free(op); +		} +		return; +	} + +	if (value && !seen) { +		/* New option appears */ +		op = (struct opt *) calloc(1, sizeof *op); +		if (op == NULL) +			err(EXIT_FAILURE, "calloc"); +		op->op_name = ns(name); +		op->op_value = ns(value); +		SLIST_INSERT_HEAD(&op_head, op, op_next); +	} + +	outf = fopen(file, "w"); +	if (outf == NULL) +		err(1, "%s", file); +	while (!SLIST_EMPTY(&op_head)) { +		op = SLIST_FIRST(&op_head); +		/* was the option in the config file? */ +		if (op->op_value) { +			fprintf(outf, "#define %s %s\n", +				op->op_name, op->op_value); +		} +		SLIST_REMOVE_HEAD(&op_head, op_next); +		free(op->op_name); +		free(op->op_value); +		free(op); +	} +	(void)fclose(outf); +} + +/* + * Find the filename to store the option spec into. + */ +static char * +tooption(char *name) +{ +	static char hbuf[MAXPATHLEN]; +	char nbuf[MAXPATHLEN]; +	struct opt_list *po; +	char *fpath; + +	/* "cannot happen"?  the otab list should be complete.. */ +	(void)strlcpy(nbuf, "options.h", sizeof(nbuf)); + +	SLIST_FOREACH(po, &otab, o_next) { +		if (eq(po->o_name, name)) { +			strlcpy(nbuf, po->o_file, sizeof(nbuf)); +			break; +		} +	} + +	fpath = path(nbuf); +	(void)strlcpy(hbuf, fpath, sizeof(hbuf)); +	free(fpath); +	return (hbuf); +} + +	 +static void +check_duplicate(const char *fname, const char *chkopt) +{ +	struct opt_list *po; + +	SLIST_FOREACH(po, &otab, o_next) { +		if (eq(po->o_name, chkopt)) { +			fprintf(stderr, "%s: Duplicate option %s.\n", +			    fname, chkopt); +			exit(1); +		} +	} +} + +static void +insert_option(const char *fname, char *optname, char *val) +{ +	struct opt_list *po; + +	check_duplicate(fname, optname); +	po = (struct opt_list *) calloc(1, sizeof *po); +	if (po == NULL) +		err(EXIT_FAILURE, "calloc"); +	po->o_name = optname; +	po->o_file = val; +	po->o_flags = 0; +	SLIST_INSERT_HEAD(&otab, po, o_next); +} + +static void +update_option(const char *optname, char *val, int flags) +{ +	struct opt_list *po; + +	SLIST_FOREACH(po, &otab, o_next) { +		if (eq(po->o_name, optname)) { +			free(po->o_file); +			po->o_file = val; +			po->o_flags = flags; +			return; +		} +	} +	/* +	 * Option not found, but that's OK, we just ignore it since it +	 * may be for another arch. +	 */ +	return; +} + +static int +read_option_file(const char *fname, int flags) +{ +	FILE *fp; +	configword wd; +	char *optname, *val; +	char genopt[MAXPATHLEN]; + +	fp = fopen(fname, "r"); +	if (fp == NULL) { +		if (verbose) { +			getcwd(genopt, sizeof(genopt)); +			fprintf(stderr, "Unable to open options file: %s\n", +			    fname); +			fprintf(stderr, "CWD: %s\n", genopt); +		} +		return (0); +	} +	while (!(wd = get_word(fp)).eof()) { +		if (wd.eol()) +			continue; +		if (wd[0] == '#') { +			while (!(wd = get_word(fp)).eof() && !wd.eol()) +				continue; +			continue; +		} +		optname = ns(wd); +		wd = get_word(fp); +		if (wd.eof()) { +			free(optname); +			break; +		} +		if (wd.eol()) { +			if (flags) { +				fprintf(stderr, "%s: compat file requires two" +				    " words per line at %s\n", fname, optname); +				exit(1); +			} +			char *s = ns(optname); +			(void)snprintf(genopt, sizeof(genopt), "opt_%s.h", +			    lower(s)); +			val = ns(genopt); +			free(s); +		} else { +			val = ns(wd); +		} + +		if (flags == 0) { +			/* +			 * insert_option takes possession of `optname` in the +			 * new option instead of making yet another copy. +			 */ +			insert_option(fname, optname, val); +		} else { +			update_option(optname, val, flags); +			free(optname); +			optname = NULL; +		} +	} +	(void)fclose(fp); +	return (1); +} + +/* + * read the options and options.<machine> files + */ +static void +read_options(void) +{ +	char fname[MAXPATHLEN]; +	struct files_name *nl, *tnl; + +	SLIST_INIT(&otab); +	read_option_file("../../conf/options", 0); +	(void)snprintf(fname, sizeof fname, "../../conf/options.%s", +	    machinename); +	if (!read_option_file(fname, 0)) { +		(void)snprintf(fname, sizeof fname, "options.%s", machinename); +		read_option_file(fname, 0); +	} +	for (nl = STAILQ_FIRST(&optfntab); nl != NULL; nl = tnl) { +		read_option_file(nl->f_name, 0); +		tnl = STAILQ_NEXT(nl, f_next); +		free(nl->f_name); +		free(nl); +	} +	read_option_file("../../conf/options-compat", OL_ALIAS); +} + +static char * +lower(char *str) +{ +	char *cp = str; + +	while (*str) { +		if (isupper(*str)) +			*str = tolower(*str); +		str++; +	} +	return (cp); +} | 
