Merge branch 'js/forkexec'

* js/forkexec:
  Use the asyncronous function infrastructure to run the content filter.
  Avoid a dup2(2) in apply_filter() - start_command() can do it for us.
  t0021-conversion.sh: Test that the clean filter really cleans content.
  upload-pack: Run rev-list in an asynchronous function.
  upload-pack: Move the revision walker into a separate function.
  Use the asyncronous function infrastructure in builtin-fetch-pack.c.
  Add infrastructure to run a function asynchronously.
  upload-pack: Use start_command() to run pack-objects in create_pack_file().
  Have start_command() create a pipe to read the stderr of the child.
  Use start_comand() in builtin-fetch-pack.c instead of explicit fork/exec.
  Use run_command() to spawn external diff programs instead of fork/exec.
  Use start_command() to run content filters instead of explicit fork/exec.
  Use start_command() in git_connect() instead of explicit fork/exec.
  Change git_connect() to return a struct child_process instead of a pid_t.

Conflicts:

	builtin-fetch-pack.c
maint
Junio C Hamano 2007-11-01 13:47:47 -07:00
commit 4340a813d0
13 changed files with 329 additions and 365 deletions

View File

@ -30,7 +30,7 @@ static int run_remote_archiver(const char *remote, int argc,
{ {
char *url, buf[LARGE_PACKET_MAX]; char *url, buf[LARGE_PACKET_MAX];
int fd[2], i, len, rv; int fd[2], i, len, rv;
pid_t pid; struct child_process *conn;
const char *exec = "git-upload-archive"; const char *exec = "git-upload-archive";
int exec_at = 0; int exec_at = 0;


@ -46,9 +46,7 @@ static int run_remote_archiver(const char *remote, int argc,
} }


url = xstrdup(remote); url = xstrdup(remote);
pid = git_connect(fd, url, exec, 0); conn = git_connect(fd, url, exec, 0);
if (pid < 0)
return pid;


for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
if (i == exec_at) if (i == exec_at)
@ -76,7 +74,7 @@ static int run_remote_archiver(const char *remote, int argc,
rv = recv_sideband("archive", fd[0], 1, 2); rv = recv_sideband("archive", fd[0], 1, 2);
close(fd[0]); close(fd[0]);
close(fd[1]); close(fd[1]);
rv |= finish_connect(pid); rv |= finish_connect(conn);


return !!rv; return !!rv;
} }

View File

@ -7,6 +7,7 @@
#include "pack.h" #include "pack.h"
#include "sideband.h" #include "sideband.h"
#include "fetch-pack.h" #include "fetch-pack.h"
#include "run-command.h"


static int transfer_unpack_limit = -1; static int transfer_unpack_limit = -1;
static int fetch_unpack_limit = -1; static int fetch_unpack_limit = -1;
@ -457,53 +458,49 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
return retval; return retval;
} }


static pid_t setup_sideband(int fd[2], int xd[2]) static int sideband_demux(int fd, void *data)
{ {
pid_t side_pid; int *xd = data;


close(xd[1]);
return recv_sideband("fetch-pack", xd[0], fd, 2);
}

static void setup_sideband(int fd[2], int xd[2], struct async *demux)
{
if (!use_sideband) { if (!use_sideband) {
fd[0] = xd[0]; fd[0] = xd[0];
fd[1] = xd[1]; fd[1] = xd[1];
return 0; return;
} }
/* xd[] is talking with upload-pack; subprocess reads from /* xd[] is talking with upload-pack; subprocess reads from
* xd[0], spits out band#2 to stderr, and feeds us band#1 * xd[0], spits out band#2 to stderr, and feeds us band#1
* through our fd[0]. * through demux->out.
*/ */
if (pipe(fd) < 0) demux->proc = sideband_demux;
die("fetch-pack: unable to set up pipe"); demux->data = xd;
side_pid = fork(); if (start_async(demux))
if (side_pid < 0)
die("fetch-pack: unable to fork off sideband demultiplexer"); die("fetch-pack: unable to fork off sideband demultiplexer");
if (!side_pid) {
/* subprocess */
close(fd[0]);
if (xd[0] != xd[1])
close(xd[1]);
if (recv_sideband("fetch-pack", xd[0], fd[1], 2))
exit(1);
exit(0);
}
close(xd[0]); close(xd[0]);
close(fd[1]); fd[0] = demux->out;
fd[1] = xd[1]; fd[1] = xd[1];
return side_pid;
} }


