Merge branch 'js/run-command-updates'

* js/run-command-updates:
  api-run-command.txt: describe error behavior of run_command functions
  run-command.c: squelch a "use before assignment" warning
  receive-pack: remove unnecessary run_status report
  run_command: report failure to execute the program, but optionally don't
  run_command: encode deadly signal number in the return value
  run_command: report system call errors instead of returning error codes
  run_command: return exit code as positive value
  MinGW: simplify waitpid() emulation macros
maint
Junio C Hamano 2009-08-10 22:14:57 -07:00
commit 08ac69685a
11 changed files with 104 additions and 123 deletions

View File

@ -35,12 +35,32 @@ Functions
Convenience functions that encapsulate a sequence of Convenience functions that encapsulate a sequence of
start_command() followed by finish_command(). The argument argv start_command() followed by finish_command(). The argument argv
specifies the program and its arguments. The argument opt is zero specifies the program and its arguments. The argument opt is zero
or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`, or or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`,
`RUN_COMMAND_STDOUT_TO_STDERR` that correspond to the members `RUN_COMMAND_STDOUT_TO_STDERR`, or `RUN_SILENT_EXEC_FAILURE`
.no_stdin, .git_cmd, .stdout_to_stderr of `struct child_process`. that correspond to the members .no_stdin, .git_cmd,
.stdout_to_stderr, .silent_exec_failure of `struct child_process`.
The argument dir corresponds the member .dir. The argument env The argument dir corresponds the member .dir. The argument env
corresponds to the member .env. corresponds to the member .env.


The functions above do the following:

. If a system call failed, errno is set and -1 is returned. A diagnostic
is printed.

. If the program was not found, then -1 is returned and errno is set to
ENOENT; a diagnostic is printed only if .silent_exec_failure is 0.

. Otherwise, the program is run. If it terminates regularly, its exit
code is returned. No diagnistic is printed, even if the exit code is
non-zero.

. If the program terminated due to a signal, then the return value is the
signal number - 128, ie. it is negative and so indicates an unusual
condition; a diagnostic is printed. This return value can be passed to
exit(2), which will report the same code to the parent process that a
POSIX shell's $? would report for a program that died from the signal.


`start_async`:: `start_async`::


Run a function asynchronously. Takes a pointer to a `struct Run a function asynchronously. Takes a pointer to a `struct
@ -143,6 +163,11 @@ string pointers (NULL terminated) in .env:
To specify a new initial working directory for the sub-process, To specify a new initial working directory for the sub-process,
specify it in the .dir member. specify it in the .dir member.


If the program cannot be found, the functions return -1 and set
errno to ENOENT. Normally, an error message is printed, but if
.silent_exec_failure is set to 1, no message is printed for this
special error condition.



* `struct async` * `struct async`



View File

@ -594,7 +594,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
discard_cache(); discard_cache();
if (read_cache() < 0) if (read_cache() < 0)
die("failed to read the cache"); die("failed to read the cache");
return -ret; return ret;
} }
} }



View File

@ -123,31 +123,6 @@ static struct command *commands;
static const char pre_receive_hook[] = "hooks/pre-receive"; static const char pre_receive_hook[] = "hooks/pre-receive";
static const char post_receive_hook[] = "hooks/post-receive"; static const char post_receive_hook[] = "hooks/post-receive";


static int run_status(int code, const char *cmd_name)
{
switch (code) {
case 0:
return 0;
case -ERR_RUN_COMMAND_FORK:
return error("fork of %s failed", cmd_name);
case -ERR_RUN_COMMAND_EXEC:
return error("execute of %s failed", cmd_name);
case -ERR_RUN_COMMAND_PIPE:
return error("pipe failed");
case -ERR_RUN_COMMAND_WAITPID:
return error("waitpid failed");
case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
return error("waitpid is confused");
case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
return error("%s died of signal", cmd_name);
case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
return error("%s died strangely", cmd_name);
default:
error("%s exited with error code %d", cmd_name, -code);
return -code;
}
}

