1// +build !windows,!plan9 2 3package pidfile 4 5import ( 6 "fmt" 7 "io" 8 "os" 9 "sync" 10 "syscall" 11) 12 13var ( 14 pidFile *os.File 15 pidFileName string 16) 17 18type pidfile struct { 19 once sync.Once 20 f *os.File 21 path string 22} 23 24func (p *pidfile) Close() error { 25 p.once.Do(func() { 26 // Try and remove file, don't care if it fails. 27 os.Remove(p.path) 28 29 p.f.Close() 30 p.f = nil 31 }) 32 33 return nil 34} 35 36// Opens and locks a file and writes the current PID to it. The file is kept open 37// until you close the returned interface, at which point it is deleted. It may also 38// be deleted if the program exits without closing the returned interface. 39func Open(path string) (io.Closer, error) { 40 return OpenWith(path, fmt.Sprintf("%d\n", os.Getpid())) 41} 42 43// Opens and locks a file and writes body to it. The file is kept open until 44// you close the returned interface, at which point it is deleted. It may also 45// be deleted if the program exits without closing the returned interface. 46func OpenWith(path, body string) (io.Closer, error) { 47 f, err := open(path) 48 if err != nil { 49 return nil, err 50 } 51 52 _, err = f.WriteString(body) 53 if err != nil { 54 f.Close() 55 return nil, err 56 } 57 58 return &pidfile{ 59 f: f, 60 path: path, 61 }, nil 62} 63 64func open(path string) (*os.File, error) { 65 var f *os.File 66 var err error 67 68 for { 69 f, err = os.OpenFile(path, 70 syscall.O_RDWR|syscall.O_CREAT|syscall.O_EXCL, 0644) 71 if err != nil { 72 if !os.IsExist(err) { 73 return nil, err 74 } 75 76 f, err = os.OpenFile(path, syscall.O_RDWR, 0644) 77 if err != nil { 78 if os.IsNotExist(err) { 79 continue 80 } 81 return nil, err 82 } 83 } 84 85 err = syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, &syscall.Flock_t{ 86 Type: syscall.F_WRLCK, 87 }) 88 if err != nil { 89 f.Close() 90 return nil, err 91 } 92 93 st1 := syscall.Stat_t{} 94 err = syscall.Fstat(int(f.Fd()), &st1) // ffs 95 if err != nil { 96 f.Close() 97 return nil, err 98 } 99 100 st2 := syscall.Stat_t{} 101 err = syscall.Stat(path, &st2) 102 if err != nil { 103 f.Close() 104 105 if os.IsNotExist(err) { 106 continue 107 } 108 109 return nil, err 110 } 111 112 if st1.Ino != st2.Ino { 113 f.Close() 114 continue 115 } 116 117 break 118 } 119 120 return f, nil 121} 122