static int get_pack(int xd[2], char **pack_lockfile) static int get_pack(int xd[2], char **pack_lockfile)
{ {
int status; struct async demux;
pid_t pid, side_pid;
int fd[2]; int fd[2];
const char *argv[20]; const char *argv[20];
char keep_arg[256]; char keep_arg[256];
char hdr_arg[256]; char hdr_arg[256];
const char **av; const char **av;
int do_keep = args.keep_pack; int do_keep = args.keep_pack;
int keep_pipe[2]; struct child_process cmd;


side_pid = setup_sideband(fd, xd); setup_sideband(fd, xd, &demux);


memset(&cmd, 0, sizeof(cmd));
cmd.argv = argv;
av = argv; av = argv;
*hdr_arg = 0; *hdr_arg = 0;
if (!args.keep_pack && unpack_limit) { if (!args.keep_pack && unpack_limit) {
@ -520,8 +517,8 @@ static int get_pack(int xd[2], char **pack_lockfile)
} }


if (do_keep) { if (do_keep) {
if (pack_lockfile && pipe(keep_pipe)) if (pack_lockfile)
die("fetch-pack: pipe setup failure: %s", strerror(errno)); cmd.out = -1;
*av++ = "index-pack"; *av++ = "index-pack";
*av++ = "--stdin"; *av++ = "--stdin";
if (!args.quiet && !args.no_progress) if (!args.quiet && !args.no_progress)
@ -545,44 +542,20 @@ static int get_pack(int xd[2], char **pack_lockfile)
*av++ = hdr_arg; *av++ = hdr_arg;
*av++ = NULL; *av++ = NULL;


pid = fork(); cmd.in = fd[0];
if (pid < 0) cmd.git_cmd = 1;
if (start_command(&cmd))
die("fetch-pack: unable to fork off %s", argv[0]); die("fetch-pack: unable to fork off %s", argv[0]);
if (!pid) {
dup2(fd[0], 0);
if (do_keep && pack_lockfile) {
dup2(keep_pipe[1], 1);
close(keep_pipe[0]);
close(keep_pipe[1]);
}
close(fd[0]);
close(fd[1]); close(fd[1]);
execv_git_cmd(argv); if (do_keep && pack_lockfile)
die("%s exec failed", argv[0]); *pack_lockfile = index_pack_lockfile(cmd.out);
}
close(fd[0]); if (finish_command(&cmd))
close(fd[1]); die("%s failed", argv[0]);
if (do_keep && pack_lockfile) { if (use_sideband && finish_async(&demux))
close(keep_pipe[1]); die("error in sideband demultiplexer");
*pack_lockfile = index_pack_lockfile(keep_pipe[0]);
close(keep_pipe[0]);
}
while (waitpid(pid, &status, 0) < 0) {
if (errno != EINTR)
die("waiting for %s: %s", argv[0], strerror(errno));
}
if (WIFEXITED(status)) {
int code = WEXITSTATUS(status);
if (code)
die("%s died with error code %d", argv[0], code);
return 0; return 0;
} }
if (WIFSIGNALED(status)) {
int sig = WTERMSIG(status);
die("%s died of signal %d", argv[0], sig);
}
die("%s died of unnatural causes %d", argv[0], status);
}


static struct ref *do_fetch_pack(int fd[2], static struct ref *do_fetch_pack(int fd[2],
int nr_match, int nr_match,
@ -763,7 +736,7 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
{ {
int i, ret; int i, ret;
int fd[2]; int fd[2];
pid_t pid; struct child_process *conn;
struct ref *ref; struct ref *ref;
struct stat st; struct stat st;


@ -774,16 +747,14 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
st.st_mtime = 0; st.st_mtime = 0;
} }


pid = git_connect(fd, (char *)dest, args.uploadpack, conn = git_connect(fd, (char *)dest, args.uploadpack,
args.verbose ? CONNECT_VERBOSE : 0); args.verbose ? CONNECT_VERBOSE : 0);
if (pid < 0)
return NULL;
if (heads && nr_heads) if (heads && nr_heads)
nr_heads = remove_duplicates(nr_heads, heads); nr_heads = remove_duplicates(nr_heads, heads);
ref = do_fetch_pack(fd, nr_heads, heads, pack_lockfile); ref = do_fetch_pack(fd, nr_heads, heads, pack_lockfile);
close(fd[0]); close(fd[0]);
close(fd[1]); close(fd[1]);
ret = finish_connect(pid); ret = finish_connect(conn);


if (!ret && nr_heads) { if (!ret && nr_heads) {
/* If the heads to pull were given, we should have /* If the heads to pull were given, we should have

View File

@ -503,8 +503,8 @@ struct ref {
#define REF_TAGS (1u << 2) #define REF_TAGS (1u << 2)


#define CONNECT_VERBOSE (1u << 0) #define CONNECT_VERBOSE (1u << 0)
extern pid_t git_connect(int fd[2], char *url, const char *prog, int flags); extern struct child_process *git_connect(int fd[2], char *url, const char *prog, int flags);
extern int finish_connect(pid_t pid); extern int finish_connect(struct child_process *conn);
extern int path_match(const char *path, int nr, char **match); extern int path_match(const char *path, int nr, char **match);
extern int get_ack(int fd, unsigned char *result_sha1); extern int get_ack(int fd, unsigned char *result_sha1);
extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags); extern struct ref **get_remote_heads(int in, struct ref **list, int nr_match, char **match, unsigned int flags);

104
connect.c
View File

@ -468,24 +468,26 @@ char *get_port(char *host)
} }


/* /*
* This returns 0 if the transport protocol does not need fork(2), * This returns NULL if the transport protocol does not need fork(2), or a
* or a process id if it does. Once done, finish the connection * struct child_process object if it does. Once done, finish the connection
* with finish_connect() with the value returned from this function * with finish_connect() with the value returned from this function
* (it is safe to call finish_connect() with 0 to support the former * (it is safe to call finish_connect() with NULL to support the former
* case). * case).
* *
* Does not return a negative value on error; it just dies. * If it returns, the connect is successful; it just dies on errors.
*/ */
pid_t git_connect(int fd[2], char *url, const char *prog, int flags) struct child_process *git_connect(int fd[2], char *url,
const char *prog, int flags)
{ {
char *host, *path = url; char *host, *path = url;
char *end; char *end;
int c; int c;
int pipefd[2][2]; struct child_process *conn;
pid_t pid;
enum protocol protocol = PROTO_LOCAL; enum protocol protocol = PROTO_LOCAL;
int free_path = 0; int free_path = 0;
char *port = NULL; char *port = NULL;
const char **arg;
struct strbuf cmd;


/* Without this we cannot rely on waitpid() to tell /* Without this we cannot rely on waitpid() to tell
* what happened to our children. * what happened to our children.
@ -568,16 +570,10 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
free(target_host); free(target_host);
if (free_path) if (free_path)
free(path); free(path);
return 0; return NULL;
} }


if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0) conn = xcalloc(1, sizeof(*conn));
die("unable to create pipe pair for communication");
pid = fork();
if (pid < 0)
die("unable to fork");
if (!pid) {
struct strbuf cmd;


strbuf_init(&cmd, MAX_CMD_LEN); strbuf_init(&cmd, MAX_CMD_LEN);
strbuf_addstr(&cmd, prog); strbuf_addstr(&cmd, prog);
@ -586,56 +582,56 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags)
if (cmd.len >= MAX_CMD_LEN) if (cmd.len >= MAX_CMD_LEN)
die("command line too long"); die("command line too long");


dup2(pipefd[1][0], 0); conn->in = conn->out = -1;
dup2(pipefd[0][1], 1); conn->argv = arg = xcalloc(6, sizeof(*arg));
close(pipefd[0][0]);
close(pipefd[0][1]);
close(pipefd[1][0]);
close(pipefd[1][1]);
if (protocol == PROTO_SSH) { if (protocol == PROTO_SSH) {
const char *ssh, *ssh_basename; const char *ssh = getenv("GIT_SSH");
ssh = getenv("GIT_SSH");
if (!ssh) ssh = "ssh"; if (!ssh) ssh = "ssh";
ssh_basename = strrchr(ssh, '/');
if (!ssh_basename)
ssh_basename = ssh;
else
ssh_basename++;


if (!port) *arg++ = ssh;
execlp(ssh, ssh_basename, host, cmd.buf, NULL); if (port) {
else *arg++ = "-p";
execlp(ssh, ssh_basename, "-p", port, host, *arg++ = port;
cmd.buf, NULL); }
*arg++ = host;
} }
else { else {
unsetenv(ALTERNATE_DB_ENVIRONMENT); /* remove these from the environment */
unsetenv(DB_ENVIRONMENT); const char *env[] = {
unsetenv(GIT_DIR_ENVIRONMENT); ALTERNATE_DB_ENVIRONMENT,
unsetenv(GIT_WORK_TREE_ENVIRONMENT); DB_ENVIRONMENT,
unsetenv(GRAFT_ENVIRONMENT); GIT_DIR_ENVIRONMENT,
unsetenv(INDEX_ENVIRONMENT); GIT_WORK_TREE_ENVIRONMENT,
execlp("sh", "sh", "-c", cmd.buf, NULL); GRAFT_ENVIRONMENT,
INDEX_ENVIRONMENT,
NULL
};
conn->env = env;
*arg++ = "sh";
*arg++ = "-c";
} }
die("exec failed"); *arg++ = cmd.buf;
} *arg = NULL;
fd[0] = pipefd[0][0];
fd[1] = pipefd[1][1]; if (start_command(conn))
close(pipefd[0][1]); die("unable to fork");
close(pipefd[1][0]);
fd[0] = conn->out; /* read from child's stdout */
fd[1] = conn->in; /* write to child's stdin */
strbuf_release(&cmd);
if (free_path) if (free_path)
free(path); free(path);
return pid; return conn;
} }


int finish_connect(pid_t pid) int finish_connect(struct child_process *conn)
{ {
if (pid == 0) int code;
if (!conn)
return 0; return 0;


while (waitpid(pid, NULL, 0) < 0) { code = finish_command(conn);
if (errno != EINTR) free(conn->argv);
return -1; free(conn);
} return code;
return 0;
} }

View File

@ -192,48 +192,39 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len,
return 1; return 1;
} }


static int filter_buffer(const char *path, const char *src, struct filter_params {
unsigned long size, const char *cmd) const char *src;
unsigned long size;
const char *cmd;
};

static int filter_buffer(int fd, void *data)
{ {
/* /*
* Spawn cmd and feed the buffer contents through its stdin. * Spawn cmd and feed the buffer contents through its stdin.
*/ */
struct child_process child_process; struct child_process child_process;
int pipe_feed[2]; struct filter_params *params = (struct filter_params *)data;
int write_err, status; int write_err, status;
const char *argv[] = { "sh", "-c", params->cmd, NULL };


memset(&child_process, 0, sizeof(child_process)); memset(&child_process, 0, sizeof(child_process));
child_process.argv = argv;
child_process.in = -1;
child_process.out = fd;


if (pipe(pipe_feed) < 0) { if (start_command(&child_process))
error("cannot create pipe to run external filter %s", cmd); return error("cannot fork to run external filter %s", params->cmd);
return 1;
}


child_process.pid = fork(); write_err = (write_in_full(child_process.in, params->src, params->size) < 0);
if (child_process.pid < 0) { if (close(child_process.in))
error("cannot fork to run external filter %s", cmd);
close(pipe_feed[0]);
close(pipe_feed[1]);
return 1;
}
if (!child_process.pid) {
dup2(pipe_feed[0], 0);
close(pipe_feed[0]);
close(pipe_feed[1]);
execlp("sh", "sh", "-c", cmd, NULL);
return 1;
}
close(pipe_feed[0]);

write_err = (write_in_full(pipe_feed[1], src, size) < 0);
if (close(pipe_feed[1]))
write_err = 1; write_err = 1;
if (write_err) if (write_err)
error("cannot feed the input to external filter %s", cmd); error("cannot feed the input to external filter %s", params->cmd);


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


@ -246,49 +237,36 @@ static int apply_filter(const char *path, const char *src, size_t len,
* *
* (child --> cmd) --> us * (child --> cmd) --> us
*/ */
int pipe_feed[2]; int ret = 1;
int status, ret = 1;
struct child_process child_process;
struct strbuf nbuf; struct strbuf nbuf;
struct async async;
struct filter_params params;


if (!cmd) if (!cmd)
return 0; return 0;


memset(&child_process, 0, sizeof(child_process)); memset(&async, 0, sizeof(async));

async.proc = filter_buffer;
if (pipe(pipe_feed) < 0) { async.data = &params;
error("cannot create pipe to run external filter %s", cmd); params.src = src;
return 0; params.size = len;
} params.cmd = cmd;


fflush(NULL); fflush(NULL);
child_process.pid = fork(); if (start_async(&async))
if (child_process.pid < 0) { return 0; /* error was already reported */
error("cannot fork to run external filter %s", cmd);
close(pipe_feed[0]);
close(pipe_feed[1]);
return 0;
}
if (!child_process.pid) {
dup2(pipe_feed[1], 1);
close(pipe_feed[0]);
close(pipe_feed[1]);
exit(filter_buffer(path, src, len, cmd));
}
close(pipe_feed[1]);


strbuf_init(&nbuf, 0); strbuf_init(&nbuf, 0);
if (strbuf_read(&nbuf, pipe_feed[0], len) < 0) { if (strbuf_read(&nbuf, async.out, len) < 0) {
error("read from external filter %s failed", cmd); error("read from external filter %s failed", cmd);
ret = 0; ret = 0;
} }
if (close(pipe_feed[0])) { if (close(async.out)) {
error("read from external filter %s failed", cmd); error("read from external filter %s failed", cmd);
ret = 0; ret = 0;
} }
status = finish_command(&child_process); if (finish_async(&async)) {
if (status) { error("external filter %s failed", cmd);
error("external filter %s failed %d", cmd, -status);
ret = 0; ret = 0;
} }



38
diff.c
View File

@ -9,6 +9,7 @@
#include "xdiff-interface.h" #include "xdiff-interface.h"
#include "color.h" #include "color.h"
#include "attr.h" #include "attr.h"
#include "run-command.h"


#ifdef NO_FAST_WORKING_DIRECTORY #ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0 #define FAST_WORKING_DIRECTORY 0
@ -1761,40 +1762,6 @@ static void remove_tempfile_on_signal(int signo)
raise(signo); raise(signo);
} }


static int spawn_prog(const char *pgm, const char **arg)
{
pid_t pid;
int status;

fflush(NULL);
pid = fork();
if (pid < 0)
die("unable to fork");
if (!pid) {
execvp(pgm, (char *const*) arg);
exit(255);
}

while (waitpid(pid, &status, 0) < 0) {
if (errno == EINTR)
continue;
return -1;
}

/* Earlier we did not check the exit status because
* diff exits non-zero if files are different, and
* we are not interested in knowing that. It was a
* mistake which made it harder to quit a diff-*
* session that uses the git-apply-patch-script as
* the GIT_EXTERNAL_DIFF. A custom GIT_EXTERNAL_DIFF
* should also exit non-zero only when it wants to
* abort the entire diff-* session.
*/
if (WIFEXITED(status) && !WEXITSTATUS(status))
return 0;
return -1;
}

/* An external diff command takes: /* An external diff command takes:
* *
* diff-cmd name infile1 infile1-sha1 infile1-mode \ * diff-cmd name infile1 infile1-sha1 infile1-mode \
@ -1847,7 +1814,8 @@ static void run_external_diff(const char *pgm,
*arg++ = name; *arg++ = name;
} }
*arg = NULL; *arg = NULL;
retval = spawn_prog(pgm, spawn_arg); fflush(NULL);
retval = run_command_v_opt(spawn_arg, 0);
remove_tempfile(); remove_tempfile();
if (retval) { if (retval) {
fprintf(stderr, "external diff died, stopping at %s.\n", name); fprintf(stderr, "external diff died, stopping at %s.\n", name);

View File

@ -25,7 +25,7 @@ int main(int argc, char **argv)
int i, ret; int i, ret;
char *dest = NULL; char *dest = NULL;
int fd[2]; int fd[2];
pid_t pid; struct child_process *conn;
int nongit = 0; int nongit = 0;
unsigned flags = 0; unsigned flags = 0;


@ -64,12 +64,10 @@ int main(int argc, char **argv)
if (!dest || i != argc - 1) if (!dest || i != argc - 1)
usage(peek_remote_usage); usage(peek_remote_usage);


pid = git_connect(fd, dest, uploadpack, 0); conn = git_connect(fd, dest, uploadpack, 0);
if (pid < 0)
return 1;
ret = peek_remote(fd, flags); ret = peek_remote(fd, flags);
close(fd[0]); close(fd[0]);
close(fd[1]); close(fd[1]);
ret |= finish_connect(pid); ret |= finish_connect(conn);
return !!ret; return !!ret;
} }

View File

@ -17,8 +17,8 @@ static inline void dup_devnull(int to)


int start_command(struct child_process *cmd) int start_command(struct child_process *cmd)
{ {
int need_in, need_out; int need_in, need_out, need_err;
int fdin[2], fdout[2]; int fdin[2], fdout[2], fderr[2];


need_in = !cmd->no_stdin && cmd->in < 0; need_in = !cmd->no_stdin && cmd->in < 0;
if (need_in) { if (need_in) {
@ -41,12 +41,26 @@ int start_command(struct child_process *cmd)
cmd->close_out = 1; cmd->close_out = 1;
} }


need_err = cmd->err < 0;
if (need_err) {
if (pipe(fderr) < 0) {
if (need_in)
close_pair(fdin);
if (need_out)
close_pair(fdout);
return -ERR_RUN_COMMAND_PIPE;
}
cmd->err = fderr[0];
}

cmd->pid = fork(); cmd->pid = fork();
if (cmd->pid < 0) { if (cmd->pid < 0) {
if (need_in) if (need_in)
close_pair(fdin); close_pair(fdin);
if (need_out) if (need_out)
close_pair(fdout); close_pair(fdout);
if (need_err)
close_pair(fderr);
return -ERR_RUN_COMMAND_FORK; return -ERR_RUN_COMMAND_FORK;
} }


@ -73,6 +87,11 @@ int start_command(struct child_process *cmd)
close(cmd->out); close(cmd->out);
} }


if (need_err) {
dup2(fderr[1], 2);
close_pair(fderr);
}

if (cmd->dir && chdir(cmd->dir)) if (cmd->dir && chdir(cmd->dir))
die("exec %s: cd to %s failed (%s)", cmd->argv[0], die("exec %s: cd to %s failed (%s)", cmd->argv[0],
cmd->dir, strerror(errno)); cmd->dir, strerror(errno));
@ -102,19 +121,17 @@ int start_command(struct child_process *cmd)
else if (cmd->out > 1) else if (cmd->out > 1)
close(cmd->out); close(cmd->out);


if (need_err)
close(fderr[1]);

return 0; return 0;
} }


int finish_command(struct child_process *cmd) static int wait_or_whine(pid_t pid)
{ {
if (cmd->close_in)
close(cmd->in);
if (cmd->close_out)
close(cmd->out);

for (;;) { for (;;) {
int status, code; int status, code;
pid_t waiting = waitpid(cmd->pid, &status, 0); pid_t waiting = waitpid(pid, &status, 0);


if (waiting < 0) { if (waiting < 0) {
if (errno == EINTR) if (errno == EINTR)
@ -122,7 +139,7 @@ int finish_command(struct child_process *cmd)
error("waitpid failed (%s)", strerror(errno)); error("waitpid failed (%s)", strerror(errno));
return -ERR_RUN_COMMAND_WAITPID; return -ERR_RUN_COMMAND_WAITPID;
} }
if (waiting != cmd->pid) if (waiting != pid)
return -ERR_RUN_COMMAND_WAITPID_WRONG_PID; return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
if (WIFSIGNALED(status)) if (WIFSIGNALED(status))
return -ERR_RUN_COMMAND_WAITPID_SIGNAL; return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
@ -136,6 +153,15 @@ int finish_command(struct child_process *cmd)
} }
} }


int finish_command(struct child_process *cmd)
{
if (cmd->close_in)
close(cmd->in);
if (cmd->close_out)
close(cmd->out);
return wait_or_whine(cmd->pid);
}

int run_command(struct child_process *cmd) int run_command(struct child_process *cmd)
{ {
int code = start_command(cmd); int code = start_command(cmd);
@ -178,3 +204,34 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const
cmd.env = env; cmd.env = env;
return run_command(&cmd); return run_command(&cmd);
} }

int start_async(struct async *async)
{
int pipe_out[2];

if (pipe(pipe_out) < 0)
return error("cannot create pipe: %s", strerror(errno));

async->pid = fork();
if (async->pid < 0) {
error("fork (async) failed: %s", strerror(errno));
close_pair(pipe_out);
return -1;
}
if (!async->pid) {
close(pipe_out[0]);
exit(!!async->proc(pipe_out[1], async->data));
}
async->out = pipe_out[0];
close(pipe_out[1]);
return 0;
}

int finish_async(struct async *async)
{
int ret = 0;

if (wait_or_whine(async->pid))
ret = error("waitpid (async) failed");
return ret;
}

View File

@ -16,6 +16,7 @@ struct child_process {
pid_t pid; pid_t pid;
int in; int in;
int out; int out;
int err;
const char *dir; const char *dir;
const char *const *env; const char *const *env;
unsigned close_in:1; unsigned close_in:1;
@ -42,4 +43,26 @@ int run_command_v_opt_cd(const char **argv, int opt, const char *dir);
*/ */
int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env); int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const char *const *env);


