svn commit: r41333 - projects/sysctl/share/tools/sysctl

Dag-Erling Smørgrav des at FreeBSD.org
Mon Apr 1 10:24:18 UTC 2013


Author: des
Date: Mon Apr  1 10:24:17 2013
New Revision: 41333
URL: http://svnweb.freebsd.org/changeset/doc/41333

Log:
  The first rough elements of the sysctl documentation harvesting
  system:
  
   - sysctlharvest is a Perl script that gathers information about
     sysctls from the system it runs on and outputs it as either a
     single XML file or one file per node.
  
   - sysctlfromelf is a C program that extracts information about
     sysctls from kernel and module binaries and outputs it as a single
     XML file.
  
  Still to do:
  
   - sysctlharvest should be able to read existing XML data, including
     both its own output and output from sysctlfromelf.
  
   - sysctlfromelf was written with the assumption that sysctl
     information can be read from modules independently.  In fact, it
     needs to load the kernel first, then perform relocation on the
     modules to be able to resolve parent-child relationships.
  
  Nice to have:
  
   - sysctlharvest should be able to write XML data to a tar file.
  
   - sysctlharvest should be able to read XML data from a tar file.

Added:
  projects/sysctl/share/tools/sysctl/
  projects/sysctl/share/tools/sysctl/Makefile   (contents, props changed)
  projects/sysctl/share/tools/sysctl/elfexplorer.c   (contents, props changed)
  projects/sysctl/share/tools/sysctl/elfexplorer.h   (contents, props changed)
  projects/sysctl/share/tools/sysctl/sysctlfromelf.c   (contents, props changed)
  projects/sysctl/share/tools/sysctl/sysctlharvest.pl   (contents, props changed)
  projects/sysctl/share/tools/sysctl/util.c   (contents, props changed)
  projects/sysctl/share/tools/sysctl/util.h   (contents, props changed)

Added: projects/sysctl/share/tools/sysctl/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/sysctl/share/tools/sysctl/Makefile	Mon Apr  1 10:24:17 2013	(r41333)
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+SCRIPTS = sysctlharvest.pl
+PROG = sysctlfromelf
+SRCS = sysctlfromelf.c elfexplorer.c util.c
+MAN =
+WARNS ?= 6
+LDADD = -lelf
+
+.include <bsd.prog.mk>

