isle/go/cmd/entrypoint/proc_lock.go

104 lines
2.4 KiB
Go

package main
import (
"context"
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"dev.mediocregopher.com/mediocre-go-lib.git/mctx"
"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
"github.com/shirou/gopsutil/process"
)
var errDaemonNotRunning = errors.New("no isle daemon process running")
func lockFilePath() string {
return filepath.Join(daemonEnvVars.RuntimeDirPath, "lock")
}
func writeLock() error {
lockFilePath := lockFilePath()
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 isle 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
}
// returns a cleanup function which will clean up the created runtime directory.
func setupAndLockRuntimeDir(ctx context.Context, logger *mlog.Logger) (func(), error) {
ctx = mctx.Annotate(ctx, "runtimeDirPath", daemonEnvVars.RuntimeDirPath)
logger.Info(ctx, "will use runtime directory for temporary state")
if err := os.MkdirAll(daemonEnvVars.RuntimeDirPath, 0700); err != nil {
return nil, fmt.Errorf("creating directory %q: %w", daemonEnvVars.RuntimeDirPath, err)
} else if err := writeLock(); err != nil {
return nil, err
}
return func() {
logger.Info(ctx, "cleaning up runtime directory")
if err := os.RemoveAll(daemonEnvVars.RuntimeDirPath); err != nil {
logger.Error(ctx, "removing temporary directory", err)
}
}, nil
}
// checks that the lock file exists and that the process which created it also
// still exists.
func assertLock() error {
lockFilePath := lockFilePath()
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
}