From 12c61899f54f9c6fa055f871603b2deb84b7a925 Mon Sep 17 00:00:00 2001 From: dj-oyu <68707227+dj-oyu@users.noreply.github.com> Date: Thu, 5 Mar 2026 02:16:07 +0900 Subject: [PATCH] style: fix whitespace/gofumpt/golines in linux/unix build-tagged files Co-Authored-By: Claude Opus 4.6 --- pkg/tools/i2c_linux.go | 238 ++++----------------------- pkg/tools/shell_process_unix.go | 53 ------ pkg/tools/shell_timeout_unix_test.go | 53 ------ pkg/tools/spi_linux.go | 196 ++++------------------ 4 files changed, 63 insertions(+), 477 deletions(-) diff --git a/pkg/tools/i2c_linux.go b/pkg/tools/i2c_linux.go index 8b710f37a..aa0c06eb7 100644 --- a/pkg/tools/i2c_linux.go +++ b/pkg/tools/i2c_linux.go @@ -8,465 +8,285 @@ import ( ) // I2C ioctl constants from Linux kernel headers (, ) - const ( i2cSlave = 0x0703 // Set slave address (fails if in use by driver) - i2cFuncs = 0x0705 // Query adapter functionality bitmask - i2cSmbus = 0x0720 // Perform SMBus transaction // I2C_FUNC capability bits - - i2cFuncSmbusQuick = 0x00010000 - + i2cFuncSmbusQuick = 0x00010000 i2cFuncSmbusReadByte = 0x00020000 // SMBus transaction types - - i2cSmbusRead = 0 - + i2cSmbusRead = 0 i2cSmbusWrite = 1 // SMBus protocol sizes - i2cSmbusQuick = 0 - - i2cSmbusByte = 1 + i2cSmbusByte = 1 ) // i2cSmbusData matches the kernel union i2c_smbus_data (34 bytes max). - // For quick and byte transactions only the first byte is used (if at all). - type i2cSmbusData [34]byte // i2cSmbusArgs matches the kernel struct i2c_smbus_ioctl_data. - type i2cSmbusArgs struct { readWrite uint8 - - command uint8 - - size uint32 - - data *i2cSmbusData + command uint8 + size uint32 + data *i2cSmbusData } // smbusProbe performs a single SMBus probe at the given address. - // Uses SMBus Quick Write (safest) or falls back to SMBus Read Byte for - // EEPROM address ranges where quick write can corrupt AT24RF08 chips. - // This matches i2cdetect's MODE_AUTO behavior. - func smbusProbe(fd int, addr int, hasQuick bool) bool { - // EEPROM ranges: use read byte (quick write can corrupt AT24RF08) - useReadByte := (addr >= 0x30 && addr <= 0x37) || (addr >= 0x50 && addr <= 0x5F) if !useReadByte && hasQuick { - // SMBus Quick Write: [START] [ADDR|W] [ACK/NACK] [STOP] - // Safest probe — no data transferred - args := i2cSmbusArgs{ - readWrite: i2cSmbusWrite, - - command: 0, - - size: i2cSmbusQuick, - - data: nil, + command: 0, + size: i2cSmbusQuick, + data: nil, } - _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), i2cSmbus, uintptr(unsafe.Pointer(&args))) - return errno == 0 - } // SMBus Read Byte: [START] [ADDR|R] [ACK/NACK] [DATA] [STOP] - var data i2cSmbusData - args := i2cSmbusArgs{ - readWrite: i2cSmbusRead, - - command: 0, - - size: i2cSmbusByte, - - data: &data, + command: 0, + size: i2cSmbusByte, + data: &data, } - _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), i2cSmbus, uintptr(unsafe.Pointer(&args))) - return errno == 0 - } // scan probes valid 7-bit addresses on a bus for connected devices. - // Uses the same hybrid probe strategy as i2cdetect's MODE_AUTO: - // SMBus Quick Write for most addresses, SMBus Read Byte for EEPROM ranges. - func (t *I2CTool) scan(args map[string]any) *ToolResult { - bus, errResult := parseI2CBus(args) - if errResult != nil { - return errResult - } devPath := fmt.Sprintf("/dev/i2c-%s", bus) - fd, err := syscall.Open(devPath, syscall.O_RDWR, 0) - if err != nil { - return ErrorResult(fmt.Sprintf("failed to open %s: %v (check permissions and i2c-dev module)", devPath, err)) - } - defer syscall.Close(fd) // Query adapter capabilities to determine available probe methods. - // I2C_FUNCS writes an unsigned long, which is word-sized on Linux. - var funcs uintptr - _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), i2cFuncs, uintptr(unsafe.Pointer(&funcs))) - if errno != 0 { - return ErrorResult(fmt.Sprintf("failed to query I2C adapter capabilities on %s: %v", devPath, errno)) - } hasQuick := funcs&i2cFuncSmbusQuick != 0 - hasReadByte := funcs&i2cFuncSmbusReadByte != 0 - if !hasQuick && !hasReadByte { - return ErrorResult( - - fmt.Sprintf("I2C adapter %s supports neither SMBus Quick nor Read Byte — cannot probe safely", devPath), + fmt.Sprintf( + "I2C adapter %s supports neither SMBus Quick nor Read Byte — cannot probe safely", + devPath, + ), ) - } type deviceEntry struct { Address string `json:"address"` - - Status string `json:"status,omitempty"` + Status string `json:"status,omitempty"` } var found []deviceEntry // Scan 0x08-0x77, skipping I2C reserved addresses 0x00-0x07 - for addr := 0x08; addr <= 0x77; addr++ { - // Set slave address — EBUSY means a kernel driver owns this address - _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), i2cSlave, uintptr(addr)) - if errno != 0 { - if errno == syscall.EBUSY { - found = append(found, deviceEntry{ - Address: fmt.Sprintf("0x%02x", addr), - - Status: "busy (in use by kernel driver)", + Status: "busy (in use by kernel driver)", }) - } - continue - } - if smbusProbe(fd, addr, hasQuick) { - found = append(found, deviceEntry{ - Address: fmt.Sprintf("0x%02x", addr), }) - } - } if len(found) == 0 { - return SilentResult(fmt.Sprintf("No devices found on %s. Check wiring and pull-up resistors.", devPath)) - } result, _ := json.MarshalIndent(map[string]any{ - - "bus": devPath, - + "bus": devPath, "devices": found, - - "count": len(found), + "count": len(found), }, "", " ") return SilentResult(fmt.Sprintf("Scan of %s:\n%s", devPath, string(result))) - } -// readDevice reads bytes from an I2C device, optionally at a specific register - +// readDevice reads bytes from an I2C device, optionally at a specific register. func (t *I2CTool) readDevice(args map[string]any) *ToolResult { - bus, errResult := parseI2CBus(args) - if errResult != nil { - return errResult - } addr, errResult := parseI2CAddress(args) - if errResult != nil { - return errResult - } length := 1 - if l, ok := args["length"].(float64); ok { - length = int(l) - } - if length < 1 || length > 256 { - return ErrorResult("length must be between 1 and 256") - } devPath := fmt.Sprintf("/dev/i2c-%s", bus) - fd, err := syscall.Open(devPath, syscall.O_RDWR, 0) - if err != nil { - return ErrorResult(fmt.Sprintf("failed to open %s: %v", devPath, err)) - } - defer syscall.Close(fd) // Set slave address - _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), i2cSlave, uintptr(addr)) - if errno != 0 { - return ErrorResult(fmt.Sprintf("failed to set I2C address 0x%02x: %v", addr, errno)) - } // If register is specified, write it first - if regFloat, ok := args["register"].(float64); ok { - reg := int(regFloat) - if reg < 0 || reg > 255 { - return ErrorResult("register must be between 0x00 and 0xFF") - } - _, err = syscall.Write(fd, []byte{byte(reg)}) - if err != nil { - return ErrorResult(fmt.Sprintf("failed to write register 0x%02x: %v", reg, err)) - } - } // Read data - buf := make([]byte, length) - n, err := syscall.Read(fd, buf) - if err != nil { - return ErrorResult(fmt.Sprintf("failed to read from device 0x%02x: %v", addr, err)) - } // Format as hex bytes - hexBytes := make([]string, n) - intBytes := make([]int, n) - for i := 0; i < n; i++ { - hexBytes[i] = fmt.Sprintf("0x%02x", buf[i]) - intBytes[i] = int(buf[i]) - } result, _ := json.MarshalIndent(map[string]any{ - - "bus": devPath, - + "bus": devPath, "address": fmt.Sprintf("0x%02x", addr), - - "bytes": intBytes, - - "hex": hexBytes, - - "length": n, + "bytes": intBytes, + "hex": hexBytes, + "length": n, }, "", " ") return SilentResult(string(result)) - } -// writeDevice writes bytes to an I2C device, optionally at a specific register - +// writeDevice writes bytes to an I2C device, optionally at a specific register. func (t *I2CTool) writeDevice(args map[string]any) *ToolResult { - confirm, _ := args["confirm"].(bool) - if !confirm { - return ErrorResult( - - "write operations require confirm: true. Please confirm with the user before writing to I2C devices, as incorrect writes can misconfigure hardware.", + "write operations require confirm: true." + + " Please confirm with the user before writing to I2C devices," + + " as incorrect writes can misconfigure hardware.", ) - } bus, errResult := parseI2CBus(args) - if errResult != nil { - return errResult - } addr, errResult := parseI2CAddress(args) - if errResult != nil { - return errResult - } dataRaw, ok := args["data"].([]any) - if !ok || len(dataRaw) == 0 { - return ErrorResult("data is required for write (array of byte values 0-255)") - } - if len(dataRaw) > 256 { - return ErrorResult("data too long: maximum 256 bytes per I2C transaction") - } data := make([]byte, 0, len(dataRaw)+1) // If register is specified, prepend it to the data - if regFloat, ok := args["register"].(float64); ok { - reg := int(regFloat) - if reg < 0 || reg > 255 { - return ErrorResult("register must be between 0x00 and 0xFF") - } - data = append(data, byte(reg)) - } for i, v := range dataRaw { - f, ok := v.(float64) - if !ok { - return ErrorResult(fmt.Sprintf("data[%d] is not a valid byte value", i)) - } - b := int(f) - if b < 0 || b > 255 { - return ErrorResult(fmt.Sprintf("data[%d] = %d is out of byte range (0-255)", i, b)) - } - data = append(data, byte(b)) - } devPath := fmt.Sprintf("/dev/i2c-%s", bus) - fd, err := syscall.Open(devPath, syscall.O_RDWR, 0) - if err != nil { - return ErrorResult(fmt.Sprintf("failed to open %s: %v", devPath, err)) - } - defer syscall.Close(fd) // Set slave address - _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), i2cSlave, uintptr(addr)) - if errno != 0 { - return ErrorResult(fmt.Sprintf("failed to set I2C address 0x%02x: %v", addr, errno)) - } // Write data - n, err := syscall.Write(fd, data) - if err != nil { - return ErrorResult(fmt.Sprintf("failed to write to device 0x%02x: %v", addr, err)) - } return SilentResult(fmt.Sprintf("Wrote %d byte(s) to device 0x%02x on %s", n, addr, devPath)) - } diff --git a/pkg/tools/shell_process_unix.go b/pkg/tools/shell_process_unix.go index d513f87ad..5d1f52951 100644 --- a/pkg/tools/shell_process_unix.go +++ b/pkg/tools/shell_process_unix.go @@ -11,129 +11,76 @@ import ( ) func prepareCommandForTermination(cmd *exec.Cmd) { - if cmd == nil { - return - } - cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} - } func terminateProcessTree(cmd *exec.Cmd) error { - if cmd == nil || cmd.Process == nil { - return nil - } - pid := cmd.Process.Pid - if pid <= 0 { - return nil - } // Kill the entire process group spawned by the shell command. - _ = syscall.Kill(-pid, syscall.SIGKILL) // Some shells/background jobs may still leave descendants around - // briefly; aggressively walk /proc and kill child processes too. - killDescendants(pid) // Fallback kill on the shell process itself. - _ = cmd.Process.Kill() return nil - } func killDescendants(ppid int) { - if ppid <= 0 { - return - } - entries, err := os.ReadDir("/proc") - if err != nil { - return - } for _, e := range entries { - if !e.IsDir() { - continue - } - childPID, err := strconv.Atoi(e.Name()) - if err != nil || childPID <= 0 || childPID == ppid { - continue - } statPath := "/proc/" + e.Name() + "/stat" - data, err := os.ReadFile(statPath) - if err != nil { - continue - } // /proc//stat: pid (comm) state ppid ... - raw := string(data) - end := strings.LastIndex(raw, ")") - if end == -1 || end+2 >= len(raw) { - continue - } - fields := strings.Fields(raw[end+2:]) - if len(fields) < 2 { - continue - } - parent, err := strconv.Atoi(fields[1]) - if err != nil || parent != ppid { - continue - } // Recurse first, then kill child process/group. - killDescendants(childPID) - _ = syscall.Kill(-childPID, syscall.SIGKILL) - _ = syscall.Kill(childPID, syscall.SIGKILL) - } - } diff --git a/pkg/tools/shell_timeout_unix_test.go b/pkg/tools/shell_timeout_unix_test.go index 5dd861442..748a055e8 100644 --- a/pkg/tools/shell_timeout_unix_test.go +++ b/pkg/tools/shell_timeout_unix_test.go @@ -14,122 +14,69 @@ import ( ) func processRunning(pid int) bool { - if pid <= 0 { - return false - } // kill(0) can return success for zombie processes too, so inspect /proc - // state and treat zombies as not-running for timeout cleanup assertions. - err := syscall.Kill(pid, 0) - if err != nil && err != syscall.EPERM { - return false - } data, readErr := os.ReadFile("/proc/" + strconv.Itoa(pid) + "/stat") - if readErr != nil { - return false - } - raw := string(data) - end := strings.LastIndex(raw, ")") - if end == -1 || end+2 >= len(raw) { - return true // best effort fallback - } - fields := strings.Fields(raw[end+2:]) - if len(fields) == 0 { - return true // best effort fallback - } - state := fields[0] - return state != "Z" - } func TestShellTool_TimeoutKillsChildProcess(t *testing.T) { - tool, err := NewExecTool(t.TempDir(), false) - if err != nil { - t.Errorf("unable to configure exec tool: %s", err) - } - tool.SetTimeout(500 * time.Millisecond) args := map[string]any{ - // Spawn a child process that would outlive the shell unless process-group kill is used. - "command": "sleep 60 & echo $! > child.pid; wait", } - result := tool.Execute(context.Background(), args) - if !result.IsError { - t.Fatalf("expected timeout error, got success: %s", result.ForLLM) - } - if !strings.Contains(result.ForLLM, "timed out") { - t.Fatalf("expected timeout message, got: %s", result.ForLLM) - } childPIDPath := filepath.Join(tool.workingDir, "child.pid") - data, err := os.ReadFile(childPIDPath) - if err != nil { - t.Fatalf("failed to read child pid file: %v", err) - } - childPID, err := strconv.Atoi(strings.TrimSpace(string(data))) - if err != nil { - t.Fatalf("failed to parse child pid: %v", err) - } deadline := time.Now().Add(2 * time.Second) - for time.Now().Before(deadline) { - if !processRunning(childPID) { - return - } - time.Sleep(50 * time.Millisecond) - } - t.Fatalf("child process %d is still running after timeout", childPID) - } diff --git a/pkg/tools/spi_linux.go b/pkg/tools/spi_linux.go index b023df0b3..450407d0f 100644 --- a/pkg/tools/spi_linux.go +++ b/pkg/tools/spi_linux.go @@ -9,321 +9,193 @@ import ( ) // SPI ioctl constants from Linux kernel headers. - // Calculated from _IOW('k', nr, size) macro: - // - // direction(1)<<30 | size<<16 | type(0x6B)<<8 | nr - const ( - spiIocWrMode = 0x40016B01 // _IOW('k', 1, __u8) - + spiIocWrMode = 0x40016B01 // _IOW('k', 1, __u8) spiIocWrBitsPerWord = 0x40016B03 // _IOW('k', 3, __u8) - - spiIocWrMaxSpeedHz = 0x40046B04 // _IOW('k', 4, __u32) - - spiIocMessage1 = 0x40206B00 // _IOW('k', 0, struct spi_ioc_transfer) — 32 bytes - + spiIocWrMaxSpeedHz = 0x40046B04 // _IOW('k', 4, __u32) + spiIocMessage1 = 0x40206B00 // _IOW('k', 0, struct spi_ioc_transfer) — 32 bytes ) // spiTransfer matches Linux kernel struct spi_ioc_transfer (32 bytes on all architectures). - type spiTransfer struct { - txBuf uint64 - - rxBuf uint64 - - length uint32 - - speedHz uint32 - - delayUsecs uint16 - + txBuf uint64 + rxBuf uint64 + length uint32 + speedHz uint32 + delayUsecs uint16 bitsPerWord uint8 - - csChange uint8 - - txNbits uint8 - - rxNbits uint8 - - wordDelay uint8 - - pad uint8 + csChange uint8 + txNbits uint8 + rxNbits uint8 + wordDelay uint8 + pad uint8 } -// configureSPI opens an SPI device and sets mode, bits per word, and speed - -func configureSPI(devPath string, mode uint8, bits uint8, speed uint32) (int, *ToolResult) { - +// configureSPI opens an SPI device and sets mode, bits per word, and speed. +func configureSPI( + devPath string, mode uint8, bits uint8, speed uint32, +) (int, *ToolResult) { fd, err := syscall.Open(devPath, syscall.O_RDWR, 0) - if err != nil { - return -1, ErrorResult(fmt.Sprintf("failed to open %s: %v (check permissions and spidev module)", devPath, err)) - } // Set SPI mode - _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), spiIocWrMode, uintptr(unsafe.Pointer(&mode))) - if errno != 0 { - syscall.Close(fd) - return -1, ErrorResult(fmt.Sprintf("failed to set SPI mode %d: %v", mode, errno)) - } // Set bits per word - _, _, errno = syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), spiIocWrBitsPerWord, uintptr(unsafe.Pointer(&bits))) - if errno != 0 { - syscall.Close(fd) - return -1, ErrorResult(fmt.Sprintf("failed to set bits per word %d: %v", bits, errno)) - } // Set max speed - _, _, errno = syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), spiIocWrMaxSpeedHz, uintptr(unsafe.Pointer(&speed))) - if errno != 0 { - syscall.Close(fd) - return -1, ErrorResult(fmt.Sprintf("failed to set SPI speed %d Hz: %v", speed, errno)) - } return fd, nil - } -// transfer performs a full-duplex SPI transfer - +// transfer performs a full-duplex SPI transfer. func (t *SPITool) transfer(args map[string]any) *ToolResult { - confirm, _ := args["confirm"].(bool) - if !confirm { - return ErrorResult( - - "transfer operations require confirm: true. Please confirm with the user before sending data to SPI devices.", + "transfer operations require confirm: true." + + " Please confirm with the user before sending data to SPI devices.", ) - } dev, speed, mode, bits, errMsg := parseSPIArgs(args) - if errMsg != "" { - return ErrorResult(errMsg) - } dataRaw, ok := args["data"].([]any) - if !ok || len(dataRaw) == 0 { - return ErrorResult("data is required for transfer (array of byte values 0-255)") - } - if len(dataRaw) > 4096 { - return ErrorResult("data too long: maximum 4096 bytes per SPI transfer") - } txBuf := make([]byte, len(dataRaw)) - for i, v := range dataRaw { - f, ok := v.(float64) - if !ok { - return ErrorResult(fmt.Sprintf("data[%d] is not a valid byte value", i)) - } - b := int(f) - if b < 0 || b > 255 { - return ErrorResult(fmt.Sprintf("data[%d] = %d is out of byte range (0-255)", i, b)) - } - txBuf[i] = byte(b) - } devPath := fmt.Sprintf("/dev/spidev%s", dev) - fd, errResult := configureSPI(devPath, mode, bits, speed) - if errResult != nil { - return errResult - } - defer syscall.Close(fd) rxBuf := make([]byte, len(txBuf)) - xfer := spiTransfer{ - - txBuf: uint64(uintptr(unsafe.Pointer(&txBuf[0]))), - - rxBuf: uint64(uintptr(unsafe.Pointer(&rxBuf[0]))), - - length: uint32(len(txBuf)), - - speedHz: speed, - + txBuf: uint64(uintptr(unsafe.Pointer(&txBuf[0]))), + rxBuf: uint64(uintptr(unsafe.Pointer(&rxBuf[0]))), + length: uint32(len(txBuf)), + speedHz: speed, bitsPerWord: bits, } _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), spiIocMessage1, uintptr(unsafe.Pointer(&xfer))) - runtime.KeepAlive(txBuf) - runtime.KeepAlive(rxBuf) - if errno != 0 { - return ErrorResult(fmt.Sprintf("SPI transfer failed: %v", errno)) - } // Format received bytes - hexBytes := make([]string, len(rxBuf)) - intBytes := make([]int, len(rxBuf)) - for i, b := range rxBuf { - hexBytes[i] = fmt.Sprintf("0x%02x", b) - intBytes[i] = int(b) - } result, _ := json.MarshalIndent(map[string]any{ - - "device": devPath, - - "sent": len(txBuf), - + "device": devPath, + "sent": len(txBuf), "received": intBytes, - - "hex": hexBytes, + "hex": hexBytes, }, "", " ") return SilentResult(string(result)) - } -// readDevice reads bytes from SPI by sending zeros (read-only, no confirm needed) - +// readDevice reads bytes from SPI by sending zeros (read-only, no confirm needed). func (t *SPITool) readDevice(args map[string]any) *ToolResult { - dev, speed, mode, bits, errMsg := parseSPIArgs(args) - if errMsg != "" { - return ErrorResult(errMsg) - } length := 0 - if l, ok := args["length"].(float64); ok { - length = int(l) - } - if length < 1 || length > 4096 { - return ErrorResult("length is required for read (1-4096)") - } devPath := fmt.Sprintf("/dev/spidev%s", dev) - fd, errResult := configureSPI(devPath, mode, bits, speed) - if errResult != nil { - return errResult - } - defer syscall.Close(fd) txBuf := make([]byte, length) // zeros - rxBuf := make([]byte, length) - xfer := spiTransfer{ - - txBuf: uint64(uintptr(unsafe.Pointer(&txBuf[0]))), - - rxBuf: uint64(uintptr(unsafe.Pointer(&rxBuf[0]))), - - length: uint32(length), - - speedHz: speed, - + txBuf: uint64(uintptr(unsafe.Pointer(&txBuf[0]))), + rxBuf: uint64(uintptr(unsafe.Pointer(&rxBuf[0]))), + length: uint32(length), + speedHz: speed, bitsPerWord: bits, } _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), spiIocMessage1, uintptr(unsafe.Pointer(&xfer))) - runtime.KeepAlive(txBuf) - runtime.KeepAlive(rxBuf) - if errno != 0 { - return ErrorResult(fmt.Sprintf("SPI read failed: %v", errno)) - } hexBytes := make([]string, len(rxBuf)) - intBytes := make([]int, len(rxBuf)) - for i, b := range rxBuf { - hexBytes[i] = fmt.Sprintf("0x%02x", b) - intBytes[i] = int(b) - } result, _ := json.MarshalIndent(map[string]any{ - "device": devPath, - - "bytes": intBytes, - - "hex": hexBytes, - + "bytes": intBytes, + "hex": hexBytes, "length": len(rxBuf), }, "", " ") return SilentResult(string(result)) - }