/*
* The purpose of the following functions is to feed a pipe by running
* a function asynchronously and providing output that the caller reads.
*
* It is expected that no synchronization and mutual exclusion between
* the caller and the feed function is necessary so that the function
* can run in a thread without interfering with the caller.
*/
struct async {
/*
* proc writes to fd and closes it;
* returns 0 on success, non-zero on failure
*/
int (*proc)(int fd, void *data);
void *data;
int out; /* caller reads from here and closes it */
pid_t pid;
};

int start_async(struct async *async);
int finish_async(struct async *async);

#endif #endif

View File

@ -384,7 +384,7 @@ int main(int argc, char **argv)
char *dest = NULL; char *dest = NULL;
char **heads = NULL; char **heads = NULL;
int fd[2], ret; int fd[2], ret;
pid_t pid; struct child_process *conn;
char *remote_name = NULL; char *remote_name = NULL;
struct remote *remote = NULL; struct remote *remote = NULL;


@ -452,12 +452,10 @@ int main(int argc, char **argv)
} }
} }


pid = git_connect(fd, dest, receivepack, verbose ? CONNECT_VERBOSE : 0); conn = git_connect(fd, dest, receivepack, verbose ? CONNECT_VERBOSE : 0);
if (pid < 0)
return 1;
ret = send_pack(fd[0], fd[1], remote, nr_heads, heads); ret = send_pack(fd[0], fd[1], remote, nr_heads, heads);
close(fd[0]); close(fd[0]);
close(fd[1]); close(fd[1]);
ret |= finish_connect(pid); ret |= finish_connect(conn);
return !!ret; return !!ret;
} }

