-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- | 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 } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- | 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