#! /bin/sh
. /usr/lib/news/lib/innshellvars

##  $Revision: 1.4 $
##  Send news via NNTP by running several innxmit processes in the background.
##  Usage:
##	nntpsend [-n][-p][-r][-s size][-S][-t timeout][-T limit][host fqdn]...
##	-a		Always have innxmit rewrite the batchfile
##	-d		debug mode, run innxmits with debug as well
##	-D		same as -d except innxmits are not debugged
##	-p		Run innxmit with -p to prune batch files
##	-r		innxmit, don't requeue on unexpected error code
##	-s size		limit the =n file to size bytes
##	-c		disable message-ID checking in streaming mode
##	-t timeout	innxmit timeout to make connection (def: 180)
##	-T limit	innxmit connection transmit time limit (def: forever)
##	-P portnum	port number to use
##	-l		innxmit, log rejected articles
##	-N		innxmit, disable streaming mode
##	-n		do not lock for nntpsend, do not sleep between sets
##	-w delay	wait delay seconds just before innxmit
##	host fqdn	send to host and qualified domain (def: nntpsend.ctl)
##  If no "host fqdn" pairs appear on the command line, then ${CTLFILE}
##  file is read.

PROGNAME=`basename $0`
LOCK=${LOCKS}/LOCK.${PROGNAME}
CTLFILE=${PATHETC}/${PROGNAME}.ctl
LOG=${MOST_LOGS}/${PROGNAME}.log

##  Set defaults.
A_FLAG=
D_FLAG=
NO_LOG_FLAG=
P_FLAG=
R_FLAG=
S_FLAG=
C_FLAG=
L_FLAG=
S2_FLAG=
TRUNC_SIZE=
T_FLAG=
TIMELIMIT=
PP_FLAG=
NOLOCK=
W_SECONDS=

##  Parse JCL.
MORETODO=true
while ${MORETODO} ; do
    case X"$1" in
    X-a)
	A_FLAG="-a"
	;;
    X-d)
	D_FLAG="-d"
	NO_LOG_FLAG="true"
	;;
    X-D)
	NO_LOG_FLAG="true"
	;;
    X-l)
	L_FLAG="-l"
	;;
    X-p)
	P_FLAG="-p"
	;;
    X-r)
	R_FLAG="-r"
	;;
    X-S)
	S_FLAG="-S"
	;;
    X-N)
	S2_FLAG="-s"
	;;
    X-c)
	C_FLAG="-c"
	;;
    X-s)
	if [ -z "$2" ] ; then
	    echo "${PROGNAME}: option requires an argument -- s" 1>&2
	    exit 1
	fi
	TRUNC_SIZE="$2"
	shift
	;;
    X-s*)
	TRUNC_SIZE="`echo $1 | ${SED} -e 's/-s//'`"
	;;
    X-t)
	if [ -z "$2" ] ; then
	    echo "${PROGNAME}: option requires an argument -- t" 1>&2
	    exit 1
	fi
	T_FLAG="-t$2"
	shift
	;;
    X-t*)
	T_FLAG="$1"
	;;
    X-P)
	if [ -z "$2" ] ; then
	    echo "${PROGNAME}: option requires an argument -- P" 1>&2
	    exit 1
	fi
	PP_FLAG="-P$2"
	shift
	;;
    X-P*)
	PP_FLAG="$1"
	;;
    X-T)
	if [ -z "$2" ] ; then
	    echo "${PROGNAME}: option requires an argument -- T" 1>&2
	    exit 1
	fi
	TIMELIMIT="-T$2"
	shift
	;;
    X-T*)
	TIMELIMIT="$1"
	;;
    X-n)
	NOLOCK=true
	;;
    X-w)
	if [ -z "$2" ] ; then
	    echo "${PROGNAME}: option requires an argument -- w" 1>&2
	    exit 1
	fi
	W_SECONDS="$2"
	shift
	;;
    X--)
	shift
	MORETODO=false
	;;
    X-*)
	echo "${PROGNAME}: illegal option -- $1" 1>&2
	exit 1
	;;
    *)
	MORETODO=false
	;;
    esac
    ${MORETODO} && shift
done

