Skip to content

Commit ec6a388

Browse files
committed
update
1 parent bc8c38a commit ec6a388

4 files changed

Lines changed: 68 additions & 58 deletions

File tree

Mailman/Handlers/Decorate.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,19 +201,31 @@ def process(mlist, msg, msgdata):
201201
msg['Content-Type'] = 'multipart/mixed'
202202

203203

204-
205204
def decorate(mlist, template, what, extradict=None):
206205
# `what' is just a descriptive phrase used in the log message
207206

208207
# If template is None, return empty string
209208
if template is None:
209+
syslog('error', 'Template is None for %s', what)
210210
return ''
211211

212212
# If template is a Message object, get its content
213213
if isinstance(template, Message):
214-
template = template.get_payload(decode=True)
215-
if isinstance(template, bytes):
216-
template = template.decode('utf-8', 'replace')
214+
try:
215+
template = template.get_payload(decode=True)
216+
if isinstance(template, bytes):
217+
template = template.decode('utf-8', 'replace')
218+
except Exception as e:
219+
syslog('error', 'Error getting payload from Message template for %s: %s', what, str(e))
220+
return ''
221+
222+
# Ensure template is a string
223+
if not isinstance(template, str):
224+
try:
225+
template = str(template)
226+
except Exception as e:
227+
syslog('error', 'Error converting template to string for %s: %s', what, str(e))
228+
return ''
217229

218230
# If template is only whitespace, ignore it.
219231
if len(re.sub(r'\s', '', template)) == 0:

Mailman/Queue/Runner.py

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,6 @@ def _oneloop(self):
186186
files = self._switchboard.files()
187187
filecnt = len(files)
188188

189-
# Only log at debug level if we found files to process
190-
if filecnt > 0:
191-
syslog('debug', 'Runner._oneloop: Found %d files to process', filecnt)
192-
193189
# Process each file
194190
for filebase in files:
195191
try:
@@ -198,41 +194,22 @@ def _oneloop(self):
198194
if msg is None:
199195
continue
200196

201-
syslog('info', 'Runner._oneloop: Successfully dequeued file %s', filebase)
202-
203197
# Process the message
204198
try:
205-
# Get the list name from the message data
206-
listname = msgdata.get('listname', mm_cfg.MAILMAN_SITE_LIST)
207-
208-
# Process the message
209-
result = self._dispose(listname, msg, msgdata)
210-
211-
# If the message should be kept in the queue, requeue it
212-
if result:
213-
self._switchboard.enqueue(msg, msgdata)
214-
syslog('info', 'Runner._oneloop: Message requeued for later processing: %s', filebase)
215-
else:
216-
syslog('info', 'Runner._oneloop: Message processing complete, moving to shunt queue %s (msgid: %s)',
217-
filebase, msg.get('message-id', 'n/a'))
218-
199+
self._dopost(msg, msgdata)
219200
except Exception as e:
220-
syslog('error', 'Runner._oneloop: Error processing message: %s\n%s',
221-
str(e), traceback.format_exc())
222-
# Move to shunt queue on error
223-
self._shunt.enqueue(msg, msgdata)
201+
syslog('error', 'Runner._oneloop: Error processing message %s: %s', filebase, str(e))
202+
continue
224203

225204
except Exception as e:
226-
syslog('error', 'Runner._oneloop: Error dequeuing file %s: %s\n%s',
227-
filebase, str(e), traceback.format_exc())
205+
syslog('error', 'Runner._oneloop: Error dequeuing file %s: %s', filebase, str(e))
206+
continue
228207

229-
# Only log completion at debug level if we processed files
230-
if filecnt > 0:
231-
syslog('debug', 'Runner._oneloop: Loop complete, processed %d files', filecnt)
232-
233208
except Exception as e:
234-
syslog('error', 'Runner._oneloop: Unexpected error in main loop: %s\n%s',
235-
str(e), traceback.format_exc())
209+
syslog('error', 'Runner._oneloop: Error in main loop: %s', str(e))
210+
return 0
211+
212+
return filecnt
236213

