aboutsummaryrefslogtreecommitdiffhomepage
path: root/internal/uevent/coldboot.go
blob: 07f6497f98abbf578a1d9a341fbedd54300aa60a (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
package uevent

import (
	"context"
	"errors"
	"io/fs"
	"log"
	"os"
	"path/filepath"
)

// synthAdd is prepared bytes written to uevent to cause a synthetic add event
// to be emitted during coldboot.
var synthAdd = []byte(KOBJ_ADD.String())

// Coldboot writes "add" to every uevent file that it finds in /sys/devices.
// This causes the kernel to regenerate the uevents for these paths.
//
// The specified pathname must present the sysfs root.
//
// Note that while [AOSP documentation] claims to also scan /sys/class and
// /sys/block, this is no longer the case, and the documentation was not updated
// when this changed.
//
// [AOSP documentation]: https://android.googlesource.com/platform/system/core/+/master/init/README.ueventd.md
func Coldboot(
	ctx context.Context,
	pathname string,
	visited chan<- string,
	handleWalkErr func(error) error,
) error {
	if handleWalkErr == nil {
		handleWalkErr = func(err error) error {
			if errors.Is(err, fs.ErrNotExist) {
				log.Println("coldboot", err)
				return nil
			}
			return err
		}
	}

	return filepath.WalkDir(filepath.Join(pathname, "devices"), func(
		path string,
		d fs.DirEntry,
		err error,
	) error {
		if err != nil {
			return handleWalkErr(err)
		}
		if err = ctx.Err(); err != nil {
			return err
		}

		if d.IsDir() || d.Name() != "uevent" {
			return nil
		}
		if err = os.WriteFile(path, synthAdd, 0); err != nil {
			return handleWalkErr(err)
		}

		select {
		case visited <- path:
			break

		case <-ctx.Done():
			return ctx.Err()
		}

		return nil
	})
}