github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/internal/osutils/lockfile/pidlock_lin_mac.go (about)

     1  //go:build linux || darwin
     2  // +build linux darwin
     3  
     4  package lockfile
     5  
     6  import (
     7  	"io"
     8  	"os"
     9  	"syscall"
    10  
    11  	"github.com/ActiveState/cli/internal/errs"
    12  )
    13  
    14  // PidExists checks if a process with the given PID exists and is running
    15  func PidExists(pid int) bool {
    16  	// this always returns a process on linux...
    17  	p, err := os.FindProcess(int(pid))
    18  	if err != nil {
    19  		return false
    20  	}
    21  	// ... so we have to send it a fake signal
    22  	err = p.Signal(syscall.Signal(0))
    23  	if err == nil {
    24  		// process exists on success
    25  		return true
    26  	}
    27  	if err.Error() == "os: process already finished" {
    28  		return false
    29  	}
    30  	errno, ok := err.(syscall.Errno)
    31  	if !ok {
    32  		return false
    33  	}
    34  	switch errno {
    35  	case syscall.ESRCH: // process does not exist if we get search failed error
    36  		return false
    37  	case syscall.EPERM:
    38  		return true // process exists if we do not have permissions to send signal
    39  	}
    40  	return false
    41  }
    42  
    43  // LockFile tries to acquire a read lock on the file f
    44  func LockFile(f *os.File) error {
    45  	ft := &syscall.Flock_t{
    46  		Whence: io.SeekStart,
    47  		Start:  0,
    48  		Len:    0,
    49  		Pid:    int32(os.Getpid()),
    50  		Type:   syscall.F_WRLCK,
    51  	}
    52  
    53  	err := syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, ft)
    54  	if err != nil {
    55  		return errs.Wrap(err, "failed to lock file")
    56  	}
    57  	return nil
    58  }
    59  
    60  func LockRelease(f *os.File) error {
    61  	ft := &syscall.Flock_t{
    62  		Whence: io.SeekStart,
    63  		Start:  0,
    64  		Len:    0,
    65  		Pid:    int32(os.Getpid()),
    66  		Type:   syscall.F_UNLCK,
    67  	}
    68  
    69  	return syscall.FcntlFlock(f.Fd(), syscall.F_SETLK, ft)
    70  }
    71  
    72  func (pl *PidLock) cleanLockFile(keep bool) error {
    73  	// On Linux we have to remove the file before removing the file lock to avoid race conditions.
    74  	if !keep {
    75  		err := os.Remove(pl.path)
    76  		if err != nil {
    77  			return errs.Wrap(err, "failed to remove lock file %s", pl.path)
    78  		}
    79  	}
    80  	err := LockRelease(pl.file)
    81  	if err != nil {
    82  		return errs.Wrap(err, "failed to release lock on lock file %s", pl.path)
    83  	}
    84  	err = pl.file.Close()
    85  	if err != nil {
    86  		return errs.Wrap(err, "failed to close lock file %s", pl.path)
    87  	}
    88  	return nil
    89  }