github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/updater/service/pid.go (about) 1 //go:build !windows 2 // +build !windows 3 4 package main 5 6 import ( 7 "fmt" 8 "os" 9 "path/filepath" 10 "syscall" 11 ) 12 13 // LockPIDFile manages a lock file containing the PID for the current process. 14 type LockPIDFile struct { 15 name string 16 file *os.File 17 log Log 18 } 19 20 // NewLockPIDFile creates a LockPIDFile for filename name. 21 func NewLockPIDFile(name string, log Log) *LockPIDFile { 22 return &LockPIDFile{name: name, log: log} 23 } 24 25 // Lock writes the pid to filename after acquiring a lock on the file. 26 // When the process exits, the lock will be released. 27 func (f *LockPIDFile) Lock() (err error) { 28 // make the parent directory 29 _, err = os.Stat(filepath.Dir(f.name)) 30 if os.IsNotExist(err) { 31 err = os.MkdirAll(filepath.Dir(f.name), 0700) 32 if err != nil { 33 return err 34 } 35 } else if err != nil { 36 return err 37 } 38 39 if f.file, err = os.OpenFile(f.name, os.O_CREATE|os.O_RDWR, 0600); err != nil { 40 return err 41 } 42 43 // LOCK_EX = exclusive 44 // LOCK_NB = nonblocking 45 if err = syscall.Flock(int(f.file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil { 46 f.file.Close() 47 f.file = nil 48 return err 49 } 50 51 pid := os.Getpid() 52 fmt.Fprintf(f.file, "%d", pid) 53 if err = f.file.Sync(); err != nil { 54 return err 55 } 56 57 f.log.Debugf("Locked pidfile %s for pid=%d", f.name, pid) 58 59 return nil 60 } 61 62 // Close releases the lock by closing and removing the file. 63 func (f *LockPIDFile) Close() (err error) { 64 if f.file != nil { 65 if e1 := f.file.Close(); e1 != nil { 66 f.log.Warningf("Error closing pid file: %s\n", e1) 67 } 68 f.log.Debugf("Cleaning up pidfile %s", f.name) 69 if err = os.Remove(f.name); err != nil { 70 f.log.Warningf("Error removing pidfile: %s\n", err) 71 } 72 } 73 return 74 }