git-annex/Utility/Daemon.hs

126 lines
3.4 KiB
Haskell
Raw Normal View History

{- daemon support
2012-06-11 04:39:09 +00:00
-
- Copyright 2012-2014 Joey Hess <joey@kitenet.net>
2012-06-11 04:39:09 +00:00
-
- Licensed under the GNU GPL version 3 or higher.
-}
2013-05-10 21:29:59 +00:00
{-# LANGUAGE CPP #-}
2012-06-11 04:39:09 +00:00
module Utility.Daemon where
import Common
import Utility.PID
2013-08-04 17:54:09 +00:00
#ifndef mingw32_HOST_OS
import Utility.LogFile
2013-08-04 17:54:09 +00:00
#endif
#ifndef mingw32_HOST_OS
2012-06-11 04:39:09 +00:00
import System.Posix
import Control.Concurrent.Async
2013-05-11 22:23:41 +00:00
#else
2014-01-29 19:24:22 +00:00
import System.PosixCompat.Types
2014-02-11 19:29:56 +00:00
import System.Win32.Console (generateConsoleCtrlEvent, cTRL_C_EVENT)
2013-05-11 22:23:41 +00:00
#endif
2012-06-11 04:39:09 +00:00
2014-02-11 17:18:59 +00:00
#ifndef mingw32_HOST_OS
2012-06-11 04:39:09 +00:00
{- Run an action as a daemon, with all output sent to a file descriptor.
-
- Can write its pid to a file, to guard against multiple instances
- running and allow easy termination.
-
- When successful, does not return. -}
daemonize :: Fd -> Maybe FilePath -> Bool -> IO () -> IO ()
daemonize logfd pidfile changedirectory a = do
maybe noop checkalreadyrunning pidfile
_ <- forkProcess child1
out
2012-12-13 04:24:19 +00:00
where
checkalreadyrunning f = maybe noop (const $ alreadyRunning)
=<< checkDaemon f
child1 = do
_ <- createSession
_ <- forkProcess child2
out
child2 = do
maybe noop lockPidFile pidfile
when changedirectory $
setCurrentDirectory "/"
nullfd <- openFd "/dev/null" ReadOnly Nothing defaultFileFlags
redir nullfd stdInput
redirLog logfd
{- forkProcess masks async exceptions; unmask them inside
- the action. -}
wait =<< asyncWithUnmask (\unmask -> unmask a)
2012-12-13 04:24:19 +00:00
out
out = exitImmediately ExitSuccess
#endif
2012-07-02 17:47:32 +00:00
{- Locks the pid file, with an exclusive, non-blocking lock.
- Writes the pid to the file, fully atomically.
- Fails if the pid file is already locked by another process. -}
lockPidFile :: FilePath -> IO ()
lockPidFile file = do
2012-08-08 18:18:02 +00:00
createDirectoryIfMissing True (parentDir file)
#ifndef mingw32_HOST_OS
2012-07-02 17:47:32 +00:00
fd <- openFd file ReadWrite (Just stdFileMode) defaultFileFlags
locked <- catchMaybeIO $ setLock fd (WriteLock, AbsoluteSeek, 0, 0)
fd' <- openFd newfile ReadWrite (Just stdFileMode) defaultFileFlags
{ trunc = True }
locked' <- catchMaybeIO $ setLock fd' (WriteLock, AbsoluteSeek, 0, 0)
case (locked, locked') of
(Nothing, _) -> alreadyRunning
(_, Nothing) -> alreadyRunning
2012-07-02 17:47:32 +00:00
_ -> do
_ <- fdWrite fd' =<< show <$> getPID
2012-07-02 17:47:32 +00:00
closeFd fd
#else
2013-05-14 21:32:03 +00:00
writeFile newfile "-1"
#endif
rename newfile file
2013-05-14 21:32:03 +00:00
where
newfile = file ++ ".new"
alreadyRunning :: IO ()
alreadyRunning = error "Daemon is already running."
2012-06-11 06:01:20 +00:00
{- Checks if the daemon is running, by checking that the pid file
- is locked by the same process that is listed in the pid file.
2012-06-11 06:01:20 +00:00
-
- If it's running, returns its pid. -}
checkDaemon :: FilePath -> IO (Maybe PID)
#ifndef mingw32_HOST_OS
checkDaemon pidfile = do
v <- catchMaybeIO $
openFd pidfile ReadOnly (Just stdFileMode) defaultFileFlags
case v of
Just fd -> do
locked <- getLock fd (ReadLock, AbsoluteSeek, 0, 0)
p <- readish <$> readFile pidfile
2013-05-27 20:48:41 +00:00
closeFd fd `after` return (check locked p)
Nothing -> return Nothing
2012-12-13 04:24:19 +00:00
where
check Nothing _ = Nothing
check _ Nothing = Nothing
check (Just (pid, _)) (Just pid')
| pid == pid' = Just pid
| otherwise = error $
"stale pid in " ++ pidfile ++
" (got " ++ show pid' ++
"; expected " ++ show pid ++ " )"
#else
2013-05-14 21:32:03 +00:00
checkDaemon pidfile = maybe Nothing readish <$> catchMaybeIO (readFile pidfile)
#endif
{- Stops the daemon, safely. -}
stopDaemon :: FilePath -> IO ()
stopDaemon pidfile = go =<< checkDaemon pidfile
2012-12-13 04:24:19 +00:00
where
go Nothing = noop
go (Just pid) =
#ifndef mingw32_HOST_OS
signalProcess sigTERM pid
#else
generateConsoleCtrlEvent cTRL_C_EVENT pid
#endif