// The flock package provides a file-system mediated locking mechanism on linux // using the `flock` system call. package flock import ( "errors" "os" "syscall" ) // Lock gets an exclusive lock on the file at the given path. If the file // doesn't exist, it's created. func Lock(path string) (*os.File, error) { return lock(path, syscall.LOCK_EX) } // TryLock will return a nil file if the file is already locked. func TryLock(path string) (*os.File, error) { return lock(path, syscall.LOCK_EX|syscall.LOCK_NB) } func LockFile(f *os.File) error { _, err := lockFile(f, syscall.LOCK_EX) return err } // Returns true if the lock was successfully acquired. func TryLockFile(f *os.File) (bool, error) { return lockFile(f, syscall.LOCK_EX|syscall.LOCK_NB) } func lockFile(f *os.File, flags int) (bool, error) { if err := flock(int(f.Fd()), flags); err != nil { if flags&syscall.LOCK_NB != 0 && errors.Is(err, syscall.EAGAIN) { return false, nil } return false, err } return true, nil } func flock(fd int, how int) error { _, _, e1 := syscall.Syscall(syscall.SYS_FLOCK, uintptr(fd), uintptr(how), 0) if e1 != 0 { return syscall.Errno(e1) } return nil } func lock(path string, flags int) (*os.File, error) { perm := os.O_CREATE | os.O_RDWR f, err := os.OpenFile(path, perm, 0600) if err != nil { return nil, err } ok, err := lockFile(f, flags) if err != nil || !ok { f.Close() f = nil } return f, err } // Unlock releases the lock acquired via the Lock function. func Unlock(f *os.File) error { return f.Close() }