/*
 * New simplified depmod without backwards compat stuff and not
 * requiring ksyms.
 *
 * (C) 2010 Jon Masters <jcm@jonmasters.org>, and others.
 * (C) 2002 Rusty Russell IBM Corporation
 */
#define _GNU_SOURCE /* asprintf */

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <elf.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/utsname.h>
#include <sys/mman.h>

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

#include "util.h"
#include "zlibsupport.h"
#include "depmod.h"
#include "logging.h"
#include "index.h"
#include "elfops.h"
#include "tables.h"
#include "config_filter.h"

#include "testing.h"

#ifndef MODULE_DIR
#define MODULE_DIR "/lib/modules/"
#endif

#ifndef MODULE_BUILTIN_KEY
#define MODULE_BUILTIN_KEY "built-in"
#endif

struct module_overrides
{
	/* Next override */
	struct module_overrides *next;

	/* overridden module */
	char *modfile;
};

struct module_search
{
	/* Next search */
	struct module_search *next;

	/* search path */
	char *search_path;
	size_t len;
};

static unsigned int skipchars;
static unsigned int make_map_files = 1; /* default to on */
static unsigned int force_map_files = 0; /* default to on */

#define SYMBOL_HASH_SIZE 1024
struct symbol
{
	struct symbol *next;
	struct module *owner;
	uint64_t ver;
	char name[0];
};

static struct symbol *symbolhash[SYMBOL_HASH_SIZE];

/* 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);
}

void add_symbol(const char *name, uint64_t ver, struct module *owner)
{
	unsigned int hash;
	struct symbol *new = NOFAIL(malloc(sizeof *new + strlen(name) + 1));

	new->owner = owner;
	new->ver = ver;
	strcpy(new->name, name);

	hash = tdb_hash(name) % SYMBOL_HASH_SIZE;
	new->next = symbolhash[hash];
	symbolhash[hash] = new;
}

static int print_unknown, check_symvers;

struct module *find_symbol(const char *name, uint64_t ver,
		const char *modname, int weak)
{
	struct symbol *s;

	/* For our purposes, .foo matches foo.  PPC64 needs this. */
	if (name[0] == '.')
		name++;

	for (s = symbolhash[tdb_hash(name) % SYMBOL_HASH_SIZE]; s; s=s->next) {
		if (streq(s->name, name))
			break;
	}
	if (s) {
		if (ver && s->ver && s->ver != ver && print_unknown && !weak)
			warn("%s disagrees about version of symbol %s\n",
					modname, name);
		return s->owner;
	}

	if (print_unknown && !weak)
		warn("%s needs unknown symbol %s\n", modname, name);

	return NULL;
}

void add_dep(struct module *mod, struct module *depends_on)
{
	unsigned int i;

	for (i = 0; i < mod->num_deps; i++)
		if (mod->deps[i] == depends_on)
			return;

	mod->deps = NOFAIL(realloc(mod->deps, sizeof(mod->deps[0])*(mod->num_deps+1)));
	mod->deps[mod->num_deps++] = depends_on;
}

static void add_fake_syms(void)
{
	/* __this_module is magic inserted by kernel loader. */
	add_symbol("__this_module", 0, NULL);
	/* On S390, this is faked up too */
	add_symbol("_GLOBAL_OFFSET_TABLE_", 0, NULL);
}

static void load_system_map(const char *filename)
{
	FILE *system_map;
	char line[10240];
	const char ksymstr[] = "__ksymtab_";
	const int ksymstr_len = strlen(ksymstr);

	system_map = fopen(filename, "r");
	if (!system_map)
		fatal("Could not open '%s': %s\n", filename, strerror(errno));

	/* eg. c0294200 R __ksymtab_devfs_alloc_devnum */
	while (fgets(line, sizeof(line)-1, system_map)) {
		char *ptr;

		/* Snip \n */
		ptr = strchr(line, '\n');
		if (ptr)
			*ptr = '\0';

		ptr = strchr(line, ' ');
		if (!ptr || !(ptr = strchr(ptr + 1, ' ')))
			continue;

		/* Covers gpl-only and normal symbols. */
		if (strstarts(ptr+1, ksymstr))
			add_symbol(ptr+1+ksymstr_len, 0, NULL);
	}

	fclose(system_map);
	add_fake_syms();
}

static void load_module_symvers(const char *filename)
{
	FILE *module_symvers;
	char line[10240];

	module_symvers = fopen(filename, "r");
	if (!module_symvers)
		fatal("Could not open '%s': %s\n", filename, strerror(errno));

	/* eg. "0xb352177e\tfind_first_bit\tvmlinux\tEXPORT_SYMBOL" */
	while (fgets(line, sizeof(line)-1, module_symvers)) {
		const char *ver, *sym, *where;

		ver = strtok(line, " \t");
		sym = strtok(NULL, " \t");
		where = strtok(NULL, " \t");
		if (!ver || !sym || !where)
			continue;

		if (streq(where, "vmlinux"))
			add_symbol(sym, strtoull(ver, NULL, 16), NULL);
	}

	fclose(module_symvers);
	add_fake_syms();
}

