#!/bin/bash
-# deny-rsync [message]: send an rsync-protocol error message
+# Send an error message via the rsync-protocol to a non-daemon client rsync.
+#
+# Usage: deny-rsync "message"
protocol_version=29
exit_code=4 # same as a daemon that refuses an option
-# byte_escape 29 ==> \035
+# e.g. byte_escape 29 => \035
function byte_escape {
- octbyte="000$(bc <<<"obase=8; $1")"
- echo -n "\\${octbyte: -3}"
+ echo -ne "\\0$(printf "%o" $1)"
}
msg="$1"
-if [ "${#msg}" -gt 255 ]; then
- # message is too long for this naive script to handle
- msg="${msg:0:252}..."
+if [ "${#msg}" -gt 254 ]; then
+ # truncate a message that is too long for this naive script to handle
+ msg="${msg:0:251}..."
fi
+msglen=$(( ${#msg} + 1 )) # add 1 for the newline we append below
-# send protocol version
+# Send protocol version. All numbers are LSB-first 4-byte ints.
echo -ne "$(byte_escape $protocol_version)\\000\\000\\000"
-# send checksum seed
+# Send a zero checksum seed.
echo -ne "\\000\\000\\000\\000"
-# the following is equivalent to rwrite(FERROR, $msg)
-# message header: length 17; MPLEX_BASE + code FERROR
-echo -ne "$(byte_escape ${#msg})\\000\\000\\010"
-# data
-echo -n "$msg"
+# The following is equivalent to rprintf(FERROR_XFER, "%s\n", $msg).
+# 1. Message header: ((MPLEX_BASE + FERROR_XFER) << 24) + $msglen.
+echo -ne "$(byte_escape $msglen)\\000\\000\\010"
+# 2. The actual data.
+echo -E "$msg"
+
+# Make sure the client gets our message, not a write failure.
+sleep 1
exit $exit_code