Skip to content

Instantly share code, notes, and snippets.

@davidnewhall
Last active October 13, 2023 08:07
Show Gist options
  • Star 12 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save davidnewhall/3627895a9fc8fa0affbd747183abca39 to your computer and use it in GitHub Desktop.
Save davidnewhall/3627895a9fc8fa0affbd747183abca39 to your computer and use it in GitHub Desktop.
How to write a PID file in Golang.
// Write a pid file, but first make sure it doesn't exist with a running pid.
func writePidFile(pidFile string) error {
// Read in the pid file as a slice of bytes.
if piddata, err := ioutil.ReadFile(pidFile); err == nil {
// Convert the file contents to an integer.
if pid, err := strconv.Atoi(string(piddata)); err == nil {
// Look for the pid in the process list.
if process, err := os.FindProcess(pid); err == nil {
// Send the process a signal zero kill.
if err := process.Signal(syscall.Signal(0)); err == nil {
// We only get an error if the pid isn't running, or it's not ours.
return fmt.Errorf("pid already running: %d", pid)
}
}
}
}
// If we get here, then the pidfile didn't exist,
// or the pid in it doesn't belong to the user running this app.
return ioutil.WriteFile(pidFile, []byte(fmt.Sprintf("%d", os.Getpid())), 0664)
}
@davidnewhall
Copy link
Author

fwiw, this is not how I would do this now days. This is very anti-go, but it works well....

@sarumaj
Copy link

sarumaj commented Oct 13, 2023

I would like to attach an example of simple PID locker:

type processLockFile struct {
	*os.File
}

func (p processLockFile) Unlock() error {
	path := p.File.Name()
	if err := p.File.Close(); err != nil {
		return err
	}

	return os.Remove(path)
}

func AcquireProcessIDLock(pidFilePath string) (interface{ Unlock() }, error)  {
	if  _, err := os.Stat(pidFilePath); !os.IsNotExist(err) {
		raw, err := os.ReadFile(pidFilePath)
		if err != nil {
			return nil, err
		}

		pid, err := strconv.Atoi(string(raw))
		if err != nil {
			return nil, err
		}

		if proc, err := os.FindProcess(int(pid)); err == nil && !errors.Is(proc.Signal(syscall.Signal(0)), os.ErrProcessDone) {
			fmt.Fprintf(os.Stderr, "Process %d is already running!\n", proc.Pid)

		} else if err = os.Remove(pidFilePath);  err != nil {
			return nil, err

		}

	}

	f, err := os.OpenFile(fpidFilePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, os.ModePerm)
	if err != nil {
		return nil, err
	}
	
	if _, err := f.Write([]byte(fmt.Sprint(os.Getpid()))); err != nil {
		return nil, err
	}

	return processLockFile{File: f}, nil
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment