| // Package devmgr is the userspace pendant to the kernel device management |
| // system. It talks to the kernel and the performs any further userspace actions |
| // related to device events. It corresponds very roughly to systemd-udevd on |
| // more conventional Linux distros. It currently only handles dynamic module |
| // loading, but will be extended to provide better device handling in other |
| // parts of the system. |
| package devmgr |
| |
| import ( |
| "context" |
| "fmt" |
| "io/fs" |
| "os" |
| "path/filepath" |
| |
| "github.com/mdlayher/kobject" |
| |
| "source.monogon.dev/metropolis/pkg/kmod" |
| "source.monogon.dev/metropolis/pkg/supervisor" |
| ) |
| |
| type Service struct{} |
| |
| func New() *Service { |
| return &Service{} |
| } |
| |
| func (s *Service) Run(ctx context.Context) error { |
| c, err := kobject.New() |
| if err != nil { |
| return fmt.Errorf("unable to create kobject uevent socket: %w", err) |
| } |
| defer c.Close() |
| |
| l := supervisor.Logger(ctx) |
| |
| modMgr, err := kmod.NewManagerFromPath("/lib/modules") |
| if err != nil { |
| return fmt.Errorf("error creating module manager: %w", err) |
| } |
| |
| // Start goroutine which instructs the kernel to generate "synthetic" |
| // uevents for all preexisting devices. This allows the kobject netlink |
| // listener below to "catch up" on devices added before it was created. |
| // This functionality is minimally documented in the Linux kernel, the |
| // closest we have is |
| // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-uevent which |
| // contains documentation on how to trigger synthetic events. |
| go func() { |
| err = filepath.WalkDir("/sys/devices", func(path string, d fs.DirEntry, err error) error { |
| if !d.IsDir() && d.Name() == "uevent" { |
| if err := os.WriteFile(path, []byte("add"), 0); err != nil { |
| return err |
| } |
| } |
| return nil |
| }) |
| if err != nil { |
| l.Errorf("failed to load initial device database: %v", err) |
| } else { |
| l.Info("Initial device loading done") |
| } |
| }() |
| |
| for { |
| e, err := c.Receive() |
| if err != nil { |
| return fmt.Errorf("error receiving kobject uevent: %w", err) |
| } |
| if e.Action == kobject.Add { |
| if e.Values["MODALIAS"] != "" { |
| if err := modMgr.LoadModulesForDevice(e.Values["MODALIAS"]); err != nil { |
| l.Errorf("Error loading kernel modules: %w", err) |
| } |
| } |
| } |
| } |
| } |