Loading commands/modutils/depmod.cpp +41 −3 Changes for commands/modutils/depmod.cpp: 41 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -62,6 +62,12 @@ namespace fresh { namespace cmd { // module built as part of one coherent kernel tree, which is the only // case this system's own kernel build produces) -- not a general // replacement for depmod's symbol-resolution fallback. // // Also writes modules.alias ("alias <pattern> <module>", each .ko's // "alias=" fields), which modprobe and syslite resolve modaliases and // request_module() names with. Paths in modules.dep are relative to the // module directory, like real depmod's: with -b (modules_install into // a buildroot) absolute ones would point into the buildroot. class DepmodCommand : public ShellCommand { public: DepmodCommand():ShellCommand("depmod"){} Loading Loading @@ -133,6 +139,22 @@ namespace fresh { namespace cmd { return ""; } // "alias=" may occur many times, modinfoField() returns the first static std::vector<std::string> modinfoFields(const std::vector<char> &data,const std::string &field){ std::vector<std::string> out; size_t i=0; while(i<data.size()){ size_t start=i; while(i<data.size() && data[i]!='\0') ++i; std::string entry(data.data()+start,i-start); ++i; size_t eq=entry.find('='); if(eq!=std::string::npos && entry.compare(0,eq,field)==0 && eq==field.size()) out.push_back(entry.substr(eq+1)); } return out; } static std::vector<std::string> splitComma(const std::string &s){ std::vector<std::string> out; size_t start=0; Loading Loading @@ -219,9 +241,14 @@ namespace fresh { namespace cmd { std::vector<std::string> koFiles; findKoFiles(dir,koFiles); // "kernel/fs/x.ko": relative to dir auto relPath=[&](const std::string &path){ return path.compare(0,dir.size()+1,dir+"/")==0 ? path.substr(dir.size()+1) : path; }; std::map<std::string,std::string> nameToPath; for(const auto &path: koFiles) nameToPath[moduleName(path)]=path; nameToPath[moduleName(path)]=relPath(path); std::string depPath=dir+"/modules.dep"; std::ofstream out(depPath); Loading @@ -229,14 +256,25 @@ namespace fresh { namespace cmd { std::cerr << "depmod: " << depPath << ": " << strerror(errno) << std::endl; return 1; } std::string aliasPath=dir+"/modules.alias"; std::ofstream aliases(aliasPath); if(!aliases.is_open()){ std::cerr << "depmod: " << aliasPath << ": " << strerror(errno) << std::endl; return 1; } aliases << "# Aliases extracted from modules themselves.\n"; for(const auto &path: koFiles){ std::vector<char> info; std::string depends; if(readModinfoSectionFromFile(path,info)) std::string name=moduleName(path); if(readModinfoSectionFromFile(path,info)){ depends=modinfoField(info,"depends"); for(const auto &a: modinfoFields(info,"alias")) aliases << "alias " << a << " " << name << "\n"; } out << path << ":"; out << relPath(path) << ":"; for(const auto &depName: splitComma(depends)){ auto it=nameToPath.find(depName); if(it!=nameToPath.end()) Loading commands/modutils/modprobe.cpp +278 −42 Changes for commands/modutils/modprobe.cpp: 278 added lines, 42 removed lines. Original line number Diff line number Diff line Loading @@ -39,6 +39,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <unistd.h> #include <fcntl.h> #include <fnmatch.h> #include <dirent.h> #include <sys/syscall.h> #include <sys/utsname.h> Loading @@ -55,12 +57,39 @@ namespace fresh { namespace cmd { return s.size()>kXzSuffixLen && s.compare(s.size()-kXzSuffixLen,kXzSuffixLen,kXzSuffix)==0; } // kmod's alias_normalize(): '-' and '_' are the same in module names // and aliases, except inside a [...] class of a pattern std::string normalize(const std::string &s){ std::string out=s; bool inClass=false; for(auto &c: out){ if(c=='[') inClass=true; else if(c==']') inClass=false; else if(c=='-' && !inClass) c='_'; } return out; } struct Alias { std::string pattern; // normalized std::string module; }; } // Loads MODULE (by name, not path) plus its dependencies, resolved from // modules.dep (see depmod). Idempotent: a module already loaded (EEXIST // from finit_module/init_module) counts as success, matching real // modprobe. // Loads modules by name or alias, their dependencies first, from the // index depmod writes (modules.dep, modules.alias) plus the kernel's own // modules.builtin and modules.builtin.modinfo: a module or alias that // is built in counts as loaded. Also the kernel's request_module() // helper (CONFIG_MODPROBE_PATH), which runs "modprobe -q -- <alias>" // for fs-nfsd, rtnl-link-bridge, net-pf-10, md-raid1, ... // // modprobe.d (/etc, /run, /usr/lib, /lib): "options <module> <params>", // "alias <pattern> <module>", "blacklist <module>" (the module's aliases // are ignored; with -b also its name); "install", "remove" and // "softdep" are ignored. Kernel command line: modprobe.blacklist=a,b and // <module>.<param>=<value>. An already loaded module (EEXIST) counts as // success. class ModprobeCommand : public ShellCommand { public: ModprobeCommand():ShellCommand("modprobe"){} Loading Loading @@ -104,7 +133,7 @@ namespace fresh { namespace cmd { errno==EEXIST; return ok; } int fd=open(koPath.c_str(),O_RDONLY); int fd=open(koPath.c_str(),O_RDONLY|O_CLOEXEC); if(fd<0) return false; bool ok=(syscall(SYS_finit_module,fd,params.c_str(),0)==0) || errno==EEXIST; close(fd); Loading @@ -112,75 +141,282 @@ namespace fresh { namespace cmd { } int runCommand(int argc,const char **argv){ if(argc<2){ std::cerr << "modprobe: usage: modprobe MODULE [param=value ...]" << std::endl; bool quiet=false,verbose=false,dryRun=false,all=false,remove=false,useBlacklist=false; std::vector<std::string> args; int i=1; for(;i<argc;++i){ std::string a=argv[i]; if(a=="--"){ ++i; break; } if(a.size()<2 || a[0]!='-') break; if(a=="--quiet") quiet=true; else if(a=="--verbose") verbose=true; else if(a=="--dry-run" || a=="--show") dryRun=true; else if(a=="--all") all=true; else if(a=="--remove") remove=true; else if(a=="--use-blacklist") useBlacklist=true; else if(a[1]=='-'){ std::cerr << "modprobe: unknown option " << a << std::endl; return 1; }else{ for(size_t j=1;j<a.size();++j){ switch(a[j]){ case 'q': quiet=true; break; case 'v': verbose=true; break; case 'n': dryRun=true; break; case 'a': all=true; break; case 'r': remove=true; break; case 'b': useBlacklist=true; break; default: std::cerr << "modprobe: unknown option -" << a[j] << std::endl; return 1; } } } } for(;i<argc;++i) args.push_back(argv[i]); if(args.empty()){ std::cerr << "modprobe: usage: modprobe [-qvnb] MODULE [param=value ...]" << std::endl << " modprobe -a [-qvnb] MODULE..." << std::endl << " modprobe -r [-qvn] MODULE..." << std::endl; return 1; } std::string wanted=argv[1]; for(auto &c: wanted) if(c=='-') c='_'; // MODULE [params] -- with -a or -r every argument is a module std::vector<std::string> wanted; std::string params; for(int i=2;i<argc;++i){ if(all || remove){ wanted=args; }else{ wanted.push_back(args[0]); for(size_t k=1;k<args.size();++k){ if(!params.empty()) params+=" "; params+=argv[i]; params+=args[k]; } } if(remove){ int rc=0; for(const auto &w: wanted){ std::string name=normalize(w); if(verbose) std::cout << "rmmod " << name << std::endl; if(dryRun) continue; if(syscall(SYS_delete_module,name.c_str(),O_NONBLOCK)!=0 && errno!=ENOENT){ if(!quiet) std::cerr << "modprobe: " << name << ": " << strerror(errno) << std::endl; rc=1; } } return rc; } struct utsname uts; uname(&uts); std::string depPath=std::string("/lib/modules/")+uts.release+"/modules.dep"; _dir=std::string("/lib/modules/")+uts.release; if(!readIndex(quiet)) return 1; readConfig(); std::ifstream in(depPath); if(!in.is_open()){ std::cerr << "modprobe: " << depPath << ": " << strerror(errno) << std::endl; return 1; int rc=0; for(const auto &w: wanted) if(!probe(w,params,quiet,verbose,dryRun,useBlacklist)) rc=1; return rc; } std::map<std::string,std::string> nameToPath; std::map<std::string,std::vector<std::string> > deps; private: std::string _dir; std::map<std::string,std::string> _nameToPath; std::map<std::string,std::vector<std::string> > _deps; std::vector<Alias> _aliases; // modules.alias std::vector<Alias> _confAliases; // modprobe.d std::set<std::string> _builtin; std::vector<std::string> _builtinAliases; // normalized patterns std::set<std::string> _blacklist; std::map<std::string,std::string> _options; std::string absPath(const std::string &p) const { return (!p.empty() && p[0]=='/') ? p : _dir+"/"+p; } bool readIndex(bool quiet){ std::string depPath=_dir+"/modules.dep"; std::ifstream in(depPath); if(!in.is_open()){ if(!quiet) std::cerr << "modprobe: " << depPath << ": " << strerror(errno) << std::endl; return false; } std::string line; while(std::getline(in,line)){ size_t colon=line.find(':'); if(colon==std::string::npos) continue; std::string path=line.substr(0,colon); std::string name=moduleName(path); nameToPath[name]=path; _nameToPath[name]=absPath(path); std::istringstream rest(line.substr(colon+1)); std::string depPathTok; while(rest >> depPathTok) deps[name].push_back(moduleName(depPathTok)); _deps[name].push_back(moduleName(depPathTok)); } auto it=nameToPath.find(wanted); if(it==nameToPath.end()){ std::cerr << "modprobe: " << wanted << ": module not found" << std::endl; return 1; std::ifstream alias(_dir+"/modules.alias"); while(std::getline(alias,line)){ std::istringstream iss(line); std::string kw,pattern,module; if(!(iss >> kw >> pattern >> module) || kw!="alias") continue; _aliases.push_back({normalize(pattern),normalize(module)}); } std::set<std::string> loaded; std::vector<std::string> order; // dependencies first, target last std::ifstream builtin(_dir+"/modules.builtin"); while(std::getline(builtin,line)) if(!line.empty()) _builtin.insert(moduleName(line)); // "<module>.alias=<pattern>" NUL-separated, from the kernel build std::ifstream binfo(_dir+"/modules.builtin.modinfo",std::ios::binary); std::string entry; while(std::getline(binfo,entry,'\0')){ size_t dot=entry.find('.'); if(dot==std::string::npos || entry.compare(dot,7,".alias=")!=0) continue; _builtinAliases.push_back(normalize(entry.substr(dot+7))); } return true; } std::function<void(const std::string &)> visit=[&](const std::string &name){ if(loaded.count(name)) return; loaded.insert(name); auto dit=deps.find(name); if(dit!=deps.end()) static std::vector<std::string> confFiles(){ std::map<std::string,std::string> files; // an earlier dir hides the same name for(const char *d: {"/etc/modprobe.d","/run/modprobe.d","/usr/lib/modprobe.d","/lib/modprobe.d"}){ DIR *dir=opendir(d); if(!dir) continue; struct dirent *ent; while((ent=readdir(dir))!=nullptr){ std::string n=ent->d_name; if(n.size()>5 && n.compare(n.size()-5,5,".conf")==0) files.emplace(n,std::string(d)+"/"+n); } closedir(dir); } std::vector<std::string> out; for(auto &f: files) out.push_back(f.second); return out; } void addOptions(const std::string &module,const std::string &opts){ if(opts.empty()) return; std::string &o=_options[normalize(module)]; if(!o.empty()) o+=" "; o+=opts; } void readConfig(){ for(const auto &file: confFiles()){ std::ifstream in(file); std::string line; while(std::getline(in,line)){ std::istringstream iss(line); std::string kw,a; if(!(iss >> kw) || kw[0]=='#') continue; if(!(iss >> a)) continue; if(kw=="blacklist"){ _blacklist.insert(normalize(a)); }else if(kw=="alias"){ std::string module; if(iss >> module) _confAliases.push_back({normalize(a),normalize(module)}); }else if(kw=="options"){ std::string rest; std::getline(iss,rest); size_t b=rest.find_first_not_of(" \t"); addOptions(a,b==std::string::npos ? "" : rest.substr(b)); } } } std::ifstream cmdline("/proc/cmdline"); std::string tok; while(cmdline >> tok){ size_t eq=tok.find('='); if(eq==std::string::npos) continue; std::string key=tok.substr(0,eq),val=tok.substr(eq+1); if(key=="modprobe.blacklist"){ std::istringstream ms(val); std::string m; while(std::getline(ms,m,',')) if(!m.empty()) _blacklist.insert(normalize(m)); continue; } size_t dot=key.find('.'); if(dot==std::string::npos || dot==0) continue; addOptions(key.substr(0,dot),tok.substr(dot+1)); } } static bool matches(const std::string &pattern,const std::string &name){ return fnmatch(pattern.c_str(),name.c_str(),0)==0; } // the modules NAME stands for; builtin: NAME is (an alias of) a // built-in module std::vector<std::string> resolve(const std::string &name,bool useBlacklist,bool &builtin) const { std::string n=normalize(name); std::vector<std::string> out; builtin=false; if(_nameToPath.count(n)){ if(!(useBlacklist && _blacklist.count(n))) out.push_back(n); return out; } if(_builtin.count(n)){ builtin=true; return out; } std::set<std::string> seen; for(const auto *list: {&_confAliases,&_aliases}){ for(const auto &a: *list){ if(!matches(a.pattern,n) || _blacklist.count(a.module)) continue; if(_builtin.count(a.module)){ builtin=true; continue; } if(seen.insert(a.module).second) out.push_back(a.module); } if(!out.empty()) return out; // modprobe.d's aliases win } if(out.empty()) for(const auto &p: _builtinAliases) if(matches(p,n)){ builtin=true; break; } return out; } bool probe(const std::string &name,const std::string ¶ms, bool quiet,bool verbose,bool dryRun,bool useBlacklist){ bool builtin=false; std::vector<std::string> modules=resolve(name,useBlacklist,builtin); if(modules.empty()){ if(builtin) return true; if(!quiet) std::cerr << "modprobe: " << name << ": module not found" << std::endl; return false; } bool ok=true; for(const auto &target: modules){ std::set<std::string> visited; std::vector<std::string> order; // dependencies first, target last std::function<void(const std::string &)> visit=[&](const std::string &m){ if(!visited.insert(m).second) return; auto dit=_deps.find(m); if(dit!=_deps.end()) for(const auto &dep: dit->second) visit(dep); order.push_back(name); order.push_back(m); }; visit(wanted); visit(target); for(const auto &name: order){ auto pit=nameToPath.find(name); if(pit==nameToPath.end()) continue; // unresolved dep name -- best-effort, matches real modprobe's tolerance for soft deps if(!loadOne(pit->second,name==wanted ? params : "")){ std::cerr << "modprobe: " << name << ": " << strerror(errno) << std::endl; return 1; for(const auto &m: order){ auto pit=_nameToPath.find(m); if(pit==_nameToPath.end()) continue; // a dependency that is built in std::string p; auto oit=_options.find(m); if(oit!=_options.end()) p=oit->second; if(m==target && !params.empty()) p+=(p.empty() ? "" : " ")+params; if(verbose) std::cout << "insmod " << pit->second << (p.empty() ? "" : " ") << p << std::endl; if(dryRun) continue; if(!loadOne(pit->second,p)){ if(!quiet) std::cerr << "modprobe: " << m << ": " << strerror(errno) << std::endl; ok=false; break; } } return 0; } return ok; } }; Loading Loading
commands/modutils/depmod.cpp +41 −3 Changes for commands/modutils/depmod.cpp: 41 added lines, 3 removed lines. Original line number Diff line number Diff line Loading @@ -62,6 +62,12 @@ namespace fresh { namespace cmd { // module built as part of one coherent kernel tree, which is the only // case this system's own kernel build produces) -- not a general // replacement for depmod's symbol-resolution fallback. // // Also writes modules.alias ("alias <pattern> <module>", each .ko's // "alias=" fields), which modprobe and syslite resolve modaliases and // request_module() names with. Paths in modules.dep are relative to the // module directory, like real depmod's: with -b (modules_install into // a buildroot) absolute ones would point into the buildroot. class DepmodCommand : public ShellCommand { public: DepmodCommand():ShellCommand("depmod"){} Loading Loading @@ -133,6 +139,22 @@ namespace fresh { namespace cmd { return ""; } // "alias=" may occur many times, modinfoField() returns the first static std::vector<std::string> modinfoFields(const std::vector<char> &data,const std::string &field){ std::vector<std::string> out; size_t i=0; while(i<data.size()){ size_t start=i; while(i<data.size() && data[i]!='\0') ++i; std::string entry(data.data()+start,i-start); ++i; size_t eq=entry.find('='); if(eq!=std::string::npos && entry.compare(0,eq,field)==0 && eq==field.size()) out.push_back(entry.substr(eq+1)); } return out; } static std::vector<std::string> splitComma(const std::string &s){ std::vector<std::string> out; size_t start=0; Loading Loading @@ -219,9 +241,14 @@ namespace fresh { namespace cmd { std::vector<std::string> koFiles; findKoFiles(dir,koFiles); // "kernel/fs/x.ko": relative to dir auto relPath=[&](const std::string &path){ return path.compare(0,dir.size()+1,dir+"/")==0 ? path.substr(dir.size()+1) : path; }; std::map<std::string,std::string> nameToPath; for(const auto &path: koFiles) nameToPath[moduleName(path)]=path; nameToPath[moduleName(path)]=relPath(path); std::string depPath=dir+"/modules.dep"; std::ofstream out(depPath); Loading @@ -229,14 +256,25 @@ namespace fresh { namespace cmd { std::cerr << "depmod: " << depPath << ": " << strerror(errno) << std::endl; return 1; } std::string aliasPath=dir+"/modules.alias"; std::ofstream aliases(aliasPath); if(!aliases.is_open()){ std::cerr << "depmod: " << aliasPath << ": " << strerror(errno) << std::endl; return 1; } aliases << "# Aliases extracted from modules themselves.\n"; for(const auto &path: koFiles){ std::vector<char> info; std::string depends; if(readModinfoSectionFromFile(path,info)) std::string name=moduleName(path); if(readModinfoSectionFromFile(path,info)){ depends=modinfoField(info,"depends"); for(const auto &a: modinfoFields(info,"alias")) aliases << "alias " << a << " " << name << "\n"; } out << path << ":"; out << relPath(path) << ":"; for(const auto &depName: splitComma(depends)){ auto it=nameToPath.find(depName); if(it!=nameToPath.end()) Loading
commands/modutils/modprobe.cpp +278 −42 Changes for commands/modutils/modprobe.cpp: 278 added lines, 42 removed lines. Original line number Diff line number Diff line Loading @@ -39,6 +39,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <unistd.h> #include <fcntl.h> #include <fnmatch.h> #include <dirent.h> #include <sys/syscall.h> #include <sys/utsname.h> Loading @@ -55,12 +57,39 @@ namespace fresh { namespace cmd { return s.size()>kXzSuffixLen && s.compare(s.size()-kXzSuffixLen,kXzSuffixLen,kXzSuffix)==0; } // kmod's alias_normalize(): '-' and '_' are the same in module names // and aliases, except inside a [...] class of a pattern std::string normalize(const std::string &s){ std::string out=s; bool inClass=false; for(auto &c: out){ if(c=='[') inClass=true; else if(c==']') inClass=false; else if(c=='-' && !inClass) c='_'; } return out; } struct Alias { std::string pattern; // normalized std::string module; }; } // Loads MODULE (by name, not path) plus its dependencies, resolved from // modules.dep (see depmod). Idempotent: a module already loaded (EEXIST // from finit_module/init_module) counts as success, matching real // modprobe. // Loads modules by name or alias, their dependencies first, from the // index depmod writes (modules.dep, modules.alias) plus the kernel's own // modules.builtin and modules.builtin.modinfo: a module or alias that // is built in counts as loaded. Also the kernel's request_module() // helper (CONFIG_MODPROBE_PATH), which runs "modprobe -q -- <alias>" // for fs-nfsd, rtnl-link-bridge, net-pf-10, md-raid1, ... // // modprobe.d (/etc, /run, /usr/lib, /lib): "options <module> <params>", // "alias <pattern> <module>", "blacklist <module>" (the module's aliases // are ignored; with -b also its name); "install", "remove" and // "softdep" are ignored. Kernel command line: modprobe.blacklist=a,b and // <module>.<param>=<value>. An already loaded module (EEXIST) counts as // success. class ModprobeCommand : public ShellCommand { public: ModprobeCommand():ShellCommand("modprobe"){} Loading Loading @@ -104,7 +133,7 @@ namespace fresh { namespace cmd { errno==EEXIST; return ok; } int fd=open(koPath.c_str(),O_RDONLY); int fd=open(koPath.c_str(),O_RDONLY|O_CLOEXEC); if(fd<0) return false; bool ok=(syscall(SYS_finit_module,fd,params.c_str(),0)==0) || errno==EEXIST; close(fd); Loading @@ -112,75 +141,282 @@ namespace fresh { namespace cmd { } int runCommand(int argc,const char **argv){ if(argc<2){ std::cerr << "modprobe: usage: modprobe MODULE [param=value ...]" << std::endl; bool quiet=false,verbose=false,dryRun=false,all=false,remove=false,useBlacklist=false; std::vector<std::string> args; int i=1; for(;i<argc;++i){ std::string a=argv[i]; if(a=="--"){ ++i; break; } if(a.size()<2 || a[0]!='-') break; if(a=="--quiet") quiet=true; else if(a=="--verbose") verbose=true; else if(a=="--dry-run" || a=="--show") dryRun=true; else if(a=="--all") all=true; else if(a=="--remove") remove=true; else if(a=="--use-blacklist") useBlacklist=true; else if(a[1]=='-'){ std::cerr << "modprobe: unknown option " << a << std::endl; return 1; }else{ for(size_t j=1;j<a.size();++j){ switch(a[j]){ case 'q': quiet=true; break; case 'v': verbose=true; break; case 'n': dryRun=true; break; case 'a': all=true; break; case 'r': remove=true; break; case 'b': useBlacklist=true; break; default: std::cerr << "modprobe: unknown option -" << a[j] << std::endl; return 1; } } } } for(;i<argc;++i) args.push_back(argv[i]); if(args.empty()){ std::cerr << "modprobe: usage: modprobe [-qvnb] MODULE [param=value ...]" << std::endl << " modprobe -a [-qvnb] MODULE..." << std::endl << " modprobe -r [-qvn] MODULE..." << std::endl; return 1; } std::string wanted=argv[1]; for(auto &c: wanted) if(c=='-') c='_'; // MODULE [params] -- with -a or -r every argument is a module std::vector<std::string> wanted; std::string params; for(int i=2;i<argc;++i){ if(all || remove){ wanted=args; }else{ wanted.push_back(args[0]); for(size_t k=1;k<args.size();++k){ if(!params.empty()) params+=" "; params+=argv[i]; params+=args[k]; } } if(remove){ int rc=0; for(const auto &w: wanted){ std::string name=normalize(w); if(verbose) std::cout << "rmmod " << name << std::endl; if(dryRun) continue; if(syscall(SYS_delete_module,name.c_str(),O_NONBLOCK)!=0 && errno!=ENOENT){ if(!quiet) std::cerr << "modprobe: " << name << ": " << strerror(errno) << std::endl; rc=1; } } return rc; } struct utsname uts; uname(&uts); std::string depPath=std::string("/lib/modules/")+uts.release+"/modules.dep"; _dir=std::string("/lib/modules/")+uts.release; if(!readIndex(quiet)) return 1; readConfig(); std::ifstream in(depPath); if(!in.is_open()){ std::cerr << "modprobe: " << depPath << ": " << strerror(errno) << std::endl; return 1; int rc=0; for(const auto &w: wanted) if(!probe(w,params,quiet,verbose,dryRun,useBlacklist)) rc=1; return rc; } std::map<std::string,std::string> nameToPath; std::map<std::string,std::vector<std::string> > deps; private: std::string _dir; std::map<std::string,std::string> _nameToPath; std::map<std::string,std::vector<std::string> > _deps; std::vector<Alias> _aliases; // modules.alias std::vector<Alias> _confAliases; // modprobe.d std::set<std::string> _builtin; std::vector<std::string> _builtinAliases; // normalized patterns std::set<std::string> _blacklist; std::map<std::string,std::string> _options; std::string absPath(const std::string &p) const { return (!p.empty() && p[0]=='/') ? p : _dir+"/"+p; } bool readIndex(bool quiet){ std::string depPath=_dir+"/modules.dep"; std::ifstream in(depPath); if(!in.is_open()){ if(!quiet) std::cerr << "modprobe: " << depPath << ": " << strerror(errno) << std::endl; return false; } std::string line; while(std::getline(in,line)){ size_t colon=line.find(':'); if(colon==std::string::npos) continue; std::string path=line.substr(0,colon); std::string name=moduleName(path); nameToPath[name]=path; _nameToPath[name]=absPath(path); std::istringstream rest(line.substr(colon+1)); std::string depPathTok; while(rest >> depPathTok) deps[name].push_back(moduleName(depPathTok)); _deps[name].push_back(moduleName(depPathTok)); } auto it=nameToPath.find(wanted); if(it==nameToPath.end()){ std::cerr << "modprobe: " << wanted << ": module not found" << std::endl; return 1; std::ifstream alias(_dir+"/modules.alias"); while(std::getline(alias,line)){ std::istringstream iss(line); std::string kw,pattern,module; if(!(iss >> kw >> pattern >> module) || kw!="alias") continue; _aliases.push_back({normalize(pattern),normalize(module)}); } std::set<std::string> loaded; std::vector<std::string> order; // dependencies first, target last std::ifstream builtin(_dir+"/modules.builtin"); while(std::getline(builtin,line)) if(!line.empty()) _builtin.insert(moduleName(line)); // "<module>.alias=<pattern>" NUL-separated, from the kernel build std::ifstream binfo(_dir+"/modules.builtin.modinfo",std::ios::binary); std::string entry; while(std::getline(binfo,entry,'\0')){ size_t dot=entry.find('.'); if(dot==std::string::npos || entry.compare(dot,7,".alias=")!=0) continue; _builtinAliases.push_back(normalize(entry.substr(dot+7))); } return true; } std::function<void(const std::string &)> visit=[&](const std::string &name){ if(loaded.count(name)) return; loaded.insert(name); auto dit=deps.find(name); if(dit!=deps.end()) static std::vector<std::string> confFiles(){ std::map<std::string,std::string> files; // an earlier dir hides the same name for(const char *d: {"/etc/modprobe.d","/run/modprobe.d","/usr/lib/modprobe.d","/lib/modprobe.d"}){ DIR *dir=opendir(d); if(!dir) continue; struct dirent *ent; while((ent=readdir(dir))!=nullptr){ std::string n=ent->d_name; if(n.size()>5 && n.compare(n.size()-5,5,".conf")==0) files.emplace(n,std::string(d)+"/"+n); } closedir(dir); } std::vector<std::string> out; for(auto &f: files) out.push_back(f.second); return out; } void addOptions(const std::string &module,const std::string &opts){ if(opts.empty()) return; std::string &o=_options[normalize(module)]; if(!o.empty()) o+=" "; o+=opts; } void readConfig(){ for(const auto &file: confFiles()){ std::ifstream in(file); std::string line; while(std::getline(in,line)){ std::istringstream iss(line); std::string kw,a; if(!(iss >> kw) || kw[0]=='#') continue; if(!(iss >> a)) continue; if(kw=="blacklist"){ _blacklist.insert(normalize(a)); }else if(kw=="alias"){ std::string module; if(iss >> module) _confAliases.push_back({normalize(a),normalize(module)}); }else if(kw=="options"){ std::string rest; std::getline(iss,rest); size_t b=rest.find_first_not_of(" \t"); addOptions(a,b==std::string::npos ? "" : rest.substr(b)); } } } std::ifstream cmdline("/proc/cmdline"); std::string tok; while(cmdline >> tok){ size_t eq=tok.find('='); if(eq==std::string::npos) continue; std::string key=tok.substr(0,eq),val=tok.substr(eq+1); if(key=="modprobe.blacklist"){ std::istringstream ms(val); std::string m; while(std::getline(ms,m,',')) if(!m.empty()) _blacklist.insert(normalize(m)); continue; } size_t dot=key.find('.'); if(dot==std::string::npos || dot==0) continue; addOptions(key.substr(0,dot),tok.substr(dot+1)); } } static bool matches(const std::string &pattern,const std::string &name){ return fnmatch(pattern.c_str(),name.c_str(),0)==0; } // the modules NAME stands for; builtin: NAME is (an alias of) a // built-in module std::vector<std::string> resolve(const std::string &name,bool useBlacklist,bool &builtin) const { std::string n=normalize(name); std::vector<std::string> out; builtin=false; if(_nameToPath.count(n)){ if(!(useBlacklist && _blacklist.count(n))) out.push_back(n); return out; } if(_builtin.count(n)){ builtin=true; return out; } std::set<std::string> seen; for(const auto *list: {&_confAliases,&_aliases}){ for(const auto &a: *list){ if(!matches(a.pattern,n) || _blacklist.count(a.module)) continue; if(_builtin.count(a.module)){ builtin=true; continue; } if(seen.insert(a.module).second) out.push_back(a.module); } if(!out.empty()) return out; // modprobe.d's aliases win } if(out.empty()) for(const auto &p: _builtinAliases) if(matches(p,n)){ builtin=true; break; } return out; } bool probe(const std::string &name,const std::string ¶ms, bool quiet,bool verbose,bool dryRun,bool useBlacklist){ bool builtin=false; std::vector<std::string> modules=resolve(name,useBlacklist,builtin); if(modules.empty()){ if(builtin) return true; if(!quiet) std::cerr << "modprobe: " << name << ": module not found" << std::endl; return false; } bool ok=true; for(const auto &target: modules){ std::set<std::string> visited; std::vector<std::string> order; // dependencies first, target last std::function<void(const std::string &)> visit=[&](const std::string &m){ if(!visited.insert(m).second) return; auto dit=_deps.find(m); if(dit!=_deps.end()) for(const auto &dep: dit->second) visit(dep); order.push_back(name); order.push_back(m); }; visit(wanted); visit(target); for(const auto &name: order){ auto pit=nameToPath.find(name); if(pit==nameToPath.end()) continue; // unresolved dep name -- best-effort, matches real modprobe's tolerance for soft deps if(!loadOne(pit->second,name==wanted ? params : "")){ std::cerr << "modprobe: " << name << ": " << strerror(errno) << std::endl; return 1; for(const auto &m: order){ auto pit=_nameToPath.find(m); if(pit==_nameToPath.end()) continue; // a dependency that is built in std::string p; auto oit=_options.find(m); if(oit!=_options.end()) p=oit->second; if(m==target && !params.empty()) p+=(p.empty() ? "" : " ")+params; if(verbose) std::cout << "insmod " << pit->second << (p.empty() ? "" : " ") << p << std::endl; if(dryRun) continue; if(!loadOne(pit->second,p)){ if(!quiet) std::cerr << "modprobe: " << m << ": " << strerror(errno) << std::endl; ok=false; break; } } return 0; } return ok; } }; Loading