Unixutils 1.50 → 1.54.3
raw patch · 12 files changed
Files
- Setup.hs +1/−1
- System/Unix/Chroot.hs +25/−18
- System/Unix/Crypt.hs +3/−3
- System/Unix/Directory.hs +6/−6
- System/Unix/FilePath.hsc +1/−1
- System/Unix/Files.hs +1/−1
- System/Unix/KillByCwd.hs +44/−0
- System/Unix/Misc.hs +3/−1
- System/Unix/Mount.hs +81/−6
- System/Unix/Process.hs +0/−89
- System/Unix/SpecialDevice.hs +27/−27
- Unixutils.cabal +20/−8
Setup.hs view
@@ -1,7 +1,7 @@ #!/usr/bin/runhaskell import Distribution.Simple-import System.Cmd+import System.Process import System.Exit main = defaultMainWithHooks simpleUserHooks
System/Unix/Chroot.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE CPP #-} -- | This module, except for useEnv, is copied from the build-env package. module System.Unix.Chroot ( fchroot@@ -7,8 +8,9 @@ -- , forceList' ) where -import Control.Exception (finally, evaluate)-+import Control.Exception (evaluate)+import Control.Monad.Catch (MonadMask, finally)+import Control.Monad.Trans (MonadIO, liftIO) import qualified Data.ByteString.Char8 as B import qualified Data.ByteString.Lazy.Char8 as L import Foreign.C.Error@@ -41,13 +43,17 @@ -- chroot and working directory of all the threads in the process, -- so... -- NOTE: will throw IOError if internal chroot fails-fchroot :: FilePath -> IO a -> IO a+fchroot :: (MonadIO m, MonadMask m) => FilePath -> m a -> m a fchroot path action =- do origWd <- getWorkingDirectory- rootFd <- openFd "/" ReadOnly Nothing defaultFileFlags- chroot path- changeWorkingDirectory "/"- action `finally` (breakFree origWd rootFd)+ do origWd <- liftIO $ getWorkingDirectory+#if MIN_VERSION_unix(2,8,0)+ rootFd <- liftIO $ openFd "/" ReadOnly defaultFileFlags+#else+ rootFd <- liftIO $ openFd "/" ReadOnly Nothing defaultFileFlags+#endif+ liftIO $ chroot path+ liftIO $ changeWorkingDirectory "/"+ action `finally` (liftIO $ breakFree origWd rootFd) where breakFree origWd rootFd = do changeWorkingDirectoryFd rootFd@@ -58,15 +64,15 @@ -- |The ssh inside of the chroot needs to be able to talk to the -- running ssh-agent. Therefore we mount --bind the ssh agent socket -- dir inside the chroot (and umount it when we exit the chroot.-useEnv :: FilePath -> (a -> IO a) -> IO a -> IO a+useEnv :: (MonadIO m, MonadMask m) => FilePath -> (a -> m a) -> m a -> m a useEnv rootPath force action = do -- In order to minimize confusion, this QIO message is output -- at default quietness. If you want to suppress it while seeing -- the output from your action, you need to say something like -- quieter (+ 1) (useEnv (quieter (\x->x-1) action))- sockPath <- getEnv "SSH_AUTH_SOCK"- home <- getEnv "HOME"- copySSH home+ sockPath <- liftIO $ getEnv "SSH_AUTH_SOCK"+ home <- liftIO $ getEnv "HOME"+ liftIO $ copySSH home -- We need to force the output before we exit the changeroot. -- Otherwise we lose our ability to communicate with the ssh -- agent and we get errors.@@ -75,18 +81,19 @@ copySSH Nothing = return () copySSH (Just home) = -- Do NOT preserve ownership, files must be owned by root.+ createDirectoryIfMissing True (rootPath ++ "/root") >> run "/usr/bin/rsync" ["-rlptgDHxS", "--delete", home ++ "/.ssh/", rootPath ++ "/root/.ssh"]+ withSock :: (MonadIO m, MonadMask m) => Maybe FilePath -> m a -> m a withSock Nothing action = action withSock (Just sockPath) action = withMountBind dir (rootPath ++ dir) action where dir = dropTrailingPathSeparator (dropFileName sockPath)+ withMountBind :: (MonadIO m, MonadMask m) => FilePath -> FilePath -> m a -> m a withMountBind toMount mountPoint action =- do createDirectoryIfMissing True mountPoint- run "/bin/mount" ["--bind", escapePathForMount toMount, escapePathForMount mountPoint]- result <- action- run "/bin/umount" [escapePathForMount mountPoint]- return result- escapePathForMount = id -- FIXME - Path arguments should be escaped+ (do liftIO $ createDirectoryIfMissing True mountPoint+ liftIO $ run "/bin/mount" ["--bind", escapePathForMount toMount, escapePathForMount mountPoint]+ action) `finally` (liftIO $ run "/bin/umount" [escapePathForMount mountPoint])+ escapePathForMount = id -- FIXME - Path arguments should be escaped run cmd args = do (code, out, err) <- readProcessWithExitCode cmd args ""
System/Unix/Crypt.hs view
@@ -4,7 +4,7 @@ -- Module : System.Unix.Crypt -- Copyright : (c) 2010 -- License : BSD3--- +-- -- Maintainer : jeremy@seereason.com -- Stability : provisional -- Portability : non-portable (requires POSIX)@@ -13,7 +13,7 @@ -- ----------------------------------------------------------------------------- -module System.Unix.Crypt +module System.Unix.Crypt ( crypt ) where@@ -23,7 +23,7 @@ foreign import ccall unsafe "unistd.h crypt" c_crypt :: CString -> CString -> IO CString --- | calls crypt(3) +-- | calls crypt(3) crypt :: String -- ^ key -> String -- ^ salt -> IO String -- ^ encrypted password
System/Unix/Directory.hs view
@@ -12,7 +12,7 @@ import Control.Exception import Data.List (isSuffixOf)-import System.Cmd+import System.Process import System.Directory import System.Exit import System.FilePath@@ -28,7 +28,7 @@ do status <- getSymbolicLinkStatus path case isDirectory status of- True -> + True -> do subs <- getDirectoryContents path >>= return . map (path </>) . filter (not . flip elem [".", ".."]) >>=@@ -84,7 +84,7 @@ -- when you are inside a chroot. removeRecursiveSafely :: FilePath -> IO () removeRecursiveSafely path =- traverse path removeFile removeDirectory umount+ System.Unix.Directory.traverse path removeFile removeDirectory umount where umount path = do@@ -102,7 +102,7 @@ -- unmounts anything it finds mounted. Note that this can be much -- slower than Mount.umountBelow, use that instead. unmountRecursiveSafely path =- traverse path noOp noOp umount+ System.Unix.Directory.traverse path noOp noOp umount where noOp _ = return () umount path =@@ -134,7 +134,7 @@ -- |temporarily change the working directory to |dir| while running |action| withWorkingDirectory :: FilePath -> IO a -> IO a-withWorkingDirectory dir action = +withWorkingDirectory dir action = bracket getCurrentDirectory setCurrentDirectory (\ _ -> setCurrentDirectory dir >> action) -- |create a temporary directory, run the action, remove the temporary directory@@ -153,7 +153,7 @@ c_mkdtemp :: CString -> IO CString mkdtemp :: FilePath -> IO FilePath-mkdtemp template = +mkdtemp template = withCString (if "XXXXXX" `isSuffixOf` template then template else (template ++ "XXXXXX")) $ \ ptr -> do cname <- throwErrnoIfNull "mkdtemp" (c_mkdtemp ptr) name <- peekCString cname
System/Unix/FilePath.hsc view
@@ -3,7 +3,7 @@ -- | The function splitFileName is taken from missingh, at the moment -- missingh will not build under sid. -module System.Unix.FilePath +module System.Unix.FilePath (dirName, baseName, realpath,
System/Unix/Files.hs view
@@ -10,7 +10,7 @@ forceSymbolicLink :: FilePath -> FilePath -> IO () forceSymbolicLink target linkName = createSymbolicLink target linkName `catch`- (\e -> if isAlreadyExistsError e + (\e -> if isAlreadyExistsError e then do removeLink linkName createSymbolicLink target linkName else ioError e)
+ System/Unix/KillByCwd.hs view
@@ -0,0 +1,44 @@+{-# LANGUAGE ScopedTypeVariables #-}+-- |A place to collect and hopefully retire all the random ways of+-- running shell commands that have accumulated over the years.+module System.Unix.KillByCwd+ ( killByCwd+ ) where++import Control.Exception (catch)+import Control.Monad (liftM, filterM)+import Data.Char (isDigit)+import Data.List (isPrefixOf)+import Prelude hiding (catch)+import System.Directory (getDirectoryContents)+import System.Posix.Files (readSymbolicLink)+import System.Posix.Signals (signalProcess, sigTERM)++{-+NOTE:+++ We should make sure this works if we are inside a chroot.+++ path needs to be absolute or we might kill processes living in+ similarly named, but different directories.+++ path is an canoncialised, absolute path, such as what realpath returns++-}+-- | Kill the processes whose working directory is in or under the+-- given directory.+killByCwd :: FilePath -> IO [(String, Maybe String)]+killByCwd path =+ do pids <- liftM (filter (all isDigit)) (getDirectoryContents "/proc")+ cwdPids <- filterM (isCwd path) pids+ exePaths <- mapM exePath cwdPids+ mapM_ kill cwdPids+ return (zip cwdPids exePaths)+ where+ isCwd :: FilePath -> String -> IO Bool+ isCwd cwd pid =+ (liftM (isPrefixOf cwd) (readSymbolicLink ("/proc/" ++ pid ++"/cwd"))) `catch` (\ (_ :: IOError) -> return False)+ exePath :: String -> IO (Maybe String)+ exePath pid = (readSymbolicLink ("/proc/" ++ pid ++"/exe") >>= return . Just) `catch` (\ (_ :: IOError) -> return Nothing)+ kill :: String -> IO ()+ kill pidStr = signalProcess sigTERM (read pidStr)
System/Unix/Misc.hs view
@@ -11,13 +11,14 @@ import Data.ByteString.Lazy.Char8 (empty, readFile, writeFile) import Data.Digest.Pure.MD5 (md5) import Data.Maybe-import System.Cmd+import System.Process import System.Directory import System.Exit import System.IO import System.Posix.Files import System.Process +-- | Deprecated: Use @Data.ByteString.Lazy.Char8.readFile path >>= return . show . Data.Digest.Pure.MD5.md5@ {-# DEPRECATED md5sum "Use Data.ByteString.Lazy.Char8.readFile path >>= return . show . Data.Digest.Pure.MD5.md5" #-} md5sum :: FilePath -> IO String md5sum path = Data.ByteString.Lazy.Char8.readFile path >>= return . show . md5@@ -33,6 +34,7 @@ _ -> error ("Error running 'md5sum " ++ path ++ "'") -} +-- | Deprecated: Use @Data.ByteString.Lazy.Char8.readFile path >>= Data.ByteString.Lazy.Char8.writeFile (path ++ \".gz\")@ {-# DEPRECATED gzip "Use Data.ByteString.Lazy.Char8.readFile path >>= Data.ByteString.Lazy.Char8.writeFile (path ++ \".gz\")" #-} gzip :: FilePath -> IO () gzip path = Data.ByteString.Lazy.Char8.readFile path >>= Data.ByteString.Lazy.Char8.writeFile (path ++ ".gz")
System/Unix/Mount.hs view
@@ -1,10 +1,16 @@+{-# LANGUAGE GeneralizedNewtypeDeriving, ScopedTypeVariables #-} -- |functions for mounting, umounting, parsing \/proc\/mounts, etc-module System.Unix.Mount - (umountBelow, -- FilePath -> IO [(FilePath, (String, String, ExitCode))]- umount, -- [String] -> IO (String, String, ExitCode)- isMountPoint) -- FilePath -> IO Bool- where+module System.Unix.Mount+ ( umountBelow -- FilePath -> IO [(FilePath, (String, String, ExitCode))]+ , umount -- [String] -> IO (String, String, ExitCode)+ , isMountPoint -- FilePath -> IO Bool + , withMount+ , WithProcAndSys(runWithProcAndSys)+ , withProcAndSys+ , withTmp+ ) where+ -- Standard GHC modules import Control.Monad@@ -16,6 +22,21 @@ import System.Posix.Files import System.Process (readProcessWithExitCode) +import Control.Applicative (Applicative)+import Control.Exception (catch)+import Control.Monad.Catch (bracket, MonadCatch, MonadMask)+import Control.Monad.Trans (MonadTrans, lift, liftIO, MonadIO)+-- import Control.Monad.Trans.Except ({- ExceptT instances -})+import Data.ByteString.Lazy as L (ByteString, empty)+import GHC.IO.Exception (IOErrorType(OtherError))+import System.Directory (createDirectoryIfMissing)+import System.Exit (ExitCode(ExitFailure, ExitSuccess))+import System.FilePath ((</>))+import System.IO (hPutStrLn, stderr)+import System.IO.Error+import System.Process (CreateProcess, proc)+import System.Process.ListLike (readCreateProcess, showCreateProcessForUser)+ -- Local Modules -- In ghc610 readFile "/proc/mounts" hangs. Use this instead.@@ -78,7 +99,7 @@ unescape ('\\':'1':'3':'4':rest) = '\\' : (unescape rest) unescape (c:rest) = c : (unescape rest) --- |'escape' - \/proc\/mount stytle string escaper+-- |'escape' - \/proc\/mount style string escaper escape :: String -> String escape [] = [] escape (' ':rest) = ('\\':'0':'4':'0':escape rest)@@ -115,3 +136,57 @@ -- It is hard to know what is going on if . or .. don't exist. -- Assume we are seeing some sort of mount point. return True++readProcess :: CreateProcess -> L.ByteString -> IO L.ByteString+readProcess p input = do+ (code, out, _err) <- readCreateProcess p input :: IO (ExitCode, L.ByteString, L.ByteString)+ case code of+ ExitFailure n -> ioError (mkIOError OtherError (showCreateProcessForUser p ++ " -> " ++ show n) Nothing Nothing)+ ExitSuccess -> return out++-- | Do an IO task with a file system remounted using mount --bind.+-- This was written to set up a build environment.+withMount :: (MonadIO m, MonadMask m) => FilePath -> FilePath -> m a -> m a+withMount directory mountpoint task =+ bracket pre (\ _ -> post) (\ _ -> task)+ where+ mount = proc "mount" ["--bind", directory, mountpoint]+ umount = proc "umount" [mountpoint]+ umountLazy = proc "umount" ["-l", mountpoint]++ pre = liftIO $ do -- hPutStrLn stderr $ "mounting /proc at " ++ show mountpoint+ createDirectoryIfMissing True mountpoint+ readProcess mount L.empty++ post = liftIO $ do -- hPutStrLn stderr $ "unmounting /proc at " ++ show mountpoint+ readProcess umount L.empty+ `catch` (\ (e :: IOError) ->+ do hPutStrLn stderr ("Exception unmounting " ++ mountpoint ++ ", trying -l: " ++ show e)+ readProcess umountLazy L.empty)++-- | Monad transformer to ensure that /proc and /sys are mounted+-- during a computation.+newtype WithProcAndSys m a = WithProcAndSys { runWithProcAndSys :: m a } deriving (Functor, Monad, Applicative)++instance MonadTrans WithProcAndSys where+ lift = WithProcAndSys++instance MonadIO m => MonadIO (WithProcAndSys m) where+ liftIO task = WithProcAndSys (liftIO task)++-- | Mount /proc and /sys in the specified build root and execute a+-- task. Typically, the task would start with a chroot into the build+-- root. If the build root given is "/" it is assumed that the file+-- systems are already mounted, no mounting or unmounting is done.+withProcAndSys :: (MonadIO m, MonadMask m) => FilePath -> WithProcAndSys m a -> m a+withProcAndSys "/" task = runWithProcAndSys task+withProcAndSys root task = do+ exists <- liftIO $ doesDirectoryExist root+ case exists of+ True -> withMount "/proc" (root </> "proc") $ withMount "/sys" (root </> "sys") $ runWithProcAndSys task+ False -> liftIO $ ioError $ mkIOError doesNotExistErrorType "chroot directory does not exist" Nothing (Just root)++-- | Do an IO task with /tmp remounted. This could be used+-- to share /tmp with a build root.+withTmp :: (MonadIO m, MonadMask m) => FilePath -> m a -> m a+withTmp root task = withMount "/tmp" (root </> "tmp") task
− System/Unix/Process.hs
@@ -1,89 +0,0 @@--- | Variations of the readProcess and readProcessWithExitCode functions--- from System.Process which read and write ByteStrings and have an--- extra argument to modify the CreateProcess value before the process--- is started.-module System.Unix.Process- ( readProcess- , readProcessWithExitCode- ) where--import Control.Concurrent (newEmptyMVar, forkIO, putMVar, takeMVar)-import qualified Control.Exception as C-import Control.Monad (when)-import qualified Data.ByteString.Lazy.Char8 as B-import GHC.IO.Exception (IOErrorType(OtherError))-import System.Exit (ExitCode(..))-import System.IO (hFlush, hClose)-import System.IO.Error (mkIOError)-import System.Process (CreateProcess(std_in, std_out, std_err, cwd), createProcess, waitForProcess, proc, StdStream(CreatePipe, Inherit), showCommandForUser)--readProcessWithExitCode- :: FilePath -- ^ command to run- -> [String] -- ^ any arguments- -> (CreateProcess -> CreateProcess) -- ^ Modify process with this - use id for System.Process.readProcessWithExitCode behavior- -> B.ByteString -- ^ standard input- -> IO (ExitCode, B.ByteString, B.ByteString) -- ^ exitcode, stdout, stderr-readProcessWithExitCode cmd args modify input = do- let modify' p = modify (p {std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe })-- (Just inh, Just outh, Just errh, pid) <-- createProcess (modify' (proc cmd args))-- outMVar <- newEmptyMVar-- -- fork off a thread to start consuming stdout- out <- B.hGetContents outh- _ <- forkIO $ C.evaluate (B.length out) >> putMVar outMVar ()-- -- fork off a thread to start consuming stderr- err <- B.hGetContents errh- _ <- forkIO $ C.evaluate (B.length err) >> putMVar outMVar ()-- -- now write and flush any input- when (not (B.null input)) $ do B.hPutStr inh input; hFlush inh- hClose inh -- done with stdin-- -- wait on the output- takeMVar outMVar- takeMVar outMVar- hClose outh- hClose errh-- -- wait on the process- ex <- waitForProcess pid-- return (ex, out, err)--readProcess- :: FilePath -- ^ command to run- -> [String] -- ^ any arguments- -> (CreateProcess -> CreateProcess) -- ^ modifies CreateProcess before passing to createProcess- -> B.ByteString -- ^ standard input- -> IO B.ByteString -- ^ stdout-readProcess cmd args modify input = do- let modify' p = modify (p {std_in = CreatePipe, std_out = CreatePipe, std_err = Inherit })- (Just inh, Just outh, _, pid) <-- createProcess (modify' (proc cmd args))-- -- fork off a thread to start consuming the output- output <- B.hGetContents outh- outMVar <- newEmptyMVar- _ <- forkIO $ C.evaluate (B.length output) >> putMVar outMVar ()-- -- now write and flush any input- when (not (B.null input)) $ do B.hPutStr inh input; hFlush inh- hClose inh -- done with stdin-- -- wait on the output- takeMVar outMVar- hClose outh-- -- wait on the process- ex <- waitForProcess pid-- case ex of- ExitSuccess -> return output- ExitFailure r ->- ioError (mkIOError OtherError ("readProcess: " ++ showCommandForUser cmd args ++- " (exit " ++ show r ++ ")")- Nothing Nothing)
System/Unix/SpecialDevice.hs view
@@ -2,30 +2,30 @@ -- | Construct an ADT representing block and character devices -- (but mostly block devices) by interpreting the contents of -- the Linux sysfs filesystem.-module System.Unix.SpecialDevice +module System.Unix.SpecialDevice (SpecialDevice,- sysMountPoint, -- IO String- ofNode, -- FilePath -> IO (Maybe SpecialDevice)- ofNodeStatus, -- FileStatus -> Maybe SpecialDevice- ofPath, -- FilePath -> IO (Maybe SpecialDevice)- rootPart, -- IO (Maybe SpecialDevice)- ofDevNo, -- (DeviceID -> SpecialDevice) -> Int -> SpecialDevice- ofSysName, -- String -> IO (Maybe SpecialDevice)- ofSysPath, -- (DeviceID -> SpecialDevice) -> FilePath -> IO (Maybe SpecialDevice)- toDevno, -- SpecialDevice -> Int- --major, -- SpecialDevice -> Int- --minor, -- SpecialDevice -> Int- ofMajorMinor, -- (DeviceID -> SpecialDevice) -> Int -> Int -> SpecialDevice- node, -- SpecialDevice -> IO (Maybe FilePath)- nodes, -- SpecialDevice -> IO [FilePath]- sysName, -- SpecialDevice -> IO (Maybe String)- splitPart, -- String -> (String, Int)- sysDir, -- SpecialDevice -> IO (Maybe FilePath)- diskOfPart, -- SpecialDevice -> IO (Maybe SpecialDevice)- getAllDisks, -- IO [SpecialDevice]- getAllPartitions, -- IO [SpecialDevice]- getAllCdroms, -- IO [SpecialDevice]- getAllRemovable, -- IO [SpecialDevice]+ sysMountPoint, -- IO String+ ofNode, -- FilePath -> IO (Maybe SpecialDevice)+ ofNodeStatus, -- FileStatus -> Maybe SpecialDevice+ ofPath, -- FilePath -> IO (Maybe SpecialDevice)+ rootPart, -- IO (Maybe SpecialDevice)+ ofDevNo, -- (DeviceID -> SpecialDevice) -> Int -> SpecialDevice+ ofSysName, -- String -> IO (Maybe SpecialDevice)+ ofSysPath, -- (DeviceID -> SpecialDevice) -> FilePath -> IO (Maybe SpecialDevice)+ toDevno, -- SpecialDevice -> Int+ --major, -- SpecialDevice -> Int+ --minor, -- SpecialDevice -> Int+ ofMajorMinor, -- (DeviceID -> SpecialDevice) -> Int -> Int -> SpecialDevice+ node, -- SpecialDevice -> IO (Maybe FilePath)+ nodes, -- SpecialDevice -> IO [FilePath]+ sysName, -- SpecialDevice -> IO (Maybe String)+ splitPart, -- String -> (String, Int)+ sysDir, -- SpecialDevice -> IO (Maybe FilePath)+ diskOfPart, -- SpecialDevice -> IO (Maybe SpecialDevice)+ getAllDisks, -- IO [SpecialDevice]+ getAllPartitions, -- IO [SpecialDevice]+ getAllCdroms, -- IO [SpecialDevice]+ getAllRemovable, -- IO [SpecialDevice] -- toDevName, -- getBlkidAlist, -- getBlkidInfo,@@ -78,7 +78,7 @@ (Just . BlockDevice . specialDeviceID $ status) else if isCharacterDevice status then (Just . CharacterDevice . specialDeviceID $ status) else- Nothing + Nothing ofSysName :: String -> IO (Maybe SpecialDevice) ofSysName name =@@ -144,7 +144,7 @@ sysName dev = sysDir dev >>= return . maybe Nothing (Just . basename) sysDir :: SpecialDevice -> IO (Maybe FilePath)-sysDir dev@(BlockDevice _) = +sysDir dev@(BlockDevice _) = do (pairs' :: [(FilePath, FileStatus)]) <- directory_find False (sysMountPoint ++ "/block") let (paths :: [FilePath]) = map fst . filter isDev $ pairs'@@ -194,10 +194,10 @@ x -> True getAllCdroms :: IO [SpecialDevice]-getAllCdroms = cdromGroup >>= getDisksInGroup +getAllCdroms = cdromGroup >>= getDisksInGroup getAllRemovable :: IO [SpecialDevice]-getAllRemovable = floppyGroup >>= getDisksInGroup +getAllRemovable = floppyGroup >>= getDisksInGroup -- ofNode "/dev/sda1" >>= maybe (return Nothing) sysDir >>= putStrLn . show -- -> Just "/sys/block/sda/sda1/dev"
Unixutils.cabal view
@@ -1,29 +1,41 @@ Name: Unixutils-Version: 1.50+Version: 1.54.3 License: BSD3 License-File: COPYING Author: Jeremy Shaw, David Fox-Homepage: http://src.seereason.com/haskell-unixutils+Homepage: https://github.com/seereason/haskell-unixutils.git Category: System Synopsis: A crude interface between Haskell and Unix-like operating systems-Maintainer: jeremy@n-heptane.com+Maintainer: David Fox <ddssff@gmail.com> Description: A collection of useful and mildly useful functions that you might expect to find in System.* which a heavy bias towards Unix-type operating systems. Build-type: Simple-Cabal-Version: >= 1.2+Cabal-Version: >= 1.10 -Library - Build-Depends: base >= 4 && <5, bytestring, directory, filepath, process, pureMD5, regex-tdfa, unix, zlib- ghc-options: -O2+Library+ Default-Language: Haskell2010+ Build-Depends:+ base >= 4.6 && <5,+ bytestring,+ directory,+ exceptions,+ filepath,+ mtl,+ process,+ process-extras >= 0.3,+ pureMD5,+ regex-tdfa,+ unix,+ zlib Exposed-modules: System.Unix.Chroot, System.Unix.Crypt, System.Unix.Directory, System.Unix.FilePath,+ System.Unix.KillByCwd, System.Unix.Misc, System.Unix.Mount,- System.Unix.Process, System.Unix.SpecialDevice, System.Unix.Files if !os(darwin)