fix: write correct child process PID to PID file

The Write() method was using os.Getpid() which writes the parent's PID
instead of the spawned child process PID. Added WritePID() method to
explicitly write a specific PID and added process verification after spawn.
This commit is contained in:
Vishnuvardhan Reddy 2026-02-24 18:24:29 +00:00
parent f62439c8d6
commit 998c49e52d
2 changed files with 24 additions and 8 deletions

View file

@ -44,24 +44,30 @@ func NewPIDFile(path string) *PIDFile {
// This prevents multiple instances of the gateway from running simultaneously,
// which could cause resource conflicts and undefined behavior.
func (p *PIDFile) Write() error {
return p.WritePID(os.Getpid())
}
// WritePID atomically writes the specified process ID to the PID file.
// Returns an error if a PID file already exists with a running process.
//
// Use this method when you need to write a PID other than the current process,
// such as when spawning a child process.
func (p *PIDFile) WritePID(pid int) error {
p.mu.Lock()
defer p.mu.Unlock()
// Check for existing PID file
if _, err := os.Stat(p.path); err == nil {
// PID file exists, check if process is running
pid, err := p.read()
if err == nil && p.isProcessRunning(pid) {
existingPid, err := p.read()
if err == nil && p.isProcessRunning(existingPid) {
return &ProcessRunningError{
pid: pid,
pid: existingPid,
Path: p.path,
}
}
// Process is not running, stale PID file, continue
}
// Write PID to temp file in same directory (atomic preparation)
pid := os.Getpid()
pidStr := strconv.Itoa(pid)
tempFile := p.path + ".tmp"

View file

@ -150,14 +150,24 @@ func (s *Service) Start() error {
pid := cmd.Process.Pid
// Write PID file atomically
if err := s.pidFile.Write(); err != nil {
// Write PID file atomically with the child's PID
if err := s.pidFile.WritePID(pid); err != nil {
// Failed to write PID file, kill the process
cmd.Process.Kill()
logFile.Close()
return fmt.Errorf("failed to write PID file: %w", err)
}
// Verify the child process is actually running
// This prevents race conditions where status is checked immediately
time.Sleep(100 * time.Millisecond)
if !s.pidFile.IsProcessRunning() {
// Child process died immediately, likely a startup error
s.pidFile.Remove()
logFile.Close()
return fmt.Errorf("gateway process failed to start (check log file: %s)", s.logConfig.Path)
}
// Update state
s.state.SetPID(pid)
s.state.SetStartTime(time.Now())