static struct option options[] = { { "all", 0, NULL, 'a' },
				   { "quick", 0, NULL, 'A' },
				   { "basedir", 1, NULL, 'b' },
				   { "config", 1, NULL, 'C' },
				   { "symvers", 1, NULL, 'E' },
				   { "filesyms", 1, NULL, 'F' },
				   { "errsyms", 0, NULL, 'e' },
				   { "unresolved-error", 0, NULL, 'u' },
				   { "quiet", 0, NULL, 'q' },
				   { "root", 0, NULL, 'r' },
				   { "verbose", 0, NULL, 'v' },
				   { "show", 0, NULL, 'n' },
				   { "dry-run", 0, NULL, 'n' },
				   { "help", 0, NULL, 'h' },
				   { "version", 0, NULL, 'V' },
				   { "warn", 0, NULL, 'w' },
				   { "map", 0, NULL, 'm' },
				   { NULL, 0, NULL, 0 } };

/* Version number or module name?  Don't assume extension. */
static int is_version_number(const char *version)
{
	unsigned int dummy;

	return (sscanf(version, "%u.%u.%u", &dummy, &dummy, &dummy) == 3);
}

static int old_module_version(const char *version)
{
	/* Expect three part version. */
	unsigned int major, sub, minor;

	sscanf(version, "%u.%u.%u", &major, &sub, &minor);

	if (major > 2) return 0;
	if (major < 2) return 1;

	/* 2.x */
	if (sub > 5) return 0;
	if (sub < 5) return 1;

	/* 2.5.x */
	if (minor >= 48) return 0;
	return 1;
}

static void print_usage(const char *name)
{
	fprintf(stderr,
	"%s " VERSION " -- part of " PACKAGE "\n"
	"%s -[aA] [-n -e -v -q -V -r -u -w -m]\n"
	"      [-b basedirectory] [forced_version]\n"
	"depmod [-n -e -v -q -r -u -w] [-F kernelsyms] module1.ko module2.ko ...\n"
	"If no arguments (except options) are given, \"depmod -a\" is assumed\n"
	"\n"
	"depmod will output a dependancy list suitable for the modprobe utility.\n"
	"\n"
	"\n"
	"Options:\n"
	"\t-a, --all            Probe all modules\n"
	"\t-A, --quick          Only does the work if there's a new module\n"
	"\t-e, --errsyms        Report not supplied symbols\n"
	"\t-m, --map            Create the legacy map files\n"
	"\t-n, --show           Write the dependency file on stdout only\n"
	"\t-V, --version        Print the release version\n"
	"\t-v, --verbose        Enable verbose mode\n"
	"\t-w, --warn		Warn on duplicates\n"
	"\t-h, --help           Print this usage message\n"
	"\n"
	"The following options are useful for people managing distributions:\n"
	"\t-b basedirectory\n"
	"\t    --basedir basedirectory    Use an image of a module tree.\n"
	"\t-F kernelsyms\n"
	"\t    --filesyms kernelsyms      Use the file instead of the\n"
	"\t                               current kernel symbols.\n"
	"\t-E Module.symvers\n"
	"\t    --symvers Module.symvers   Use Module.symvers file to check\n"
	"\t                               symbol versions.\n",
	"depmod", "depmod");
}

static int ends_in(const char *name, const char *ext)
{
	unsigned int namelen, extlen;

	/* Grab lengths */
	namelen = strlen(name);
	extlen = strlen(ext);

	if (namelen < extlen) return 0;

	if (streq(name + namelen - extlen, ext))
		return 1;
	return 0;
}

static struct module *grab_module(const char *dirname, const char *filename)
{
	struct module *new;

	new = NOFAIL(malloc(sizeof(*new)
			    + strlen(dirname?:"") + 1 + strlen(filename) + 1));
	if (dirname)
		sprintf(new->pathname, "%s/%s", dirname, filename);
	else
		strcpy(new->pathname, filename);
	new->basename = my_basename(new->pathname);

	INIT_LIST_HEAD(&new->dep_list);
	new->order = INDEX_PRIORITY_MIN;

	new->file = grab_elf_file(new->pathname);
	if (!new->file) {
		warn("Can't read module %s: %s\n",
		     new->pathname, strerror(errno));
		free(new);
		return NULL;
	}
	return new;
}

struct module_traverse
{
	struct module_traverse *prev;
	struct module *mod;
};

static int in_loop(struct module *mod, const struct module_traverse *traverse)
{
	const struct module_traverse *i;

	for (i = traverse; i; i = i->prev) {
		if (i->mod == mod)
			return 1;
	}
	return 0;
}

/* Assume we are doing all the modules, so only report each loop once. */
static void report_loop(const struct module *mod,
			const struct module_traverse *traverse)
{
	const struct module_traverse *i;

	/* Check that start is least alphabetically.  eg.  a depends
	   on b depends on a will get reported for a, not b.  */
	for (i = traverse->prev; i->prev; i = i->prev) {
		if (strcmp(mod->pathname, i->mod->pathname) > 0)
			return;
	}

	/* Is start in the loop?  If not, don't report now. eg. a
	   depends on b which depends on c which depends on b.  Don't
	   report when generating depends for a. */
	if (mod != i->mod)
		return;

	warn("Loop detected: %s ", mod->pathname);
	for (i = traverse->prev; i->prev; i = i->prev)
		fprintf(stderr, "needs %s ", i->mod->basename);
	fprintf(stderr, "which needs %s again!\n", i->mod->basename);
}

/* This is damn slow, but loops actually happen, and we don't want to
   just exit() and leave the user without any modules. */