## grab the lock if not -n
NNTPLOCK=${LOCKS}/LOCK.nntpsend
if [ -z "${NOLOCK}" ]; then
    shlock -p $$ -f ${NNTPLOCK} || {
	# nothing to do
	exit 0
    }
fi

##  Parse arguments; host/fqdn pairs.
INPUT=${TMPDIR}/nntpsend$$
cp /dev/null ${INPUT}
while [ $# -gt 0 ]; do
    if [ $# -lt 2 ]; then
	echo "${PROGNAME}:  Bad host/fqdn pair" 1>&2
	rm -f ${NNTPLOCK}
	exit 1
    fi
    echo "$1 $2" >>${INPUT}
    shift
    shift
done

##  If nothing specified on the command line, read the control file.
if [ ! -s ${INPUT} ] ; then
    if [ ! -r ${CTLFILE} ]; then
	echo "${PROGNAME}: cannot read ${CTLFILE}"
	rm -f ${NNTPLOCK}
	exit 1
    fi
    ${SED} -e 's/#.*//' -e '/^$/d' -e 's/::\([^:]*\)$/:max:\1/' \
	-e 's/:/ /g' <${CTLFILE} >${INPUT}
fi

##  Go to where the action is.
if [ ! -d ${BATCH} ]; then
    echo "${PROGNAME}: directory ${BATCH} not found" 1>&2
    rm -f ${NNTPLOCK}
    exit 1
fi
cd ${BATCH}

##  Set up log file.
umask 002
if [ -z "${NO_LOG_FLAG}" ]; then
    test ! -f ${LOG} && touch ${LOG}
    chmod 0660 ${LOG}
    exec >>${LOG} 2>&1
fi
PARENTPID=$$
echo "${PROGNAME}: [${PARENTPID}] start"

##  Set up environment.
export BATCH PROGNAME PARENTPID INNFLAGS

##  Loop over all sites.
cat ${INPUT} | while read SITE HOST SIZE_ARG FLAGS; do
    ## Parse the input parameters.
    if [ -z "${SITE}" -o -z "${HOST}" ] ; then
	echo "Ignoring bad line: ${SITE} ${HOST} ${SIZE_ARG} ${FLAGS}" 1>&2
	continue
    fi

    ## give up early if we cannot even lock it
    ##
    ## NOTE: This lock is not nntpsend's lock but rather the
    ##	     lock that the parent shell of innxmit will use.
    ##	     Later on the child will take the lock from us.
    ##
    LOCK="${LOCKS}/LOCK.${SITE}"
    shlock -p $$ -f "${LOCK}" || continue

    ## Compute the specific parameters for this site.
    test "${SIZE_ARG}" = "max" && SIZE_ARG=
    if [ -n "${TRUNC_SIZE}" ]; then
	SIZE_ARG="${TRUNC_SIZE}"
    fi
    ## Parse the SIZE_ARG for either MaxSize-TruncSize or TruncSize
    case "${SIZE_ARG}" in
    *-*) MAXSIZE="`echo ${SIZE_ARG} | ${SED} -e 's/-.*//'`";
	 SIZE="`echo ${SIZE_ARG} | ${SED} -e 's/^.*-//'`" ;;
    *) MAXSIZE="${SIZE_ARG}";
       SIZE="${SIZE_ARG}" ;;
    esac
    D_PARAM=
    R_PARAM=
    S_PARAM=
    S2_PARAM=
    C_PARAM=
    PP_PARAM=
    L_PARAM=
    TIMEOUT_PARAM=
    TIMELIMIT_PARAM=
    if [ -z "${FLAGS}" ]; then
	MORETODO=false
    else
	MORETODO=true
	set -- ${FLAGS}
    fi
    while ${MORETODO} ; do
	case "X$1" in
	X-a)
	    ;;
	X-d)
	    D_PARAM="-d"
	    ;;
	X-c)
	    C_PARAM="-c"
	    ;;
	X-p)
	    P_PARAM="-p"
	    ;;
	X-r)
	    R_PARAM="-r"
	    ;;
	X-S)
	    S_PARAM="-S"
	    ;;
	X-s)
	    S2_PARAM="-s"
	    ;;
	X-l)
	    L_PARAM="-l"
	    ;;
	X-t)
	    if [ -z "$2" ] ; then
		echo "${PROGNAME}: option requires an argument -- t" 1>&2
		rm -f "${NNTPLOCK}" "${LOCK}"
		exit 1
	    fi
	    TIMEOUT_PARAM="-t$2"
	    shift
	    ;;
	X-t*)
	    TIMEOUT_PARAM="$1"
	    ;;
	X-P)
	    if [ -z "$2" ] ; then
		echo "${PROGNAME}: option requires an argument -- P" 1>&2
		rm -f "${NNTPLOCK}" "${LOCK}"
		exit 1
	    fi
	    PP_PARAM="-P$2"
	    shift
	    ;;
	X-P*)
	    PP_PARAM="$1"
	    ;;
	X-T)
	    if [ -z "$2" ] ; then
		echo "${PROGNAME}: option requires an argument -- T" 1>&2
		rm -f "${NNTPLOCK}" "${LOCK}"
		exit 1
	    fi
	    TIMELIMIT_PARAM="-T$2"
	    shift
	    ;;
	X-T*)
	    TIMELIMIT_PARAM="$1"
	    ;;
	X-w)
	    if [ -z "$2" ] ; then
		echo "${PROGNAME}: option requires an argument -- w" 1>&2
	    rm -f "${NNTPLOCK}" "${LOCK}"
	    exit 1
	    fi
	    W_SECONDS="$2"
	    shift
            ;;
	*)
	    MORETODO=false
	    ;;
	esac
	${MORETODO} && shift
    done
    if [ -z "${SIZE}" -o -n "${A_FLAG}" ]; then
	# rewrite batch file if we do not have a size limit
	INNFLAGS="-a"
    else
	# we have a size limit, let shrinkfile rewrite the file
	INNFLAGS=
    fi
    if [ -n "${D_FLAG}" ]; then
	INNFLAGS="${INNFLAGS} ${D_FLAG}"
    else
	test -n "${D_PARAM}" && INNFLAGS="${INNFLAGS} ${D_PARAM}"
    fi
    if [ -n "${C_FLAG}" ]; then
        INNFLAGS="${INNFLAGS} ${C_FLAG}"
    else
        test -n "${C_PARAM}" && INNFLAGS="${INNFLAGS} ${C_PARAM}"
    fi
    if [ -n "${P_FLAG}" ]; then
	INNFLAGS="${INNFLAGS} ${P_FLAG}"
    else
	test -n "${P_PARAM}" && INNFLAGS="${INNFLAGS} ${P_PARAM}"
    fi
    if [ -n "${L_FLAG}" ]; then
	INNFLAGS="${INNFLAGS} ${L_FLAG}"
    else
	test -n "${L_PARAM}" && INNFLAGS="${INNFLAGS} ${L_PARAM}"
    fi
    if [ -n "${R_FLAG}" ]; then
	INNFLAGS="${INNFLAGS} ${R_FLAG}"
    else
	test -n "${R_PARAM}" && INNFLAGS="${INNFLAGS} ${R_PARAM}"
    fi
    if [ -n "${S_FLAG}" ]; then
	INNFLAGS="${INNFLAGS} ${S_FLAG}"
    else
	test -n "${S_PARAM}" && INNFLAGS="${INNFLAGS} ${S_PARAM}"
    fi
    if [ -n "${S2_FLAG}" ]; then
	INNFLAGS="${INNFLAGS} ${S2_FLAG}"
    else
	test -n "${S2_PARAM}" && INNFLAGS="${INNFLAGS} ${S2_PARAM}"
    fi
    if [ -n "${T_FLAG}" ]; then
	INNFLAGS="${INNFLAGS} ${T_FLAG}"
    else
	test -n "${TIMEOUT_PARAM}" && INNFLAGS="${INNFLAGS} ${TIMEOUT_PARAM}"
    fi
    if [ -n "${PP_FLAG}" ]; then
	INNFLAGS="${INNFLAGS} ${PP_FLAG}"
    else
	test -n "${PP_PARAM}" && INNFLAGS="${INNFLAGS} ${PP_PARAM}"
    fi
    if [ -n "${TIMELIMIT}" ]; then
	INNFLAGS="${INNFLAGS} ${TIMELIMIT}"
    else
	test -n "${TIMELIMIT_PARAM}" \
	    && INNFLAGS="${INNFLAGS} ${TIMELIMIT_PARAM}"
    fi

    ## Flush the buffers for the site now, rather than in the child.
    ## This helps pace the number of ctlinnd commands because the
    ## nntpsend process does not proceed until the site flush has
    ## been completed.
    ##
    # carry old unfinished work over to this task
    BATCHFILE="${SITE}=n"
    if [ -f "${SITE}.work" ] ; then
	cat ${SITE}.work >>"${BATCHFILE}"
	rm -f "${SITE}.work"
    fi
    # form BATCHFILE to hold the work for this site
    if [ -f "${SITE}" ]; then
	mv "${SITE}" "${SITE}.work"
	if ctlinnd -s -t30 flush ${SITE} ; then
	    cat ${SITE}.work >>"${BATCHFILE}"
	    rm -f ${SITE}.work
	else
	    # flush failed, continue if we have any batchfile to work on
	    echo "${PROGNAME}: bad flush for ${HOST} via ${SITE}"
	    if [ -f "${BATCHFILE}" ]; then
		echo "${PROGNAME}: trying ${HOST} via ${SITE} anyway"
	    else
		echo "${PROGNAME}: skipping ${HOST} via ${SITE}"
		rm -f ${LOCK}
		continue
	    fi
	fi
    else
	# nothing to work on, so flush and move on
	ctlinnd -s -t30 flush ${SITE}
	echo "${PROGNAME}: file ${BATCH}/${SITE} for ${HOST} not found"
	if [ -f "${BATCHFILE}" ]; then
	    echo "${PROGNAME}: trying ${HOST} via ${SITE} anyway"
	else
	    echo "${PROGNAME}: skipping ${HOST} via ${SITE}"
	    rm -f ${LOCK}
	    continue
	fi
    fi

    ##  Start sending this site in the background.
    export MAXSIZE SITE HOST PROGNAME PARENTPID SIZE TMPDIR LOCK BATCHFILE W_SECONDS
    sh -c '
	# grab the lock from the parent
	#
	# This is safe because only the parent will have locked
	# the site.  We break the lock and reclaim it.
	rm -f ${LOCK}
	trap "rm -f ${LOCK} ; exit 1" 1 2 3 15
	shlock -p $$ -f ${LOCK} || {
	    WHY="`cat ${LOCK}`"
	    echo "${PROGNAME}: [${PARENTPID}:$$] ${SITE} locked ${WHY} `date`"
	    exit
	}
	# process the site BATCHFILE
	if [ -f "${BATCHFILE}" ]; then
	    test -n "${SIZE}" && shrinkfile -m${MAXSIZE} -s${SIZE} -v ${BATCHFILE}
	    if [ -s ${BATCHFILE} ] ; then
		if [ -n "${W_SECONDS}" ] ; then
		    echo "${PROGNAME}: [${PARENTPID}:$$] sleeping ${W_SECONDS} seconds before ${SITE}"
		    sleep "${W_SECONDS}"
		fi
		echo "${PROGNAME}: [${PARENTPID}:$$] begin ${SITE} `date`"
		echo "${PROGNAME}: [${PARENTPID}:$$] innxmit ${INNFLAGS} ${HOST} ..."
		eval innxmit ${INNFLAGS} ${HOST} ${BATCH}/${BATCHFILE}
		echo "${PROGNAME}: [${PARENTPID}:$$] end ${SITE} `date`"
	    else
		rm -f ${BATCHFILE}
	    fi
	else
	    echo "${PROGNAME}: file ${BATCH}/${BATCHFILE} for ${HOST} not found"
	fi
	rm -f ${LOCK}
    ' &
done

## release the nntpsend lock and clean up before we wait on child processes
if [ -z "${NOLOCK}" ]; then
    rm -f ${NNTPLOCK}
fi
rm -f ${INPUT}

## wait for child processes to finish
wait

## all done
echo "${PROGNAME}: [${PARENTPID}] stop"
exit 0
