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