| 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() |
| Lorenz Brun | bafa7bd | 2025-08-04 13:59:23 +0200 | [diff] [blame^] | 37 | // The default buffer is very small, use a larger one to not constantly |
| 38 | // run out of buffer space needing a restart of this runnable. |
| 39 | c.SetReadBuffer(4 * 1024 * 1024) |
| Lorenz Brun | 46bf7d6 | 2023-06-01 12:24:19 +0200 | [diff] [blame] | 40 | |
| 41 | l := supervisor.Logger(ctx) |
| 42 | |
| 43 | modMgr, err := kmod.NewManagerFromPath("/lib/modules") |
| 44 | if err != nil { |
| 45 | return fmt.Errorf("error creating module manager: %w", err) |
| 46 | } |
| 47 | |
| 48 | // Start goroutine which instructs the kernel to generate "synthetic" |
| 49 | // uevents for all preexisting devices. This allows the kobject netlink |
| 50 | // listener below to "catch up" on devices added before it was created. |
| 51 | // This functionality is minimally documented in the Linux kernel, the |
| 52 | // closest we have is |
| 53 | // https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-uevent which |
| 54 | // contains documentation on how to trigger synthetic events. |
| 55 | go func() { |
| 56 | err = filepath.WalkDir("/sys/devices", func(path string, d fs.DirEntry, err error) error { |
| 57 | if !d.IsDir() && d.Name() == "uevent" { |
| 58 | if err := os.WriteFile(path, []byte("add"), 0); err != nil { |
| 59 | return err |
| 60 | } |
| 61 | } |
| 62 | return nil |
| 63 | }) |
| 64 | if err != nil { |
| 65 | l.Errorf("failed to load initial device database: %v", err) |
| 66 | } else { |
| 67 | l.Info("Initial device loading done") |
| 68 | } |
| 69 | }() |
| 70 | |
| 71 | for { |
| 72 | e, err := c.Receive() |
| 73 | if err != nil { |
| 74 | return fmt.Errorf("error receiving kobject uevent: %w", err) |
| 75 | } |
| 76 | if e.Action == kobject.Add { |
| 77 | if e.Values["MODALIAS"] != "" { |
| Serge Bazanski | 3722025 | 2023-06-29 12:39:08 +0200 | [diff] [blame] | 78 | go func(e *kobject.Event) { |
| 79 | if err := modMgr.LoadModulesForDevice(e.Values["MODALIAS"]); err != nil { |
| 80 | l.Errorf("Error loading kernel modules: %w", err) |
| 81 | } |
| 82 | }(e) |
| Lorenz Brun | 46bf7d6 | 2023-06-01 12:24:19 +0200 | [diff] [blame] | 83 | } |
| 84 | } |
| 85 | } |
| 86 | } |