Skip to content

Commit 114bc80

Browse files
committed
Improve file handling and cleanup in Runner and Switchboard classes
- Add proper file cleanup in Runner._onefile() after successful processing - Enhance Switchboard.finish() with better error handling and logging - Add cleanup of stale backup files in Switchboard - Improve file locking and atomic operations - Add detailed logging for file operations - Fix file removal after processing - Add validation of file operations
1 parent 94fc278 commit 114bc80

2 files changed

Lines changed: 81 additions & 46 deletions

File tree

Mailman/Queue/Runner.py

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -211,20 +211,45 @@ def _oneloop(self):
211211
try:
212212
# Dequeue the file
213213
msg, msgdata = self._switchboard.dequeue(filebase)
214-
if msg is None:
214+
if msg is None or msgdata is None:
215+
syslog('error', 'Runner._oneloop: Failed to dequeue file %s (got None values)', filebase)
215216
continue
216-
217+
218+
# Get the list name
219+
listname = msgdata.get('listname', 'unknown')
220+
try:
221+
mlist = MailList.MailList(listname, lock=False)
222+
except Errors.MMUnknownListError:
223+
syslog('error', 'Runner._oneloop: Unknown list %s for message %s',
224+
listname, msg.get('message-id', 'n/a'))
225+
self._shunt.enqueue(msg, msgdata)
226+
continue
227+
217228
# Process the message
218229
try:
219-
self._onefile(msg, msgdata)
230+
result = self._onefile(mlist, msg, msgdata)
231+
if result:
232+
# Message was successfully processed, finish and remove the file
233+
self._switchboard.finish(filebase)
234+
syslog('debug', 'Runner._oneloop: Successfully processed message %s, removed file %s',
235+
msg.get('message-id', 'n/a'), filebase)
236+
elif result is False:
237+
# Message needs to be requeued
238+
self._switchboard.enqueue(msg, msgdata)
239+
syslog('debug', 'Runner._oneloop: Requeued message %s', msg.get('message-id', 'n/a'))
240+
else:
241+
# Message was shunted
242+
self._shunt.enqueue(msg, msgdata)
243+
syslog('debug', 'Runner._oneloop: Shunted message %s', msg.get('message-id', 'n/a'))
244+
return True
220245
except Exception as e:
221-
# Log the error and shunt the message
222-
self._handle_error(e, msg=msg, mlist=None)
223-
continue
224-
246+
syslog('error', 'Runner._oneloop: Error processing message %s: %s\nTraceback:\n%s',
247+
msg.get('message-id', 'n/a'), str(e), traceback.format_exc())
248+
self._shunt.enqueue(msg, msgdata)
249+
return False
225250
except Exception as e:
226-
# Log the error and continue
227-
syslog('error', 'Runner._oneloop: Error dequeuing file %s: %s', filebase, str(e))
251+
syslog('error', 'Runner._oneloop: Error processing file %s: %s\nTraceback:\n%s',
252+
filebase, str(e), traceback.format_exc())
228253
continue
229254

230255
except Exception as e:
@@ -302,7 +327,7 @@ def _validate_message(self, msg, msgdata):
302327
msgid, str(e), traceback.format_exc())
303328
return msg, False
304329

305-
def _onefile(self, msg, msgdata):
330+
def _onefile(self, mlist, msg, msgdata):
306331
"""Process a single file from the queue."""
307332
try:
308333
# Get the list name from the message data

Mailman/Queue/Switchboard.py

Lines changed: 46 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -244,64 +244,74 @@ def dequeue(self, filebase):
244244
pass
245245

246246
def finish(self, filebase, preserve=False):
247+
"""Finish processing a file by either removing it or moving it to the shunt queue.
248+
249+
Args:
250+
filebase: The base name of the file to process
251+
preserve: If True, move the file to the shunt queue instead of removing it
252+
"""
253+
if not filebase:
254+
mailman_log('error', 'Switchboard.finish: No filebase provided')
255+
return
256+
247257
bakfile = os.path.join(self.__whichq, filebase + '.bak')
258+
pckfile = os.path.join(self.__whichq, filebase + '.pck')
259+
260+
# First check if the backup file exists
261+
if not os.path.exists(bakfile):
262+
mailman_log('error', 'Switchboard.finish: Backup file does not exist: %s', bakfile)
263+
# Try to clean up the .pck file if it exists
264+
if os.path.exists(pckfile):
265+
try:
266+
os.unlink(pckfile)
267+
mailman_log('info', 'Switchboard.finish: Removed stale .pck file: %s', pckfile)
268+
except OSError as e:
269+
mailman_log('error', 'Switchboard.finish: Failed to remove stale .pck file %s: %s',
270+
pckfile, str(e))
271+
return
272+
248273
try:
249274
if preserve:
250-
psvfile = os.path.join(mm_cfg.BADQUEUE_DIR, filebase + '.psv')
251-
# Log the reason for moving to bad queue
252-
mailman_log('info', 'Moving message to bad queue: %s (queue: %s)', filebase, self.__whichq)
275+
# Move the file to the shunt queue
276+
psvfile = os.path.join(mm_cfg.SHUNTQUEUE_DIR, filebase + '.bak')
253277