static int has_dep_loop(struct module *module, struct module_traverse *prev)
{
	unsigned int i;
	struct module_traverse traverse = { .prev = prev, .mod = module };

	if (in_loop(module, prev)) {
		report_loop(module, &traverse);
		return 1;
	}

	for (i = 0; i < module->num_deps; i++)
		if (has_dep_loop(module->deps[i], &traverse))
			return 1;
	return 0;
}

/* Uniquifies and orders a dependency list. */
static void order_dep_list(struct module *start, struct module *mod)
{
	unsigned int i;

	for (i = 0; i < mod->num_deps; i++) {
		/* If it was previously depended on, move it to the
		   tail.  ie. if a needs b and c, and c needs b, we
		   must order b after c. */
		list_del(&mod->deps[i]->dep_list);
		list_add_tail(&mod->deps[i]->dep_list, &start->dep_list);
		order_dep_list(start, mod->deps[i]);
	}
}

static struct module *deleted = NULL;

static void del_module(struct module **modules, struct module *delme)
{
	struct module **i;

	/* Find pointer to it. */ 
	if (modules) {
		for (i = modules; *i != delme; i = &(*i)->next);
		
		*i = delme->next;
	}
	
	/* Save on a list to quiet valgrind.
	   Can't free - other modules may depend on them */
	delme->next = deleted;
	deleted = delme;
}

/* convert to relative path if possible */
static const char *compress_path(const char *path, const char *basedir)
{
	int len = strlen(basedir);

	if (strncmp(path, basedir, len) == 0)
		path += len + 1;
	return path;
}

static int output_deps(struct module *modules,
			FILE *out, char *dirname)
{
	struct module *i;

	for (i = modules; i; i = i->next) {
		struct list_head *j, *tmp;
		order_dep_list(i, i);

		fprintf(out, "%s:", compress_path(i->pathname, dirname));
		list_for_each_safe(j, tmp, &i->dep_list) {
			struct module *dep
				= list_entry(j, struct module, dep_list);
			fprintf(out, " %s",
			        compress_path(dep->pathname, dirname));
			list_del_init(j);
		}
		fprintf(out, "\n");
	}
	return 1;
}

/* warn whenever duplicate module aliases, deps, or symbols are found. */
int warn_dups = 0;

static int output_deps_bin(struct module *modules,
			FILE *out, char *dirname)
{
	struct module *i;
	struct index_node *index;
	char *line;
	char *p;

	index = index_create();

	for (i = modules; i; i = i->next) {
		struct list_head *j, *tmp;
		char modname[strlen(i->pathname)+1];
		
		order_dep_list(i, i);
		
		filename2modname(modname, i->pathname);
		nofail_asprintf(&line, "%s:",
		                compress_path(i->pathname, dirname));
		p = line;
		list_for_each_safe(j, tmp, &i->dep_list) {
			struct module *dep
				= list_entry(j, struct module, dep_list);
			nofail_asprintf(&line, "%s %s",
			                p,
			                compress_path(dep->pathname, dirname));
			free(p);
			p = line;
			list_del_init(j);
		}
		if (index_insert(index, modname, line, i->order) && warn_dups)
			warn("duplicate module deps:\n%s\n",line);
		free(line);
	}
	
	index_write(index, out);
	index_destroy(index);

	return 1;
}


static int smells_like_module(const char *name)
{
	return ends_in(name,".ko") || ends_in(name, ".ko.gz");
}

typedef struct module *(*do_module_t)(const char *dirname,
				      const char *filename,
				      struct module *next,
				      struct module_search *search,
				      struct module_overrides *overrides);

static int is_higher_priority(const char *newpath, const char *oldpath,
			      struct module_search *search,
			      struct module_overrides *overrides)
{
	struct module_search *tmp;
	struct module_overrides *ovtmp;
	int i = 0;
	int prio_builtin = -1;
	int prio_new = -1;
	int prio_old = -1;

/* The names already match, now we check for overrides and directory search
 * order
 */
	for (ovtmp = overrides; ovtmp != NULL; ovtmp = ovtmp->next) {
		if (streq(ovtmp->modfile, newpath))
			return 1;
		if (streq(ovtmp->modfile, oldpath))
			return 0;
	}
	for (i = 0, tmp = search; tmp != NULL; tmp = tmp->next, i++) {
		if (streq(tmp->search_path, MODULE_BUILTIN_KEY))
			prio_builtin = i;
		else if (strncmp(tmp->search_path, newpath, tmp->len) == 0)
			prio_new = i;
		else if (strncmp(tmp->search_path, oldpath, tmp->len) == 0)
			prio_old = i;
	}
	if (prio_new < 0)
		prio_new = prio_builtin;
	if (prio_old < 0)
		prio_old = prio_builtin;

	return prio_new > prio_old;
}


static struct module *do_module(const char *dirname,
				       const char *filename,
				       struct module *list,
				       struct module_search *search,
				       struct module_overrides *overrides)
{
	struct module *new, **i;

	new = grab_module(dirname, filename);
	if (!new)
		return list;

	/* Check if module is already in the list. */
	for (i = &list; *i; i = &(*i)->next) {

		if (streq((*i)->basename, filename)) {
			char newpath[strlen(dirname) + strlen("/")
				      + strlen(filename) + 1];

			sprintf(newpath, "%s/%s", dirname, filename);

			if (is_higher_priority(newpath, (*i)->pathname,search,
					       overrides)) {
				del_module(i, *i);
				
				new->next = *i;
				*i = new;
			} else
				del_module(NULL, new);

			return list;
		}
	}

