summaryrefslogtreecommitdiff
path: root/lib/ProcessUtils.hs
blob: a6902beb1340b15212dbc02e384e7e09edcc2528 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
{-# LANGUAGE CPP #-}
module ProcessUtils
    ( ExitCode(ExitFailure,ExitSuccess)
    , systemEnv
    , readPipe
    , readProcessWithErrorH
    ) where

import GHC.IO.Exception ( ioException, IOErrorType(..) )
import System.Process
import System.Posix.Signals
import System.Process.Internals (runGenProcess_,defaultSignal)
import System.Environment
import Data.Maybe ( isNothing )
import System.IO.Error ( mkIOError, ioeSetErrorString ) 
import System.Exit ( ExitCode(..) )
import System.IO
import Control.Applicative
import Control.Exception (bracket)


-- | systemEnv
-- This is like System.Process.system except that it lets you set
-- some environment variables.
systemEnv :: [(String, String)] -> String -> IO ExitCode
systemEnv _    ""  = 
    ioException (ioeSetErrorString (mkIOError InvalidArgument "system" Nothing Nothing) "null command")
systemEnv vars cmd = do
    env0 <- getEnvironment
    let env1 = filter (isNothing . flip lookup vars . fst) env0
        env = vars ++ env1
    syncProcess "system" $ (shell cmd) {env=Just env}
 where
    -- This is a non-exported function from System.Process
    syncProcess fun c = do
      -- The POSIX version of system needs to do some manipulation of signal
      -- handlers.  Since we're going to be synchronously waiting for the child,
      -- we want to ignore ^C in the parent, but handle it the default way
      -- in the child (using SIG_DFL isn't really correct, it should be the
      -- original signal handler, but the GHC RTS will have already set up
      -- its own handler and we don't want to use that).
      old_int  <- installHandler sigINT  Ignore Nothing
      old_quit <- installHandler sigQUIT Ignore Nothing
      (_,_,_,p) <- runGenProcess_ fun c
            (Just defaultSignal) (Just defaultSignal)
      r <- waitForProcess p
      _ <- installHandler sigINT  old_int Nothing
      _ <- installHandler sigQUIT old_quit Nothing
      return r

-- | readPipe
--
-- This is like System.Process.readProcess but instead of a single
-- command with arguments, you pass it a list which it then executes
-- piping the the output from the prior command into the input for
-- the next command. Like readProcess the next parameter is a string
-- which is sent as input into the initial standard in.
--
--  > readPipe [("grep",["HostConfig"]),("wc",["-l"]) =<< readFile "input"
--
-- is equivalent to shell code:
--
--  > grep HostConfig input | wc -l
--
readPipe :: [(FilePath,[String])] -> String -> IO String
readPipe [(cmd,args)] input = readProcess cmd args input
readPipe ((cmd,args):xs) stdin0 = do
    let p = (shell cmd) { std_in = CreatePipe
                        , std_out = CreatePipe
                        , std_err = CreatePipe
                        , cmdspec = RawCommand cmd args
                        }
    (Just sinh,Just sout,Just serr, ph) <- createProcess p
    hPutStr sinh stdin0
    hClose sinh
    readPipe0 xs sout
    where
        readPipe0 :: [(FilePath,[String])] -> Handle -> IO String
        readPipe0 ((cmd,args):xs) stdin0 = do
            let p = (shell cmd)
                        { std_in = UseHandle stdin0
                        , std_out = CreatePipe
                        , std_err = CreatePipe
                        , cmdspec = RawCommand cmd args
                        }
            (Nothing,Just sout,Just serr, ph) <- createProcess p
            readPipe0 xs sout
        readPipe0 [] h = hGetContents h


readProcessWithErrorH :: FilePath -> [String] -> String -> Handle -> IO String
readProcessWithErrorH cmd args stdin erH = do
    let p = (shell cmd) { std_in = CreatePipe
                        , std_out = CreatePipe
                        , std_err = UseHandle erH
                        , cmdspec = RawCommand cmd args
                        }
    bracket (createProcess p) 
            (\(Just sinh,Just sout,_, ph) ->
                mapM_ hClose [sinh,sout]         )
            (\(Just sinh,Just sout,_, ph) -> do
                hPutStr sinh stdin
                hClose sinh
                hGetContents sout                )