Skip to content

Commit

Permalink
Merge branch 'js/forkexec'
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Junio C Hamano committed Nov 1, 2007
2 parents 6959893 + 546bb58 commit 4340a81
Show file tree
Hide file tree
Showing 13 changed files with 331 additions and 367 deletions.
8 changes: 3 additions & 5 deletions builtin-archive.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ static int run_remote_archiver(const char *remote, int argc,
{
char *url, buf[LARGE_PACKET_MAX];
int fd[2], i, len, rv;
pid_t pid;
struct child_process *conn;
const char *exec = "git-upload-archive";
int exec_at = 0;

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

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

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

return !!rv;
}
Expand Down
101 changes: 36 additions & 65 deletions builtin-fetch-pack.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "pack.h"
#include "sideband.h"
#include "fetch-pack.h"
#include "run-command.h"

static int transfer_unpack_limit = -1;
static int fetch_unpack_limit = -1;
Expand Down Expand Up @@ -457,53 +458,49 @@ static int everything_local(struct ref **refs, int nr_match, char **match)
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) {
fd[0] = xd[0];
fd[1] = xd[1];
return 0;
return;
}
/* xd[] is talking with upload-pack; subprocess reads from
* xd[0], spits out band#2 to stderr, and feeds us band#1
* through our fd[0].
* through demux->out.
*/
if (pipe(fd) < 0)
die("fetch-pack: unable to set up pipe");
side_pid = fork();
if (side_pid < 0)
demux->proc = sideband_demux;
demux->data = xd;
if (start_async(demux))
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(fd[1]);
fd[0] = demux->out;
fd[1] = xd[1];
return side_pid;
}

static int get_pack(int xd[2], char **pack_lockfile)
{
int status;
pid_t pid, side_pid;
struct async demux;
int fd[2];
const char *argv[20];
char keep_arg[256];
char hdr_arg[256];
const char **av;
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;
*hdr_arg = 0;
if (!args.keep_pack && unpack_limit) {
Expand All @@ -520,8 +517,8 @@ static int get_pack(int xd[2], char **pack_lockfile)
}

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

pid = fork();
if (pid < 0)
cmd.in = fd[0];
cmd.git_cmd = 1;
if (start_command(&cmd))
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]);
execv_git_cmd(argv);
die("%s exec failed", argv[0]);
}
close(fd[0]);
close(fd[1]);
if (do_keep && pack_lockfile) {
close(keep_pipe[1]);
*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;
}
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);
if (do_keep && pack_lockfile)
*pack_lockfile = index_pack_lockfile(cmd.out);

if (finish_command(&cmd))
die("%s failed", argv[0]);
if (use_sideband && finish_async(&demux))
die("error in sideband demultiplexer");
return 0;
}

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

Expand All @@ -774,16 +747,14 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args,
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);
if (pid < 0)
return NULL;
if (heads && nr_heads)
nr_heads = remove_duplicates(nr_heads, heads);
ref = do_fetch_pack(fd, nr_heads, heads, pack_lockfile);
close(fd[0]);
close(fd[1]);
ret = finish_connect(pid);
ret = finish_connect(conn);

if (!ret && nr_heads) {
/* If the heads to pull were given, we should have
Expand Down
4 changes: 2 additions & 2 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -503,8 +503,8 @@ struct ref {
#define REF_TAGS (1u << 2)

#define CONNECT_VERBOSE (1u << 0)
extern pid_t git_connect(int fd[2], char *url, const char *prog, int flags);
extern int finish_connect(pid_t pid);
extern struct child_process *git_connect(int fd[2], char *url, const char *prog, int flags);
extern int finish_connect(struct child_process *conn);
extern int path_match(const char *path, int nr, char **match);
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);
Expand Down
128 changes: 62 additions & 66 deletions connect.c
Original file line number Diff line number Diff line change
Expand Up @@ -468,24 +468,26 @@ char *get_port(char *host)
}

/*
* This returns 0 if the transport protocol does not need fork(2),
* or a process id if it does. Once done, finish the connection
* This returns NULL if the transport protocol does not need fork(2), or a
* struct child_process object if it does. Once done, finish the connection
* 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).
*
* 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 *end;
int c;
int pipefd[2][2];
pid_t pid;
struct child_process *conn;
enum protocol protocol = PROTO_LOCAL;
int free_path = 0;
char *port = NULL;
const char **arg;
struct strbuf cmd;

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

if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0)
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_addstr(&cmd, prog);
strbuf_addch(&cmd, ' ');
sq_quote_buf(&cmd, path);
if (cmd.len >= MAX_CMD_LEN)
die("command line too long");

dup2(pipefd[1][0], 0);
dup2(pipefd[0][1], 1);
close(pipefd[0][0]);
close(pipefd[0][1]);
close(pipefd[1][0]);
close(pipefd[1][1]);
if (protocol == PROTO_SSH) {
const char *ssh, *ssh_basename;
ssh = getenv("GIT_SSH");
if (!ssh) ssh = "ssh";
ssh_basename = strrchr(ssh, '/');
if (!ssh_basename)
ssh_basename = ssh;
else
ssh_basename++;

if (!port)
execlp(ssh, ssh_basename, host, cmd.buf, NULL);
else
execlp(ssh, ssh_basename, "-p", port, host,
cmd.buf, NULL);
}
else {
unsetenv(ALTERNATE_DB_ENVIRONMENT);
unsetenv(DB_ENVIRONMENT);
unsetenv(GIT_DIR_ENVIRONMENT);
unsetenv(GIT_WORK_TREE_ENVIRONMENT);
unsetenv(GRAFT_ENVIRONMENT);
unsetenv(INDEX_ENVIRONMENT);
execlp("sh", "sh", "-c", cmd.buf, NULL);
conn = xcalloc(1, sizeof(*conn));

strbuf_init(&cmd, MAX_CMD_LEN);
strbuf_addstr(&cmd, prog);
strbuf_addch(&cmd, ' ');
sq_quote_buf(&cmd, path);
if (cmd.len >= MAX_CMD_LEN)
die("command line too long");

conn->in = conn->out = -1;
conn->argv = arg = xcalloc(6, sizeof(*arg));
if (protocol == PROTO_SSH) {
const char *ssh = getenv("GIT_SSH");
if (!ssh) ssh = "ssh";

*arg++ = ssh;
if (port) {
*arg++ = "-p";
*arg++ = port;
}
die("exec failed");
*arg++ = host;
}
fd[0] = pipefd[0][0];
fd[1] = pipefd[1][1];
close(pipefd[0][1]);
close(pipefd[1][0]);
else {
/* remove these from the environment */
const char *env[] = {
ALTERNATE_DB_ENVIRONMENT,
DB_ENVIRONMENT,
GIT_DIR_ENVIRONMENT,
GIT_WORK_TREE_ENVIRONMENT,
GRAFT_ENVIRONMENT,
INDEX_ENVIRONMENT,
NULL
};
conn->env = env;
*arg++ = "sh";
*arg++ = "-c";
}
*arg++ = cmd.buf;
*arg = NULL;

if (start_command(conn))
die("unable to fork");

fd[0] = conn->out; /* read from child's stdout */
fd[1] = conn->in; /* write to child's stdin */
strbuf_release(&cmd);
if (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;

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

0 comments on commit 4340a81

Please sign in to comment.