diff --git a/ChangeLog b/ChangeLog
index 489d1cf40e73c71c6390618aef8fa9eb3a6421c6..9ede1027474d87ac7881430726b7ed3d01c64ce1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,55 @@
 2007-06-14  Per Cederqvist  <ceder@shafan.lysator.liu.se>
 
+	New options --continue and --only-clone.  Better remote watching
+	of the status.
+
+	* backup-one-task: Handle --only-clone.  Adjusted to the new CLI
+	of lysrdiff-set-status.
+
+	* lysrdiff-set-status.py (open_socket): New function.
+	(sendmsg): Use it to simplify code.
+	(completion): New function.
+	(Top level): New mandatory first argument: --status, --ok,
+	--warning or --fail.
+
+	* backup-all: Added --continue and --only-clone.
+
+	* lysrdiff-monitord.py (now): New function.
+	(Client.__init__): New API: use sock and peer arguments instead of
+	accept_retval.  Renamed the input buffer from __readbuf to
+	_readbuf so that derived classes can use it.
+	(Client.read_event): Fixed misnamed errno name.  Track name change
+	of _readbuf.
+	(Client.got_eof): Track name change of _readbuf.
+	(Client.write_event): Handle EAGAIN, ETIMEDOUT and EHOSTUNREACH
+	errors.
+	(Client.parse): Track name change of _readbuf.
+	(Client.handle_cmd): New command: completed.
+	(Client.dispatcher): New method.
+	(Server.read_event): Track API change of Client.  Set the new
+	client socket nonblocking.
+	(Vt100Client.__init__): Negotiate telnet to get a raw byte channel
+	using WILL ECHO, WILL SuppressGoahead, DO SuppressGoahead.  Use
+	vt100 escape sequences to disable line wrap and query the screen
+	size.  Don't register with the dispatcher.
+	(Vt100Client.parse): New method.  Register with the dispatcher
+	once the screen size report has arrived from the client.
+	(Dispatcher.toploop): Write the curren time to vt100 clients every
+	second.
+	(Dispatcher.register_vt100): Position the cursor at the command
+	line position.
+	(Dispatcher.write_monitor_status): Format the status separately
+	for each client, as their screen sizes may vary.
+	(Dispatcher.format_monitor_status): New argument: client.
+	Position the cursor properly.
+	(Dispatcher.set_status): Use now() to simplify code.  Don't crash
+	if a client connection is found to be broken.  Format the status
+	separately for each client, as their screen sizes may vary.
+	(Dispatcher.format_status_vt100): New argument: client.  Truncate
+	the status to the line length reported by the client.  Position
+	the cursor at the command line position.
+	(Dispatcher.completed): New method.
+
 	More categories.
 	* fetch-backup-work: New categories: quota1, shafan and vesuvius.
 
diff --git a/backup-all b/backup-all
index 4057a8128efecc4aeba91e985600decb59477485..5a2e3d341bc8b0f3e80cf936036a8cde913005e7 100755
--- a/backup-all
+++ b/backup-all
@@ -5,12 +5,16 @@ usage () {
   echo ' --failed   Only run backup jobs that failed.' >&2
   echo ' --retry    Only run backup jobs that produced warnings.' >&2
   echo ' --new      Only run backup jobs that have never completed.' >&2
+  echo ' --continue Resume last run.' >&2
+  echo ' --only-clone Only clone the jobs.' >&2
 }
 
 failed=
 retry=
 new=
 only_prune=
+only_clone=
+cont=0
 
 while true
 do
@@ -27,6 +31,12 @@ do
     x--only-prune)
       shift
       only_prune=--only-prune;;
+    x--only-clone)
+      shift
+      only_clone=--only-clone;;
+    x--continue)
+      shift
+      cont=1;;
     x--*)
       usage
       exit 1;;
@@ -66,7 +76,10 @@ do
   fi
 done
 
-/opt/LYSrdiff/bin/distribute-tasks $PARTS
+if [ $cont = 0 ]
+then
+    /opt/LYSrdiff/bin/distribute-tasks $PARTS
+fi
 
 total=0
 for lysrdiffpart in $PARTS
@@ -81,6 +94,13 @@ for lysrdiffpart in $PARTS
 do
   disk=`echo $lysrdiffpart|sed sx/.*xx`
   part=`echo $lysrdiffpart|sed sx.*/xx`