Added: projects/sysctl/share/tools/sysctl/elfexplorer.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/sysctl/share/tools/sysctl/elfexplorer.c	Mon Apr  1 10:24:17 2013	(r41333)
@@ -0,0 +1,303 @@
+/*-
+ * Copyright (c) 2013 Dag-Erling Smørgrav
+ * 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.
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "elfexplorer.h"
+#include "util.h"
+
+/*
+ * Compare two sections by their names.
+ */
+static int
+elfx_compare_sections_by_name(const void *ap, const void *bp)
+{
+	const elfx_section *a = ap, *b = bp;
+
+	return (strcmp(a->name, b->name));
+}
+
+/*
+ * Compare two sections by their start addresses.
+ */
+static int
+elfx_compare_sections_by_addr(const void *ap, const void *bp)
+{
+	const elfx_section *a = ap, *b = bp;
+
+	/* no overlap allowed */
+	assert(a == b || a->baddr == 0 ||
+	    a->eaddr < b->baddr || a->baddr > b->eaddr);
+	return (a->baddr < b->baddr ? -1 : a->baddr > b->baddr ? 1 : 0);
+}
+
+/*
+ * Free memory that was allocated to store information about sections.
+ */
+static void
+elfx_free_sections(elfx_file *ef)
+{
+
+	if (ef == NULL)
+		return;
+	if (ef->sections != NULL) {
+		for (unsigned int i = 0; i < ef->nsections; ++i)
+			if (ef->sections[i].ptr && ef->sections[i].data &&
+			    ef->sections[i].ptr != ef->sections[i].data->d_buf)
+				free(ef->sections[i].ptr);
+		free(ef->sections);
+		ef->sections = NULL;
+	}
+	if (ef->sections_by_name != NULL) {
+		free(ef->sections_by_name);
+		ef->sections_by_name = NULL;
+	}
+	if (ef->sections_by_addr != NULL) {
+		free(ef->sections_by_addr);
+		ef->sections_by_addr = NULL;
+	}
+	ef->nsections = 0;
+}
+
+/*
+ * Retrieve information about sections.
+ */
+static int
+elfx_load_sections(elfx_file *ef)
+{
+	elfx_section *es;
+	size_t sections_size;
+
+	/* get translated ELF header */
+	if (gelf_getehdr(ef->elf, &ef->hdr) == NULL)
+		goto elf_error;
+	/* get number of sections in file */
+	if (elf_getshdrnum(ef->elf, &ef->nsections) != ELF_E_NONE)
+		goto elf_error;
+	sections_size = ef->nsections * sizeof *ef->sections;
+	if ((ef->sections = calloc(sections_size, 1)) == NULL)
+		goto mem_error;
+	verbose("%4s %4s %8s %8s %s", "sect", "type", "start", "size", "name");
+	/* iterate over sections */
+	for (unsigned int i = 0; i < ef->nsections; ++i) {
+		es = &ef->sections[i];
+		es->file = ef;
+		es->index = i;
+		/* get section from file */
+		if ((es->scn = elf_getscn(ef->elf, i)) == NULL)
+			goto elf_error;
+		/* get translated section header */
+		if ((gelf_getshdr(es->scn, &es->hdr)) == NULL)
+			goto elf_error;
+		/* get section name */
+		if ((es->name = elf_strptr(ef->elf, ef->hdr.e_shstrndx,
+		    es->hdr.sh_name)) == NULL)
+			goto elf_error;
+		verbose("%4d %4d %08x %08lx %s", elf_ndxscn(es->scn),
+		    es->hdr.sh_type, (unsigned long)es->hdr.sh_addr,
+		    (unsigned long)es->hdr.sh_size, es->name);
+		/* get data, if any */
+		if ((es->data = elf_getdata(es->scn, NULL)) != NULL) {
+			assert(es->data->d_off == 0 &&
+			    es->data->d_size == es->hdr.sh_size);
+			if (es->hdr.sh_type == SHT_NOBITS)
+				es->ptr = calloc(es->hdr.sh_size, 1);
+			else
+				es->ptr = es->data->d_buf;
+			es->baddr = es->hdr.sh_addr;
+			es->eaddr = es->hdr.sh_addr + es->hdr.sh_size - 1;
+			es->size = es->hdr.sh_size;
+			/* only one data descriptor */
+			assert(elf_getdata(es->scn, es->data) == NULL);
+		}
+	}
+	/* create copy of section list, sorted by name */
+	if ((ef->sections_by_name = calloc(sections_size, 1)) == NULL)
+		goto mem_error;
+	memcpy(ef->sections_by_name, ef->sections, sections_size);
+	mergesort(ef->sections_by_name, ef->nsections,
+	    sizeof *ef->sections_by_name, elfx_compare_sections_by_name);
+	/* create copy of section list, sorted by address */
+	if ((ef->sections_by_addr = calloc(sections_size, 1)) == NULL)
+		goto mem_error;
+	memcpy(ef->sections_by_addr, ef->sections, sections_size);
+	mergesort(ef->sections_by_addr, ef->nsections,
+	    sizeof *ef->sections_by_addr, elfx_compare_sections_by_addr);
+	return (ef->nsections);
+elf_error:
+	info("%s: %s", ef->path, elf_errmsg(elf_errno()));
+mem_error:
+	elfx_free_sections(ef);
+	return (-1);
+}
+
+/*
+ * Open an ELF file and retrieve the information we need.
+ */
+elfx_file *
+elfx_open(const char *path)
+{
+	elfx_file *ef;
+	struct stat sb;
+
+	if ((ef = calloc(sizeof *ef, 1)) == NULL)
+		goto fail;
+	if (realpath(path, ef->path) == NULL)
+		goto fail;
+	if ((ef->fd = open(ef->path, O_RDONLY)) == -1)
+		goto fail;
+	if (fstat(ef->fd, &sb) != 0)
+		goto fail;
+	ef->size = sb.st_size;
+	if ((ef->map = mmap(NULL, ef->size, PROT_READ, MAP_PRIVATE,
+	    ef->fd, 0)) == NULL)
+		goto fail;
+	if ((ef->elf = elf_memory(ef->map, ef->size)) == NULL)
+		goto fail;
+	if (elf_kind(ef->elf) != ELF_K_ELF)
+		goto fail;
+	if (elfx_load_sections(ef) < 0)
+		goto fail;
+	return (ef);
+fail:
+	elfx_close(ef);
+	return (NULL);
+}
+
+/*
+ * Close an ELF file and free all allocated memory.
+ */
+void
+elfx_close(elfx_file *ef)
+{
+
+	if (ef == NULL)
+		return;
+	elfx_free_sections(ef);
+	if (ef->elf != NULL)
+		elf_end(ef->elf);
+	if (ef->map != NULL)
+		munmap(ef->map, ef->size);
+	if (ef->fd <= 0)
+		close(ef->fd);
+	free(ef);
+}
+
+/*
+ * Retrieve a section by its name.
+ */
+elfx_section *
+elfx_get_section_by_name(elfx_file *ef, const char *name)
+{
+	elfx_section *es;
+	int lo, hi, mid;
+	int cmp;
+
+	es = ef->sections_by_name;
+	lo = 0;
+	hi = ef->nsections - 1;
+	for (;;) {
+		mid = (lo + hi) / 2;
+//		verbose("(%d, %d, %d) %s == %s",
+//		    lo, hi, mid, es[mid].name, name);
+		if ((cmp = strcmp(name, es[mid].name)) == 0)
+			return (&es[mid]);
+		else if (lo == hi)
+			return (NULL);
+		else if (cmp < 0)
+			hi = mid - 1;
+		else
+			lo = mid + 1;
+	}
+}
+
+/*
+ * Retrieve the section that contains a specified address.
+ */
+elfx_section *
+elfx_get_section_by_addr(elfx_file *ef, uintptr_t addr, elfx_section *hint)
+{
+	elfx_section *es;
+	int lo, hi, mid;
+
+	if (hint != NULL && hint->baddr <= addr && addr <= hint->eaddr)
+		return (hint);
+	es = ef->sections_by_addr;
+	lo = 0;
+	hi = ef->nsections - 1;
+	for (;;) {
+		mid = (lo + hi) / 2;
+//		verbose("(%d, %d, %d) %08x <= %08x <= %08x", lo, mid, hi,
+//		    es[mid].baddr, addr, es[mid].eaddr);
+		if (es[mid].baddr <= addr && addr <= es[mid].eaddr)
+			return (&es[mid]);
+		else if (lo == hi)
+			return (NULL);
+		else if (addr < es[mid].baddr)
+			hi = mid - 1;
+		else
+			lo = mid + 1;
+	}
+}
+
+/*
+ * Look up a symbol in the symbol table
+ */
+uintptr_t
+elfx_get_symbol(elfx_file *ef, const char *name)
+{
+	elfx_section *symtab;
+	GElf_Sym sym;
+	int nsyms;
+	char *symname;
+
+	/* XXX cache symtab */
+	if ((symtab = elfx_get_section_by_name(ef, ".symtab")) == NULL)
+		return (0);
+	nsyms = symtab->hdr.sh_size / symtab->hdr.sh_entsize;
+	for (int i = 0; i < nsyms; ++i) {
+		gelf_getsym(symtab->data, i, &sym);
+		if ((symname = elf_strptr(ef->elf, symtab->hdr.sh_link,
+		    sym.st_name)) != NULL) {
+//			verbose("%08x %s", sym.st_value, symname);
+			if (strcmp(symname, name) == 0) {
+//				verbose("found %s at %08x", name, sym.st_value);
+				return (sym.st_value);
+			}
+		}
+	}
+	return (0);
+}

