|
| 1 | +// This Source Code Form is subject to the terms of the Mozilla Public |
| 2 | +// License, v. 2.0. If a copy of the MPL was not distributed with this |
| 3 | +// file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 4 | +package main |
| 5 | + |
| 6 | +import ( |
| 7 | + "bufio" |
| 8 | + "fmt" |
| 9 | + "log" |
| 10 | + "os" |
| 11 | + "strconv" |
| 12 | + "strings" |
| 13 | + "syscall" // Primarily for Mknod and Stat_t |
| 14 | + "time" |
| 15 | + |
| 16 | + // Import the unix package from x/sys for Mkdev, Major, Minor |
| 17 | + // You'll need to run 'go get golang.org/x/sys/unix' |
| 18 | + "golang.org/x/sys/unix" |
| 19 | +) |
| 20 | + |
| 21 | +const ( |
| 22 | + deviceName = "gdrdrv" // The driver name we're looking for |
| 23 | + devicePath = "/dev/" + deviceName |
| 24 | + procDevicesPath = "/proc/devices" // File containing major numbers |
| 25 | + targetPerms = 0o666 // Desired file permissions (rw-rw-rw-) |
| 26 | + minorNumber = 0 // Standard minor number for gdrdrv |
| 27 | + retryDelay = 1 * time.Second // How long to wait between checks |
| 28 | +) |
| 29 | + |
| 30 | +func findDeviceMajor() (int, error) { |
| 31 | + file, err := os.Open(procDevicesPath) |
| 32 | + if err != nil { |
| 33 | + return -1, fmt.Errorf("error opening %s: %w", procDevicesPath, err) |
| 34 | + } |
| 35 | + defer file.Close() |
| 36 | + |
| 37 | + scanner := bufio.NewScanner(file) |
| 38 | + for scanner.Scan() { |
| 39 | + line := strings.TrimSpace(scanner.Text()) |
| 40 | + fields := strings.Fields(line) |
| 41 | + |
| 42 | + if len(fields) == 2 && fields[1] == deviceName { |
| 43 | + major, err := strconv.Atoi(fields[0]) |
| 44 | + if err != nil { |
| 45 | + return -1, fmt.Errorf("error parsing major number '%s' for %s: %w", fields[0], deviceName, err) |
| 46 | + } |
| 47 | + return major, nil |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + if err := scanner.Err(); err != nil { |
| 52 | + return -1, fmt.Errorf("error reading %s: %w", procDevicesPath, err) |
| 53 | + } |
| 54 | + |
| 55 | + return -1, fmt.Errorf("device '%s' not found in %s", deviceName, procDevicesPath) |
| 56 | +} |
| 57 | + |
| 58 | +func main() { |
| 59 | + log.SetPrefix("gdrdrv-mknod: ") |
| 60 | + log.Printf("waiting for %s driver to register in %s...", deviceName, procDevicesPath) |
| 61 | + |
| 62 | + var major int |
| 63 | + var err error |
| 64 | + |
| 65 | + // Loop indefinitely until the major number is found |
| 66 | + for { |
| 67 | + major, err = findDeviceMajor() |
| 68 | + if err == nil { |
| 69 | + log.Printf("Found %s major number: %d", deviceName, major) |
| 70 | + break |
| 71 | + } |
| 72 | + // Log the error and wait before retrying |
| 73 | + log.Printf("waiting for device... (%v)", err) |
| 74 | + time.Sleep(retryDelay) |
| 75 | + } |
| 76 | + |
| 77 | + // Attempt to remove the device node if it already exists |
| 78 | + log.Printf("checking for existing device node at %s...", devicePath) |
| 79 | + if _, err := os.Stat(devicePath); err == nil { |
| 80 | + log.Printf("removing existing %s...", devicePath) |
| 81 | + if err := os.Remove(devicePath); err != nil { |
| 82 | + log.Fatalf("failed to remove existing %s: %v", devicePath, err) |
| 83 | + } |
| 84 | + } else if !os.IsNotExist(err) { |
| 85 | + // If stat failed for a reason other than the file not existing, it's a problem |
| 86 | + log.Fatalf("error: checking status of %s: %v", devicePath, err) |
| 87 | + } |
| 88 | + |
| 89 | + log.Printf("creating character device %s (Major: %d, Minor: %d)...", devicePath, major, minorNumber) |
| 90 | + |
| 91 | + mode := uint32(syscall.S_IFCHR | targetPerms) |
| 92 | + |
| 93 | + // Combine major and minor numbers into the format needed by Mknod |
| 94 | + // We use unix.Mkdev from the x/sys/unix package |
| 95 | + dev := unix.Mkdev(uint32(major), uint32(minorNumber)) |
| 96 | + |
| 97 | + // Execute the Mknod syscall |
| 98 | + if err := syscall.Mknod(devicePath, mode, int(dev)); err != nil { |
| 99 | + log.Fatalf("Failed to create device node with mknod: %v", err) |
| 100 | + } |
| 101 | + |
| 102 | + // Mknod *should* set permissions, but explicitly call Chmod for robustness |
| 103 | + log.Printf("Setting permissions on %s to %#o...", devicePath, targetPerms) |
| 104 | + if err := os.Chmod(devicePath, targetPerms); err != nil { |
| 105 | + log.Printf("Warning: Failed to set permissions with chmod (mknod might have already set them): %v", err) |
| 106 | + } |
| 107 | + |
| 108 | + // Verify creation and permissions by stating the file (like ls -l does) |
| 109 | + fileInfo, err := os.Stat(devicePath) |
| 110 | + if err != nil { |
| 111 | + log.Fatalf("Failed to stat the created device %s: %v", devicePath, err) |
| 112 | + } |
| 113 | + |
| 114 | + // Extract major/minor from stat info for confirmation log |
| 115 | + devDetails := "" |
| 116 | + if stat_t, ok := fileInfo.Sys().(*syscall.Stat_t); ok { |
| 117 | + maj := unix.Major(uint64(stat_t.Rdev)) |
| 118 | + min := unix.Minor(uint64(stat_t.Rdev)) |
| 119 | + devDetails = fmt.Sprintf(" (Major: %d, Minor: %d)", maj, min) |
| 120 | + } |
| 121 | + |
| 122 | + log.Printf("Successfully created %s: Mode=%s%s", |
| 123 | + devicePath, |
| 124 | + fileInfo.Mode().String(), |
| 125 | + devDetails, |
| 126 | + ) |
| 127 | +} |
0 commit comments