	/* Not in the list already. Just prepend. */
	new->next = list;
	return new;
}

static struct module *grab_dir(const char *dirname,
			       DIR *dir,
			       struct module *next,
			       do_module_t do_mod,
			       struct module_search *search,
			       struct module_overrides *overrides)
{
	struct dirent *dirent;

	while ((dirent = readdir(dir)) != NULL) {
		if (smells_like_module(dirent->d_name))
			next = do_mod(dirname, dirent->d_name, next,
				      search, overrides);
		else if (!streq(dirent->d_name, ".")
			 && !streq(dirent->d_name, "..")
			 && !streq(dirent->d_name, "source")
			 && !streq(dirent->d_name, "build")) {

			DIR *sub;
			char subdir[strlen(dirname) + 1
				   + strlen(dirent->d_name) + 1];
			sprintf(subdir, "%s/%s", dirname, dirent->d_name);
			sub = opendir(subdir);
			if (sub) {
				next = grab_dir(subdir, sub, next, do_mod,
						search, overrides);
				closedir(sub);
			}
		}
	}
	return next;
}

static struct module *grab_basedir(const char *dirname,
				   struct module_search *search,
				   struct module_overrides *overrides)
{
	DIR *dir;
	struct module *list;

	dir = opendir(dirname);
	if (!dir) {
		warn("Couldn't open directory %s: %s\n",
		     dirname, strerror(errno));
		return NULL;
	}
	list = grab_dir(dirname, dir, NULL, do_module, search, overrides);
	closedir(dir);

	return list;
}

static struct module *sort_modules(const char *dirname, struct module *list)
{
	struct module *tlist = NULL, **tpos = &tlist;
	FILE *modorder;
	int dir_len = strlen(dirname) + 1;
	char file_name[dir_len + strlen("modules.order") + 1];
	char line[10240];
	unsigned int linenum = 0;

	sprintf(file_name, "%s/%s", dirname, "modules.order");

	modorder = fopen(file_name, "r");
	if (!modorder) {
		/* Older kernels don't generate modules.order.  Just
		   return if the file doesn't exist. */
		if (errno == ENOENT)
			return list;
		fatal("Could not open '%s': %s\n", file_name, strerror(errno));
	}

	sprintf(line, "%s/", dirname);

	/* move modules listed in modorder file to tlist in order */
	while (fgets(line, sizeof(line), modorder)) {
		struct module **pos, *mod;
		int len = strlen(line);

		linenum++;
		if (line[len - 1] == '\n')
			line[len - 1] = '\0';

		for (pos = &list; (mod = *pos); pos = &(*pos)->next) {
			if (streq(line, mod->pathname + dir_len)) {
				mod->order = linenum;
				*pos = mod->next;
				mod->next = NULL;
				*tpos = mod;
				tpos = &mod->next;
				break;
			}
		}
	}

	/* append the rest */
	*tpos = list;

	fclose(modorder);

	return tlist;
}

/* Calculate the dependencies for this module */
static void calculate_deps(struct module *module)
{
	unsigned int i;
	struct string_table *symnames;
	struct string_table *symtypes;
	uint64_t *symvers = NULL;
	struct elf_file *file;

	module->num_deps = 0;
	module->deps = NULL;
	file = module->file;

	symnames = file->ops->load_dep_syms(file, &symtypes,
			check_symvers ? &symvers : NULL);
	if (!symnames || !symtypes)
		return;

	for (i = 0; i < symnames->cnt; i++) {
		const char *name;
		uint64_t ver;
		struct module *owner;
		int weak;

		name = symnames->str[i];
		ver = symvers ? symvers[i] : 0;
		weak = (*(symtypes->str[i]) == 'W');
		owner = find_symbol(name, ver, module->pathname, weak);
		if (owner) {
			info("%s needs \"%s\": %s\n",
			       module->pathname, name,
			       owner->pathname);
			add_dep(module, owner);
		}
	}

	free(symnames);
	free(symtypes);
	free(symvers);
}

static struct module *parse_modules(struct module *list)
{
	struct module *i;
	struct elf_file *file;
	struct string_table *syms;
	int j;

	for (i = list; i; i = i->next) {
		uint64_t *symvers = NULL;
		file = i->file;
		syms = file->ops->load_symbols(file,
				check_symvers ? &symvers : NULL);
		if (syms) {
			for (j = 0; j < syms->cnt; j++)
				add_symbol(syms->str[j],
					symvers ? symvers[j] : 0, i);
			strtbl_free(syms);
		}
		free(symvers);
		file->ops->fetch_tables(file, &i->tables);
	}
	
	for (i = list; i; i = i->next)
		calculate_deps(i);
	
	/* Strip out modules with dependency loops. */
 again:
	for (i = list; i; i = i->next) {
		if (has_dep_loop(i, NULL)) {
			warn("Module %s ignored, due to loop\n",
			     i->pathname + skipchars);
			del_module(&list, i);
			goto again;
		}
	}
	
	return list;
}

