mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-05-20 08:32:50 -06:00
GF.Infra.SIO.hs: adding the SIO monad (where S = Shell or Safe)
The SIO monad is a restriction of the IO monad with two purposes: + Access to arbitrary IO operations can be turned off by setting the environment variable GF_RESTRICTED. There is a limited set of IO operations that are considered safe and always allowed. + It allows output to stdout to be captured. This can be used in gf -server mode, where output of GF shell commands is made part of HTTP responses returned to clients.
This commit is contained in:
91
src/compiler/GF/Infra/SIO.hs
Normal file
91
src/compiler/GF/Infra/SIO.hs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
-- | Shell IO: a monad that can restrict acesss to arbitrary IO and has the
|
||||||
|
-- ability to capture output that normally would be sent to stdout.
|
||||||
|
module GF.Infra.SIO(
|
||||||
|
-- * The SIO monad
|
||||||
|
SIO,
|
||||||
|
-- * Running SIO operations
|
||||||
|
runSIO,hRunSIO,captureSIO,
|
||||||
|
-- * Unrestricted, safe operations
|
||||||
|
-- ** From the standard libraries
|
||||||
|
getCPUTime,getCurrentDirectory,getLibraryDirectory,
|
||||||
|
newStdGen,print,putStrLn,
|
||||||
|
-- ** Specific to GF
|
||||||
|
importGrammar,importSource,
|
||||||
|
putStrLnFlush,runInterruptibly,
|
||||||
|
-- * Restricted accesss to arbitrary (potentially unsafe) IO operations
|
||||||
|
-- | If the environment variable GF_RESTRICTED is defined, these
|
||||||
|
-- operations will fail. Otherwise, they will be executed normally.
|
||||||
|
-- Output to stdout will /not/ be captured or redirected.
|
||||||
|
restricted,restrictedSystem
|
||||||
|
) where
|
||||||
|
import Prelude hiding (putStrLn,print)
|
||||||
|
import Control.Monad(liftM)
|
||||||
|
import System.IO(Handle,hPutStrLn,hFlush,stdout)
|
||||||
|
import System.IO.Error(try)
|
||||||
|
import System.Cmd(system)
|
||||||
|
import System.Environment(getEnv)
|
||||||
|
import Control.Concurrent.Chan(newChan,writeChan,getChanContents)
|
||||||
|
import qualified System.CPUTime as IO(getCPUTime)
|
||||||
|
import qualified System.Directory as IO(getCurrentDirectory)
|
||||||
|
import qualified System.Random as IO(newStdGen)
|
||||||
|
import qualified GF.Infra.UseIO as IO(getLibraryDirectory)
|
||||||
|
import qualified GF.System.Signal as IO(runInterruptibly)
|
||||||
|
import qualified GF.Command.Importing as GF(importGrammar, importSource)
|
||||||
|
|
||||||
|
-- * The SIO monad
|
||||||
|
|
||||||
|
type PutStrLn = String -> IO ()
|
||||||
|
newtype SIO a = SIO {unS::PutStrLn->IO a}
|
||||||
|
|
||||||
|
instance Functor SIO where fmap = liftM
|
||||||
|
|
||||||
|
instance Monad SIO where
|
||||||
|
return x = SIO (const (return x))
|
||||||
|
SIO m1 >>= xm2 = SIO $ \ h -> m1 h >>= \ x -> unS (xm2 x) h
|
||||||
|
|
||||||
|
-- * Running SIO operations
|
||||||
|
|
||||||
|
-- | Run normally
|
||||||
|
runSIO = hRunSIO stdout
|
||||||
|
|
||||||
|
-- | Redirect 'stdout' to the given handle
|
||||||
|
hRunSIO h sio = unS sio (\s->hPutStrLn h s>>hFlush h)
|
||||||
|
|
||||||
|
-- | Capture 'stdout'
|
||||||
|
captureSIO sio = do ch <- newChan
|
||||||
|
result <- unS sio (writeChan ch . Just)
|
||||||
|
writeChan ch Nothing
|
||||||
|
output <- fmap takeJust (getChanContents ch)
|
||||||
|
return (output,result)
|
||||||
|
where
|
||||||
|
takeJust (Just xs:ys) = xs++takeJust ys
|
||||||
|
takeJust _ = []
|
||||||
|
|
||||||
|
-- * Restricted accesss to arbitrary (potentially unsafe) IO operations
|
||||||
|
|
||||||
|
restricted io = SIO (const (restrictedIO io))
|
||||||
|
restrictedSystem = restricted . system
|
||||||
|
|
||||||
|
restrictedIO io =
|
||||||
|
either (const io) (const $ fail message) =<< try (getEnv "GF_RESTRICTED")
|
||||||
|
where
|
||||||
|
message =
|
||||||
|
"This operation is not allowed when GF is running in restricted mode."
|
||||||
|
|
||||||
|
-- * Unrestricted, safe IO operations
|
||||||
|
|
||||||
|
lift0 io = SIO $ const io
|
||||||
|
lift1 f io = SIO $ f . unS io
|
||||||
|
|
||||||
|
putStrLn = putStrLnFlush
|
||||||
|
putStrLnFlush s = SIO ($ s)
|
||||||
|
print x = putStrLn (show x)
|
||||||
|
|
||||||
|
getCPUTime = lift0 IO.getCPUTime
|
||||||
|
getCurrentDirectory = lift0 IO.getCurrentDirectory
|
||||||
|
getLibraryDirectory = lift0 . IO.getLibraryDirectory
|
||||||
|
newStdGen = lift0 IO.newStdGen
|
||||||
|
runInterruptibly = lift1 IO.runInterruptibly
|
||||||
|
|
||||||
|
importGrammar pgf opts files = lift0 $ GF.importGrammar pgf opts files
|
||||||
|
importSource src opts files = lift0 $ GF.importSource src opts files
|
||||||
Reference in New Issue
Block a user