Yes, I ran into the same thing a while back. The problem is that the
subprocess has already been forked off before it runs exec() and finds
out the file doesn't exist. The reason python reports the right error
is that it sets up a pipe from child to parent to communicate just
this error. It's more friendly, but on the other hand the
implementation is more complicated.
If you don't want to hack up the whole send-exception-over-the-pipe
thing, the easiest thing to do is to wait for the processes's return
code. If you don't want to do that, you can at least have the
subproces log, e.g.:
loggedProcess :: Process.CreateProcess -> IO (Maybe IO.Handle,
Maybe IO.Handle, Maybe IO.Handle, Process.ProcessHandle)
loggedProcess create = do
r@(_, _, _, pid) <- Process.createProcess create
Concurrent.forkIO $ do
code <- Process.waitForProcess pid
case code of
Exit.ExitFailure c -> notice $
"subprocess " ++ show (binaryOf create) ++ " failed: "
++ if c == 127 then "binary not found" else show c
_ -> return ()
return r
where
binaryOf create = case Process.cmdspec create of
Process.RawCommand fn _ -> fn
Process.ShellCommand cmd -> fst $ break (==' ') cmd
As an aside, I've had the idea to at some point go look at the latest
python's version of the subprocess module and see about porting it to
haskell, or at least make sure the haskell version doesn't suffer from
problems fixed in the python one. They went through a lot of
iterations trying to get it right (and earlier python versions are
broken in one way or another) and we might as well build on their
work.
In Unix, at least, "check, then act" is generally considered unwise:
1. Something can go wrong between checking and acting.
2. You might not be checking the right thing(s). In this case, the fact that the file exists is not useful if you don't have permission to execute it. You may not be able to determine whether you have the appropriate permissions without fairly deep manipulation of ACLs.
3. Even if the OS has the info at hand, making the system call(s) necessary to get it is not free. Various non-free things happen every time control passes between user-space and kernel-space.
This isn't that hard - a pipe shouldn't be needed anymore. Just require a post-2003 glibc.
Though speaking of platforms, I guess one large headache would be
what to do about Microsoft operating systems. Given the unusual
fexecve is now in the Single Unix Specification, based on
On 13/08/2012, at 11:26 PM, Alexander Kjeldaas wrote:
>
> This isn't that hard - a pipe shouldn't be needed anymore. Just require a post-2003 glibc.
>
> fexecve is a system call in most BSDs. It is also implemented in glibc using a /proc hack.
POSIX as of 2008, I believe. However,
http://www.gnu.org/software/gnulib/manual/html_node/fexecve.html
says
Portability problems not fixed by Gnulib:
* This function is missing on many non-glibc platforms: MacOS X 10.5, FreeBSD 6.0,
NetBSD 5.0, OpenBSD 3.8, Minix 3.1.8, AIX 5.1, HP-UX 11, IRIX 6.5, OSF/1 5.1,
Solaris 11 2010-11, Cygwin 1.5.x, mingw, MSVC 9, Interix 3.5, BeOS.
That warning doesn't seem to be fully up to date. I'm using MacOS X 10.6.8
and fexecve() isn't in the manuals or in <unistd.h>.
Furthermore it would add unnecessary overhead,
createProcess can be run thousands of times in a program and should be
lean and mean.