/* Simply dump hash table. */
static int output_symbols(struct module *unused, FILE *out, char *dirname)
{
	unsigned int i;

	fprintf(out, "# Aliases for symbols, used by symbol_request().\n");
	for (i = 0; i < SYMBOL_HASH_SIZE; i++) {
		struct symbol *s;

		for (s = symbolhash[i]; s; s = s->next) {
			if (s->owner) {
				char modname[strlen(s->owner->pathname)+1];
				filename2modname(modname, s->owner->pathname);
				fprintf(out, "alias symbol:%s %s\n",
					s->name, modname);
			}
		}
	}
	return 1;
}

static int output_symbols_bin(struct module *unused, FILE *out, char *dirname)
{
	struct index_node *index;
	unsigned int i;
	char *alias;
	int duplicate;

	index = index_create();
	
	for (i = 0; i < SYMBOL_HASH_SIZE; i++) {
		struct symbol *s;

		for (s = symbolhash[i]; s; s = s->next) {
			if (s->owner) {
				char modname[strlen(s->owner->pathname)+1];
				filename2modname(modname, s->owner->pathname);
				nofail_asprintf(&alias, "symbol:%s", s->name);
				duplicate = index_insert(index, alias, modname,
							 s->owner->order);
				if (duplicate && warn_dups)
					warn("duplicate module syms:\n%s %s\n",
						alias, modname);
				free(alias);
			}
		}
	}
	
	index_write(index, out);
	index_destroy(index);

	return 1;
}

static int output_builtin_bin(struct module *unused, FILE *out, char *dirname)
{
	struct index_node *index;
	char *textfile, *line;
	unsigned int linenum;
	FILE *f;

	nofail_asprintf(&textfile, "%s/modules.builtin", dirname);
	if (!(f = fopen(textfile, "r"))) {
		if (errno != ENOENT)
			fatal("Could not open '%s': %s\n",
					textfile, strerror(errno));
		free(textfile);
		return 0;
	}
	free(textfile);
	index = index_create();

	while ((line = getline_wrapped(f, &linenum)) != NULL) {
		char *module = line;

		if (!*line || *line == '#') {
			free(line);
			continue;
		}
		filename2modname(module, module);
		index_insert(index, module, "", 0);
		free(line);
	}
	fclose(f);
	index_write(index, out);
	index_destroy(index);

	return 1;
}

static int output_aliases(struct module *modules, FILE *out, char *dirname)
{
	struct module *i;
	struct elf_file *file;
	struct string_table *tbl;
	int j;

	fprintf(out, "# Aliases extracted from modules themselves.\n");
	for (i = modules; i; i = i->next) {
		char modname[strlen(i->pathname)+1];

		file = i->file;
		filename2modname(modname, i->pathname);

		/* Grab from old-style .modalias section. */
		tbl = file->ops->load_strings(file, ".modalias", NULL);
		for (j = 0; tbl && j < tbl->cnt; j++)
			fprintf(out, "alias %s %s\n", tbl->str[j], modname);
		strtbl_free(tbl);

		/* Grab from new-style .modinfo section. */
		tbl = file->ops->load_strings(file, ".modinfo", NULL);
		for (j = 0; tbl && j < tbl->cnt; j++) {
			const char *p = tbl->str[j];
			if (strstarts(p, "alias="))
				fprintf(out, "alias %s %s\n",
					p + strlen("alias="), modname);
		}
		strtbl_free(tbl);
	}
	return 1;
}

static int output_aliases_bin(struct module *modules, FILE *out, char *dirname)
{
	struct module *i;
	struct elf_file *file;
	struct string_table *tbl;
	int j;
	char *alias;
	struct index_node *index;
	int duplicate;

	index = index_create();
	
	for (i = modules; i; i = i->next) {
		char modname[strlen(i->pathname)+1];

		file = i->file;
		filename2modname(modname, i->pathname);

		/* Grab from old-style .modalias section. */
		tbl = file->ops->load_strings(file, ".modalias", NULL);
		for (j = 0; tbl && j < tbl->cnt; j++) {
			alias = NOFAIL(strdup(tbl->str[j]));
			underscores(alias);
			duplicate = index_insert(index, alias, modname, i->order);
			if (duplicate && warn_dups)
				warn("duplicate module alias:\n%s %s\n",
					alias, modname);
			free(alias);
		}
		strtbl_free(tbl);

		/* Grab from new-style .modinfo section. */
		tbl = file->ops->load_strings(file, ".modinfo", NULL);
		for (j = 0; tbl && j < tbl->cnt; j++) {
			const char *p = tbl->str[j];
			if (strstarts(p, "alias=")) {
				alias = NOFAIL(strdup(p + strlen("alias=")));
				underscores(alias);
				duplicate = index_insert(index, alias, modname, i->order);
				if (duplicate && warn_dups)
					warn("duplicate module alias:\n%s %s\n",
						alias, modname);
				free(alias);
			}
		}
		strtbl_free(tbl);
	}
	
	index_write(index, out);
	index_destroy(index);

	return 1;
}

