86 lines
2.2 KiB
Go
86 lines
2.2 KiB
Go
//go:build !windows
|
|
|
|
package agent
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
|
|
"reasonix/internal/store"
|
|
|
|
"golang.org/x/sys/unix"
|
|
)
|
|
|
|
func lockSessionFile(path string) (func(), error) {
|
|
f, err := os.OpenFile(store.SessionLockFile(path), os.O_CREATE|os.O_RDWR, 0o600)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil {
|
|
_ = f.Close()
|
|
return nil, err
|
|
}
|
|
return func() {
|
|
_ = unix.Flock(int(f.Fd()), unix.LOCK_UN)
|
|
_ = f.Close()
|
|
}, nil
|
|
}
|
|
|
|
// sessionLockFile is a non-blocking exclusive lock on a lock file itself,
|
|
// used by cleanup paths that may need to delete the file they locked.
|
|
type sessionLockFile struct {
|
|
f *os.File
|
|
}
|
|
|
|
// tryTakeSessionLockFile opens lockPath and takes its exclusive flock without
|
|
// blocking. A live holder surfaces as errSessionFileLockHeld.
|
|
func tryTakeSessionLockFile(lockPath string) (*sessionLockFile, error) {
|
|
f, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0o600)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := unix.Flock(int(f.Fd()), unix.LOCK_EX|unix.LOCK_NB); err != nil {
|
|
_ = f.Close()
|
|
if errors.Is(err, unix.EWOULDBLOCK) || errors.Is(err, unix.EAGAIN) {
|
|
return nil, errSessionFileLockHeld
|
|
}
|
|
return nil, err
|
|
}
|
|
return &sessionLockFile{f: f}, nil
|
|
}
|
|
|
|
func (l *sessionLockFile) Unlock() {
|
|
_ = unix.Flock(int(l.f.Fd()), unix.LOCK_UN)
|
|
_ = l.f.Close()
|
|
}
|
|
|
|
// RemoveAndUnlock deletes the lock file atomically with the release: the
|
|
// unlink happens while the flock is still held, so a waiter blocked on this
|
|
// inode can never adopt a file that is about to disappear for everyone else.
|
|
func (l *sessionLockFile) RemoveAndUnlock() error {
|
|
removeErr := os.Remove(l.f.Name())
|
|
l.Unlock()
|
|
if removeErr != nil && !os.IsNotExist(removeErr) {
|
|
return removeErr
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func tryLockSessionLeaseFile(path string) (func(), error) {
|
|
f, err := os.OpenFile(store.SessionLeaseLock(path), os.O_CREATE|os.O_RDWR, 0o600)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := unix.Flock(int(f.Fd()), unix.LOCK_EX|unix.LOCK_NB); err != nil {
|
|
_ = f.Close()
|
|
if errors.Is(err, unix.EWOULDBLOCK) || errors.Is(err, unix.EAGAIN) {
|
|
return nil, ErrSessionLeaseHeld
|
|
}
|
|
return nil, err
|
|
}
|
|
return func() {
|
|
_ = unix.Flock(int(f.Fd()), unix.LOCK_UN)
|
|
_ = f.Close()
|
|
}, nil
|
|
}
|