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

     1  //go:build windows
     2  // +build windows
     3  
     4  package lockfile
     5  
     6  import (
     7  	"os"
     8  	"syscall"
     9  
    10  	"github.com/ActiveState/cli/internal/errs"
    11  )
    12  
    13  // ErrorLockViolation is the error no returned if a lock fails on Windows
    14  const ErrorLockViolation syscall.Errno = 0x21 // 33
    15  
    16  // PidExists checks if a process with the given PID exists and is running
    17  func PidExists(pid int) bool {
    18  	_, err := os.FindProcess(int(pid))
    19  	return err == nil
    20  }
    21  
    22  // LockFile attempts to add a lock to the file f
    23  func LockFile(f *os.File) error {
    24  	_, errNo := lockFileEx(syscall.Handle(f.Fd()), winLockfileExclusiveLock|winLockfileFailImmediately, 0, 1, 0, &syscall.Overlapped{})
    25  
    26  	if errNo > 0 {
    27  		if errNo == ErrorLockViolation || errNo == syscall.ERROR_IO_PENDING {
    28  			return errs.New("cannot obtain lock")
    29  		}
    30  
    31  		return errs.New("unknown error: %d", errNo)
    32  	}
    33  	return nil
    34  }
    35  
    36  // LockRelease releases a file lock
    37  func LockRelease(f *os.File) error {
    38  	// mark the file as unlocked
    39  	if _, errNo := unlockFileEx(syscall.Handle(f.Fd()), 0, 1, 0, &syscall.Overlapped{}); errNo > 0 {
    40  		return errs.New("error releasing windows file lock. errno: %d", errNo)
    41  	}
    42  	return nil
    43  }
    44  
    45  func (pl *PidLock) cleanLockFile(keep bool) error {
    46  	err := LockRelease(pl.file)
    47  	if err != nil {
    48  		return errs.Wrap(err, "failed to release lock on lock file %s", pl.path)
    49  	}
    50  	err = pl.file.Close()
    51  	if err != nil {
    52  		return errs.Wrap(err, "failed to close lock file %s", pl.path)
    53  	}
    54  	// On Windows, we have to remove the file AFTER it is closed.
    55  	// It is not an error if it fails, because that could mean that another
    56  	// process has opened a new file handle for the file already.
    57  	if !keep {
    58  		_ = os.Remove(pl.path)
    59  	}
    60  	return nil
    61  }