static int output_softdeps(struct module *modules, FILE *out, char *dirname)
{
	struct module *i;
	struct elf_file *file;
	struct string_table *tbl;
	int j;

	fprintf(out, "# Soft dependencies extracted from modules themselves.\n");
	fprintf(out, "# Copy, with a .conf extension, to /etc/modprobe.d to use "
		"it with modprobe.\n");
	for (i = modules; i; i = i->next) {
		char modname[strlen(i->pathname)+1];

		file = i->file;
		filename2modname(modname, i->pathname);

		/* Grab from new-style .modinfo section. */
		tbl = file->ops->load_strings(file, ".modinfo", NULL);
		for (j = 0; tbl && j < tbl->cnt; j++) {
			const char *p = tbl->str[j];
			if (strstarts(p, "softdep="))
				fprintf(out, "softdep %s %s\n",
					modname, p + strlen("softdep="));
		}
		strtbl_free(tbl);
	}
	return 1;
}

static int output_devname(struct module *modules, FILE *out, char *dirname)
{
	struct module *m;

	fprintf(out, "# Device nodes to trigger on-demand module loading.\n");
	for (m = modules; m != NULL; m = m->next) {
		struct string_table *tbl;
		int i;
		char type = '\0';
		const char *devname = NULL;

		tbl = m->file->ops->load_strings(m->file, ".modinfo", NULL);
		for (i = 0; tbl && i < tbl->cnt; i++) {
			const char *p = tbl->str[i];
			unsigned int maj, min;

			if (sscanf(p, "alias=char-major-%u-%u", &maj, &min) == 2)
				type = 'c';
			else if (sscanf(p, "alias=block-major-%u-%u", &maj, &min) == 2)
				type = 'b';
			else if (strstarts(p, "alias=devname:"))
				devname = &p[strlen("alias=devname:")];

			if (type && devname) {
				char modname[strlen(m->pathname)+1];

				filename2modname(modname, m->pathname);
				fprintf(out, "%s %s %c%u:%u\n",
					modname, devname, type, maj, min);
				break;
			}
		}
		strtbl_free(tbl);
	}
	return 1;
}

struct depfile {
	char *name;
	int (*func)(struct module *, FILE *, char *dirname);
	int map_file;
};

static struct depfile depfiles[] = {
	{ "modules.dep", output_deps, 0 }, /* This is what we check for '-A'. */
	{ "modules.dep.bin", output_deps_bin, 0 },
	{ "modules.pcimap", output_pci_table, 1 },
	{ "modules.usbmap", output_usb_table, 1 },
	{ "modules.ccwmap", output_ccw_table, 1 },
	{ "modules.ieee1394map", output_ieee1394_table, 1 },
	{ "modules.isapnpmap", output_isapnp_table, 1 },
	{ "modules.inputmap", output_input_table, 1 },
	{ "modules.ofmap", output_of_table, 1 },
	{ "modules.seriomap", output_serio_table, 1 },
	{ "modules.alias", output_aliases, 0 },
	{ "modules.alias.bin", output_aliases_bin, 0 },
	{ "modules.softdep", output_softdeps, 0 },
	{ "modules.symbols", output_symbols, 0 },
	{ "modules.symbols.bin", output_symbols_bin, 0 },
	{ "modules.builtin.bin", output_builtin_bin, 0 },
	{ "modules.devname", output_devname, 0 },
};

/* If we can't figure it out, it's safe to say "true". */
static int any_modules_newer(const char *dirname, time_t mtime)
{
	DIR *dir;
	struct dirent *dirent;

	dir = opendir(dirname);
	if (!dir)
		return 1;

	while ((dirent = readdir(dir)) != NULL) {
		struct stat st;
		char file[strlen(dirname) + 1 + strlen(dirent->d_name) + 1];

		if (streq(dirent->d_name, ".") || streq(dirent->d_name, ".."))
			continue;

		sprintf(file, "%s/%s", dirname, dirent->d_name);
		if (lstat(file, &st) != 0)
			goto ret_true;

		if (smells_like_module(dirent->d_name)) {
			if (st.st_mtime > mtime)
				goto ret_true;
		} else if (S_ISDIR(st.st_mode)) {
			if (any_modules_newer(file, mtime))
				goto ret_true;
		}
	}
	closedir(dir);
	return 0;

ret_true:
	closedir(dir);
	return 1;
}

static int depfile_out_of_date(const char *dirname)
{
	struct stat st;
	char depfile[strlen(dirname) + 1 + strlen(depfiles[0].name) + 1];

	sprintf(depfile, "%s/%s", dirname, depfiles[0].name);

	if (stat(depfile, &st) != 0)
		return 1;

	return any_modules_newer(dirname, st.st_mtime);
}

static char *strsep_skipspace(char **string, char *delim)
{
	if (!*string)
		return NULL;
	*string += strspn(*string, delim);
	return strsep(string, delim);
}

static struct module_search *add_search(const char *search_path,
					size_t len,
					struct module_search *search)
{

	struct module_search *new;
	
	new = NOFAIL(malloc(sizeof(*new)));
	new->search_path = NOFAIL(strdup(search_path));
	new->len = len;
	new->next = search;

	return new;
	
}

static struct module_overrides *add_override(const char *modfile,
					     struct module_overrides *overrides)
{

	struct module_overrides *new;
	
	new = NOFAIL(malloc(sizeof(*new)));
	new->modfile = NOFAIL(strdup(modfile));
	new->next = overrides;

	return new;
	
}

static int parse_config_scan(const char *filename,
			     const char *basedir,
			     const char *kernelversion,
			     struct module_search **search,
			     struct module_overrides **overrides);

static int parse_config_file(const char *filename,
			     const char *basedir,
			     const char *kernelversion,
			     struct module_search **search,
			     struct module_overrides **overrides)
{
	char *line;
	unsigned int linenum = 0;
	FILE *cfile;