Added: projects/sysctl/share/tools/sysctl/elfexplorer.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/sysctl/share/tools/sysctl/elfexplorer.h	Mon Apr  1 10:24:17 2013	(r41333)
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2013 Dag-Erling Smørgrav
+ * 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.
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef ELFEXPLORER_H_INCLUDED
+#define ELFEXPLORER_H_INCLUDED
+
+#include <libelf.h>
+#include <gelf.h>
+
+typedef struct elfx_file {
+	char			 path[PATH_MAX];
+	int			 fd;
+	size_t			 size;
+	void			*map;
+	Elf			*elf;
+	GElf_Ehdr		 hdr;
+	size_t			 nsections;
+	struct elfx_section	*sections;
+	struct elfx_section	*sections_by_name;
+	struct elfx_section	*sections_by_addr;
+} elfx_file;
+
+typedef struct elfx_section {
+	struct elfx_file	*file;
+	int			 index;
+	Elf_Scn			*scn;
+	GElf_Shdr		 hdr;
+	char			*name;
+	Elf_Data		*data;
+	uintptr_t		 baddr;
+	uintptr_t		 eaddr;
+	size_t			 size;
+	void			*ptr;
+} elfx_section;
+
+
+elfx_file *elfx_open(const char *);
+void elfx_close(elfx_file *);
+
+elfx_section *elfx_get_section_by_name(elfx_file *, const char *);
+elfx_section *elfx_get_section_by_addr(elfx_file *, uintptr_t, elfx_section *);
+#define elfx_get_data(s, a) \
+	(void *)((char *)(s)->ptr + (uintptr_t)(a) - (s)->baddr)
+uintptr_t elfx_get_symbol(elfx_file *, const char *);
+
+
+#endif

