// SPDX-License-Identifier: MIT //go:build unix package helper import ( "errors" "fmt" "os" "path/filepath" "syscall" ) // lockState serialises concurrent apply/remove operations using an advisory // file lock stored next to the state file. It returns a release function that // must always be called (typically deferred); its error reports a release that // failed, which the caller should surface. func lockState(statePath string) (func() error, error) { dir := filepath.Dir(statePath) if err := os.MkdirAll(dir, 0o755); err != nil { return nil, fmt.Errorf("create %s: %w", dir, err) } path := filepath.Join(dir, "prx.lock") f, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0o600) if err != nil { return nil, fmt.Errorf("open lock %s: %w", path, err) } if err := syscall.Flock(int(f.Fd()), syscall.LOCK_EX); err != nil { _ = f.Close() return nil, fmt.Errorf("lock %s: %w", path, err) } return func() error { var errs []error if err := syscall.Flock(int(f.Fd()), syscall.LOCK_UN); err != nil { errs = append(errs, fmt.Errorf("unlock %s: %w", path, err)) } if err := f.Close(); err != nil { errs = append(errs, fmt.Errorf("close %s: %w", path, err)) } return errors.Join(errs...) }, nil }