+  if [ $cont = 1 ]
+  then
+      skipuntil=`cat /opt/LYSrdiff/var/running-task-$disk-$part`
+  else
+      skipuntil=0
+  fi
+
   while read category subcategory server serverpath
   do
 
@@ -100,10 +120,18 @@ do
 	echo Hold file disappeared.  Continuing. >&2
     fi
 
-    /opt/LYSrdiff/bin/backup-one-task \
-      $failed $retry $new $only_prune \
-      $disk $part "$category" "$subcategory" "$server" "$serverpath" \
-      "$ctr/$total $disk/$part $category $subcategory"
+    if [ $ctr -lt $skipuntil ]
+    then
+	echo Skipping $ctr/$total $category $subcategory
+    else
+        /opt/LYSrdiff/bin/backup-one-task \
+          $failed $retry $new $only_prune $only_clone \
+          $disk $part "$category" "$subcategory" "$server" "$serverpath" \
+          "$ctr/$total $disk/$part $category $subcategory"
+
+        echo $ctr > /opt/LYSrdiff/var/running-task-$disk-$part
+
+    fi
     ctr=`expr $ctr + 1`
   done < /lysrdiff/$disk/perm/$part/lysrdiff/tasks
 done
diff --git a/backup-one-task b/backup-one-task
index 438bc51dcc70e2b1c06b79e77994576b712e6ee1..d865571df21f5014f1eb839fa1aec42e84995d94 100755
--- a/backup-one-task
+++ b/backup-one-task
@@ -8,6 +8,7 @@ usage () {
     echo ' --retry      Only run backups with output from rdiff-backup' >&2
     echo ' --new        Only run backups that never completed' >&2
     echo ' --only-prune Only prune old increments' >&2
+    echo ' --only-clone Only clone to copy disks' >&2
     echo 'Backups are always performed to the "perm" disk.' >&2
     echo 'Once the backup is finished, it is cloned to all mounted copies.' >&2
 }
@@ -16,6 +17,7 @@ failed=0
 retry=0
 new=0
 only_prune=0
+only_clone=0
 
 ss=/opt/LYSrdiff/bin/lysrdiff-set-status.py
 
@@ -34,6 +36,9 @@ do
     x--only-prune)
       shift
       only_prune=1;;
+    x--only-clone)
+      shift
+      only_clone=1;;
     x--*)
       usage
       exit 1;;
@@ -122,152 +127,163 @@ unset SSH_ASKPASS
 unset SSH_AUTH_SOCK
 
 CLR='\e[2K\r'
-
 AGE=120D
 
-echo -ne "${CLR}${msg}: removing increments older than $AGE"
-$ss $disk $part "${msg}: removing increments older than $AGE"
+if [ $only_clone = 0 ]
+then
+
+    echo -ne "${CLR}${msg}: removing increments older than $AGE"
+    $ss --status $disk $part "${msg}: removing increments older than $AGE"
 
-/opt/LYSrdiff/bin/rdiff-backup \
-    --remove-older-than $AGE \
-    --null-separator \
-    --force \
-    -v \
-    "$files" > "$rdifflogfile" 2>&1
+    /opt/LYSrdiff/bin/rdiff-backup \
+        --remove-older-than $AGE \
+        --null-separator \
+        --force \
+        -v \
+        "$files" > "$rdifflogfile" 2>&1
+fi
 
 if [ $only_prune = 1 ]
 then
     rmdir "$lockdir"
-    $ss $disk $part ""
+    $ss --status $disk $part ""
     exit 0
 fi
 
-echo -ne "${CLR}${msg}: fetching exclude file"
-$ss $disk $part "${msg}: fetching exclude file"
-
-# Fetch an up-to-date exclude file.
-rm -f "$exclude"
-scp -B -q -i /root/.ssh/backupkey \
-    "$server":"$serverpath"/.lysrdiff-exclude "$exclude" >/dev/null 2>&1
-# Create an emtpy exclude file if non existed on the server.
-touch "$exclude"
-
-sed < "$exclude" \
-    -e 's%^\([-+] \|\)\([^/]\)%\1'"$serverpath"'/\2%' \
-    -e 's%^\([-+] \|\)//%\1/%' \
-| tr '\n' '\0' > "$excl_abs"
-
-
-# See how long time the previous backup took.
-if [ -f "$statebase"--start ] && [ -f "$statebase"--end ]
+if [ $only_clone = 0 ]
 then