static int run_receive_hook(const char *hook_name) static int run_receive_hook(const char *hook_name)
{ {
static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4]; static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
@ -174,7 +149,7 @@ static int run_receive_hook(const char *hook_name)


code = start_command(&proc); code = start_command(&proc);
if (code) if (code)
return run_status(code, hook_name); return code;
for (cmd = commands; cmd; cmd = cmd->next) { for (cmd = commands; cmd; cmd = cmd->next) {
if (!cmd->error_string) { if (!cmd->error_string) {
size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n", size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
@ -186,7 +161,7 @@ static int run_receive_hook(const char *hook_name)
} }
} }
close(proc.in); close(proc.in);
return run_status(finish_command(&proc), hook_name); return finish_command(&proc);
} }


static int run_update_hook(struct command *cmd) static int run_update_hook(struct command *cmd)
@ -203,9 +178,8 @@ static int run_update_hook(struct command *cmd)
argv[3] = sha1_to_hex(cmd->new_sha1); argv[3] = sha1_to_hex(cmd->new_sha1);
argv[4] = NULL; argv[4] = NULL;


return run_status(run_command_v_opt(argv, RUN_COMMAND_NO_STDIN | return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN |
RUN_COMMAND_STDOUT_TO_STDERR), RUN_COMMAND_STDOUT_TO_STDERR);
update_hook);
} }


static int is_ref_checked_out(const char *ref) static int is_ref_checked_out(const char *ref)
@ -419,7 +393,6 @@ static void run_update_post_hook(struct command *cmd)
argv[argc] = NULL; argv[argc] = NULL;
status = run_command_v_opt(argv, RUN_COMMAND_NO_STDIN status = run_command_v_opt(argv, RUN_COMMAND_NO_STDIN
| RUN_COMMAND_STDOUT_TO_STDERR); | RUN_COMMAND_STDOUT_TO_STDERR);
run_status(status, update_post_hook);
} }


static void execute_commands(const char *unpacker_error) static void execute_commands(const char *unpacker_error)
@ -537,7 +510,6 @@ static const char *unpack(void)
code = run_command_v_opt(unpacker, RUN_GIT_CMD); code = run_command_v_opt(unpacker, RUN_GIT_CMD);
if (!code) if (!code)
return NULL; return NULL;
run_status(code, unpacker[0]);
return "unpack-objects abnormal exit"; return "unpack-objects abnormal exit";
} else { } else {
const char *keeper[7]; const char *keeper[7];
@ -563,7 +535,6 @@ static const char *unpack(void)
ip.git_cmd = 1; ip.git_cmd = 1;
status = start_command(&ip); status = start_command(&ip);
if (status) { if (status) {
run_status(status, keeper[0]);
return "index-pack fork failed"; return "index-pack fork failed";
} }
pack_lockfile = index_pack_lockfile(ip.out); pack_lockfile = index_pack_lockfile(ip.out);
@ -573,7 +544,6 @@ static const char *unpack(void)
reprepare_packed_git(); reprepare_packed_git();
return NULL; return NULL;
} }
run_status(status, keeper[0]);
return "index-pack abnormal exit"; return "index-pack abnormal exit";
} }
} }

View File

@ -17,9 +17,10 @@ typedef int pid_t;
#define S_IROTH 0 #define S_IROTH 0
#define S_IXOTH 0 #define S_IXOTH 0


#define WIFEXITED(x) ((unsigned)(x) < 259) /* STILL_ACTIVE */ #define WIFEXITED(x) 1
#define WIFSIGNALED(x) 0
#define WEXITSTATUS(x) ((x) & 0xff) #define WEXITSTATUS(x) ((x) & 0xff)
#define WIFSIGNALED(x) ((unsigned)(x) > 259) #define WTERMSIG(x) SIGTERM


#define SIGHUP 1 #define SIGHUP 1
#define SIGQUIT 3 #define SIGQUIT 3

View File

@ -267,7 +267,7 @@ static int filter_buffer(int fd, void *data)


status = finish_command(&child_process); status = finish_command(&child_process);
if (status) if (status)
error("external filter %s failed %d", params->cmd, -status); error("external filter %s failed %d", params->cmd, status);
return (write_err || status); return (write_err || status);
} }



10
git.c
View File

