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 } var exitError *exec.ExitError if !errors.As(err, &exitError) || 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(), }) } }