packages feed

shellmate 0.3.2.2 → 0.3.3

raw patch · 6 files changed

+215/−9 lines, 6 filesdep +unix

Dependencies added: unix

Files

Control/Shell.hs view
@@ -35,8 +35,9 @@   , inTempDirectory, inCustomTempDirectory      -- * Working with handles-  , Handle, IOMode (..)-  , hFlush, hClose+  , Handle, IOMode (..), BufferMode (..)+  , hFlush, hClose, hReady+  , hGetBuffering, hSetBuffering   , getStdIn, getStdOut, getStdErr      -- * Text I/O
Control/Shell/Concurrent.hs view
@@ -1,15 +1,19 @@ {-# LANGUAGE CPP #-} -- | Concurrency for Shellmate programs. module Control.Shell.Concurrent (-    Future,+    Future, ThreadId,+    Control.Shell.Concurrent.forkIO, Control.Shell.Concurrent.killThread,+    fork2, fork3,     future, await, check,     parallel, parallel_,     chunks   ) where-import Control.Concurrent+import Control.Concurrent as CC import Control.Monad import Control.Shell+import Control.Shell.Internal (inEnv) import Data.IORef+import System.Process  -- | Only used to have something reliable to attach the futures' weakrefs to. type FinalizerHandle = IORef ThreadId@@ -35,12 +39,12 @@   env <- getShellEnv   unsafeLiftIO $ do     v <- newEmptyMVar-    tid <- forkIO $ runSh env m >>= putMVar v+    tid <- CC.forkIO $ runSh env m >>= putMVar v     -- We need a WeakRef to something that's not referenced by the computation     -- to be able to kill it when the result is not reachable. IORef to TID is     -- as good as anything.     r <- newIORef tid-    _ <- mkWeakIORef r (killThread tid)+    _ <- mkWeakIORef r (CC.killThread tid)     return $ Future r v  -- | Wait for a future value.@@ -73,3 +77,40 @@ chunks _ []                 = [] chunks n xs | length xs > n = take n xs : chunks n (drop n xs)             | otherwise     = [xs]++-- | Run a computation in a separate thread. If the thread throws an error,+--   it will simply die; the error will not propagate to its parent thread.+forkIO :: Shell () -> Shell ThreadId+forkIO m = do+  env <- getShellEnv+  unsafeLiftIO $ do+    CC.forkIO $ void $ runSh env m++-- | Run a computation in a separate thread, with its standard input and output+--   provided by the first and second handles returned respectively.+--   The handles are line buffered by default.+fork2 :: Shell () -> Shell (Handle, Handle, ThreadId)+fork2 m = do+  env <- getShellEnv+  (ri, wi) <- unsafeLiftIO createPipe+  (ro, wo) <- unsafeLiftIO createPipe+  mapM_ (flip hSetBuffering LineBuffering) [wi,wo]+  tid <- Control.Shell.Concurrent.forkIO $ do+    inEnv (env {envStdOut = wo, envStdIn = ri}) m+  return (wi, ro, tid)++-- | Like 'fork2', but adds a third handle for standard error.+fork3 :: Shell () -> Shell (Handle, Handle, Handle, ThreadId)+fork3 m = do+  env <- getShellEnv+  (ri, wi) <- unsafeLiftIO createPipe+  (ro, wo) <- unsafeLiftIO createPipe+  (re, we) <- unsafeLiftIO createPipe+  mapM_ (flip hSetBuffering LineBuffering) [wi,wo,we]+  tid <- inEnv (env {envStdOut = wo, envStdErr = we, envStdIn = ri}) $ do+    Control.Shell.Concurrent.forkIO m+  return (wi, ro, re, tid)++-- | Terminate a thread spawned by 'Control.Shell.Concurrent.forkIO'.+killThread :: ThreadId -> Shell ()+killThread = liftIO . CC.killThread
+ Control/Shell/Daemon.hs view
@@ -0,0 +1,48 @@+-- | 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
Control/Shell/Handle.hs view
@@ -1,9 +1,11 @@ -- | Writing Shell programs using 'Handle's. module Control.Shell.Handle (+    IO.BufferMode (..),     hPutStr, hPutStrLn,     hGetLine, hGetContents,     hGetBytes, hPutBytes, hGetByteLine, hGetByteContents,-    hFlush, hClose+    hReady, hFlush, hClose,+    hGetBuffering, hSetBuffering   ) where import qualified System.IO as IO import qualified Data.ByteString as BS@@ -24,6 +26,18 @@ -- | Flush a handle. hFlush :: IO.Handle -> Shell () hFlush = unsafeLiftIO . IO.hFlush++-- | Is the handle ready for reading?+hReady :: IO.Handle -> Shell Bool+hReady = unsafeLiftIO . IO.hReady++-- | Set the buffering mode of the given handle.+hSetBuffering :: IO.Handle -> IO.BufferMode -> Shell ()+hSetBuffering h = unsafeLiftIO . IO.hSetBuffering h++-- | Get the buffering mode of the given handle.+hGetBuffering :: IO.Handle -> Shell IO.BufferMode+hGetBuffering = unsafeLiftIO . IO.hGetBuffering  -- | Read a line of input from a handle. hGetLine :: IO.Handle -> Shell String
+ Control/Shell/Posix.hs view
@@ -0,0 +1,91 @@+-- | Working with daemons, users, groups, file permissions and file ownerships.+--   Not present on Windows unless shellmate is installed with the @with-posix@+--   flag.+module Control.Shell.Posix+  ( -- * Users and groups+    User, Group+  , getCurrentUser, Control.Shell.Posix.getCurrentGroups+  , getOwner, getGroup, getOwnership+  , setOwner, setGroup, setOwnership++    -- * File permissions+  , CMode+  , unionFileModes, intersectFileModes+  , nullFileMode+  , ownerReadMode, ownerWriteMode, ownerExecuteMode, ownerModes+  , groupReadMode, groupWriteMode, groupExecuteMode, groupModes+  , otherReadMode, otherWriteMode, otherExecuteMode, otherModes+  , stdFileMode, accessModes+  , Control.Shell.Posix.setFileMode, getFileMode+  , Control.Shell.Posix.setFileCreationMask++    -- * Daemonizing shell computations+  , daemonize+  ) where+import System.Posix as Posix+import Control.Shell+import Control.Shell.Daemon++type User = String+type Group = String++-- | Set the mode (permissions, etc.) of the given file.+setFileMode :: FilePath -> CMode -> Shell ()+setFileMode f = liftIO . Posix.setFileMode f++-- | Get the mode of the given file.+getFileMode :: FilePath -> Shell CMode+getFileMode = liftIO . fmap fileMode . getFileStatus++-- | Get the currently effective user name.+getCurrentUser :: Shell String+getCurrentUser = liftIO getEffectiveUserName++-- | Get the list of groups associated with the current process.+getCurrentGroups :: Shell [String]+getCurrentGroups = liftIO $ do+  gids <- Posix.getGroups+  mapM (fmap groupName . getGroupEntryForID) gids++-- | Set the owner of the given file.+setOwner :: User -> FilePath -> Shell ()+setOwner name file = liftIO $ do+  uid <- userID <$> getUserEntryForName name+  setOwnerAndGroup file uid (-1)++-- | Set the group of the given file.+setGroup :: Group -> FilePath -> Shell ()+setGroup group file = liftIO $ do+  gid <- groupID <$> getGroupEntryForName group+  setOwnerAndGroup file (-1) gid++-- | Set the owner and group of the given file.+setOwnership :: User -> Group -> FilePath -> Shell ()+setOwnership name group file = liftIO $ do+  uid <- userID <$> getUserEntryForName name+  gid <- groupID <$> getGroupEntryForName group+  setOwnerAndGroup file uid gid++-- | Get the owner of the given file.+getOwner :: FilePath -> Shell User+getOwner file = liftIO $ do+  uid <- fileOwner <$> getFileStatus file+  userName <$> getUserEntryForID uid++-- | Get the group of the given file.+getGroup :: FilePath -> Shell Group+getGroup file = liftIO $ do+  gid <- fileGroup <$> getFileStatus file+  groupName <$> getGroupEntryForID gid++-- | Get the owner and group of the given file.+getOwnership :: FilePath -> Shell (User, Group)+getOwnership file = liftIO $ do+  st <- getFileStatus file+  user <- userName <$> getUserEntryForID (fileOwner st)+  group <- groupName <$> getGroupEntryForID (fileGroup st)+  return (user, group)++-- | Set the file creation mask of this process.+setFileCreationMask :: CMode -> Shell CMode+setFileCreationMask = unsafeLiftIO . Posix.setFileCreationMask
shellmate.cabal view
@@ -1,5 +1,5 @@ name:                shellmate-version:             0.3.2.2+version:             0.3.3 synopsis:            Simple interface for shell scripting in Haskell. description:         Aims to simplify development of cross-platform shell scripts and similar things. homepage:            https://github.com/valderman/shellmate@@ -15,7 +15,13 @@ source-repository head     type:       git     location:   https://github.com/valderman/shellmate.git-    ++flag with-posix+    default: False+    description:+      Build the POSIX-specific Control.Shell.Posix module even when on Windows.+      This requires a POSIX compatibility layer such as Cygwin.+ library   exposed-modules:     Control.Shell@@ -41,3 +47,8 @@     Haskell2010   ghc-options:     -Wall+  if flag(with-posix) || !os(windows)+    cpp-options:     -DWITH_POSIX+    exposed-modules: Control.Shell.Posix+    other-modules:   Control.Shell.Daemon+    build-depends:   unix >=2.7 && <2.8