Hi list,

Recently I had some headache with darcs not reporting the correct error when I had set DARCS_APPLY_HTTP wrong, so I decided to do something about the error reporting. I have attached a patch.

What this patch does is, it introduces a new exception ExecFailed that gets thrown if exec_ fails to at all execute the file. Because of the design of unix, detectin this condition requires some hackery involving pipes (njs suggested this to me on irc). I also added a handler at toplevel main to report the ExecFailed exceptions to the user. Other than this, exec_ and friends behave exactly as before.

Let me know what you think. I'm ready to do some revision if you guys think it's needed.

/ Magnus Jonsson
New patches:

[Made exec_ throw an exception when a command failed to execute.
Magnus Jonsson <[EMAIL PROTECTED]>**20060924210338
 Before, exec_ just returned an exit code of 1 if it failed to run a
 program. The user would get strange error messages that didn't point
 to the real problem. For example if the user had set DARCS_APPLY_HTTP
 to some executable that didn't exist, darcs wouldn't tell the user
 the correct reason for the failure. This patch adds detection of execvp
 failure by employing a tradition unix solution involving pipes. When a
 failure occurs, a new ExecFailure exception is thrown and caught at the
 toplevel where an error message is printed. 
] {
hunk ./Exec.lhs 18
-{-# OPTIONS -fffi #-}
-module Exec ( exec, exec_, exec_interactive
+{-# OPTIONS -fffi -fglasgow-exts #-}
+module Exec ( exec, exec_, exec_interactive, ExecException (ExecFailure)
hunk ./Exec.lhs 26
+import Control.Exception (throwDyn)
+import Data.Typeable (Typeable)
+
hunk ./Exec.lhs 33
-import Monad ( liftM )
hunk ./Exec.lhs 40
+-- This exception is thrown if an exec can't do it's job,
+-- for example because fork/pipe/execvp failed or because there was
+-- an error setting up the file redirecs.
+data ExecException = ExecFailure String [String] (Maybe FilePath) (Maybe 
FilePath) (Maybe FilePath)
+                   deriving (Show,Typeable)
+
hunk ./Exec.lhs 54
+-- currently on win32 there's no way to discern whether the
+-- execution of c failed or whether c returned an error code,
+-- so we assume that the command was run.
hunk ./Exec.lhs 75
-exec_ c args minp mout merr = do
-  fval <- c_fork
-  let -- set up stdin redirection if needed
-      withStdin job =
-       case minp of
-         Nothing  -> job
-         Just inp -> withCString inp $ \in_c -> do
-                       fdin <- open_read in_c
-                       c_dup2 fdin 0
-                       job
-      -- set up stdout/stderr redirection if needed
-      withStdout job =
-       case mout of
-        -- no stdout redirection
-        Nothing ->
-         case merr of
-           Nothing -> job
-           Just e  -> withCString e $ \err_c -> do
-                        fderr <- open_write err_c
-                        c_dup2 fderr 2
-                        job
-        -- stdout redirection
-        Just out -> withCString out $ \out_c -> do
-         fdout <- open_write out_c
-         c_dup2 fdout 1
-         case merr of
-           Nothing -> do c_dup2 fdout 2 -- stderr to stdout
-                         job
-           Just e  -> withCString e $ \err_c -> do
-                         fderr <- open_write err_c
-                         c_dup2 fderr 2
-                         job
-  -- fork and go
-  case fval of
-     -1 -> return $ ExitFailure $ 1
-     0 -> withStdin $ withStdout $
-          withCString c $ \c_c ->
-          withCStrings (c:args) $ \c_args -> do
-                -- execvp only returns if there is an error:
-                ExitFailure `liftM` execvp_no_vtalarm c_c c_args
-     pid -> do ecode <- smart_wait pid
-               if ecode == 0 then return ExitSuccess
-                             else return $ ExitFailure ecode
+exec_ cmd args minp mout merr = do
+  pid <- withCString cmd $ \ c_cmd ->
+         withCStrings (cmd:args) $ \ c_args ->
+         withCString (maybe "" id minp) $ \ c_minp ->
+         withCString (maybe "" id mout) $ \ c_mout ->
+         withCString (maybe "" id merr) $ \ c_merr ->
+             exec_extern c_cmd c_args c_minp c_mout c_merr
+  if pid `elem` [(-1),(-2),(-3)]
+    then throwDyn (ExecFailure cmd args minp mout merr)
+    else do ecode <- smart_wait pid
+            case ecode of
+              0 -> return ExitSuccess
+              _ -> return (ExitFailure ecode)
hunk ./Exec.lhs 89
-foreign import ccall unsafe "static unistd.h dup2" c_dup2
-    :: CInt -> CInt -> IO CInt
hunk ./Exec.lhs 91
-foreign import ccall unsafe "static compat.h open_read" open_read
-    :: CString -> IO CInt
-foreign import ccall unsafe "static compat.h open_write" open_write
-    :: CString -> IO CInt
-foreign import ccall unsafe "static unistd.h fork" c_fork
-    :: IO Int
-foreign import ccall unsafe "static compat.h execvp_no_vtalarm" 
execvp_no_vtalarm
-    :: CString -> Ptr CString -> IO Int
+foreign import ccall unsafe "static compat.h exec_extern" exec_extern
+    :: CString -> Ptr CString -> CString -> CString -> CString -> IO Int
hunk ./Exec.lhs 110
+
hunk ./c_compat.c 20
-int open_read(const char *fname) {
+static int open_read(const char *fname) {
hunk ./c_compat.c 24
-int open_write(const char *fname) {
+static int open_write(const char *fname) {
hunk ./c_compat.c 60
-int execvp_no_vtalarm(const char *file, char *const argv[]) {
+static int execvp_no_vtalarm(const char *file, char *const argv[]) {
hunk ./c_compat.c 75
+
+static int is_some(const char* string) {
+  return string != NULL && string[0] != '\0';
+}
+
+/*
+ * exec_extern(file,argv) tries to execute
+ * returns -1 if fork() failed
+ * returns -2 if pipe() failed
+ * returns -3 if execvp_no_vtalarm() failed
+ * returns the pid of the child otherwise
+ */
+int exec_extern(const char* file, char *const argv[], const char* minp, const 
char* mout, const char* merr) {
+  int pipefd[2] = { 0, 0 };
+  int fval = 0;
+  char buf[1] = { 0 };
+
+  /* this pipe is used to signal from the
+     child process in case of an error */
+  if(pipe(pipefd) != 0)
+    return -2;
+
+  /* pipefd[0] is now a fd for reading from the pipe
+     pipefd[1] is now a fd for writing to the pipe */
+
+  fval = fork();
+  switch(fval) {
+  case -1:
+    {
+      /* the fork failed */
+      close(pipefd[0]);
+      close(pipefd[1]);
+      return -1;
+    }
+  case 0:
+    {
+      /* we're inside the child process */
+      close(pipefd[0]);
+
+      /* redirect inputs and outputs */
+
+      if(is_some(minp)) {
+       int fdinp = open_read(minp);
+       if(fdinp == -1)
+         goto error;
+       dup2(fdinp,0);
+      }
+      if(is_some(mout)) {
+       /* stdout redirection */
+       int fdout = open_write(mout);
+       if (fdout == -1)
+         goto error;
+       dup2(fdout,1);
+       if(is_some(merr)) {
+         int fderr = open_write(merr);
+         if(fderr == -1)
+           goto error;
+         dup2(fderr,2);
+       }
+       else {
+         /* stderr to stdout */
+         dup2(fdout,2);
+       }
+      }
+      else {
+       /* no stdout redirection */
+       if(is_some(merr)) {
+         int fderr = open_write(merr);
+         if (fderr == -1)
+           goto error;
+         dup2(open_write(merr),2);
+       }
+      }
+      
+      /* execvp only returns if there is an error */
+      execvp_no_vtalarm(file,argv);
+
+    error:
+      /* execvp failed or one of the redirects failed.*/
+
+      /* signal to the parent process that we got a problem */
+      write(pipefd[1],buf,1);
+      
+      close(pipefd[1]);
+      
+      exit(127);
+    }
+  default:
+    {
+      int const pid = fval;
+      /* we're inside the parent process and fval is the pid of the child */
+      ssize_t len=0;
+
+      close(pipefd[1]);
+      
+      do {
+       len = read(pipefd[0],buf,1);
+      } while(len == -1 && errno == EINTR);
+      
+      close(pipefd[0]);
+      
+      if(len == 0) {
+       /* EOF. The child process closed the pipe without writing
+          anything to it, indicating that everything went fine. */
+       return pid;
+      }
+      else {
+       /* There was an error with the redirects or the execvp call. */
+       smart_wait(pid);
+       return -3;
+      }
+    }
+  };
+}
hunk ./compat.h 6
-int open_read(const char *fname);
-int open_write(const char *fname);
hunk ./compat.h 8
-int execvp_no_vtalarm(const char *file, char *const argv[]);
+int exec_extern(const char *file, char *const argv[],
+               const char *minp,
+               const char *mout,
+               const char *merr);
hunk ./darcs.lhs 34
-import Control.Exception ( Exception( AssertionFailed ), handleJust, )
+import Control.Exception ( Exception( AssertionFailed ), handleJust, catchDyn )
+import Exec ( ExecException ( ExecFailure ) )
hunk ./darcs.lhs 658
-main = with_atexit $ withSignalsHandled $ handleJust assertions bug $ do
+main = with_atexit $ withSignalsHandled $ handleJust assertions bug $
+       flip catchDyn (\ (ExecFailure cmd args _ _ _) ->
+                          bug $ "Failed to execute external command: "
+                                  ++ unwords (cmd:args)
+                                  ++ "\nHere are some possible reasons:\n"
+                                  ++ "- The command doesn't exist.\n"
+                                  ++ "- The file permissions for the command 
are wrong.\n"
+                                  ++ "- The command is not in your path.\n")
+       $ do
}

Context:

[whatsnew --look-for-adds doesn't read unadded files (fix for issue79)
Jason Dagit <[EMAIL PROTECTED]>**20060910193803
 The default mode for whatsnew --look-for-adds is summary mode.  In summary
 mode full patches are not needed.  This fix changes whatsnew
 --look-for-adds to stop computing the full patch for a file when the
 file is not managed by darcs.
] 
[Correct canonical email for Kirill Smelkov
Kirill Smelkov <[EMAIL PROTECTED]>**20060912080004] 
[Be explicit about timezone handling (issue220); assume local by default.
Eric Kow <[EMAIL PROTECTED]>**20060812102034
 
 Except for the local timezone in the user interface, this patch is not
 expected to change darcs's behaviour.  It merely makes current practice
 explicit:
 
 - Assume local timezone when parsing date strings from the user
   interface (previous behaviour was assuming UTC).
 
 - Assume UTC timezone when parsing date strings from PatchInfo.
   Newer patch date strings do *not* specify the timezone, so it
   would be prudent to treat these as UTC.
  
 - Disregard timezone information altogether when reading patch
   dates (issue220).  Note that this bug was not caused by assuming local
   timezone, because legacy patch date strings explicitly tell you what
   the timezone to use.  The bug was caused by a patch that fixed
   issue173 by using timezone information correctly.  To preserve
   backwards-compatability, we deliberatly replicate the incorrect
   behaviour of overriding the timezone with UTC.
   (PatchInfo.make_filename)
  
] 
[Account for timezone offset in cleanDate  (Fixes issue173).
Eric Kow <[EMAIL PROTECTED]>**20060610193049
 
] 
[move test for tabs from makefile to haskell_policy test
Tommy Pettersson <[EMAIL PROTECTED]>**20060730122348] 
[add test for haskell policy
Tommy Pettersson <[EMAIL PROTECTED]>**20060730121404] 
[ratify some uses of readFile and hGetContents
Tommy Pettersson <[EMAIL PROTECTED]>**20060730121158] 
[Remove direct dependency to mapi32.dll; Improve MAPI compatibility.
Esa Ilari Vuokko <[EMAIL PROTECTED]>**20051130000915] 
[Canonize Kirill Smelkov and Anders Hockersten.
Eric Kow <[EMAIL PROTECTED]>**20060910052541] 
[Correct 'one one' in web page.
Eric Kow <[EMAIL PROTECTED]>**20060908191241] 
[Fix merge conflicts.
Juliusz Chroboczek <[EMAIL PROTECTED]>**20060906191317] 
[fix bug in pristine handling when dealing with multiple patches.
David Roundy <[EMAIL PROTECTED]>**20060731111404] 
[fix ordering of operations to call pull_first_middles properly.
David Roundy <[EMAIL PROTECTED]>**20060730111409] 
[make amend-record.pl test a bit pickier.
David Roundy <[EMAIL PROTECTED]>**20060730103854] 
[simplify code a tad in get.
David Roundy <[EMAIL PROTECTED]>**20060726121737] 
[fix bug in refactoring of get.
David Roundy <[EMAIL PROTECTED]>**20060726121655] 
[refactor Population.
David Roundy <[EMAIL PROTECTED]>**20060716034837] 
[add TODO for refactoring get_markedup_file.
David Roundy <[EMAIL PROTECTED]>**20060716034339] 
[partial refactoring in annotate.
David Roundy <[EMAIL PROTECTED]>**20060716034319] 
[don't use DarcsRepo in list_authors.
David Roundy <[EMAIL PROTECTED]>**20060716033450] 
[I've now eliminated need to export DarcsRepo.write_patch.
David Roundy <[EMAIL PROTECTED]>**20060716033109] 
[partially refactor Optimize.
David Roundy <[EMAIL PROTECTED]>**20060716032934] 
[partial refactoring of Get.
David Roundy <[EMAIL PROTECTED]>**20060716031605] 
[refactor amend-record.
David Roundy <[EMAIL PROTECTED]>**20060716021003] 
[add TODO to refactor unrevert handling.
David Roundy <[EMAIL PROTECTED]>**20060716020247] 
[refactor Unrecord, adding tentativelyRemovePatches.
David Roundy <[EMAIL PROTECTED]>**20060716015150] 
[refactor tag.
David Roundy <[EMAIL PROTECTED]>**20060716011853] 
[refactor Repository to allow truly atomic updates.
David Roundy <[EMAIL PROTECTED]>**20060716011245] 
[Do not redirect to or from /dev/null when calling ssh.
Eric Kow <[EMAIL PROTECTED]>**20060903214831
 
 Redirection of stdin and stdout breaks putty, which uses these to
 interact with the user.  Quiet mode, and redirecting stderr are good
 enough for making ssh silent.
 
] 
[Exec improvements : Windows redirection, and more redirection control.
Eric Kow <[EMAIL PROTECTED]>**20060707054134
 
 - Implement ability to redirect to /dev/null under Windows
   (eivuokko on #darcs points out that it is NUL under Windows)
 
 - Add exec_ function, which does the same thing as exec,
   but allows redirection on stderr, and also allows us
   to NOT redirect stdin/stderr
 
] 
[Ignore .git if _darcs found.
Juliusz Chroboczek <[EMAIL PROTECTED]>**20060831231933] 
[overhaul the darcs.net front page.
Mark Stosberg <[EMAIL PROTECTED]>**20060820191415
 
 The themes to this change are:
 
 - Focus on the key benefits of darcs:
     Distributed. Interactive. Smart.
 
 - Recognize that the wiki is the central resource,
    and remove some information that is duplicated here
    and reference the wik instead. 
 
 I can post a demo of this HTML for easy comparison if you'd like.
 
     Mark
] 
[Reimplement --disable-ssh-cm flag (issue239).
Eric Kow <[EMAIL PROTECTED]>**20060812134856
 
 My patch to "Only launch SSH control master on demand" accidentally
 removed the ability to disable use of SSH ControlMaster.  Also, the
 way it was implemented is not compatible with launching on demand.
 This implementation relies on a notion of global variables using
 unsafe IORefs.
 
] 
[Compile Global.lhs in place of AtExit.lhs.
Eric Kow <[EMAIL PROTECTED]>**20060812121943] 
[Rename AtExit module to Global.
Eric Kow <[EMAIL PROTECTED]>**20060812121925
 
 The goal is to capture some broad "global" notions like exit handlers
 and global variables.  Note the GPL header thrown in for good measure.
 
] 
[Raise exception if unable to open logfile (issue142).
Zachary P. Landau <[EMAIL PROTECTED]>**20060810034035] 
[Make the pull 'permission test' work when run as root
Jon Olsson <[EMAIL PROTECTED]>**20060831193834] 
[TAG darcs-unstable-20060831
Juliusz Chroboczek <[EMAIL PROTECTED]>**20060831191554] 
Patch bundle hash:
67e240b70a618a2147e78583c3f356622018ef23
_______________________________________________
darcs-devel mailing list
[email protected]
http://www.abridgegame.org/cgi-bin/mailman/listinfo/darcs-devel

Reply via email to