perl version of histogram prog
[tridge/junkcode.git] / mail.runner
index 17657797647d728545519dd1f6a62da270fea603..5423c1ec1353718602f47e808c3eff6befb15b49 100755 (executable)
 #!/bin/bash
+
 ###################
 # a simple mail delivery and transmission system
 # Copyright Andrew Tridgell <tridge@samba.org> 1997-2002
 # released under the GNU General Public License version 2 or later
 
+VERSION="1.2"
+
 # setup the default configuration
 MAIL_SSH="ssh"
 MAIL_DIR="Mail"
+MAIL_INBOX="$HOME/InBox"
 MAIL_RUNNER="mail.runner"
 SENDMAIL="/usr/lib/sendmail"
-SENDMAIL_OPTS=""
+SENDMAIL_OPTS="-t -i"
+RSYNC_OPTS="-Pavz"
 MAIL_TIMEOUT=300
-VERSION="1.0"
+PROCMAIL_COMMAND="formail -s procmail"
+MAIL_VERBOSE=2
+MAIL_NO_PIDOF=0
 
 # sensible paths are often not setup when invoking commands remotely
-export PATH=$PATH:$HOME/bin:/usr/local/bin:/usr/bin:/bin
+export PATH=$PATH:$HOME/bin:/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin
+
+
+# define an empty sendmail hook - replace this in .mail.runner for 
+# interesting multi-host tricks
+mail_sendmail_hook() {
+ msg="$1"
+}
+
 
 # load the user specific config
-. $HOME/.mail.runner
+if [ -f $HOME/.mail.runner ]; then
+    HAVE_MAIL_RUNNER_CONFIG=1
+    . $HOME/.mail.runner
+else 
+    echo "YOU MUST HAVE A .mail.runner config!" 1>&2
+    HAVE_MAIL_RUNNER_CONFIG=0
+fi
 
-# make sure the necessary variables are defined
-if [ "$MAIL_HOST" = "" ]; then
-    echo "You must define a MAIL_HOST in $HOME/.mail.runner" 1>&2
-    echo "Use -h for more help" 1>&2
-    exit 1
+if [ ! -z "$MAIL_RUNNER_CONF" -a -r "$MAIL_RUNNER_CONF" ]; then
+ . $MAIL_RUNNER_CONF
+fi
+
+if [ -z "$MAIL_OUT_DIR" ]; then
+    MAIL_OUT_DIR="$MAIL_DIR"
 fi
 
 
