Git.pm: Handle failed commands' output
[gitweb.git] / upload-pack.c
index a9a8f2ed121a3327714364792d4bf8732addc33d..b18eb9ba0dbe8b5020aaaa698d98a4d1b76aa49c 100644 (file)
@@ -1,3 +1,6 @@
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/poll.h>
 #include "cache.h"
 #include "refs.h"
 #include "pkt-line.h"
@@ -5,9 +8,6 @@
 #include "object.h"
 #include "commit.h"
 #include "exec_cmd.h"
-#include <signal.h>
-#include <sys/poll.h>
-#include <sys/wait.h>
 
 static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
 
@@ -21,6 +21,7 @@ static int use_thin_pack = 0;
 static unsigned char has_sha1[MAX_HAS][20];
 static unsigned char needs_sha1[MAX_NEEDS][20];
 static unsigned int timeout = 0;
+static int use_sideband = 0;
 
 static void reset_timeout(void)
 {
@@ -34,13 +35,54 @@ static int strip(char *line, int len)
        return len;
 }
 
+#define PACKET_MAX 1000
+static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
+{
+       ssize_t ssz;
+       const char *p;
+
+       if (!data) {
+               if (!use_sideband)
+                       return 0;
+               packet_flush(1);
+       }
+
+       if (!use_sideband) {
+               if (fd == 3)
+                       /* emergency quit */
+                       fd = 2;
+               return safe_write(fd, data, sz);
+       }
+       p = data;
+       ssz = sz;
+       while (sz) {
+               unsigned n;
+               char hdr[5];
+
+               n = sz;
+               if (PACKET_MAX - 5 < n)
+                       n = PACKET_MAX - 5;
+               sprintf(hdr, "%04x", n + 5);
+               hdr[4] = fd;
+               safe_write(1, hdr, 5);
+               safe_write(1, p, n);
+               p += n;
+               sz -= n;
+       }
+       return ssz;
+}
+
 static void create_pack_file(void)
 {
-       /* Pipes between rev-list to pack-objects and pack-objects to us. */
-       int lp_pipe[2], pu_pipe[2];
+       /* 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 == nr_needs && !nr_has);
-       char data[8193];
+       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)
@@ -53,8 +95,8 @@ static void create_pack_file(void)
                int i;
                int args;
                const char **argv;
+               const char **p;
                char *buf;
-               char **p;
 
                if (create_full_pack) {
                        args = 10;
@@ -95,6 +137,8 @@ static void create_pack_file(void)
 
        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) {
                /* daemon sets things up to ignore TERM */
@@ -104,12 +148,15 @@ static void create_pack_file(void)
        if (!pid_pack_objects) {
                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]);
-               execl_git_cmd("pack-objects", "--stdout", NULL);
+               close(pe_pipe[0]);
+               close(pe_pipe[1]);
+               execl_git_cmd("pack-objects", "--stdout", "--progress", NULL);
                kill(pid_rev_list, SIGKILL);
                die("git-upload-pack: unable to exec git-pack-objects");
        }
@@ -117,8 +164,10 @@ static void create_pack_file(void)
        close(lp_pipe[0]);
        close(lp_pipe[1]);
 
-       /* We read from pu_pipe[0] to capture the pack data.
+       /* 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) {
@@ -127,10 +176,10 @@ static void create_pack_file(void)
                pid_t pid;
                int status;
                ssize_t sz;
-               int pu, pollsize;
+               int pe, pu, pollsize;
 
                pollsize = 0;
-               pu = -1;
+               pe = pu = -1;
 
                if (0 <= pu_pipe[0]) {
                        pfd[pollsize].fd = pu_pipe[0];
@@ -138,6 +187,12 @@ static void create_pack_file(void)
                        pu = pollsize;
                        pollsize++;
                }
+               if (0 <= pe_pipe[0]) {
+                       pfd[pollsize].fd = pe_pipe[0];
+                       pfd[pollsize].events = POLLIN;
+                       pe = pollsize;
+                       pollsize++;
+               }
 
                if (pollsize) {
                        if (poll(pfd, pollsize, -1) < 0) {
@@ -183,10 +238,25 @@ static void create_pack_file(void)
                                }
                                else
                                        buffered = -1;
-                               sz = xwrite(1, data, sz);
+                               sz = send_client_data(1, data, sz);
                                if (sz < 0)
                                        goto fail;
                        }
+                       if (0 <= pe && (pfd[pe].revents & (POLLIN|POLLHUP))) {
+                               /* Status ready; we ship that in the side-band
+                                * or dump to the standard error.
+                                */
+                               sz = read(pe_pipe[0], progress,
+                                         sizeof(progress));
+                               if (0 < sz)
+                                       send_client_data(2, progress, sz);
+                               else if (sz == 0) {
+                                       close(pe_pipe[0]);
+                                       pe_pipe[0] = -1;
+                               }
+                               else
+                                       goto fail;
+                       }
                }
 
                /* See if the children are still there */
@@ -227,11 +297,12 @@ static void create_pack_file(void)
                /* flush the data */
                if (0 <= buffered) {
                        data[0] = buffered;
-                       sz = xwrite(1, data, 1);
+                       sz = send_client_data(1, data, 1);
                        if (sz < 0)
                                goto fail;
                        fprintf(stderr, "flushed.\n");
                }
+               send_client_data(1, NULL, 0);
                return;
        }
  fail:
@@ -239,7 +310,8 @@ static void create_pack_file(void)
                kill(pid_pack_objects, SIGKILL);
        if (pid_rev_list)
                kill(pid_rev_list, SIGKILL);
-       die("git-upload-pack: aborting due to possible repository corruption on the remote side.");
+       send_client_data(3, abort_msg, sizeof(abort_msg));
+       die("git-upload-pack: %s", abort_msg);
 }
 
 static int got_sha1(char *hex, unsigned char *sha1)
@@ -346,6 +418,8 @@ static int receive_needs(void)
                        multi_ack = 1;
                if (strstr(line+45, "thin-pack"))
                        use_thin_pack = 1;
+               if (strstr(line+45, "side-band"))
+                       use_sideband = 1;
 
                /* We have sent all our refs already, and the other end
                 * should have chosen out of them; otherwise they are
@@ -367,7 +441,7 @@ static int receive_needs(void)
 
 static int send_ref(const char *refname, const unsigned char *sha1)
 {
-       static char *capabilities = "multi_ack thin-pack";
+       static const char *capabilities = "multi_ack thin-pack side-band";
        struct object *o = parse_object(sha1);
 
        if (!o)