Improve INCLUDE_CONFIG_FILE support.

This change will let us to have full configuration of a running kernel
available in sysctl:

	sysctl -b kern.conftxt

The same configuration is also contained within the kernel image. It can be
obtained with:

	config -x <kernelfile>

Current functionality lets you to quickly recover kernel configuration, by
simply redirecting output from commands presented above and starting kernel
build procedure. "include" statements are also honored, which means options
and devices from included files are also included.

Please note that comments from configuration files are not preserved by
default. In order to preserve them, you can use -C flag for config(8). This
will bring configuration file and included files literally; however,
redirection to a file no longer works directly.

This commit was followed by discussion, that took place on freebsd-current@.
For more details, look here:

	http://lists.freebsd.org/pipermail/freebsd-current/2007-March/069994.html
	http://lists.freebsd.org/pipermail/freebsd-current/2007-May/071844.html

Development of this patch took place in Perforce, hierarchy:

	//depot/user/wkoszek/wkoszek_kconftxt/

Support from:	freebsd-current@ (links above)
Reviewed by:	imp@
Approved by:	imp@
This commit is contained in:
Wojciech A. Koszek 2007-05-12 19:38:18 +00:00
parent fbe4fc2ec0
commit 744b947ef8
8 changed files with 422 additions and 90 deletions

View File

@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/sbuf.h>
#include <sys/systm.h>
#include <sys/sysctl.h>
#include <sys/proc.h>
@ -295,6 +296,38 @@ SYSCTL_PROC(_kern, KERN_SECURELVL, securelevel,
CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_PRISON, 0, 0, sysctl_kern_securelvl,
"I", "Current secure level");
/* Actual kernel configuration options. */
extern char kernconfstring[];
static int
sysctl_kern_config(SYSCTL_HANDLER_ARGS)
{
struct sbuf *sb;
int error;
char *p;
sb = sbuf_new(NULL, NULL, 2048, SBUF_AUTOEXTEND);
if (sb == NULL)
return (ENOMEM);
sbuf_clear(sb);
p = kernconfstring;
if (p == NULL || *p == '\0') {
sbuf_printf(sb, "No kernel configuration\n");
} else {
sbuf_printf(sb, "%s", p);
}
sbuf_trim(sb);
sbuf_putc(sb, '\n');
sbuf_finish(sb);
error = sysctl_handle_string(oidp, sbuf_data(sb), sbuf_len(sb), req);
if (error)
return (error);
sbuf_delete(sb);
return (error);
}
SYSCTL_PROC(_kern, OID_AUTO, conftxt, CTLTYPE_STRING|CTLFLAG_RW,
0, 0, sysctl_kern_config, "", "Kernel configuration file");
char domainname[MAXHOSTNAMELEN];
SYSCTL_STRING(_kern, KERN_NISDOMAINNAME, domainname, CTLFLAG_RW,
&domainname, sizeof(domainname), "Name of the current YP/NIS domain");

View File

@ -4,13 +4,18 @@
PROG= config
MAN= config.5 config.8
SRCS= config.y main.c lang.l mkmakefile.c mkheaders.c \
mkoptions.c y.tab.h
mkoptions.c y.tab.h kernconf.c
kernconf.c: kernconf.tmpl
file2c 'char kernconfstr[] = {' ',0};' < kernconf.tmpl > kernconf.c
WARNS?= 6
CFLAGS+= -I. -I${.CURDIR}
DPADD= ${LIBL}
LDADD= -ll
LDADD= -ll -lsbuf
CLEANFILES+= kernconf.c
mkmakefile.o: configvers.h

View File

@ -28,7 +28,7 @@
.\" @(#)config.8 8.2 (Berkeley) 4/19/94
.\" $FreeBSD$
.\"
.Dd December 16, 2004
.Dd May 8, 2007
.Dt CONFIG 8
.Os
.Sh NAME
@ -36,9 +36,11 @@
.Nd build system configuration files
.Sh SYNOPSIS
.Nm
.Op Fl Vgp
.Op Fl CVgp
.Op Fl d Ar destdir
.Ar SYSTEM_NAME
.Nm
.Op Fl x Ar kernel
.Sh DESCRIPTION
.\" This is the old version of the
.\" .Nm
@ -76,6 +78,11 @@ Available options and operands:
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 d Ar destdir
Use
.Ar destdir
@ -87,6 +94,12 @@ does not append
to the directory given.
.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 p
Configure a system for profiling; for example,
.Xr kgmon 8
@ -151,17 +164,6 @@ the problems in the configuration file should be corrected and
should be run again.
Attempts to compile a system that had configuration errors
are likely to fail.
.Pp
If the
.Cd "options INCLUDE_CONFIG_FILE"
is used in the configuration file the
entire input file is embedded in the new kernel.
This means that
.Xr strings 1
can be used to extract it from a kernel:
to extract the configuration information, use the command
.Pp
.Dl "strings -n 3 kernel | sed -n 's/^___//p'"
.Sh DEBUG KERNELS
Traditional
.Bx
@ -254,5 +256,19 @@ 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.