-    startdec="`stat --format '%Y' \"$statebase\"--start`"
-    enddec="`stat --format '%Y' \"$statebase\"--end`"
-    seconds=`expr $enddec - $startdec`
-    ETA=`date +' (ETA: %H:%M:%S)' -d "$seconds seconds"`
-else
-    ETA=""
-fi
-
-touch "$base"/backup-attempt-start
-touch "$statebase"--attempt
-
-echo -ne "${CLR}${msg}: running rdiff-backup$ETA"
-$ss $disk $part "${msg}: rdiff-backup$ETA"
+    echo -ne "${CLR}${msg}: fetching exclude file"
+    $ss --status $disk $part "${msg}: fetching exclude file"
 
-schema="ssh -o BatchMode=yes -o ServerAliveInterval=120"
-schema="$schema -a -k -x -i /root/.ssh/backupkey"
-schema="$schema %s $remoterdiff --server"
+    # Fetch an up-to-date exclude file.
+    rm -f "$exclude"
+    scp -B -q -i /root/.ssh/backupkey \
+        "$server":"$serverpath"/.lysrdiff-exclude "$exclude" >/dev/null 2>&1
+    # Create an emtpy exclude file if non existed on the server.
+    touch "$exclude"
 
-/opt/LYSrdiff/bin/rdiff-backup \
-    --exclude-other-filesystems --null-separator \
-    --include-globbing-filelist "$excl_abs" \
-    --remote-schema "$schema" \
-    --force \
-    "$server"::"$serverpath" "$files" > "$rdifflogfile" 2>&1
-exit=$?
+    sed < "$exclude" \
+        -e 's%^\([-+] \|\)\([^/]\)%\1'"$serverpath"'/\2%' \
+        -e 's%^\([-+] \|\)//%\1/%' \
+    | tr '\n' '\0' > "$excl_abs"
 
-echo -ne "${CLR}"
-$ss $disk $part "${msg}: updating status files"
 
-if [ $exit = 0 ]
-then
-    touch "$base"/last-good-backup
-    mv "$base"/backup-attempt-start "$base"/last-good-start
-    touch "$statebase"--end
-    mv "$statebase"--attempt "$statebase"--start
-
-    starttime="`stat --format '%y' \"$statebase\"--start|sed 's/\..*//'`"
-    endtime="`stat --format '%y' \"$statebase\"--end|sed 's/\..*//'`"
-    startdec="`stat --format '%Y' \"$statebase\"--start`"
-    enddec="`stat --format '%Y' \"$statebase\"--end`"
+    # See how long time the previous backup took.
+    if [ -f "$statebase"--start ] && [ -f "$statebase"--end ]
+    then
+        startdec="`stat --format '%Y' \"$statebase\"--start`"
+        enddec="`stat --format '%Y' \"$statebase\"--end`"
+        seconds=`expr $enddec - $startdec`
+        ETA=`date +' (ETA: %H:%M:%S)' -d "$seconds seconds"`
+    else
+        ETA=""
+    fi
 
-    echo -ne "${CLR}${msg}: running du"
-    $ss $disk $part "${msg}: running du"
+    touch "$base"/backup-attempt-start
+    touch "$statebase"--attempt
 
-    sizes=""
+    echo -ne "${CLR}${msg}: running rdiff-backup$ETA"
+    $ss --status $disk $part "${msg}: rdiff-backup$ETA"
 
-    if [ -d "$files" ]
-    then
-	totalsize=`du -ks "$files" | awk '{print $1}'`
-	sizes="$sizes totalsize=$totalsize"
-	echo $totalsize > "$base"/totalsize
-    fi
+    schema="ssh -o BatchMode=yes -o ServerAliveInterval=120"
+    schema="$schema -a -k -x -i /root/.ssh/backupkey"
+    schema="$schema %s $remoterdiff --server"
 
-    if [ -d "$rdiffdir" ]
-    then
-	metasize=`du -ks "$rdiffdir" --exclude increments | awk '{print $1}'`
-	sizes="$sizes metasize=$metasize"
-	echo $metasize > "$base"/metasize
-    fi
-	
-    if [ -d "$incrementsdir" ]
-    then
-	incrementsize=`du -ks "$incrementsdir" | awk '{print $1}'`
-	sizes="$sizes incrementsize=$incrementsize"
-	echo $incrementsize > "$base"/incrementsize
-    fi
+    /opt/LYSrdiff/bin/rdiff-backup \
+        --exclude-other-filesystems --null-separator \
+        --include-globbing-filelist "$excl_abs" \
+        --remote-schema "$schema" \
+        --force \
+        "$server"::"$serverpath" "$files" > "$rdifflogfile" 2>&1
+    exit=$?
 
     echo -ne "${CLR}"