View File

@ -42,7 +42,12 @@ test_expect_success check '
git diff --raw --exit-code :test :test.i && git diff --raw --exit-code :test :test.i &&
id=$(git rev-parse --verify :test) && id=$(git rev-parse --verify :test) &&
embedded=$(sed -ne "$script" test.i) && embedded=$(sed -ne "$script" test.i) &&
test "z$id" = "z$embedded" test "z$id" = "z$embedded" &&

git cat-file blob :test.t > test.r &&

./rot13.sh < test.o > test.t &&
cmp test.r test.t
' '


# If an expanded ident ever gets into the repository, we want to make sure that # If an expanded ident ever gets into the repository, we want to make sure that

View File

@ -601,18 +601,13 @@ static struct ref *get_refs_via_connect(const struct transport *transport)
struct git_transport_data *data = transport->data; struct git_transport_data *data = transport->data;
struct ref *refs; struct ref *refs;
int fd[2]; int fd[2];
pid_t pid;
char *dest = xstrdup(transport->url); char *dest = xstrdup(transport->url);

struct child_process *conn = git_connect(fd, dest, data->uploadpack, 0);
pid = git_connect(fd, dest, data->uploadpack, 0);

if (pid < 0)
die("Failed to connect to \"%s\"", transport->url);


get_remote_heads(fd[0], &refs, 0, NULL, 0); get_remote_heads(fd[0], &refs, 0, NULL, 0);
packet_flush(fd[1]); packet_flush(fd[1]);


