summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcron/gate_news203
1 files changed, 80 insertions, 123 deletions
diff --git a/cron/gate_news b/cron/gate_news
index eb729b96d..7c97a08f5 100755
--- a/cron/gate_news
+++ b/cron/gate_news
@@ -22,14 +22,6 @@ Usage: gate_news [options]
Where options are
- --stderrs
- -s
- Print errors to stderr in addition to being logged to logs/fromusenet
-
- --quiet
- -q
- Run quietly. Nothing is output unless there is an exception.
-
--help
-h
Print this text and exit.
@@ -40,18 +32,17 @@ import sys
import os
import string
import time
-import traceback
-import socket
import getopt
-import errno
import paths
from Mailman import mm_cfg
from Mailman import MailList
from Mailman import Utils
from Mailman import Message
+from Mailman import LockFile
from Mailman.Handlers import HandlerAPI
from Mailman.Logging.Utils import LogStdErr
+from Mailman.Logging.Syslog import syslog
# The version we have is from Python 1.5.2+ and fixes the "mode reader"
# problem.
@@ -61,9 +52,17 @@ from Mailman.pythonlib import nntplib
import signal
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
+GATENEWS_LOCK_FILE = os.path.join(mm_cfg.LOCK_DIR, 'gate_news.lock')
+
+LogStdErr('fromusenet', 'gate_news', manual_reprime=0, tee_to_stdout=1)
+
-VERBOSE = 1
-BLOCKFILE = 'gate_news.lck'
+
+def usage(status, msg=''):
+ print __doc__ % globals()
+ if msg:
+ print msg
+ sys.exit(status)
@@ -80,15 +79,14 @@ def open_newsgroup(mlist):
-# XXX: Bogus, but might as we do it `legally'
-QuickEscape = 'QuickEscape'
-
+# This function requires the list to be locked.
def poll_newsgroup(mlist, conn, first, last):
# NEWNEWS is not portable and has synchronization issues.
for num in range(first, last):
try:
headers = conn.head(`num`)[3]
found_to = 0
+ beenthere = 0
for header in headers:
i = string.find(header, ':')
value = string.lower(header[:i])
@@ -97,42 +95,42 @@ def poll_newsgroup(mlist, conn, first, last):
if value <> 'x-beenthere':
continue
if header[i:] == ': %s' % mlist.GetListEmail():
- raise QuickEscape
- body = conn.body(`num`)[3]
- # Usenet originated messages will not have a Unix envelope
- # (i.e. "From " header). This breaks Pipermail archiving, so we
- # will synthesize one. Be sure to use the format searched for by
- # mailbox.UnixMailbox._isrealfromline()
- timehdr = time.ctime(time.time())
- lines = ['From ' + mlist.GetAdminEmail() + ' ' + timehdr]
- lines.extend(headers)
- lines.append('')
- lines.extend(body)
- lines.append('')
- msg = Message.OutgoingMessage(string.join(lines, '\n'))
- msg.fromusenet = 1
- if found_to:
- msg['X-Originally-To'] = msg['To']
- msg['To'] = mlist.GetListEmail()
- # Post the message to the locked list
- if VERBOSE:
- sys.stderr.write('posting msgid %d to list %s\n' %
- (num, mlist.internal_name()))
- HandlerAPI.DeliverToList(mlist, msg, {})
- # record the last gated article number
+ beenthere = 1
+ break
+ if not beenthere:
+ body = conn.body(`num`)[3]
+ # Usenet originated messages will not have a Unix envelope
+ # (i.e. "From " header). This breaks Pipermail archiving, so
+ # we will synthesize one. Be sure to use the format searched
+ # for by mailbox.UnixMailbox._isrealfromline()
+ timehdr = time.ctime(time.time())
+ lines = ['From ' + mlist.GetAdminEmail() + ' ' + timehdr]
+ lines.extend(headers)
+ lines.append('')
+ lines.extend(body)
+ lines.append('')
+ msg = Message.OutgoingMessage(string.join(lines, '\n'))
+ if found_to:
+ msg['X-Originally-To'] = msg['To']
+ msg['To'] = mlist.GetListEmail()
+ # Post the message to the locked list
+ syslog('fromusenet', 'posting msgid %d to list %s' %
+ (num, mlist.internal_name()))
+ HandlerAPI.DeliverToList(mlist, msg, {'fromusenet': 1,
+ '_enqueue_immediate': 1})
+ syslog('fromusenet', 'posted msgid %d to list %s' %
+ (num, mlist.internal_name()))
+ # Even if we don't post the message because it was seen on the
+ # list already, update the watermark
mlist.usenet_watermark = num
- if VERBOSE:
- sys.stderr.write('posted msgid %d to list %s\n' %
- (num, mlist.internal_name()))
except nntplib.error_temp, msg:
- sys.stderr.write('encountered NNTP error for list %s\n' %
- mlist.internal_name())
- sys.stderr.write(str(msg) + '\n')
- except QuickEscape:
- pass # We gated this TO news, don't repost it!
+ syslog('fromusenet', 'NNTP error for list %s, article %d' %
+ (mlist.internal_name(), num))
+ syslog('fromusenet', str(msg))
+# This function requires the list to be locked.
def gate_list(mlist):
# Get the list's watermark, i.e. the last article number that this gated
# from news to mail. None means that this list has never polled its
@@ -159,25 +157,9 @@ def gate_list(mlist):
-def reap(children):
- if not children:
- return
- # See if any children have exited yet
- pid, status = os.waitpid(-1, os.WNOHANG)
- if pid == 0:
- # Nope, none are ready
- return
- try:
- del children[pid]
- except KeyError:
- # Huh? how could this happen?
- pass
-
-
-
def process_lists():
# for waitpids
- children = {}
+ kids = {}
for listname in Utils.list_names():
# Open the list unlocked just to check to see if it is gating news to
# mail. If not, we're done with the list. Otherwise, create a fork
@@ -187,72 +169,55 @@ def process_lists():
continue
pid = os.fork()
if pid:
- # In the parent. record the pid of the child, the child's list
- # name, and last message number. when the child successfully
- # exits, we'll update it's watermark
- children[pid] = pid
+ # In the parent.
+ kids[pid] = pid
else:
- # In the child.
- status = 0
+ # In the child. Try to get the list lock.
+ try:
+ mlist.Lock(timeout=mm_cfg.LIST_LOCK_TIMEOUT)
+ except LockFile.TimeOutError:
+ # oh well, try again later
+ os._exit(0)
try:
- try:
- mlist.Lock()
- gate_list(mlist)
- if VERBOSE:
- sys.stderr.write('%s watermark: %d\n' %
- (mlist.internal_name(),
- mlist.usenet_watermark))
- except:
- # if anything else bad happens, log the exception to
- # stderr. TBD: we should probably generalize
- # scripts/driver to handle this situation
- status = 1
- traceback.print_exc()
+ gate_list(mlist)
+ syslog('fromusenet', '%s watermark: %d' %
+ (mlist.internal_name(), mlist.usenet_watermark))
finally:
mlist.Save()
mlist.Unlock()
- os._exit(status)
- # we're done forking off all the gating children, now just wait for them
- # all to exit, and then we're done
- while children:
- reap(children)
+ # TBD: I'm not 100% sure this is the right thing to do here. What
+ # we want is to guarantee that no matter what happens, the list
+ # data is saved and the lock is relinquished. The finally clause
+ # should make sure about this. If no exception occurs, a child
+ # exit status of 0 should signal a-okay. Otherwise, the exception
+ # should percolate to the top, causing a non-zero exit status,
+ # which will trigger an email by cron.
+ os._exit(0)
+ return kids
def main():
- # block any other gate_news process from running
- blockfile = os.path.join(mm_cfg.DATA_DIR, BLOCKFILE)
+ lock = LockFile.LockFile(GATENEWS_LOCK_FILE,
+ # it's okay to hijack this
+ lifetime=mm_cfg.QRUNNER_LOCK_LIFETIME)
try:
- fd = os.open(blockfile, os.O_CREAT | os.O_EXCL)
- os.close(fd)
- except OSError, e:
- if e.errno <> errno.EEXIST:
- raise
- # some other gate_news process is already running
- if VERBOSE:
- sys.stderr.write('some other gate_news is already running\n')
+ # gate_news runs every 10 minutes
+ lock.lock(timeout=mm_cfg.minutes(5))
+ except LockFile.TimeOutError:
+ syslog('fromusenet', 'could not acquire gate_news lock')
return
try:
- process_lists()
+ kids = process_lists()
finally:
- os.unlink(blockfile)
-
-
-
-def usage(status, msg=''):
- print __doc__ % globals()
- if msg:
- print msg
- sys.exit(status)
-
+ lock.unlock(unconditionally=1)
+ Utils.reap(kids)
+
if __name__ == '__main__':
- global VERBOSE
-
try:
- opts, args = getopt.getopt(sys.argv[1:], 'shq',
- ['stderrs', 'quiet', 'help'])
+ opts, args = getopt.getopt(sys.argv[1:], 'h', ['help'])
except getopt.error, msg:
usage(1, msg)
@@ -260,19 +225,11 @@ if __name__ == '__main__':
usage(1, 'No args are expected')
tee_to_stdout = 0
- VERBOSE = 1
for opt, arg in opts:
if opt in ('-h', '--help'):
usage(0)
- elif opt in ('-s', '--stderrs'):
- tee_to_stdout = 1
- elif opt in ('-q', '--quiet'):
- VERBOSE = 0
# Set up stderr
- LogStdErr('fromusenet', 'gate_news', tee_to_stdout=tee_to_stdout)
- if VERBOSE:
- sys.stderr.write('begin gating\n')
+ syslog('fromusenet', 'begin gating')
main()
- if VERBOSE:
- sys.stderr.write('end gating\n')
+ syslog('fromusenet', 'end gating')