@ -416,13 +416,9 @@ static void execv_dashed_external(const char **argv)
* if we fail because the command is not found, it is * if we fail because the command is not found, it is
* OK to return. Otherwise, we just pass along the status code. * OK to return. Otherwise, we just pass along the status code.
*/ */
status = run_command_v_opt(argv, 0); status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE);
if (status != -ERR_RUN_COMMAND_EXEC) { if (status >= 0 || errno != ENOENT)
if (IS_RUN_COMMAND_ERR(status)) exit(status);
die("unable to run '%s'", argv[0]);
exit(-status);
}
errno = ENOENT; /* as if we called execvp */


argv[0] = tmp; argv[0] = tmp;



View File

@ -192,10 +192,6 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,


args[2] = cmd.buf; args[2] = cmd.buf;
status = run_command_v_opt(args, 0); status = run_command_v_opt(args, 0);
if (status < -ERR_RUN_COMMAND_FORK)
; /* failure in run-command */
else
status = -status;
fd = open(temp[1], O_RDONLY); fd = open(temp[1], O_RDONLY);
if (fd < 0) if (fd < 0)
goto bad; goto bad;

View File

@ -19,6 +19,7 @@ int start_command(struct child_process *cmd)
{ {
int need_in, need_out, need_err; int need_in, need_out, need_err;
int fdin[2], fdout[2], fderr[2]; int fdin[2], fdout[2], fderr[2];
int failed_errno = failed_errno;


/* /*
* In case of errors we must keep the promise to close FDs * In case of errors we must keep the promise to close FDs
@ -28,9 +29,10 @@ int start_command(struct child_process *cmd)
need_in = !cmd->no_stdin && cmd->in < 0; need_in = !cmd->no_stdin && cmd->in < 0;
if (need_in) { if (need_in) {
if (pipe(fdin) < 0) { if (pipe(fdin) < 0) {
failed_errno = errno;
if (cmd->out > 0) if (cmd->out > 0)
close(cmd->out); close(cmd->out);
return -ERR_RUN_COMMAND_PIPE; goto fail_pipe;
} }
cmd->in = fdin[1]; cmd->in = fdin[1];
} }
@ -40,11 +42,12 @@ int start_command(struct child_process *cmd)
&& cmd->out < 0; && cmd->out < 0;
if (need_out) { if (need_out) {
if (pipe(fdout) < 0) { if (pipe(fdout) < 0) {
failed_errno = errno;
if (need_in) if (need_in)
close_pair(fdin); close_pair(fdin);
else if (cmd->in) else if (cmd->in)
close(cmd->in); close(cmd->in);
return -ERR_RUN_COMMAND_PIPE; goto fail_pipe;
} }
cmd->out = fdout[0]; cmd->out = fdout[0];
} }
@ -52,6 +55,7 @@ int start_command(struct child_process *cmd)
need_err = !cmd->no_stderr && cmd->err < 0; need_err = !cmd->no_stderr && cmd->err < 0;
if (need_err) { if (need_err) {
if (pipe(fderr) < 0) { if (pipe(fderr) < 0) {
failed_errno = errno;
if (need_in) if (need_in)
close_pair(fdin); close_pair(fdin);
else if (cmd->in) else if (cmd->in)
@ -60,7 +64,11 @@ int start_command(struct child_process *cmd)
close_pair(fdout); close_pair(fdout);
else if (cmd->out) else if (cmd->out)
close(cmd->out); close(cmd->out);
return -ERR_RUN_COMMAND_PIPE; fail_pipe:
error("cannot create pipe for %s: %s",
cmd->argv[0], strerror(failed_errno));
errno = failed_errno;
return -1;
} }
cmd->err = fderr[0]; cmd->err = fderr[0];
} }
@ -122,6 +130,9 @@ int start_command(struct child_process *cmd)
strerror(errno)); strerror(errno));
exit(127); exit(127);
} }
if (cmd->pid < 0)
error("cannot fork() for %s: %s", cmd->argv[0],
strerror(failed_errno = errno));
#else #else
int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */ int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */
const char **sargv = cmd->argv; const char **sargv = cmd->argv;
@ -173,6 +184,9 @@ int start_command(struct child_process *cmd)
} }


cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env); cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env);
failed_errno = errno;
if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
error("cannot spawn %s: %s", cmd->argv[0], strerror(errno));


if (cmd->env) if (cmd->env)
free_environ(env); free_environ(env);
@ -189,7 +203,6 @@ int start_command(struct child_process *cmd)
#endif #endif


if (cmd->pid < 0) { if (cmd->pid < 0) {
int err = errno;
if (need_in) if (need_in)
close_pair(fdin); close_pair(fdin);
else if (cmd->in) else if (cmd->in)
@ -200,9 +213,8 @@ int start_command(struct child_process *cmd)
close(cmd->out); close(cmd->out);
if (need_err) if (need_err)
close_pair(fderr); close_pair(fderr);
return err == ENOENT ? errno = failed_errno;
-ERR_RUN_COMMAND_EXEC : return -1;
-ERR_RUN_COMMAND_FORK;
} }


if (need_in) if (need_in)
@ -221,40 +233,51 @@ int start_command(struct child_process *cmd)
return 0; return 0;
} }


static int wait_or_whine(pid_t pid) static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
{ {
for (;;) { int status, code = -1;
int status, code; pid_t waiting;
pid_t waiting = waitpid(pid, &status, 0); int failed_errno = 0;


if (waiting < 0) { while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
if (errno == EINTR) ; /* nothing */
continue;
error("waitpid failed (%s)", strerror(errno));
return -ERR_RUN_COMMAND_WAITPID;
}
if (waiting != pid)
return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
if (WIFSIGNALED(status))
return -ERR_RUN_COMMAND_WAITPID_SIGNAL;


if (!WIFEXITED(status)) if (waiting < 0) {
return -ERR_RUN_COMMAND_WAITPID_NOEXIT; failed_errno = errno;
error("waitpid for %s failed: %s", argv0, strerror(errno));
} else if (waiting != pid) {
error("waitpid is confused (%s)", argv0);
} else if (WIFSIGNALED(status)) {
code = WTERMSIG(status);
error("%s died of signal %d", argv0, code);
/*
* This return value is chosen so that code & 0xff
* mimics the exit code that a POSIX shell would report for
* a program that died from this signal.
*/
code -= 128;
} else if (WIFEXITED(status)) {
code = WEXITSTATUS(status); code = WEXITSTATUS(status);
switch (code) { /*
case 127: * Convert special exit code when execvp failed.
return -ERR_RUN_COMMAND_EXEC; */
case 0: if (code == 127) {
return 0; code = -1;
default: failed_errno = ENOENT;
return -code; if (!silent_exec_failure)
error("cannot run %s: %s", argv0,
strerror(ENOENT));
} }
} else {
error("waitpid is confused (%s)", argv0);
} }
errno = failed_errno;
return code;
} }


int finish_command(struct child_process *cmd) int finish_command(struct child_process *cmd)
{ {
return wait_or_whine(cmd->pid); return wait_or_whine(cmd->pid, cmd->argv[0], cmd->silent_exec_failure);
} }


int run_command(struct child_process *cmd) int run_command(struct child_process *cmd)
@ -274,6 +297,7 @@ static void prepare_run_command_v_opt(struct child_process *cmd,
cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0; cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0; cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0; cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
cmd->silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;
} }


int run_command_v_opt(const char **argv, int opt) int run_command_v_opt(const char **argv, int opt)
@ -338,10 +362,7 @@ int start_async(struct async *async)
int finish_async(struct async *async) int finish_async(struct async *async)
{ {
#ifndef __MINGW32__ #ifndef __MINGW32__
int ret = 0; int ret = wait_or_whine(async->pid, "child process", 0);

if (wait_or_whine(async->pid))
ret = error("waitpid (async) failed");
#else #else
DWORD ret = 0; DWORD ret = 0;
if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0) if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0)
@ -385,15 +406,7 @@ int run_hook(const char *index_file, const char *name, ...)
hook.env = env; hook.env = env;
} }


ret = start_command(&hook); ret = run_command(&hook);
free(argv); free(argv);
if (ret) {
warning("Could not spawn %s", argv[0]);
return ret;
}
ret = finish_command(&hook);
if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL)
warning("%s exited due to uncaught signal", argv[0]);

