aboutsummaryrefslogtreecommitdiffhomepage
path: root/cmd/earlyinit/modprobe.go
blob: 8addf973312787a5b17cfdac7bb2e27e091c5848 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package main

import (
	"context"
	"errors"
	"fmt"
	"log"
	"os/exec"
	"strings"

	"hakurei.app/internal/kobject"
	"hakurei.app/internal/report"
	"hakurei.app/internal/uevent"
)

// ModprobeError describes an unsuccessful modprobe invocation.
type ModprobeError struct {
	ModAlias string `json:"modalias"`
	Stdout   string `json:"stdout"`
	Stderr   string `json:"stderr"`
	ExitCode int    `json:"exit_code"`
}

var _ report.RepresentableError = ModprobeError{}

func (ModprobeError) Representable() {}
func (e ModprobeError) Error() string {
	return fmt.Sprintf(
		"modprobe exit status %d: %s",
		e.ExitCode, strings.TrimSpace(e.Stderr),
	)
}

// dispatchModprobe invokes modprobe for [uevent.KOBJ_ADD] events raising new
// MODALIAS strings.
func dispatchModprobe(
	ctx context.Context,
	s *kobject.State,
) {
	aliases := make(chan string, 1<<8)
	go func() {
		defer close(aliases)
		s.Range(ctx, func(o *kobject.Object, act uevent.KobjectAction) bool {
			if act == uevent.KOBJ_ADD && o.Driver == "" && o.ModAlias != "" {
				aliases <- o.ModAlias
			}
			return true
		})
	}()

	for alias := range aliases {
		stdout, err := exec.Command("/system/sbin/modprobe", alias).Output()
		if err == nil {
			if len(stdout) > 0 {
				log.Println(string(stdout))
			}
			continue
		}

		exitError, ok := errors.AsType[*exec.ExitError](err)
		if !ok || exitError == nil {
			r.Dispatch(report.Degraded, "invoke modprobe", err)
			continue
		}

		r.Dispatch(report.Trivial, "load device driver", ModprobeError{
			ModAlias: alias,
			Stdout:   string(stdout),
			Stderr:   string(exitError.Stderr),
			ExitCode: exitError.ExitCode(),
		})
	}
}