-    $ss $disk $part "${msg}: updating log file"
-
-    time=`expr $enddec - $startdec`
-    echo $time > "$base"/time
-    echo $starttime $endtime time=$time"$sizes" \
-	>> "$summaryfile"
+    $ss --status $disk $part "${msg}: updating status files"
 
-    rm -f "$base"/last-failure
-    rm -f "$statebase"--fail
-    if [ `wc -c < "$rdifflogfile"` = 0 ]
+    if [ $exit = 0 ]
     then
-      touch "$statebase"--nowarn
-      touch "$base"/last-nowarn-backup
-      rm -f "$statebase"--neverwarnfree
-      rm -f "$statebase"--warn
-      logger -p local5.info -t LYSrdiff "$lysrdiffpart $category $subcategory: OK"
+        touch "$base"/last-good-backup
+        mv "$base"/backup-attempt-start "$base"/last-good-start
+        touch "$statebase"--end
+        mv "$statebase"--attempt "$statebase"--start
+    
+        starttime="`stat --format '%y' \"$statebase\"--start|sed 's/\..*//'`"
+        endtime="`stat --format '%y' \"$statebase\"--end|sed 's/\..*//'`"
+        startdec="`stat --format '%Y' \"$statebase\"--start`"
+        enddec="`stat --format '%Y' \"$statebase\"--end`"
+    
+        echo -ne "${CLR}${msg}: running du"
+        $ss --status $disk $part "${msg}: running du"
+    
+        sizes=""
+    
+        if [ -d "$files" ]
+        then
+    	totalsize=`du -ks "$files" | awk '{print $1}'`
+    	sizes="$sizes totalsize=$totalsize"
+    	echo $totalsize > "$base"/totalsize
+        fi
+    
+        if [ -d "$rdiffdir" ]
+        then
+    	metasize=`du -ks "$rdiffdir" --exclude increments | awk '{print $1}'`
+    	sizes="$sizes metasize=$metasize"
+    	echo $metasize > "$base"/metasize
+        fi
+    	
+        if [ -d "$incrementsdir" ]
+        then
+    	incrementsize=`du -ks "$incrementsdir" | awk '{print $1}'`
+    	sizes="$sizes incrementsize=$incrementsize"
+    	echo $incrementsize > "$base"/incrementsize
+        fi
+    
+        echo -ne "${CLR}"
+        $ss --status $disk $part "${msg}: updating log file"
+    
+        time=`expr $enddec - $startdec`
+        echo $time > "$base"/time
+        echo $starttime $endtime time=$time"$sizes" \
+    	>> "$summaryfile"
+    
+        rm -f "$base"/last-failure
+        rm -f "$statebase"--fail
+        if [ `wc -c < "$rdifflogfile"` = 0 ]
+        then
+          touch "$statebase"--nowarn
+          touch "$base"/last-nowarn-backup
+          rm -f "$statebase"--neverwarnfree
+          rm -f "$statebase"--warn
+          logger -p local5.info -t LYSrdiff "$lysrdiffpart $category $subcategory: OK"
+          $ss --ok $disk $part "$category" "$subcategory"
+        else
+          echo ${msg}: WARNING: output from rdiff-backup:
+          echo
+          sed 's/^/ /' "$rdifflogfile"
+          echo
+          if [ ! -f "$statebase"--warn ]
+          then
+            touch "$statebase"--warn
+          fi
+          if [ ! -f "$statebase"--nowarn ] && [ ! -f "$statebase"--neverwarnfree ]
+          then
+	      touch "$statebase"--neverwarnfree
+          fi
+          logger -p local5.info -t LYSrdiff \
+	      "$lysrdiffpart $category $subcategory: OK with warnings"
+          $ss --warn $disk $part "$category" "$subcategory"
+        fi
     else
