ldb speed test - needs free block dev
[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 # show the outgoing mail queue
216 ###############################
217 mail_queue() {
218     for f in $HOME/$MAIL_DIR/mail.out.*.*; do
219         echo `basename $f`:
220         egrep '^(From|To|Subject):' $f | sed 's/^/      /'
221         echo
222     done
223 }
224
225
226 ###############################################################################
227 # display help text
228 mail_help() {
229 cat <<EOF
230 mail.runner version $VERSION
231 -----------------------
232
233 mail.runner is a script for sending and receiving email. 
234
235 FEATURES
236 --------
237
238 The main features of mail.runner are
239
240 - good performance over very slow links
241 - secure mail transfer via rsync and ssh
242 - incremental transfer in case the link is lost
243 - no mta installation needed on the client
244
245
246 REQUIREMENTS
247 ------------
248
249 To use mail.runner you need the following:
250
251 - a working 'rsync' and 'ssh' installation
252 - the 'bash' shell
253 - the 'movemail' utility
254 - the 'formail' utility
255 - the Linux 'pidof' utility
256 - a working 'procmail' installation for mail delivery on the client
257 - a working 'sendmail' (or equivalent) installation on the server for sending 
258   email
259 - optionally you may use the 'frm' utility
260
261
262 INSTALLATION
263 ------------
264
265 To install mail.runner you need to do the following:
266
267 - install the mail.runner script on both the server and client 
268 - create a .mail.runner configuration file on the server and client (see
269   the CONFIGURATION section of this help for details)
270 - tell your email program to use "mail.runner sendmail" as the local
271   program to send email with
272
273 How you do the last step depends on your email program. I use RMAIL in
274 emacs to read and send email, so I used the following in my .emacs:
275   (setq sendmail-program "~/bin/mail.sendmail")
276 and then I created a one line script called 'mail.sendmail' that runs
277 'mail.runner sendmail'.
278
279
280 RUNNING
281 -------
282
283 Once installed, you should just run 'mail.runner' to fetch any
284 outstanding emails from the server and send any pending messages. You
285 can also ask mail.runner to just send or fetch by using:
286     mail.runner send
287 or
288     mail.runner fetch
289
290 Any diagnostics messages should be self explanatory.
291
292 EMACS
293 -----
294
295 If you use an Emacs-based email program then you should set the
296 variable mail-interactive to t.  This means that the email program
297 will notice when 'mail.runner sendmail' fails to queue a message,
298 perhaps if mail.runner exits (with non-0 status) because another
299 instance is already running (possibly doing 'mail.runner send').
300 Otherwise it may look like 'mail.runner sendmail' has queued a
301 message, but it has really been discarded!
302
303 CONFIGURATION
304 -------------
305
306 You should configure mail.runner using a file called .mail.runner in
307 your home directory on both the client and the server. The minimal
308 configuration would contain the single option MAIL_HOST like this:
309
310   MAIL_HOST="your.mail.server"
311
312 Other options are:
313
314   MAIL_SSH
315     This specifies the remote shell to use for rsync. Defaults to "ssh"
316
317   MAIL_DIR
318     This specifies the name of the directory where you store mail on
319     both the client and the server. This should be a relative path,
320     relative to your home directory.  The default is "Mail".
321
322   MAIL_INBOX
323     This specifies the location of your mail inbox on your mail server.
324     The default is '\$HOME/InBox'
325
326   MAIL_RUNNER 
327     This specifies the location of the mail.runner script on the
328     server, relative to your home directory. The default is "mail.runner".
329
330   MAIL_TIMEOUT
331     This specifies the number of seconds to wait for transfers. It is
332     passed as a timeout parameter to rsync. The default is 300.
333
334   SENDMAIL
335     This specifies the name of your sendmail program on the
336     server. This program should accept raw emails on stdin and send
337     them. The default is "/usr/lib/sendmail"
338
339   SENDMAIL_OPTS
340     This specifies additional command line options to be passed to
341     sendmail. The default is no additional options.
342
343   MAIL_FRM
344     If this is set to "1" then mail.runner will use the 'frm' utility
345     to display the subject and sender of incoming emails as they are
346     processed.
347
348   MAIL_PROCMAIL_TAIL
349     If this is set to "1" then mail.runner will display the last 10
350     lines of your '\$MAIL_DIR/procmail.log' after processing incoming
351     emails.
352
353   RSYNC_OPTS 
354     This defaults to "$RSYNC_OPTS", you may wish to remove the
355     -v and add -q for quieter operation
356
357   PROCMAIL_COMMAND
358     This sets the command used to deliver mail locally. 
359     It defaults to "$PROCMAIL_COMMAND"
360
361   MAIL_VERBOSE
362     Verbosity level:
363       1 - Summary of messages sent and received.
364       2 - Additional whitespace and output when no mail to send.
365
366   MAIL_NO_PIDOF
367     If this is set to "1" no "pidof" checking is done.  You should set
368     this to "1" if you don't have pidof or your pidof is broken in 
369     some way.
370
371
372 LICENSE
373 -------
374
375 mail.runner is released under the GNU General Public License, version
376 2 or later. Please see http://www.gnu.org/ for a full copy of the license.
377
378
379 AUTHOR
380 ------
381
382 mail.runner was written by Andrew Tridgell <tridge@samba.org>
383
384 EOF
385 }
386
387
388 ###############################################################################
389 # START OF MAIN SCRIPT
390 ###############################################################################
391
392 # See if user is just after help, if so display and exit
393 if [ "$1" = "-h" -o "$1" = "--help" -o $HAVE_MAIL_RUNNER_CONFIG == 0 ]; then
394     mail_help
395     exit 0
396 fi
397
398 # we rely on expansion to a null list
399 shopt -s nullglob
400
401 # make sure the necessary variables are defined
402 if [ "$MAIL_HOST" = "" ]; then
403     error_msg "You must define a MAIL_HOST in $HOME/.mail.runner";
404     error_msg "Use -h for more help";
405     exit 1
406 fi
407
408 # when an argument is passed then call the corresponding mail_* function 
409 # with the remaining arguments. This is used to invoke the right function
410 # remotely, but can also be used to do things like "mail.runner send" from
411 # the command line
412 if [ $# != 0 ]; then
413
414     cmd=$1
415     shift
416     mail_$cmd $*
417     exit $?
418 fi
419
420 # the default is to fetch then send
421 mail_fetch
422 mail_send