Repo VS TPLink

RepoTPLink 是两个风马牛不相及的事物; 前者是 google 开发的用来管理 多个 git 仓库的工具; 后者是国内一家知名的网络设备商。

但笔者最近碰到一个非常诡异的问题,如果目录的名字出现 tplink 字符串,就会导致 Repo 工作异常; 如果将目录 tplink 更改为其他名字,比如 tp 或者 test 等等,Repo 就工作正常; 笔者一度怀疑 Repo 的开发者是否和 TPLink 公司有些过节,因此挖一个坑。

# mkdir tplink
# cd tplink
# repo init -u ssh://username@reposerver.com:29418/menifest.git
......

bind: atch: Cannot assign requested address
ssh: connect to host 29418 port 22: Cannot assign requested address
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

抱着吃瓜群众看热闹的心理,笔者想找到那个和 TPLink 过节的程序员,和他聊聊人生。

Repo 在实际运作过程中,首先创建一个共享的 SSH 隧道,后续运行的 ssh 命令将复用这个隧道,类似于开来一个本地代理。

Repo 设置了环境变量 GIT_SSH, 其定义为:

#!/bin/sh
exec ssh -o "ControlMaster no" -o "ControlPath $REPO_SSH_SOCK" "$@"

Repo 设置 GIT_SSH 变量的原因是为了减少建立 SSH 隧道的次数;repo 通常要管理几十个甚至上百个 git 仓库; 如果克隆每个仓库都建立一次 SSH 隧道,会明显增加时间消耗。

Debug 发现,代理 SSH (就是创建隧道的那个)收到的 参数不对。

演示一下 GIT_SSH 的用法。

  1. 打开共享 SSH 隧道
#mkdir /tmp/ssh
#ssh -v -M -N -p 22 -o 'ControlPath /tmp/ssh/test_git_ssh' tony@192.168.3.8
###// 此处打开了 -v 选项,用来查看 ssh log 信息
  1. 使用共享的 SSH 隧道 (正常)
#mkdir ~/work/check_git/nolink
#cd ~/work/check_git/nolink
#echo '#!/bin/sh' > git_ssh
#echo 'exec ssh  -o "ControlMaster no" -o "ControlPath $REPO_SSH_SOCK" "$@"' >> git_ssh
#chmod a+x git_ssh
#export GIT_SSH='/home/tony_nie/work/check_git/nolink/git_ssh'
#export REPO_SSH_SOCK='/tmp/ssh/test_git_ssh'
#pwd
/home/tony_nie/work/check_git/nolink
#git clone ssh://tony@192.168.3.8:22/Users/tony/code/check_git/test_git
Cloning into 'test_git'...
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 6 (delta 0), reused 6 (delta 0)
Receiving objects: 100% (6/6), done.
Checking connectivity... done.
  1. 使用共享的 SSH 隧道 (失败)
#mkdir ~/work/check_git/tplink
#cd ~/work/check_git/tplink
#echo '#!/bin/sh' > git_ssh
#echo 'exec ssh  -o "ControlMaster no" -o "ControlPath $REPO_SSH_SOCK" "$@"' >> git_ssh
#chmod a+x git_ssh
#export GIT_SSH='/home/tony_nie/work/check_git/tplink/git_ssh'
#export REPO_SSH_SOCK='/tmp/ssh/test_git_ssh'
#pwd
/home/tony_nie/work/check_git/tplink
#git clone ssh://tony@192.168.3.8:22/Users/tony/code/check_git/test_git
Cloning into 'test_git'...
bash: tony@192.168.3.8: command not found
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.

执行 git clone 命令时可以看到共享隧道有 log 信息输出。

正常:

