You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2012/03/14 20:40:24 UTC
svn commit: r1300696 [6/6] - in /subversion/branches/fix-rdump-editor: ./
build/generator/ build/win32/ notes/ notes/directory-index/
subversion/bindings/javahl/ subversion/bindings/javahl/native/
subversion/bindings/javahl/tests/org/apache/subversion/...
Modified: subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/rc.d/svnwcsub.debian
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/rc.d/svnwcsub.debian?rev=1300696&r1=1295003&r2=1300696&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/rc.d/svnwcsub.debian (original)
+++ subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/rc.d/svnwcsub.debian Wed Mar 14 19:40:19 2012
@@ -14,21 +14,23 @@
svnwcsub_user=${svnwcsub_user-"svnwc"}
svnwcsub_group=${svnwcsub_group-"svnwc"}
-svnwcsub_reactor=${svnwcsub_reactor-"poll"}
svnwcsub_pidfile=${svnwcsub_pidfile-"/var/run/svnwcsub.pid"}
+svnwcsub_config=${svnwcsub_config-"/etc/svnwcsub.conf"}
+svnwcsub_logfile=${svnwcsub_logfile-"/var/bwlog/svnwcsub/svnwcsub.log"}
pidfile="${svnwcsub_pidfile}"
-TWSITD_CMD="/usr/bin/twistd -y /opt/svnpubsub/svnwcsub.tac \
- --logfile=/var/bwlog/svnpubsub/svnwcsub.log \
- --pidfile=${pidfile} \
- --uid=${svnwcsub_user} --gid=${svnwcsub_group} \
- -r${svnwcsub_reactor}"
+SVNWCSUB_CMD="/opt/svnpubsub/svnwcsub.py \
+ --daemon \
+ --logfile=${svnwcsub_logfile} \
+ --pidfile=${pidfile} \
+ --uid=${svnwcsub_user} --gid=${svnwcsub_group} \
+ ${svnwcsub_config} "
RETVAL=0
start() {
echo "Starting SvnWcSub Server: "
- $TWSITD_CMD
+ $SVNWCSUB_CMD
RETVAL=$?
[ $RETVAL -eq 0 ] && echo "ok" || echo "failed"
return $RETVAL
Modified: subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/rc.d/svnwcsub.solaris
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/rc.d/svnwcsub.solaris?rev=1300696&r1=1295003&r2=1300696&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/rc.d/svnwcsub.solaris (original)
+++ subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/rc.d/svnwcsub.solaris Wed Mar 14 19:40:19 2012
@@ -5,22 +5,24 @@
svnwcsub_user=${svnwcsub_user-"svnwc"}
svnwcsub_group=${svnwcsub_group-"other"}
-svnwcsub_reactor=${svnwcsub_reactor-"poll"}
svnwcsub_pidfile=${svnwcsub_pidfile-"/var/run/svnwcsub/svnwcsub.pid"}
+svnwcsub_config=${svnwcsub_config-"/etc/svnwcsub.conf"}
+svnwcsub_logfile=${svnwcsub_logfile-"/x1/log/svnwcsub/svnwcsub.log"}
pidfile="${svnwcsub_pidfile}"
-TWSITD_CMD="/opt/python/2.6.2/bin/twistd -y /usr/local/svnpubsub/svnwcsub.tac \
- --logfile=/x1/log/svnwcsub.log \
- --pidfile=${pidfile} \
- --umask=002 \
- --uid=${svnwcsub_user} --gid=${svnwcsub_group} \
- -r${svnwcsub_reactor}"
+SVNWCSUB_CMD="/usr/local/svnpubsub/svnwcsub.py \
+ --daemon \
+ --logfile=${svnwcsub_logfile} \
+ --pidfile=${pidfile} \
+ --umask=002 \
+ --uid=${svnwcsub_user} --gid=${svnwcsub_group} \
+ ${svnwcsub_config}"
RETVAL=0
start() {
echo "Starting SvnWcSub Server: "
- $TWSITD_CMD
+ $SVNWCSUB_CMD
RETVAL=$?
[ $RETVAL -eq 0 ] && echo "ok" || echo "failed"
return $RETVAL
Modified: subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svnpubsub/client.py
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svnpubsub/client.py?rev=1300696&r1=1295003&r2=1300696&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svnpubsub/client.py (original)
+++ subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svnpubsub/client.py Wed Mar 14 19:40:19 2012
@@ -74,7 +74,12 @@ class Client(asynchat.async_chat):
self.skipping_headers = True
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
- self.connect((host, port))
+ try:
+ self.connect((host, port))
+ except:
+ self.handle_error()
+ return
+
### should we allow for repository restrictions?
self.push('GET /commits/xml HTTP/1.0\r\n\r\n')
@@ -117,7 +122,7 @@ class XMLStreamHandler(xml.sax.handler.C
def startElement(self, name, attrs):
if name == 'commit':
- self.rev = Revision(attrs['repository'], attrs['revision'])
+ self.rev = Revision(attrs['repository'], int(attrs['revision']))
# No other elements to worry about.
def characters(self, data):
@@ -145,8 +150,8 @@ class XMLStreamHandler(xml.sax.handler.C
class Revision(object):
- def __init__(self, repos, rev):
- self.repos = repos
+ def __init__(self, uuid, rev):
+ self.uuid = uuid
self.rev = rev
self.dirs_changed = [ ]
self.author = None
Modified: subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svnpubsub/server.py
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svnpubsub/server.py?rev=1300696&r1=1295003&r2=1300696&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svnpubsub/server.py (original)
+++ subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svnpubsub/server.py Wed Mar 14 19:40:19 2012
@@ -37,7 +37,7 @@
# URL is built into 3 parts:
# /${type}/${optional_repo_uuid}/${format}
#
-# If the repository UUID is included in the URl, you will only recieve
+# If the repository UUID is included in the URl, you will only receive
# messages about that repository.
#
# Example Pub clients:
@@ -88,7 +88,7 @@ class Revision:
'author': self.author,
'log': self.log,
'date': self.date}}) +","
- elif format == "xml":
+ elif format == "xml":
c = ET.Element('commit', {'repository': self.repos, 'revision': "%d" % (self.rev)})
ET.SubElement(c, 'author').text = self.author
ET.SubElement(c, 'date').text = self.date
@@ -107,7 +107,7 @@ class Revision:
return json.dumps({'commit': {'repository': self.repos,
'revision': self.rev,
'dirs_changed': self.dirs_changed}}) +","
- elif format == "xml":
+ elif format == "xml":
c = ET.Element('commit', {'repository': self.repos, 'revision': "%d" % (self.rev)})
d = ET.SubElement(c, 'dirs_changed')
for p in self.dirs_changed:
@@ -133,7 +133,7 @@ class Client(object):
def finished(self, reason):
self.alive = False
log.msg("CLOSE: %s:%d (%d clients online)"% (self.r.getClientIP(), self.r.client.port, self.pubsub.cc()))
- try:
+ try:
self.pubsub.remove(self)
except ValueError:
pass
@@ -210,11 +210,11 @@ class SvnPubSub(resource.Resource):
else:
fmt = uri[3]
uuid = uri[2]
-
+
if type not in self.clients.keys():
request.setResponseCode(400)
return "Invalid Reuqest Type\n"
-
+
clients = {'json': JSONClient, 'xml': XMLClient}
clientCls = clients.get(fmt)
if clientCls == None:
@@ -262,7 +262,7 @@ def svnpubsub_server():
root.putChild("commits", s)
root.putChild("commit", s)
return server.Site(root)
-
+
if __name__ == "__main__":
log.startLogging(sys.stdout)
# Port 2069 "HTTP Event Port", whatever, sounds good to me
Modified: subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svntweet.py
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svntweet.py?rev=1300696&r1=1295003&r2=1300696&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svntweet.py (original)
+++ subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svntweet.py Wed Mar 14 19:40:19 2012
@@ -21,7 +21,7 @@
#
# Example:
# svntweet.py my-config.json
-#
+#
# With my-config.json containing stream paths and the twitter auth info:
# {"stream": "http://svn-master.apache.org:2069/commits/xml",
# "username": "asfcommits",
@@ -90,9 +90,9 @@ class Revision:
self.log = None
self.date = None
-class StreamHandler(handler.ContentHandler):
+class StreamHandler(handler.ContentHandler):
def __init__(self, bdec):
- handler.ContentHandler.__init__(self)
+ handler.ContentHandler.__init__(self)
self.bdec = bdec
self.rev = None
self.text_value = None
@@ -115,7 +115,7 @@ class StreamHandler(handler.ContentHandl
self.bdec.stillalive()
def characters(self, data):
if self.text_value is not None:
- self.text_value = self.text_value + data
+ self.text_value = self.text_value + data
else:
self.text_value = data
@@ -179,7 +179,7 @@ class BigDoEverythingClasss(object):
def pageStart(self):
log.msg("Stream Connection Established")
self.failures = 0
-
+
def _restartStream(self):
(self.stream, self.transport) = connectTo(self.url, self)
self.stream.deferred.addBoth(self.streamDead)
@@ -253,6 +253,6 @@ def main(config_file):
if __name__ == "__main__":
if len(sys.argv) != 2:
print "invalid args, read source code"
- sys.exit(0)
+ sys.exit(0)
log.startLogging(sys.stdout)
main(sys.argv[1])
Modified: subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svnwcsub.py
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svnwcsub.py?rev=1300696&r1=1295003&r2=1300696&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svnwcsub.py (original)
+++ subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/svnwcsub.py Wed Mar 14 19:40:19 2012
@@ -36,25 +36,22 @@ import os
import re
import ConfigParser
import time
-import logging
+import logging.handlers
import Queue
+import optparse
+import functools
+import urlparse
-from twisted.internet import reactor, task, threads
-from twisted.internet.utils import getProcessOutput
-from twisted.application import internet
-from twisted.web.client import HTTPClientFactory, HTTPPageDownloader
-from urlparse import urlparse
-from xml.sax import handler, make_parser
-from twisted.internet import protocol
-
+import daemonize
+import svnpubsub.client
# check_output() is only available in Python 2.7. Allow us to run with
# earlier versions
try:
check_output = subprocess.check_output
except AttributeError:
- def check_output(args): # note: we don't use anything beyond args
- pipe = subprocess.Popen(args, stdout=subprocess.PIPE)
+ def check_output(args, env): # note: we only use these two args
+ pipe = subprocess.Popen(args, stdout=subprocess.PIPE, env=env)
output, _ = pipe.communicate()
if pipe.returncode:
raise subprocess.CalledProcessError(pipe.returncode, args)
@@ -64,10 +61,10 @@ except AttributeError:
### note: this runs synchronously. within the current Twisted environment,
### it is called from ._get_match() which is run on a thread so it won't
### block the Twisted main loop.
-def svn_info(svnbin, path):
+def svn_info(svnbin, env, path):
"Run 'svn info' on the target path, returning a dict of info data."
args = [svnbin, "info", "--non-interactive", "--", path]
- output = check_output(args).strip()
+ output = check_output(args, env=env).strip()
info = { }
for line in output.split('\n'):
idx = line.index(':')
@@ -77,20 +74,14 @@ def svn_info(svnbin, path):
class WorkingCopy(object):
def __init__(self, bdec, path, url):
- self.bdec = bdec
self.path = path
self.url = url
- self.repos = None
- self.match = None
- d = threads.deferToThread(self._get_match)
- d.addCallback(self._set_match)
-
- def _set_match(self, value):
- self.match = str(value[0])
- self.url = value[1]
- self.repos = value[2]
- self.uuid = value[3]
- self.bdec.wc_ready(self)
+
+ try:
+ self.match, self.uuid = self._get_match(bdec.svnbin, bdec.env)
+ bdec.wc_ready(self)
+ except:
+ logging.exception('problem with working copy: %s', path)
def update_applies(self, uuid, path):
if self.uuid != uuid:
@@ -113,182 +104,44 @@ class WorkingCopy(object):
return True
return False
- def _get_match(self):
+ def _get_match(self, svnbin, env):
### quick little hack to auto-checkout missing working copies
if not os.path.isdir(self.path):
logging.info("autopopulate %s from %s" % (self.path, self.url))
- subprocess.check_call([self.bdec.svnbin, 'co', '-q',
+ subprocess.check_call([svnbin, 'co', '-q',
'--non-interactive',
- '--config-dir',
- '/home/svnwc/.subversion',
- '--', self.url, self.path])
+ '--', self.url, self.path],
+ env=env)
# Fetch the info for matching dirs_changed against this WC
- info = svn_info(self.bdec.svnbin, self.path)
+ info = svn_info(svnbin, env, self.path)
+ root = info['Repository Root']
url = info['URL']
- repos = info['Repository Root']
+ relpath = url[len(root):] # also has leading '/'
uuid = info['Repository UUID']
- relpath = url[len(repos):] # also has leading '/'
- return [relpath, url, repos, uuid]
-
-
-class HTTPStream(HTTPClientFactory):
- protocol = HTTPPageDownloader
-
- def __init__(self, url):
- self.url = url
- HTTPClientFactory.__init__(self, url, method="GET", agent="SvnWcSub/0.1.0")
-
- def pageStart(self, partial):
- pass
-
- def pagePart(self, data):
- pass
+ return str(relpath), uuid
- def pageEnd(self):
- pass
-
-class Revision:
- def __init__(self, repos, rev):
- self.repos = repos
- self.rev = rev
- self.dirs_changed = []
-
-class StreamHandler(handler.ContentHandler):
- def __init__(self, stream, bdec):
- handler.ContentHandler.__init__(self)
- self.stream = stream
- self.bdec = bdec
- self.rev = None
- self.text_value = None
-
- def startElement(self, name, attrs):
- #print "start element: %s" % (name)
- """
- <commit revision="7">
- <dirs_changed><path>/</path></dirs_changed>
- </commit>
- """
- if name == "commit":
- self.rev = Revision(attrs['repository'], int(attrs['revision']))
- elif name == "stillalive":
- self.bdec.stillalive(self.stream)
- def characters(self, data):
- if self.text_value is not None:
- self.text_value = self.text_value + data
- else:
- self.text_value = data
-
- def endElement(self, name):
- #print "end element: %s" % (name)
- if name == "commit":
- self.bdec.commit(self.stream, self.rev)
- self.rev = None
- if name == "path" and self.text_value is not None and self.rev is not None:
- self.rev.dirs_changed.append(self.text_value.strip())
- self.text_value = None
-
-
-class XMLHTTPStream(HTTPStream):
- def __init__(self, url, bdec):
- HTTPStream.__init__(self, url)
- self.alive = 0
- self.bdec = bdec
- self.parser = make_parser(['xml.sax.expatreader'])
- self.handler = StreamHandler(self, bdec)
- self.parser.setContentHandler(self.handler)
-
- def pageStart(self, parital):
- self.bdec.pageStart(self)
-
- def pagePart(self, data):
- self.parser.feed(data)
-
- def pageEnd(self):
- self.bdec.pageEnd(self)
-
-def connectTo(url, bdec):
- u = urlparse(url)
- port = u.port
- if not port:
- port = 80
- s = XMLHTTPStream(url, bdec)
- if bdec.service:
- conn = internet.TCPClient(u.hostname, u.port, s)
- conn.setServiceParent(bdec.service)
- else:
- conn = reactor.connectTCP(u.hostname, u.port, s)
- return [s, conn]
-
-CHECKBEAT_TIME = 60
PRODUCTION_RE_FILTER = re.compile("/websites/production/[^/]+/")
class BigDoEverythingClasss(object):
- def __init__(self, config, service = None):
- self.urls = [s.strip() for s in config.get_value('streams').split()]
+ def __init__(self, config):
self.svnbin = config.get_value('svnbin')
self.env = config.get_env()
+ self.tracking = config.get_track()
self.worker = BackgroundWorker(self.svnbin, self.env)
- self.worker.start()
- self.service = service
- self.failures = 0
- self.alive = time.time()
- self.checker = task.LoopingCall(self._checkalive)
- self.transports = {}
- self.streams = {}
- for u in self.urls:
- self._restartStream(u)
- self.watch = []
- for path, url in config.get_track().items():
+ self.watch = [ ]
+
+ self.hostports = [ ]
+ ### switch from URLs in the config to just host:port pairs
+ for url in config.get_value('streams').split():
+ parsed = urlparse.urlparse(url.strip())
+ self.hostports.append((parsed.hostname, parsed.port))
+
+ def start(self):
+ for path, url in self.tracking.items():
# working copies auto-register with the BDEC when they are ready.
WorkingCopy(self, path, url)
- self.checker.start(CHECKBEAT_TIME)
-
- def pageStart(self, stream):
- logging.info("Stream %s Connection Established" % (stream.url))
- self.failures = 0
-
- def pageEnd(self, stream):
- logging.info("Stream %s Connection Dead" % (stream.url))
- self.streamDead(stream.url)
-
- def _restartStream(self, url):
- (self.streams[url], self.transports[url]) = connectTo(url, self)
- self.streams[url].deferred.addBoth(self.streamDead, url)
- self.streams[url].alive = time.time()
-
- def _checkalive(self):
- n = time.time()
- for k in self.streams.keys():
- s = self.streams[k]
- if n - s.alive > CHECKBEAT_TIME:
- logging.info("Stream %s is dead, reconnecting" % (s.url))
- #self.transports[s.url].disconnect()
- self.streamDead(self, s.url)
-
-# d=filter(lambda x:x not in self.streams.keys(), self.urls)
-# for u in d:
-# self._restartStream(u)
-
- def stillalive(self, stream):
- stream.alive = time.time()
-
- def streamDead(self, url, result=None):
- s = self.streams.get(url)
- if not s:
- logging.info("Stream %s is messed up" % (url))
- return
- BACKOFF_SECS = 5
- BACKOFF_MAX = 60
- #self.checker.stop()
-
- self.streams[url] = None
- self.transports[url] = None
- self.failures += 1
- backoff = min(self.failures * BACKOFF_SECS, BACKOFF_MAX)
- logging.info("Stream disconnected, trying again in %d seconds.... %s" % (backoff, s.url))
- reactor.callLater(backoff, self._restartStream, url)
def wc_ready(self, wc):
# called when a working copy object has its basic info/url,
@@ -302,8 +155,10 @@ class BigDoEverythingClasss(object):
return "/" + path
return os.path.abspath(path)
- def commit(self, stream, rev):
- logging.info("COMMIT r%d (%d paths) via %s" % (rev.rev, len(rev.dirs_changed), stream.url))
+ def commit(self, host, port, rev):
+ logging.info("COMMIT r%d (%d paths) from %s:%d"
+ % (rev.rev, len(rev.dirs_changed), host, port))
+
paths = map(self._normalize_path, rev.dirs_changed)
if len(paths):
pre = os.path.commonprefix(paths)
@@ -317,7 +172,7 @@ class BigDoEverythingClasss(object):
break
#print "Common Prefix: %s" % (pre)
- wcs = [wc for wc in self.watch if wc.update_applies(rev.repos, pre)]
+ wcs = [wc for wc in self.watch if wc.update_applies(rev.uuid, pre)]
logging.info("Updating %d WC for r%d" % (len(wcs), rev.rev))
for wc in wcs:
self.worker.add_work(OP_UPDATE, wc)
@@ -333,12 +188,15 @@ class BackgroundWorker(threading.Thread)
threading.Thread.__init__(self)
# The main thread/process should not wait for this thread to exit.
- self.daemon = True
+ ### compat with Python 2.5
+ self.setDaemon(True)
self.svnbin = svnbin
self.env = env
self.q = Queue.Queue()
+ self.has_started = False
+
def run(self):
while True:
if self.q.qsize() > BACKLOG_TOO_HIGH:
@@ -360,6 +218,12 @@ class BackgroundWorker(threading.Thread)
self.q.task_done()
def add_work(self, operation, wc):
+ # Start the thread when work first arrives. Thread-start needs to
+ # be delayed in case the process forks itself to become a daemon.
+ if not self.has_started:
+ self.start()
+ self.has_started = True
+
self.q.put((operation, wc))
def _update(self, wc):
@@ -375,7 +239,6 @@ class BackgroundWorker(threading.Thread)
### still specific to the ASF setup.
args = [self.svnbin, 'update',
'--quiet',
- '--config-dir', '/home/svnwc/.subversion',
'--non-interactive',
'--trust-server-cert',
'--ignore-externals',
@@ -383,7 +246,7 @@ class BackgroundWorker(threading.Thread)
subprocess.check_call(args, env=self.env)
### check the loglevel before running 'svn info'?
- info = svn_info(self.svnbin, wc.path)
+ info = svn_info(self.svnbin, self.env, wc.path)
logging.info("updated: %s now at r%s", wc.path, info['Revision'])
def _cleanup(self, wc):
@@ -392,7 +255,6 @@ class BackgroundWorker(threading.Thread)
### we need to move some of these args into the config. these are
### still specific to the ASF setup.
args = [self.svnbin, 'cleanup',
- '--config-dir', '/home/svnwc/.subversion',
'--non-interactive',
'--trust-server-cert',
wc.path]
@@ -443,21 +305,145 @@ class ReloadableConfig(ConfigParser.Safe
return str(option)
-def main(config_file):
+class Daemon(daemonize.Daemon):
+ def __init__(self, logfile, pidfile, umask, bdec):
+ daemonize.Daemon.__init__(self, logfile, pidfile)
+
+ self.umask = umask
+ self.bdec = bdec
+
+ def setup(self):
+ # There is no setup which the parent needs to wait for.
+ pass
+
+ def run(self):
+ logging.info('svnwcsub started, pid=%d', os.getpid())
+
+ # Set the umask in the daemon process. Defaults to 000 for
+ # daemonized processes. Foreground processes simply inherit
+ # the value from the parent process.
+ if self.umask is not None:
+ umask = int(self.umask, 8)
+ os.umask(umask)
+ logging.info('umask set to %03o', umask)
+
+ # Start the BDEC (on the main thread), then start the client
+ self.bdec.start()
+
+ mc = svnpubsub.client.MultiClient(self.bdec.hostports,
+ self.bdec.commit,
+ self._event)
+ mc.run_forever()
+
+ def _event(self, host, port, event_name):
+ if event_name == 'error':
+ logging.exception('from %s:%s', host, port)
+ elif event_name == 'ping':
+ logging.debug('ping from %s:%s', host, port)
+ else:
+ logging.info('"%s" from %s:%s', event_name, host, port)
+
+
+def prepare_logging(logfile):
+ "Log to the specified file, or to stdout if None."
+
+ if logfile:
+ # Rotate logs daily, keeping 7 days worth.
+ handler = logging.handlers.TimedRotatingFileHandler(
+ logfile, when='midnight', backupCount=7,
+ )
+ else:
+ handler = logging.StreamHandler(sys.stdout)
+
+ # Add a timestamp to the log records
+ formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(message)s',
+ '%Y-%m-%d %H:%M:%S')
+ handler.setFormatter(formatter)
+
+ # Apply the handler to the root logger
+ root = logging.getLogger()
+ root.addHandler(handler)
+
+ ### use logging.INFO for now. switch to cmdline option or a config?
+ root.setLevel(logging.INFO)
+
+
+def handle_options(options):
+ # Set up the logging, then process the rest of the options.
+ prepare_logging(options.logfile)
+
+ # In daemon mode, we let the daemonize module handle the pidfile.
+ # Otherwise, we should write this (foreground) PID into the file.
+ if options.pidfile and not options.daemon:
+ pid = os.getpid()
+ open(options.pidfile, 'w').write('%s\n' % pid)
+ logging.info('pid %d written to %s', pid, options.pidfile)
+
+ if options.gid:
+ try:
+ gid = int(options.gid)
+ except ValueError:
+ import grp
+ gid = grp.getgrnam(options.gid)[2]
+ logging.info('setting gid %d', gid)
+ os.setgid(gid)
+
+ if options.uid:
+ try:
+ uid = int(options.uid)
+ except ValueError:
+ import pwd
+ uid = pwd.getpwnam(options.uid)[2]
+ logging.info('setting uid %d', uid)
+ os.setuid(uid)
+
+
+def main(args):
+ parser = optparse.OptionParser(
+ description='An SvnPubSub client to keep working copies synchronized '
+ 'with a repository.',
+ usage='Usage: %prog [options] CONFIG_FILE',
+ )
+ parser.add_option('--logfile',
+ help='filename for logging')
+ parser.add_option('--pidfile',
+ help="the process' PID will be written to this file")
+ parser.add_option('--uid',
+ help='switch to this UID before running')
+ parser.add_option('--gid',
+ help='switch to this GID before running')
+ parser.add_option('--umask',
+ help='set this (octal) umask before running')
+ parser.add_option('--daemon', action='store_true',
+ help='run as a background daemon')
+
+ options, extra = parser.parse_args(args)
+
+ if len(extra) != 1:
+ parser.error('CONFIG_FILE is required')
+ config_file = extra[0]
+
+ if options.daemon and not options.logfile:
+ parser.error('LOGFILE is required when running as a daemon')
+ if options.daemon and not options.pidfile:
+ parser.error('PIDFILE is required when running as a daemon')
+
+ # Process any provided options.
+ handle_options(options)
+
c = ReloadableConfig(config_file)
- big = BigDoEverythingClasss(c)
- reactor.run()
+ bdec = BigDoEverythingClasss(c)
+
+ # We manage the logfile ourselves (along with possible rotation). The
+ # daemon process can just drop stdout/stderr into /dev/null.
+ d = Daemon('/dev/null', options.pidfile, options.umask, bdec)
+ if options.daemon:
+ # Daemonize the process and call sys.exit() with appropriate code
+ d.daemonize_exit()
+ else:
+ # Just run in the foreground (the default)
+ d.foreground()
if __name__ == "__main__":
- if len(sys.argv) != 2:
- print "invalid args, read source code"
- sys.exit(0)
-
- ### use logging.INFO for now. review/adjust the calls above for the
- ### proper logging level. then remove the level (to return to default).
- ### future: switch to config for logfile and loglevel.
- logging.basicConfig(level=logging.INFO, stream=sys.stdout,
- datefmt='%Y-%m-%d %H:%M:%S',
- format='%(asctime)s [%(levelname)s] %(message)s')
- main(sys.argv[1])
+ main(sys.argv[1:])
Modified: subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/test.conf
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/test.conf?rev=1300696&r1=1295003&r2=1300696&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/test.conf (original)
+++ subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/test.conf Wed Mar 14 19:40:19 2012
@@ -1,4 +1,9 @@
-# For use with testserver.py
+# For use with connecting to testserver.py
[DEFAULT]
+svnbin: svn
streams: http://127.0.0.1:2069/commits/xml
+
+[env]
+
+[track]
Modified: subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/watcher.py
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/watcher.py?rev=1300696&r1=1295003&r2=1300696&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/watcher.py (original)
+++ subversion/branches/fix-rdump-editor/tools/server-side/svnpubsub/watcher.py Wed Mar 14 19:40:19 2012
@@ -53,5 +53,5 @@ def main(config_file):
if __name__ == "__main__":
if len(sys.argv) != 2:
print "invalid args, read source code"
- sys.exit(0)
+ sys.exit(0)
main(sys.argv[1])