Added: projects/sysctl/share/tools/sysctl/sysctlfromelf.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/sysctl/share/tools/sysctl/sysctlfromelf.c	Mon Apr  1 10:24:17 2013	(r41333)
@@ -0,0 +1,266 @@
+/*-
+ * Copyright (c) 2012-2013 Dag-Erling Smørgrav
+ * 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
+ *    in this position and unchanged.
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/stat.h>
+
+#define _KERNEL
+#include <sys/sysctl.h>
+#undef _KERNEL
+
+#include <assert.h>
+#include <dirent.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "elfexplorer.h"
+#include "util.h"
+
+static void process_oid(const struct sysctl_oid *);
+
+/*
+ * Process an OID.
+ */
+static void
+process_oid(const struct sysctl_oid *oid)
+{
+	const struct sysctl_oid_list *children;
+	const struct sysctl_oid *child;
+
+	printf("<sysctl:node name='%s'>\n", oid->oid_name);
+	if (oid->oid_descr)
+		printf("<sysctl:short><![CDATA[%s]]></sysctl:short>\n",
+		    oid->oid_descr);
+//	printf("<sysctl:kind>%08x</sysctl:kind>\n", oid->oid_kind);
+	if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE) {
+		children = (const struct sysctl_oid_list *)oid->oid_arg1;
+		SLIST_FOREACH(child, children, oid_link)
+			process_oid(child);
+	}
+	printf("</sysctl:node>\n");
+}
+
+/*
+ * Process an individual file.
+ */
+static int
+process_file(const char *path)
+{
+	elfx_file *ef;
+	elfx_section *sect;
+	unsigned int nsysctls;
+	struct sysctl_oid_list *sysctl_root;
+	struct sysctl_oid *oid, **oids;
+	uintptr_t rootaddr;
+
+	if ((ef = elfx_open(path)) == NULL)
+		return (-1);
+	/* find the root list */
+	rootaddr = elfx_get_symbol(ef, "sysctl__children");
+	sect = elfx_get_section_by_addr(ef, rootaddr, NULL);
+	assert(sect != NULL);
+	sysctl_root = elfx_get_data(sect, rootaddr);
+	/* find the sysctl linker set */
+	if ((sect = elfx_get_section_by_name(ef, "set_sysctl_set")) == NULL) {
+		verbose("no sysctl linker set");
+		elfx_close(ef);
+		return (0);
+	}
+	assert(sect->size % sizeof(uintptr_t) == 0);
+	nsysctls = sect->size / sizeof(uintptr_t);
+	oids = (struct sysctl_oid **)sect->ptr;
+	verbose("%zd sysctls found in section %d", nsysctls, sect->index);
+	/* retrieve OIDs and fix up various pointers */
+	for (unsigned int i = 0; i < nsysctls; ++i) {
+		sect = elfx_get_section_by_addr(ef, (uintptr_t)oids[i], sect);
+		assert(sect != NULL);
+		oids[i] = oid = elfx_get_data(sect, oids[i]);
+		/* name */
+		sect = elfx_get_section_by_addr(ef,
+		    (uintptr_t)oid->oid_name, sect);
+		assert(sect != NULL);
+		oid->oid_name = elfx_get_data(sect, oid->oid_name);
+		/* descriptions */
+		if (oid->oid_descr != NULL) {
+			sect = elfx_get_section_by_addr(ef,
+			    (uintptr_t)oid->oid_descr, sect);
+			assert(sect != NULL);
+			oid->oid_descr = elfx_get_data(sect, oid->oid_descr);
+		}
+		/* siblings */
+		if (oid->oid_link.sle_next != NULL) {
+			sect = elfx_get_section_by_addr(ef,
+			    (uintptr_t)oid->oid_link.sle_next, sect);
+			assert(sect != NULL);
+			oid->oid_link.sle_next = elfx_get_data(sect,
+			    oid->oid_link.sle_next);
+		}
+		/* children */
+		if ((oid->oid_kind & CTLTYPE) == CTLTYPE_NODE &&
+		    oid->oid_arg1 != 0) {
+			sect = elfx_get_section_by_addr(ef,
+			    (uintptr_t)oid->oid_arg1, sect);
+			assert(sect != NULL);
+			oid->oid_arg1 = elfx_get_data(sect,
+			    (uintptr_t)oid->oid_arg1);
+		}
+		/* parent */
+		sect = elfx_get_section_by_addr(ef,
+		    (uintptr_t)oid->oid_parent, sect);
+		assert(sect != NULL);
+		oid->oid_parent = elfx_get_data(sect, oid->oid_parent);
+		SLIST_INSERT_HEAD(oid->oid_parent, oid, oid_link);
+	}
+	/* list all OIDs! */
+	printf("<?xml version='1.0' encoding='utf-8'?>\n"
+	    "<sysctl:tree xmlns:sysctl='http://www.FreeBSD.org/XML/sysctl'>\n");
+	SLIST_FOREACH(oid, sysctl_root, oid_link) {
+		process_oid(oid);
+	}
+	printf("</sysctl:tree>\n");
+	elfx_close(ef);
+	return (0);
+}
+
+/*
+ * Traverse a directory and process any file it named "kernel" or "*.ko".
+ */
+static int
+process_directory(const char *path)
+{
+	DIR *dir;
+	struct dirent ent, *res;
+	char pathbuf[PATH_MAX];
+	int ret;
+
+	verbose("%s(%s)", __func__, path);
+	if ((dir = opendir(path)) == NULL) {
+		warn("%s", path);
+		return (-1);
+	}
+	ret = 0;
+	for (;;) {
+		if (readdir_r(dir, &ent, &res) != 0) {
+			warn("%s", path);
+			closedir(dir);
+			return (-1);
+		}
+		if (res == NULL)
+			break;
+		assert(res == &ent);
+		if (snprintf(pathbuf, sizeof(pathbuf), "%s/%s", path, ent.d_name) >=
+		    (int)sizeof(pathbuf)) {
+			errno = ENAMETOOLONG;
+			warn("%s/%s", path, ent.d_name);
+			continue;
+		}
+		if (strcmp(ent.d_name, "kernel") == 0 ||
+		    (ent.d_namlen > 4 &&
+		    strcmp(ent.d_name + ent.d_namlen - 3, ".ko") == 0))
+			if (process_file(pathbuf) != 0)
+				ret = -1;
+	}
+	closedir(dir);
+	return (ret);
+}
+
+/*
+ * Check whether a path is a file or a directory and pass it on to the
+ * appropriate processing function.
+ */
+static int
+process_path(const char *path)
+{
+	struct stat sb;
+
+	verbose("%s(%s)", __func__, path);
+	if (stat(path, &sb) != 0) {
+		warn("%s", path);
+		return (-1);
+	}
+	switch (sb.st_mode & S_IFMT) {
+	case S_IFREG:
+		return (process_file(path));
+	case S_IFDIR:
+		return (process_directory(path));
+	default:
+		warnx("%s is neither a regular file nor a directory", path);
+		return (-1);
+	}
+}
+
+/*
+ * Print usage message and exit.
+ */
+static int
+usage(void)
+{
+
+	fprintf(stderr, "usage: sysctlfromelf [-v] [path ...]\n");
+	exit(1);
+}
+
+/*
+ * Main loop
+ */
+int
+main(int argc, char *argv[])
+{
+	int opt;
+
+	while ((opt = getopt(argc, argv, "v")) != -1)
+		switch (opt) {
+		case 'v':
+			++opt_v;
+			break;
+		default:
+			usage();
+		}
+
+	argc -= optind;
+	argv += optind;
+
+	if (elf_version(EV_CURRENT) == EV_NONE)
+		errx(1, "incorrect ELF library version");
+	verbose("ELF library version %d", elf_version(EV_NONE));
+
+	if (argc == 0)
+		process_path("/boot/kernel");
+	else
+		while (argc-- > 0)
+			process_path(*argv++);
+
+	exit(0);
+}

