mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
Merge pull request #3000 from chengzhichao-xydt/codex/pid-verify-process-identity
fix(pid): verify process identity in singleton PID check
This commit is contained in:
+8
-1
@@ -64,7 +64,14 @@ func WritePidFile(homePath, host string, port int) (*PidFileData, error) {
|
||||
// pass the isProcessRunning check, blocking new gateway starts.
|
||||
// Treat recorded PID 1 as always stale.
|
||||
if data.PID != 1 && isProcessRunning(data.PID) {
|
||||
return nil, fmt.Errorf("gateway is already running (PID: %d, version: %s)", data.PID, data.Version)
|
||||
// Verify the process is actually a picoclaw instance.
|
||||
// If the PID was reused by an unrelated process
|
||||
// (e.g. systemd-resolved after a kill -9), treat
|
||||
// the PID file as stale and proceed with startup.
|
||||
if isPicoclawProcess(data.PID) {
|
||||
return nil, fmt.Errorf("gateway is already running (PID: %d, version: %s)", data.PID, data.Version)
|
||||
}
|
||||
logger.Warnf("found pid file (PID: %d) but process is not picoclaw", data.PID)
|
||||
}
|
||||
logger.Warnf("not running (PID: %d) so will remove the pid file: %s", data.PID, pidPath)
|
||||
}
|
||||
|
||||
+16
-1
@@ -4,7 +4,9 @@ package pid
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
@@ -18,7 +20,7 @@ func isProcessRunning(pid int) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
// Signal(nil) does not kill the process but checks existence on Unix.
|
||||
// Signal(0) does not kill the process but checks existence on Unix.
|
||||
err = p.Signal(syscall.Signal(0))
|
||||
if err == nil {
|
||||
return true
|
||||
@@ -27,3 +29,16 @@ func isProcessRunning(pid int) bool {
|
||||
// EPERM means the process exists but we are not allowed to signal it.
|
||||
return errors.As(err, &errno) && errno == syscall.EPERM
|
||||
}
|
||||
|
||||
// isPicoclawProcess reads /proc/<pid>/comm to confirm the process name
|
||||
// contains "picoclaw". Returns false when the comm file can be read and
|
||||
// the name does not match (e.g., PID was reused by an unrelated process).
|
||||
// Returns true if /proc/<pid>/comm is unreadable so the call site falls
|
||||
// back to trusting the liveness check alone.
|
||||
func isPicoclawProcess(pid int) bool {
|
||||
data, err := os.ReadFile(fmt.Sprintf("/proc/%d/comm", pid))
|
||||
if err != nil {
|
||||
return true // cannot verify — trust liveness check
|
||||
}
|
||||
return strings.Contains(strings.TrimSpace(string(data)), "picoclaw")
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package pid
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -12,6 +13,7 @@ var (
|
||||
procOpenProcess = kernel32.NewProc("OpenProcess")
|
||||
procGetExitCodeProcess = kernel32.NewProc("GetExitCodeProcess")
|
||||
procCloseHandle = kernel32.NewProc("CloseHandle")
|
||||
procQueryFullProcessImageNameW = kernel32.NewProc("QueryFullProcessImageNameW")
|
||||
processQueryLimitedInformation = uint32(0x1000)
|
||||
stillActive = uint32(259)
|
||||
)
|
||||
@@ -40,3 +42,33 @@ func isProcessRunning(pid int) bool {
|
||||
}
|
||||
return exitCode == stillActive
|
||||
}
|
||||
|
||||
// isPicoclawProcess uses QueryFullProcessImageNameW to confirm the
|
||||
// process image name contains "picoclaw". Returns false when the name
|
||||
// clearly does not match. Returns true if the query fails, falling
|
||||
// back to trusting the liveness check alone.
|
||||
func isPicoclawProcess(pid int) bool {
|
||||
handle, _, _ := procOpenProcess.Call(
|
||||
uintptr(processQueryLimitedInformation),
|
||||
0,
|
||||
uintptr(pid),
|
||||
)
|
||||
if handle == 0 {
|
||||
return true // cannot open — trust liveness check
|
||||
}
|
||||
defer procCloseHandle.Call(handle)
|
||||
|
||||
var buf [260]uint16
|
||||
var size uint32 = 260
|
||||
ret, _, _ := procQueryFullProcessImageNameW.Call(
|
||||
uintptr(handle),
|
||||
0, // WIN32_NAME_FORMAT
|
||||
uintptr(unsafe.Pointer(&buf[0])),
|
||||
uintptr(unsafe.Pointer(&size)),
|
||||
)
|
||||
if ret == 0 {
|
||||
return true // cannot verify — trust liveness check
|
||||
}
|
||||
name := strings.ToLower(syscall.UTF16ToString(buf[:size]))
|
||||
return strings.Contains(name, "picoclaw")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user