From 98158e9cfd2808613f305bf587ce697762c884bb Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 19 Oct 2007 21:47:53 +0200 Subject: Change git_connect() to return a struct child_process instead of a pid_t. This prepares the API of git_connect() and finish_connect() to operate on a struct child_process. Currently, we just use that object as a placeholder for the pid that we used to return. A follow-up patch will change the implementation of git_connect() and finish_connect() to make full use of the object. Old code had early-return-on-error checks at the calling sites of git_connect(), but since git_connect() dies on errors anyway, these checks were removed. [sp: Corrected style nit of "conn == NULL" to "!conn"] Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- builtin-archive.c | 8 +++----- builtin-fetch-pack.c | 8 +++----- cache.h | 4 ++-- connect.c | 31 +++++++++++++++++-------------- peek-remote.c | 8 +++----- send-pack.c | 8 +++----- transport.c | 9 ++------- 7 files changed, 33 insertions(+), 43 deletions(-) diff --git a/builtin-archive.c b/builtin-archive.c index 04385dea0..76f8d3d83 100644 --- a/builtin-archive.c +++ b/builtin-archive.c @@ -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; @@ -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) @@ -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; } diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 8f25d509a..f56592fea 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -762,7 +762,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; @@ -773,16 +773,14 @@ struct ref *fetch_pack(struct fetch_pack_args *my_args, st.st_mtime = 0; } - pid = git_connect(fd, (char *)dest, uploadpack, + conn = git_connect(fd, (char *)dest, 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 diff --git a/cache.h b/cache.h index 27485d36c..bfffa05df 100644 --- a/cache.h +++ b/cache.h @@ -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); diff --git a/connect.c b/connect.c index 3d5c4ab75..a6d7b1375 100644 --- a/connect.c +++ b/connect.c @@ -468,21 +468,22 @@ 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; @@ -568,15 +569,16 @@ 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) + conn = xcalloc(1, sizeof(*conn)); + conn->pid = fork(); + if (conn->pid < 0) die("unable to fork"); - if (!pid) { + if (!conn->pid) { struct strbuf cmd; strbuf_init(&cmd, MAX_CMD_LEN); @@ -625,17 +627,18 @@ pid_t git_connect(int fd[2], char *url, const char *prog, int flags) close(pipefd[1][0]); 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) + if (!conn) return 0; - while (waitpid(pid, NULL, 0) < 0) { + while (waitpid(conn->pid, NULL, 0) < 0) { if (errno != EINTR) return -1; } + free(conn); return 0; } diff --git a/peek-remote.c b/peek-remote.c index ceb787170..8d20f7c9c 100644 --- a/peek-remote.c +++ b/peek-remote.c @@ -25,7 +25,7 @@ int main(int argc, char **argv) int i, ret; char *dest = NULL; int fd[2]; - pid_t pid; + struct child_process *conn; int nongit = 0; unsigned flags = 0; @@ -64,12 +64,10 @@ int main(int argc, char **argv) if (!dest || i != argc - 1) usage(peek_remote_usage); - pid = git_connect(fd, dest, uploadpack, 0); - if (pid < 0) - return 1; + conn = git_connect(fd, dest, uploadpack, 0); ret = peek_remote(fd, flags); close(fd[0]); close(fd[1]); - ret |= finish_connect(pid); + ret |= finish_connect(conn); return !!ret; } diff --git a/send-pack.c b/send-pack.c index 7b776243e..147f44b01 100644 --- a/send-pack.c +++ b/send-pack.c @@ -365,7 +365,7 @@ int main(int argc, char **argv) char *dest = NULL; char **heads = NULL; int fd[2], ret; - pid_t pid; + struct child_process *conn; char *remote_name = NULL; struct remote *remote = NULL; @@ -433,12 +433,10 @@ int main(int argc, char **argv) } } - pid = git_connect(fd, dest, receivepack, verbose ? CONNECT_VERBOSE : 0); - if (pid < 0) - return 1; + conn = git_connect(fd, dest, receivepack, verbose ? CONNECT_VERBOSE : 0); ret = send_pack(fd[0], fd[1], remote, nr_heads, heads); close(fd[0]); close(fd[1]); - ret |= finish_connect(pid); + ret |= finish_connect(conn); return !!ret; } diff --git a/transport.c b/transport.c index 400af71c7..5132d289d 100644 --- a/transport.c +++ b/transport.c @@ -601,18 +601,13 @@ static struct ref *get_refs_via_connect(const struct transport *transport) struct git_transport_data *data = transport->data; struct ref *refs; int fd[2]; - pid_t pid; char *dest = xstrdup(transport->url); - - pid = git_connect(fd, dest, data->uploadpack, 0); - - if (pid < 0) - die("Failed to connect to \"%s\"", transport->url); + struct child_process *conn = git_connect(fd, dest, data->uploadpack, 0); get_remote_heads(fd[0], &refs, 0, NULL, 0); packet_flush(fd[1]); - finish_connect(pid); + finish_connect(conn); free(dest); -- cgit v1.2.1 From f364cb88238bbb468b0e076c53a055bcb8603b60 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 19 Oct 2007 21:47:54 +0200 Subject: Use start_command() in git_connect() instead of explicit fork/exec. The child process handling is delegated to start_command() and finish_command(). Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- connect.c | 103 +++++++++++++++++++++++++++++--------------------------------- 1 file changed, 48 insertions(+), 55 deletions(-) diff --git a/connect.c b/connect.c index a6d7b1375..44e423daf 100644 --- a/connect.c +++ b/connect.c @@ -482,11 +482,12 @@ struct child_process *git_connect(int fd[2], char *url, char *host, *path = url; char *end; int c; - int pipefd[2][2]; 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. @@ -572,59 +573,52 @@ struct child_process *git_connect(int fd[2], char *url, return NULL; } - if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0) - die("unable to create pipe pair for communication"); conn = xcalloc(1, sizeof(*conn)); - conn->pid = fork(); - if (conn->pid < 0) - die("unable to fork"); - if (!conn->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); + 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; } - 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); - } - die("exec failed"); + *arg++ = host; + } + 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"; } - fd[0] = pipefd[0][0]; - fd[1] = pipefd[1][1]; - close(pipefd[0][1]); - close(pipefd[1][0]); + *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 conn; @@ -632,13 +626,12 @@ struct child_process *git_connect(int fd[2], char *url, int finish_connect(struct child_process *conn) { + int code; if (!conn) return 0; - while (waitpid(conn->pid, NULL, 0) < 0) { - if (errno != EINTR) - return -1; - } + code = finish_command(conn); + free(conn->argv); free(conn); - return 0; + return code; } -- cgit v1.2.1 From dc1bfdcd1a8af81885f1831c5e6dcfe5cf61372e Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 19 Oct 2007 21:47:55 +0200 Subject: Use start_command() to run content filters instead of explicit fork/exec. The previous code already used finish_command() to wait for the process to terminate, but did not use start_command() to run it. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- convert.c | 30 +++++++----------------------- 1 file changed, 7 insertions(+), 23 deletions(-) diff --git a/convert.c b/convert.c index aa95834eb..d83c2fcc5 100644 --- a/convert.c +++ b/convert.c @@ -199,34 +199,18 @@ static int filter_buffer(const char *path, const char *src, * Spawn cmd and feed the buffer contents through its stdin. */ struct child_process child_process; - int pipe_feed[2]; int write_err, status; + const char *argv[] = { "sh", "-c", cmd, NULL }; memset(&child_process, 0, sizeof(child_process)); + child_process.argv = argv; + child_process.in = -1; - if (pipe(pipe_feed) < 0) { - error("cannot create pipe to run external filter %s", cmd); - return 1; - } - - child_process.pid = fork(); - if (child_process.pid < 0) { - 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]); + if (start_command(&child_process)) + return error("cannot fork to run external filter %s", cmd); - write_err = (write_in_full(pipe_feed[1], src, size) < 0); - if (close(pipe_feed[1])) + write_err = (write_in_full(child_process.in, src, size) < 0); + if (close(child_process.in)) write_err = 1; if (write_err) error("cannot feed the input to external filter %s", cmd); -- cgit v1.2.1 From d5535ec75ce1cdf57ef52b57c58c548121ce99ba Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 19 Oct 2007 21:47:56 +0200 Subject: Use run_command() to spawn external diff programs instead of fork/exec. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- diff.c | 38 +++----------------------------------- 1 file changed, 3 insertions(+), 35 deletions(-) diff --git a/diff.c b/diff.c index 6648e0152..30b754417 100644 --- a/diff.c +++ b/diff.c @@ -9,6 +9,7 @@ #include "xdiff-interface.h" #include "color.h" #include "attr.h" +#include "run-command.h" #ifdef NO_FAST_WORKING_DIRECTORY #define FAST_WORKING_DIRECTORY 0 @@ -1748,40 +1749,6 @@ static void remove_tempfile_on_signal(int 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: * * diff-cmd name infile1 infile1-sha1 infile1-mode \ @@ -1834,7 +1801,8 @@ static void run_external_diff(const char *pgm, *arg++ = name; } *arg = NULL; - retval = spawn_prog(pgm, spawn_arg); + fflush(NULL); + retval = run_command_v_opt(spawn_arg, 0); remove_tempfile(); if (retval) { fprintf(stderr, "external diff died, stopping at %s.\n", name); -- cgit v1.2.1 From 477822c35dbf3d5f16fce1425638e761e60ede3d Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 19 Oct 2007 21:47:57 +0200 Subject: Use start_comand() in builtin-fetch-pack.c instead of explicit fork/exec. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- builtin-fetch-pack.c | 56 +++++++++++++++------------------------------------- 1 file changed, 16 insertions(+), 40 deletions(-) diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index f56592fea..871b7042e 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -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; @@ -491,18 +492,19 @@ static pid_t setup_sideband(int fd[2], int xd[2]) static int get_pack(int xd[2], char **pack_lockfile) { - int status; - pid_t pid, side_pid; + pid_t side_pid; 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); + memset(&cmd, 0, sizeof(cmd)); + cmd.argv = argv; av = argv; *hdr_arg = 0; if (!args.keep_pack && unpack_limit) { @@ -519,8 +521,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) @@ -544,43 +546,17 @@ 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]); + return 0; } static struct ref *do_fetch_pack(int fd[2], -- cgit v1.2.1 From f3b33f1d220056243d3c1da154d3859410630189 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 19 Oct 2007 21:47:58 +0200 Subject: Have start_command() create a pipe to read the stderr of the child. This adds another stanza that allocates a pipe that is connected to the child's stderr and that the caller can read from. In order to request this pipe, the caller sets cmd->err to -1. The implementation is not exactly modeled after the stdout case: For stdout the caller can supply an existing file descriptor, but this facility is nowhere needed in the stderr case. Additionally, the caller is required to close cmd->err. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- run-command.c | 26 ++++++++++++++++++++++++-- run-command.h | 1 + 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/run-command.c b/run-command.c index 7e779d33e..d00c03bbd 100644 --- a/run-command.c +++ b/run-command.c @@ -17,8 +17,8 @@ static inline void dup_devnull(int to) int start_command(struct child_process *cmd) { - int need_in, need_out; - int fdin[2], fdout[2]; + int need_in, need_out, need_err; + int fdin[2], fdout[2], fderr[2]; need_in = !cmd->no_stdin && cmd->in < 0; if (need_in) { @@ -41,12 +41,26 @@ int start_command(struct child_process *cmd) 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(); if (cmd->pid < 0) { if (need_in) close_pair(fdin); if (need_out) close_pair(fdout); + if (need_err) + close_pair(fderr); return -ERR_RUN_COMMAND_FORK; } @@ -73,6 +87,11 @@ int start_command(struct child_process *cmd) close(cmd->out); } + if (need_err) { + dup2(fderr[1], 2); + close_pair(fderr); + } + if (cmd->dir && chdir(cmd->dir)) die("exec %s: cd to %s failed (%s)", cmd->argv[0], cmd->dir, strerror(errno)); @@ -102,6 +121,9 @@ int start_command(struct child_process *cmd) else if (cmd->out > 1) close(cmd->out); + if (need_err) + close(fderr[1]); + return 0; } diff --git a/run-command.h b/run-command.h index 7958eb1e0..35b9fb61f 100644 --- a/run-command.h +++ b/run-command.h @@ -16,6 +16,7 @@ struct child_process { pid_t pid; int in; int out; + int err; const char *dir; const char *const *env; unsigned close_in:1; -- cgit v1.2.1 From cc41fa8da9b9e9d23221d3be47a80409a89732d4 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 19 Oct 2007 21:47:59 +0200 Subject: upload-pack: Use start_command() to run pack-objects in create_pack_file(). This gets rid of an explicit fork/exec. Since upload-pack has to coordinate two processes (rev-list and pack-objects), we cannot use the normal finish_command(), but have to monitor the processes explicitly. Hence, the waitpid() call remains. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- upload-pack.c | 105 ++++++++++++++++++++++++---------------------------------- 1 file changed, 44 insertions(+), 61 deletions(-) diff --git a/upload-pack.c b/upload-pack.c index fe96ef15c..5c0c0cc8e 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -9,6 +9,7 @@ #include "diff.h" #include "revision.h" #include "list-objects.h" +#include "run-command.h" static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] "; @@ -98,16 +99,18 @@ static void show_edge(struct commit *commit) static void create_pack_file(void) { - /* Pipes between rev-list to pack-objects, pack-objects to us - * and pack-objects error stream for progress bar. + /* Pipe from rev-list to pack-objects */ - int lp_pipe[2], pu_pipe[2], pe_pipe[2]; - pid_t pid_rev_list, pid_pack_objects; + int lp_pipe[2]; + pid_t pid_rev_list; + struct child_process 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; + const char *argv[10]; + int arg = 0; if (pipe(lp_pipe) < 0) die("git-upload-pack: unable to create pipe"); @@ -154,52 +157,32 @@ static void create_pack_file(void) exit(0); } - if (pipe(pu_pipe) < 0) - die("git-upload-pack: unable to create pipe"); - if (pipe(pe_pipe) < 0) - die("git-upload-pack: unable to create pipe"); - pid_pack_objects = fork(); - if (pid_pack_objects < 0) { + /* writable pipe end must not be inherited by pack_objects */ + close(lp_pipe[1]); + + 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 = lp_pipe[0]; /* 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 */ kill(pid_rev_list, SIGKILL); 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); - dup2(pu_pipe[1], 1); - 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. + /* We read from pack_objects.err to capture stderr output for + * progress bar, and pack_objects.out to capture the pack data. */ - close(pe_pipe[1]); - close(pu_pipe[1]); while (1) { const char *who; @@ -214,14 +197,14 @@ static void create_pack_file(void) pollsize = 0; pe = pu = -1; - if (0 <= pu_pipe[0]) { - pfd[pollsize].fd = pu_pipe[0]; + if (0 <= pack_objects.out) { + pfd[pollsize].fd = pack_objects.out; pfd[pollsize].events = POLLIN; pu = pollsize; pollsize++; } - if (0 <= pe_pipe[0]) { - pfd[pollsize].fd = pe_pipe[0]; + if (0 <= pack_objects.err) { + pfd[pollsize].fd = pack_objects.err; pfd[pollsize].events = POLLIN; pe = pollsize; pollsize++; @@ -254,13 +237,13 @@ static void create_pack_file(void) *cp++ = buffered; outsz++; } - sz = xread(pu_pipe[0], cp, + sz = xread(pack_objects.out, cp, sizeof(data) - outsz); if (0 < sz) ; else if (sz == 0) { - close(pu_pipe[0]); - pu_pipe[0] = -1; + close(pack_objects.out); + pack_objects.out = -1; } else goto fail; @@ -279,13 +262,13 @@ static void create_pack_file(void) /* Status ready; we ship that in the side-band * or dump to the standard error. */ - sz = xread(pe_pipe[0], progress, + sz = xread(pack_objects.err, progress, sizeof(progress)); if (0 < sz) send_client_data(2, progress, sz); else if (sz == 0) { - close(pe_pipe[0]); - pe_pipe[0] = -1; + close(pack_objects.err); + pack_objects.err = -1; } else goto fail; @@ -293,12 +276,12 @@ static void create_pack_file(void) } /* See if the children are still there */ - if (pid_rev_list || pid_pack_objects) { + if (pid_rev_list || pack_objects.pid) { pid = waitpid(-1, &status, WNOHANG); if (!pid) continue; who = ((pid == pid_rev_list) ? "git-rev-list" : - (pid == pid_pack_objects) ? "git-pack-objects" : + (pid == pack_objects.pid) ? "git-pack-objects" : NULL); if (!who) { if (pid < 0) { @@ -317,9 +300,9 @@ static void create_pack_file(void) } if (pid == pid_rev_list) pid_rev_list = 0; - if (pid == pid_pack_objects) - pid_pack_objects = 0; - if (pid_rev_list || pid_pack_objects) + if (pid == pack_objects.pid) + pack_objects.pid = 0; + if (pid_rev_list || pack_objects.pid) continue; } @@ -340,8 +323,8 @@ static void create_pack_file(void) return; } fail: - if (pid_pack_objects) - kill(pid_pack_objects, SIGKILL); + if (pack_objects.pid) + kill(pack_objects.pid, SIGKILL); if (pid_rev_list) kill(pid_rev_list, SIGKILL); send_client_data(3, abort_msg, sizeof(abort_msg)); -- cgit v1.2.1 From 2d22c208304156892fd6674e0055a3212c1e2d2e Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 19 Oct 2007 21:48:00 +0200 Subject: Add infrastructure to run a function asynchronously. This adds start_async() and finish_async(), which runs a function asynchronously. Communication with the caller happens only via pipes. For this reason, this implementation forks off a child process that runs the function. [sp: Style nit fixed by removing unnecessary block on if condition inside of start_async()] Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- run-command.c | 51 +++++++++++++++++++++++++++++++++++++++++++-------- run-command.h | 22 ++++++++++++++++++++++ 2 files changed, 65 insertions(+), 8 deletions(-) diff --git a/run-command.c b/run-command.c index d00c03bbd..d99a6c4ea 100644 --- a/run-command.c +++ b/run-command.c @@ -127,16 +127,11 @@ int start_command(struct child_process *cmd) 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 (;;) { int status, code; - pid_t waiting = waitpid(cmd->pid, &status, 0); + pid_t waiting = waitpid(pid, &status, 0); if (waiting < 0) { if (errno == EINTR) @@ -144,7 +139,7 @@ int finish_command(struct child_process *cmd) error("waitpid failed (%s)", strerror(errno)); return -ERR_RUN_COMMAND_WAITPID; } - if (waiting != cmd->pid) + if (waiting != pid) return -ERR_RUN_COMMAND_WAITPID_WRONG_PID; if (WIFSIGNALED(status)) return -ERR_RUN_COMMAND_WAITPID_SIGNAL; @@ -158,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 code = start_command(cmd); @@ -200,3 +204,34 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const cmd.env = env; 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; +} diff --git a/run-command.h b/run-command.h index 35b9fb61f..94e1e9d51 100644 --- a/run-command.h +++ b/run-command.h @@ -43,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); +/* + * 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 -- cgit v1.2.1 From 088fab5fc48ebb8b476c3b32dd25df3aa4236f94 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 19 Oct 2007 21:48:01 +0200 Subject: Use the asyncronous function infrastructure in builtin-fetch-pack.c. We run the sideband demultiplexer in an asynchronous function. Note that earlier there was a check in the child process that closed xd[1] only if it was different from xd[0]; this test is no longer needed because git_connect() always returns two different file descriptors (see ec587fde0a76780931c7ac32474c8c000aa45134). Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- builtin-fetch-pack.c | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/builtin-fetch-pack.c b/builtin-fetch-pack.c index 871b7042e..51d8a3279 100644 --- a/builtin-fetch-pack.c +++ b/builtin-fetch-pack.c @@ -457,42 +457,37 @@ 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) { - pid_t side_pid; + struct async demux; int fd[2]; const char *argv[20]; char keep_arg[256]; @@ -501,7 +496,7 @@ static int get_pack(int xd[2], char **pack_lockfile) int do_keep = args.keep_pack; struct child_process cmd; - side_pid = setup_sideband(fd, xd); + setup_sideband(fd, xd, &demux); memset(&cmd, 0, sizeof(cmd)); cmd.argv = argv; @@ -556,6 +551,8 @@ static int get_pack(int xd[2], char **pack_lockfile) if (finish_command(&cmd)) die("%s failed", argv[0]); + if (use_sideband && finish_async(&demux)) + die("error in sideband demultiplexer"); return 0; } -- cgit v1.2.1 From 80ccaa78a8b95ad3b4f6e24dc35a9aa3cae5fd86 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 19 Oct 2007 21:48:02 +0200 Subject: upload-pack: Move the revision walker into a separate function. This allows us later to use start_async() with this function, and at the same time is a nice cleanup that makes a long function (create_pack_file()) shorter. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- upload-pack.c | 70 +++++++++++++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/upload-pack.c b/upload-pack.c index 5c0c0cc8e..ccdc30653 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -97,6 +97,42 @@ static void show_edge(struct commit *commit) fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1)); } +static void do_rev_list(int create_full_pack) +{ + int i; + struct rev_info revs; + + if (create_full_pack) + use_thin_pack = 0; /* no point doing it */ + init_revisions(&revs, NULL); + revs.tag_objects = 1; + revs.tree_objects = 1; + revs.blob_objects = 1; + if (use_thin_pack) + revs.edge_hint = 1; + + if (create_full_pack) { + const char *args[] = {"rev-list", "--all", NULL}; + setup_revisions(2, args, &revs, NULL); + } else { + for (i = 0; i < want_obj.nr; i++) { + struct object *o = want_obj.objects[i].item; + /* why??? */ + o->flags &= ~UNINTERESTING; + add_pending_object(&revs, o, NULL); + } + for (i = 0; i < have_obj.nr; i++) { + struct object *o = have_obj.objects[i].item; + o->flags |= UNINTERESTING; + add_pending_object(&revs, o, NULL); + } + setup_revisions(0, NULL, &revs, NULL); + } + prepare_revision_walk(&revs); + mark_edges_uninteresting(revs.commits, &revs, show_edge); + traverse_commit_list(&revs, show_commit, show_object); +} + static void create_pack_file(void) { /* Pipe from rev-list to pack-objects @@ -119,41 +155,9 @@ static void create_pack_file(void) die("git-upload-pack: unable to fork git-rev-list"); if (!pid_rev_list) { - int i; - struct rev_info revs; - close(lp_pipe[0]); pack_pipe = fdopen(lp_pipe[1], "w"); - - if (create_full_pack) - use_thin_pack = 0; /* no point doing it */ - init_revisions(&revs, NULL); - revs.tag_objects = 1; - revs.tree_objects = 1; - revs.blob_objects = 1; - if (use_thin_pack) - revs.edge_hint = 1; - - if (create_full_pack) { - const char *args[] = {"rev-list", "--all", NULL}; - setup_revisions(2, args, &revs, NULL); - } else { - for (i = 0; i < want_obj.nr; i++) { - struct object *o = want_obj.objects[i].item; - /* why??? */ - o->flags &= ~UNINTERESTING; - add_pending_object(&revs, o, NULL); - } - for (i = 0; i < have_obj.nr; i++) { - struct object *o = have_obj.objects[i].item; - o->flags |= UNINTERESTING; - add_pending_object(&revs, o, NULL); - } - setup_revisions(0, NULL, &revs, NULL); - } - prepare_revision_walk(&revs); - mark_edges_uninteresting(revs.commits, &revs, show_edge); - traverse_commit_list(&revs, show_commit, show_object); + do_rev_list(create_full_pack); exit(0); } -- cgit v1.2.1 From 21edd3f197df80e9493233a3b9849b61764ebf46 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 19 Oct 2007 21:48:03 +0200 Subject: upload-pack: Run rev-list in an asynchronous function. This gets rid of an explicit fork(). Since upload-pack has to coordinate two processes (rev-list and pack-objects), we cannot use the normal finish_async(), but have to monitor the process explicitly. Hence, there are no changes at this front. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- upload-pack.c | 46 ++++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/upload-pack.c b/upload-pack.c index ccdc30653..67994680f 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -97,11 +97,12 @@ static void show_edge(struct commit *commit) fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1)); } -static void do_rev_list(int create_full_pack) +static int do_rev_list(int fd, void *create_full_pack) { int i; struct rev_info revs; + pack_pipe = fdopen(fd, "w"); if (create_full_pack) use_thin_pack = 0; /* no point doing it */ init_revisions(&revs, NULL); @@ -131,14 +132,12 @@ static void do_rev_list(int create_full_pack) prepare_revision_walk(&revs); mark_edges_uninteresting(revs.commits, &revs, show_edge); traverse_commit_list(&revs, show_commit, show_object); + return 0; } static void create_pack_file(void) { - /* Pipe from rev-list to pack-objects - */ - int lp_pipe[2]; - pid_t pid_rev_list; + struct async rev_list; struct child_process pack_objects; int create_full_pack = (nr_our_refs == want_obj.nr && !have_obj.nr); char data[8193], progress[128]; @@ -148,22 +147,12 @@ static void create_pack_file(void) const char *argv[10]; int arg = 0; - if (pipe(lp_pipe) < 0) - die("git-upload-pack: unable to create pipe"); - pid_rev_list = fork(); - if (pid_rev_list < 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"); - if (!pid_rev_list) { - close(lp_pipe[0]); - pack_pipe = fdopen(lp_pipe[1], "w"); - do_rev_list(create_full_pack); - exit(0); - } - - /* writable pipe end must not be inherited by pack_objects */ - close(lp_pipe[1]); - argv[arg++] = "pack-objects"; argv[arg++] = "--stdout"; if (!no_progress) @@ -173,14 +162,15 @@ static void create_pack_file(void) argv[arg++] = NULL; memset(&pack_objects, 0, sizeof(pack_objects)); - pack_objects.in = lp_pipe[0]; /* start_command closes it */ + 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 */ - kill(pid_rev_list, SIGKILL); + kill(rev_list.pid, SIGKILL); die("git-upload-pack: unable to fork git-pack-objects"); } @@ -280,11 +270,11 @@ static void create_pack_file(void) } /* See if the children are still there */ - if (pid_rev_list || pack_objects.pid) { + if (rev_list.pid || pack_objects.pid) { pid = waitpid(-1, &status, WNOHANG); if (!pid) continue; - who = ((pid == pid_rev_list) ? "git-rev-list" : + who = ((pid == rev_list.pid) ? "git-rev-list" : (pid == pack_objects.pid) ? "git-pack-objects" : NULL); if (!who) { @@ -302,11 +292,11 @@ static void create_pack_file(void) who); goto fail; } - if (pid == pid_rev_list) - pid_rev_list = 0; + if (pid == rev_list.pid) + rev_list.pid = 0; if (pid == pack_objects.pid) pack_objects.pid = 0; - if (pid_rev_list || pack_objects.pid) + if (rev_list.pid || pack_objects.pid) continue; } @@ -329,8 +319,8 @@ static void create_pack_file(void) fail: if (pack_objects.pid) kill(pack_objects.pid, SIGKILL); - if (pid_rev_list) - kill(pid_rev_list, SIGKILL); + if (rev_list.pid) + kill(rev_list.pid, SIGKILL); send_client_data(3, abort_msg, sizeof(abort_msg)); die("git-upload-pack: %s", abort_msg); } -- cgit v1.2.1 From a0ae35ae2d92cc706f902227cb0f34e2a2f0fd50 Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 19 Oct 2007 21:48:04 +0200 Subject: t0021-conversion.sh: Test that the clean filter really cleans content. This test uses a rot13 filter, which is its own inverse. It tested only that the content was the same as the original after both the 'clean' and the 'smudge' filter were applied. This way it would not detect whether any filter was run at all. Hence, here we add another test that checks that the repository contained content that was processed by the 'clean' filter. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- t/t0021-conversion.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh index a839f4e07..cb860296e 100755 --- a/t/t0021-conversion.sh +++ b/t/t0021-conversion.sh @@ -42,7 +42,12 @@ test_expect_success check ' git diff --raw --exit-code :test :test.i && id=$(git rev-parse --verify :test) && 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 -- cgit v1.2.1 From 7683b6e81fa0f1f55d4974d69fb87c7c7b6b394e Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 19 Oct 2007 21:48:05 +0200 Subject: Avoid a dup2(2) in apply_filter() - start_command() can do it for us. When apply_filter() runs the external (clean or smudge) filter program, it needs to pass the writable end of a pipe as its stdout. For this purpose, it used to dup2(2) the file descriptor explicitly to stdout. Now we use the facilities of start_command() to do it for us. Furthermore, the path argument of a subordinate function, filter_buffer(), was not used, so here we replace it to pass the fd instead. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- convert.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/convert.c b/convert.c index d83c2fcc5..8dc996508 100644 --- a/convert.c +++ b/convert.c @@ -192,7 +192,7 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len, return 1; } -static int filter_buffer(const char *path, const char *src, +static int filter_buffer(int fd, const char *src, unsigned long size, const char *cmd) { /* @@ -205,6 +205,7 @@ static int filter_buffer(const char *path, const char *src, memset(&child_process, 0, sizeof(child_process)); child_process.argv = argv; child_process.in = -1; + child_process.out = fd; if (start_command(&child_process)) return error("cannot fork to run external filter %s", cmd); @@ -254,10 +255,8 @@ static int apply_filter(const char *path, const char *src, size_t len, 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)); + exit(filter_buffer(pipe_feed[1], src, len, cmd)); } close(pipe_feed[1]); -- cgit v1.2.1 From 546bb5823249678bc6ad11e65661d896ed83448a Mon Sep 17 00:00:00 2001 From: Johannes Sixt Date: Fri, 19 Oct 2007 21:48:06 +0200 Subject: Use the asyncronous function infrastructure to run the content filter. Signed-off-by: Johannes Sixt Signed-off-by: Shawn O. Pearce --- convert.c | 61 ++++++++++++++++++++++++++++--------------------------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/convert.c b/convert.c index 8dc996508..4df75595b 100644 --- a/convert.c +++ b/convert.c @@ -192,15 +192,21 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len, return 1; } -static int filter_buffer(int fd, const char *src, - unsigned long size, const char *cmd) +struct filter_params { + 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. */ struct child_process child_process; + struct filter_params *params = (struct filter_params *)data; int write_err, status; - const char *argv[] = { "sh", "-c", cmd, NULL }; + const char *argv[] = { "sh", "-c", params->cmd, NULL }; memset(&child_process, 0, sizeof(child_process)); child_process.argv = argv; @@ -208,17 +214,17 @@ static int filter_buffer(int fd, const char *src, child_process.out = fd; if (start_command(&child_process)) - return error("cannot fork to run external filter %s", cmd); + return error("cannot fork to run external filter %s", params->cmd); - write_err = (write_in_full(child_process.in, src, size) < 0); + write_err = (write_in_full(child_process.in, params->src, params->size) < 0); if (close(child_process.in)) write_err = 1; 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); if (status) - error("external filter %s failed %d", cmd, -status); + error("external filter %s failed %d", params->cmd, -status); return (write_err || status); } @@ -231,47 +237,36 @@ static int apply_filter(const char *path, const char *src, size_t len, * * (child --> cmd) --> us */ - int pipe_feed[2]; - int status, ret = 1; - struct child_process child_process; + int ret = 1; struct strbuf nbuf; + struct async async; + struct filter_params params; if (!cmd) return 0; - memset(&child_process, 0, sizeof(child_process)); - - if (pipe(pipe_feed) < 0) { - error("cannot create pipe to run external filter %s", cmd); - return 0; - } + memset(&async, 0, sizeof(async)); + async.proc = filter_buffer; + async.data = ¶ms; + params.src = src; + params.size = len; + params.cmd = cmd; fflush(NULL); - child_process.pid = fork(); - if (child_process.pid < 0) { - error("cannot fork to run external filter %s", cmd); - close(pipe_feed[0]); - close(pipe_feed[1]); - return 0; - } - if (!child_process.pid) { - close(pipe_feed[0]); - exit(filter_buffer(pipe_feed[1], src, len, cmd)); - } - close(pipe_feed[1]); + if (start_async(&async)) + return 0; /* error was already reported */ 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); ret = 0; } - if (close(pipe_feed[0])) { + if (close(async.out)) { error("read from external filter %s failed", cmd); ret = 0; } - status = finish_command(&child_process); - if (status) { - error("external filter %s failed %d", cmd, -status); + if (finish_async(&async)) { + error("external filter %s failed", cmd); ret = 0; } -- cgit v1.2.1