| package kmod | 
 |  | 
 | import ( | 
 | 	"bufio" | 
 | 	"fmt" | 
 | 	"os" | 
 | 	"path/filepath" | 
 | 	"strings" | 
 | 	"sync" | 
 |  | 
 | 	"golang.org/x/sys/unix" | 
 | 	"google.golang.org/protobuf/proto" | 
 |  | 
 | 	kmodpb "source.monogon.dev/metropolis/pkg/kmod/spec" | 
 | ) | 
 |  | 
 | // Manager contains all the logic and metadata required to efficiently load | 
 | // Linux kernel modules. It has internal loading tracking, thus it's recommended | 
 | // to only keep one Manager instance running per kernel. It can recreate its | 
 | // internal state quite well, but there are edge cases where the kernel makes | 
 | // it hard to do so (MODULE_STATE_UNFORMED) thus if possible that single | 
 | // instance should be kept alive. It currently does not support unloading | 
 | // modules, but that can be added to the existing design if deemed necessary. | 
 | type Manager struct { | 
 | 	// Directory the modules are loaded from. The path in each module Meta | 
 | 	// message is relative to this. | 
 | 	modulesPath string | 
 | 	meta        *kmodpb.Meta | 
 | 	// Extra map to quickly find module indexes from names | 
 | 	moduleIndexes map[string]uint32 | 
 |  | 
 | 	// mutex protects loadedModules, rest is read-only | 
 | 	// This cannot use a RWMutex as locks cannot be upgraded | 
 | 	mutex         sync.Mutex | 
 | 	loadedModules map[uint32]bool | 
 | } | 
 |  | 
 | // NewManager instantiates a kernel module loading manager. Please take a look | 
 | // at the additional considerations on the Manager type itself. | 
 | func NewManager(meta *kmodpb.Meta, modulesPath string) (*Manager, error) { | 
 | 	modIndexes := make(map[string]uint32) | 
 | 	for i, m := range meta.Modules { | 
 | 		modIndexes[m.Name] = uint32(i) | 
 | 	} | 
 | 	modulesFile, err := os.Open("/proc/modules") | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	loadedModules := make(map[uint32]bool) | 
 | 	s := bufio.NewScanner(modulesFile) | 
 | 	for s.Scan() { | 
 | 		fields := strings.Fields(s.Text()) | 
 | 		if len(fields) == 0 { | 
 | 			// Skip invalid lines | 
 | 			continue | 
 | 		} | 
 | 		modIdx, ok := modIndexes[fields[0]] | 
 | 		if !ok { | 
 | 			// Certain modules are only available as built-in and are thus not | 
 | 			// part of the module metadata. They do not need to be handled by | 
 | 			// this code, ignore them. | 
 | 			continue | 
 | 		} | 
 | 		loadedModules[modIdx] = true | 
 | 	} | 
 | 	return &Manager{ | 
 | 		modulesPath:   modulesPath, | 
 | 		meta:          meta, | 
 | 		moduleIndexes: modIndexes, | 
 | 		loadedModules: loadedModules, | 
 | 	}, nil | 
 | } | 
 |  | 
 | // NewManagerFromPath instantiates a new kernel module loading manager from a | 
 | // path containing a meta.pb file containing a kmod.Meta message as well as the | 
 | // kernel modules within. Please take a look at the additional considerations on | 
 | // the Manager type itself. | 
 | func NewManagerFromPath(modulesPath string) (*Manager, error) { | 
 | 	moduleMetaRaw, err := os.ReadFile(filepath.Join(modulesPath, "meta.pb")) | 
 | 	if err != nil { | 
 | 		return nil, fmt.Errorf("error reading module metadata: %w", err) | 
 | 	} | 
 | 	var moduleMeta kmodpb.Meta | 
 | 	if err := proto.Unmarshal(moduleMetaRaw, &moduleMeta); err != nil { | 
 | 		return nil, fmt.Errorf("error decoding module metadata: %w", err) | 
 | 	} | 
 | 	return NewManager(&moduleMeta, modulesPath) | 
 | } | 
 |  | 
 | // ErrNotFound is returned when an attempt is made to load a module which does | 
 | // not exist according to the loaded metadata. | 
 | type ErrNotFound struct { | 
 | 	Name string | 
 | } | 
 |  | 
 | func (e *ErrNotFound) Error() string { | 
 | 	return fmt.Sprintf("module %q does not exist", e.Name) | 
 | } | 
 |  | 
 | // LoadModule loads the module with the given name. If the module is already | 
 | // loaded or  built-in, it returns no error. If it failed loading the module or | 
 | // the module does not exist, it returns an error. | 
 | func (s *Manager) LoadModule(name string) error { | 
 | 	modIdx, ok := s.moduleIndexes[name] | 
 | 	if !ok { | 
 | 		return &ErrNotFound{Name: name} | 
 | 	} | 
 | 	s.mutex.Lock() | 
 | 	defer s.mutex.Unlock() | 
 | 	return s.loadModuleRecursive(modIdx) | 
 | } | 
 |  | 
 | // LoadModulesForDevice loads all modules whose device match expressions | 
 | // (modaliases) match the given device modalias. It only returns an error if | 
 | // a module which matched the device or one of its dependencies caused an error | 
 | // when loading. A device modalias string which matches nothing is not an error. | 
 | func (s *Manager) LoadModulesForDevice(devModalias string) error { | 
 | 	matches := make(map[uint32]bool) | 
 | 	lookupModulesRec(s.meta.ModuleDeviceMatches, devModalias, matches) | 
 | 	s.mutex.Lock() | 
 | 	defer s.mutex.Unlock() | 
 | 	for m := range matches { | 
 | 		if err := s.loadModuleRecursive(m); err != nil { | 
 | 			return err | 
 | 		} | 
 | 	} | 
 | 	return nil | 
 | } | 
 |  | 
 | // Caller is REQUIRED to hold s.mutex! | 
 | func (s *Manager) loadModuleRecursive(modIdx uint32) error { | 
 | 	if s.loadedModules[modIdx] { | 
 | 		return nil | 
 | 	} | 
 | 	modMeta := s.meta.Modules[modIdx] | 
 | 	if modMeta.Path == "" { | 
 | 		// Module is built-in, dependency always satisfied | 
 | 		return nil | 
 | 	} | 
 | 	for _, dep := range modMeta.Depends { | 
 | 		if err := s.loadModuleRecursive(dep); err != nil { | 
 | 			// Pass though as is, recursion can otherwise cause | 
 | 			// extremely large errors | 
 | 			return err | 
 | 		} | 
 | 	} | 
 | 	module, err := os.Open(filepath.Join(s.modulesPath, modMeta.Path)) | 
 | 	if err != nil { | 
 | 		return fmt.Errorf("error opening kernel module: %w", err) | 
 | 	} | 
 | 	defer module.Close() | 
 | 	err = LoadModule(module, "", 0) | 
 | 	if err != nil && err != unix.EEXIST { | 
 | 		return fmt.Errorf("error loading kernel module %v: %w", modMeta.Name, err) | 
 | 	} | 
 | 	s.loadedModules[modIdx] = true | 
 | 	return nil | 
 | } |