+
+# processing now continues in MAIN SCRIPT section at bottom of file
+
+
+##############################################################
+# print a message if the level <= $MAIL_VERBOSE
+debug_msg() {
+    level=$1;
+    if [ $level -le $MAIL_VERBOSE ]; then
+       message="$2";
+       echo "$message";
+    fi
+}
+
+##################################
+# print an error message to stderr
+error_msg() {
+    echo "$1" 1>&2;
+}
+
+
+##############################################################
+# try to make sure two copies of this script don't run at once
+exit_if_running() {
+    if [ "$MAIL_NO_PIDOF" == 1 ]; then
+       return;
+    fi
+    pids=`pidof -x -o $$ mail.runner`;
+    myid=`id -u`;
+    for p in $pids; do
+       pid_owner=`stat -c"%u" /proc/$p/stat`
+       if [ x$pid_owner = x$myid ]; then
+           echo "mail.runner is already running - pid $p"
+           exit 1
+        fi
+    done
+}
+
+
+
+
 ###############################################################################
 # send all pending mail, invoking mail.deliver at the other end
 mail_send() {
-    list=`echo $HOME/$MAIL_DIR/mail.out.*.*`
-    echo
+    exit_if_running
+
+    list=`echo $HOME/$MAIL_OUT_DIR/mail.out.*.*[0-9]`
+    debug_msg 2 "";
     if [ "$list" = "" ]; then
-       echo "No messages to send";
+       debug_msg 2 "No messages to send";
        return 0;
     fi
-    echo "Sending `echo $list | wc -w` messages"
+    debug_msg 1 "Sending `echo $list | wc -w` messages";
 
     if rsync --rsync-path="$MAIL_RUNNER remote_deliver" \
-       --timeout=$MAIL_TIMEOUT -Pavze $MAIL_SSH \
+       --timeout=$MAIL_TIMEOUT $RSYNC_OPTS -e "$MAIL_SSH" \
        $list $MAIL_HOST:$MAIL_DIR/; then
          rm -f $list
     fi
@@ -49,21 +114,23 @@ mail_send() {
 ###############################################################################
 # fetch mail from the remote host and deliver it via procmail
 mail_fetch() {
-    if ! rsync --timeout=$MAIL_TIMEOUT -Pavze $MAIL_SSH \
+    exit_if_running
+
+    if ! rsync --timeout=$MAIL_TIMEOUT $RSYNC_OPTS -e "$MAIL_SSH" \
            --rsync-path="$MAIL_RUNNER remote_send" \
            $MAIL_HOST:$MAIL_DIR/EMPTY_FILE $HOME/$MAIL_DIR/; then
-       echo transfer failed;
+       error_msg "transfer failed";
        return 1;
     fi
 
     if [ "`echo $HOME/$MAIL_DIR/mail.in.*.*`" = "" ]; then 
-       echo
-       echo "No mail to retrieve"
+       debug_msg 1 "";
+       debug_msg 1 "No mail to retrieve";
        return 1
     fi
 
     for f in $HOME/$MAIL_DIR/mail.in.*.*; do
-       if cat $f | formail -s procmail; then
+       if cat $f | $PROCMAIL_COMMAND; then
            # it is useful seeing what mail is being processed
            if [ "$MAIL_FRM" = "1" ]; then
                frm $f
@@ -74,7 +141,9 @@ mail_fetch() {
 
     # this is useful for seeing where procmail has put things ...
     if [ "$MAIL_PROCMAIL_TAIL" = "1" ]; then
-       echo ; echo ; tail $HOME/$MAIL_DIR/procmail.log
+       debug_msg 2 "";
+       debug_msg 2 "";
+       debug_msg 2 `tail $HOME/$MAIL_DIR/procmail.log`;
     fi
 
     return 0;
@@ -85,7 +154,11 @@ mail_fetch() {
 # a sendmail-like call. This just places the mail in a known location for 
 # mail_send to find
 mail_sendmail() {
-    cat > $HOME/$MAIL_DIR/mail.out.$RANDOM.$$
+    msg=$HOME/$MAIL_DIR/mail.out.$RANDOM.$$
+    tmp=$msg.tmp
+    cat > $tmp
+    /bin/mv $tmp $msg
+    mail_sendmail_hook $msg
     exit 0
 }
 
@@ -93,6 +166,8 @@ mail_sendmail() {
 ##############################################################################
 # a rsync wrapper function that delivers mail on the remote host
 mail_remote_deliver() {
+    exit_if_running
+
     rsync $*
     status=$?
 
@@ -100,11 +175,11 @@ mail_remote_deliver() {
        return $status;
     fi
 
-    for f in $HOME/$MAIL_DIR/mail.out.*.*; do
+    for f in $HOME/$MAIL_DIR/mail.out.*.*[0-9]; do
            if $SENDMAIL $SENDMAIL_OPTS < $f; then
                rm -f $f
            else
-               echo Mail of $f failed $status 1>&2
+               error_msg "Mail of $f failed $status";
            fi
     done
     return 0
@@ -113,9 +188,11 @@ mail_remote_deliver() {
 ###############################################################################
 # a rsync wrapper function for fetching mail from the remote host
 mail_remote_send() {
+    exit_if_running
+
     # atomically move mail from InBox to a temporary file
-    if test -s $HOME/InBox ; then
-       movemail $HOME/InBox $HOME/$MAIL_DIR/mail.in.$RANDOM.$$ > /dev/null
+    if test -s $MAIL_INBOX; then
+       movemail $MAIL_INBOX $HOME/$MAIL_DIR/mail.in.$RANDOM.$$ > /dev/null
     fi
 
     # by using an empty file we avoid error messages from rsync about not
@@ -134,6 +211,17 @@ mail_remote_send() {
     return 0
 }
 
+###############################
+# show the outgoing mail queue
+###############################
+mail_queue() {
+    for f in $HOME/$MAIL_DIR/mail.out.*.*; do
+        echo `basename $f`:
+        egrep '^(From|To|Subject):' $f | sed 's/^/      /'
+        echo
+    done
+}
+
 
 ###############################################################################
 # display help text
@@ -150,7 +238,7 @@ FEATURES
 The main features of mail.runner are
 
 - good performance over very slow links
-- secure transfer via ssh
+- secure mail transfer via rsync and ssh
 - incremental transfer in case the link is lost
 - no mta installation needed on the client
 
@@ -160,13 +248,13 @@ REQUIREMENTS
 
 To use mail.runner you need the following:
 
-- a working rsync and ssh installation
+- a working 'rsync' and 'ssh' installation
 - the 'bash' shell
 - the 'movemail' utility
 - the 'formail' utility
 - the Linux 'pidof' utility
-- a working procmail installation for mail delivery on the client
-- a working sendmail (or equivalent) installation on the server for sending 
+- a working 'procmail' installation for mail delivery on the client
+- a working 'sendmail' (or equivalent) installation on the server for sending 
   email
 - optionally you may use the 'frm' utility
 
@@ -185,10 +273,33 @@ To install mail.runner you need to do the following:
 How you do the last step depends on your email program. I use RMAIL in
 emacs to read and send email, so I used the following in my .emacs:
   (setq sendmail-program "~/bin/mail.sendmail")
-and then I created a one line script called 'mail.sendmail' that ran
+and then I created a one line script called 'mail.sendmail' that runs
 'mail.runner sendmail'.
 
 
+RUNNING
+-------
+
+Once installed, you should just run 'mail.runner' to fetch any
+outstanding emails from the server and send any pending messages. You
+can also ask mail.runner to just send or fetch by using:
+    mail.runner send
+or
+    mail.runner fetch
+
+Any diagnostics messages should be self explanatory.
+
+EMACS
+-----
+
+If you use an Emacs-based email program then you should set the
+variable mail-interactive to t.  This means that the email program
+will notice when 'mail.runner sendmail' fails to queue a message,
+perhaps if mail.runner exits (with non-0 status) because another
+instance is already running (possibly doing 'mail.runner send').
+Otherwise it may look like 'mail.runner sendmail' has queued a
+message, but it has really been discarded!
+
 CONFIGURATION
 -------------
 
@@ -208,6 +319,10 @@ Other options are:
     both the client and the server. This should be a relative path,
     relative to your home directory.  The default is "Mail".
 
+  MAIL_INBOX
+    This specifies the location of your mail inbox on your mail server.
+    The default is '\$HOME/InBox'
+
   MAIL_RUNNER 
     This specifies the location of the mail.runner script on the
     server, relative to your home directory. The default is "mail.runner".
@@ -232,9 +347,27 @@ Other options are:
 
   MAIL_PROCMAIL_TAIL
     If this is set to "1" then mail.runner will display the last 10
-    lines of your $MAIL_DIR/procmail.log after processing incoming
+    lines of your '\$MAIL_DIR/procmail.log' after processing incoming
     emails.
 
+  RSYNC_OPTS 
+    This defaults to "$RSYNC_OPTS", you may wish to remove the
+    -v and add -q for quieter operation
+
+  PROCMAIL_COMMAND
+    This sets the command used to deliver mail locally. 
+    It defaults to "$PROCMAIL_COMMAND"
+
+  MAIL_VERBOSE
+    Verbosity level:
+      1 - Summary of messages sent and received.
+      2 - Additional whitespace and output when no mail to send.
+
+  MAIL_NO_PIDOF
+    If this is set to "1" no "pidof" checking is done.  You should set
+    this to "1" if you don't have pidof or your pidof is broken in 
+    some way.
+
 
 LICENSE
 -------
@@ -256,14 +389,21 @@ EOF
 # START OF MAIN SCRIPT
 ###############################################################################
 
+# See if user is just after help, if so display and exit
+if [ "$1" = "-h" -o "$1" = "--help" -o $HAVE_MAIL_RUNNER_CONFIG == 0 ]; then
+    mail_help
+    exit 0
+fi
+
 # we rely on expansion to a null list
 shopt -s nullglob
 
-# try to make sure two copies of this script don't run at once
-if pidof -x -o $$ `basename $0` > /dev/null; then
-    echo "`basename $0` is already running" 1>&2
+# make sure the necessary variables are defined
+if [ "$MAIL_HOST" = "" ]; then
+    error_msg "You must define a MAIL_HOST in $HOME/.mail.runner";
+    error_msg "Use -h for more help";
     exit 1
-fi  
+fi
 
 # when an argument is passed then call the corresponding mail_* function 
 # with the remaining arguments. This is used to invoke the right function
@@ -271,12 +411,6 @@ fi
 # the command line
 if [ $# != 0 ]; then
 
-    # explicit check for -h or --help
-    if [ "$1" = "-h" -o "$1" = "--help" ]; then
-       mail_help
-       exit 0
-    fi
-
     cmd=$1
     shift
     mail_$cmd $*