windows: Fix daemon pid file locking.

Well, as much as it can be fixed on windows. Not atomic; not entirely
guarded against the wrong process having the pid file locked.
This commit is contained in:
Joey Hess 2014-02-13 16:03:49 -04:00
parent f11f7520b5
commit c60f0b57d2
2 changed files with 45 additions and 13 deletions

View file

@ -15,6 +15,7 @@ import Utility.PID
import Utility.LogFile import Utility.LogFile
#else #else
import Utility.WinProcess import Utility.WinProcess
import Utility.WinLock
#endif #endif
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
@ -55,14 +56,16 @@ daemonize logfd pidfile changedirectory a = do
out = exitImmediately ExitSuccess out = exitImmediately ExitSuccess
#endif #endif
{- Locks the pid file, with an exclusive, non-blocking lock. {- Locks the pid file, with an exclusive, non-blocking lock,
- and leaves it locked on return.
-
- Writes the pid to the file, fully atomically. - Writes the pid to the file, fully atomically.
- Fails if the pid file is already locked by another process. -} - Fails if the pid file is already locked by another process. -}
lockPidFile :: FilePath -> IO () lockPidFile :: FilePath -> IO ()
lockPidFile file = do lockPidFile pidfile = do
createDirectoryIfMissing True (parentDir file) createDirectoryIfMissing True (parentDir pidfile)
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
fd <- openFd file ReadWrite (Just stdFileMode) defaultFileFlags fd <- openFd pidfile ReadWrite (Just stdFileMode) defaultFileFlags
locked <- catchMaybeIO $ setLock fd (WriteLock, AbsoluteSeek, 0, 0) locked <- catchMaybeIO $ setLock fd (WriteLock, AbsoluteSeek, 0, 0)
fd' <- openFd newfile ReadWrite (Just stdFileMode) defaultFileFlags fd' <- openFd newfile ReadWrite (Just stdFileMode) defaultFileFlags
{ trunc = True } { trunc = True }
@ -73,12 +76,17 @@ lockPidFile file = do
_ -> do _ -> do
_ <- fdWrite fd' =<< show <$> getPID _ <- fdWrite fd' =<< show <$> getPID
closeFd fd closeFd fd
#else rename newfile pidfile
writeFile newfile . show =<< getPID
#endif
rename newfile file
where where
newfile = file ++ ".new" newfile = pidfile ++ ".new"
#else
{- Not atomic on Windows, oh well. -}
pid <- getPID
writeFile pidfile (show pid)
lckfile <- winLockFile pid pidfile
writeFile lckfile ""
void $ lockExclusive lckfile
#endif
alreadyRunning :: IO () alreadyRunning :: IO ()
alreadyRunning = error "Daemon is already running." alreadyRunning = error "Daemon is already running."
@ -108,7 +116,17 @@ checkDaemon pidfile = do
" (got " ++ show pid' ++ " (got " ++ show pid' ++
"; expected " ++ show pid ++ " )" "; expected " ++ show pid ++ " )"
#else #else
checkDaemon pidfile = maybe Nothing readish <$> catchMaybeIO (readFile pidfile) checkDaemon pidfile = maybe (return Nothing) (check . readish)
=<< catchMaybeIO (readFile pidfile)
where
check Nothing = return Nothing
check (Just pid) = do
v <- lockShared =<< winLockFile pid pidfile
case v of
Just h -> do
dropLock h
return Nothing
Nothing -> return (Just pid)
#endif #endif
{- Stops the daemon, safely. -} {- Stops the daemon, safely. -}
@ -122,3 +140,20 @@ stopDaemon pidfile = go =<< checkDaemon pidfile
#else #else
terminatePID pid terminatePID pid
#endif #endif
{- Windows locks a lock file that corresponds with the pid of the process.
- This allows changing the process in the pid file and taking a new lock
- when eg, restarting the daemon.
-}
#ifdef mingw32_HOST_OS
winLockFile :: PID -> FilePath -> IO FilePath
winLockFile pid pidfile = do
cleanstale
return $ prefix ++ show pid ++ suffix
where
prefix = pidfile ++ "."
suffix = ".lck"
cleanstale = mapM_ (void . tryIO . removeFile) =<<
(filter iswinlockfile <$> dirContents (parentDir pidfile))
iswinlockfile f = suffix `isSuffixOf` f && prefix `isPrefixOf` f
#endif

View file

@ -52,9 +52,6 @@ now! --[[Joey]]
there. there.
* Deleting a git repository from inside the webapp fails "RemoveDirectory * Deleting a git repository from inside the webapp fails "RemoveDirectory
permision denined ... file is being used by another process" permision denined ... file is being used by another process"
* Shutting down the webapp does not stop the daemon; the ctrl-c hack
doesn't work. (Restarting the daemon also does not stop the old process,
same reason.)
## stuff needing testing ## stuff needing testing