diff --git a/Milter/__init__.py b/Milter/__init__.py
index 26eecf5..0e80797 100755
--- a/Milter/__init__.py
+++ b/Milter/__init__.py
@@ -28,6 +28,7 @@ def uniqueID():
_seq_lock.release()
return seqno
+## @private
OPTIONAL_CALLBACKS = {
'connect':(P_NR_CONN,P_NOCONNECT),
'hello':(P_NR_HELO,P_NOHELO),
@@ -40,6 +41,7 @@ OPTIONAL_CALLBACKS = {
'header':(P_NR_HDR,P_NOHDRS)
}
+## @private
def decode_mask(bits,names):
t = [ (s,getattr(milter,s)) for s in names]
nms = [s for s,m in t if bits & m]
@@ -47,6 +49,13 @@ def decode_mask(bits,names):
if bits: nms += hex(bits)
return nms
+## @fn set_flags(flags)
+# @brief Enable optional %milter actions.
+# Certain %milter actions need to be enabled before calling milter.runmilter()
+# or they throw an exception.
+# @param flags Bit ored mask of optional actions to enable
+
+
## Class decorator to enable optional protocol steps.
# P_SKIP is enabled by default when supported, but
# applications may wish to enable P_HDR_LEADSPC
@@ -538,12 +547,6 @@ class Milter(Base):
# change in configuration.
factory = Milter
-## @fn set_flags(flags)
-# @brief Enable optional %milter actions.
-# Certain %milter actions need to be enabled before calling milter.runmilter()
-# or they throw an exception.
-# @param flags Bit ored mask of optional actions to enable
-
## @private
# @brief Connect context to connection instance and return enabled callbacks.
def negotiate_callback(ctx,opts):
diff --git a/README b/README
index 45dd4ec..c4fc663 100644
--- a/README
+++ b/README
@@ -69,8 +69,7 @@ Not-so-quick Installation
First install Sendmail. Make sure you read libmilter/README in the Sendmail
source directory, and make sure you enable libmilter before you build. The
8.11 series had libmilter marked as FFR (For Future Release); 8.12
-officially
-supports libmilter, but it's still not built by default.
+officially supports libmilter, but it's still not built by default.
Install Python, and enable threading in Modules/Setup.
diff --git a/doc/mainpage.py b/doc/mainpage.py
index e63f2b2..f8d31b8 100644
--- a/doc/mainpage.py
+++ b/doc/mainpage.py
@@ -1,6 +1,5 @@
## @mainpage Writing Milters in Python
#
-#
# At the lowest level, the milter module provides a thin wrapper
# around the sendmail
# libmilter API. This API lets you register callbacks for a number of
@@ -34,3 +33,21 @@
# The mime module provides a wrapper for the Python email package
# that fixes some bugs, and simplifies modifying selected parts of a MIME
# message.
+#
+# @section threading
+#
+# The libmilter library which pymilter wraps
+# handles
+# all signals itself, and expects to be called from a single main thread.
+# It handles SIGTERM, SIGHUP, and SIGINT, mapping the first two to
+# smfi_stop
+# and the last to an internal ABORT.
+#
+# If you use python threads or threading modules, then signal handling gets
+# confused. Threads may still be useful, but you may need to provide an
+# alternate means of causing graceful shutdown.
+#
+# You may find the
+#
+# multiprocessing module useful. It can be a drop-in
+# replacement for threading as illustrated in @ref milter-template.py.
diff --git a/milter-template.py b/milter-template.py
index d995c17..ee72380 100644
--- a/milter-template.py
+++ b/milter-template.py
@@ -14,7 +14,13 @@ import email
import sys
from socket import AF_INET, AF_INET6
from Milter.utils import parse_addr
+if True:
+ from multiprocessing import Process as Thread, Queue
+else:
+ from threading import Thread
+ from Queue import Queue
+logq = Queue(maxsize=4)
class myMilter(Milter.Base):
@@ -117,16 +123,24 @@ class myMilter(Milter.Base):
## === Support Functions ===
def log(self,*msg):
- print "%s [%d]" % (time.strftime('%Y%b%d %H:%M:%S'),self.id),
+ logq.put((msg,self.id,time.time()))
+
+def background():
+ while True:
+ t = logq.get()
+ if not t: break
+ msg,id,ts = t
+ print "%s [%d]" % (time.strftime('%Y%b%d %H:%M:%S',time.localtime(ts)),id),
# 2005Oct13 02:34:11 [1] msg1 msg2 msg3 ...
for i in msg: print i,
print
-
## ===
def main():
- socketname = "/tmp/pythonsock"
+ bt = Thread(target=background)
+ bt.start()
+ socketname = "/home/stuart/pythonsock"
timeout = 600
# Register to have the Milter factory create instances of your class:
Milter.factory = myMilter
@@ -136,7 +150,9 @@ def main():
Milter.set_flags(flags) # tell Sendmail which features we use
print "%s milter startup" % time.strftime('%Y%b%d %H:%M:%S')
sys.stdout.flush()
- Milter.runmilter("test",socketname,timeout)
+ Milter.runmilter("pythonfilter",socketname,timeout)
+ logq.put(None)
+ bt.join()
print "%s bms milter shutdown" % time.strftime('%Y%b%d %H:%M:%S')
if __name__ == "__main__":
diff --git a/pymilter.spec b/pymilter.spec
index 09f3a85..8a7b70b 100644
--- a/pymilter.spec
+++ b/pymilter.spec
@@ -13,8 +13,9 @@ License: GPLv2+
Group: Development/Libraries
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
Url: http://www.bmsi.com/python/milter.html
-Requires: %{pythonbase}, sendmail >= 8.13
-# Need python2.4 specific pydns, not the version for system python
+# python-2.6.4 gets RuntimeError: not holding the import lock
+Requires: %{pythonbase} >= 2.6.5, sendmail >= 8.13
+# Need python2.6 specific pydns, not the version for system python
Requires: %{pythonbase}-pydns
# Needed for callbacks, not a core function but highly useful for milters
BuildRequires: ed, %{pythonbase}-devel, sendmail-devel >= 8.13
@@ -77,6 +78,7 @@ rm -rf $RPM_BUILD_ROOT
* Wed Mar 02 2010 Stuart Gathman 0.9.5-1
- Print milter.error for invalid callback return type.
(Since stacktrace is empty, the TypeError exception is confusing.)
+- Fix milter-template.py
* Wed Mar 02 2010 Stuart Gathman 0.9.4-1
- Handle IP6 in Milter.utils.iniplist()