diff --git a/Control/Shell.hs b/Control/Shell.hs
--- a/Control/Shell.hs
+++ b/Control/Shell.hs
@@ -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
diff --git a/Control/Shell/Concurrent.hs b/Control/Shell/Concurrent.hs
--- a/Control/Shell/Concurrent.hs
+++ b/Control/Shell/Concurrent.hs
@@ -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
diff --git a/Control/Shell/Daemon.hs b/Control/Shell/Daemon.hs
new file mode 100644
--- /dev/null
+++ b/Control/Shell/Daemon.hs
@@ -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
diff --git a/Control/Shell/Handle.hs b/Control/Shell/Handle.hs
--- a/Control/Shell/Handle.hs
+++ b/Control/Shell/Handle.hs
@@ -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
diff --git a/Control/Shell/Posix.hs b/Control/Shell/Posix.hs
new file mode 100644
--- /dev/null
+++ b/Control/Shell/Posix.hs
@@ -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
diff --git a/shellmate.cabal b/shellmate.cabal
--- a/shellmate.cabal
+++ b/shellmate.cabal
@@ -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
