Codebase list golang-github-nightlyone-lockfile / 4529c2b
New upstream release. Debian Janitor 2 years ago
6 changed file(s) with 81 addition(s) and 76 deletion(s). Raw diff Collapse all Expand all
00 language: go
11 go:
2 - 1.4.3
3 - 1.6.2
2 - 1.13
3 - 1.14
44 - tip
55
66 # Only test commits to production branch and all pull requests
0 golang-github-nightlyone-lockfile (0.0~git20170804.6a197d5-2) UNRELEASED; urgency=medium
0 golang-github-nightlyone-lockfile (1.0.0-1) UNRELEASED; urgency=medium
11
22 [ Alexandre Viau ]
33 * Point Vcs-* urls to salsa.debian.org.
55 [ Debian Janitor ]
66 * Remove constraints unnecessary since stretch:
77 + Build-Depends: Drop versioned constraint on debhelper.
8 * New upstream release.
89
9 -- Alexandre Viau <aviau@debian.org> Mon, 02 Apr 2018 19:38:45 -0400
10 -- Alexandre Viau <aviau@debian.org> Fri, 19 Nov 2021 20:26:03 -0000
1011
1112 golang-github-nightlyone-lockfile (0.0~git20170804.6a197d5-1) unstable; urgency=medium
1213
0 module github.com/nightlyone/lockfile
1
2 go 1.11
2525 // Temporary returns always true.
2626 // It exists, so you can detect it via
2727 // if te, ok := err.(interface{ Temporary() bool }); ok {
28 // fmt.Println("I am a temporay error situation, so wait and retry")
28 // fmt.Println("I am a temporary error situation, so wait and retry")
2929 // }
3030 func (t TemporaryError) Temporary() bool { return true }
3131
4444 if !filepath.IsAbs(path) {
4545 return Lockfile(""), ErrNeedAbsPath
4646 }
47
4748 return Lockfile(path), nil
4849 }
4950
6263 if err != nil {
6364 return nil, err
6465 }
66
6567 running, err := isRunning(pid)
6668 if err != nil {
6769 return nil, err
7274 if err != nil {
7375 return nil, err
7476 }
77
7578 return proc, nil
7679 }
80
7781 return nil, ErrDeadOwner
78
7982 }
8083
8184 // TryLock tries to own the lock.
9295 panic(ErrNeedAbsPath)
9396 }
9497
95 tmplock, err := ioutil.TempFile(filepath.Dir(name), filepath.Base(name)+".")
96 if err != nil {
97 return err
98 }
99
100 cleanup := func() {
101 _ = tmplock.Close()
102 _ = os.Remove(tmplock.Name())
103 }
98 tmplock, cleanup, err := makePidFile(name, os.Getpid())
99 if err != nil {
100 return err
101 }
102
104103 defer cleanup()
105104
106 if err := writePidLine(tmplock, os.Getpid()); err != nil {
107 return err
108 }
109
110 // EEXIST and similiar error codes, caught by os.IsExist, are intentionally ignored,
105 // EEXIST and similar error codes, caught by os.IsExist, are intentionally ignored,
111106 // as it means that someone was faster creating this link
112107 // and ignoring this kind of error is part of the algorithm.
113 // The we will probably fail the pid owner check later, if this process is still alive.
108 // Then we will probably fail the pid owner check later, if this process is still alive.
114109 // We cannot ignore ALL errors, since failure to support hard links, disk full
115110 // as well as many other errors can happen to a filesystem operation
116111 // and we really want to abort on those.
117 if err := os.Link(tmplock.Name(), name); err != nil {
112 if err := os.Link(tmplock, name); err != nil {
118113 if !os.IsExist(err) {
119114 return err
120115 }
121116 }
122117
123 fiTmp, err := os.Lstat(tmplock.Name())
124 if err != nil {
125 return err
126 }
118 fiTmp, err := os.Lstat(tmplock)
119 if err != nil {
120 return err
121 }
122
127123 fiLock, err := os.Lstat(name)
128124 if err != nil {
129125 // tell user that a retry would be a good idea
130126 if os.IsNotExist(err) {
131127 return ErrNotExist
132128 }
129
133130 return err
134131 }
135132
147144 if proc.Pid != os.Getpid() {
148145 return ErrBusy
149146 }
150 case ErrDeadOwner, ErrInvalidPid:
151 // cases we can fix below
147 case ErrDeadOwner, ErrInvalidPid: // cases we can fix below
152148 }
153149
154150 // clean stale/invalid lockfile
164160 return l.TryLock()
165161 }
166162
167 // Unlock a lock again, if we owned it. Returns any error that happend during release of lock.
163 // Unlock a lock again, if we owned it. Returns any error that happened during release of lock.
168164 func (l Lockfile) Unlock() error {
169165 proc, err := l.GetOwner()
170166 switch err {
188184 }
189185 }
190186
191 func writePidLine(w io.Writer, pid int) error {
192 _, err := io.WriteString(w, fmt.Sprintf("%d\n", pid))
193 return err
194 }
195
196187 func scanPidLine(content []byte) (int, error) {
197188 if len(content) == 0 {
198189 return 0, ErrInvalidPid
206197 if pid <= 0 {
207198 return 0, ErrInvalidPid
208199 }
200
209201 return pid, nil
210202 }
203
204 func makePidFile(name string, pid int) (tmpname string, cleanup func(), err error) {
205 tmplock, err := ioutil.TempFile(filepath.Dir(name), filepath.Base(name)+".")
206 if err != nil {
207 return "", nil, err
208 }
209
210 cleanup = func() {
211 _ = tmplock.Close()
212 _ = os.Remove(tmplock.Name())
213 }
214
215 if _, err := io.WriteString(tmplock, fmt.Sprintf("%d\n", pid)); err != nil {
216 cleanup() // Do cleanup here, so call doesn't have to.
217 return "", nil, err
218 }
219
220 return tmplock.Name(), cleanup, nil
221 }
1515 fmt.Printf("Cannot init lock. reason: %v", err)
1616 panic(err) // handle properly please!
1717 }
18 err = lock.TryLock()
1918
2019 // Error handling is essential, as we only try to get the lock.
21 if err != nil {
20 if err = lock.TryLock(); err != nil {
2221 fmt.Printf("Cannot lock %q, reason: %v", lock, err)
2322 panic(err) // handle properly please!
2423 }
2524
26 defer lock.Unlock()
25 defer func() {
26 if err := lock.Unlock(); err != nil {
27 fmt.Printf("Cannot unlock %q, reason: %v", lock, err)
28 panic(err) // handle properly please!
29 }
30 }()
2731
2832 fmt.Println("Do stuff under lock")
2933 // Output: Do stuff under lock
6064 func GetDeadPID() int {
6165 // I have no idea how windows handles large PIDs, or if they even exist.
6266 // So limit it to be less or equal to 4096 to be safe.
63
6467 const maxPid = 4095
6568
6669 // limited iteration, so we finish one day
7073 if seen[pid] {
7174 continue
7275 }
76
7377 seen[pid] = true
78
7479 running, err := isRunning(pid)
7580 if err != nil {
7681 fmt.Println("Error checking PID: ", err)
163168 t.Fatal(err)
164169 return
165170 }
171
166172 defer os.Remove(path)
167173
168174 err = lf.Unlock()
171177 return
172178 }
173179
174 if _, err := os.Stat(path); os.IsNotExist(err) {
175 t.Fatal("lockfile should not be deleted by us, if we didn't create it")
176 } else {
177 if err != nil {
178 t.Fatalf("unexpected error %v", err)
179 }
180 if _, err := os.Stat(path); err != nil {
181 if os.IsNotExist(err) {
182 content, _ := ioutil.ReadFile(path)
183 t.Fatalf("lockfile %q (%q) should not be deleted by us, if we didn't create it", path, content)
184 }
185 t.Fatalf("unexpected error %v", err)
180186 }
181187 }
182188
214220 t.Fatal(err)
215221 return
216222 }
223
217224 if err := ioutil.WriteFile(path, []byte("\n"), 0666); err != nil {
218225 t.Fatal(err)
219226 return
220227 }
228
221229 defer os.Remove(path)
222230
223231 lf, err := New(path)
249257 pid int
250258 xfail error
251259 }{
252 {
253 xfail: ErrInvalidPid,
254 },
255 {
256 input: []byte(""),
257 xfail: ErrInvalidPid,
258 },
259 {
260 input: []byte("\n"),
261 xfail: ErrInvalidPid,
262 },
263 {
264 input: []byte("-1\n"),
265 xfail: ErrInvalidPid,
266 },
267 {
268 input: []byte("0\n"),
269 xfail: ErrInvalidPid,
270 },
271 {
272 input: []byte("a\n"),
273 xfail: ErrInvalidPid,
274 },
275 {
276 input: []byte("1\n"),
277 pid: 1,
278 },
260 {xfail: ErrInvalidPid},
261 {input: []byte(""), xfail: ErrInvalidPid},
262 {input: []byte("\n"), xfail: ErrInvalidPid},
263 {input: []byte("-1\n"), xfail: ErrInvalidPid},
264 {input: []byte("0\n"), xfail: ErrInvalidPid},
265 {input: []byte("a\n"), xfail: ErrInvalidPid},
266 {input: []byte("1\n"), pid: 1},
279267 }
280268
281269 // test positive cases first
283271 if tc.xfail != nil {
284272 continue
285273 }
286 want := tc.pid
274
287275 got, err := scanPidLine(tc.input)
288276 if err != nil {
289277 t.Fatalf("%d: unexpected error %v", step, err)
290278 }
291 if got != want {
279
280 if want := tc.pid; got != want {
292281 t.Errorf("%d: expected pid %d, got %d", step, want, got)
293282 }
294283 }
298287 if tc.xfail == nil {
299288 continue
300289 }
301 want := tc.xfail
290
302291 _, got := scanPidLine(tc.input)
303 if got != want {
292 if want := tc.xfail; got != want {
304293 t.Errorf("%d: expected error %v, got %v", step, want, got)
305294 }
306295 }
0 // +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
0 // +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris aix
11
22 package lockfile
33
1515 if err := proc.Signal(syscall.Signal(0)); err != nil {
1616 return false, nil
1717 }
18
1819 return true, nil
1920 }