added latency tool
[tridge/junkcode.git] / mail.runner
1 #!/bin/bash
2
3 ###################
4 # a simple mail delivery and transmission system
5 # Copyright Andrew Tridgell <tridge@samba.org> 1997-2002
6 # released under the GNU General Public License version 2 or later
7
8 VERSION="1.2"
9
10 # setup the default configuration
11 MAIL_SSH="ssh"
12 MAIL_DIR="Mail"
13 MAIL_INBOX="$HOME/InBox"
14 MAIL_RUNNER="mail.runner"
15 SENDMAIL="/usr/lib/sendmail"
16 SENDMAIL_OPTS="-t -i"
17 RSYNC_OPTS="-Pavz"
18 MAIL_TIMEOUT=300
19 PROCMAIL_COMMAND="formail -s procmail"
20 MAIL_VERBOSE=2
21 MAIL_NO_PIDOF=0
22
23 # sensible paths are often not setup when invoking commands remotely
24 export PATH=$PATH:$HOME/bin:/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin
25
26
27 # define an empty sendmail hook - replace this in .mail.runner for 
28 # interesting multi-host tricks
29 mail_sendmail_hook() {
30  msg="$1"
31 }
32
33
34 # load the user specific config
35 if [ -f $HOME/.mail.runner ]; then
36     HAVE_MAIL_RUNNER_CONFIG=1
37     . $HOME/.mail.runner
38 else 
39     echo "YOU MUST HAVE A .mail.runner config!" 1>&2
40     HAVE_MAIL_RUNNER_CONFIG=0
41 fi
42
43 if [ ! -z "$MAIL_RUNNER_CONF" -a -r "$MAIL_RUNNER_CONF" ]; then
44  . $MAIL_RUNNER_CONF
45 fi
46
47 if [ -z "$MAIL_OUT_DIR" ]; then
48     MAIL_OUT_DIR="$MAIL_DIR"
49 fi
50
51
52
53 # processing now continues in MAIN SCRIPT section at bottom of file
54
55
56 ##############################################################
57 # print a message if the level <= $MAIL_VERBOSE
58 debug_msg() {
59     level=$1;
60     if [ $level -le $MAIL_VERBOSE ]; then
61         message="$2";
62         echo "$message";
63     fi
64 }
65
66 ##################################
67 # print an error message to stderr
68 error_msg() {
69     echo "$1" 1>&2;
70 }
71
72
73 ##############################################################
74 # try to make sure two copies of this script don't run at once
75 exit_if_running() {
76     if [ "$MAIL_NO_PIDOF" == 1 ]; then
77         return;
78     fi
79     pids=`pidof -x -o $$ mail.runner`;
80     myid=`id -u`;
81     for p in $pids; do
82         pid_owner=`stat -c"%u" /proc/$p/stat`
83         if [ x$pid_owner = x$myid ]; then
84             echo "mail.runner is already running - pid $p"
85             exit 1
86         fi
87     done
88 }
89
90
91
92
93 ###############################################################################
94 # send all pending mail, invoking mail.deliver at the other end
95 mail_send() {
96     exit_if_running
97
98     list=`echo $HOME/$MAIL_OUT_DIR/mail.out.*.*[0-9]`
99     debug_msg 2 "";
100     if [ "$list" = "" ]; then
101         debug_msg 2 "No messages to send";
102         return 0;
103     fi
104     debug_msg 1 "Sending `echo $list | wc -w` messages";
105
106     if rsync --rsync-path="$MAIL_RUNNER remote_deliver" \
107         --timeout=$MAIL_TIMEOUT $RSYNC_OPTS -e "$MAIL_SSH" \
108         $list $MAIL_HOST:$MAIL_DIR/; then
109          rm -f $list
110     fi
111     return 0;
112 }
113
114 ###############################################################################
115 # fetch mail from the remote host and deliver it via procmail
116 mail_fetch() {
117     exit_if_running
118
119     if ! rsync --timeout=$MAIL_TIMEOUT $RSYNC_OPTS -e "$MAIL_SSH" \
120             --rsync-path="$MAIL_RUNNER remote_send" \
121             $MAIL_HOST:$MAIL_DIR/EMPTY_FILE $HOME/$MAIL_DIR/; then
122         error_msg "transfer failed";
123         return 1;
124     fi
125
126     if [ "`echo $HOME/$MAIL_DIR/mail.in.*.*`" = "" ]; then 
127         debug_msg 1 "";
128         debug_msg 1 "No mail to retrieve";
129         return 1
130     fi
131
132     for f in $HOME/$MAIL_DIR/mail.in.*.*; do
133         if cat $f | $PROCMAIL_COMMAND; then
134             # it is useful seeing what mail is being processed
135             if [ "$MAIL_FRM" = "1" ]; then
136                 frm $f
137             fi
138             rm -f $f
139         fi
140     done
141
142     # this is useful for seeing where procmail has put things ...
143     if [ "$MAIL_PROCMAIL_TAIL" = "1" ]; then
144         debug_msg 2 "";
145         debug_msg 2 "";
146         debug_msg 2 `tail $HOME/$MAIL_DIR/procmail.log`;
147     fi
148
149     return 0;
150 }
151
152
153 ###############################################################################
154 # a sendmail-like call. This just places the mail in a known location for 
155 # mail_send to find
156 mail_sendmail() {
157     msg=$HOME/$MAIL_DIR/mail.out.$RANDOM.$$
158     tmp=$msg.tmp
159     cat > $tmp
160     /bin/mv $tmp $msg
161     mail_sendmail_hook $msg
162     exit 0
163 }
164
165
166 ##############################################################################
167 # a rsync wrapper function that delivers mail on the remote host
168 mail_remote_deliver() {
169     exit_if_running
170
171     rsync $*
172     status=$?
173
174     if [ $status != 0 ]; then
175         return $status;
176     fi
177
178     for f in $HOME/$MAIL_DIR/mail.out.*.*[0-9]; do
179             if $SENDMAIL $SENDMAIL_OPTS < $f; then
180                 rm -f $f
181             else
182                 error_msg "Mail of $f failed $status";
183             fi
184     done
185     return 0
186 }
187
188 ###############################################################################
189 # a rsync wrapper function for fetching mail from the remote host
190 mail_remote_send() {
191     exit_if_running
192
193     # atomically move mail from InBox to a temporary file
194     if test -s $MAIL_INBOX; then
195         movemail $MAIL_INBOX $HOME/$MAIL_DIR/mail.in.$RANDOM.$$ > /dev/null
196     fi
197
198     # by using an empty file we avoid error messages from rsync about not
199     # having any files to transfer
200     if [ ! -f $HOME/$MAIL_DIR/EMPTY_FILE ]; then
201         touch $HOME/$MAIL_DIR/EMPTY_FILE
202     fi
203
204     rsync $* $HOME/$MAIL_DIR/mail.in.*.*
205     status=$?
206     if [ $status != 0 ]; then
207         return $status;
208     fi
209
210     rm -f $HOME/$MAIL_DIR/mail.in.*.*
211     return 0
212 }
213
214
215 ###############################################################################
216 # display help text
217 mail_help() {
218 cat <<EOF
219 mail.runner version $VERSION
220 -----------------------
221
222 mail.runner is a script for sending and receiving email. 
223
224 FEATURES
225 --------
226
227 The main features of mail.runner are
228
229 - good performance over very slow links
230 - secure mail transfer via rsync and ssh
231 - incremental transfer in case the link is lost
232 - no mta installation needed on the client
233
234
235 REQUIREMENTS
236 ------------
237
238 To use mail.runner you need the following:
239
240 - a working 'rsync' and 'ssh' installation
241 - the 'bash' shell
242 - the 'movemail' utility
243 - the 'formail' utility
244 - the Linux 'pidof' utility
245 - a working 'procmail' installation for mail delivery on the client
246 - a working 'sendmail' (or equivalent) installation on the server for sending 
247   email
248 - optionally you may use the 'frm' utility
249
250
251 INSTALLATION
252 ------------
253
254 To install mail.runner you need to do the following:
255
256 - install the mail.runner script on both the server and client 
257 - create a .mail.runner configuration file on the server and client (see
258   the CONFIGURATION section of this help for details)
259 - tell your email program to use "mail.runner sendmail" as the local
260   program to send email with
261
262 How you do the last step depends on your email program. I use RMAIL in
263 emacs to read and send email, so I used the following in my .emacs:
264   (setq sendmail-program "~/bin/mail.sendmail")
265 and then I created a one line script called 'mail.sendmail' that runs
266 'mail.runner sendmail'.
267
268
269 RUNNING
270 -------
271
272 Once installed, you should just run 'mail.runner' to fetch any
273 outstanding emails from the server and send any pending messages. You
274 can also ask mail.runner to just send or fetch by using:
275     mail.runner send
276 or
277     mail.runner fetch
278
279 Any diagnostics messages should be self explanatory.
280
281 CONFIGURATION
282 -------------
283
284 You should configure mail.runner using a file called .mail.runner in
285 your home directory on both the client and the server. The minimal
286 configuration would contain the single option MAIL_HOST like this:
287
288   MAIL_HOST="your.mail.server"
289
290 Other options are:
291
292   MAIL_SSH
293     This specifies the remote shell to use for rsync. Defaults to "ssh"
294
295   MAIL_DIR
296     This specifies the name of the directory where you store mail on
297     both the client and the server. This should be a relative path,
298     relative to your home directory.  The default is "Mail".
299
300   MAIL_INBOX
301     This specifies the location of your mail inbox on your mail server.
302     The default is '\$HOME/InBox'
303
304   MAIL_RUNNER 
305     This specifies the location of the mail.runner script on the
306     server, relative to your home directory. The default is "mail.runner".
307
308   MAIL_TIMEOUT
309     This specifies the number of seconds to wait for transfers. It is
310     passed as a timeout parameter to rsync. The default is 300.
311
312   SENDMAIL
313     This specifies the name of your sendmail program on the
314     server. This program should accept raw emails on stdin and send
315     them. The default is "/usr/lib/sendmail"
316
317   SENDMAIL_OPTS
318     This specifies additional command line options to be passed to
319     sendmail. The default is no additional options.
320
321   MAIL_FRM
322     If this is set to "1" then mail.runner will use the 'frm' utility
323     to display the subject and sender of incoming emails as they are
324     processed.
325
326   MAIL_PROCMAIL_TAIL
327     If this is set to "1" then mail.runner will display the last 10
328     lines of your '\$MAIL_DIR/procmail.log' after processing incoming
329     emails.
330
331   RSYNC_OPTS 
332     This defaults to "$RSYNC_OPTS", you may wish to remove the
333     -v and add -q for quieter operation
334
335   PROCMAIL_COMMAND
336     This sets the command used to deliver mail locally. 
337     It defaults to "$PROCMAIL_COMMAND"
338
339   MAIL_VERBOSE
340     Verbosity level:
341       1 - Summary of messages sent and received.
342       2 - Additional whitespace and output when no mail to send.
343
344   MAIL_NO_PIDOF
345     If this is set to "1" no "pidof" checking is done.  You should set
346     this to "1" if you don't have pidof or your pidof is broken in 
347     some way.
348
349
350 LICENSE
351 -------
352
353 mail.runner is released under the GNU General Public License, version
354 2 or later. Please see http://www.gnu.org/ for a full copy of the license.
355
356
357 AUTHOR
358 ------
359
360 mail.runner was written by Andrew Tridgell <tridge@samba.org>
361
362 EOF
363 }
364
365
366 ###############################################################################
367 # START OF MAIN SCRIPT
368 ###############################################################################
369
370 # See if user is just after help, if so display and exit
371 if [ "$1" = "-h" -o "$1" = "--help" -o $HAVE_MAIL_RUNNER_CONFIG == 0 ]; then
372     mail_help
373     exit 0
374 fi
375
376 # we rely on expansion to a null list
377 shopt -s nullglob
378
379 # make sure the necessary variables are defined
380 if [ "$MAIL_HOST" = "" ]; then
381     error_msg "You must define a MAIL_HOST in $HOME/.mail.runner";
382     error_msg "Use -h for more help";
383     exit 1
384 fi
385
386 # when an argument is passed then call the corresponding mail_* function 
387 # with the remaining arguments. This is used to invoke the right function
388 # remotely, but can also be used to do things like "mail.runner send" from
389 # the command line
390 if [ $# != 0 ]; then
391
392     cmd=$1
393     shift
394     mail_$cmd $*
395     exit $?
396 fi
397
398 # the default is to fetch then send
399 mail_fetch
400 mail_send