chiark / gitweb /
close idle connections and spot unresponsive ones
[innduct.git] / backends / nntpsend.in
1 #! /bin/sh
2 # fixscript will replace this line with code to load innshellvars
3
4 ##  $Revision: 5047 $
5 ##  Send news via NNTP by running several innxmit processes in the background.
6 ##  Usage:
7 ##      nntpsend [-n][-p][-r][-s size][-S][-t timeout][-T limit][host fqdn]...
8 ##      -a              Always have innxmit rewrite the batchfile
9 ##      -d              debug mode, run innxmits with debug as well
10 ##      -D              same as -d except innxmits are not debugged
11 ##      -p              Run innxmit with -p to prune batch files
12 ##      -r              innxmit, don't requeue on unexpected error code
13 ##      -s size         limit the =n file to size bytes
14 ##      -c              disable message-ID checking in streaming mode
15 ##      -t timeout      innxmit timeout to make connection (def: 180)
16 ##      -T limit        innxmit connection transmit time limit (def: forever)
17 ##      -P portnum      port number to use
18 ##      -l              innxmit, log rejected articles
19 ##      -N              innxmit, disable streaming mode
20 ##      -n              do not lock for nntpsend, do not sleep between sets
21 ##      -w delay        wait delay seconds just before innxmit
22 ##      host fqdn       send to host and qualified domain (def: nntpsend.ctl)
23 ##  If no "host fqdn" pairs appear on the command line, then ${CTLFILE}
24 ##  file is read.
25
26 PROGNAME=`basename $0`
27 LOCK=${LOCKS}/LOCK.${PROGNAME}
28 CTLFILE=${PATHETC}/${PROGNAME}.ctl
29 LOG=${MOST_LOGS}/${PROGNAME}.log
30
31 ##  Set defaults.
32 A_FLAG=
33 D_FLAG=
34 NO_LOG_FLAG=
35 P_FLAG=
36 R_FLAG=
37 S_FLAG=
38 C_FLAG=
39 L_FLAG=
40 S2_FLAG=
41 TRUNC_SIZE=
42 T_FLAG=
43 TIMELIMIT=
44 PP_FLAG=
45 NOLOCK=
46 W_SECONDS=
47
48 ##  Parse JCL.
49 MORETODO=true
50 while ${MORETODO} ; do
51     case X"$1" in
52     X-a)
53         A_FLAG="-a"
54         ;;
55     X-d)
56         D_FLAG="-d"
57         NO_LOG_FLAG="true"
58         ;;
59     X-D)
60         NO_LOG_FLAG="true"
61         ;;
62     X-l)
63         L_FLAG="-l"
64         ;;
65     X-p)
66         P_FLAG="-p"
67         ;;
68     X-r)
69         R_FLAG="-r"
70         ;;
71     X-S)
72         S_FLAG="-S"
73         ;;
74     X-N)
75         S2_FLAG="-s"
76         ;;
77     X-c)
78         C_FLAG="-c"
79         ;;
80     X-s)
81         if [ -z "$2" ] ; then
82             echo "${PROGNAME}: option requires an argument -- s" 1>&2
83             exit 1
84         fi
85         TRUNC_SIZE="$2"
86         shift
87         ;;
88     X-s*)
89         TRUNC_SIZE="`echo $1 | ${SED} -e 's/-s//'`"
90         ;;
91     X-t)
92         if [ -z "$2" ] ; then
93             echo "${PROGNAME}: option requires an argument -- t" 1>&2
94             exit 1
95         fi
96         T_FLAG="-t$2"
97         shift
98         ;;
99     X-t*)
100         T_FLAG="$1"
101         ;;
102     X-P)
103         if [ -z "$2" ] ; then
104             echo "${PROGNAME}: option requires an argument -- P" 1>&2
105             exit 1
106         fi
107         PP_FLAG="-P$2"
108         shift
109         ;;
110     X-P*)
111         PP_FLAG="$1"
112         ;;
113     X-T)
114         if [ -z "$2" ] ; then
115             echo "${PROGNAME}: option requires an argument -- T" 1>&2
116             exit 1
117         fi
118         TIMELIMIT="-T$2"
119         shift
120         ;;
121     X-T*)
122         TIMELIMIT="$1"
123         ;;
124     X-n)
125         NOLOCK=true
126         ;;
127     X-w)
128         if [ -z "$2" ] ; then
129             echo "${PROGNAME}: option requires an argument -- w" 1>&2
130             exit 1
131         fi
132         W_SECONDS="$2"
133         shift
134         ;;
135     X--)
136         shift
137         MORETODO=false
138         ;;
139     X-*)
140         echo "${PROGNAME}: illegal option -- $1" 1>&2
141         exit 1
142         ;;
143     *)
144         MORETODO=false
145         ;;
146     esac
147     ${MORETODO} && shift
148 done
149
150 ## grab the lock if not -n
151 NNTPLOCK=${LOCKS}/LOCK.nntpsend
152 if [ -z "${NOLOCK}" ]; then
153     shlock -p $$ -f ${NNTPLOCK} || {
154         # nothing to do
155         exit 0
156     }
157 fi
158
159 ##  Parse arguments; host/fqdn pairs.
160 INPUT=${TMPDIR}/nntpsend$$
161 cp /dev/null ${INPUT}
162 while [ $# -gt 0 ]; do
163     if [ $# -lt 2 ]; then
164         echo "${PROGNAME}:  Bad host/fqdn pair" 1>&2
165         rm -f ${NNTPLOCK}
166         exit 1
167     fi
168     echo "$1 $2" >>${INPUT}
169     shift
170     shift
171 done
172
173 ##  If nothing specified on the command line, read the control file.
174 if [ ! -s ${INPUT} ] ; then
175     if [ ! -r ${CTLFILE} ]; then
176         echo "${PROGNAME}: cannot read ${CTLFILE}"
177         rm -f ${NNTPLOCK}
178         exit 1
179     fi
180     ${SED} -e 's/#.*//' -e '/^$/d' -e 's/::\([^:]*\)$/:max:\1/' \
181         -e 's/:/ /g' <${CTLFILE} >${INPUT}
182 fi
183
184 ##  Go to where the action is.
185 if [ ! -d ${BATCH} ]; then
186     echo "${PROGNAME}: directory ${BATCH} not found" 1>&2
187     rm -f ${NNTPLOCK}
188     exit 1
189 fi
190 cd ${BATCH}
191
192 ##  Set up log file.
193 umask 002
194 if [ -z "${NO_LOG_FLAG}" ]; then
195     test ! -f ${LOG} && touch ${LOG}
196     chmod 0660 ${LOG}
197     exec >>${LOG} 2>&1
198 fi
199 PARENTPID=$$
200 echo "${PROGNAME}: [${PARENTPID}] start"
201
202 ##  Set up environment.
203 export BATCH PROGNAME PARENTPID INNFLAGS
204
205 ##  Loop over all sites.
206 cat ${INPUT} | while read SITE HOST SIZE_ARG FLAGS; do
207     ## Parse the input parameters.
208     if [ -z "${SITE}" -o -z "${HOST}" ] ; then
209         echo "Ignoring bad line: ${SITE} ${HOST} ${SIZE_ARG} ${FLAGS}" 1>&2
210         continue
211     fi
212
213     ## give up early if we cannot even lock it
214     ##
215     ## NOTE: This lock is not nntpsend's lock but rather the
216     ##       lock that the parent shell of innxmit will use.
217     ##       Later on the child will take the lock from us.
218     ##
219     LOCK="${LOCKS}/LOCK.${SITE}"
220     shlock -p $$ -f "${LOCK}" || continue
221
222     ## Compute the specific parameters for this site.
223     test "${SIZE_ARG}" = "max" && SIZE_ARG=
224     if [ -n "${TRUNC_SIZE}" ]; then
225         SIZE_ARG="${TRUNC_SIZE}"
226     fi
227     ## Parse the SIZE_ARG for either MaxSize-TruncSize or TruncSize
228     case "${SIZE_ARG}" in
229     *-*) MAXSIZE="`echo ${SIZE_ARG} | ${SED} -e 's/-.*//'`";
230          SIZE="`echo ${SIZE_ARG} | ${SED} -e 's/^.*-//'`" ;;
231     *) MAXSIZE="${SIZE_ARG}";
232        SIZE="${SIZE_ARG}" ;;
233     esac
234     D_PARAM=
235     R_PARAM=
236     S_PARAM=
237     S2_PARAM=
238     C_PARAM=
239     PP_PARAM=
240     L_PARAM=
241     TIMEOUT_PARAM=
242     TIMELIMIT_PARAM=
243     if [ -z "${FLAGS}" ]; then
244         MORETODO=false
245     else
246         MORETODO=true
247         set -- ${FLAGS}
248     fi
249     while ${MORETODO} ; do
250         case "X$1" in
251         X-a)
252             ;;
253         X-d)
254             D_PARAM="-d"
255             ;;
256         X-c)
257             C_PARAM="-c"
258             ;;
259         X-p)
260             P_PARAM="-p"
261             ;;
262         X-r)
263             R_PARAM="-r"
264             ;;
265         X-S)
266             S_PARAM="-S"
267             ;;
268         X-s)
269             S2_PARAM="-s"
270             ;;
271         X-l)
272             L_PARAM="-l"
273             ;;
274         X-t)
275             if [ -z "$2" ] ; then
276                 echo "${PROGNAME}: option requires an argument -- t" 1>&2
277                 rm -f "${NNTPLOCK}" "${LOCK}"
278                 exit 1
279             fi
280             TIMEOUT_PARAM="-t$2"
281             shift
282             ;;
283         X-t*)
284             TIMEOUT_PARAM="$1"
285             ;;
286         X-P)
287             if [ -z "$2" ] ; then
288                 echo "${PROGNAME}: option requires an argument -- P" 1>&2
289                 rm -f "${NNTPLOCK}" "${LOCK}"
290                 exit 1
291             fi
292             PP_PARAM="-P$2"
293             shift
294             ;;
295         X-P*)
296             PP_PARAM="$1"
297             ;;
298         X-T)
299             if [ -z "$2" ] ; then
300                 echo "${PROGNAME}: option requires an argument -- T" 1>&2
301                 rm -f "${NNTPLOCK}" "${LOCK}"
302                 exit 1
303             fi
304             TIMELIMIT_PARAM="-T$2"
305             shift
306             ;;
307         X-T*)
308             TIMELIMIT_PARAM="$1"
309             ;;
310         X-w)
311             if [ -z "$2" ] ; then
312                 echo "${PROGNAME}: option requires an argument -- w" 1>&2
313             rm -f "${NNTPLOCK}" "${LOCK}"
314             exit 1
315             fi
316             W_SECONDS="$2"
317             shift
318             ;;
319         *)
320             MORETODO=false
321             ;;
322         esac
323         ${MORETODO} && shift
324     done
325     if [ -z "${SIZE}" -o -n "${A_FLAG}" ]; then
326         # rewrite batch file if we do not have a size limit
327         INNFLAGS="-a"
328     else
329         # we have a size limit, let shrinkfile rewrite the file
330         INNFLAGS=
331     fi
332     if [ -n "${D_FLAG}" ]; then
333         INNFLAGS="${INNFLAGS} ${D_FLAG}"
334     else
335         test -n "${D_PARAM}" && INNFLAGS="${INNFLAGS} ${D_PARAM}"
336     fi
337     if [ -n "${C_FLAG}" ]; then
338         INNFLAGS="${INNFLAGS} ${C_FLAG}"
339     else
340         test -n "${C_PARAM}" && INNFLAGS="${INNFLAGS} ${C_PARAM}"
341     fi
342     if [ -n "${P_FLAG}" ]; then
343         INNFLAGS="${INNFLAGS} ${P_FLAG}"
344     else
345         test -n "${P_PARAM}" && INNFLAGS="${INNFLAGS} ${P_PARAM}"
346     fi
347     if [ -n "${L_FLAG}" ]; then
348         INNFLAGS="${INNFLAGS} ${L_FLAG}"
349     else
350         test -n "${L_PARAM}" && INNFLAGS="${INNFLAGS} ${L_PARAM}"
351     fi
352     if [ -n "${R_FLAG}" ]; then
353         INNFLAGS="${INNFLAGS} ${R_FLAG}"
354     else
355         test -n "${R_PARAM}" && INNFLAGS="${INNFLAGS} ${R_PARAM}"
356     fi
357     if [ -n "${S_FLAG}" ]; then
358         INNFLAGS="${INNFLAGS} ${S_FLAG}"
359     else
360         test -n "${S_PARAM}" && INNFLAGS="${INNFLAGS} ${S_PARAM}"
361     fi
362     if [ -n "${S2_FLAG}" ]; then
363         INNFLAGS="${INNFLAGS} ${S2_FLAG}"
364     else
365         test -n "${S2_PARAM}" && INNFLAGS="${INNFLAGS} ${S2_PARAM}"
366     fi
367     if [ -n "${T_FLAG}" ]; then
368         INNFLAGS="${INNFLAGS} ${T_FLAG}"
369     else
370         test -n "${TIMEOUT_PARAM}" && INNFLAGS="${INNFLAGS} ${TIMEOUT_PARAM}"
371     fi
372     if [ -n "${PP_FLAG}" ]; then
373         INNFLAGS="${INNFLAGS} ${PP_FLAG}"
374     else
375         test -n "${PP_PARAM}" && INNFLAGS="${INNFLAGS} ${PP_PARAM}"
376     fi
377     if [ -n "${TIMELIMIT}" ]; then
378         INNFLAGS="${INNFLAGS} ${TIMELIMIT}"
379     else
380         test -n "${TIMELIMIT_PARAM}" \
381             && INNFLAGS="${INNFLAGS} ${TIMELIMIT_PARAM}"
382     fi
383
384     ## Flush the buffers for the site now, rather than in the child.
385     ## This helps pace the number of ctlinnd commands because the
386     ## nntpsend process does not proceed until the site flush has
387     ## been completed.
388     ##
389     # carry old unfinished work over to this task
390     BATCHFILE="${SITE}=n"
391     if [ -f "${SITE}.work" ] ; then
392         cat ${SITE}.work >>"${BATCHFILE}"
393         rm -f "${SITE}.work"
394     fi
395     # form BATCHFILE to hold the work for this site
396     if [ -f "${SITE}" ]; then
397         mv "${SITE}" "${SITE}.work"
398         if ctlinnd -s -t30 flush ${SITE} ; then
399             cat ${SITE}.work >>"${BATCHFILE}"
400             rm -f ${SITE}.work
401         else
402             # flush failed, continue if we have any batchfile to work on
403             echo "${PROGNAME}: bad flush for ${HOST} via ${SITE}"
404             if [ -f "${BATCHFILE}" ]; then
405                 echo "${PROGNAME}: trying ${HOST} via ${SITE} anyway"
406             else
407                 echo "${PROGNAME}: skipping ${HOST} via ${SITE}"
408                 rm -f ${LOCK}
409                 continue
410             fi
411         fi
412     else
413         # nothing to work on, so flush and move on
414         ctlinnd -s -t30 flush ${SITE}
415         echo "${PROGNAME}: file ${BATCH}/${SITE} for ${HOST} not found"
416         if [ -f "${BATCHFILE}" ]; then
417             echo "${PROGNAME}: trying ${HOST} via ${SITE} anyway"
418         else
419             echo "${PROGNAME}: skipping ${HOST} via ${SITE}"
420             rm -f ${LOCK}
421             continue
422         fi
423     fi
424
425     ##  Start sending this site in the background.
426     export MAXSIZE SITE HOST PROGNAME PARENTPID SIZE TMPDIR LOCK BATCHFILE W_SECONDS
427     sh -c '
428         # grab the lock from the parent
429         #
430         # This is safe because only the parent will have locked
431         # the site.  We break the lock and reclaim it.
432         rm -f ${LOCK}
433         trap "rm -f ${LOCK} ; exit 1" 1 2 3 15
434         shlock -p $$ -f ${LOCK} || {
435             WHY="`cat ${LOCK}`"
436             echo "${PROGNAME}: [${PARENTPID}:$$] ${SITE} locked ${WHY} `date`"
437             exit
438         }
439         # process the site BATCHFILE
440         if [ -f "${BATCHFILE}" ]; then
441             test -n "${SIZE}" && shrinkfile -m${MAXSIZE} -s${SIZE} -v ${BATCHFILE}
442             if [ -s ${BATCHFILE} ] ; then
443                 if [ -n "${W_SECONDS}" ] ; then
444                     echo "${PROGNAME}: [${PARENTPID}:$$] sleeping ${W_SECONDS} seconds before ${SITE}"
445                     sleep "${W_SECONDS}"
446                 fi
447                 echo "${PROGNAME}: [${PARENTPID}:$$] begin ${SITE} `date`"
448                 echo "${PROGNAME}: [${PARENTPID}:$$] innxmit ${INNFLAGS} ${HOST} ..."
449                 eval innxmit ${INNFLAGS} ${HOST} ${BATCH}/${BATCHFILE}
450                 echo "${PROGNAME}: [${PARENTPID}:$$] end ${SITE} `date`"
451             else
452                 rm -f ${BATCHFILE}
453             fi
454         else
455             echo "${PROGNAME}: file ${BATCH}/${BATCHFILE} for ${HOST} not found"
456         fi
457         rm -f ${LOCK}
458     ' &
459 done
460
461 ## release the nntpsend lock and clean up before we wait on child processes
462 if [ -z "${NOLOCK}" ]; then
463     rm -f ${NNTPLOCK}
464 fi
465 rm -f ${INPUT}
466
467 ## wait for child processes to finish
468 wait
469
470 ## all done
471 echo "${PROGNAME}: [${PARENTPID}] stop"
472 exit 0