blob: 8133c7742056d11d070ef4d89537d521fd0c8a60 [file] [log] [blame]
Lorenz Brun46bf7d62023-06-01 12:24:19 +02001// 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.
7package devmgr
8
9import (
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
22type Service struct{}
23
24func New() *Service {
25 return &Service{}
26}
27
28func (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"] != "" {
Serge Bazanski37220252023-06-29 12:39:08 +020072 go func(e *kobject.Event) {
73 if err := modMgr.LoadModulesForDevice(e.Values["MODALIAS"]); err != nil {
74 l.Errorf("Error loading kernel modules: %w", err)
75 }
76 }(e)
Lorenz Brun46bf7d62023-06-01 12:24:19 +020077 }
78 }
79 }
80}