diff --git a/usr.sbin/config/config.5 b/usr.sbin/config/config.5 index f77b79a31510..8c2c62b706bd 100644 --- a/usr.sbin/config/config.5 +++ b/usr.sbin/config/config.5 @@ -1,448 +1,457 @@ .\" 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 October 17, 2022 .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 Example: .Bd -literal -offset indent -compact makeoptions MYMAKEOPTION="foo" makeoptions MYMAKEOPTION+="bar" makeoptions MYNULLMAKEOPTION .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 index ccb61cdf2786..e0912a4bc104 100644 --- a/usr.sbin/config/config.8 +++ b/usr.sbin/config/config.8 @@ -1,275 +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 CVgp +.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 index 16562a7f8210..6085c52d1b95 100644 --- a/usr.sbin/config/config.h +++ b/usr.sbin/config/config.h @@ -1,276 +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 #include #include #include #include #ifdef __cplusplus #include 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 index 4120ffa54b1c..15139197dbb6 100644 --- a/usr.sbin/config/config.y +++ b/usr.sbin/config/config.y @@ -1,481 +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 ENVLINE %token ID %token NUMBER %type Save_id %type Opt_value %type Dev %token 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 #include #include #include #include #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/lang.l b/usr.sbin/config/lang.l index a6558237db5a..2052d98d1fdd 100644 --- a/usr.sbin/config/lang.l +++ b/usr.sbin/config/lang.l @@ -1,335 +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 #include #include #include #include #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 %% [^#\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; } [^# \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; } <> { 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 index 0c0b9bb27dc3..1e89cbf3230a 100644 --- a/usr.sbin/config/main.cc +++ b/usr.sbin/config/main.cc @@ -1,814 +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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); - while ((ch = getopt(argc, argv, "Cd:gI:mps:Vx:")) != -1) + 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/mkoptions.cc b/usr.sbin/config/mkoptions.cc index 1580700dc08d..3b5ed17e455c 100644 --- a/usr.sbin/config/mkoptions.cc +++ b/usr.sbin/config/mkoptions.cc @@ -1,443 +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 #include #include #include +#include #include #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) { - /* fprintf(stderr, "maxusers not specified; will auto-size\n"); */ + 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 .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 (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. 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); }