better pid file locking code
This commit is contained in:
parent
bdcabb3cfa
commit
7daa269853
1 changed files with 34 additions and 21 deletions
|
@ -27,7 +27,7 @@ daemonize logfd pidfile changedirectory a = do
|
||||||
_ <- forkProcess child2
|
_ <- forkProcess child2
|
||||||
out
|
out
|
||||||
child2 = do
|
child2 = do
|
||||||
maybe noop (lockPidFile True alreadyrunning) pidfile
|
maybe noop (lockPidFile alreadyrunning) pidfile
|
||||||
when changedirectory $
|
when changedirectory $
|
||||||
setCurrentDirectory "/"
|
setCurrentDirectory "/"
|
||||||
nullfd <- openFd "/dev/null" ReadOnly Nothing defaultFileFlags
|
nullfd <- openFd "/dev/null" ReadOnly Nothing defaultFileFlags
|
||||||
|
@ -42,31 +42,44 @@ daemonize logfd pidfile changedirectory a = do
|
||||||
alreadyrunning = error "Daemon is already running."
|
alreadyrunning = error "Daemon is already running."
|
||||||
out = exitImmediately ExitSuccess
|
out = exitImmediately ExitSuccess
|
||||||
|
|
||||||
lockPidFile :: Bool -> IO () -> FilePath -> IO ()
|
{- Locks the pid file, with an exclusive, non-blocking lock.
|
||||||
lockPidFile write onfailure file = do
|
- Runs an action on failure. On success, writes the pid to the file,
|
||||||
fd <- openFd file ReadWrite (Just stdFileMode)
|
- fully atomically. -}
|
||||||
defaultFileFlags { trunc = write }
|
lockPidFile :: IO () -> FilePath -> IO ()
|
||||||
locked <- catchMaybeIO $ setLock fd (locktype, AbsoluteSeek, 0, 0)
|
lockPidFile onfailure file = do
|
||||||
case locked of
|
fd <- openFd file ReadWrite (Just stdFileMode) defaultFileFlags
|
||||||
Nothing -> onfailure
|
locked <- catchMaybeIO $ setLock fd (WriteLock, AbsoluteSeek, 0, 0)
|
||||||
_ -> when write $ void $
|
fd' <- openFd newfile ReadWrite (Just stdFileMode) defaultFileFlags
|
||||||
fdWrite fd =<< show <$> getProcessID
|
{ trunc = True }
|
||||||
|
locked' <- catchMaybeIO $ setLock fd' (WriteLock, AbsoluteSeek, 0, 0)
|
||||||
|
case (locked, locked') of
|
||||||
|
(Nothing, _) -> onfailure
|
||||||
|
(_, Nothing) -> onfailure
|
||||||
|
_ -> do
|
||||||
|
_ <- fdWrite fd' =<< show <$> getProcessID
|
||||||
|
renameFile newfile file
|
||||||
|
closeFd fd
|
||||||
where
|
where
|
||||||
locktype
|
newfile = file ++ ".new"
|
||||||
| write = WriteLock
|
|
||||||
| otherwise = ReadLock
|
|
||||||
|
|
||||||
{- Stops the daemon.
|
{- Stops the daemon.
|
||||||
-
|
-
|
||||||
- The pid file is used to get the daemon's pid.
|
- The pid file is used to get the daemon's pid.
|
||||||
-
|
-
|
||||||
- To guard against a stale pid, try to take a nonblocking shared lock
|
- To guard against a stale pid, check the lock of the pid file,
|
||||||
- of the pid file. If this *fails*, the daemon must be running,
|
- and compare the process that has it locked with the file content.
|
||||||
- and have the exclusive lock, so the pid file is trustworthy.
|
|
||||||
-}
|
-}
|
||||||
stopDaemon :: FilePath -> IO ()
|
stopDaemon :: FilePath -> IO ()
|
||||||
stopDaemon pidfile = lockPidFile False go pidfile
|
stopDaemon pidfile = do
|
||||||
where
|
fd <- openFd pidfile ReadOnly (Just stdFileMode) defaultFileFlags
|
||||||
go = do
|
locked <- getLock fd (ReadLock, AbsoluteSeek, 0, 0)
|
||||||
pid <- readish <$> readFile pidfile
|
p <- readish <$> readFile pidfile
|
||||||
maybe noop (signalProcess sigTERM) pid
|
case (locked, p) of
|
||||||
|
(Nothing, _) -> noop
|
||||||
|
(_, Nothing) -> noop
|
||||||
|
(Just (pid, _), Just pid')
|
||||||
|
| pid == pid' -> signalProcess sigTERM pid
|
||||||
|
| otherwise -> error $
|
||||||
|
"stale pid in " ++ pidfile ++
|
||||||
|
" (got " ++ show pid' ++
|
||||||
|
"; expected" ++ show pid ++ " )"
|
||||||
|
|
Loading…
Reference in a new issue