transport.con commit Remove more debugging from builtin-fetch (f383959)
   1#include "cache.h"
   2#include "transport.h"
   3#include "run-command.h"
   4#include "http.h"
   5#include "pkt-line.h"
   6#include "fetch-pack.h"
   7#include "walker.h"
   8#include "bundle.h"
   9
  10/* Generic functions for using commit walkers */
  11
  12static int fetch_objs_via_walker(struct transport *transport,
  13                                 int nr_objs, struct ref **to_fetch)
  14{
  15        char *dest = xstrdup(transport->url);
  16        struct walker *walker = transport->data;
  17        char **objs = xmalloc(nr_objs * sizeof(*objs));
  18        int i;
  19
  20        walker->get_all = 1;
  21        walker->get_tree = 1;
  22        walker->get_history = 1;
  23        walker->get_verbosely = transport->verbose;
  24        walker->get_recover = 0;
  25
  26        for (i = 0; i < nr_objs; i++)
  27                objs[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
  28
  29        if (walker_fetch(walker, nr_objs, objs, NULL, NULL))
  30                die("Fetch failed.");
  31
  32        for (i = 0; i < nr_objs; i++)
  33                free(objs[i]);
  34        free(objs);
  35        free(dest);
  36        return 0;
  37}
  38
  39static int disconnect_walker(struct transport *transport)
  40{
  41        struct walker *walker = transport->data;
  42        if (walker)
  43                walker_free(walker);
  44        return 0;
  45}
  46
  47static const struct transport_ops rsync_transport;
  48
  49static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
  50        const char **argv;
  51        int argc;
  52        int err;
  53
  54        argv = xmalloc((refspec_nr + 11) * sizeof(char *));
  55        argv[0] = "http-push";
  56        argc = 1;
  57        if (flags & TRANSPORT_PUSH_ALL)
  58                argv[argc++] = "--all";
  59        if (flags & TRANSPORT_PUSH_FORCE)
  60                argv[argc++] = "--force";
  61        argv[argc++] = transport->url;
  62        while (refspec_nr--)
  63                argv[argc++] = *refspec++;
  64        argv[argc] = NULL;
  65        err = run_command_v_opt(argv, RUN_GIT_CMD);
  66        switch (err) {
  67        case -ERR_RUN_COMMAND_FORK:
  68                error("unable to fork for %s", argv[0]);
  69        case -ERR_RUN_COMMAND_EXEC:
  70                error("unable to exec %s", argv[0]);
  71                break;
  72        case -ERR_RUN_COMMAND_WAITPID:
  73        case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
  74        case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
  75        case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
  76                error("%s died with strange error", argv[0]);
  77        }
  78        return !!err;
  79}
  80
  81#ifndef NO_CURL
  82static int missing__target(int code, int result)
  83{
  84        return  /* file:// URL -- do we ever use one??? */
  85                (result == CURLE_FILE_COULDNT_READ_FILE) ||
  86                /* http:// and https:// URL */
  87                (code == 404 && result == CURLE_HTTP_RETURNED_ERROR) ||
  88                /* ftp:// URL */
  89                (code == 550 && result == CURLE_FTP_COULDNT_RETR_FILE)
  90                ;
  91}
  92
  93#define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
  94
  95static struct ref *get_refs_via_curl(const struct transport *transport)
  96{
  97        struct buffer buffer;
  98        char *data, *start, *mid;
  99        char *ref_name;
 100        char *refs_url;
 101        int i = 0;
 102
 103        struct active_request_slot *slot;
 104        struct slot_results results;
 105
 106        struct ref *refs = NULL;
 107        struct ref *ref = NULL;
 108        struct ref *last_ref = NULL;
 109
 110        data = xmalloc(4096);
 111        buffer.size = 4096;
 112        buffer.posn = 0;
 113        buffer.buffer = data;
 114
 115        refs_url = xmalloc(strlen(transport->url) + 11);
 116        sprintf(refs_url, "%s/info/refs", transport->url);
 117
 118        http_init();
 119
 120        slot = get_active_slot();
 121        slot->results = &results;
 122        curl_easy_setopt(slot->curl, CURLOPT_FILE, &buffer);
 123        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, fwrite_buffer);
 124        curl_easy_setopt(slot->curl, CURLOPT_URL, refs_url);
 125        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, NULL);
 126        if (start_active_slot(slot)) {
 127                run_active_slot(slot);
 128                if (results.curl_result != CURLE_OK) {
 129                        if (missing_target(&results)) {
 130                                free(buffer.buffer);
 131                                return NULL;
 132                        } else {
 133                                free(buffer.buffer);
 134                                error("%s", curl_errorstr);
 135                                return NULL;
 136                        }
 137                }
 138        } else {
 139                free(buffer.buffer);
 140                error("Unable to start request");
 141                return NULL;
 142        }
 143
 144        http_cleanup();
 145
 146        data = buffer.buffer;
 147        start = NULL;
 148        mid = data;
 149        while (i < buffer.posn) {
 150                if (!start)
 151                        start = &data[i];
 152                if (data[i] == '\t')
 153                        mid = &data[i];
 154                if (data[i] == '\n') {
 155                        data[i] = 0;
 156                        ref_name = mid + 1;
 157                        ref = xmalloc(sizeof(struct ref) +
 158                                      strlen(ref_name) + 1);
 159                        memset(ref, 0, sizeof(struct ref));
 160                        strcpy(ref->name, ref_name);
 161                        get_sha1_hex(start, ref->old_sha1);
 162                        if (!refs)
 163                                refs = ref;
 164                        if (last_ref)
 165                                last_ref->next = ref;
 166                        last_ref = ref;
 167                        start = NULL;
 168                }
 169                i++;
 170        }
 171
 172        free(buffer.buffer);
 173
 174        return refs;
 175}
 176
 177static int fetch_objs_via_curl(struct transport *transport,
 178                                 int nr_objs, struct ref **to_fetch)
 179{
 180        if (!transport->data)
 181                transport->data = get_http_walker(transport->url);
 182        return fetch_objs_via_walker(transport, nr_objs, to_fetch);
 183}
 184
 185#else
 186
 187static struct ref *get_refs_via_curl(const struct transport *transport)
 188{
 189        die("Cannot fetch from '%s' without curl ...", transport->url);
 190        return NULL;
 191}
 192
 193static int fetch_objs_via_curl(struct transport *transport,
 194                                 int nr_objs, struct ref **to_fetch)
 195{
 196        die("Cannot fetch from '%s' without curl ...", transport->url);
 197        return -1;
 198}
 199
 200#endif
 201
 202static const struct transport_ops curl_transport = {
 203        /* set_option */        NULL,
 204        /* get_refs_list */     get_refs_via_curl,
 205        /* fetch */             fetch_objs_via_curl,
 206        /* push */              curl_transport_push,
 207        /* disconnect */        disconnect_walker
 208};
 209
 210struct bundle_transport_data {
 211        int fd;
 212        struct bundle_header header;
 213};
 214
 215static struct ref *get_refs_from_bundle(const struct transport *transport)
 216{
 217        struct bundle_transport_data *data = transport->data;
 218        struct ref *result = NULL;
 219        int i;
 220
 221        if (data->fd > 0)
 222                close(data->fd);
 223        data->fd = read_bundle_header(transport->url, &data->header);
 224        if (data->fd < 0)
 225                die ("Could not read bundle '%s'.", transport->url);
 226        for (i = 0; i < data->header.references.nr; i++) {
 227                struct ref_list_entry *e = data->header.references.list + i;
 228                struct ref *ref = alloc_ref(strlen(e->name));
 229                hashcpy(ref->old_sha1, e->sha1);
 230                strcpy(ref->name, e->name);
 231                ref->next = result;
 232                result = ref;
 233        }
 234        return result;
 235}
 236
 237static int fetch_refs_from_bundle(struct transport *transport,
 238                               int nr_heads, struct ref **to_fetch)
 239{
 240        struct bundle_transport_data *data = transport->data;
 241        return unbundle(&data->header, data->fd);
 242}
 243
 244static int close_bundle(struct transport *transport)
 245{
 246        struct bundle_transport_data *data = transport->data;
 247        if (data->fd > 0)
 248                close(data->fd);
 249        return 0;
 250}
 251
 252static const struct transport_ops bundle_transport = {
 253        /* set_option */        NULL,
 254        /* get_refs_list */     get_refs_from_bundle,
 255        /* fetch */             fetch_refs_from_bundle,
 256        /* push */              NULL,
 257        /* disconnect */        close_bundle
 258};
 259
 260struct git_transport_data {
 261        unsigned thin : 1;
 262        unsigned keep : 1;
 263
 264        int unpacklimit;
 265
 266        int depth;
 267
 268        const char *uploadpack;
 269        const char *receivepack;
 270};
 271
 272static int set_git_option(struct transport *connection,
 273                          const char *name, const char *value)
 274{
 275        struct git_transport_data *data = connection->data;
 276        if (!strcmp(name, TRANS_OPT_UPLOADPACK)) {
 277                data->uploadpack = value;
 278                return 0;
 279        } else if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {
 280                data->receivepack = value;
 281                return 0;
 282        } else if (!strcmp(name, TRANS_OPT_THIN)) {
 283                data->thin = !!value;
 284                return 0;
 285        } else if (!strcmp(name, TRANS_OPT_KEEP)) {
 286                data->keep = !!value;
 287                return 0;
 288        } else if (!strcmp(name, TRANS_OPT_UNPACKLIMIT)) {
 289                data->unpacklimit = atoi(value);
 290                return 0;
 291        } else if (!strcmp(name, TRANS_OPT_DEPTH)) {
 292                if (!value)
 293                        data->depth = 0;
 294                else
 295                        data->depth = atoi(value);
 296                return 0;
 297        }
 298        return 1;
 299}
 300
 301static struct ref *get_refs_via_connect(const struct transport *transport)
 302{
 303        struct git_transport_data *data = transport->data;
 304        struct ref *refs;
 305        int fd[2];
 306        pid_t pid;
 307        char *dest = xstrdup(transport->url);
 308
 309        pid = git_connect(fd, dest, data->uploadpack, 0);
 310
 311        if (pid < 0)
 312                die("Failed to connect to \"%s\"", transport->url);
 313
 314        get_remote_heads(fd[0], &refs, 0, NULL, 0);
 315        packet_flush(fd[1]);
 316
 317        finish_connect(pid);
 318
 319        free(dest);
 320
 321        return refs;
 322}
 323
 324static int fetch_refs_via_pack(struct transport *transport,
 325                               int nr_heads, struct ref **to_fetch)
 326{
 327        struct git_transport_data *data = transport->data;
 328        char **heads = xmalloc(nr_heads * sizeof(*heads));
 329        char **origh = xmalloc(nr_heads * sizeof(*origh));
 330        struct ref *refs;
 331        char *dest = xstrdup(transport->url);
 332        struct fetch_pack_args args;
 333        int i;
 334
 335        args.uploadpack = data->uploadpack;
 336        args.quiet = 0;
 337        args.keep_pack = data->keep;
 338        args.unpacklimit = data->unpacklimit;
 339        args.use_thin_pack = data->thin;
 340        args.fetch_all = 0;
 341        args.verbose = transport->verbose;
 342        args.depth = data->depth;
 343        args.no_progress = 0;
 344
 345        setup_fetch_pack(&args);
 346
 347        for (i = 0; i < nr_heads; i++)
 348                origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
 349        refs = fetch_pack(dest, nr_heads, heads, &transport->pack_lockfile);
 350
 351        for (i = 0; i < nr_heads; i++)
 352                free(origh[i]);
 353        free(origh);
 354        free(heads);
 355        free_refs(refs);
 356        free(dest);
 357        return 0;
 358}
 359
 360static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
 361        struct git_transport_data *data = transport->data;
 362        const char **argv;
 363        char *rem;
 364        int argc;
 365        int err;
 366
 367        argv = xmalloc((refspec_nr + 11) * sizeof(char *));
 368        argv[0] = "send-pack";
 369        argc = 1;
 370        if (flags & TRANSPORT_PUSH_ALL)
 371                argv[argc++] = "--all";
 372        if (flags & TRANSPORT_PUSH_FORCE)
 373                argv[argc++] = "--force";
 374        if (data->receivepack) {
 375                char *rp = xmalloc(strlen(data->receivepack) + 16);
 376                sprintf(rp, "--receive-pack=%s", data->receivepack);
 377                argv[argc++] = rp;
 378        }
 379        if (data->thin)
 380                argv[argc++] = "--thin";
 381        rem = xmalloc(strlen(transport->remote->name) + 10);
 382        sprintf(rem, "--remote=%s", transport->remote->name);
 383        argv[argc++] = rem;
 384        argv[argc++] = transport->url;
 385        while (refspec_nr--)
 386                argv[argc++] = *refspec++;
 387        argv[argc] = NULL;
 388        err = run_command_v_opt(argv, RUN_GIT_CMD);
 389        switch (err) {
 390        case -ERR_RUN_COMMAND_FORK:
 391                error("unable to fork for %s", argv[0]);
 392        case -ERR_RUN_COMMAND_EXEC:
 393                error("unable to exec %s", argv[0]);
 394                break;
 395        case -ERR_RUN_COMMAND_WAITPID:
 396        case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
 397        case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
 398        case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
 399                error("%s died with strange error", argv[0]);
 400        }
 401        return !!err;
 402}
 403
 404static const struct transport_ops git_transport = {
 405        /* set_option */        set_git_option,
 406        /* get_refs_list */     get_refs_via_connect,
 407        /* fetch */             fetch_refs_via_pack,
 408        /* push */              git_transport_push
 409};
 410
 411static int is_local(const char *url)
 412{
 413        const char *colon = strchr(url, ':');
 414        const char *slash = strchr(url, '/');
 415        return !colon || (slash && slash < colon);
 416}
 417
 418static int is_file(const char *url)
 419{
 420        struct stat buf;
 421        if (stat(url, &buf))
 422                return 0;
 423        return S_ISREG(buf.st_mode);
 424}
 425
 426struct transport *transport_get(struct remote *remote, const char *url)
 427{
 428        struct transport *ret = xcalloc(1, sizeof(*ret));
 429
 430        ret->remote = remote;
 431        ret->url = url;
 432
 433        if (!prefixcmp(url, "rsync://")) {
 434                ret->ops = &rsync_transport;
 435        } else if (!prefixcmp(url, "http://")
 436                || !prefixcmp(url, "https://")
 437                || !prefixcmp(url, "ftp://")) {
 438                ret->ops = &curl_transport;
 439        } else if (is_local(url) && is_file(url)) {
 440                struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
 441                ret->data = data;
 442                ret->ops = &bundle_transport;
 443        } else {
 444                struct git_transport_data *data = xcalloc(1, sizeof(*data));
 445                ret->data = data;
 446                data->thin = 1;
 447                data->uploadpack = "git-upload-pack";
 448                if (remote && remote->uploadpack)
 449                        data->uploadpack = remote->uploadpack;
 450                data->receivepack = "git-receive-pack";
 451                if (remote && remote->receivepack)
 452                        data->receivepack = remote->receivepack;
 453                data->unpacklimit = -1;
 454                ret->ops = &git_transport;
 455        }
 456
 457        return ret;
 458}
 459
 460int transport_set_option(struct transport *transport,
 461                         const char *name, const char *value)
 462{
 463        int ret = 1;
 464        if (transport->ops->set_option)
 465                ret = transport->ops->set_option(transport, name, value);
 466        if (ret < 0)
 467                fprintf(stderr, "For '%s' option %s cannot be set to '%s'\n",
 468                        transport->url, name, value);
 469        if (ret > 0)
 470                fprintf(stderr, "For '%s' option %s is ignored\n",
 471                        transport->url, name);
 472        return ret;
 473}
 474
 475int transport_push(struct transport *transport,
 476                   int refspec_nr, const char **refspec, int flags)
 477{
 478        if (!transport->ops->push)
 479                return 1;
 480        return transport->ops->push(transport, refspec_nr, refspec, flags);
 481}
 482
 483struct ref *transport_get_remote_refs(struct transport *transport)
 484{
 485        if (!transport->remote_refs)
 486                transport->remote_refs =
 487                        transport->ops->get_refs_list(transport);
 488        return transport->remote_refs;
 489}
 490
 491int transport_fetch_refs(struct transport *transport, struct ref *refs)
 492{
 493        int rc;
 494        int nr_heads = 0, nr_alloc = 0;
 495        struct ref **heads = NULL;
 496        struct ref *rm;
 497
 498        for (rm = refs; rm; rm = rm->next) {
 499                if (rm->peer_ref &&
 500                    !hashcmp(rm->peer_ref->old_sha1, rm->old_sha1))
 501                        continue;
 502                ALLOC_GROW(heads, nr_heads + 1, nr_alloc);
 503                heads[nr_heads++] = rm;
 504        }
 505
 506        rc = transport->ops->fetch(transport, nr_heads, heads);
 507        free(heads);
 508        return rc;
 509}
 510
 511void transport_unlock_pack(struct transport *transport)
 512{
 513        if (transport->pack_lockfile) {
 514                unlink(transport->pack_lockfile);
 515                free(transport->pack_lockfile);
 516                transport->pack_lockfile = NULL;
 517        }
 518}
 519
 520int transport_disconnect(struct transport *transport)
 521{
 522        int ret = 0;
 523        if (transport->ops->disconnect)
 524                ret = transport->ops->disconnect(transport);
 525        free(transport);
 526        return ret;
 527}