@@ -211,14 +211,82 @@ static const char **prepare_shell_cmd(struct argv_array *out, const char **argv)
211211#ifndef GIT_WINDOWS_NATIVE
212212static int child_notifier = -1 ;
213213
214- static void notify_parent (void )
214+ enum child_errcode {
215+ CHILD_ERR_CHDIR ,
216+ CHILD_ERR_ENOENT ,
217+ CHILD_ERR_SILENT ,
218+ CHILD_ERR_ERRNO
219+ };
220+
221+ struct child_err {
222+ enum child_errcode err ;
223+ int syserr ; /* errno */
224+ };
225+
226+ static void child_die (enum child_errcode err )
215227{
216- /*
217- * execvp failed. If possible, we'd like to let start_command
218- * know, so failures like ENOENT can be handled right away; but
219- * otherwise, finish_command will still report the error.
220- */
221- xwrite (child_notifier , "" , 1 );
228+ struct child_err buf ;
229+
230+ buf .err = err ;
231+ buf .syserr = errno ;
232+
233+ /* write(2) on buf smaller than PIPE_BUF (min 512) is atomic: */
234+ xwrite (child_notifier , & buf , sizeof (buf ));
235+ _exit (1 );
236+ }
237+
238+ /*
239+ * parent will make it look like the child spewed a fatal error and died
240+ * this is needed to prevent changes to t0061.
241+ */
242+ static void fake_fatal (const char * err , va_list params )
243+ {
244+ vreportf ("fatal: " , err , params );
245+ }
246+
247+ static void child_error_fn (const char * err , va_list params )
248+ {
249+ const char msg [] = "error() should not be called in child\n" ;
250+ xwrite (2 , msg , sizeof (msg ) - 1 );
251+ }
252+
253+ static void child_warn_fn (const char * err , va_list params )
254+ {
255+ const char msg [] = "warn() should not be called in child\n" ;
256+ xwrite (2 , msg , sizeof (msg ) - 1 );
257+ }
258+
259+ static void NORETURN child_die_fn (const char * err , va_list params )
260+ {
261+ const char msg [] = "die() should not be called in child\n" ;
262+ xwrite (2 , msg , sizeof (msg ) - 1 );
263+ _exit (2 );
264+ }
265+
266+ /* this runs in the parent process */
267+ static void child_err_spew (struct child_process * cmd , struct child_err * cerr )
268+ {
269+ static void (* old_errfn )(const char * err , va_list params );
270+
271+ old_errfn = get_error_routine ();
272+ set_error_routine (fake_fatal );
273+ errno = cerr -> syserr ;
274+
275+ switch (cerr -> err ) {
276+ case CHILD_ERR_CHDIR :
277+ error_errno ("exec '%s': cd to '%s' failed" ,
278+ cmd -> argv [0 ], cmd -> dir );
279+ break ;
280+ case CHILD_ERR_ENOENT :
281+ error_errno ("cannot run %s" , cmd -> argv [0 ]);
282+ break ;
283+ case CHILD_ERR_SILENT :
284+ break ;
285+ case CHILD_ERR_ERRNO :
286+ error_errno ("cannot exec '%s'" , cmd -> argv [0 ]);
287+ break ;
288+ }
289+ set_error_routine (old_errfn );
222290}
223291
224292static void prepare_cmd (struct argv_array * out , const struct child_process * cmd )
@@ -341,13 +409,6 @@ static int wait_or_whine(pid_t pid, const char *argv0, int in_signal)
341409 code += 128 ;
342410 } else if (WIFEXITED (status )) {
343411 code = WEXITSTATUS (status );
344- /*
345- * Convert special exit code when execvp failed.
346- */
347- if (code == 127 ) {
348- code = -1 ;
349- failed_errno = ENOENT ;
350- }
351412 } else {
352413 error ("waitpid is confused (%s)" , argv0 );
353414 }
@@ -435,6 +496,7 @@ int start_command(struct child_process *cmd)
435496 int null_fd = -1 ;
436497 char * * childenv ;
437498 struct argv_array argv = ARGV_ARRAY_INIT ;
499+ struct child_err cerr ;
438500
439501 if (pipe (notify_pipe ))
440502 notify_pipe [0 ] = notify_pipe [1 ] = -1 ;
@@ -453,20 +515,16 @@ int start_command(struct child_process *cmd)
453515 failed_errno = errno ;
454516 if (!cmd -> pid ) {
455517 /*
456- * Redirect the channel to write syscall error messages to
457- * before redirecting the process's stderr so that all die()
458- * in subsequent call paths use the parent's stderr.
518+ * Ensure the default die/error/warn routines do not get
519+ * called, they can take stdio locks and malloc.
459520 */
460- if (cmd -> no_stderr || need_err ) {
461- int child_err = dup (2 );
462- set_cloexec (child_err );
463- set_error_handle (fdopen (child_err , "w" ));
464- }
521+ set_die_routine (child_die_fn );
522+ set_error_routine (child_error_fn );
523+ set_warn_routine (child_warn_fn );
465524
466525 close (notify_pipe [0 ]);
467526 set_cloexec (notify_pipe [1 ]);
468527 child_notifier = notify_pipe [1 ];
469- atexit (notify_parent );
470528
471529 if (cmd -> no_stdin )
472530 dup2 (null_fd , 0 );
@@ -501,8 +559,7 @@ int start_command(struct child_process *cmd)
501559 }
502560
503561 if (cmd -> dir && chdir (cmd -> dir ))
504- die_errno ("exec '%s': cd to '%s' failed" , cmd -> argv [0 ],
505- cmd -> dir );
562+ child_die (CHILD_ERR_CHDIR );
506563
507564 /*
508565 * Attempt to exec using the command and arguments starting at
@@ -517,12 +574,11 @@ int start_command(struct child_process *cmd)
517574 (char * const * ) childenv );
518575
519576 if (errno == ENOENT ) {
520- if (!cmd -> silent_exec_failure )
521- error ("cannot run %s: %s" , cmd -> argv [0 ],
522- strerror (ENOENT ));
523- exit (127 );
577+ if (cmd -> silent_exec_failure )
578+ child_die (CHILD_ERR_SILENT );
579+ child_die (CHILD_ERR_ENOENT );
524580 } else {
525- die_errno ( "cannot exec '%s'" , cmd -> argv [ 0 ] );
581+ child_die ( CHILD_ERR_ERRNO );
526582 }
527583 }
528584 if (cmd -> pid < 0 )
@@ -533,17 +589,18 @@ int start_command(struct child_process *cmd)
533589 /*
534590 * Wait for child's exec. If the exec succeeds (or if fork()
535591 * failed), EOF is seen immediately by the parent. Otherwise, the
536- * child process sends a single byte .
592+ * child process sends a child_err struct .
537593 * Note that use of this infrastructure is completely advisory,
538594 * therefore, we keep error checks minimal.
539595 */
540596 close (notify_pipe [1 ]);
541- if (read (notify_pipe [0 ], & notify_pipe [ 1 ], 1 ) == 1 ) {
597+ if (xread (notify_pipe [0 ], & cerr , sizeof ( cerr )) == sizeof ( cerr ) ) {
542598 /*
543599 * At this point we know that fork() succeeded, but exec()
544600 * failed. Errors have been reported to our stderr.
545601 */
546602 wait_or_whine (cmd -> pid , cmd -> argv [0 ], 0 );
603+ child_err_spew (cmd , & cerr );
547604 failed_errno = errno ;
548605 cmd -> pid = -1 ;
549606 }
0 commit comments