Skip to content

Commit

Permalink
Ⓜ️ Verify running processes when locking
Browse files Browse the repository at this point in the history
  • Loading branch information
jsnjack committed Dec 18, 2023
1 parent ef4816f commit 3749142
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 6 deletions.
64 changes: 64 additions & 0 deletions cmd/process.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package cmd

import (
"errors"
"fmt"
"io"
"os"
"strconv"
"strings"
)

var ErrNoProcessFound = errors.New("no process found")

// findProcessWithPrefix finds a process with the given prefix in its command line
func findProcessWithPrefix(prefix string) (int, error) {
d, err := os.Open("/proc")
if err != nil {
return 0, err
}
defer d.Close()

for {
names, err := d.Readdirnames(10)
if err == io.EOF {
break
}
if err != nil {
return 0, err
}

for _, name := range names {
// We only care if the name starts with a numeric
if name[0] < '0' || name[0] > '9' {
continue
}

// From this point forward, any errors we just ignore, because
// it might simply be that the process doesn't exist anymore.
pid, err := strconv.ParseInt(name, 10, 0)
if err != nil {
continue
}

cmdline, err := readCmdline(int(pid))
if err != nil {
continue
}
if strings.HasPrefix(cmdline, prefix) {
return int(pid), nil
}
}
}
return 0, ErrNoProcessFound
}

// readCmdline reads the command line of a process
func readCmdline(pid int) (string, error) {
cmdlinePath := fmt.Sprintf("/proc/%d/cmdline", pid)
dataBytes, err := os.ReadFile(cmdlinePath)
if err != nil {
return "", err
}
return string(dataBytes), nil
}
4 changes: 0 additions & 4 deletions cmd/script.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ import (
"strings"
)

// LockAcquireAttempts is the number of attempts to acquire the lock. Also
// correlates with the number of seconds to wait for the lock.
const LockAcquireAttempts = 180

// Script represents a Python script
type Script struct {
AbsolutePath string // Full path to the script
Expand Down
35 changes: 33 additions & 2 deletions cmd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ import (
const CyanColor = "\033[1;36m"
const ResetColor = "\033[0m"

// LockAcquireAttempts is the number of attempts to acquire the lock. Also
// correlates with the number of seconds to wait for the lock.
const LockAcquireAttempts = 300

// LockStaleTime is the time after which the lock is considered stale
const LockStaleTime = 15 * time.Minute

// getFileHash calculates the SHA256 hash of the file
func getFileHash(filename string) (string, error) {
// Check that the file exists
Expand Down Expand Up @@ -80,12 +87,36 @@ func acquireLock(envDir string, attempt int) error {
if err != nil {
return err
}
_, err = os.Stat(lockFileName)
stat, err := os.Stat(lockFileName)
if err == nil {
if attempt >= LockAcquireAttempts {
return fmt.Errorf("failed to acquire lock")
}
// Sleep for 1 second

// Lockfile exists, check if it is stale by checking its age
if time.Since(stat.ModTime()) > LockStaleTime {
if flagDebug {
loggerErr.Printf("lockfile %s is stale, removing it\n", lockFileName)
}
err = os.Remove(lockFileName)
if err != nil {
return err
}
}

// Lockfile is not stale, check if there is a process which uses the venv
_, err := findProcessWithPrefix(envDir)
if err == ErrNoProcessFound {
if flagDebug {
loggerErr.Printf("process which is using virtual environment not found, removing lockfile %s\n", lockFileName)
}
err = os.Remove(lockFileName)
if err != nil {
return err
}
}

// Lockfile is not stale, wait for 1 second and try again
time.Sleep(1 * time.Second)
return acquireLock(envDir, attempt+1)
}
Expand Down

0 comments on commit 3749142

Please sign in to comment.