finish_connect(pid); finish_connect(conn);


free(dest); free(dest);



View File

@ -9,6 +9,7 @@
#include "diff.h" #include "diff.h"
#include "revision.h" #include "revision.h"
#include "list-objects.h" #include "list-objects.h"
#include "run-command.h"


static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>"; static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";


@ -96,32 +97,12 @@ static void show_edge(struct commit *commit)
fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1)); fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1));
} }


static void create_pack_file(void) static int do_rev_list(int fd, void *create_full_pack)
{ {
/* Pipes between rev-list to pack-objects, pack-objects to us
* and pack-objects error stream for progress bar.
*/
int lp_pipe[2], pu_pipe[2], pe_pipe[2];
pid_t pid_rev_list, pid_pack_objects;
int create_full_pack = (nr_our_refs == want_obj.nr && !have_obj.nr);
char data[8193], progress[128];
char abort_msg[] = "aborting due to possible repository "
"corruption on the remote side.";
int buffered = -1;

if (pipe(lp_pipe) < 0)
die("git-upload-pack: unable to create pipe");
pid_rev_list = fork();
if (pid_rev_list < 0)
die("git-upload-pack: unable to fork git-rev-list");

if (!pid_rev_list) {
int i; int i;
struct rev_info revs; struct rev_info revs;


close(lp_pipe[0]); pack_pipe = fdopen(fd, "w");
pack_pipe = fdopen(lp_pipe[1], "w");

if (create_full_pack) if (create_full_pack)
use_thin_pack = 0; /* no point doing it */ use_thin_pack = 0; /* no point doing it */
init_revisions(&revs, NULL); init_revisions(&revs, NULL);
@ -151,55 +132,51 @@ static void create_pack_file(void)
prepare_revision_walk(&revs); prepare_revision_walk(&revs);
mark_edges_uninteresting(revs.commits, &revs, show_edge); mark_edges_uninteresting(revs.commits, &revs, show_edge);
traverse_commit_list(&revs, show_commit, show_object); traverse_commit_list(&revs, show_commit, show_object);
exit(0); return 0;
} }


if (pipe(pu_pipe) < 0) static void create_pack_file(void)
die("git-upload-pack: unable to create pipe"); {
if (pipe(pe_pipe) < 0) struct async rev_list;
die("git-upload-pack: unable to create pipe"); struct child_process pack_objects;
pid_pack_objects = fork(); int create_full_pack = (nr_our_refs == want_obj.nr && !have_obj.nr);
if (pid_pack_objects < 0) { char data[8193], progress[128];
char abort_msg[] = "aborting due to possible repository "
"corruption on the remote side.";
int buffered = -1;
const char *argv[10];
int arg = 0;

rev_list.proc = do_rev_list;
/* .data is just a boolean: any non-NULL value will do */
rev_list.data = create_full_pack ? &rev_list : NULL;
if (start_async(&rev_list))
die("git-upload-pack: unable to fork git-rev-list");

argv[arg++] = "pack-objects";
argv[arg++] = "--stdout";
if (!no_progress)
argv[arg++] = "--progress";
if (use_ofs_delta)
argv[arg++] = "--delta-base-offset";
argv[arg++] = NULL;

memset(&pack_objects, 0, sizeof(pack_objects));
pack_objects.in = rev_list.out; /* start_command closes it */
pack_objects.out = -1;
pack_objects.err = -1;
pack_objects.git_cmd = 1;
pack_objects.argv = argv;

if (start_command(&pack_objects)) {
/* daemon sets things up to ignore TERM */ /* daemon sets things up to ignore TERM */
kill(pid_rev_list, SIGKILL); kill(rev_list.pid, SIGKILL);
die("git-upload-pack: unable to fork git-pack-objects"); die("git-upload-pack: unable to fork git-pack-objects");
} }
if (!pid_pack_objects) {
const char *argv[10];
int i = 0;


dup2(lp_pipe[0], 0); /* We read from pack_objects.err to capture stderr output for
dup2(pu_pipe[1], 1); * progress bar, and pack_objects.out to capture the pack data.
dup2(pe_pipe[1], 2);

close(lp_pipe[0]);
close(lp_pipe[1]);
close(pu_pipe[0]);
close(pu_pipe[1]);
close(pe_pipe[0]);
close(pe_pipe[1]);

argv[i++] = "pack-objects";
argv[i++] = "--stdout";
if (!no_progress)
argv[i++] = "--progress";
if (use_ofs_delta)
argv[i++] = "--delta-base-offset";
argv[i++] = NULL;

execv_git_cmd(argv);
kill(pid_rev_list, SIGKILL);
die("git-upload-pack: unable to exec git-pack-objects");
}

close(lp_pipe[0]);
close(lp_pipe[1]);

/* We read from pe_pipe[0] to capture stderr output for
* progress bar, and pu_pipe[0] to capture the pack data.
*/ */
close(pe_pipe[1]);
close(pu_pipe[1]);


while (1) { while (1) {
const char *who; const char *who;
@ -214,14 +191,14 @@ static void create_pack_file(void)
pollsize = 0; pollsize = 0;
pe = pu = -1; pe = pu = -1;


if (0 <= pu_pipe[0]) { if (0 <= pack_objects.out) {
pfd[pollsize].fd = pu_pipe[0]; pfd[pollsize].fd = pack_objects.out;
pfd[pollsize].events = POLLIN; pfd[pollsize].events = POLLIN;
pu = pollsize; pu = pollsize;
pollsize++; pollsize++;
} }
if (0 <= pe_pipe[0]) { if (0 <= pack_objects.err) {
pfd[pollsize].fd = pe_pipe[0]; pfd[pollsize].fd = pack_objects.err;
pfd[pollsize].events = POLLIN; pfd[pollsize].events = POLLIN;
pe = pollsize; pe = pollsize;
pollsize++; pollsize++;
@ -254,13 +231,13 @@ static void create_pack_file(void)
*cp++ = buffered; *cp++ = buffered;
outsz++; outsz++;
} }
sz = xread(pu_pipe[0], cp, sz = xread(pack_objects.out, cp,
sizeof(data) - outsz); sizeof(data) - outsz);
if (0 < sz) if (0 < sz)
; ;
else if (sz == 0) { else if (sz == 0) {
close(pu_pipe[0]); close(pack_objects.out);
pu_pipe[0] = -1; pack_objects.out = -1;
} }
else else
goto fail; goto fail;
@ -279,13 +256,13 @@ static void create_pack_file(void)
/* Status ready; we ship that in the side-band /* Status ready; we ship that in the side-band
* or dump to the standard error. * or dump to the standard error.
*/ */
sz = xread(pe_pipe[0], progress, sz = xread(pack_objects.err, progress,
sizeof(progress)); sizeof(progress));
if (0 < sz) if (0 < sz)
send_client_data(2, progress, sz); send_client_data(2, progress, sz);
else if (sz == 0) { else if (sz == 0) {
close(pe_pipe[0]); close(pack_objects.err);
pe_pipe[0] = -1; pack_objects.err = -1;
} }
else else
goto fail; goto fail;
@ -293,12 +270,12 @@ static void create_pack_file(void)
} }


/* See if the children are still there */ /* See if the children are still there */
if (pid_rev_list || pid_pack_objects) { if (rev_list.pid || pack_objects.pid) {
pid = waitpid(-1, &status, WNOHANG); pid = waitpid(-1, &status, WNOHANG);
if (!pid) if (!pid)
continue; continue;
who = ((pid == pid_rev_list) ? "git-rev-list" : who = ((pid == rev_list.pid) ? "git-rev-list" :
(pid == pid_pack_objects) ? "git-pack-objects" : (pid == pack_objects.pid) ? "git-pack-objects" :
NULL); NULL);
if (!who) { if (!who) {
if (pid < 0) { if (pid < 0) {
@ -315,11 +292,11 @@ static void create_pack_file(void)
who); who);
goto fail; goto fail;
} }
if (pid == pid_rev_list) if (pid == rev_list.pid)
pid_rev_list = 0; rev_list.pid = 0;
if (pid == pid_pack_objects) if (pid == pack_objects.pid)
pid_pack_objects = 0; pack_objects.pid = 0;
if (pid_rev_list || pid_pack_objects) if (rev_list.pid || pack_objects.pid)
continue; continue;
} }


@ -340,10 +317,10 @@ static void create_pack_file(void)
return; return;
} }
fail: fail:
if (pid_pack_objects) if (pack_objects.pid)
kill(pid_pack_objects, SIGKILL); kill(pack_objects.pid, SIGKILL);
if (pid_rev_list) if (rev_list.pid)
kill(pid_rev_list, SIGKILL); kill(rev_list.pid, SIGKILL);
send_client_data(3, abort_msg, sizeof(abort_msg)); send_client_data(3, abort_msg, sizeof(abort_msg));
die("git-upload-pack: %s", abort_msg); die("git-upload-pack: %s", abort_msg);
} }