| package dkms | |
| import ( | |
| "context" | |
| "fmt" | |
| "os" | |
| "path/filepath" | |
| "cos.googlesource.com/cos/tools.git/src/pkg/fs" | |
| "cos.googlesource.com/cos/tools.git/src/pkg/gcs" | |
| "cos.googlesource.com/cos/tools.git/src/pkg/utils" | |
| "github.com/golang/glog" | |
| ) | |
| // Install installs all of the modules from a DKMS package into the install | |
| // tree. | |
| // | |
| // This will add and build the package if it is not already added and built. | |
| // | |
| // For each module in the package, this will check if the version being | |
| // installed is newer than the version of the module which is already | |
| // installed, if applicable. If the module version is older than the one | |
| // already installed, this will skip installing that module and emit an info | |
| // message. options.ForceVersionOverride can be set to bypass the version | |
| // check and install the module anyway. | |
| // | |
| // If options.ModprobeOnInstall is passed, then modprobe will be called on | |
| // the module after it is copied to the install tree to insert it into the | |
| // running kernel. | |
| func Install(pkg *Package, options *Options) error { | |
| // This must happen first in order to set the package config. | |
| if err := Build(pkg, options); err != nil { | |
| return err | |
| } | |
| if IsInstalled(pkg) { | |
| if !options.Force { | |
| glog.Info("package already installed; skipping installation") | |
| return nil | |
| } | |
| if err := Uninstall(pkg); err != nil { | |
| return fmt.Errorf("failed to uninstall package before forcing reinstall: %v", err) | |
| } | |
| } | |
| glog.Infof("installing package %s-%s", pkg.Name, pkg.Version) | |
| if pkg.Config.PreInstall != "" { | |
| if err := utils.RunCommandString(pkg.BuildDir(), pkg.Config.PreInstall, os.Stderr); err != nil { | |
| return fmt.Errorf("failed to run pre-install script: %v", err) | |
| } | |
| } | |
| var modulesToInstall []Module | |
| if options.ForceVersionOverride { | |
| modulesToInstall = pkg.Config.Modules | |
| } else { | |
| modules, err := ModulesToInstall(pkg.Config.Modules, pkg.Trees.Install) | |
| if err != nil { | |
| return err | |
| } | |
| modulesToInstall = modules | |
| } | |
| for _, module := range modulesToInstall { | |
| srcPath := module.BuiltPath() | |
| dstPath := module.DestPath() | |
| glog.Infof("copying %s to %s", srcPath, dstPath) | |
| if err := os.MkdirAll(filepath.Dir(dstPath), 0777); err != nil { | |
| return err | |
| } | |
| if err := fs.CopyFile(srcPath, dstPath, 0666); err != nil { | |
| return err | |
| } | |
| } | |
| if options.InsertOnInstall { | |
| kernelModulePaths, err := FindKernelModules(pkg.Trees.KernelModules) | |
| if err != nil { | |
| return err | |
| } | |
| installedModulePaths, err := FindKernelModules(pkg.Trees.Install) | |
| if err != nil { | |
| return err | |
| } | |
| modulePaths := make(map[string]string) | |
| for module, path := range kernelModulePaths { | |
| modulePaths[module] = path | |
| } | |
| // Modules in the install tree should take precedence over ones in the kernel tree | |
| for module, path := range installedModulePaths { | |
| modulePaths[module] = path | |
| } | |
| var moduleNames []string | |
| for _, module := range modulesToInstall { | |
| moduleNames = append(moduleNames, module.DestName) | |
| } | |
| if err := InsertModules(moduleNames, modulePaths, pkg.ModuleParams); err != nil { | |
| return err | |
| } | |
| } | |
| if options.ModprobeOnInstall { | |
| if !options.NoDepmod { | |
| if err := Depmod(); err != nil { | |
| return err | |
| } | |
| } | |
| if err := ModprobeModules(pkg.Config.Modules); err != nil { | |
| return err | |
| } | |
| } | |
| if pkg.Config.PostInstall != "" { | |
| if err := utils.RunCommandString(pkg.BuildDir(), pkg.Config.PostInstall, os.Stderr); err != nil { | |
| return fmt.Errorf("failed to run post-install script: %v", err) | |
| } | |
| } | |
| return nil | |
| } | |
| // CachedInstall installs all of the modules from a DKMS package into the | |
| // install tree, using CachedBuild to build the package if necessary. | |
| // | |
| // See Install for more information on how modules are installed locally. | |
| func CachedInstall(ctx context.Context, pkg *Package, cache *gcs.GCSBucket, options *Options) error { | |
| if err := CachedBuild(ctx, pkg, cache, options); err != nil { | |
| return err | |
| } | |
| return Install(pkg, options) | |
| } | |
| // ModulesToInstall returns the list of modules which should be installed. | |
| // | |
| // This takes a list of candidate modules to install and a directory where | |
| // they would be installed and checks for each module if there is a newer | |
| // version of that module already installed. If there is a newer or equal | |
| // version already installed, the module is omitted from the output list | |
| // and an info message is emitted. | |
| func ModulesToInstall(modules []Module, installTree string) ([]Module, error) { | |
| installedKernelModules, err := FindKernelModules(installTree) | |
| if err != nil { | |
| return nil, fmt.Errorf("could not find all installed kernel modules") | |
| } | |
| var result []Module | |
| for _, module := range modules { | |
| installName := module.DestName + ".ko" | |
| installedModulePath, ok := installedKernelModules[installName] | |
| if !ok { | |
| result = append(result, module) | |
| continue // module is not yet installed | |
| } | |
| installedVersion, err := ModuleVersion(installedModulePath) | |
| if err != nil { | |
| return nil, fmt.Errorf("could not determine installed module version for module %s: %s", module.DestName, err) | |
| } | |
| builtVersion, err := ModuleVersion(module.BuiltPath()) | |
| if err != nil { | |
| return nil, fmt.Errorf("could not determine built module version for module %s: %v", module.BuiltName, err) | |
| } | |
| comparison := CompareVersions(builtVersion, installedVersion) | |
| if comparison < 0 { | |
| glog.Infof( | |
| "installed version of module %s is newer than built version (%s > %s); not reinstalling; pass --force-version-override to install anyway", | |
| module.DestName, installedVersion, builtVersion, | |
| ) | |
| } else if comparison == 0 { | |
| glog.Infof( | |
| "module %s is already installed with version %s; not reinstalling; pass --force-version-override to install anyway", | |
| module.DestName, installedVersion, | |
| ) | |
| } else { | |
| result = append(result, module) | |
| } | |
| } | |
| return result, nil | |
| } | |
| // IsInstalled returns whether or not all of a package's modules have been | |
| // installed. | |
| // | |
| // If the package does not have its config set, this returns false. | |
| func IsInstalled(pkg *Package) bool { | |
| if pkg.Config == nil { | |
| return false | |
| } | |
| for _, module := range pkg.Config.Modules { | |
| if !fs.IsFile(module.DestPath()) { | |
| return false | |
| } | |
| } | |
| return true | |
| } | |