254-
# Create the directory if it doesn't yet exist.
255-
# Copied from __init__.
256-
omask = os.umask(0) # rwxrws---
257-
try:
278+
# Ensure the shunt queue directory exists
279+
if not os.path.exists(mm_cfg.SHUNTQUEUE_DIR):
258280
try:
259-
os.mkdir(mm_cfg.BADQUEUE_DIR, 0o0770)
281+
os.makedirs(mm_cfg.SHUNTQUEUE_DIR, 0o775)
260282
except OSError as e:
261-
if e.errno != errno.EEXIST:
262-
mailman_log('error', 'Failed to create shunt queue directory %s: %s\nTraceback:\n%s',
263-
mm_cfg.BADQUEUE_DIR, str(e), traceback.format_exc())
264-
raise
265-
finally:
266-
os.umask(omask)
283+
mailman_log('error', 'Switchboard.finish: Failed to create shunt queue directory: %s',
284+
str(e))
285+
raise
267286

268-
# Verify source file exists before moving
269-
if not os.path.exists(bakfile):
270-
mailman_log('error', 'Source backup file does not exist: %s', bakfile)
271-
return
272-
273287
# Move the file and verify
274288
try:
275289
os.rename(bakfile, psvfile)
276290
if not os.path.exists(psvfile):
277-
mailman_log('error', 'Failed to move backup file to shunt queue: %s -> %s',
278-
bakfile, psvfile)
291+
mailman_log('error', 'Switchboard.finish: Failed to move backup file to shunt queue: %s -> %s',
292+
bakfile, psvfile)
279293
else:
280-
mailman_log('info', 'Successfully moved backup file to shunt queue: %s -> %s',
281-
bakfile, psvfile)
294+
mailman_log('info', 'Switchboard.finish: Successfully moved backup file to shunt queue: %s -> %s',
295+
bakfile, psvfile)
282296
except OSError as e:
283-
mailman_log('error', 'Failed to move backup file to shunt queue: %s -> %s: %s\nTraceback:\n%s',
284-
bakfile, psvfile, str(e), traceback.format_exc())
297+
mailman_log('error', 'Switchboard.finish: Failed to move backup file to shunt queue: %s -> %s: %s',
298+
bakfile, psvfile, str(e))
285299
raise
286300
else:
287-
# Verify file exists before unlinking
288-
if not os.path.exists(bakfile):
289-
mailman_log('error', 'Backup file does not exist for unlinking: %s', bakfile)
290-
return
291-
301+
# Remove the backup file
292302
try:
293303
os.unlink(bakfile)
294304
if os.path.exists(bakfile):
295-
mailman_log('error', 'Failed to unlink backup file: %s', bakfile)
305+
mailman_log('error', 'Switchboard.finish: Failed to unlink backup file: %s', bakfile)
296306
else:
297-
mailman_log('info', 'Successfully unlinked backup file: %s', bakfile)
307+
mailman_log('info', 'Switchboard.finish: Successfully unlinked backup file: %s', bakfile)
298308
except OSError as e:
299-
mailman_log('error', 'Failed to unlink backup file %s: %s\nTraceback:\n%s',
300-
bakfile, str(e), traceback.format_exc())
309+
mailman_log('error', 'Switchboard.finish: Failed to unlink backup file %s: %s',
310+
bakfile, str(e))
301311
raise
302312
except Exception as e:
303-
mailman_log('error', 'Failed to finish processing backup file %s: %s\nTraceback:\n%s',
304-
bakfile, str(e), traceback.format_exc())
313+
mailman_log('error', 'Switchboard.finish: Failed to finish processing backup file %s: %s',
314+
bakfile, str(e))
305315
raise
306316

307317
def files(self, extension='.pck'):

0 commit comments

Comments
 (0)