blob: 41730fc3dd0e4d2df6c48d087e986864a20f58f1 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Lorenz Brun46bf7d62023-06-01 12:24:19 +02004// 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.
10package devmgr
11
12import (
13 "context"
14 "fmt"
15 "io/fs"
16 "os"
17 "path/filepath"
18
19 "github.com/mdlayher/kobject"
20
Tim Windelschmidt9f21f532024-05-07 15:14:20 +020021 "source.monogon.dev/osbase/kmod"
22 "source.monogon.dev/osbase/supervisor"
Lorenz Brun46bf7d62023-06-01 12:24:19 +020023)
24
25type Service struct{}
26
27func New() *Service {
28 return &Service{}
29}
30
31func (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 Bazanski37220252023-06-29 12:39:08 +020075 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 Brun46bf7d62023-06-01 12:24:19 +020080 }
81 }
82 }
83}