edbdc3c60ed5ec0b49298f9d70e123cf0a5f1e39
   1#include "cache.h"
   2#include "transport.h"
   3#include "run-command.h"
   4
   5static const struct transport_ops rsync_transport;
   6
   7static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
   8        const char **argv;
   9        int argc;
  10        int err;
  11
  12        argv = xmalloc((refspec_nr + 11) * sizeof(char *));
  13        argv[0] = "http-push";
  14        argc = 1;
  15        if (flags & TRANSPORT_PUSH_ALL)
  16                argv[argc++] = "--all";
  17        if (flags & TRANSPORT_PUSH_FORCE)
  18                argv[argc++] = "--force";
  19        argv[argc++] = transport->url;
  20        while (refspec_nr--)
  21                argv[argc++] = *refspec++;
  22        argv[argc] = NULL;
  23        err = run_command_v_opt(argv, RUN_GIT_CMD);
  24        switch (err) {
  25        case -ERR_RUN_COMMAND_FORK:
  26                error("unable to fork for %s", argv[0]);
  27        case -ERR_RUN_COMMAND_EXEC:
  28                error("unable to exec %s", argv[0]);
  29                break;
  30        case -ERR_RUN_COMMAND_WAITPID:
  31        case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
  32        case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
  33        case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
  34                error("%s died with strange error", argv[0]);
  35        }
  36        return !!err;
  37}
  38
  39static const struct transport_ops curl_transport = {
  40        /* set_option */        NULL,
  41        /* push */              curl_transport_push
  42};
  43
  44static const struct transport_ops bundle_transport = {
  45};
  46
  47struct git_transport_data {
  48        unsigned thin : 1;
  49
  50        const char *receivepack;
  51};
  52
  53static int set_git_option(struct transport *connection,
  54                          const char *name, const char *value)
  55{
  56        struct git_transport_data *data = connection->data;
  57        if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {
  58                data->receivepack = value;
  59                return 0;
  60        } else if (!strcmp(name, TRANS_OPT_THIN)) {
  61                data->thin = !!value;
  62                return 0;
  63        }
  64        return 1;
  65}
  66
  67static int git_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags) {
  68        struct git_transport_data *data = transport->data;
  69        const char **argv;
  70        char *rem;
  71        int argc;
  72        int err;
  73
  74        argv = xmalloc((refspec_nr + 11) * sizeof(char *));
  75        argv[0] = "send-pack";
  76        argc = 1;
  77        if (flags & TRANSPORT_PUSH_ALL)
  78                argv[argc++] = "--all";
  79        if (flags & TRANSPORT_PUSH_FORCE)
  80                argv[argc++] = "--force";
  81        if (data->receivepack) {
  82                char *rp = xmalloc(strlen(data->receivepack) + 16);
  83                sprintf(rp, "--receive-pack=%s", data->receivepack);
  84                argv[argc++] = rp;
  85        }
  86        if (data->thin)
  87                argv[argc++] = "--thin";
  88        rem = xmalloc(strlen(transport->remote->name) + 10);
  89        sprintf(rem, "--remote=%s", transport->remote->name);
  90        argv[argc++] = rem;
  91        argv[argc++] = transport->url;
  92        while (refspec_nr--)
  93                argv[argc++] = *refspec++;
  94        argv[argc] = NULL;
  95        err = run_command_v_opt(argv, RUN_GIT_CMD);
  96        switch (err) {
  97        case -ERR_RUN_COMMAND_FORK:
  98                error("unable to fork for %s", argv[0]);
  99        case -ERR_RUN_COMMAND_EXEC:
 100                error("unable to exec %s", argv[0]);
 101                break;
 102        case -ERR_RUN_COMMAND_WAITPID:
 103        case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
 104        case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
 105        case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
 106                error("%s died with strange error", argv[0]);
 107        }
 108        return !!err;
 109}
 110
 111static const struct transport_ops git_transport = {
 112        /* set_option */        set_git_option,
 113        /* push */              git_transport_push
 114};
 115
 116static int is_local(const char *url)
 117{
 118        const char *colon = strchr(url, ':');
 119        const char *slash = strchr(url, '/');
 120        return !colon || (slash && slash < colon);
 121}
 122
 123static int is_file(const char *url)
 124{
 125        struct stat buf;
 126        if (stat(url, &buf))
 127                return 0;
 128        return S_ISREG(buf.st_mode);
 129}
 130
 131struct transport *transport_get(struct remote *remote, const char *url,
 132                                int fetch)
 133{
 134        struct transport *ret = NULL;
 135        if (!prefixcmp(url, "rsync://")) {
 136                ret = xmalloc(sizeof(*ret));
 137                ret->data = NULL;
 138                ret->ops = &rsync_transport;
 139        } else if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://") ||
 140                   !prefixcmp(url, "ftp://")) {
 141                ret = xmalloc(sizeof(*ret));
 142                ret->ops = &curl_transport;
 143                ret->data = NULL;
 144        } else if (is_local(url) && is_file(url)) {
 145                ret = xmalloc(sizeof(*ret));
 146                ret->data = NULL;
 147                ret->ops = &bundle_transport;
 148        } else {
 149                struct git_transport_data *data = xcalloc(1, sizeof(*data));
 150                ret = xcalloc(1, sizeof(*ret));
 151                ret->data = data;
 152                data->thin = 1;
 153                data->receivepack = "git-receive-pack";
 154                if (remote && remote->receivepack)
 155                        data->receivepack = remote->receivepack;
 156                ret->ops = &git_transport;
 157        }
 158        if (ret) {
 159                ret->remote = remote;
 160                ret->url = url;
 161                ret->fetch = !!fetch;
 162        }
 163        return ret;
 164}
 165
 166int transport_set_option(struct transport *transport,
 167                         const char *name, const char *value)
 168{
 169        int ret = 1;
 170        if (transport->ops->set_option)
 171                ret = transport->ops->set_option(transport, name, value);
 172        if (ret < 0)
 173                fprintf(stderr, "For '%s' option %s cannot be set to '%s'\n",
 174                        transport->url, name, value);
 175        if (ret > 0)
 176                fprintf(stderr, "For '%s' option %s is ignored\n",
 177                        transport->url, name);
 178        return ret;
 179}
 180
 181int transport_push(struct transport *transport,
 182                   int refspec_nr, const char **refspec, int flags)
 183{
 184        if (!transport->ops->push)
 185                return 1;
 186        return transport->ops->push(transport, refspec_nr, refspec, flags);
 187}
 188
 189int transport_disconnect(struct transport *transport)
 190{
 191        int ret = 0;
 192        if (transport->ops->disconnect)
 193                ret = transport->ops->disconnect(transport);
 194        free(transport);
 195        return ret;
 196}