Subject: Re: ssh often hangs where pserver succeeds
From: thospel at mail dot dma dot be (Ton Hospel)
To: gcc at gcc dot gnu dot org
Date: 5 May 2000 02:20:48 GMT

In article <[email-masked]>, "H . J . Lu" <[email-masked]> writes:

> On Wed, May 03, 2000 at 02:30:55AM -0700, Greg McGary wrote:
>> FYI...
>> 
>> I have noticed that cvs over ssh often hangs, especially for
>> long-running diffs and updates.  Running without locks via `cvs -n'
>> helps for diffs, but doesn't work correctly for updates.  On a whim I
>> reverted to pserver and so far it has succeeded every time where ssh
>> had been hanging.
>> 
>> Has anyone else experienced hangs with cvs over ssh?
> 
> I heard rsync hangs over ssh.
> 

I had the same problem on an rsync where I had extended the protocol a
bit. (not sure if standard rsync can also trigger it). I traced back the
rare (happened about 1 in a hundred times) errors to a classic deadlock
were rsync was blocked trying to write to ssh, and ssh was blocked trying
to write to rsync. The deadlock is very sensitive to your exact network
speeds and buffer settings.

Rsync is a pure read then write then read protocol, but ssh does in fact do
a clean select on stdin/stdout and buffering of pending data. But it forgets
to put the filedescriptors in non blocking mode. So I tend to call ssh the
cause (though there's no reason for rsync to not put it's pipes in non-block
to give the slave program an extra chance).

I never contributed my patch to ssh since I stopped helping them after they
unfreed their copyright (thereby stealing the code of mine already in it).

Compile the following wrapper, and put it in the same directary as you have
ssh. Tell your rsync to use this unblock_ssh program. If it solves your
problem, then you were running into this same deadlock.

---------------------------
/* Written by Ton Hospel */
/* Hereby put under GNU copyleft */

#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <errno.h>
#ifndef HAVE_NO_UNISTD_H
# include <unistd.h>
#endif /* HAVE_NO_UNISTD_H */
#include <fcntl.h>

static char ssh[] = "ssh";

int main(int argc, char **argv) {
    int rc;
    char *ptr, *work;

    rc = fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
    if (rc < 0) {
        fprintf(stderr, "Could not unblock stdin: %s\n", strerror(errno));
        return 1;
    }
    rc = fcntl(fileno(stdout), F_SETFL, O_NONBLOCK);
    if (rc < 0) {
        fprintf(stderr, "Could not unblock stdout: %s\n", strerror(errno));
        return 1;
    }
    rc = fcntl(fileno(stderr), F_SETFL, O_NONBLOCK);
    if (rc < 0) {
        fprintf(stderr, "Could not unblock stderr: %s\n", strerror(errno));
        return 1;
    }
    
    ptr = rindex(argv[0], '/');
    if (ptr == NULL) ptr = argv[0];
    else ptr++;
    work = malloc(ptr-argv[0]+sizeof(ssh));
    if (!work) {
        fprintf(stderr, "Out of memory. Buy more ?\n");
        return 1;
    }
    memcpy(work, argv[0], ptr-argv[0]);
    memcpy(work+(ptr-argv[0]), ssh, sizeof(ssh));
    argv[0] = work;
    rc = execvp(work, argv);
    fprintf(stderr, "Could not exec %.300s: %s\n", work, strerror(errno));
    return rc;
}