Skip to content

Instantly share code, notes, and snippets.

@nh2

nh2/ExecSafe.hs Secret

Last active August 29, 2015 14:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nh2/4932ecf5ca919659ae51 to your computer and use it in GitHub Desktop.
Save nh2/4932ecf5ca919659ae51 to your computer and use it in GitHub Desktop.
Haskell: Example of how to open a file descriptor with the O_CLOEXEC flag to deal with "Text file busy" problems in programs that use any form of the exec() syscall
-- | Exports some functions from `System.Posix.IO`, with the difference
-- that `Fd`s are opened with the `O_CLOEXEC` function, which makes sure
-- that the `Fd`s are not inherited by child processes (or more precise,
-- processes that are exec()ed from the current process).
module System.Posix.IO.ExecSafe
( openFd
, createFile
) where
import Foreign.C
import Data.Bits
import System.Posix.Types
import System.Posix.Error
import System.Posix.IO hiding (createFile, openFd)
import System.Posix.Internals (withFilePath, c_open)
-- For O_CLOEXEC:
#include <fcntl.h>
-- | Like `System.Posix.IO.Common.open_`, but with `O_CLOEXEC` set.
open_ :: CString
-> OpenMode
-> Maybe FileMode -- ^Just x => creates the file with the given modes, Nothing => the file must exist.
-> OpenFileFlags
-> IO Fd
open_ str how maybe_mode (OpenFileFlags appendFlag exclusiveFlag nocttyFlag
nonBlockFlag truncateFlag) = do
fd <- c_open str all_flags mode_w
return (Fd fd)
where
all_flags = creat .|. flags .|. open_mode
flags =
(#const O_CLOEXEC) .|. -- this is the critical difference to `System.Posix.IO.Common.open_`
(if appendFlag then (#const O_APPEND) else 0) .|.
(if exclusiveFlag then (#const O_EXCL) else 0) .|.
(if nocttyFlag then (#const O_NOCTTY) else 0) .|.
(if nonBlockFlag then (#const O_NONBLOCK) else 0) .|.
(if truncateFlag then (#const O_TRUNC) else 0)
(creat, mode_w) = case maybe_mode of
Nothing -> (0,0)
Just x -> ((#const O_CREAT), x)
open_mode = case how of
ReadOnly -> (#const O_RDONLY)
WriteOnly -> (#const O_WRONLY)
ReadWrite -> (#const O_RDWR)
-- | Like `System.Posix.IO.openFd`, but with `O_CLOEXEC` set.
openFd :: FilePath
-> OpenMode
-> Maybe FileMode -- ^Just x => creates the file with the given modes, Nothing => the file must exist.
-> OpenFileFlags
-> IO Fd
openFd name how maybe_mode flags = do
withFilePath name $ \str -> do
throwErrnoPathIfMinus1Retry "openFd" name $
open_ str how maybe_mode flags
-- | Like `System.Posix.IO.createFile`, but with `O_CLOEXEC` set.
createFile :: FilePath -> FileMode -> IO Fd
createFile name mode
= openFd name WriteOnly (Just mode) defaultFileFlags{ trunc=True }
-- | Writes a `BSL.ByteString` to a path, like `BSL.writeFile`, but opens the
-- path with the `O_CLOEXEC` option to make sure that the (write) file
-- descriptor is not inherited / held open by fork()+exec()ed child processes.
writeFileExecSafe :: FilePath -> BSL.ByteString -> IO ()
writeFileExecSafe f txt =
bracket
(do fd <- ExecSafe.createFile f stdFileMode
fdToHandle fd `onException` closeFd fd)
hClose
(\hdl -> BSL.hPut hdl txt)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment