Tuesday 7 April 2009

Linux: Getting passed rsync's most notorious error messages and determining whether a shell is interactive or not.

While trying to back up my personal data using rsync -aruvzh --delete /home/thanassis/jdevhome/ thanassis@192.168.1.68:/home/thanassis/jdevhome I run into the following error message.

protocol version mismatch - is your shell clean?
(see the rsync man page for an explanation)
rsync error: protocol incompatibility (code 2) at compat.c(69)

A little googling and a final look at the rsync manual revealed that :

DIAGNOSTICS
     rsync occasionally produces error messages that may seem a little cryp-
     tic.  The  one that seems to cause the most confusion is “protocol ver-
     sion mismatch — is your shell clean?”.

     This message is usually caused by your startup scripts or remote  shell
     facility  producing  unwanted garbage on the stream that rsync is using
     for its transport. The way to diagnose this  problem  is  to  run  your
     remote shell like this:

            ssh remotehost /bin/true > out.dat

     then  look  at out.dat. If everything is working correctly then out.dat
     should be a zero length file. If you are getting the above  error  from
     rsync  then  you  will probably find that out.dat contains some text or
     data. Look at the contents and try to work out what  is  producing  it.
     The  most  common cause is incorrectly configured shell startup scripts
     (such as .cshrc or .profile) that contain output  statements  for  non-
     interactive logins.

Needless to say that fortune was the one to blame. My .bashrc file had the following last statements since the days of SuSE 8.2 and although I am now using a completely different distro, I still refuse to get them out of my startup files.

# Something to remember the old SuSE days
if [ -x /usr/bin/fortune ] ; then
    echo
    /usr/bin/fortune
    echo
fi

So, the solution would be to display a fortune cookie only when the current shell is interactive and then it was time for the Bash Reference Manual to come to our rescue. Their answers are straightforward :

To determine within a startup script whether Bash is running interactively or not, examine the variable $PS1; it is unset in non-interactive shells, and set in interactive shells. Thus:
     if [ -z "$PS1" ]; then
        echo This shell is not interactive
     else
        echo This shell is interactive
     fi
Alternatively, startup scripts may test the value of the `-' special parameter. It contains i when the shell is interactive. For example:
     case "$-" in
     *i*)  echo This shell is interactive ;;
     *)    echo This shell is not interactive ;;
     esac

In my case I wanted to run fortune on interactive shells only. So again I modified my bashrc file to look like :

# Display a fortune cookie on interactive logins only
if [ -n "$PS1" ]; then
        # Some people don't like fortune. If you uncomment the following lines,
        # you will have a fortune each time you log in ;-)
        if [ -x /usr/bin/fortune ] ; then
                echo
                /usr/bin/fortune
                echo
        fi
fi

... and that did it.

No comments :