-      echo ${msg}: WARNING: output from rdiff-backup:
-      echo
-      sed 's/^/ /' "$rdifflogfile"
-      echo
-      if [ ! -f "$statebase"--warn ]
-      then
-        touch "$statebase"--warn
-      fi
-      if [ ! -f "$statebase"--nowarn ] && [ ! -f "$statebase"--neverwarnfree ]
-      then
-	touch "$statebase"--neverwarnfree
-      fi
-      logger -p local5.info -t LYSrdiff "$lysrdiffpart $category $subcategory: OK with warnings"
+        mv "$base"/backup-attempt-start "$base"/last-failure
+        mv "$statebase"--attempt "$statebase"--fail
+        echo ${msg}: FAIL:
+        echo
+        sed 's/^/  /' "$rdifflogfile"
+        echo
+        logger -p local5.info -t LYSrdiff \
+	    "$lysrdiffpart $category $subcategory: FAIL"
+	$ss --fail $disk $part "$category" "$subcategory"
     fi
-else
-    mv "$base"/backup-attempt-start "$base"/last-failure
-    mv "$statebase"--attempt "$statebase"--fail
-    echo ${msg}: FAIL:
-    echo
-    sed 's/^/  /' "$rdifflogfile"
-    echo
-    logger -p local5.info -t LYSrdiff "$lysrdiffpart $category $subcategory: FAIL"
 fi
 
 for copy in A B
@@ -292,7 +308,7 @@ do
 	    if mkdir "$copybase"/lock
 	    then
 		echo -ne "${CLR}${msg}: copying to $copy"
-		$ss $disk $part "${msg}: copying to $copy"
+		$ss --status $disk $part "${msg}: copying to $copy"
 
     	        rsync -a --delete "$base"/ "$copybase"
 	        rm -f "$copystatebase"--* || true
@@ -306,6 +322,6 @@ do
     fi
 done
 
-$ss $disk $part ""
+$ss --status $disk $part ""
 
 rmdir "$lockdir"
diff --git a/lysrdiff-monitord.py b/lysrdiff-monitord.py
index 34207d2ec9cdd99dedd402f688001482c07f95da..49523d8a79965fa77389ac05809a621e61e70e46 100755
--- a/lysrdiff-monitord.py
+++ b/lysrdiff-monitord.py
@@ -1,18 +1,23 @@
 #!/usr/bin/env python
 
+import re
 import socket
+import errno
 import select
 import sys
 import time
 
+def now():
+    return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
+
 class Client(object):
-    def __init__(self, accept_retval, dispatcher):
-        self.__s, peer = accept_retval
+    def __init__(self, sock, peer, dispatcher):
+        self.__s = sock
         self.__peer_ip = peer[0]
         self.__peer_port = peer[1]
         dispatcher.register(self)
         self.__writebuf = ""
-        self.__readbuf = ""
+        self._readbuf = ""
         self.__got_eof = False
         self.__dispatcher = dispatcher
 
@@ -35,7 +40,7 @@ class Client(object):
         try:
             bytes = self.__s.recv(100)
         except socket.error, e:
-            if e[0] == errno.ECONNTIMEOUT:
+            if e[0] == errno.ETIMEDOUT:
                 self.got_eof(True)
                 return
             raise
@@ -43,32 +48,43 @@ class Client(object):
             self.got_eof()
             return
 
-        self.__readbuf += bytes
+        self._readbuf += bytes
         self.parse()
 
     def got_eof(self, error=False):
         self.__got_eof = True
         if not error:
             self.parse()
-        if len(self.__readbuf) > 0:
+        if len(self._readbuf) > 0:
             sys.stderr.write("EOF with %d bytes pending from client.\n" % (
-                len(self.__readbuf)))
+                len(self._readbuf)))
         if len(self.__writebuf) == 0 or error:
             self.__dispatcher.unregister(self)
             self.__s.close()
             del self
 
     def write_event(self):
-        rv = self.__s.send(self.__writebuf)
+	try:
+            rv = self.__s.send(self.__writebuf)
+        except socket.error, e:
+	    if e[0] == errno.EAGAIN:
+	        rv = 0
+		# Give the rest of the system a chance to recover.
+		time.sleep(0.05)
+	    elif e[0] in [errno.ETIMEDOUT, errno.EHOSTUNREACH]:
+		self.got_eof(True)
+		return
+	    else:
+		raise
         if rv > 0:
             self.__writebuf = self.__writebuf[rv:]
 
     def parse(self):
