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  }