debug1: multiplexing control connection
debug1: channel 1: new [mux-control]
debug1: channel 2: new [client-session]
debug1: Sending environment.
debug1: Sending env LC_ALL = en_US.UTF-8
debug1: Sending env LANG = zh_CN.UTF-8
debug1: Sending env LC_CTYPE = en_US.UTF-8
debug1: Sending command: git-upload-pack '/home/tony_nie/work/test_git'
debug1: client_input_channel_req: channel 2 rtype exit-status reply 0
debug1: client_input_channel_req: channel 2 rtype eow@openssh.com reply 0
debug1: channel 2: free: client-session, nchannels 3
debug1: channel 1: free: mux-control, nchannels 2

异常:

debug1: multiplexing control connection
debug1: channel 1: new [mux-control]
debug1: channel 2: new [client-session]
debug1: Sending environment.
debug1: Sending env LC_PAPER = zh_CN.UTF-8
debug1: Sending env LC_ADDRESS = zh_CN.UTF-8
debug1: Sending env LC_MONETARY = zh_CN.UTF-8
debug1: Sending env LC_NUMERIC = zh_CN.UTF-8
debug1: Sending env LC_ALL = en_US.UTF-8
debug1: Sending env LC_TELEPHONE = zh_CN.UTF-8
debug1: Sending env LC_IDENTIFICATION = zh_CN.UTF-8
debug1: Sending env LANG = en_US.UTF-8
debug1: Sending env LC_MEASUREMENT = zh_CN.UTF-8
debug1: Sending env LC_CTYPE = en_US.UTF-8
debug1: Sending env LC_TIME = zh_CN.UTF-8
debug1: Sending env LC_NAME = zh_CN.UTF-8
debug1: Sending command: tony@192.168.3.8 git-upload-pack '/Users/tony/code/check_git/test_git'
debug1: client_input_channel_req: channel 2 rtype exit-status reply 0
debug1: client_input_channel_req: channel 2 rtype eow@openssh.com reply 0
debug1: channel 2: free: client-session, nchannels 3
debug1: channel 1: free: mux-control, nchannels 2

发现如果GIT_SSH 环境变量中如果包含 tplink 字样就会出错; 和 repo 没有关系哎。难道是 git 和 TPLink 有过节?

download git (1.9.1) 的 code 研究一下吧。

现在整个工程中 grep TPlink 字符串,没有发现;再 grep plink;咦,有发现哎。

git v1.9.1 .. sourcecode:

if (protocol == PROTO_SSH) {
        const char *ssh = getenv("GIT_SSH");
        int putty = ssh && strcasestr(ssh, "plink");
        char *ssh_host = hostandport;
        const char *port = NULL;
        get_host_and_port(&ssh_host, &port);
        port = get_port_numeric(port);

        if (!ssh) ssh = "ssh";
        *arg++ = ssh;
        if (putty && !strcasestr(ssh, "tortoiseplink"))
                *arg++ = "-batch";
        if (port) {
                /* P is for PuTTY, p is for OpenSSH */
                *arg++ = putty ? "-P" : "-p";
                *arg++ = port;
        }
        *arg++ = ssh_host;
}

很明显在判断 是否 putty 时,出了问题;认为GIT_SSH 环境变量出现 plink 就为 putty :> <:

git v2.5

ssh = getenv("GIT_SSH_COMMAND");
if (ssh) {
        conn->use_shell = 1;
        putty = 0;
} else {
        const char *base;
        char *ssh_dup;

        ssh = getenv("GIT_SSH");
        if (!ssh)
        ssh = "ssh";

        ssh_dup = xstrdup(ssh);
        base = basename(ssh_dup);

        tortoiseplink = !strcasecmp(base, "tortoiseplink") ||
                !strcasecmp(base, "tortoiseplink.exe");
        putty = !strcasecmp(base, "plink") ||
                !strcasecmp(base, "plink.exe") || tortoiseplink;

        free(ssh_dup);
}

还好 v2.5 修正了这个问题。

看来不是 repo 和 TPLink 有过节,而是 git 1.9.1 和 TPLink 开了个玩笑 :)