-- | Daemonize a shellmate computation.
--   Not present on Windows, unless shellmate is installed with the
--   @with-posix@ flag.
module Control.Shell.Daemon (daemonize) where
import Control.Shell hiding (stdin)
import System.Directory
import System.Exit
import System.IO
import System.Posix

-- | Daemonize a shellmate computation. This should be the last thing a
--   computation does, as this function will terminate the parent computation.
--   In short, daemonizing a computation involves setting the file creation
--   mask to 0, closing standard input, output and error file descriptors,
--   blocking sighup and changing the working directory to @/@.
--
--   On Windows without Cygwin, @daemonize@ is a no-op. Consider running any
--   program intended to be deamonized using @START /B your_app@, or better yet,
--   rewriting it as a Windows service.
daemonize :: Shell () -> Shell ()
daemonize m = do
    env <- getShellEnv
    unsafeLiftIO $ do
      void $ setFileCreationMask 0
      void $ forkProcess (p env)
      exitImmediately ExitSuccess
  where
    p env = do
      void $ createSession
      void $ forkProcess (p' env)
      exitImmediately ExitSuccess
    p' env = do
      changeWorkingDirectory "/"
      devnull <- openFd "/dev/null" ReadWrite Nothing defaultFileFlags
      mapM_ closeFd [stdInput, stdOutput, stdError]
      mapM_ (dupTo devnull) [stdInput, stdOutput, stdError]
      void $ installHandler sigHUP Ignore Nothing
      dir <- getCurrentDirectory
      res <- flip runSh m $ env
        { envStdIn   = stdin
        , envStdOut  = stdout
        , envStdErr  = stderr
        , envWorkDir = dir
        , envEnvVars = envEnvVars env
        }
      case res of
        Left (Failure _) -> exitFailure
        _                -> exitSuccess