Added: projects/sysctl/share/tools/sysctl/sysctlharvest.pl
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/sysctl/share/tools/sysctl/sysctlharvest.pl	Mon Apr  1 10:24:17 2013	(r41333)
@@ -0,0 +1,264 @@
+#!/usr/bin/perl -Tw
+#-
+# Copyright (c) 2012 Dag-Erling Smørgrav
+# 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
+#    in this position and unchanged.
+# 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.
+#
+# $FreeBSD$
+#
+
+require v5.10;
+use strict;
+use warnings;
+use utf8;
+use Getopt::Long;
+use Pod::Usage;
+use constant SYSCTL_CMD => "/sbin/sysctl";
+use constant XML_PREAMBLE => "<?xml version='1.0' encoding='utf-8'?>\n";
+
+# Command-line options
+our $from_sysctl;
+our $from_binaries;
+our $from_source;
+our $source_path;
+our $tunables;
+our $output;
+our $clobber;
+
+# Hashes of harvested sysctls and tunables
+our %sysctl;
+our %tunable;
+
+# Set of leaves
+our %leaves;
+
+#
+# Harvest from the output of the sysctl(1) command
+#
+sub harvest_from_sysctl() {
+    open(my $ph, "-|", SYSCTL_CMD, "-Ade")
+	or die("unable to execute @{[SYSCTL_CMD]}\n");
+    while (<$ph>) {
+	next unless m/^([\w%-]+(?:\.[\w%-]+)*)=(.*?)\s*$/;
+	my ($name, $short) = ($1, $2);
+
+	if ($name =~ m/^dev\.(\w+)\.\d+\.([\w%-]+(?:\.[\w%-]+)*)$/) {
+	    # per-device
+	    my ($device, $variable) = ($1, $2);
+	    # skip the device metadata
+	    next if $variable =~ m/^%/;
+	    $name = "dev.$device.N.$variable";
+	} elsif ($name =~ m/^dev\.(\w+)\.([\w%-]+(?:\.[\w%-]+)*)$/) {
+	    # per-driver
+	    my ($driver, $variable) = ($1, $2);
+	    # skip the device metadata
+	    next if $variable =~ m/^%/;
+	    $name = "dev.$driver.$variable";
+	}
+
+	my @path = split(/\./, $name);
+	$leaves{$name} = [ @path ];
+	my $node = \%sysctl;
+	foreach my $cnp (@path) {
+	    $node->{$cnp} //= { "-name" => $cnp };
+	    $node = $node->{$cnp};
+	}
+	$node->{"-leaf"} = 1;
+	$node->{"-short"} ||= $short;
+    }
+    close($ph);
+}
+
+# Quote XML-unsafe characters
+sub quote($) { $_[0] =~ s/([^\s\w.-])/&#@{[ord $1]};/gr; }
+
+#
+# Output an XML fragment describing a particular sysctl node and all
+# its descendents.  The first argument is the file handle to write to;
+# the second is a hashref to the node; the third is the indentation
+# level; any subsequent arguments, if present, are interpreted as a
+# path to a particular subtree (or leaf).
+#
+sub output_tree($$$;@);
+sub output_tree($$$;@) {
+    my ($fh, $tree, $level, @path) = @_;
+
+    print($fh XML_PREAMBLE,
+	  "<sysctl:tree xmlns:sysctl='http://www.FreeBSD.org/XML/sysctl'>\n")
+	if $level == 1;
+    my $indent = "  " x $level;
+    foreach (@path ? shift @path : sort keys %$tree) {
+	next if m/^-/;
+	my $node = $$tree{$_};
+	print($fh "$indent<sysctl:node");
+	foreach my $attr (qw/name type readonly/) {
+	    print($fh " $attr='", quote($$node{"-$attr"}), "'")
+		if $$node{"-$attr"};
+	}
+	print($fh ">\n");
+	print($fh "$indent  <sysctl:short>",
+	      quote($$node{"-short"} || "No description available"),
+	      "</sysctl:short>\n")
+	    if $$node{"-leaf"};
+	output_tree($fh, $node, $level + 1, @path);
+	print($fh "$indent</sysctl:node>\n");
+    }
+    print($fh "</sysctl:tree>\n")
+	if $level == 1;
+}
+
+#
+# Top-level output routine
+#
+sub output() {
+    if (defined($output) && -d $output) {	
+	foreach my $leaf (sort keys %leaves) {
+	    my $fn = "$output/$leaf.xml";
+	    next if (!$clobber && -f $fn);
+	    open(my $fh, ">", $fn)
+		or die("$fn: $!\n");
+	    output_tree($fh, \%sysctl, 1, @{$leaves{$leaf}});
+	    close($fh);
+	}
+    } else {
+	$output //= "/dev/stdout";
+	open(my $fh, ">", $output)
+	    or die("$output: $!\n");
+	output_tree($fh, \%sysctl, 1);
+	close($fh);
+    }
+}
+
+MAIN:{
+    $ENV{PATH} = "/bin:/sbin:/usr/bin:/usr/sbin";
+    $ENV{PERLDOC} = "-wcenter:'FreeBSD documentation tools'";
+    GetOptions(
+	"sysctl+"	=> \$from_sysctl,
+	"binaries+"	=> \$from_binaries,
+	"source+"	=> \$from_source,
+	"sys=s"		=> \$source_path,
+	"tunables+"	=> \$tunables,
+	"output=s"	=> \$output,
+	"clobber+"	=> \$clobber,
+	"help"		=> sub { pod2usage(-exitval => 0, -verbose => 0); },
+	"man"		=> sub { pod2usage(-exitval => 0, -verbose => 2); })
+	or pod2usage(1);
+
+    if (!$from_sysctl && !$from_binaries && !$from_source) {
+	$from_sysctl = 1;
+    }
+
+    harvest_from_sysctl()
+	if $from_sysctl;
+#    harvest_from_binaries()
+#	if $from_binaries;
+#    harvest_from_source()
+#	if $from_source;
+
+    # output the result
+    # XXX allow the user to specify which nodes / subtrees to output
+    # XXX through @ARGS
+    output();
+}
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+sysctlharvest - Generate sysctl documentation stubs
+
+=head1 SYNOPSIS
+
+sysctlharvest [options]
+
+ Options:
+   --help           brief help message
+   --man	    complete documentation
+   --sysctl         use sysctl tree
+   --binaries       use kernel binaries
+   --source         use kernel source
+   --sys=path       path to kernel source
+   --tunables       include tunables
+   --output=path    output file or directory
+   --clobber        overwrite existing files
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<--help>
+
+Print a brief help message and exits.
+
+=item B<--man>
+
+Print the complete documentation and exits.
+
+=item B<--sysctl>
+
+Harvest information from the sysctl tree of the current system.
+
+=item B<--source>
+
+Harvest information from a kernel source tree.
+
+=item B<--sys>=I<path>
+
+Used with the B<--source> option to specify the location of the kernel
+source tree.  The default is to look first in the current directory,
+then in a C<sys> subdirectory, then in C</sys>, and finally in
+C</usr/src/sys>.
+
+=item B<--tunables>
+
+When harvesting from a kernel source tree, include tunables.
+
+=item B<--output>=I<path>
+
+Output path.  If the path exists and is a directory, individual files
+are created for each sysctl or tunable.  If it does not exist, or it
+exists and is a file, all the harvested information is written to a
+single file.  The default is to output all the information to stdout.
+
+=item B<--clobber>
+
+When writing individual files, overwrite any that already exist.
+
+=back
+
+=head1 DESCRIPTION
+
+The B<sysctlharvest> utility gathers information about sysctl nodes
+and generates documentation stubs based on that information.
+
+=head1 HISTORY
+
+The B<sysctlharvest> utility and this manual page were written by
+Dag-Erling Smørgrav <des at freebsd.org>.
+
+=cut
+
+1;

Added: projects/sysctl/share/tools/sysctl/util.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ projects/sysctl/share/tools/sysctl/util.c	Mon Apr  1 10:24:17 2013	(r41333)
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 2013 Dag-Erling Smørgrav
+ * 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.
+ *
+ * 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.
+ *
+ * $Id$
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "util.h"
+
+int opt_v;
+
+/*
+ * Print an informational message.
+ */
+void
+info(const char *fmt, ...)
+{
+	va_list ap;
+
+	fprintf(stderr, "%s: ", getprogname());
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
+}
+
+/*
+ * Print a hexdump
+ */
+void
+hexdump(const void *datap, size_t len, unsigned long base)
+{
+	const uint8_t *data = datap;
+	int i, n;
+
+	while (len > 0) {
+		n = (len > 16) ? 16 : len;
+		fprintf(stderr, "%08x |", (unsigned int)base);
+		for (i = 0; i < n; ++i)
+			fprintf(stderr, " %02x", data[i]);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***


More information about the svn-doc-projects mailing list