Lorenz Brun | 46bf7d6 | 2023-06-01 12:24:19 +0200 | [diff] [blame^] | 1 | // Package devmgr is the userspace pendant to the kernel device management |
| 2 | // system. It talks to the kernel and the performs any further userspace actions |
| 3 | // related to device events. It corresponds very roughly to systemd-udevd on |
| 4 | // more conventional Linux distros. It currently only handles dynamic module |
| 5 | // loading, but will be extended to provide better device handling in other |
| 6 | // parts of the system. |
| 7 | package devmgr |
| 8 | |
| 9 | import ( |
| 10 | "context" |
| 11 | "fmt" |
| 12 | "io/fs" |
| 13 | "os" |
| 14 | "path/filepath" |
| 15 | |
| 16 | "github.com/mdlayher/kobject" |
| 17 | |
| 18 | "source.monogon.dev/metropolis/pkg/kmod" |
| 19 | "source.monogon.dev/metropolis/pkg/supervisor" |
| 20 | ) |
| 21 | |
| 22 | type Service struct{} |
| 23 | |
| 24 | func New() *Service { |
| 25 | return &Service{} |
| 26 | } |
| 27 | |
| 28 | func (s *Service) Run(ctx context.Context) error { |
| 29 | c, err := kobject.New() |
| 30 | if err != nil { |
| 31 | return fmt.Errorf("unable to create kobject uevent socket: %w", err) |
| 32 | } |
| 33 | defer c.Close() |
| 34 | |
| 35 | l := supervisor.Logger(ctx) |
| 36 | |
| 37 | modMgr, err := kmod.NewManagerFromPath("/lib/modules") |
| 38 | if err != nil { |
| 39 | return fmt.Errorf("error creating module manager: %w", err) |
| 40 | } |
| 41 | |
| 42 | // Start goroutine which instructs the kernel to generate "synthetic" |
| 43 | // uevents for all preexisting devices. This allows the kobject netlink |
| 44 | // listener below to "catch up" on devices added before it was created. |
| 45 | // This functionality is minimally documented in the Linux kernel, the |
| 46 | // closest we have is |
| 47 | // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-uevent which |
| 48 | // contains documentation on how to trigger synthetic events. |
| 49 | go func() { |
| 50 | err = filepath.WalkDir("/sys/devices", func(path string, d fs.DirEntry, err error) error { |
| 51 | if !d.IsDir() && d.Name() == "uevent" { |
| 52 | if err := os.WriteFile(path, []byte("add"), 0); err != nil { |
| 53 | return err |
| 54 | } |
| 55 | } |
| 56 | return nil |
| 57 | }) |
| 58 | if err != nil { |
| 59 | l.Errorf("failed to load initial device database: %v", err) |
| 60 | } else { |
| 61 | l.Info("Initial device loading done") |
| 62 | } |
| 63 | }() |
| 64 | |
| 65 | for { |
| 66 | e, err := c.Receive() |
| 67 | if err != nil { |
| 68 | return fmt.Errorf("error receiving kobject uevent: %w", err) |
| 69 | } |
| 70 | if e.Action == kobject.Add { |
| 71 | if e.Values["MODALIAS"] != "" { |
| 72 | if err := modMgr.LoadModulesForDevice(e.Values["MODALIAS"]); err != nil { |
| 73 | l.Errorf("Error loading kernel modules: %w", err) |
| 74 | } |
| 75 | } |
| 76 | } |
| 77 | } |
| 78 | } |