/* * Specification: /etc/conf.modules / format * Modules may be located at different place in the filesystem. * We expect some standard to emerge. We expect that the * FSSTND will address this in the future. * * There will always be some need to override this, especially for * modules developpers. * * The file /etc/conf.modules will contain different definition to * control the manipulation of the module. * * The format will be fairly simple: * * parameter=value * . * parameter=value * * Standard Unix style comments and continuation line are supported. * Comments begin with a # and continue until the end of the line. * A line continue on the next one if the last non-white character * is a \. */ /* #Specification: /etc/conf.modules / format / official name */ #include #include #include #include #include #include #include #include #include #include #include "link.h" struct PATH_TYPE{ char *type; char *path; }; static PATH_TYPE tb[100]; static int nb=0; struct EXEC_TYPE { int when; char *module; char *cmd; }; static struct EXEC_TYPE ex[100]; static int nex = 0; static char *depfile=NULL; char *insmod_opt = NULL; static char *tbpath[]={ "/lib/modules/VERSION", /* just a placeholder, look at config_read */ "/lib/modules/default", "/lib/modules", NULL /* marks the end of the list! */ }; static char *tbtype[]={ "fs", "misc", "net", "scsi", "block", "cdrom", "ipv4", NULL /* marks the end of the list! */ }; static char *aliaslist[100]={ "binfmt-204 binfmt_aout", "binfmt-263 binfmt_aout", "binfmt-264 binfmt_aout", "binfmt-267 binfmt_aout", "binfmt-332 iBCS", "binfmt--310 binfmt_java", "block-major-1 rd", "block-major-2 floppy", "block-major-3 off", /* not modularized! */ "block-major-7 loop", "block-major-8 sd_mod", "block-major-11 sr_mod", "block-major-13 xd", "block-major-15 cdu31a", "block-major-16 gscd", "block-major-17 optcd", "block-major-18 sjcd", "block-major-20 mcdx", "block-major-23 mcd", "block-major-24 sonycd535", "block-major-25 sbpcd", "block-major-26 sbpcd", "block-major-27 sbpcd", "block-major-29 aztcd", "block-major-32 cm206", "char-major-4 serial", "char-major-5 serial", "char-major-6 lp", "char-major-9 st", "char-major-10 misc", /* was: mouse */ "char-major-10-0 busmouse", /* /dev/logibm Logitech bus mouse */ "char-major-10-1 psaux", /* /dev/psaux PS/2-style mouse port */ "char-major-10-2 msbusmouse", /* /dev/inportbm Microsoft Inport bus mouse */ "char-major-10-3 atixlmouse", /* /dev/atibm ATI XL bus mouse */ /* /dev/jbm J-mouse */ /* /dev/amigamouse Amiga mouse (68k/Amiga) */ /* /dev/atarimouse Atari mouse */ /* /dev/sunmouse Sun mouse */ /* /dev/beep Fancy beep device */ /* /dev/modreq Kernel module load request */ "char-major-10-130 wdt", /* /dev/watchdog Watchdog timer port */ "char-major-10-131 wdt", /* /dev/temperature Machine internal temperature */ /* /dev/hwtrap Hardware fault trap */ /* /dev/exttrp External device trap */ "char-major-14 sound", "char-major-19 cyclades", "char-major-20 cyclades", "char-major-21 sg", "char-major-27 ftape", "char-major-34 scc", "char-major-35 tclmidi", "char-major-36 netlink", "char-major-48 riscom8", "char-major-49 riscom8", "char-major-63 kdebug", "dos msdos", "dummy0 dummy", "dummy1 dummy", "eth0 off", "iso9660 isofs", "md-personality-1 linear", "md-personality-2 raid0", "net-pf-3 off", /* PF_UNIX 1 Unix domain sockets */ /* PF_INET 2 Internet IP Protocol */ /* PF_AX25 3 Amateur Radio AX.25 */ "net-pf-4 ipx", /* PF_IPX 4 Novell IPX */ "net-pf-5 appletalk", /* PF_APPLETALK 5 Appletalk DDP */ /* PF_NETROM 6 Amateur radio NetROM */ /* PF_BRIDGE 7 Multiprotocol bridge */ /* PF_AAL5 8 Reserved for Werner's ATM */ /* PF_X25 9 Reserved for X.25 project */ /* PF_INET6 10 IP version 6 */ "netalias-2 ip_alias", "plip0 plip", "plip1 plip", "ppp0 ppp", "ppp1 ppp", "scsi_hostadapter off", /* if not in config file */ "slip0 slip", "slip1 slip", "tty-ldisc-1 slip", "tty-ldisc-3 ppp", NULL /* marks the end of the list! */ }; char *optlist[100]={ "dummy0 -o dummy0", "dummy1 -o dummy1", NULL /* marks the end of the list! */ }; /* * Do a popen with error message reporting. * If quit != 0, the function does not return if an error occur. * * Return the FILE *. Must be close with pclose(). */ FILE *popen_err ( const char *cmd, const char *mode, int quit) { FILE *ret = popen (cmd,mode); if (ret == NULL){ depmod_error("Can't execute: %s",cmd); if (quit) exit(-1); } return ret; } static char *next_word(char *pt) { char *pt2; for (pt2 = pt; *pt2 && !(isspace(*pt2)); ++pt2) ; if (*pt2) { *pt2++ = '\0'; while (*pt2 && isspace(*pt2)) ++pt2; } return pt2; } static char *no_uname_r (char *pt, char *force) { char *p; char *s; if ((p = strchr(pt, '`')) != NULL) { for (s = p+1; isspace(*s); ++s) ; if (strncmp(s, "uname", 5) == 0) { while (*s && (*s != '`')) ++s; if (*s == '`') { /* Oh dear, belt and suspenders... */ char no_uname_buf[100]; *p = '\0'; sprintf(no_uname_buf, "%s%s%s", pt, force, s+1); return strdup_err (no_uname_buf); } } } return strdup_err (pt); } /* * Read the configuration file. * Return -1 if any error. Error messages a generated. */ int config_read (char *force_ver) { struct utsname uts_info; int assgn; int drop_default_paths = 1; int noline = 0; int ret = 0; char buf[3000]; char depfile_tmp[PATH_MAX]; char firstline[100]; if (force_ver) sprintf(depfile_tmp, "/lib/modules/%s/modules.dep", force_ver); else depfile_tmp[0] = '\0'; /* * Specification: /etc/conf.modules / missing * This file is optional. No error is printed if it * is missing. If it is missing the following content * is assumed. * * path[boot]=/lib/modules/boot * path[fs]=/lib/modules/`uname -r`/fs * path[misc]=/lib/modules/`uname -r`/misc * path[net]=/lib/modules/`uname -r`/net * path[scsi]=/lib/modules/`uname -r`/scsi * * path[fs]=/lib/modules/default/fs * path[misc]=/lib/modules/default/misc * path[net]=/lib/modules/default/net * path[scsi]=/lib/modules/default/scsi * * path[fs]=/lib/modules/fs * path[misc]=/lib/modules/misc * path[net]=/lib/modules/net * path[scsi]=/lib/modules/scsi * * The idea is that modprobe will look first it the * modules compiled for the current release of the kernel. * If not found, it will look into the default release. * And if not found, it will look in the other directory. * * The strategy should be like this. When you install a * new linux, the modules should go in a directory * related to the release of the kernel you are installing. * Then you do a symlink default to this directory. * * Each time you compile a new kernel, the make modules_install * will set new directory, but won't change de default. * * When you get a module unrelated to the kernel distribution * you place it in one of the last three directory. * * This is the default strategy. Off course you can overide * this in /etc/conf.modules. */ /* * Build the default "path[type]" configuration */ uname(&uts_info); sprintf (firstline,"/lib/modules/%s", force_ver?force_ver:uts_info.release); tbpath[0] = firstline; tb[0].type = strdup_err("boot"); tb[0].path = strdup_err("/lib/modules/boot"); nb = 1; for (char **pathp = tbpath; *pathp; ++pathp) { for (char **type = tbtype; *type; ++type) { char path[100]; sprintf (path,"%s/%s", *pathp, *type); tb[nb].type = strdup_err(*type); tb[nb].path = strdup_err(path); nb++; } } FILE *fin = fopen (ETC_CONF_MODULES,"r"); if (fin == NULL) fin = fopen("/etc/modules.conf", "r"); /* So what... */ while (fin && fgets_strip(buf, sizeof(buf)-1, fin, &noline) != NULL) { char *parm = str_skip (buf); if (*parm == '\0') continue; int one_err = 1; char *pt = parm; char *pt2; while (*pt > ' ' && *pt != '=') pt++; if (*pt == '=') assgn = 1; else assgn = 0; *pt++ = '\0'; pt = str_skip (pt); /* * options */ if (!assgn && strcmp(parm,"options")==0){ // Replace previous (default) definitions int i; char modname[100]; sscanf(pt, "%s", modname); int len = strlen(modname); for (i = 0; optlist[i]; ++i) { if (strncmp(optlist[i], modname, len) == 0 && isspace(optlist[i][len])) { optlist[i] = strdup_err(pt); break; } } if (optlist[i] == (char *)0) { optlist[i++] = strdup_err(pt); optlist[i] = (char *)0; } one_err = 0; } /* * alias */ else if (!assgn && strcmp(parm,"alias")==0){ // Replace previous (default) definitions char modname[100]; sscanf(pt, "%s", modname); int len = strlen(modname); int i; for (i = 0; aliaslist[i]; i++) { if (strncmp(aliaslist[i], modname, len) == 0 && isspace(aliaslist[i][len])) { aliaslist[i] = strdup_err(pt); break; } } if (aliaslist[i] == (char *)0) { aliaslist[i++] = strdup_err(pt); aliaslist[i] = (char *)0; } one_err = 0; } /* * keep */ else if (!assgn && (strcmp(parm,"keep") == 0)) { drop_default_paths = 0; one_err = 0; } /* * pre-install */ else if (!assgn && (strcmp(parm,"pre-install")==0)) { ex[nex].when = EXEC_PRE_INSTALL; pt2 = next_word(pt); ex[nex].module = strdup_err(pt); ex[nex].cmd = strdup_err(pt2); ++nex; one_err = 0; } /* * install */ else if (!assgn && (strcmp(parm,"install")==0)) { ex[nex].when = EXEC_INSTALL; pt2 = next_word(pt); ex[nex].module = strdup_err(pt); ex[nex].cmd = strdup_err(pt2); ++nex; one_err = 0; } /* * post-install */ else if (!assgn && (strcmp(parm,"post-install")==0)) { ex[nex].when = EXEC_POST_INSTALL; pt2 = next_word(pt); ex[nex].module = strdup_err(pt); ex[nex].cmd = strdup_err(pt2); ++nex; one_err = 0; } /* * pre-remove */ else if (!assgn && (strcmp(parm,"pre-remove")==0)) { ex[nex].when = EXEC_PRE_REMOVE; pt2 = next_word(pt); ex[nex].module = strdup_err(pt); ex[nex].cmd = strdup_err(pt2); ++nex; one_err = 0; } /* * remove */ else if (!assgn && (strcmp(parm,"remove")==0)) { ex[nex].when = EXEC_REMOVE; pt2 = next_word(pt); ex[nex].module = strdup_err(pt); ex[nex].cmd = strdup_err(pt2); ++nex; one_err = 0; } /* * post-remove */ else if (!assgn && (strcmp(parm,"post-remove")==0)) { ex[nex].when = EXEC_POST_REMOVE; pt2 = next_word(pt); ex[nex].module = strdup_err(pt); ex[nex].cmd = strdup_err(pt2); ++nex; one_err = 0; } /* * insmod_opt= */ else if (assgn && (strcmp(parm,"insmod_opt")==0)) { insmod_opt = strdup_err(pt); one_err = 0; } /* * depfile= */ else if (assgn && (strcmp(parm,"depfile")==0) && (depfile_tmp[0] == '\0')) { strcpy (depfile_tmp,pt); one_err = 0; } /* * path...= */ else if (assgn && strncmp(parm,"path",4)==0){ /* * Specification: config file / path parameter * The path parameter specify a directory to * search for module. This parameter may * be repeated multiple time. * * Optionally the path parameter carries * a tag. This tells us a little more about * the purpose of this directory and * allows some automated operations. * The tag is sticked to the path word * enclose in square braket. * # * path[boot]=/lib/modules/boot * # * * This identifies the path a of directory * holding modules loadable a boot time. */ if (drop_default_paths){ /* * Specification: config file / path / default * Whenever there is a path[] specification * in the config file, all the default * path are reset. * * If one instead wants to _add_ to the default * set of paths, one has to have the option * keep * before the first path[]-specification line * in the configuration file. */ drop_default_paths = 0; for (int n = 1; n < nb; n++){ free (tb[n].path); free (tb[n].type); } nb = 1; } assert ((unsigned)nb < sizeof(tb)/sizeof(tb[0])); /* * If the tag is missing, the word "misc" * is assumed. */ if (parm[4] == '\0'){ tb[nb].type = strdup_err ("misc"); if (force_ver) tb[nb].path = no_uname_r(pt, force_ver); else tb[nb].path = strdup_err (pt); nb++; one_err = 0; } /* * get tag */ else if (parm[4] == '['){ char *pt_type = parm+5; while (*pt_type != '\0' && *pt_type != ']') pt_type++; if (*pt_type == ']' && pt_type[1] == '\0'){ *pt_type = '\0'; tb[nb].type = strdup_err (parm+5); if (force_ver) tb[nb].path = no_uname_r (pt, force_ver); else tb[nb].path = strdup_err (pt); nb++; one_err = 0; } } } /* * any errors so far? */ if (one_err){ depmod_error ("Invalid line %d in " ETC_CONF_MODULES "\n\t%s", noline,buf); ret = -1; } } if (fin) fclose (fin); if (ret != -1){ if (depfile_tmp[0] == '\0'){ /* * Specification: config file / depfile parameter * The default value for the depfile parameter is: * * depfile=/lib/modules/`uname -r`/modules.dep * * If the config file exist but lack a depfile * specification, it is used also since the system * can't work without one. * * Once we have the depfile value, we pass to the shell * with a popen() to resolve whatever shell construct in * its value. We execute a echo command. * * echo read_value */ struct utsname uts_info; uname(&uts_info); sprintf(depfile_tmp,"/lib/modules/%s/modules.dep", uts_info.release); depfile = strdup_err (depfile_tmp); } else { // depfile defined in conf.modules char cmd[PATH_MAX+10]; sprintf (cmd,"echo %s",depfile_tmp); FILE *fin = popen_err (cmd,"r",0); if (fin != NULL){ if (fgets(depfile_tmp,sizeof(depfile_tmp)-1,fin)!=NULL){ strip_end (depfile_tmp); depfile = strdup_err (depfile_tmp); } else ret = -1; pclose (fin); } } } return ret; } /* * Add conditionally a file name if it exist */ static int config_add (const char *acc, char *lst[], int &nb) { int ret = 0; if (access(acc, R_OK) == 0) { lst[nb++] = strdup_err (acc); ret = 1; } return ret; } /* * Find all modules matching the name "match" in directory of type "type" * * Return the number of module located and placed in lst[]. The caller * must free all entry in lst[] itself by doing a tbstr_free(lst,nb); */ int config_lstmod ( const char *match, const char *type, // Type of directory (path[type]), or NULL to match all char *lst[], int many) // Are we looking for the first match or all { /* * Specification: reading directory / echo command * We are using the command echo to locate entries in directories. * This has the advantage of allowing complex wildcard specification * in /etc/conf.modules. For example. * * path[misc]=/lib/modules/1.1.5?/misc */ char cmd[1000]; int find_one = 0; int ret = 0; { char *pt = stpcpy (cmd,"echo "); for (int i=0; i acc){ *pt = '\0'; config_add (acc,lst,ret); pt = acc; } } if (pt > acc){ *pt = '\0'; config_add (acc,lst,ret); } pclose (fin); } dup2(fd2,2); close (fd2); } } return ret; } /* * Return the path of the depandancy file to produce */ const char *config_getdepfile() { return depfile; } /* * Print out all the configuration in use */ void config_show () { PATH_TYPE *pttb = tb; int i; printf ("# Generated by modprobe -c (%s)\n", DEPMOD_RELEASE); for (i=0; itype != NULL){ printf ("path[%s]=%s\n",pttb->type,pttb->path); }else{ printf ("path=%s\n",pttb->path); } } puts ("# Aliases"); for (i=0; aliaslist[i] != (char *)0; i++) printf ("alias %s\n",aliaslist[i]); puts ("# Options"); for (i=0; optlist[i]; i++) printf ("options %s\n",optlist[i]); if (nex > 0) { puts ("# Commands"); for (i=0; i < nex; i++) { switch (ex[i].when) { case EXEC_PRE_INSTALL: printf("pre-install "); break; case EXEC_INSTALL: printf("install "); break; case EXEC_POST_INSTALL: printf("post-install "); break; case EXEC_PRE_REMOVE: printf("pre-remove "); break; case EXEC_REMOVE: printf("remove "); break; case EXEC_POST_REMOVE: printf("post-remove "); break; } printf ("%s %s\n", ex[i].module, ex[i].cmd); } } } /* * Check if a module needs an external command * while being loaded or unloaded. * Return the command, or NULL if there was no command defined */ char *exec_cmd(int when, const char *mod) { const char *modname = stripo(mod); /* * Specification: /etc/conf.modules * The format of the commands in /etc/conf.modules are: * * pre-install module command * install module command * post-install module command * pre-remove module command * remove module command * post-remove module command * * The different words are separated by tabs or spaces. */ for (int i = 0; i < nex; i++) { if (ex[i].when != when) continue; if (strcmp(ex[i].module, modname) == 0) return ex[i].cmd; } return NULL; } /* * Check if a module name is indeed an alias for another module. * Return the real module or this one (mod) if it is not an alias */ const char *any_alias(const char *mod) { const char *modname = stripo(mod); int len = strlen(modname); /* * Specification: /etc/conf.modules / alias / format * The format of the alias command in /etc/conf.modules * is * # * alias alias_name module_name * # * THe different word are separated by tabs or spaces. */ for (int i = 0; aliaslist[i]; i++) { if (strncmp(aliaslist[i], modname, len) == 0 && isspace(aliaslist[i][len])) { return str_skip(&(aliaslist[i][len])); } } return mod; } /* * Locate all module matching "match". * Return the number of modules found. The caller must free the content * of abs_path[]. */ int config_locate ( const char *match, char *abs_path[1000], const char *type) // Restric search to path[type] // or type is NULL (No restriction) { int ret=0; if (strchr(match,'/')!=NULL){ abs_path[0] = strdup_err (match); return 0; } /* else */ char *lst[1000]; match = any_alias (match); if (!match || !(*match) || (strcmp(match, "off") == 0)) return -1; char match_o[PATH_MAX]; sprintf (match_o,"%s.o", match); int nb = config_lstmod (match_o,type,lst, strpbrk(match, SHELL_WILD) != NULL ? 1 : 0); if (nb == 0) { nb = config_lstmod (match,type,lst, strpbrk(match, SHELL_WILD) != NULL ? 1 : 0); } if (nb <= 0) return ret; /* else */ // The list may contain many duplication of some module // for example, the module slip may be in /lib/modules/default/net // and in /lib/modules/x.y.z/net for (int i=0; i