	cfile = fopen(filename, "r");
	if (!cfile) {
		if (errno != ENOENT)
			fatal("could not open '%s', reason: %s\n", filename,
			      strerror(errno));
		return 0;
	}

	while ((line = getline_wrapped(cfile, &linenum)) != NULL) {
		char *ptr = line;
		char *cmd, *modname;

		cmd = strsep_skipspace(&ptr, "\t ");

		if (cmd == NULL || cmd[0] == '#' || cmd[0] == '\0') {
			free(line);
			continue;
		}

		if (streq(cmd, "search")) {
			char *search_path;
			
			while ((search_path = strsep_skipspace(&ptr, "\t "))) {
				char *dirname;
				size_t len;

				if (strcmp(search_path,
						MODULE_BUILTIN_KEY) == 0) {
					*search = add_search(MODULE_BUILTIN_KEY,
							     0, *search);
					continue;
				}
				nofail_asprintf(&dirname, "%s%s%s/%s", basedir,
					MODULE_DIR, kernelversion, search_path);
				len = strlen(dirname);
				*search = add_search(dirname, len, *search);
				free(dirname);
			}
		} else if (streq(cmd, "override")) {
			char *pathname = NULL, *version, *subdir;
			modname = strsep_skipspace(&ptr, "\t ");
			version = strsep_skipspace(&ptr, "\t ");
			subdir = strsep_skipspace(&ptr, "\t ");

			if (!regex_match(kernelversion, (const char *)version))
				continue;

			nofail_asprintf(&pathname, "%s%s%s/%s/%s.ko", basedir,
				MODULE_DIR, kernelversion, subdir, modname);

			*overrides = add_override(pathname, *overrides);
			free(pathname);
		} else if (streq(cmd, "include")) {
			char *newfilename;

			newfilename = strsep_skipspace(&ptr, "\t ");
			if (!newfilename) {
				grammar(cmd, filename, linenum);
			} else {
				warn("\"include %s\" is deprecated, "
				     "please use /etc/depmod.d\n", newfilename);
				if (strstarts(newfilename, "/etc/depmod.d")) {
					warn("\"include /etc/depmod.d\" is "
					     "the default, ignored\n");
				} else {
					if (!parse_config_scan(newfilename, basedir,
							       kernelversion,
							       search, overrides))
					warn("Failed to open included"
					     " config file %s: %s\n",
					     newfilename, strerror(errno));
				}
			}
		} else if (streq(cmd, "make_map_files")) {
			char *option;

			option = strsep_skipspace(&ptr, "\t ");
			if (!option)
				grammar(cmd, filename, linenum);
			else {
				if (streq(option, "yes"))
					make_map_files = 1;
				else if (streq(option, "no"))
					make_map_files = 0;
				else
					grammar(cmd, filename, linenum);
			}
                } else
                        grammar(cmd, filename, linenum);

                free(line);
        }
        fclose(cfile);
        return 1;
}

static int parse_config_scan(const char *filename,
			     const char *basedir,
			     const char *kernelversion,
			     struct module_search **search,
			     struct module_overrides **overrides)
{
	DIR *dir;
	int ret = 0;

	dir = opendir(filename);
	if (dir) {
		struct file_entry {
			struct list_head node;
			char name[];
		};
		LIST_HEAD(files_list);
		struct file_entry *fe, *fe_tmp;
		struct dirent *i;

		/* sort files from directory into list */
		while ((i = readdir(dir)) != NULL) {
			size_t len;

			if (i->d_name[0] == '.')
				continue;
			if (!config_filter(i->d_name))
				continue;

			len = strlen(i->d_name);
			if (len < 6 || strcmp(&i->d_name[len-5], ".conf") != 0)
				warn("All config files need .conf: %s/%s, "
				     "it will be ignored in a future release.\n",
				     filename, i->d_name);
			fe = malloc(sizeof(struct file_entry) + len + 1);
			if (fe == NULL)
				continue;
			strcpy(fe->name, i->d_name);
			list_for_each_entry(fe_tmp, &files_list, node)
				if (strcmp(fe_tmp->name, fe->name) >= 0)
					break;
			list_add_tail(&fe->node, &fe_tmp->node);
		}
		closedir(dir);

		/* parse list of files */
		list_for_each_entry_safe(fe, fe_tmp, &files_list, node) {
			char *cfgfile;

			nofail_asprintf(&cfgfile, "%s/%s", filename, fe->name);
			if (!parse_config_file(cfgfile, basedir, kernelversion,
					       search, overrides))
				warn("Failed to open config file "
				     "%s: %s\n", fe->name, strerror(errno));
			free(cfgfile);
			list_del(&fe->node);
			free(fe);
		}

		ret = 1;
	} else {
		if (parse_config_file(filename, basedir, kernelversion, search,
				      overrides))
			ret = 1;
	}

	return ret;
}

static void parse_toplevel_config(const char *filename,
				  const char *basedir,
				  const char *kernelversion,
				  struct module_search **search,
				  struct module_overrides **overrides)
{
	if (filename) {
		if (!parse_config_scan(filename, basedir, kernelversion, search,
				 overrides))
			fatal("Failed to open config file %s: %s\n",
			      filename, strerror(errno));
		return;
	}

