pidfile.go 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. package pidfile
  2. import (
  3. "errors"
  4. "fmt"
  5. "io/ioutil"
  6. "os"
  7. "strconv"
  8. "strings"
  9. "syscall"
  10. )
  11. var (
  12. ErrProcessRunning = errors.New("process is running")
  13. ErrFileStale = errors.New("pidfile exists but process is not running")
  14. ErrFileInvalid = errors.New("pidfile has invalid contents")
  15. )
  16. // Remove a pidfile
  17. func Remove(filename string) error {
  18. return os.RemoveAll(filename)
  19. }
  20. // Write writes a pidfile, returning an error
  21. // if the process is already running or pidfile is orphaned
  22. func Write(filename string) error {
  23. return WriteControl(filename, os.Getpid(), false)
  24. }
  25. func WriteControl(filename string, pid int, overwrite bool) error {
  26. // Check for existing pid
  27. oldpid, err := pidfileContents(filename)
  28. if err != nil && !os.IsNotExist(err) {
  29. return err
  30. }
  31. // We have a pid
  32. if err == nil {
  33. if pidIsRunning(oldpid) {
  34. return ErrProcessRunning
  35. }
  36. if !overwrite {
  37. return ErrFileStale
  38. }
  39. }
  40. // We're clear to (over)write the file
  41. return ioutil.WriteFile(filename, []byte(fmt.Sprintf("%d\n", pid)), 0644)
  42. }
  43. func pidfileContents(filename string) (int, error) {
  44. contents, err := ioutil.ReadFile(filename)
  45. if err != nil {
  46. return 0, err
  47. }
  48. pid, err := strconv.Atoi(strings.TrimSpace(string(contents)))
  49. if err != nil {
  50. return 0, ErrFileInvalid
  51. }
  52. return pid, nil
  53. }
  54. func pidIsRunning(pid int) bool {
  55. process, err := os.FindProcess(pid)
  56. if err != nil {
  57. return false
  58. }
  59. err = process.Signal(syscall.Signal(0))
  60. if err != nil && err.Error() == "no such process" {
  61. return false
  62. }
  63. if err != nil && err.Error() == "os: process already finished" {
  64. return false
  65. }
  66. return true
  67. }