-        ix = self.__readbuf.find("\n")
+        ix = self._readbuf.find("\n")
         if ix < 0:
             return
-        cmd = self.__readbuf[:ix]
-        self.__readbuf = self.__readbuf[ix+1:]
+        cmd = self._readbuf[:ix]
+        self._readbuf = self._readbuf[ix+1:]
         self.handle_cmd(cmd)
 
     def handle_cmd(self, cmd):
@@ -83,6 +99,11 @@ class Client(object):
                 cmd, disk, part, status = split
             dispatcher.set_status(int(disk), int(part), status)
 
+        elif cmd.startswith("completed "):
+            cmd, disk, part, category, subcategory, outcome = cmd.split()
+	    dispatcher.completed(int(disk), int(part), category, subcategory, 
+			         outcome)
+
         else:
             sys.stderr.write("Unknown command received from client.\n")
 
@@ -90,6 +111,10 @@ class Client(object):
         self.__writebuf += buf
         self.write_event()
 
+    def dispatcher(self):
+	return self.__dispatcher
+
+
 class Server(object):
     IP = '127.0.0.1'
     PORT = 9933
@@ -114,16 +139,47 @@ class Server(object):
         return self.__s.fileno()
 
     def read_event(self):
-        self.CLIENT(self.__s.accept(), self.__dispatcher)
+	sock, peer = self.__s.accept()
+	sock.setblocking(False)
+        self.CLIENT(sock, peer, self.__dispatcher)
 
 class Vt100Client(Client):
 
-    def handle_cmd(self, cmd):
-        pass
+    def __init__(self, sock, peer, dispatcher):
+        Client.__init__(self, sock, peer, dispatcher)
+	self.write("\377\373\001") # IAC WILL ECHO
+	self.write("\377\373\003") # IAC WILL SuppressGoahead
+	self.write("\377\375\003") # IAC DO SuppressGoahead
+	self.write("\033[7l")      # Disable line wrap.
+	self.write("\033[r")	   # Scroll entire screen.
+	self.write("\033[999;999H") # Move to row 999, col 999.
+	self.write("\033[6n")	   # Report cursor position.
+	self.rows = None
+	self.cols = None
+
+    def parse(self):
+	print "Got data", repr(self._readbuf)
+	while True:
+            ix = self._readbuf.find("\033")
+	    if ix < 0:
+		print "Discarding"
+	        self._readbuf = ""
+	        return
+	    m = re.match("\033\\[(?P<row>\\d+);(?P<col>\\d+)R", 
+			 self._readbuf[ix:])
+	    if m is None:
+		print "ESC at", ix, "but discarding it"
+		self._readbuf = self._readbuf[ix+1:]
+	    else:
+		break
+	print "Got something nice"
+	self.rows = int(m.group("row"))
+	self.cols = int(m.group("col"))
+	self._readbuf = self._readbuf[ix+1:]
+	print "Got", self.rows, "rows and", self.cols, "cols"
+	sys.stdout.flush()
+        self.dispatcher().register_vt100(self)
 
-    def __init__(self, accept_retval, dispatcher):
-        Client.__init__(self, accept_retval, dispatcher)
-        dispatcher.register_vt100(self)
 
 class Vt100Server(Server):
     IP = ''
@@ -147,6 +203,7 @@ class Dispatcher(object):
         self.write_monitor_status()
 
     def toploop(self):
+	prev_ts = now()
         while True:
             r = []
             w = []
@@ -156,7 +213,14 @@ class Dispatcher(object):
                 r += c.want_read()
                 w += c.want_write()
 
-            r, w, e = select.select(r, w, [], None)
+	    t = time.time()
+	    maxwait = 1 - (t - int(t)) + 0.05
+	    if maxwait < 0:
+		maxwait = 0
+
+            r, w, e = select.select(r, w, [], maxwait)
+
+	    ts = now()
 
             for c in w:
                 if self.__break:
@@ -168,54 +232,65 @@ class Dispatcher(object):
                     break
                 c.read_event()
 
+	    if ts != prev_ts:
+		for c in list(self.__vt100):
+		    c.write("\033[1;%dH%s\033[%d;1H" % (
+			c.cols - len(ts), ts, c.rows - 1))
+
     def register_vt100(self, client):
         self.__vt100.add(client)
 
         msg = "\033[2J\033[HLYSrdiff status\r\n==============="
         for disk in self.__status.keys():
             for part in self.__status[disk].keys():
