In message <3E1A68F5.1000704@zytor.com> you write:> This adds [f]getc() and fgets() for parsing config files. Probably hard
> to avoid. Still trying to decide if I actually want to add system() or
not.
>
> -hpa
I was hoping to test this first, but here's the mini-modprobe.
Unfortunately I've had to travel away for a funeral, so all I can say
is that it compiles.
Add features to taste.
/* Minature, simplified, self-standing modprobe. */
#include <stdio.h>
#include <elf.h>
#include <dirent.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/unistd.h>
/* All modules are in this directory. */
#define MODDIR "/lib/modules"
/* All modules end in this extension, eg. /lib/modules/rusty.ko */
#define MODEXT ".ko"
/* All option files end in this extension, eg. /lib/modules/rusty.ko.options */
#define OPTEXT ".options"
struct symbol
{
struct symbol *next;
const char *owner;
char name[0];
};
#define SYMBOL_HASH_SIZE 1024
static struct symbol *symbolhash[SYMBOL_HASH_SIZE];
#define error(fmt , ...) fprintf(stderr, fmt , ## __VA_ARGS__)
#define fatal(fmt , ...) \
do { fprintf(stderr, fmt , ## __VA_ARGS__); exit(1); } while(0)
static int modprobe(const char *modname, unsigned int depth);
/* Read in the entire file. */
static void *read_in(const char *entname, unsigned long *len, int noexistok)
{
char *ret;
unsigned long done = 0;
int r, fd;
struct stat statbuf;
char filename[strlen(MODDIR) + 1 + strlen(entname) + 1];
sprintf(filename, "%s/%s", MODDIR, entname);
fd = open(filename, O_RDONLY);
if (fd < 0) {
if (errno == ENOENT && noexistok) {
*len = 0;
return strdup("");
}
fatal("Failed to open %s: %s\n", filename, strerror(errno));
}
fstat(fd, &statbuf);
*len = statbuf.st_size;
ret = malloc(*len + 1);
while (done < *len) {
r = read(fd, ret + done, *len - done);
if (r <= 0)
fatal("Failed to read %s: %s\n", filename,
strerror(errno));
done += r;
}
ret[*len] = '\0';
return ret;
}
/* This is based on the hash agorithm from gdbm, via tdb */
static inline unsigned int tdb_hash(const char *name)
{
unsigned value; /* Used to compute the hash value. */
unsigned i; /* Used to cycle through random values. */
/* Set the initial value from the key size. */
for (value = 0x238F13AF * strlen(name), i=0; name[i]; i++)
value = (value + (((unsigned char *)name)[i] << (i*5 % 24)));
return (1103515243 * value + 12345);
}
static void add_symbol(const char *name, const char *owner)
{
unsigned int hash;
struct symbol *new = malloc(sizeof *new + strlen(name) + 1);
new->owner = owner;
strcpy(new->name, name);
hash = tdb_hash(name) % SYMBOL_HASH_SIZE;
new->next = symbolhash[hash];
symbolhash[hash] = new;
}
static struct symbol *get_symbol(const char *name)
{
struct symbol *s;
for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s = s->next)
if (strcmp(s->name, name) == 0)
break;
return s;
}
struct kernel_symbol32 {
char value[4];
char name[64 - 4];
};
struct kernel_symbol64 {
char value[8];
char name[64 - 8];
};
static void load_symbols32(Elf32_Ehdr *hdr, const char *module)
{
unsigned int i, j;
struct kernel_symbol32 *ksyms;
Elf32_Shdr *sechdrs = (void *)hdr + hdr->e_shoff;
char *secnames = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
for (i = 1; i < hdr->e_shnum; i++)
if (strcmp(secnames+sechdrs[i].sh_name, "__ksymtab") == 0) {
ksyms = (void *)hdr + sechdrs[i].sh_offset;
for (j = 0; j < sechdrs[i].sh_size/sizeof(*ksyms); j++)
add_symbol(ksyms[j].name, module);
}
}
static void load_symbols64(Elf64_Ehdr *hdr, const char *module)
{
unsigned int i, j;
struct kernel_symbol64 *ksyms;
Elf64_Shdr *sechdrs = (void *)hdr + hdr->e_shoff;
char *secnames = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
for (i = 1; i < hdr->e_shnum; i++)
if (strcmp(secnames+sechdrs[i].sh_name, "__ksymtab") == 0) {
ksyms = (void *)hdr + sechdrs[i].sh_offset;
for (j = 0; j < sechdrs[i].sh_size/sizeof(*ksyms); j++)
add_symbol(ksyms[j].name, module);
}
}
/* "\177ELF" <byte> where byte = 001 for 32-bit, 002 for 64 */
static int elf_type(void *hdr, const char *entname)
{
if (memcmp(hdr, ELFMAG, SELFMAG) != 0) {
error("Module %s is not ELF!\n", entname);
return ELFCLASSNONE;
}
return (((char *)hdr)[EI_CLASS]);
}
static void load_symbols(const char *entname)
{
void *file;
unsigned long len;
file = read_in(entname, &len, 0);
if (elf_type(file, entname) == ELFCLASS32)
load_symbols32(file, entname);
else if (elf_type(file, entname) == ELFCLASS64)
load_symbols64(file, entname);
free(file);
}
static void get_needs32(Elf32_Ehdr *hdr, unsigned int depth)
{
Elf32_Shdr *sechdrs = (void *)hdr + hdr->e_shoff;
Elf32_Sym *symtab;
char *strtab;
unsigned int i, j;
struct symbol *sym;
for (i = 1; i < hdr->e_shnum; i++) {
if (sechdrs[i].sh_type != SHT_SYMTAB)
continue;
symtab = (void *)hdr + sechdrs[i].sh_offset;
strtab = (void *)hdr + sechdrs[sechdrs[i].sh_link].sh_offset;
for (j = 0; j<sechdrs[i].sh_size/sizeof(*symtab); j++) {
if (symtab[j].st_shndx != SHN_UNDEF)
continue;
if ((sym = get_symbol(strtab + symtab[i].st_name)))
modprobe(sym->owner, depth);
}
}
}
static void get_needs64(Elf64_Ehdr *hdr, unsigned int depth)
{
Elf64_Shdr *sechdrs = (void *)hdr + hdr->e_shoff;
Elf64_Sym *symtab;
char *strtab;
unsigned int i, j;
struct symbol *sym;
for (i = 1; i < hdr->e_shnum; i++) {
if (sechdrs[i].sh_type != SHT_SYMTAB)
continue;
symtab = (void *)hdr + sechdrs[i].sh_offset;
strtab = (void *)hdr + sechdrs[sechdrs[i].sh_link].sh_offset;
for (j = 0; j<sechdrs[i].sh_size/sizeof(*symtab); j++) {
if (symtab[j].st_shndx != SHN_UNDEF)
continue;
if ((sym = get_symbol(strtab + symtab[i].st_name)))
modprobe(sym->owner, depth);
}
}
}
static int modprobe(const char *modname, unsigned int depth)
{
int ret;
void *file;
char *options, *ptr;
unsigned long len, optlen;
char optionname[strlen(modname) + 1 + sizeof(OPTEXT)];
if (depth > 50)
fatal("Modprobe loop: %s\n", modname);
/* Grab options if any: convert \n to ' ' */
sprintf(optionname, "%s%s", modname, OPTEXT);
options = read_in(optionname, &optlen, 1);
while ((ptr = strchr(options, '\n')) != NULL)
*ptr = ' ';
/* Grab dependencies */
file = read_in(modname, &len, 0);
if (elf_type(file, modname) == ELFCLASS32)
get_needs32(file, depth+1);
else if (elf_type(file, modname) == ELFCLASS64)
get_needs64(file, depth+1);
ret = init_module(file, len, options);
if (ret < 0 && errno != EEXIST)
fprintf(stderr, "Error loading module %s: %s\n",
modname, strerror(errno));
free(file);
free(options);
return ret;
}
int main(int argc, char *argv[])
{
DIR *dir;
struct dirent *dirent;
char modname[strlen(argv[1]?:"") + sizeof(MODEXT)];
if (argc != 1)
fatal("Usage: %s modulename\n"
" Where modules are kept in "MODDIR"\n"
" And options have extension "OPTEXT"\n", argv[0]);
if (!(dir = opendir(MODDIR)))
fatal("Could not open directory %s\n", MODDIR);
while ((dirent = readdir(dir)) != NULL) {
int len = strlen(dirent->d_name);
if (len >= strlen(MODEXT)
&& strcmp(dirent->d_name+len-strlen(MODEXT), MODEXT) == 0)
load_symbols(dirent->d_name);
}
sprintf(modname, "%s%s", argv[1], MODEXT);
return modprobe(modname, 0);
}
--
Anyone who quotes me in their sig is an idiot. -- Rusty Russell.