blob: fc626b0790dcbf5071d0f0db265d5e41627b5868 [file] [log] [blame]
// Package devmgr is the userspace pendant to the kernel device management
// system. It talks to the kernel and the performs any further userspace actions
// related to device events. It corresponds very roughly to systemd-udevd on
// more conventional Linux distros. It currently only handles dynamic module
// loading, but will be extended to provide better device handling in other
// parts of the system.
package devmgr
import (
"context"
"fmt"
"io/fs"
"os"
"path/filepath"
"github.com/mdlayher/kobject"
"source.monogon.dev/metropolis/pkg/kmod"
"source.monogon.dev/metropolis/pkg/supervisor"
)
type Service struct{}
func New() *Service {
return &Service{}
}
func (s *Service) Run(ctx context.Context) error {
c, err := kobject.New()
if err != nil {
return fmt.Errorf("unable to create kobject uevent socket: %w", err)
}
defer c.Close()
l := supervisor.Logger(ctx)
modMgr, err := kmod.NewManagerFromPath("/lib/modules")
if err != nil {
return fmt.Errorf("error creating module manager: %w", err)
}
// Start goroutine which instructs the kernel to generate "synthetic"
// uevents for all preexisting devices. This allows the kobject netlink
// listener below to "catch up" on devices added before it was created.
// This functionality is minimally documented in the Linux kernel, the
// closest we have is
// https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-uevent which
// contains documentation on how to trigger synthetic events.
go func() {
err = filepath.WalkDir("/sys/devices", func(path string, d fs.DirEntry, err error) error {
if !d.IsDir() && d.Name() == "uevent" {
if err := os.WriteFile(path, []byte("add"), 0); err != nil {
return err
}
}
return nil
})
if err != nil {
l.Errorf("failed to load initial device database: %v", err)
} else {
l.Info("Initial device loading done")
}
}()
for {
e, err := c.Receive()
if err != nil {
return fmt.Errorf("error receiving kobject uevent: %w", err)
}
if e.Action == kobject.Add {
if e.Values["MODALIAS"] != "" {
if err := modMgr.LoadModulesForDevice(e.Values["MODALIAS"]); err != nil {
l.Errorf("Error loading kernel modules: %w", err)
}
}
}
}
}