237214
def _validate_message(self, msg, msgdata):
238215
"""Validate and convert message if needed.

Mailman/Queue/Switchboard.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -114,81 +114,67 @@ def enqueue(self, msg, msgdata=None, listname=None, _plaintext=False, **kwargs):
114114
# Store any additional keyword arguments in msgdata
115115
msgdata.update(kwargs)
116116

117-
mailman_log('debug', 'Switchboard.enqueue: Starting to enqueue message for list %s', listname or 'unknown')
118-
119117
# Convert string message to Message object if needed
120118
if isinstance(msg, str):
121119
try:
122120
msg = email.message_from_string(msg)
123-
mailman_log('debug', 'Switchboard.enqueue: Converted string message to Message object')
124121
except Exception as e:
125122
mailman_log('error', 'Switchboard.enqueue: Failed to convert string message to Message object: %s', str(e))
126123
raise
127124

128125
# Generate a unique filebase
129126
filebase = self._make_filebase(msg, msgdata)
130-
mailman_log('debug', 'Switchboard.enqueue: Generated filebase %s', filebase)
131127

132128
# Calculate the filename
133129
filename = os.path.join(self.__whichq, filebase + '.pck')
134-
mailman_log('debug', 'Switchboard.enqueue: Target filename is %s', filename)
135130

136131
# Create a lock file
137132
lockfile = filename + '.lock'
138133
try:
139134
fd = os.open(lockfile, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o644)
140135
os.close(fd)
141-
mailman_log('debug', 'Switchboard.enqueue: Created lock file for %s', filebase)
142136
except OSError as e:
143137
if e.errno != errno.EEXIST:
144138
mailman_log('error', 'Switchboard.enqueue: Failed to create lock file for %s: %s', filebase, str(e))
145139
raise
146-
mailman_log('debug', 'Switchboard.enqueue: Lock file already exists for %s', filebase)
147140
return None
148141

149142
try:
150143
# Write the message and metadata
151144
try:
152145
self._enqueue(filename, msg, msgdata, _plaintext)
153-
mailman_log('debug', 'Switchboard.enqueue: Successfully wrote message and data to %s', filebase)
154146
except Exception as e:
155147
mailman_log('error', 'Switchboard.enqueue: Failed to write message to %s: %s', filebase, str(e))
156148
raise
157149

158-
mailman_log('debug', 'Switchboard.enqueue: Successfully enqueued message to %s', filebase)
159150
return filebase
160151
finally:
161152
# Always clean up the lock file
162153
try:
163154
os.unlink(lockfile)
164-
mailman_log('debug', 'Switchboard.enqueue: Cleaned up lock file %s', lockfile)
165155
except OSError:
166156
pass
167157

168158
def dequeue(self, filebase):
169159
# Calculate the filename from the given filebase.
170160
filename = os.path.join(self.__whichq, filebase + '.pck')
171-
mailman_log('debug', 'Switchboard.dequeue: Attempting to dequeue file %s', filebase)
172161

173162
# Create a lock file
174163
lockfile = filename + '.lock'
175164
try:
176165
# Try to create the lock file
177166
fd = os.open(lockfile, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o644)
178167
os.close(fd)
179-
mailman_log('debug', 'Switchboard.dequeue: Created lock file for %s', filebase)
180168
except OSError as e:
181169
if e.errno != errno.EEXIST:
182170
mailman_log('error', 'Switchboard.dequeue: Failed to create lock file for %s: %s', filebase, str(e))
183171
raise
184-
mailman_log('debug', 'Switchboard.dequeue: Lock file already exists for %s', filebase)
185172
return None, None
186173

187174
try:
188175
# Read the message and metadata
189176
try:
190177
msg, data = self._dequeue(filename)
191-
mailman_log('debug', 'Switchboard.dequeue: Successfully read message and data from %s', filebase)
192178
except Exception as e:
193179
mailman_log('error', 'Switchboard.dequeue: Failed to read message from %s: %s', filebase, str(e))
194180
raise
@@ -198,13 +184,11 @@ def dequeue(self, filebase):
198184
mailman_log('error', 'Switchboard.dequeue: Invalid data structure in %s: expected dict, got %s', filename, type(data))
199185
return None, None
200186

201-
mailman_log('debug', 'Switchboard.dequeue: Successfully dequeued file %s', filebase)
202187
return msg, data
203188
finally:
204189
# Always clean up the lock file
205190
try:
206191
os.unlink(lockfile)
207-
mailman_log('debug', 'Switchboard.dequeue: Cleaned up lock file %s', lockfile)
208192
except OSError:
209193
pass
210194

bin/mailmanctl

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ import errno
103103
import pwd
104104
import grp
105105
import socket
106+
import psutil # Add psutil import
106107

107108
import paths
108109
from Mailman import mm_cfg
@@ -210,19 +211,53 @@ def get_lock_data():
210211

211212

212213
def qrunner_state():
213-
# 1 if proc exists on host (but is it qrunner? ;)
214-
# 0 if host matches but no proc
214+
# 1 if proc exists on host and is owned by mailman user
215+
# 0 if host matches but no proc or wrong owner
215216
# hostname if hostname doesn't match
216217
hostname, pid, tempfile = get_lock_data()
217218
if hostname != socket.gethostname():
218219
return hostname
219220
# Find out if the process exists by calling kill with a signal 0.
220221
try:
221222
os.kill(pid, 0)
223+
# Process exists, now check if it's owned by the mailman user
224+
mailman_uid = pwd.getpwnam(mm_cfg.MAILMAN_USER).pw_uid
225+
try:
226+
# Try to get process owner using platform-specific methods
227+
if os.name == 'posix':
228+
# On Unix-like systems, try to get process owner
229+
try:
230+
# Try using /proc on Linux
231+
if os.path.exists('/proc'):
232+
with open(f'/proc/{pid}/status') as f:
233+
for line in f:
234+
if line.startswith('Uid:'):
235+
uid = int(line.split()[1])
236+
if uid != mailman_uid:
237+
syslog('error', 'Process %d exists but is owned by uid %d, not mailman user %d',
238+
pid, uid, mailman_uid)
239+
return 0
240+
break
241+
else:
242+
# On other Unix systems, we can't easily check the owner
243+
# without external tools, so we'll assume it's valid
244+
# if the process exists
245+
return 1
246+
except (IOError, OSError) as e:
247+
syslog('error', 'Error checking process %d ownership: %s', pid, str(e))
248+
return 0
249+
else:
250+
# On non-Unix systems, we can't easily check the owner
251+
# without external tools, so we'll assume it's valid
252+
# if the process exists
253+
return 1
254+
return 1
255+
except Exception as e:
256+
syslog('error', 'Error checking process %d ownership: %s', pid, str(e))
257+
return 0
222258
except OSError as e:
223259
if e.errno != errno.ESRCH: raise
224260
return 0
225-
return 1
226261

227262

228263
def acquire_lock_1(force):
@@ -233,10 +268,12 @@ def acquire_lock_1(force):
233268
lock.lock(0.1)
234269
return lock
235270
except LockFile.TimeOutError:
236-
# If we're not forcing or the lock can't be determined to be stale.
237-
if not force or qrunner_state():
271+
# Check if the lock is stale by examining the process
272+
status = qrunner_state()
273+
if status == 1:
274+
# Process exists and is running, so lock is valid
238275
raise
239-
# Force removal of lock first
276+
# Lock appears to be stale - clean it up
240277
try:
241278
# Read the current lock file content
242279
with open(LOCKFILE) as fp:

0 commit comments

Comments
 (0)