57 lines
1.1 KiB
Go
57 lines
1.1 KiB
Go
package execute
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"syscall"
|
|
)
|
|
|
|
// acquireFlock opens (or creates) a lock file in dir named name and acquires
|
|
// LOCK_SH (exclusive=false) or LOCK_EX (exclusive=true).
|
|
// Returns nil, nil when dir is empty (locking disabled).
|
|
// Caller must Close the returned file to release the lock.
|
|
func acquireFlock(dir, name string, exclusive bool) (*os.File, error) {
|
|
if dir == "" {
|
|
return nil, nil
|
|
}
|
|
if err := os.MkdirAll(dir, 0700); err != nil {
|
|
return nil, err
|
|
}
|
|
how := syscall.LOCK_SH
|
|
if exclusive {
|
|
how = syscall.LOCK_EX
|
|
}
|
|
path := filepath.Join(dir, sanitizeLockName(name)+".lock")
|
|
f, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0600)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := syscall.Flock(int(f.Fd()), how); err != nil {
|
|
f.Close()
|
|
return nil, err
|
|
}
|
|
return f, nil
|
|
}
|
|
|
|
|
|
func sanitizeLockName(s string) string {
|
|
var b strings.Builder
|
|
for _, r := range s {
|
|
switch r {
|
|
case '/', '\\', ':', '*', '?', '<', '>', '|', '"', ' ':
|
|
b.WriteRune('_')
|
|
default:
|
|
b.WriteRune(r)
|
|
}
|
|
}
|
|
name := b.String()
|
|
if len(name) > 64 {
|
|
name = name[:64]
|
|
}
|
|
if name == "" {
|
|
return "unnamed"
|
|
}
|
|
return name
|
|
}
|