#include #include #include #include #include #include #include #include "link.h" #include "kernelsyms.h" static char verbose_flag = 0; static int from_kerneld = 0; static void verbose (const char *ctl, ...) { if (verbose_flag){ va_list list; va_start (list,ctl); vprintf (ctl,list); va_end (list); fflush (stdout); } } /* Strip the extension of a file name Return a pointer to a static buffer */ const char *stripo(const char *fname) { const char *pt = fname; while ((pt=strchr(fname,'/'))!=NULL) fname = pt+1; if ((pt = strrchr(fname, '.')) && ((strcmp(pt, ".o") == 0) || (strcmp(pt, ".mod") == 0))) { char *leak = (char *)malloc(pt - fname + 1); strncpy(leak, fname, pt - fname); leak[pt - fname] = '\0'; fname = leak; } return fname; } /* Return the options associated with a module. Return NULL if there is none. */ static char *any_options(const char *mod) { const char *modname = stripo(mod); int len = strlen(modname); int i; for (i = 0; optlist[i]; ++i) { if (strncmp(optlist[i], modname, len) == 0 && isspace (optlist[i][len])) { return str_skip(&(optlist[i][len])); } } return NULL; } class NODE{ public: NODE *next; NODE *info; char *str; /*~PROTOBEG~ NODE */ public: NODE (const char *_str, NODE *_next); NODE *lookup (const char *name); ~NODE (void); /*~PROTOEND~ NODE */ }; PUBLIC NODE::NODE(const char *_str, NODE *_next) { next = _next; info = NULL; str = strdup_err (_str); } PUBLIC NODE::~NODE() { delete info; delete next; free (str); } PUBLIC NODE * NODE::lookup (const char *name) { NODE *ret = NULL; if (strcmp(name,str)==0){ ret = this; }else if (next != NULL){ ret = next->lookup(name); } return ret; } class DEPEND{ NODE *in_kernel; NODE *dep_file; /*~PROTOBEG~ DEPEND */ public: DEPEND (void); int insmod (const char *mod, NODE **newin_kernel, char *options[]); int read (const char *cfgfile); int readcur (void); int unload (const char *mod); ~DEPEND (void); /*~PROTOEND~ DEPEND */ }; PUBLIC DEPEND::DEPEND() { in_kernel = NULL; dep_file = NULL; } PUBLIC DEPEND::~DEPEND() { delete in_kernel; delete dep_file; } /* Read the liste of module already loaded in the kernel */ PUBLIC int DEPEND::readcur () { int ret = 0; delete in_kernel; in_kernel = NULL; struct kernel_sym *ksym; int so_far = 0; load_kernel_symbols(); for (ksym = ksymtab; so_far < nksyms ; ++so_far, ksym++) { if (ksym->name[0] == '#') { if (ksym->name[1]) { in_kernel = new NODE(ksym->name + 1,in_kernel); } else break; } } return ret; } /* Read the dependancy file. The format is like a makefile. */ PUBLIC int DEPEND::read (const char *cfgfile) { int ret = -1; int line_no; FILE *fin = fopen (cfgfile,"r"); if (fin == NULL) { depmod_error ("Can't open dependancies file %s (%s)" ,cfgfile,strerror(errno)); return ret; } char buf[3000]; ret = 0; while(fgets_strip(buf,sizeof(buf)-1,fin, &line_no)!=NULL){ char line[3000]; str_strip (buf,line); if (line[0] == '\0') continue; char *pt = line; while (isspace(*pt)) pt++; // Extract the lhs char *modname = pt; while (*pt != ':' && *pt != '\0' && !isspace(*pt)) pt++; if (*pt != ':') { depmod_error ("line %d: Invalid dependancy\n\t%s", line_no, line); ret = -1; break; } *pt++ = '\0'; dep_file = new NODE (modname,dep_file); // Parse the list of modules while (*pt != '\0'){ while (isspace (*pt)) pt++; if (*pt > ' ') { char *depname = pt; while (*pt > ' ') pt++; if (*pt != '\0') *pt++ = '\0'; dep_file->info = new NODE (depname ,dep_file->info); } } } fclose (fin); return ret; } static int call_rmmod(const char *mod) { int ret = 0; char *ex; mod = stripo(mod); #if 1 if ((ex = exec_cmd(EXEC_PRE_REMOVE, mod)) != NULL) { if (system(ex) != 0) depmod_error("pre-remove %s failed\n", mod); } if ((ex = exec_cmd(EXEC_REMOVE, mod)) != NULL) { if ((ret = system(ex)) != 0) depmod_error("remove %s failed\n", mod); } else if ((ret = delete_module(stripo(mod))) < 0 ) perror(stripo(mod)); if (!ret && (ex = exec_cmd(EXEC_POST_REMOVE, mod)) != NULL) { if (system(ex) != 0) depmod_error("post-remove %s failed\n", mod); } #else char cmd[300]; sprintf (cmd,"/sbin/rmmod %s %s",(depmod_syslog ? "-s" : ""),mod); // This special sequence is there so error message generated // by rmmod are indented. verbose ("\r\t%s\n\t\t",cmd); int ret = system(cmd); #endif return ret; } /* Unload all submodule in reverse order they were loaded. Return -1 if any error. */ static int rmmod (NODE *nod) { int ret = 0; if (nod != NULL){ ret = rmmod (nod->next); if (ret == 0){ ret = call_rmmod(nod->str); } } return ret; } static NODE *lookup (NODE *nod, const char *str) { NODE *ret = NULL; if (nod != NULL){ ret = nod->lookup (str); } return ret; } /* Try to load a module and the sub-module needed. Return -1 if any error. If the module can't ne loaded, undo everything. */ PUBLIC int DEPEND::insmod ( const char *mod, NODE **newin_kernel, // modules added by this session char *options[]) { int ret = 0; if (mod != NULL && lookup (in_kernel,stripo(mod))==NULL && lookup (*newin_kernel,mod)==NULL){ NODE *nod = lookup (dep_file,mod); if (nod == NULL){ depmod_error ("No dependancy information for module %s",mod); return -1; } /* else */ NODE *dep = nod->info; while (dep != NULL && ret == 0){ ret = insmod (dep->str,newin_kernel,NULL); dep = dep->next; } if (ret == 0) { char cmd[300]; sprintf (cmd,"/sbin/insmod %s %s %s %s" ,from_kerneld ? "-k" : "" ,depmod_syslog ? "-s" : "" ,insmod_opt ? insmod_opt : "" ,mod); char *op; if (options && options[1] && strchr(options[1], '=')) { int nopt; for (nopt = 1; options[nopt]; ++nopt) { if (strchr(options[nopt], '=')){ strcat(cmd, " "); strcat(cmd, options[nopt]); } else break; } } else { if ((op = any_options(mod))) { strcat(cmd, " "); strcat(cmd, op); } if (options && options[0] && (op = any_options(options[0]))) { strcat(cmd, " "); strcat(cmd, op); } } verbose ("\r\t%s\n\t\t",cmd); /* For now, seems like insmod is not reporting */ /* success or failure correctly, so we read */ /* /proc/modules each time */ char *ex; if ((ex = exec_cmd(EXEC_PRE_INSTALL, mod)) != NULL) { if ((ret = system(ex)) != 0) depmod_error("pre-install %s failed\n", mod); } if (!ret) { if ((ex = exec_cmd(EXEC_INSTALL, mod)) != NULL) ret = system(ex); else ret = system(cmd); } if (!ret && (ex = exec_cmd(EXEC_POST_INSTALL, mod)) != NULL) { if ((ret = system(ex)) != 0) depmod_error("post-install %s failed\n", mod); } if (ret != 0){ // Must unload all the sub-module rmmod (nod->info); }else{ *newin_kernel = new NODE (mod,*newin_kernel); } } else { rmmod (nod->info); readcur(); } } return (ret)?-1:0; } /* Unload a module and whatever modules was requiered by this module. We blindly remove everything in order, even if a module is needed by another module. The idea is that this module will refuse to unload. Return -1 if the first module cound not be unloaded. */ PUBLIC int DEPEND::unload ( const char *mod) { int ret = 0; if (mod != NULL){ char *tbpath[1000]; int nbl = config_locate (mod,tbpath,NULL); if (nbl == 0){ /* #Specification: modprobe -r / unknown module If there is no information about a module in the dependancy file, we simply call /sbin/rmmod on the module without further checking. */ ret = call_rmmod(mod); }else{ int m; for (m=0; minfo; while (dep != NULL){ unload (dep->str); dep = dep->next; } } } } tbstr_free (tbpath,nbl); } } return ret; } static int modprobe_fromlist ( DEPEND &dep, char *list[], int nb, const char *type, int loadall) { int ret = -1; int i; for (i=0; i 0 && argv[0][0] == '-'; ++argv, --argc) { char *arg = argv[0]; while (*++arg) { switch (*arg) { case 't': if (argc > 1) { type = argv[1]; ++argv; --argc; } else { depmod_error ("Missing value for option -t"); ret = -1; } break; case 'a': loadall = 1; break; case 'c': showconfig = 1; break; case 'd': debugmode = 1; break; case 'l': list = 1; break; case 'r': remove = 1; break; case 'v': verbose_flag = 1; break; case 'k': from_kerneld = 1; break; case 's': depmod_setsyslog ("modprobe"); break; case 'V': printf("modprobe version %s\n", DEPMOD_RELEASE); break; default: depmod_error ("Invalid option %c", *arg); ret = -1; break; } } } if (ret == -1) return -1; /* * argv now points to the first non-option argument * argc is the remaining argument count */ if (showconfig) config_show(); else if (remove) { if (argc > 0) for (; argc > 0 && ret == 0; ++argv, --argc) ret = dep.unload (*argv); else modprobe_nothing ("remove"); } else if (list) { if (argc > 0) for (; argc > 0 && ret == 0; ++argv, --argc) modprobe_printlist (*argv, type); else modprobe_printlist ("*", type); } else { if (argc > 0) ret = modprobe_fromlist (dep,argv,argc,type,loadall); else modprobe_nothing ("load"); } verbose ("\n"); return ret; }