View File

@ -38,6 +38,12 @@
#include <stdlib.h>
#include <string.h>
struct cfgfile {
STAILQ_ENTRY(cfgfile) cfg_next;
char *cfg_path;
};
STAILQ_HEAD(, cfgfile) cfgfiles;
struct file_list {
STAILQ_ENTRY(file_list) f_next;
char *f_fn; /* the name */
@ -117,7 +123,7 @@ struct opt {
SLIST_ENTRY(opt) op_next;
};
SLIST_HEAD(opt_head, opt) opt, mkopt;
SLIST_HEAD(opt_head, opt) opt, mkopt, rmopts;
struct opt_list {
char *o_name;
@ -134,11 +140,26 @@ struct hint {
STAILQ_HEAD(hint_head, hint) hints;
/*
* Tag present in the kernelconf.tmlp 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 *env;
extern char kernconfstr[];
extern int do_trace;
extern int envmode;
extern int hintmode;
extern int incignore;
char *get_word(FILE *);
char *get_quoted_word(FILE *);
@ -153,8 +174,10 @@ void makefile(void);
void makeenv(void);
void makehints(void);
void headers(void);
void cfgfile_add(const char *);
void cfgfile_removeall(void);
extern STAILQ_HEAD(device_head, device) dtab;
extern STAILQ_HEAD(device_head, device) dtab, rmdtab;
extern char errbuf[80];
extern int yyline;

View File

@ -70,6 +70,7 @@
* $FreeBSD$
*/
#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <stdio.h>
@ -77,7 +78,7 @@
#include "config.h"
struct device_head dtab;
struct device_head dtab, rmdtab;
char *ident;
char *env;
int envmode;
@ -104,6 +105,9 @@ devopt(char *dev)
return ret;
}
static void rmoptall(struct opt_head *list, struct opt_head *torem);
static void rmdevall(struct device_head *dh, struct device_head *torem);
%}
%%
Configuration:
@ -122,7 +126,10 @@ Spec:
Config_spec SEMICOLON
|
INCLUDE ID SEMICOLON
= { include($2, 0); };
= {
if (incignore == 0)
include($2, 0);
};
|
FILES ID SEMICOLON
= { newfile($2); };
@ -170,11 +177,11 @@ Config_spec:
OPTIONS Opt_list
|
NOOPTION Save_id
= { rmopt(&opt, $2); } |
= { rmopt_schedule(&rmopts, $2); } |
MAKEOPTIONS Mkopt_list
|
NOMAKEOPTION Save_id
= { rmopt(&mkopt, $2); } |
= { rmopt_schedule(&mkopt, $2); } |
IDENT ID
= { ident = $2; } |
System_spec
@ -298,10 +305,10 @@ NoDevice:
= {
char *s = devopt($1);
rmopt(&opt, s);
rmopt_schedule(&rmopts, s);
free(s);
/* and the device part */
rmdev($1);
rmdev_schedule(&rmdtab, $1);
} ;
%%
@ -317,14 +324,16 @@ int
yywrap(void)
{
if (found_defaults) {
if (freopen(PREFIX, "r", stdin) == NULL)
err(2, "%s", PREFIX);
yyfile = PREFIX;
if (found_defaults == 0 && incignore == 0) {
if (freopen("DEFAULTS", "r", stdin) == NULL)
return 1;
yyfile = "DEFAULTS";
yyline = 0;
found_defaults = 0;
found_defaults = 1;
return 0;
}
rmoptall(&opt, &rmopts);
rmdevall(&dtab, &rmdtab);
return 1;
}
@ -345,11 +354,11 @@ newfile(char *name)
* Find a device in the list of devices.
*/
static struct device *
finddev(char *name)
finddev(struct device_head *dlist, char *name)
{
struct device *dp;
STAILQ_FOREACH(dp, &dtab, d_next)
STAILQ_FOREACH(dp, dlist, d_next)
if (eq(dp->d_name, name))
return (dp);
@ -364,7 +373,7 @@ newdev(char *name)
{
struct device *np;
if (finddev(name)) {
if (finddev(&dtab, name)) {
printf("WARNING: duplicate device `%s' encountered.\n", name);
return;
}
@ -375,17 +384,36 @@ newdev(char *name)
}
/*
* Remove a device from the list of devices.
* Schedule a device to removal.
*/
static void
rmdev(char *name)
rmdev_schedule(struct device_head *dh, char *name)
{
struct device *dp;
dp = finddev(name);
if (dp != NULL) {
STAILQ_REMOVE(&dtab, dp, device, d_next);
free(dp->d_name);
dp = calloc(1, sizeof(struct device));
dp->d_name = strdup(name);
assert(dp->d_name != NULL);
STAILQ_INSERT_HEAD(dh, dp, d_next);
}
/*
* Take care a devices previously scheduled for removal.
*/
static void
rmdevall(struct device_head *dh, struct device_head *torem)
{
struct device *dp, *rdp;
while (!STAILQ_EMPTY(torem)) {
dp = STAILQ_FIRST(torem);
STAILQ_REMOVE_HEAD(torem, d_next);
rdp = finddev(dh, dp->d_name);
if (rdp != NULL) {
STAILQ_REMOVE(dh, rdp, device, d_next);
free(rdp->d_name);
free(rdp);
}
free(dp);
}
}
@ -413,6 +441,14 @@ newopt(struct opt_head *list, char *name, char *value)
{
struct opt *op;
/*
* Ignore inclusions listed explicitly for configuration files.
*/
if (eq(name, OPT_AUTOGEN)) {
incignore = 1;
return;
}
if (findopt(list, name)) {
printf("WARNING: duplicate option `%s' encountered.\n", name);
return;
@ -429,16 +465,35 @@ newopt(struct opt_head *list, char *name, char *value)
* Remove an option from the list of options.
*/
static void
rmopt(struct opt_head *list, char *name)
rmopt_schedule(struct opt_head *list, char *name)
{
struct opt *op;
op = findopt(list, name);
if (op != NULL) {
SLIST_REMOVE(list, op, opt, op_next);
free(op->op_name);
if (op->op_value != NULL)
free(op->op_value);
op = calloc(1, sizeof(*op));
op->op_name = ns(name);
SLIST_INSERT_HEAD(list, op, op_next);
}
/*
* Remove all options that were scheduled for removal.
*/
static void
rmoptall(struct opt_head *list, struct opt_head *torem)
{
struct opt *op, *rop;
op = rop = NULL;
while (!SLIST_EMPTY(torem)) {
op = SLIST_FIRST(torem);
SLIST_REMOVE_HEAD(torem, op_next);
rop = findopt(list, op->op_name);
if (rop != NULL) {
SLIST_REMOVE(list, rop, opt, op_next);
free(rop->op_name);
if (rop->op_value != NULL)
free(rop->op_value);
free(rop);
}
free(op);
}
}

View File

@ -0,0 +1,17 @@
/*
* 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.
*
* $FreeBSD$
*/
#include "opt_config.h"
#ifdef INCLUDE_CONFIG_FILE
const char kernconfstring[] __attribute__ ((section("kern_conf"))) =
"%%KERNCONFFILE%%";
#else
const char kernconfstring[] = "\0";
#endif /* INCLUDE_CONFIG_FILE */

View File

@ -207,6 +207,30 @@ hex(const char *str)
return num;
}
void
cfgfile_add(const char *fname)
{
struct cfgfile *cf;
cf = calloc(1, sizeof(*cf));
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
@ -222,6 +246,7 @@ include(const char *fname, int ateof)
struct incl *in;
char *fnamebuf;
fnamebuf = NULL;
fp = fopen(fname, "r");
if (fp == NULL && fname[0] != '.' && fname[0] != '/') {
asprintf(&fnamebuf, "../../conf/%s", fname);
@ -234,6 +259,7 @@ include(const char *fname, int ateof)
yyerror("cannot open included file");
return (-1);
}
cfgfile_add(fnamebuf == NULL ? fname : fnamebuf);
in = malloc(sizeof(*in));
assert(in != NULL);
in->in_prev = inclp;

View File

@ -43,9 +43,12 @@ static const char rcsid[] =
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sbuf.h>
#include <sys/file.h>
#include <sys/mman.h>
#include <sys/param.h>
#include <assert.h>
#include <ctype.h>
#include <err.h>
#include <stdio.h>
@ -73,11 +76,19 @@ char srcdir[MAXPATHLEN];
int debugging;
int profiling;
int found_defaults;
int incignore;
/*
* Preserve old behaviour in INCLUDE_CONFIG_FILE handling (files are included
* literally).
*/
int filebased = 0;
static void configfile(void);
static void get_srcdir(void);
static void usage(void);
static void cleanheaders(char *);
static void kernconfdump(const char *);
struct hdr_list {
char *h_name;
@ -96,13 +107,14 @@ main(int argc, char **argv)
int ch, len;
char *p;
char xxx[MAXPATHLEN];
FILE *fp;
char *kernfile;
while ((ch = getopt(argc, argv, "d:gpV")) != -1)
kernfile = NULL;
while ((ch = getopt(argc, argv, "Cd:gpVx:")) != -1)
switch (ch) {
case 'V':
printf("%d\n", CONFIGVERS);
exit(0);
case 'C':
filebased = 1;
break;
case 'd':
if (*destdir == '\0')
strlcpy(destdir, optarg, sizeof(destdir));
@ -115,6 +127,12 @@ main(int argc, char **argv)
case 'p':
profiling++;
break;
case 'V':
printf("%d\n", CONFIGVERS);
exit(0);
case 'x':
kernfile = optarg;
break;
case '?':
default:
usage();
@ -122,23 +140,23 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
if (kernfile != NULL) {
kernconfdump(kernfile);
exit(EXIT_SUCCESS);
}
if (argc != 1)
usage();
PREFIX = *argv;
fp = fopen(PREFIX, "r");
if (fp == NULL)
/*
* We mark lack of DEFAULTS here. Once we hit EOF in PREFIX, yywrap()
* will try to bring DEFAULTS to the playground, if this exists.
*/
found_defaults = 0;
if (freopen(PREFIX, "r", stdin) == NULL)
err(2, "%s", PREFIX);
fclose(fp);
if (freopen("DEFAULTS", "r", stdin) != NULL) {
found_defaults = 1;
yyfile = "DEFAULTS";
} else {
if (freopen(PREFIX, "r", stdin) == NULL)
err(2, "%s", PREFIX);
yyfile = PREFIX;
}
yyfile = PREFIX;
if (*destdir != '\0') {
len = strlen(destdir);
while (len > 1 && destdir[len - 1] == '/')
@ -156,11 +174,16 @@ main(int argc, char **argv)
} else if (!S_ISDIR(buf.st_mode))
errx(2, "%s isn't a directory", p);
SLIST_INIT(&cputype);
SLIST_INIT(&mkopt);
SLIST_INIT(&opt);
SLIST_INIT(&rmopts);
STAILQ_INIT(&cfgfiles);
STAILQ_INIT(&dtab);
STAILQ_INIT(&fntab);
SLIST_INIT(&cputype);
STAILQ_INIT(&ftab);
STAILQ_INIT(&hints);
STAILQ_INIT(&rmdtab);
if (yyparse())
exit(3);
@ -206,12 +229,12 @@ main(int argc, char **argv)
(void) unlink(path(machinearch));
(void) symlink(xxx, path(machinearch));
}
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 */
configfile(); /* put config file into kernel*/
cleanheaders(p);
printf("Kernel build directory is %s\n", p);
printf("Don't forget to do ``make cleandepend && make depend''\n");
@ -235,8 +258,9 @@ static void
usage(void)
{
fprintf(stderr, "usage: config [-Vgp] [-d destdir] sysname\n");
exit(1);
fprintf(stderr, "usage: config [-CgpV] [-d destdir] sysname\n");
fprintf(stderr, " config -x kernel\n");
exit(EX_USAGE);
}
/*
@ -362,40 +386,119 @@ path(const char *file)
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(struct sbuf *sb)
{
struct cputype *cput;
struct device *d;
struct opt *ol;
char *lend;
asprintf(&lend, "\\n\\\n");
assert(lend != NULL);
sbuf_printf(sb, "options\t%s%s", OPT_AUTOGEN, lend);
sbuf_printf(sb, "ident\t%s%s", ident, lend);
sbuf_printf(sb, "machine\t%s%s", machinename, lend);
SLIST_FOREACH(cput, &cputype, cpu_next)
sbuf_printf(sb, "cpu\t%s%s", cput->cpu_name, lend);
SLIST_FOREACH(ol, &mkopt, op_next)
sbuf_printf(sb, "makeoptions\t%s=%s%s", ol->op_name,
ol->op_value, lend);
SLIST_FOREACH(ol, &opt, op_next) {
if (strncmp(ol->op_name, "DEV_", 4) == 0)
continue;
sbuf_printf(sb, "options\t%s", ol->op_name);
if (ol->op_value != NULL) {
sbuf_printf(sb, "=%s%s", ol->op_value, lend);
} else {
sbuf_printf(sb, "%s", lend);
}
}
/*
* Mark this file as containing everything we need.
*/
STAILQ_FOREACH(d, &dtab, d_next)
sbuf_printf(sb, "device\t%s%s", d->d_name, lend);
free(lend);
}
/*
* Generate file from the configuration files.
*/
static void
configfile_filebased(struct sbuf *sb)
{
FILE *cff;
struct cfgfile *cf;
int i;
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')
sbuf_printf(sb, "\\n\\\n");
else if (i == '"' || i == '\'')
sbuf_printf(sb, "\\%c", i);
else
sbuf_putc(sb, i);
}
fclose(cff);
}
}
static void
configfile(void)
{
FILE *fi, *fo;
FILE *fo;
struct sbuf *sb;
char *p;
int i;
fi = fopen(PREFIX, "r");
if (!fi)
err(2, "%s", PREFIX);
fo = fopen(p=path("config.c.new"), "w");
/* 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);
fprintf(fo, "#include \"opt_config.h\"\n");
fprintf(fo, "#ifdef INCLUDE_CONFIG_FILE \n");
fprintf(fo, "const char config[] = \"\\\n");
fprintf(fo, "START CONFIG FILE %s\\n\\\n___", PREFIX);
while (EOF != (i=getc(fi))) {
if (i == '\n') {
fprintf(fo, "\\n\\\n___");
} else if (i == '\"') {
fprintf(fo, "\\\"");
} else if (i == '\\') {
fprintf(fo, "\\\\");
} else {
putc(i, fo);
}
sb = sbuf_new(NULL, NULL, 2048, SBUF_AUTOEXTEND);
assert(sb != NULL);
sbuf_clear(sb);
/*
* 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.
*/
if (filebased) {
/* Is needed, can be used for backward compatibility. */
configfile_filebased(sb);
} else {
configfile_dynamic(sb);
}
fprintf(fo, "\\n\\\nEND CONFIG FILE %s\\n\\\n", PREFIX);
fprintf(fo, "\";\n");
fprintf(fo, "\n#endif /* INCLUDE_CONFIG_FILE */\n");
fclose(fi);
sbuf_finish(sb);
/*
* We print first part of the tamplate, 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", sbuf_data(sb));
p += strlen(KERNCONFTAG);
fprintf(fo, "%s", p);
sbuf_delete(sb);
fclose(fo);
moveifchanged(path("config.c.new"), path("config.c"));
cfgfile_removeall();
}
/*
@ -524,3 +627,57 @@ remember(const char *file)
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, len, osz, r;
unsigned int off, size;
char *cmd, *o;
r = open(file, O_RDONLY);
if (r == -1)
errx(EXIT_FAILURE, "Couldn't open file '%s'", file);
error = fstat(r, &st);
if (error == -1)
errx(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)
errx(EXIT_FAILURE, "fdopen() failed");
osz = 1024;
o = calloc(1, osz);
if (o == NULL)
errx(EXIT_FAILURE, "Couldn't allocate memory");
/* ELF note section header. */
asprintf(&cmd, "/usr/bin/elfdump -c %s | grep -A 5 kern_conf"
"| tail -2 | 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);
len = fread(o, osz, 1, pp);
pclose(pp);
r = sscanf(o, "%d\t%d", &off, &size);
free(o);
if (r != 2)
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)
errx(EXIT_FAILURE, "fseek() failed");
while ((r = fgetc(fp)) != EOF && size-- > 0)
fputc(r, stdout);
fclose(fp);
}