/*
* kmod-depmod - calculate modules.dep using libkmod.
*
- * Copyright (C) 2011-2012 ProFUSION embedded systems
+ * Copyright (C) 2011-2013 ProFUSION embedded systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include "libkmod.h"
-#include "libkmod-array.h"
-#include "libkmod-hash.h"
-#include "libkmod-util.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <getopt.h>
+#include <assert.h>
+#include <ctype.h>
+#include <dirent.h>
#include <errno.h>
-#include <string.h>
-#include <syslog.h>
+#include <getopt.h>
#include <limits.h>
-#include <dirent.h>
-#include <sys/utsname.h>
-#include <sys/stat.h>
#include <regex.h>
-#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
-#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
-#define DEFAULT_VERBOSE LOG_WARNING
-static int verbose = DEFAULT_VERBOSE;
+#include <shared/array.h>
+#include <shared/hash.h>
+#include <shared/macro.h>
+#include <shared/util.h>
+#include <shared/scratchbuf.h>
-#define KMOD_EXT_UNC 0
+#include <libkmod/libkmod-internal.h>
-static const struct kmod_ext {
- const char *ext;
- size_t len;
-} kmod_exts[] = {
- {".ko", sizeof(".ko") - 1},
-#ifdef ENABLE_ZLIB
- {".ko.gz", sizeof(".ko.gz") - 1},
-#endif
-#ifdef ENABLE_XZ
- {".ko.xz", sizeof(".ko.xz") - 1},
-#endif
- {NULL, 0},
-};
+#undef ERR
+#undef DBG
+
+#include "kmod.h"
+
+#define DEFAULT_VERBOSE LOG_WARNING
+static int verbose = DEFAULT_VERBOSE;
static const char CFG_BUILTIN_KEY[] = "built-in";
static const char *default_cfg_paths[] = {
{ "verbose", no_argument, 0, 'v' },
{ "show", no_argument, 0, 'n' },
{ "dry-run", no_argument, 0, 'n' },
- { "symbol-prefix", no_argument, 0, 'P' },
+ { "symbol-prefix", required_argument, 0, 'P' },
{ "warn", no_argument, 0, 'w' },
{ "map", no_argument, 0, 'm' }, /* deprecated */
{ "version", no_argument, 0, 'V' },
{ }
};
-static void help(const char *progname)
+static void help(void)
{
- fprintf(stderr,
- "Usage:\n"
+ printf("Usage:\n"
"\t%s -[aA] [options] [forced_version]\n"
"\n"
"If no arguments (except options) are given, \"depmod -a\" is assumed\n"
"\t current kernel symbols.\n"
"\t-E, --symvers=FILE Use Module.symvers file to check\n"
"\t symbol versions.\n",
- progname);
+ program_invocation_short_name);
}
+_printf_format_(1, 2)
static inline void _show(const char *fmt, ...)
{
va_list args;
fflush(stdout);
va_end(args);
}
-
-static void _log(int prio, const char *fmt, ...)
-{
- const char *prioname;
- char buf[32], *msg;
- va_list args;
-
- if (prio > verbose)
- return;
-
- va_start(args, fmt);
- if (vasprintf(&msg, fmt, args) < 0)
- msg = NULL;
- va_end(args);
- if (msg == NULL)
- return;
-
- switch (prio) {
- case LOG_CRIT:
- prioname = "FATAL";
- break;
- case LOG_ERR:
- prioname = "ERROR";
- break;
- case LOG_WARNING:
- prioname = "WARNING";
- break;
- case LOG_NOTICE:
- prioname = "NOTICE";
- break;
- case LOG_INFO:
- prioname = "INFO";
- break;
- case LOG_DEBUG:
- prioname = "DEBUG";
- break;
- default:
- snprintf(buf, sizeof(buf), "LOG-%03d", prio);
- prioname = buf;
- }
-
- fprintf(stderr, "%s: %s", prioname, msg);
- free(msg);
-
- if (prio <= LOG_CRIT)
- exit(EXIT_FAILURE);
-}
-#define CRIT(...) _log(LOG_CRIT, __VA_ARGS__)
-#define ERR(...) _log(LOG_ERR, __VA_ARGS__)
-#define WRN(...) _log(LOG_WARNING, __VA_ARGS__)
-#define INF(...) _log(LOG_INFO, __VA_ARGS__)
-#define DBG(...) _log(LOG_DEBUG, __VA_ARGS__)
#define SHOW(...) _show(__VA_ARGS__)
/* binary index write *************************************************/
#include <arpa/inet.h>
-#include "macro.h"
/* BEGIN: code from module-init-tools/index.c just modified to compile here.
*
* Original copyright:
* along with these programs. If not, see <http://www.gnu.org/licenses/>.
*/
-/* Integers are stored as 32 bit unsigned in "network" order, i.e. MSB first.
- All files start with a magic number.
+/* see documentation in libkmod/libkmod-index.c */
- Magic spells "BOOTFAST". Second one used on newer versioned binary files.
- */
-/* #define INDEX_MAGIC_OLD 0xB007FA57 */
#define INDEX_MAGIC 0xB007F457
-
-/* We use a version string to keep track of changes to the binary format
- * This is stored in the form: INDEX_MAJOR (hi) INDEX_MINOR (lo) just in
- * case we ever decide to have minor changes that are not incompatible.
- */
-
#define INDEX_VERSION_MAJOR 0x0002
#define INDEX_VERSION_MINOR 0x0001
#define INDEX_VERSION ((INDEX_VERSION_MAJOR<<16)|INDEX_VERSION_MINOR)
-
-/* The index file maps keys to values. Both keys and values are ASCII strings.
- Each key can have multiple values. Values are sorted by an integer priority.
-
- The reader also implements a wildcard search (including range expressions)
- where the keys in the index are treated as patterns.
- This feature is required for module aliases.
-*/
-
-/* Implementation is based on a radix tree, or "trie".
- Each arc from parent to child is labelled with a character.
- Each path from the root represents a string.
-
- == Example strings ==
-
- ask
- ate
- on
- once
- one
-
- == Key ==
- + Normal node
- * Marked node, representing a key and it's values.
-
- +
- |-a-+-s-+-k-*
- | |
- | `-t-+-e-*
- |
- `-o-+-n-*-c-+-e-*
- |
- `-e-*
-
- Naive implementations tend to be very space inefficient; child pointers
- are stored in arrays indexed by character, but most child pointers are null.
-
- Our implementation uses a scheme described by Wikipedia as a Patrica trie,
-
- "easiest to understand as a space-optimized trie where
- each node with only one child is merged with its child"
-
- +
- |-a-+-sk-*
- | |
- | `-te-*
- |
- `-on-*-ce-*
- |
- `-e-*
-
- We still use arrays of child pointers indexed by a single character;
- the remaining characters of the label are stored as a "prefix" in the child.
-
- The paper describing the original Patrica trie works on individiual bits -
- each node has a maximum of two children, which increases space efficiency.
- However for this application it is simpler to use the ASCII character set.
- Since the index file is read-only, it can be compressed by omitting null
- child pointers at the start and end of arrays.
-*/
-
-#define INDEX_PRIORITY_MIN UINT32_MAX
+#define INDEX_CHILDMAX 128
struct index_value {
struct index_value *next;
};
/* In-memory index (depmod only) */
-
-#define INDEX_CHILDMAX 128
struct index_node {
char *prefix; /* path compression */
struct index_value *values;
struct index_node *children[INDEX_CHILDMAX]; /* indexed by character */
};
-/* Disk format:
-
- uint32_t magic = INDEX_MAGIC;
- uint32_t version = INDEX_VERSION;
- uint32_t root_offset;
-
- (node_offset & INDEX_NODE_MASK) specifies the file offset of nodes:
-
- char[] prefix; // nul terminated
-
- char first;
- char last;
- uint32_t children[last - first + 1];
-
- uint32_t value_count;
- struct {
- uint32_t priority;
- char[] value; // nul terminated
- } values[value_count];
-
- (node_offset & INDEX_NODE_FLAGS) indicates which fields are present.
- Empty prefixes are omitted, leaf nodes omit the three child-related fields.
-
- This could be optimised further by adding a sparse child format
- (indicated using a new flag).
- */
/* Format of node offsets within index file */
enum node_offset {
fputc(node->first, out);
fputc(node->last, out);
fwrite(child_offs, sizeof(uint32_t), child_count, out);
- free(child_offs);
offset |= INDEX_NODE_CHILDS;
}
+ free(child_offs);
+
if (node->values) {
const struct index_value *v;
unsigned int value_count;
/* Second word is reserved for the offset of the root node */
initial_offset = ftell(out);
+ assert(initial_offset >= 0);
u = 0;
fwrite(&u, sizeof(uint32_t), 1, out);
/* Update first word */
final_offset = ftell(out);
- fseek(out, initial_offset, SEEK_SET);
+ assert(final_offset >= 0);
+ (void)fseek(out, initial_offset, SEEK_SET);
fwrite(&u, sizeof(uint32_t), 1, out);
- fseek(out, final_offset, SEEK_SET);
+ (void)fseek(out, final_offset, SEEK_SET);
}
/* END: code from module-init-tools/index.c just modified to compile here.
*/
-/* utils (variants of libkmod-utils.c) *********************************/
-static const char *underscores2(const char *input, char *output, size_t outputlen)
-{
- size_t i;
-
- for (i = 0; input[i] != '\0' && i < outputlen - 1; i++) {
- switch (input[i]) {
- case '-':
- output[i] = '_';
- break;
-
- case ']':
- WRN("Unmatched bracket in %s\n", input);
- return NULL;
-
- case '[': {
- size_t off = strcspn(input + i, "]");
- if (input[i + off] == '\0') {
- WRN("Unmatched bracket in %s\n", input);
- return NULL;
- }
- memcpy(output + i, input + i, off + 1);
- i += off;
- break;
- }
-
- default:
- output[i] = input[i];
- }
- }
- output[i] = '\0';
-
- return output;
-}
-
/* configuration parsing **********************************************/
struct cfg_override {
struct cfg_override *next;
return err;
}
- while ((line = getline_wrapped(fp, &linenum)) != NULL) {
+ while ((line = freadline_wrapped(fp, &linenum)) != NULL) {
char *cmd, *saveptr;
if (line[0] == '\0' || line[0] == '#')
static int cfg_files_list(struct cfg_file ***p_files, size_t *p_n_files,
const char *path)
{
+ struct dirent *dent;
DIR *d;
int err = 0;
struct stat st;
return err;
}
- if (S_ISREG(st.st_mode)) {
+ if (!S_ISDIR(st.st_mode)) {
cfg_files_insert_sorted(p_files, p_n_files, path, NULL);
return 0;
- } if (!S_ISDIR(st.st_mode)) {
- ERR("unsupported file mode %s: %#x\n", path, st.st_mode);
- return -EINVAL;
}
d = opendir(path);
return -EINVAL;
}
- for (;;) {
- struct dirent ent, *entp;
-
- err = readdir_r(d, &ent, &entp);
- if (err != 0) {
- ERR("reading entry %s\n", strerror(-err));
- break;
- }
- if (entp == NULL)
- break;
- if (cfg_files_filter_out(d, path, entp->d_name))
+ for (dent = readdir(d); dent != NULL; dent = readdir(d)) {
+ if (cfg_files_filter_out(d, path, dent->d_name))
continue;
- cfg_files_insert_sorted(p_files, p_n_files, path, entp->d_name);
+ cfg_files_insert_sorted(p_files, p_n_files, path, dent->d_name);
}
closedir(d);
/* depmod calculations ***********************************************/
+struct vertex;
struct mod {
struct kmod_module *kmod;
char *path;
struct kmod_list *dep_sym_list;
struct array deps; /* struct symbol */
size_t baselen; /* points to start of basename/filename */
- size_t modnamelen;
+ size_t modnamesz;
int sort_idx; /* sort index using modules.order */
int dep_sort_idx; /* topological sort index */
uint16_t idx; /* index in depmod->modules.array */
uint16_t users; /* how many modules depend on this one */
- uint8_t dep_loop : 1;
+ bool visited; /* helper field to report cycles */
+ struct vertex *vertex; /* helper field to report cycles */
char modname[];
};
struct hash *modules_by_uncrelpath;
struct hash *modules_by_name;
struct hash *symbols;
- unsigned int dep_loops;
};
static void mod_free(struct mod *mod)
{
DBG("free %p kmod=%p, path=%s\n", mod, mod->kmod, mod->path);
array_free_array(&mod->deps);
- assert(mod->kmod == NULL);
+ kmod_module_unref(mod->kmod);
kmod_module_info_free_list(mod->info_list);
kmod_module_dependency_symbols_free_list(mod->dep_sym_list);
free(mod->uncrelpath);
{
const struct cfg *cfg = depmod->cfg;
const char *modname, *lastslash;
- size_t modnamelen;
+ size_t modnamesz;
struct mod *mod;
int err;
modname = kmod_module_get_name(kmod);
- modnamelen = strlen(modname) + 1;
+ modnamesz = strlen(modname) + 1;
- mod = calloc(1, sizeof(struct mod) + modnamelen);
+ mod = calloc(1, sizeof(struct mod) + modnamesz);
if (mod == NULL)
return -ENOMEM;
mod->kmod = kmod;
mod->sort_idx = depmod->modules.count + 1;
mod->dep_sort_idx = INT32_MAX;
- memcpy(mod->modname, modname, modnamelen);
- mod->modnamelen = modnamelen;
+ memcpy(mod->modname, modname, modnamesz);
+ mod->modnamesz = modnamesz;
array_init(&mod->deps, 4);
}
if (mod->relpath != NULL) {
- size_t uncrelpathlen = lastslash - mod->relpath + modnamelen
- + kmod_exts[KMOD_EXT_UNC].len;
+ size_t uncrelpathlen = lastslash - mod->relpath + modnamesz
+ + strlen(KMOD_EXTENSION_UNCOMPRESSED);
mod->uncrelpath = memdup(mod->relpath, uncrelpathlen + 1);
mod->uncrelpath[uncrelpathlen] = '\0';
err = hash_add_unique(depmod->modules_by_uncrelpath,
mod->uncrelpath, mod);
if (err < 0) {
ERR("hash_add_unique %s: %s\n",
- mod->relpath, strerror(-err));
+ mod->uncrelpath, strerror(-err));
hash_del(depmod->modules_by_name, mod->modname);
goto fail;
}
{
DBG("del %p kmod=%p, path=%s\n", mod, mod->kmod, mod->path);
- if (mod->relpath != NULL)
- hash_del(depmod->modules_by_uncrelpath, mod->relpath);
+ if (mod->uncrelpath != NULL)
+ hash_del(depmod->modules_by_uncrelpath, mod->uncrelpath);
hash_del(depmod->modules_by_name, mod->modname);
const struct cfg *cfg = depmod->cfg;
const struct cfg_override *ov;
const struct cfg_search *se;
+
+ /* baselen includes the last '/' and mod->baselen doesn't. So it's
+ * actually correct to use modnamelen in the first and modnamesz in
+ * the latter */
size_t newlen = baselen + modnamelen;
- size_t oldlen = mod->baselen + mod->modnamelen;
+ size_t oldlen = mod->baselen + mod->modnamesz;
const char *oldpath = mod->path;
int i, bprio = -1, oldprio = -1, newprio = -1;
DBG("search %s\n", se->builtin ? "built-in" : se->path);
if (se->builtin)
bprio = i;
- else if (newlen >= se->len &&
+ else if (newlen > se->len && newpath[se->len] == '/' &&
memcmp(se->path, newpath, se->len) == 0)
newprio = i;
- else if (oldlen >= se->len &&
+ else if (oldlen > se->len && oldpath[se->len] == '/' &&
memcmp(se->path, oldpath, se->len) == 0)
oldprio = i;
}
oldprio = bprio;
DBG("priorities: built-in: %d, old: %d, new: %d\n",
- bprio, newprio, oldprio);
+ bprio, oldprio, newprio);
return newprio <= oldprio;
}
struct mod *mod;
const char *relpath;
char modname[PATH_MAX];
- const struct kmod_ext *eitr;
size_t modnamelen;
- uint8_t matches = 0;
int err;
- for (eitr = kmod_exts; eitr->ext != NULL; eitr++) {
- if (namelen <= eitr->len)
- continue;
- if (streq(path + baselen + namelen - eitr->len, eitr->ext)) {
- matches = 1;
- break;
- }
- }
- if (!matches)
+ if (!path_ends_with_kmod_ext(path + baselen, namelen))
return 0;
if (path_to_modname(path, modname, &modnamelen) == NULL) {
namelen = strlen(name);
if (baselen + namelen + 2 >= PATH_MAX) {
path[baselen] = '\0';
- ERR("path is too long %s%s %zd\n", path, name);
+ ERR("path is too long %s%s\n", path, name);
continue;
}
memcpy(path + baselen, name, namelen + 1);
static int mod_cmp(const void *pa, const void *pb) {
const struct mod *a = *(const struct mod **)pa;
const struct mod *b = *(const struct mod **)pb;
- if (a->dep_loop == b->dep_loop)
- return a->sort_idx - b->sort_idx;
- else if (a->dep_loop)
- return 1;
- else if (b->dep_loop)
- return -1;
return a->sort_idx - b->sort_idx;
}
}
static int depmod_symbol_add(struct depmod *depmod, const char *name,
- uint64_t crc, const struct mod *owner)
+ bool prefix_skipped, uint64_t crc,
+ const struct mod *owner)
{
size_t namelen;
int err;
struct symbol *sym;
- if (name[0] == depmod->cfg->sym_prefix)
+ if (!prefix_skipped && (name[0] == depmod->cfg->sym_prefix))
name++;
namelen = strlen(name) + 1;
kmod_list_foreach(l, list) {
const char *name = kmod_module_symbol_get_symbol(l);
uint64_t crc = kmod_module_symbol_get_crc(l);
- depmod_symbol_add(depmod, name, crc, mod);
+ depmod_symbol_add(depmod, name, false, crc, mod);
}
kmod_module_symbols_free_list(list);
mod->kmod = NULL;
}
- DBG("loaded symbols (%zd modules, %zd symbols)\n",
+ DBG("loaded symbols (%zd modules, %u symbols)\n",
depmod->modules.count, hash_get_count(depmod->symbols));
return 0;
{
struct mod **itr, **itr_end;
- DBG("load dependencies (%zd modules, %zd symbols)\n",
+ DBG("load dependencies (%zd modules, %u symbols)\n",
depmod->modules.count, hash_get_count(depmod->symbols));
itr = (struct mod **)depmod->modules.array;
depmod_load_module_dependencies(depmod, mod);
}
- DBG("loaded dependencies (%zd modules, %zd symbols)\n",
+ DBG("loaded dependencies (%zd modules, %u symbols)\n",
depmod->modules.count, hash_get_count(depmod->symbols));
return 0;
{
const struct mod *a = *(const struct mod **)pa;
const struct mod *b = *(const struct mod **)pb;
- if (a->dep_loop == b->dep_loop)
- return a->dep_sort_idx - b->dep_sort_idx;
- else if (a->dep_loop)
- return 1;
- else if (b->dep_loop)
- return -1;
return a->dep_sort_idx - b->dep_sort_idx;
}
}
}
+struct vertex {
+ struct vertex *parent;
+ struct mod *mod;
+};
+
+static struct vertex *vertex_new(struct mod *mod, struct vertex *parent)
+{
+ struct vertex *v;
+
+ v = malloc(sizeof(*v));
+ if (v == NULL)
+ return NULL;
+
+ v->parent = parent;
+ v->mod = mod;
+ return v;
+}
+
+static void depmod_list_remove_data(struct kmod_list **list, void *data)
+{
+ struct kmod_list *l;
+
+ l = kmod_list_remove_data(*list, data);
+ *list = l;
+}
+
+static void depmod_report_one_cycle(struct depmod *depmod,
+ struct vertex *vertex,
+ struct kmod_list **roots,
+ struct hash *loop_set)
+{
+ const char sep[] = " -> ";
+ size_t sz;
+ char *buf;
+ struct array reverse;
+ int i;
+ int n;
+ struct vertex *v;
+
+ array_init(&reverse, 3);
+
+ sz = 0;
+ for (v = vertex->parent, n = 0;
+ v != NULL;
+ v = v->parent, n++) {
+
+ sz += v->mod->modnamesz - 1;
+ array_append(&reverse, v);
+ hash_add(loop_set, v->mod->modname, NULL);
+ }
+ sz += vertex->mod->modnamesz - 1;
+
+ buf = malloc(sz + n * strlen(sep) + 1);
+
+ sz = 0;
+ for (i = reverse.count - 1; i >= 0; i--) {
+ size_t len;
+
+ v = reverse.array[i];
+
+ len = v->mod->modnamesz - 1;
+ memcpy(buf + sz, v->mod->modname, len);
+ sz += len;
+ strcpy(buf + sz, sep);
+ sz += strlen(sep);
+
+ depmod_list_remove_data(roots, v->mod);
+ }
+ strcpy(buf + sz, vertex->mod->modname);
+ ERR("Cycle detected: %s\n", buf);
+
+ free(buf);
+ array_free_array(&reverse);
+}
+
+static int depmod_report_cycles_from_root(struct depmod *depmod,
+ struct mod *root_mod,
+ struct kmod_list **roots,
+ void **stack,
+ size_t stack_size,
+ struct hash *loop_set)
+{
+ struct kmod_list *free_list = NULL; /* struct vertex */
+ struct kmod_list *l;
+ struct vertex *root;
+ struct vertex *vertex;
+ struct vertex *v;
+ struct mod *m;
+ struct mod **itr, **itr_end;
+ size_t is;
+
+ root = vertex_new(root_mod, NULL);
+ if (root == NULL) {
+ ERR("No memory to report cycles\n");
+ return -ENOMEM;
+ }
+
+ l = kmod_list_append(free_list, root);
+ if (l == NULL) {
+ ERR("No memory to report cycles\n");
+ return -ENOMEM;
+ }
+ free_list = l;
+
+ is = 0;
+ stack[is++] = (void *)root;
+
+ while (is > 0) {
+ vertex = stack[--is];
+ m = vertex->mod;
+ /*
+ * because of the topological sort we can start only
+ * from part of a loop or from a branch after a loop
+ */
+ if (m->visited && m == root->mod) {
+ depmod_report_one_cycle(depmod, vertex,
+ roots, loop_set);
+ continue;
+ }
+
+ m->visited = true;
+ if (m->deps.count == 0) {
+ /*
+ * boundary condition: if there is more than one
+ * single node branch (not a loop), it is
+ * recognized as a loop by the code above:
+ * m->visited because more then one,
+ * m == root->mod since it is a single node.
+ * So, prevent deeping into the branch second
+ * time.
+ */
+ depmod_list_remove_data(roots, m);
+
+ continue;
+ }
+
+ itr = (struct mod **) m->deps.array;
+ itr_end = itr + m->deps.count;
+ for (; itr < itr_end; itr++) {
+ struct mod *dep = *itr;
+ v = vertex_new(dep, vertex);
+ if (v == NULL) {
+ ERR("No memory to report cycles\n");
+ return -ENOMEM;
+ }
+ assert(is < stack_size);
+ stack[is++] = v;
+
+ l = kmod_list_append(free_list, v);
+ if (l == NULL) {
+ ERR("No memory to report cycles\n");
+ return -ENOMEM;
+ }
+ free_list = l;
+
+ }
+ }
+ while (free_list) {
+ v = free_list->data;
+ l = kmod_list_remove(free_list);
+ free_list = l;
+ free(v);
+ }
+
+ return 0;
+}
+
+static void depmod_report_cycles(struct depmod *depmod, uint16_t n_mods,
+ uint16_t *users)
+{
+ int num_cyclic = 0;
+ struct kmod_list *roots = NULL; /* struct mod */
+ struct kmod_list *l;
+ size_t n_r; /* local n_roots */
+ int i;
+ int err;
+ _cleanup_free_ void **stack = NULL;
+ struct mod *m;
+ struct mod *root;
+ struct hash *loop_set;
+
+ for (i = 0, n_r = 0; i < n_mods; i++) {
+ if (users[i] <= 0)
+ continue;
+ m = depmod->modules.array[i];
+ l = kmod_list_append(roots, m);
+ if (l == NULL) {
+ ERR("No memory to report cycles\n");
+ return;
+ }
+ roots = l;
+ n_r++;
+ }
+
+ stack = malloc(n_r * sizeof(void *));
+ if (stack == NULL) {
+ ERR("No memory to report cycles\n");
+ return;
+ }
+
+ loop_set = hash_new(16, NULL);
+ if (loop_set == NULL) {
+ ERR("No memory to report cycles\n");
+ return;
+ }
+
+ while (roots != NULL) {
+ root = roots->data;
+ l = kmod_list_remove(roots);
+ roots = l;
+ err = depmod_report_cycles_from_root(depmod,
+ root,
+ &roots,
+ stack, n_r, loop_set);
+ if (err < 0)
+ goto err;
+ }
+
+ num_cyclic = hash_get_count(loop_set);
+ ERR("Found %d modules in dependency cycles!\n", num_cyclic);
+
+err:
+ hash_free(loop_set);
+}
+
static int depmod_calculate_dependencies(struct depmod *depmod)
{
const struct mod **itrm;
uint16_t *users, *roots, *sorted;
uint16_t i, n_roots = 0, n_sorted = 0, n_mods = depmod->modules.count;
+ int ret = 0;
users = malloc(sizeof(uint16_t) * n_mods * 3);
if (users == NULL)
roots = users + n_mods;
sorted = roots + n_mods;
- DBG("calculate dependencies and ordering (%zd modules)\n", n_mods);
+ DBG("calculate dependencies and ordering (%hu modules)\n", n_mods);
assert(depmod->modules.count < UINT16_MAX);
}
if (n_sorted < n_mods) {
- WRN("found %hu modules in dependency cycles!\n",
- n_mods - n_sorted);
- for (i = 0; i < n_mods; i++) {
- struct mod *m;
- if (users[i] == 0)
- continue;
- m = depmod->modules.array[i];
- WRN("%s in dependency cycle!\n", m->path);
- m->dep_loop = 1;
- m->dep_sort_idx = INT32_MAX;
- depmod->dep_loops++;
- }
+ depmod_report_cycles(depmod, n_mods, users);
+ ret = -EINVAL;
+ goto exit;
}
depmod_sort_dependencies(depmod);
- DBG("calculated dependencies and ordering (%u loops, %zd modules)\n",
- depmod->dep_loops, n_mods);
+ DBG("calculated dependencies and ordering (%hu modules)\n", n_mods);
+exit:
free(users);
- return 0;
+ return ret;
}
static int depmod_load(struct depmod *depmod)
const char *p = mod_get_compressed_path(mod);
size_t j, n_deps;
- if (mod->dep_loop) {
- DBG("Ignored %s due dependency loops\n", p);
- continue;
- }
-
fprintf(out, "%s:", p);
if (mod->deps.count == 0)
for (j = 0; j < n_deps; j++) {
const struct mod *d = deps[j];
- if (d->dep_loop) {
- DBG("Ignored %s (dependency of %s) "
- "due dependency loops\n",
- mod_get_compressed_path(d), p);
- continue;
- }
fprintf(out, " %s", mod_get_compressed_path(d));
}
free(deps);
size_t j, n_deps, linepos, linelen, slen;
int duplicate;
- if (mod->dep_loop) {
- DBG("Ignored %s due dependency loops\n", p);
- continue;
- }
-
deps = mod_get_all_sorted_dependencies(mod, &n_deps);
if (deps == NULL && n_deps > 0) {
ERR("could not get all sorted dependencies of %s\n", p);
linelen = strlen(p) + 1;
for (j = 0; j < n_deps; j++) {
const struct mod *d = deps[j];
- if (d->dep_loop) {
- DBG("Ignored %s (dependency of %s) "
- "due dependency loops\n",
- mod_get_compressed_path(d), p);
- continue;
- }
linelen += 1 + strlen(mod_get_compressed_path(d));
}
for (j = 0; j < n_deps; j++) {
const struct mod *d = deps[j];
const char *dp;
- if (d->dep_loop)
- continue;
+
line[linepos] = ' ';
linepos++;
static int output_aliases_bin(struct depmod *depmod, FILE *out)
{
- char buf[1024];
struct index_node *idx;
size_t i;
kmod_list_foreach(l, mod->info_list) {
const char *key = kmod_module_info_get_key(l);
const char *value = kmod_module_info_get_value(l);
+ char buf[PATH_MAX];
const char *alias;
int duplicate;
if (!streq(key, "alias"))
continue;
- alias = underscores2(value, buf, sizeof(buf));
- if (alias == NULL)
+ if (alias_normalize(value, buf, NULL) < 0) {
+ WRN("Unmatched bracket in %s\n", value);
continue;
+ }
+ alias = buf;
duplicate = index_insert(idx, alias, mod->modname,
mod->idx);
size_t i;
fputs("# Soft dependencies extracted from modules themselves.\n", out);
- fputs("# Copy, with a .conf extension, to /etc/modprobe.d to use "
- "it with modprobe.\n", out);
for (i = 0; i < depmod->modules.count; i++) {
const struct mod *mod = depmod->modules.array[i];
{
struct index_node *idx;
char alias[1024];
+ _cleanup_(scratchbuf_release) struct scratchbuf salias =
+ SCRATCHBUF_INITIALIZER(alias);
size_t baselen = sizeof("symbol:") - 1;
struct hash_iter iter;
const void *v;
+ int ret = 0;
if (out == stdout)
return 0;
return -ENOMEM;
memcpy(alias, "symbol:", baselen);
+
hash_iter_init(depmod->symbols, &iter);
while (hash_iter_next(&iter, NULL, &v)) {
int duplicate;
const struct symbol *sym = v;
+ size_t len;
if (sym->owner == NULL)
continue;
- strcpy(alias + baselen, sym->name);
+ len = strlen(sym->name);
+
+ if (scratchbuf_alloc(&salias, baselen + len + 1) < 0) {
+ ret = -ENOMEM;
+ goto err_scratchbuf;
+ }
+ memcpy(scratchbuf_str(&salias) + baselen, sym->name, len + 1);
duplicate = index_insert(idx, alias, sym->owner->modname,
sym->owner->idx);
}
index_write(idx, out);
+
+err_scratchbuf:
index_destroy(idx);
- return 0;
+ if (ret < 0)
+ ERR("output symbols: %s\n", strerror(-ret));
+
+ return ret;
}
static int output_builtin_bin(struct depmod *depmod, FILE *out)
static int output_devname(struct depmod *depmod, FILE *out)
{
size_t i;
-
- fputs("# Device nodes to trigger on-demand module loading.\n", out);
+ bool empty = true;
for (i = 0; i < depmod->modules.count; i++) {
const struct mod *mod = depmod->modules.array[i];
minor = min;
}
- if (type != '\0' && devname != NULL) {
+ if (type != '\0' && devname != NULL)
+ break;
+ }
+
+ if (devname != NULL) {
+ if (type != '\0') {
+ if (empty) {
+ fputs("# Device nodes to trigger on-demand module loading.\n",
+ out);
+ empty = false;
+ }
fprintf(out, "%s %s %c%u:%u\n", mod->modname,
devname, type, major, minor);
- break;
- }
+ } else
+ ERR("Module '%s' has devname (%s) but "
+ "lacks major and minor information. "
+ "Ignoring.\n", mod->modname, devname);
}
}
static void depmod_add_fake_syms(struct depmod *depmod)
{
/* __this_module is magic inserted by kernel loader. */
- depmod_symbol_add(depmod, "__this_module", 0, NULL);
+ depmod_symbol_add(depmod, "__this_module", true, 0, NULL);
/* On S390, this is faked up too */
- depmod_symbol_add(depmod, "_GLOBAL_OFFSET_TABLE_", 0, NULL);
+ depmod_symbol_add(depmod, "_GLOBAL_OFFSET_TABLE_", true, 0, NULL);
+ /* On PowerPC64 ABIv2, .TOC. is more or less _GLOBAL_OFFSET_TABLE_ */
+ depmod_symbol_add(depmod, "TOC.", true, 0, NULL);
}
static int depmod_load_symvers(struct depmod *depmod, const char *filename)
continue;
}
- depmod_symbol_add(depmod, sym, crc, NULL);
+ depmod_symbol_add(depmod, sym, false, crc, NULL);
}
depmod_add_fake_syms(depmod);
goto invalid_syntax;
p++;
+ /* skip prefix */
+ if (p[0] == depmod->cfg->sym_prefix)
+ p++;
+
/* Covers gpl-only and normal symbols. */
if (strncmp(p, ksymstr, ksymstr_len) != 0)
continue;
if (end != NULL)
*end = '\0';
- depmod_symbol_add(depmod, p + ksymstr_len, 0, NULL);
+ depmod_symbol_add(depmod, p + ksymstr_len, true, 0, NULL);
continue;
invalid_syntax:
namelen = strlen(name);
if (baselen + namelen + 2 >= PATH_MAX) {
path[baselen] = '\0';
- ERR("path is too long %s%s %zd\n", path, name);
+ ERR("path is too long %s%s\n", path, name);
continue;
}
path);
closedir(subdir);
} else if (S_ISREG(st.st_mode)) {
- const struct kmod_ext *eitr;
- uint8_t matches = 0;
- for (eitr = kmod_exts; eitr->ext != NULL; eitr++) {
- if (namelen <= eitr->len)
- continue;
- if (streq(name + namelen - eitr->len, eitr->ext)) {
- matches = 1;
- break;
- }
- }
- if (!matches)
+ if (!path_ends_with_kmod_ext(name, namelen))
continue;
+
memcpy(path + baselen, name, namelen + 1);
err = st.st_mtime <= mtime;
if (err == 0) {
{
FILE *out = NULL;
int err = 0, all = 0, maybe_all = 0, n_config_paths = 0;
- char *root = NULL;
- const char **config_paths = NULL;
+ _cleanup_free_ char *root = NULL;
+ _cleanup_free_ const char **config_paths = NULL;
const char *system_map = NULL;
const char *module_symvers = NULL;
const char *null_kmod_config = NULL;
maybe_all = 1;
break;
case 'b':
+ if (root)
+ free(root);
root = path_make_absolute_cwd(optarg);
break;
case 'C': {
break;
case 'h':
- help(basename(argv[0]));
- free(config_paths);
+ help();
return EXIT_SUCCESS;
case 'V':
puts(PACKAGE " version " VERSION);
- free(config_paths);
+ puts(KMOD_FEATURES);
return EXIT_SUCCESS;
case '?':
goto cmdline_failed;
}
}
- if (optind < argc && is_version_number(argv[optind])) {
+ if (optind < argc) {
+ if (!is_version_number(argv[optind])) {
+ ERR("Bad version passed %s\n", argv[optind]);
+ goto cmdline_failed;
+ }
cfg.kversion = argv[optind];
optind++;
} else {
if (out == stdout)
goto done;
/* ignore up-to-date errors (< 0) */
- if (depfile_up_to_date(cfg.dirname) == 1) {
- DBG("%s/modules.dep is up to date!\n", cfg.dirname);
+ if (depfile_up_to_date(cfg.dirname) == 1)
goto done;
- }
- DBG("%s/modules.dep is outdated, do -a\n", cfg.dirname);
all = 1;
}
CRIT("kmod_new(\"%s\", {NULL}) failed: %m\n", cfg.dirname);
goto cmdline_failed;
}
- kmod_set_log_priority(ctx, verbose);
+
+ log_setup_kmod_log(ctx, verbose);
err = depmod_init(&depmod, &cfg, ctx);
if (err < 0) {
done:
depmod_shutdown(&depmod);
cfg_free(&cfg);
- free(config_paths);
return err >= 0 ? EXIT_SUCCESS : EXIT_FAILURE;
cmdline_modules_failed:
kmod_unref(ctx);
cmdline_failed:
cfg_free(&cfg);
- free(config_paths);
- free(root);
return EXIT_FAILURE;
}
-#include "kmod.h"
-
const struct kmod_cmd kmod_cmd_compat_depmod = {
.name = "depmod",
.cmd = do_depmod,