199767f8919635c4928607450d9e0abb932109ceToomas Soome * Copyright (c) 1998 Michael Smith <msmith@freebsd.org>
199767f8919635c4928607450d9e0abb932109ceToomas Soome * All rights reserved.
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Redistribution and use in source and binary forms, with or without
199767f8919635c4928607450d9e0abb932109ceToomas Soome * modification, are permitted provided that the following conditions
199767f8919635c4928607450d9e0abb932109ceToomas Soome * 1. Redistributions of source code must retain the above copyright
199767f8919635c4928607450d9e0abb932109ceToomas Soome * notice, this list of conditions and the following disclaimer.
199767f8919635c4928607450d9e0abb932109ceToomas Soome * 2. Redistributions in binary form must reproduce the above copyright
199767f8919635c4928607450d9e0abb932109ceToomas Soome * notice, this list of conditions and the following disclaimer in the
199767f8919635c4928607450d9e0abb932109ceToomas Soome * documentation and/or other materials provided with the distribution.
199767f8919635c4928607450d9e0abb932109ceToomas Soome * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
199767f8919635c4928607450d9e0abb932109ceToomas Soome * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
199767f8919635c4928607450d9e0abb932109ceToomas Soome * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
199767f8919635c4928607450d9e0abb932109ceToomas Soome * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
199767f8919635c4928607450d9e0abb932109ceToomas Soome * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
199767f8919635c4928607450d9e0abb932109ceToomas Soome * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
199767f8919635c4928607450d9e0abb932109ceToomas Soome * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
199767f8919635c4928607450d9e0abb932109ceToomas Soome * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
199767f8919635c4928607450d9e0abb932109ceToomas Soome * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
199767f8919635c4928607450d9e0abb932109ceToomas Soome * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
199767f8919635c4928607450d9e0abb932109ceToomas Soome * SUCH DAMAGE.
199767f8919635c4928607450d9e0abb932109ceToomas Soome * file/module function dispatcher, support, etc.
199767f8919635c4928607450d9e0abb932109ceToomas Soome u_char *d_hints; /* content of linker.hints file */
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic int file_load(char *filename, vm_offset_t dest, struct preloaded_file **result);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic int file_load_dependencies(struct preloaded_file *base_mod);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic char * file_search(const char *name, const char **extlist);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic struct kernel_module * file_findmodule(struct preloaded_file *fp, char *modname, struct mod_depend *verinfo);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic char *mod_searchmodule(char *name, struct mod_depend *verinfo);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void file_insert_tail(struct preloaded_file *mp);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestruct file_metadata* metadata_next(struct file_metadata *base_mp, int type);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void moduledir_readhints(struct moduledir *mdp);
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic void moduledir_rebuild(void);
199767f8919635c4928607450d9e0abb932109ceToomas Soome/* load address should be tweaked by first module loaded (kernel) */
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic const char *default_searchpath ="/platform/i86pc";
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic STAILQ_HEAD(, moduledir) moduledir_list = STAILQ_HEAD_INITIALIZER(moduledir_list);
199767f8919635c4928607450d9e0abb932109ceToomas Soome * load an object, either a disk file or code module.
199767f8919635c4928607450d9e0abb932109ceToomas Soome * To load a file, the syntax is:
199767f8919635c4928607450d9e0abb932109ceToomas Soome * load -t <type> <path>
199767f8919635c4928607450d9e0abb932109ceToomas Soome * code modules are loaded as:
199767f8919635c4928607450d9e0abb932109ceToomas Soome * load <path> <options>
199767f8919635c4928607450d9e0abb932109ceToomas SoomeCOMMAND_SET(load, "load", "load a kernel or module", command_load);
199767f8919635c4928607450d9e0abb932109ceToomas Soome while ((ch = getopt(argc, argv, "kt:")) != -1) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* getopt has already reported an error */
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Request to load a raw file?
199767f8919635c4928607450d9e0abb932109ceToomas Soome snprintf(command_errbuf, sizeof (command_errbuf),
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (file_loadraw(argv[1], typestr, argc - 2, argv + 2, 1) != NULL)
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* Failing to load mfs_root is never going to end well! */
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Do we have explicit KLD load ?
199767f8919635c4928607450d9e0abb932109ceToomas Soome error = mod_loadkld(argv[1], argc - 2, argv + 2);
199767f8919635c4928607450d9e0abb932109ceToomas Soome snprintf(command_errbuf, sizeof (command_errbuf),
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Looks like a request for a module.
199767f8919635c4928607450d9e0abb932109ceToomas Soome error = mod_load(argv[1], NULL, argc - 2, argv + 2);
199767f8919635c4928607450d9e0abb932109ceToomas Soome snprintf(command_errbuf, sizeof (command_errbuf),
199767f8919635c4928607450d9e0abb932109ceToomas Soome "warning: module '%s' already loaded", argv[1]);
199767f8919635c4928607450d9e0abb932109ceToomas SoomeCOMMAND_SET(load_geli, "load_geli", "load a geli key", command_load_geli);
199767f8919635c4928607450d9e0abb932109ceToomas Soome command_errmsg = "usage is [-n key#] <prov> <file>";
199767f8919635c4928607450d9e0abb932109ceToomas Soome while ((ch = getopt(argc, argv, "n:")) != -1) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome snprintf(command_errbuf, sizeof (command_errbuf),
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* getopt has already reported an error */
199767f8919635c4928607450d9e0abb932109ceToomas Soome sprintf(typestr, "%s:geli_keyfile%d", argv[1], num);
199767f8919635c4928607450d9e0abb932109ceToomas Soome return (file_loadraw(argv[2], typestr, 0, NULL, 1) ? CMD_OK : CMD_ERROR);
199767f8919635c4928607450d9e0abb932109ceToomas Soome#endif /* __FreeBSD__ */
199767f8919635c4928607450d9e0abb932109ceToomas SoomeCOMMAND_SET(unload, "unload", "unload all modules", command_unload);
199767f8919635c4928607450d9e0abb932109ceToomas SoomeCOMMAND_SET(lsmod, "lsmod", "list loaded modules", command_lsmod);
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* getopt has already reported an error */
199767f8919635c4928607450d9e0abb932109ceToomas Soome for (fp = preloaded_files; fp; fp = fp->f_next) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome sprintf(lbuf, " (%s, 0x%lx)\n", fp->f_type, (long)fp->f_size);
199767f8919635c4928607450d9e0abb932109ceToomas Soome for (mp = fp->f_modules; mp; mp = mp->m_next) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome sprintf(lbuf, "%s.%d ", mp->m_name, mp->m_version);
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* XXX could add some formatting smarts here to display some better */
199767f8919635c4928607450d9e0abb932109ceToomas Soome for (md = fp->f_metadata; md != NULL; md = md->md_next) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome sprintf(lbuf, " 0x%04x, 0x%lx\n", md->md_type, (long) md->md_size);
199767f8919635c4928607450d9e0abb932109ceToomas Soome * File level interface, functions file_*
199767f8919635c4928607450d9e0abb932109ceToomas Soomefile_load(char *filename, vm_offset_t dest, struct preloaded_file **result)
199767f8919635c4928607450d9e0abb932109ceToomas Soome dest = archsw.arch_loadaddr(LOAD_RAW, filename, dest);
199767f8919635c4928607450d9e0abb932109ceToomas Soome error = (file_formats[i]->l_load)(filename, dest, &fp);
199767f8919635c4928607450d9e0abb932109ceToomas Soome fp->f_loader = last_file_format = i; /* remember the loader */
199767f8919635c4928607450d9e0abb932109ceToomas Soome } else if (last_file_format == i && i != 0) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* Restart from the beginning */
199767f8919635c4928607450d9e0abb932109ceToomas Soome continue; /* Unknown to this handler? */
199767f8919635c4928607450d9e0abb932109ceToomas Soome snprintf(command_errbuf, sizeof (command_errbuf),
199767f8919635c4928607450d9e0abb932109ceToomas Soome "can't load file '%s': %s", filename, strerror(error));
199767f8919635c4928607450d9e0abb932109ceToomas Soomefile_load_dependencies(struct preloaded_file *base_file)
199767f8919635c4928607450d9e0abb932109ceToomas Soome md = file_findmetadata(base_file, MODINFOMD_DEPLIST);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (file_findmodule(NULL, dmodname, verinfo) == NULL) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome printf("loading required module '%s'\n", dmodname);
199767f8919635c4928607450d9e0abb932109ceToomas Soome * If module loaded via kld name which isn't listed
199767f8919635c4928607450d9e0abb932109ceToomas Soome * in the linker.hints file, we should check if it have
199767f8919635c4928607450d9e0abb932109ceToomas Soome * required version.
199767f8919635c4928607450d9e0abb932109ceToomas Soome snprintf(command_errbuf, sizeof (command_errbuf),
199767f8919635c4928607450d9e0abb932109ceToomas Soome "module '%s' exists but with wrong version", dmodname);
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* Load failed; discard everything */
199767f8919635c4928607450d9e0abb932109ceToomas Soome * We've been asked to load (fname) as (type), so just suck it in,
199767f8919635c4928607450d9e0abb932109ceToomas Soome * no arguments or anything.
199767f8919635c4928607450d9e0abb932109ceToomas Soomefile_loadraw(const char *fname, char *type, int argc, char **argv, int insert)
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* We can't load first */
199767f8919635c4928607450d9e0abb932109ceToomas Soome command_errmsg = "can't load file before kernel";
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* locate the file on the load path */
199767f8919635c4928607450d9e0abb932109ceToomas Soome snprintf(command_errbuf, sizeof (command_errbuf),
199767f8919635c4928607450d9e0abb932109ceToomas Soome snprintf(command_errbuf, sizeof (command_errbuf),
199767f8919635c4928607450d9e0abb932109ceToomas Soome loadaddr = archsw.arch_loadaddr(LOAD_RAW, name, loadaddr);
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* read in 4k chunks; size is not really important */
199767f8919635c4928607450d9e0abb932109ceToomas Soome snprintf(command_errbuf, sizeof (command_errbuf),
199767f8919635c4928607450d9e0abb932109ceToomas Soome "error reading '%s': %s", name, strerror(errno));
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* Looks OK so far; create & populate control structure */
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* recognise space consumption */
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* Add to the list of loaded files */
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Load the module (name), pass it (argc),(argv), add container file
199767f8919635c4928607450d9e0abb932109ceToomas Soome * to the list of loaded files.
199767f8919635c4928607450d9e0abb932109ceToomas Soome * If module is already loaded just assign new argc/argv.
199767f8919635c4928607450d9e0abb932109ceToomas Soomemod_load(char *modname, struct mod_depend *verinfo, int argc, char *argv[])
199767f8919635c4928607450d9e0abb932109ceToomas Soome printf("Warning: mod_load() called instead of mod_loadkld() for module '%s'\n", modname);
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* see if module is already loaded */
199767f8919635c4928607450d9e0abb932109ceToomas Soome snprintf(command_errbuf, sizeof (command_errbuf),
199767f8919635c4928607450d9e0abb932109ceToomas Soome "warning: module '%s' already loaded", mp->m_name);
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* locate file with the module on the search path */
199767f8919635c4928607450d9e0abb932109ceToomas Soome snprintf(command_errbuf, sizeof (command_errbuf),
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Load specified KLD. If path is omitted, then try to locate it via
199767f8919635c4928607450d9e0abb932109ceToomas Soome * search path.
199767f8919635c4928607450d9e0abb932109ceToomas Soomemod_loadkld(const char *kldname, int argc, char *argv[])
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Get fully qualified KLD name
199767f8919635c4928607450d9e0abb932109ceToomas Soome snprintf(command_errbuf, sizeof (command_errbuf),
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Check if KLD already loaded
199767f8919635c4928607450d9e0abb932109ceToomas Soome snprintf(command_errbuf, sizeof (command_errbuf),
199767f8919635c4928607450d9e0abb932109ceToomas Soome last_file != NULL && last_file->f_next != NULL;
199767f8919635c4928607450d9e0abb932109ceToomas Soome file_insert_tail(fp); /* Add to the list of loaded files */
199767f8919635c4928607450d9e0abb932109ceToomas Soome loadaddr = last_file->f_addr + last_file->f_size;
199767f8919635c4928607450d9e0abb932109ceToomas Soome snprintf(command_errbuf, sizeof (command_errbuf),
199767f8919635c4928607450d9e0abb932109ceToomas Soome "don't know how to load module '%s'", filename);
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Find a file matching (name) and (type).
199767f8919635c4928607450d9e0abb932109ceToomas Soome * NULL may be passed as a wildcard to either.
199767f8919635c4928607450d9e0abb932109ceToomas Soomefile_findfile(const char *name, const char *type)
199767f8919635c4928607450d9e0abb932109ceToomas Soome for (fp = preloaded_files; fp != NULL; fp = fp->f_next) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (((name == NULL) || !strcmp(name, fp->f_name)) &&
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Find a module matching (name) inside of given file.
199767f8919635c4928607450d9e0abb932109ceToomas Soome * NULL may be passed as a wildcard.
199767f8919635c4928607450d9e0abb932109ceToomas Soomefile_findmodule(struct preloaded_file *fp, char *modname,
199767f8919635c4928607450d9e0abb932109ceToomas Soome for (fp = preloaded_files; fp; fp = fp->f_next) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome for (mp = fp->f_modules; mp; mp = mp->m_next) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Make a copy of (size) bytes of data from (p), and associate them as
199767f8919635c4928607450d9e0abb932109ceToomas Soome * metadata of (type) to the module (mp).
199767f8919635c4928607450d9e0abb932109ceToomas Soomefile_addmetadata(struct preloaded_file *fp, int type, size_t size, void *p)
199767f8919635c4928607450d9e0abb932109ceToomas Soome md = malloc(sizeof(struct file_metadata) - sizeof(md->md_data) + size);
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Find a metadata object of (type) associated with the file (fp)
199767f8919635c4928607450d9e0abb932109ceToomas Soomefile_findmetadata(struct preloaded_file *fp, int type)
199767f8919635c4928607450d9e0abb932109ceToomas Soome for (md = fp->f_metadata; md != NULL; md = md->md_next)
199767f8919635c4928607450d9e0abb932109ceToomas Soomemetadata_next(struct file_metadata *md, int type)
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic const char *emptyextlist[] = { "", NULL };
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Check if the given file is in place and return full path to it.
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic char *
199767f8919635c4928607450d9e0abb932109ceToomas Soomefile_lookup(const char *path, const char *name, int namelen, const char **extlist)
199767f8919635c4928607450d9e0abb932109ceToomas Soome result = malloc(pathlen + namelen + extlen + 2);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (stat(result, &st) == 0 && S_ISREG(st.st_mode))
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Check if file name have any qualifiers
199767f8919635c4928607450d9e0abb932109ceToomas Soome return (cp != name || strchr(name, '/') != NULL);
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Attempt to find the file (name) on the module searchpath.
199767f8919635c4928607450d9e0abb932109ceToomas Soome * If (name) is qualified in any way, we simply check it and
199767f8919635c4928607450d9e0abb932109ceToomas Soome * return it or NULL. If it is not qualified, then we attempt
199767f8919635c4928607450d9e0abb932109ceToomas Soome * to construct a path using entries in the environment variable
199767f8919635c4928607450d9e0abb932109ceToomas Soome * module_path.
199767f8919635c4928607450d9e0abb932109ceToomas Soome * The path we return a pointer to need never be freed, as we manage
199767f8919635c4928607450d9e0abb932109ceToomas Soome * it internally.
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic char *
199767f8919635c4928607450d9e0abb932109ceToomas Soomefile_search(const char *name, const char **extlist)
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* Don't look for nothing */
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* Qualified, so just see if it exists */
199767f8919635c4928607450d9e0abb932109ceToomas Soome result = file_lookup(mdp->d_path, name, namelen, extlist);
199767f8919635c4928607450d9e0abb932109ceToomas Soome (base) + (((ptr) - (base) + sizeof(int) - 1) & ~(sizeof(int) - 1))
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic char *
199767f8919635c4928607450d9e0abb932109ceToomas Soomemod_search_hints(struct moduledir *mdp, const char *modname,
199767f8919635c4928607450d9e0abb932109ceToomas Soome int *intp, bestver, blen, clen, found, ival, modnamelen, reclen;
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (clen != modnamelen || bcmp(cp, modname, clen) != 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome cp += sizeof(int);
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (verinfo == NULL || ival == verinfo->md_ver_preferred) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Finally check if KLD is in the place
199767f8919635c4928607450d9e0abb932109ceToomas Soome result = file_lookup(mdp->d_path, (char *)cp, clen, NULL);
199767f8919635c4928607450d9e0abb932109ceToomas Soome result = file_lookup(mdp->d_path, (char *)best, blen, NULL);
199767f8919635c4928607450d9e0abb932109ceToomas Soome * If nothing found or hints is absent - fallback to the old way
199767f8919635c4928607450d9e0abb932109ceToomas Soome * by using "kldname[.ko]" as module name.
199767f8919635c4928607450d9e0abb932109ceToomas Soome result = file_lookup(mdp->d_path, modname, modnamelen, kld_ext_list);
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Attempt to locate the file containing the module (name)
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic char *
199767f8919635c4928607450d9e0abb932109ceToomas Soomemod_searchmodule(char *name, struct mod_depend *verinfo)
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Now we ready to lookup module in the given directories
199767f8919635c4928607450d9e0abb932109ceToomas Soomefile_addmodule(struct preloaded_file *fp, char *modname, int version,
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Throw a file away
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Allocate a new file; must be used instead of malloc()
199767f8919635c4928607450d9e0abb932109ceToomas Soome * to ensure safe initialisation.
199767f8919635c4928607450d9e0abb932109ceToomas Soome if ((fp = malloc(sizeof(struct preloaded_file))) != NULL) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Add a module to the chain
199767f8919635c4928607450d9e0abb932109ceToomas Soome /* Append to list of loaded file */
199767f8919635c4928607450d9e0abb932109ceToomas Soome for (cm = preloaded_files; cm->f_next != NULL; cm = cm->f_next)
199767f8919635c4928607450d9e0abb932109ceToomas Soomestatic char *
199767f8919635c4928607450d9e0abb932109ceToomas Soomemoduledir_fullpath(struct moduledir *mdp, const char *fname)
199767f8919635c4928607450d9e0abb932109ceToomas Soome cp = malloc(strlen(mdp->d_path) + strlen(fname) + 2);
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Read linker.hints file into memory performing some sanity checks.
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (mdp->d_hints != NULL || (mdp->d_flags & MDIR_NOHINTS))
199767f8919635c4928607450d9e0abb932109ceToomas Soome path = moduledir_fullpath(mdp, "linker.hints");
199767f8919635c4928607450d9e0abb932109ceToomas Soome st.st_size < (ssize_t)(sizeof(version) + sizeof(int)) ||
199767f8919635c4928607450d9e0abb932109ceToomas Soome st.st_size > LINKER_HINTS_MAX || (fd = open(path, O_RDONLY)) < 0) {
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (size != sizeof(version) || version != LINKER_HINTS_VERSION)
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Extract directories from the ';' separated list, remove duplicates.
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Rebuild list of module directories if it changed
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Ignore trailing slashes
199767f8919635c4928607450d9e0abb932109ceToomas Soome for (cplen = ep - cp; cplen > 1 && cp[cplen - 1] == '/'; cplen--)
199767f8919635c4928607450d9e0abb932109ceToomas Soome if (strlen(mdp->d_path) != cplen || bcmp(cp, mdp->d_path, cplen) != 0)
199767f8919635c4928607450d9e0abb932109ceToomas Soome STAILQ_INSERT_TAIL(&moduledir_list, mdp, d_link);
199767f8919635c4928607450d9e0abb932109ceToomas Soome * Delete unused directories if any