package main import ( "context" "errors" "fmt" "io/fs" "os" "path/filepath" "github.com/mediocregopher/mediocre-go-lib/v2/mctx" "github.com/mediocregopher/mediocre-go-lib/v2/mlog" "github.com/shirou/gopsutil/process" ) var errDaemonNotRunning = errors.New("no isle daemon process running") func lockFilePath() string { return filepath.Join(envRuntimeDirPath, "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", envRuntimeDirPath) logger.Info(ctx, "will use runtime directory for temporary state") if err := os.MkdirAll(envRuntimeDirPath, 0700); err != nil { return nil, fmt.Errorf("creating directory %q: %w", envRuntimeDirPath, err) } else if err := writeLock(); err != nil { return nil, err } return func() { logger.Info(ctx, "cleaning up runtime directory") if err := os.RemoveAll(envRuntimeDirPath); 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 }