3c6abaede73868fdd6d154fe29d0dc701bc0b63a
   1#include "cache.h"
   2#include "pkt-line.h"
   3#include <sys/wait.h>
   4
   5static const char send_pack_usage[] = "git-send-pack [--exec=other] destination [heads]*";
   6static const char *exec = "git-receive-pack";
   7
   8static int path_match(const char *path, int nr, char **match)
   9{
  10        int i;
  11        int pathlen = strlen(path);
  12
  13        for (i = 0; i < nr; i++) {
  14                char *s = match[i];
  15                int len = strlen(s);
  16
  17                if (!len || len > pathlen)
  18                        continue;
  19                if (memcmp(path + pathlen - len, s, len))
  20                        continue;
  21                if (pathlen > len && path[pathlen - len - 1] != '/')
  22                        continue;
  23                *s = 0;
  24                return 1;
  25        }
  26        return 0;
  27}
  28
  29struct ref {
  30        struct ref *next;
  31        unsigned char old_sha1[20];
  32        unsigned char new_sha1[20];
  33        char name[0];
  34};
  35
  36static void exec_pack_objects(void)
  37{
  38        static char *args[] = {
  39                "git-pack-objects",
  40                "--stdout",
  41                NULL
  42        };
  43        execvp("git-pack-objects", args);
  44        die("git-pack-objects exec failed (%s)", strerror(errno));
  45}
  46
  47static void exec_rev_list(struct ref *refs)
  48{
  49        static char *args[1000];
  50        int i = 0;
  51
  52        args[i++] = "git-rev-list";     /* 0 */
  53        args[i++] = "--objects";        /* 1 */
  54        while (refs) {
  55                char *buf = malloc(100);
  56                if (i > 900)
  57                        die("git-rev-list environment overflow");
  58                args[i++] = buf;
  59                snprintf(buf, 50, "^%s", sha1_to_hex(refs->old_sha1));
  60                buf += 50;
  61                args[i++] = buf;
  62                snprintf(buf, 50, "%s", sha1_to_hex(refs->new_sha1));
  63                refs = refs->next;
  64        }
  65        args[i] = NULL;
  66        execvp("git-rev-list", args);
  67        die("git-rev-list exec failed (%s)", strerror(errno));
  68}
  69
  70static void rev_list(int fd, struct ref *refs)
  71{
  72        int pipe_fd[2];
  73        pid_t pack_objects_pid;
  74
  75        if (pipe(pipe_fd) < 0)
  76                die("rev-list setup: pipe failed");
  77        pack_objects_pid = fork();
  78        if (!pack_objects_pid) {
  79                dup2(pipe_fd[0], 0);
  80                dup2(fd, 1);
  81                close(pipe_fd[0]);
  82                close(pipe_fd[1]);
  83                close(fd);
  84                exec_pack_objects();
  85                die("pack-objects setup failed");
  86        }
  87        if (pack_objects_pid < 0)
  88                die("pack-objects fork failed");
  89        dup2(pipe_fd[1], 1);
  90        close(pipe_fd[0]);
  91        close(pipe_fd[1]);
  92        close(fd);
  93        exec_rev_list(refs);
  94}
  95
  96static int pack_objects(int fd, struct ref *refs)
  97{
  98        pid_t rev_list_pid;
  99
 100        rev_list_pid = fork();
 101        if (!rev_list_pid) {
 102                rev_list(fd, refs);
 103                die("rev-list setup failed");
 104        }
 105        if (rev_list_pid < 0)
 106                die("rev-list fork failed");
 107        /*
 108         * We don't wait for the rev-list pipeline in the parent:
 109         * we end up waiting for the other end instead
 110         */
 111        return 0;
 112}
 113
 114static int read_ref(const char *ref, unsigned char *sha1)
 115{
 116        int fd, ret;
 117        static char pathname[PATH_MAX];
 118        char buffer[60];
 119        const char *git_dir = gitenv(GIT_DIR_ENVIRONMENT) ? : DEFAULT_GIT_DIR_ENVIRONMENT;
 120
 121        snprintf(pathname, sizeof(pathname), "%s/%s", git_dir, ref);
 122        fd = open(pathname, O_RDONLY);
 123        if (fd < 0)
 124                return -1;
 125        ret = -1;
 126        if (read(fd, buffer, sizeof(buffer)) >= 40)
 127                ret = get_sha1_hex(buffer, sha1);
 128        close(fd);
 129        return ret;
 130}
 131
 132static int send_pack(int in, int out, int nr_match, char **match)
 133{
 134        struct ref *ref_list = NULL, **last_ref = &ref_list;
 135        struct ref *ref;
 136
 137        for (;;) {
 138                unsigned char old_sha1[20];
 139                unsigned char new_sha1[20];
 140                static char buffer[1000];
 141                char *name;
 142                int len;
 143
 144                len = packet_read_line(in, buffer, sizeof(buffer));
 145                if (!len)
 146                        break;
 147                if (buffer[len-1] == '\n')
 148                        buffer[--len] = 0;
 149
 150                if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ')
 151                        die("protocol error: expected sha/ref, got '%s'", buffer);
 152                name = buffer + 41;
 153                if (nr_match && !path_match(name, nr_match, match))
 154                        continue;
 155                if (read_ref(name, new_sha1) < 0)
 156                        return error("no such local reference '%s'", name);
 157                if (!has_sha1_file(old_sha1))
 158                        return error("remote '%s' points to object I don't have", name);
 159                if (!memcmp(old_sha1, new_sha1, 20)) {
 160                        fprintf(stderr, "'%s' unchanged\n", name);
 161                        continue;
 162                }
 163                ref = xmalloc(sizeof(*ref) + len - 40);
 164                memcpy(ref->old_sha1, old_sha1, 20);
 165                memcpy(ref->new_sha1, new_sha1, 20);
 166                memcpy(ref->name, buffer + 41, len - 40);
 167                ref->next = NULL;
 168                *last_ref = ref;
 169                last_ref = &ref->next;
 170        }
 171
 172        for (ref = ref_list; ref; ref = ref->next) {
 173                char old_hex[60], *new_hex;
 174                strcpy(old_hex, sha1_to_hex(ref->old_sha1));
 175                new_hex = sha1_to_hex(ref->new_sha1);
 176                packet_write(out, "%s %s %s", old_hex, new_hex, ref->name);
 177                fprintf(stderr, "'%s': updating from %s to %s\n", ref->name, old_hex, new_hex);
 178        }
 179        
 180        packet_flush(out);
 181        if (ref_list)
 182                pack_objects(out, ref_list);
 183        close(out);
 184        return 0;
 185}
 186
 187/*
 188 * First, make it shell-safe.  We do this by just disallowing any
 189 * special characters. Somebody who cares can do escaping and let
 190 * through the rest. But since we're doing to feed this to ssh as
 191 * a command line, we're going to be pretty damn anal for now.
 192 */
 193static char *shell_safe(char *url)
 194{
 195        char *n = url;
 196        unsigned char c;
 197        static const char flags[256] = {
 198                ['0'...'9'] = 1,
 199                ['a'...'z'] = 1,
 200                ['A'...'Z'] = 1,
 201                ['.'] = 1, ['/'] = 1,
 202                ['-'] = 1, ['+'] = 1,
 203                [':'] = 1
 204        };
 205
 206        while ((c = *n++) != 0) {
 207                if (flags[c] != 1)
 208                        die("I don't like '%c'. Sue me.", c);
 209        }
 210        return url;
 211}
 212
 213/*
 214 * Yeah, yeah, fixme. Need to pass in the heads etc.
 215 */
 216static int setup_connection(int fd[2], char *url, char **heads)
 217{
 218        char command[1024];
 219        const char *host, *path;
 220        char *colon;
 221        int pipefd[2][2];
 222        pid_t pid;
 223
 224        url = shell_safe(url);
 225        host = NULL;
 226        path = url;
 227        colon = strchr(url, ':');
 228        if (colon) {
 229                *colon = 0;
 230                host = url;
 231                path = colon+1;
 232        }
 233        snprintf(command, sizeof(command), "%s %s", exec, path);
 234        if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0)
 235                die("unable to create pipe pair for communication");
 236        pid = fork();
 237        if (!pid) {
 238                dup2(pipefd[1][0], 0);
 239                dup2(pipefd[0][1], 1);
 240                close(pipefd[0][0]);
 241                close(pipefd[0][1]);
 242                close(pipefd[1][0]);
 243                close(pipefd[1][1]);
 244                if (host)
 245                        execlp("ssh", "ssh", host, command, NULL);
 246                else
 247                        execlp("sh", "sh", "-c", command, NULL);
 248                die("exec failed");
 249        }               
 250        fd[0] = pipefd[0][0];
 251        fd[1] = pipefd[1][1];
 252        close(pipefd[0][1]);
 253        close(pipefd[1][0]);
 254        return pid;
 255}
 256
 257int main(int argc, char **argv)
 258{
 259        int i, nr_heads = 0;
 260        char *dest = NULL;
 261        char **heads = NULL;
 262        int fd[2], ret;
 263        pid_t pid;
 264
 265        argv++;
 266        for (i = 1; i < argc; i++) {
 267                char *arg = *argv++;
 268
 269                if (*arg == '-') {
 270                        if (!strncmp(arg, "--exec=", 7)) {
 271                                exec = arg + 7;
 272                                continue;
 273                        }
 274                        usage(send_pack_usage);
 275                }
 276                dest = arg;
 277                heads = argv;
 278                nr_heads = argc - i -1;
 279                break;
 280        }
 281        if (!dest)
 282                usage(send_pack_usage);
 283        pid = setup_connection(fd, dest, heads);
 284        if (pid < 0)
 285                return 1;
 286        ret = send_pack(fd[0], fd[1], nr_heads, heads);
 287        close(fd[0]);
 288        close(fd[1]);
 289        waitpid(pid, NULL, 0);
 290        return ret;
 291}