100 lines
2.1 KiB
Go
100 lines
2.1 KiB
Go
|
package crypticnet
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io/fs"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
|
||
|
"github.com/shirou/gopsutil/process"
|
||
|
)
|
||
|
|
||
|
var errDaemonNotRunning = errors.New("no cryptic-net daemon process running")
|
||
|
|
||
|
// ProcLock is used to lock a process.
|
||
|
type ProcLock interface {
|
||
|
|
||
|
// WriteLock creates a new lock, or errors if the lock is alread held.
|
||
|
WriteLock() error
|
||
|
|
||
|
// AssertLock returns an error if the lock already exists.
|
||
|
AssertLock() error
|
||
|
}
|
||
|
|
||
|
type procLock struct {
|
||
|
dir string
|
||
|
}
|
||
|
|
||
|
// NewProcLock returns a ProcLock which will use a file in the given directory
|
||
|
// to lock the process.
|
||
|
func NewProcLock(dir string) ProcLock {
|
||
|
return &procLock{dir: dir}
|
||
|
}
|
||
|
|
||
|
func (pl *procLock) path() string {
|
||
|
return filepath.Join(pl.dir, "lock")
|
||
|
}
|
||
|
|
||
|
func (pl *procLock) WriteLock() error {
|
||
|
|
||
|
lockFilePath := pl.path()
|
||
|
|
||
|
lockFile, err := os.OpenFile(
|
||
|
lockFilePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0400,
|
||
|
)
|
||
|
|
||
|
if errors.Is(err, os.ErrExist) {
|
||
|
return fmt.Errorf(
|
||
|
"lock file %q already exists, if the cryptic-net daemon is not already running you can safely delete this file",
|
||
|
lockFilePath,
|
||
|
)
|
||
|
|
||
|
} else if err != nil {
|
||
|
return fmt.Errorf("opening lockfile %q: %w", lockFilePath, err)
|
||
|
}
|
||
|
|
||
|
defer lockFile.Close()
|
||
|
|
||
|
if _, err := fmt.Fprintf(lockFile, "%d\n", os.Getpid()); err != nil {
|
||
|
return fmt.Errorf("writing pid to %q: %w", lockFilePath, err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// checks that the lock file exists and that the process which created it also
|
||
|
// still exists.
|
||
|
func (pl *procLock) AssertLock() error {
|
||
|
|
||
|
lockFilePath := pl.path()
|
||
|
|
||
|
lockFile, err := os.Open(lockFilePath)
|
||
|
|
||
|
if errors.Is(err, fs.ErrNotExist) {
|
||
|
return errDaemonNotRunning
|
||
|
|
||
|
} else if err != nil {
|
||
|
return fmt.Errorf("checking lock file %q: %w", lockFilePath, err)
|
||
|
}
|
||
|
|
||
|
defer lockFile.Close()
|
||
|
|
||
|
var pid int32
|
||
|
|
||
|
if _, err := fmt.Fscan(lockFile, &pid); err != nil {
|
||
|
return fmt.Errorf("scanning pid from lock file %q: %w", lockFilePath, err)
|
||
|
}
|
||
|
|
||
|
procExists, err := process.PidExists(pid)
|
||
|
|
||
|
if err != nil {
|
||
|
return fmt.Errorf("checking if process %d exists: %w", pid, err)
|
||
|
|
||
|
} else if !procExists {
|
||
|
return errDaemonNotRunning
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|