| Trees | Indices | Help |
|
|---|
|
|
1 # This file is part of Buildbot. Buildbot is free software: you can
2 # redistribute it and/or modify it under the terms of the GNU General Public
3 # License as published by the Free Software Foundation, version 2.
4 #
5 # This program is distributed in the hope that it will be useful, but WITHOUT
6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
8 # details.
9 #
10 # You should have received a copy of the GNU General Public License along with
11 # this program; if not, write to the Free Software Foundation, Inc., 51
12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
13 #
14 # Copyright Buildbot Team Members
15
16
17 # This is a class which watches a maildir for new messages. It uses the
18 # linux dirwatcher API (if available) to look for new files. The
19 # .messageReceived method is invoked with the filename of the new message,
20 # relative to the top of the maildir (so it will look like "new/blahblah").
21
22 import os
23 from twisted.python import log
24 from twisted.application import service, internet
25 from twisted.internet import reactor
26 dnotify = None
27 try:
28 import dnotify
29 except:
30 # I'm not actually sure this log message gets recorded
31 log.msg("unable to import dnotify, so Maildir will use polling instead")
32
35
37 """I watch a maildir for new messages. I should be placed as the service
38 child of some MultiService instance. When running, I use the linux
39 dirwatcher API (if available) or poll for new files in the 'new'
40 subdirectory of my maildir path. When I discover a new message, I invoke
41 my .messageReceived() method with the short filename of the new message,
42 so the full name of the new file can be obtained with
43 os.path.join(maildir, 'new', filename). messageReceived() should be
44 overridden by a subclass to do something useful. I will not move or
45 delete the file on my own: the subclass's messageReceived() should
46 probably do that.
47 """
48 pollinterval = 10 # only used if we don't have DNotify
49
51 """Create the Maildir watcher. BASEDIR is the maildir directory (the
52 one which contains new/ and tmp/)
53 """
54 service.MultiService.__init__(self)
55 self.basedir = basedir
56 self.files = []
57 self.dnotify = None
58
60 # some users of MaildirService (scheduler.Try_Jobdir, in particular)
61 # don't know their basedir until setServiceParent, since it is
62 # relative to the buildmaster's basedir. So let them set it late. We
63 # don't actually need it until our own startService.
64 self.basedir = basedir
65
67 service.MultiService.startService(self)
68 self.newdir = os.path.join(self.basedir, "new")
69 if not os.path.isdir(self.basedir) or not os.path.isdir(self.newdir):
70 raise NoSuchMaildir("invalid maildir '%s'" % self.basedir)
71 try:
72 if dnotify:
73 # we must hold an fd open on the directory, so we can get
74 # notified when it changes.
75 self.dnotify = dnotify.DNotify(self.newdir,
76 self.dnotify_callback,
77 [dnotify.DNotify.DN_CREATE])
78 except (IOError, OverflowError):
79 # IOError is probably linux<2.4.19, which doesn't support
80 # dnotify. OverflowError will occur on some 64-bit machines
81 # because of a python bug
82 log.msg("DNotify failed, falling back to polling")
83 if not self.dnotify:
84 t = internet.TimerService(self.pollinterval, self.poll)
85 t.setServiceParent(self)
86 self.poll()
87
89 log.msg("dnotify noticed something, now polling")
90
91 # give it a moment. I found that qmail had problems when the message
92 # was removed from the maildir instantly. It shouldn't, that's what
93 # maildirs are made for. I wasn't able to eyeball any reason for the
94 # problem, and safecat didn't behave the same way, but qmail reports
95 # "Temporary_error_on_maildir_delivery" (qmail-local.c:165,
96 # maildir_child() process exited with rc not in 0,2,3,4). Not sure
97 # why, and I'd have to hack qmail to investigate further, so it's
98 # easier to just wait a second before yanking the message out of new/
99
100 reactor.callLater(0.1, self.poll)
101
102
104 if self.dnotify:
105 self.dnotify.remove()
106 self.dnotify = None
107 return service.MultiService.stopService(self)
108
110 assert self.basedir
111 # see what's new
112 for f in self.files:
113 if not os.path.isfile(os.path.join(self.newdir, f)):
114 self.files.remove(f)
115 newfiles = []
116 for f in os.listdir(self.newdir):
117 if not f in self.files:
118 newfiles.append(f)
119 self.files.extend(newfiles)
120 # TODO: sort by ctime, then filename, since safecat uses a rather
121 # fine-grained timestamp in the filename
122 for n in newfiles:
123 # TODO: consider catching exceptions in messageReceived
124 self.messageReceived(n)
125
127 """Called when a new file is noticed. Will call
128 self.parent.messageReceived() with a path relative to maildir/new.
129 Should probably be overridden in subclasses."""
130 self.parent.messageReceived(filename)
131
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Sun Dec 19 18:26:51 2010 | http://epydoc.sourceforge.net |