blob: efad4ecd7866fb74454cc2698535ac0121f03fd7 [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()
Lorenz Brunbafa7bd2025-08-04 13:59:23 +020037 // 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 Brun46bf7d62023-06-01 12:24:19 +020040
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 Bazanski37220252023-06-29 12:39:08 +020078 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 Brun46bf7d62023-06-01 12:24:19 +020083 }
84 }
85 }
86}