-                msg += self.format_status_vt100(disk, part)
+                msg += self.format_status_vt100(disk, part, client)
+	msg += "\033[%d;%dr" % (9, client.rows - 2)
         client.write(msg)
         self.write_monitor_status()
 
     def write_monitor_status(self):
-        msg = self.format_monitor_status()
-        for c in self.__vt100:
+        for c in list(self.__vt100):
+	    msg = self.format_monitor_status(c)
             c.write(msg)
 
-    def format_monitor_status(self):
-        msg = "\033[9;1H"
+    def format_monitor_status(self, client):
+        msg = "\033[%d;1H" % client.rows
         msg += "%d clients" % len(self.__vt100)
         msg += "\033[K"
-        msg += "\033[H"
+        msg += "\033[%d;1H" % (client.rows - 1)
         return msg
 
     def set_status(self, disk, part, status):
         if disk not in self.__status:
             self.__status[disk] = {}
 
-        timestamp = time.strftime("%Y-%m-%d %H:%M:%S",
-                                  time.localtime(time.time()))
+        timestamp = now()
         self.__status[disk][part] = (timestamp, status)
 
-        msg = self.format_status_vt100(disk, part)
-        for c in self.__vt100:
+        for c in list(self.__vt100):
+	    msg = self.format_status_vt100(disk, part, c)
             c.write(msg)
 
         print timestamp, "%d/%d" % (disk, part), status
 
-    def format_status_vt100(self, disk, part):
+    def format_status_vt100(self, disk, part, client):
         timestamp, status = self.__status[disk][part]
         
         msg = "\033[%d;1H" % (2 + 2 * disk + part)
-        msg += timestamp
-        msg += " "
-        msg += status
+        msg += (timestamp + " " + status)[:client.cols]
         msg += "\033[K"
-        msg += "\033[H"
+        msg += "\033[%d;1H" % (client.rows - 1)
 
         return msg
 
+    def completed(self, disk, part, category, subcategory, outcome):
+	msg = "%s %d/%d %s %s %s" % (now(), disk, part, outcome, 
+				     category, subcategory)
+	for c in list(self.__vt100):
+	    c.write("\033[%d;1H\r\n%s\033[%d;1H" % (c.rows - 2,
+						    msg,
+						    c.rows - 1))
+
 if __name__ == '__main__':
     dispatcher = Dispatcher()
     Server(dispatcher)
diff --git a/lysrdiff-set-status.py b/lysrdiff-set-status.py
index c88e9d1b37fd983175f48996eb78da4bdd521fa7..c94e509824d64ea23b37b248740c9126e6dd01b8 100755
--- a/lysrdiff-set-status.py
+++ b/lysrdiff-set-status.py
@@ -4,16 +4,41 @@ import sys
 import errno
 import socket
 
-def sendmsg(disk, part, msg):
+def open_socket():
     s = socket.socket()
     try:
         s.connect(("127.0.0.1", 9933))
     except socket.error, e:
         if e[0] == errno.ECONNREFUSED:
-            return
+            return None
         raise
+    return s
+
+def sendmsg(disk, part, msg):
+    s = open_socket()
+    if s is None:
+	return
     s.send("set-status %d %d %s\n" % (disk, part, msg))
     s.close()
 
+def completion(disk, part, category, subcategory, outcome):
+    s = open_socket()
+    if s is None:
+	return
+    s.send("completed %d %d %s %s %s\n" % (disk, part, 
+					   category, subcategory, 
+					   outcome))
+    s.close()
+
 if __name__ == '__main__':
-    sendmsg(int(sys.argv[1]), int(sys.argv[2]), sys.argv[3])
+    if sys.argv[1] == "--status":
+        sendmsg(int(sys.argv[2]), int(sys.argv[3]), sys.argv[4])
+    if sys.argv[1] == "--ok":
+        completion(int(sys.argv[2]), int(sys.argv[3]), 
+		   sys.argv[4], sys.argv[5], "OK")
+    if sys.argv[1] == "--warning":
+        completion(int(sys.argv[2]), int(sys.argv[3]), 
+		   sys.argv[4], sys.argv[5], "WARN")
+    if sys.argv[1] == "--fail":
+        completion(int(sys.argv[2]), int(sys.argv[3]), 
+		   sys.argv[4], sys.argv[5], "FAIL")