git.sr.ht/~pingoo/stdx@v0.0.0-20240218134121-094174641f6e/singleinstance/singleinstance_unix.go (about) 1 //go:build !windows 2 3 package singleinstance 4 5 import ( 6 "fmt" 7 "os" 8 "syscall" 9 ) 10 11 // Lock tries to obtain an exclude lock on a lockfile and exits the program if an error occurs 12 func (s *SingleInstance) Lock() error { 13 // open/create lock file 14 f, err := os.OpenFile(s.Lockfile(), os.O_RDWR|os.O_CREATE, 0600) 15 if err != nil { 16 return err 17 } 18 19 s.file = f 20 // set the lock type to F_WRLCK, therefor the file has to be opened writable 21 flock := syscall.Flock_t{ 22 Type: syscall.F_WRLCK, 23 Pid: int32(os.Getpid()), 24 } 25 // try to obtain an exclusive lock - FcntlFlock seems to be the portable *ix way 26 if err := syscall.FcntlFlock(s.file.Fd(), syscall.F_SETLK, &flock); err != nil { 27 return ErrAlreadyRunning 28 } 29 30 return nil 31 } 32 33 // Unlock releases the lock, closes and removes the lockfile 34 func (s *SingleInstance) Unlock() error { 35 // set the lock type to F_UNLCK 36 flock := syscall.Flock_t{ 37 Type: syscall.F_UNLCK, 38 Pid: int32(os.Getpid()), 39 } 40 41 if err := syscall.FcntlFlock(s.file.Fd(), syscall.F_SETLK, &flock); err != nil { 42 return fmt.Errorf("failed to unlock the lock file: %w", err) 43 } 44 45 if err := s.file.Close(); err != nil { 46 return fmt.Errorf("failed to close the lock file: %w", err) 47 } 48 49 if err := os.Remove(s.Lockfile()); err != nil { 50 return fmt.Errorf("failed to remove the lock file: %w", err) 51 } 52 53 return nil 54 }