package servicelock import ( "errors" "os" "path/filepath" "syscall" ) var ErrHeld = errors.New("luci serve consumer already holds this data directory") type Lock struct{ file *os.File } // Acquire exclusively locks one Luci data directory until Close or process exit. func Acquire(dataDir string) (Lock, error) { if err := os.MkdirAll(dataDir, 0o755); err != nil { return Lock{}, err } file, err := os.OpenFile(filepath.Join(dataDir, ".serve.lock"), os.O_CREATE|os.O_RDWR, 0o644) if err != nil { return Lock{}, err } if err := syscall.Flock(int(file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB); err != nil { _ = file.Close() if errors.Is(err, syscall.EWOULDBLOCK) || errors.Is(err, syscall.EAGAIN) { return Lock{}, ErrHeld } return Lock{}, err } return Lock{file: file}, nil } func (l Lock) Close() error { if l.file == nil { return nil } return l.file.Close() }