	/* deprecated config file */
	if (parse_config_file("/etc/depmod.conf", basedir, kernelversion,
			      search, overrides) > 0)
		warn("Deprecated config file /etc/depmod.conf, "
		      "all config files belong into /etc/depmod.d/.\n");

	/* default config */
	parse_config_scan("/etc/depmod.d", basedir, kernelversion,
			  search, overrides);
}

/* Local to main, but not freed on exit.  Keep valgrind quiet. */
struct module *list = NULL;
struct module_search *search = NULL;
struct module_overrides *overrides = NULL;

int main(int argc, char *argv[])
{
	int opt, all = 0, maybe_all = 0, doing_stdout = 0;
	char *basedir = "", *dirname, *version;
	char *system_map = NULL, *module_symvers = NULL;
	int i;
	const char *config = NULL;

	if (native_endianness() == 0)
		abort();

	while ((opt = getopt_long(argc, argv, "aAb:C:E:F:euqrvnhVwm", options, NULL))
	       != -1) {
		switch (opt) {
		case 'a':
			all = 1;
			break;
		case 'A':
			maybe_all = 1;
			break;
		case 'b':
			basedir = optarg;
			skipchars = strlen(basedir);
			break;
		case 'C':
			config = optarg;
			break;
		case 'E':
			module_symvers = optarg;
			check_symvers = 1;
			break;
		case 'F':
			system_map = optarg;
			break;
		case 'e':
			print_unknown = 1;
			break;
		case 'u':
		case 'q':
		case 'r':
			break;
		case 'v':
			verbose = 1;
			break;
		case 'n':
			doing_stdout = 1;
			break;
		case 'h':
			print_usage(argv[0]);
			exit(0);
			break;
		case 'V':
			printf("%s %s\n", PACKAGE, VERSION);
			exit(0);
		case 'w':
			warn_dups = 1;
			break;
		case 'm':
			force_map_files = 1;
			break;
		default:
			print_usage(argv[0]);
			exit(1);
		}
	}

	if (module_symvers)
		load_module_symvers(module_symvers);
	else if (system_map)
		load_system_map(system_map);
	else if (print_unknown) {
		warn("-e needs -E or -F\n");
		print_unknown = 0;
	}

	/* They can specify the version naked on the command line */
	if (optind < argc && is_version_number(argv[optind])) {
		version = NOFAIL(strdup(argv[optind]));
		optind++;
	} else {
		struct utsname buf;
		uname(&buf);
		version = NOFAIL(strdup(buf.release));
	}

	/* Check for old version. */
	if (old_module_version(version)) {
		fprintf(stderr, "Kernel version %s requires old depmod\n",
			version);
		exit(2);
	}

	/* Depmod -a by default if no names. */
	if (optind == argc)
		all = 1;

	nofail_asprintf(&dirname, "%s%s%s", basedir, MODULE_DIR, version);

	if (maybe_all) {
		if (!doing_stdout && !depfile_out_of_date(dirname))
			exit(0);
		all = 1;
	}

	parse_toplevel_config(config, basedir, version, &search, &overrides);

	/* For backward compatibility add "updates" to the head of the search
	 * list here. But only if there was no "search" option specified.
	 */
	if (!search) {
		char *dirname;
		size_t len;

		nofail_asprintf(&dirname, "%s%s%s/updates", basedir,
				MODULE_DIR, version);
		len = strlen(dirname);
		search = add_search(dirname, len, search);
	}
	if (!all) {
		/* Do command line args. */
		for (opt = optind; opt < argc; opt++) {
			struct module *new;

			if (argv[opt][0] != '/')
				fatal("modules must be specified using absolute paths.\n"
					"\"%s\" is a relative path\n", argv[opt]);

			new = grab_module(NULL, argv[opt]);
			if (!new) {
				/* cmd-line specified modules must exist */
				fatal("grab_module() failed for module %s\n", argv[opt]);
			}
			new->next = list;
			list = new;
		}
	} else {
		list = grab_basedir(dirname,search,overrides);
	}
	list = sort_modules(dirname,list);
	list = parse_modules(list);

	for (i = 0; i < sizeof(depfiles)/sizeof(depfiles[0]); i++) {
		FILE *out;
		int res;
		struct depfile *d = &depfiles[i];
		char depname[strlen(dirname) + 1 + strlen(d->name) + 1];
		char tmpname[strlen(dirname) + 1 + strlen(d->name) +
						strlen(".temp") + 1];

		if (d->map_file && !make_map_files && !force_map_files)
			continue;

		sprintf(depname, "%s/%s", dirname, d->name);
		sprintf(tmpname, "%s/%s.temp", dirname, d->name);
		if (!doing_stdout) {
			out = fopen(tmpname, "w");
			if (!out)
				fatal("Could not open %s for writing: %s\n",
					tmpname, strerror(errno));
		} else {
			out = stdout;
			if (ends_in(depname, ".bin"))
				continue;
		}
		res = d->func(list, out, dirname);
		if (doing_stdout)
			continue;
		fclose(out);
		if (res) {
			if (rename(tmpname, depname) < 0)
				fatal("Could not rename %s into %s: %s\n",
					tmpname, depname, strerror(errno));
		} else {
			if (unlink(tmpname) < 0)
				warn("Could not delete %s: %s\n",
					tmpname, strerror(errno));
		}
	}

	free(dirname);
	free(version);
	
	return 0;
}
