summaryrefslogtreecommitdiff
path: root/internal/namespace/lock_linux.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/namespace/lock_linux.go')
-rw-r--r--internal/namespace/lock_linux.go40
1 files changed, 40 insertions, 0 deletions
diff --git a/internal/namespace/lock_linux.go b/internal/namespace/lock_linux.go
new file mode 100644
index 0000000..8da98f6
--- /dev/null
+++ b/internal/namespace/lock_linux.go
@@ -0,0 +1,40 @@
+//go:build linux
+
+package namespace
+
+import (
+ "fmt"
+ "os"
+ "path/filepath"
+
+ "git.theodohertyfamily.com/tools/wg-wrap/internal/paths"
+ "golang.org/x/sys/unix"
+)
+
+// AcquireProfileLock locks the profile to prevent concurrent startup races.
+func AcquireProfileLock(pm *paths.PathManager, profile string) (*os.File, error) {
+ lockPath := filepath.Join(pm.RuntimeBaseDir(), "profiles", profile+".lock")
+ if err := os.MkdirAll(filepath.Dir(lockPath), 0755); err != nil {
+ return nil, fmt.Errorf("failed to create lock directory: %w", err)
+ }
+
+ file, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0600)
+ if err != nil {
+ return nil, fmt.Errorf("failed to open lock file: %w", err)
+ }
+
+ if err := unix.Flock(int(file.Fd()), unix.LOCK_EX); err != nil {
+ _ = file.Close()
+ return nil, fmt.Errorf("failed to lock profile: %w", err)
+ }
+
+ return file, nil
+}
+
+// ReleaseProfileLock unlocks the profile.
+func ReleaseProfileLock(file *os.File) {
+ if file != nil {
+ _ = unix.Flock(int(file.Fd()), unix.LOCK_UN)
+ _ = file.Close()
+ }
+}