return ret; return ret;
} }

View File

@ -1,17 +1,6 @@
#ifndef RUN_COMMAND_H #ifndef RUN_COMMAND_H
#define RUN_COMMAND_H #define RUN_COMMAND_H


enum {
ERR_RUN_COMMAND_FORK = 10000,
ERR_RUN_COMMAND_EXEC,
ERR_RUN_COMMAND_PIPE,
ERR_RUN_COMMAND_WAITPID,
ERR_RUN_COMMAND_WAITPID_WRONG_PID,
ERR_RUN_COMMAND_WAITPID_SIGNAL,
ERR_RUN_COMMAND_WAITPID_NOEXIT,
};
#define IS_RUN_COMMAND_ERR(x) (-(x) >= ERR_RUN_COMMAND_FORK)

struct child_process { struct child_process {
const char **argv; const char **argv;
pid_t pid; pid_t pid;
@ -42,6 +31,7 @@ struct child_process {
unsigned no_stdout:1; unsigned no_stdout:1;
unsigned no_stderr:1; unsigned no_stderr:1;
unsigned git_cmd:1; /* if this is to be git sub-command */ unsigned git_cmd:1; /* if this is to be git sub-command */
unsigned silent_exec_failure:1;
unsigned stdout_to_stderr:1; unsigned stdout_to_stderr:1;
void (*preexec_cb)(void); void (*preexec_cb)(void);
}; };
@ -55,6 +45,7 @@ extern int run_hook(const char *index_file, const char *name, ...);
#define RUN_COMMAND_NO_STDIN 1 #define RUN_COMMAND_NO_STDIN 1
#define RUN_GIT_CMD 2 /*If this is to be git sub-command */ #define RUN_GIT_CMD 2 /*If this is to be git sub-command */
#define RUN_COMMAND_STDOUT_TO_STDERR 4 #define RUN_COMMAND_STDOUT_TO_STDERR 4
#define RUN_SILENT_EXEC_FAILURE 8
int run_command_v_opt(const char **argv, int opt); int run_command_v_opt(const char **argv, int opt);


/* /*

View File

@ -54,7 +54,10 @@ test_expect_success 'upload-pack fails due to error in rev-list' '
! echo "0032want $(git rev-parse HEAD) ! echo "0032want $(git rev-parse HEAD)
0034shallow $(git rev-parse HEAD^)00000009done 0034shallow $(git rev-parse HEAD^)00000009done
0000" | git upload-pack . > /dev/null 2> output.err && 0000" | git upload-pack . > /dev/null 2> output.err &&
grep "waitpid (async) failed" output.err # pack-objects survived
grep "Total.*, reused" output.err &&
# but there was an error, which must have been in rev-list
grep "bad tree object" output.err
' '


test_expect_success 'upload-pack fails due to error in pack-objects enumeration' ' test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '

View File

@ -396,7 +396,6 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons
{ {
const char **argv; const char **argv;
int argc; int argc;
int err;


if (flags & TRANSPORT_PUSH_MIRROR) if (flags & TRANSPORT_PUSH_MIRROR)
return error("http transport does not support mirror mode"); return error("http transport does not support mirror mode");
@ -416,20 +415,7 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons
while (refspec_nr--) while (refspec_nr--)
argv[argc++] = *refspec++; argv[argc++] = *refspec++;
argv[argc] = NULL; argv[argc] = NULL;
err = run_command_v_opt(argv, RUN_GIT_CMD); return !!run_command_v_opt(argv, RUN_GIT_CMD);
switch (err) {
case -ERR_RUN_COMMAND_FORK:
error("unable to fork for %s", argv[0]);
case -ERR_RUN_COMMAND_EXEC:
error("unable to exec %s", argv[0]);
break;
case -ERR_RUN_COMMAND_WAITPID:
case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
error("%s died with strange error", argv[0]);
}
return !!err;
} }


static struct ref *get_refs_via_curl(struct transport *transport, int for_push) static struct ref *get_refs_via_curl(struct transport *transport, int for_push)