summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorhallgren <hallgren@chalmers.se>2012-09-25 18:38:13 +0000
committerhallgren <hallgren@chalmers.se>2012-09-25 18:38:13 +0000
commit1adc0ed9f7ef98480f441474353eb39293d988c7 (patch)
tree91a7c457c60a4b750df68a3b3e8863635a7fb149 /src
parent69de623c17ad9b71005beff1cb208ed9a0b90ee5 (diff)
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.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/GF/Infra/SIO.hs91
1 files changed, 91 insertions, 0 deletions
diff --git a/src/compiler/GF/Infra/SIO.hs b/src/compiler/GF/Infra/SIO.hs
new file mode 100644
index 000000000..f8c554aca
--- /dev/null
+++ b/src/compiler/GF/Infra/SIO.hs
@@ -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 \ No newline at end of file