| 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, runtime
24 from twisted.application import service, internet
25 from twisted.internet import reactor, defer
26 dnotify = None
27 try:
28 import dnotify
29 except:
30 log.msg("unable to import dnotify, so Maildir will use polling instead")
34
36 pollinterval = 10 # only used if we don't have DNotify
37
39 service.MultiService.__init__(self)
40 if basedir:
41 self.setBasedir(basedir)
42 self.files = []
43 self.dnotify = None
44
46 # some users of MaildirService (scheduler.Try_Jobdir, in particular)
47 # don't know their basedir until setServiceParent, since it is
48 # relative to the buildmaster's basedir. So let them set it late. We
49 # don't actually need it until our own startService.
50 self.basedir = basedir
51 self.newdir = os.path.join(self.basedir, "new")
52 self.curdir = os.path.join(self.basedir, "cur")
53
55 service.MultiService.startService(self)
56 if not os.path.isdir(self.newdir) or not os.path.isdir(self.curdir):
57 raise NoSuchMaildir("invalid maildir '%s'" % self.basedir)
58 try:
59 if dnotify:
60 # we must hold an fd open on the directory, so we can get
61 # notified when it changes.
62 self.dnotify = dnotify.DNotify(self.newdir,
63 self.dnotify_callback,
64 [dnotify.DNotify.DN_CREATE])
65 except (IOError, OverflowError):
66 # IOError is probably linux<2.4.19, which doesn't support
67 # dnotify. OverflowError will occur on some 64-bit machines
68 # because of a python bug
69 log.msg("DNotify failed, falling back to polling")
70 if not self.dnotify:
71 t = internet.TimerService(self.pollinterval, self.poll)
72 t.setServiceParent(self)
73 self.poll()
74
76 log.msg("dnotify noticed something, now polling")
77
78 # give it a moment. I found that qmail had problems when the message
79 # was removed from the maildir instantly. It shouldn't, that's what
80 # maildirs are made for. I wasn't able to eyeball any reason for the
81 # problem, and safecat didn't behave the same way, but qmail reports
82 # "Temporary_error_on_maildir_delivery" (qmail-local.c:165,
83 # maildir_child() process exited with rc not in 0,2,3,4). Not sure
84 # why, and I'd have to hack qmail to investigate further, so it's
85 # easier to just wait a second before yanking the message out of new/
86
87 reactor.callLater(0.1, self.poll)
88
89
91 if self.dnotify:
92 self.dnotify.remove()
93 self.dnotify = None
94 return service.MultiService.stopService(self)
95
96 @defer.inlineCallbacks
98 assert self.basedir
99 # see what's new
100 for f in self.files:
101 if not os.path.isfile(os.path.join(self.newdir, f)):
102 self.files.remove(f)
103 newfiles = []
104 for f in os.listdir(self.newdir):
105 if not f in self.files:
106 newfiles.append(f)
107 self.files.extend(newfiles)
108 for n in newfiles:
109 try:
110 yield self.messageReceived(n)
111 except:
112 log.msg("while reading '%s' from maildir '%s':" % (n, self.basedir))
113 log.err()
114
116 if runtime.platformType == "posix":
117 # open the file before moving it, because I'm afraid that once
118 # it's in cur/, someone might delete it at any moment
119 path = os.path.join(self.newdir, filename)
120 f = open(path, "r")
121 os.rename(os.path.join(self.newdir, filename),
122 os.path.join(self.curdir, filename))
123 elif runtime.platformType == "win32":
124 # do this backwards under windows, because you can't move a file
125 # that somebody is holding open. This was causing a Permission
126 # Denied error on bear's win32-twisted1.3 buildslave.
127 os.rename(os.path.join(self.newdir, filename),
128 os.path.join(self.curdir, filename))
129 path = os.path.join(self.curdir, filename)
130 f = open(path, "r")
131
132 return f
133
136
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Wed Nov 21 16:22:52 2012 | http://epydoc.sourceforge.net |