<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[13445] CalendarServer/trunk/txdav/base/datastore/subpostgres.py</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/13445">13445</a></dd>
<dt>Author</dt> <dd>wsanchez@apple.com</dd>
<dt>Date</dt> <dd>2014-05-05 14:26:20 -0700 (Mon, 05 May 2014)</dd>
</dl>
<h3>Log Message</h3>
<pre>Use pipes.quote() to quote shell args to postgres -o</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServertrunktxdavbasedatastoresubpostgrespy">CalendarServer/trunk/txdav/base/datastore/subpostgres.py</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServertrunktxdavbasedatastoresubpostgrespy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/base/datastore/subpostgres.py (13444 => 13445)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/base/datastore/subpostgres.py        2014-05-05 16:30:29 UTC (rev 13444)
+++ CalendarServer/trunk/txdav/base/datastore/subpostgres.py        2014-05-05 21:26:20 UTC (rev 13445)
</span><span class="lines">@@ -23,17 +23,17 @@
</span><span class="cx"> import pwd
</span><span class="cx"> import re
</span><span class="cx"> import signal
</span><del>-
</del><span class="cx"> from hashlib import md5
</span><ins>+from pipes import quote as shell_quote
</ins><span class="cx">
</span><ins>+import pgdb
+
</ins><span class="cx"> from twisted.python.procutils import which
</span><span class="cx"> from twisted.internet.protocol import ProcessProtocol
</span><span class="cx">
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> from twext.python.filepath import CachingFilePath
</span><span class="cx">
</span><del>-import pgdb
-
</del><span class="cx"> from twisted.protocols.basic import LineReceiver
</span><span class="cx"> from twisted.internet.defer import Deferred
</span><span class="cx"> from txdav.base.datastore.dbapiclient import DBAPIConnector
</span><span class="lines">@@ -87,7 +87,10 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def processEnded(self, reason):
</span><del>- log.warn("postgres process ended with status {status}", status=reason.value.status)
</del><ins>+ log.warn(
+ "postgres process ended with status {status}",
+ status=reason.value.status
+ )
</ins><span class="cx"> # If pg_ctl exited with zero, we were successful in starting postgres
</span><span class="cx"> # If pg_ctl exited with nonzero, we need to give up.
</span><span class="cx"> self.lineReceiver.connectionLost(reason)
</span><span class="lines">@@ -164,21 +167,23 @@
</span><span class="cx">
</span><span class="cx"> class PostgresService(MultiService):
</span><span class="cx">
</span><del>- def __init__(self, dataStoreDirectory, subServiceFactory,
- schema, resetSchema=False, databaseName='subpostgres',
- clusterName="cluster",
- logFile="postgres.log",
- logDirectory="",
- socketDir="",
- listenAddresses=[], sharedBuffers=30,
- maxConnections=20, options=[],
- testMode=False,
- uid=None, gid=None,
- spawnedDBUser="caldav",
- importFileName=None,
- pgCtl="pg_ctl",
- initDB="initdb",
- reactor=None):
</del><ins>+ def __init__(
+ self, dataStoreDirectory, subServiceFactory,
+ schema, resetSchema=False, databaseName="subpostgres",
+ clusterName="cluster",
+ logFile="postgres.log",
+ logDirectory="",
+ socketDir="",
+ listenAddresses=[], sharedBuffers=30,
+ maxConnections=20, options=[],
+ testMode=False,
+ uid=None, gid=None,
+ spawnedDBUser="caldav",
+ importFileName=None,
+ pgCtl="pg_ctl",
+ initDB="initdb",
+ reactor=None,
+ ):
</ins><span class="cx"> """
</span><span class="cx"> Initialize a L{PostgresService} pointed at a data store directory.
</span><span class="cx">
</span><span class="lines">@@ -191,7 +196,8 @@
</span><span class="cx">
</span><span class="cx"> @param spawnedDBUser: the postgres role
</span><span class="cx"> @type spawnedDBUser: C{str}
</span><del>- @param importFileName: path to SQL file containing previous data to import
</del><ins>+ @param importFileName: path to SQL file containing previous data to
+ import
</ins><span class="cx"> @type importFileName: C{str}
</span><span class="cx"> """
</span><span class="cx">
</span><span class="lines">@@ -220,10 +226,13 @@
</span><span class="cx"> # Make logFile absolute in case the working directory of postgres is
</span><span class="cx"> # elsewhere:
</span><span class="cx"> self.logFile = os.path.abspath(logFile)
</span><del>- self.logDirectory = os.path.abspath(logDirectory) if logDirectory else ""
</del><ins>+ if logDirectory:
+ self.logDirectory = os.path.abspath(logDirectory)
+ else:
+ self.logDirectory = ""
</ins><span class="cx">
</span><del>- # Always use our own configured socket dir in case the built-in postgres tries to use
- # a directory we don't have permissions for
</del><ins>+ # Always use our own configured socket dir in case the built-in
+ # postgres tries to use a directory we don't have permissions for
</ins><span class="cx"> if not socketDir:
</span><span class="cx"> # Socket directory was not specified, so come up with one
</span><span class="cx"> # in /tmp and based on a hash of the data store directory
</span><span class="lines">@@ -232,8 +241,14 @@
</span><span class="cx"> self.socketDir = CachingFilePath(socketDir)
</span><span class="cx">
</span><span class="cx"> if listenAddresses:
</span><del>- self.host, self.port = listenAddresses[0].split(":") if ":" in listenAddresses[0] else (listenAddresses[0], None,)
- self.listenAddresses = [addr.split(":")[0] for addr in listenAddresses]
</del><ins>+ if ":" in listenAddresses[0]:
+ self.host, self.port = listenAddresses[0].split(":")
+ else:
+ self.host, self.port = (listenAddresses[0], None)
+
+ self.listenAddresses = [
+ addr.split(":")[0] for addr in listenAddresses
+ ]
</ins><span class="cx"> else:
</span><span class="cx"> self.host = self.socketDir.path
</span><span class="cx"> self.port = None
</span><span class="lines">@@ -302,16 +317,19 @@
</span><span class="cx"> databaseName = self.databaseName
</span><span class="cx">
</span><span class="cx"> if self.spawnedDBUser:
</span><del>- dsn = "%s:dbname=%s:%s" % (self.host, databaseName, self.spawnedDBUser)
</del><ins>+ dsn = "{}:dbname={}:{}".format(
+ self.host, databaseName, self.spawnedDBUser
+ )
</ins><span class="cx"> elif self.uid is not None:
</span><del>- dsn = "%s:dbname=%s:%s" % (self.host, databaseName,
- pwd.getpwuid(self.uid).pw_name)
</del><ins>+ dsn = "{}:dbname={}:{}".format(
+ self.host, databaseName, pwd.getpwuid(self.uid).pw_name
+ )
</ins><span class="cx"> else:
</span><del>- dsn = "%s:dbname=%s" % (self.host, databaseName)
</del><ins>+ dsn = "{}:dbname={}".format(self.host, databaseName)
</ins><span class="cx">
</span><span class="cx"> kwargs = {}
</span><span class="cx"> if self.port:
</span><del>- kwargs["host"] = "%s:%s" % (self.host, self.port,)
</del><ins>+ kwargs["host"] = "{}:{}".format(self.host, self.port)
</ins><span class="cx">
</span><span class="cx"> return DBAPIConnector(pgdb, postgresPreflight, dsn, **kwargs)
</span><span class="cx">
</span><span class="lines">@@ -333,21 +351,22 @@
</span><span class="cx"> if self.resetSchema:
</span><span class="cx"> try:
</span><span class="cx"> createDatabaseCursor.execute(
</span><del>- "drop database %s" % (self.databaseName)
</del><ins>+ "drop database {}".format(self.databaseName)
</ins><span class="cx"> )
</span><span class="cx"> except pgdb.DatabaseError:
</span><span class="cx"> pass
</span><span class="cx">
</span><span class="cx"> try:
</span><span class="cx"> createDatabaseCursor.execute(
</span><del>- "create database %s with encoding 'UTF8'" % (self.databaseName)
</del><ins>+ "create database {} with encoding 'UTF8'"
+ .format(self.databaseName)
</ins><span class="cx"> )
</span><span class="cx"> except:
</span><span class="cx"> # database already exists
</span><span class="cx"> executeSQL = False
</span><span class="cx"> else:
</span><del>- # database does not yet exist; if dump file exists, execute it, otherwise
- # execute schema
</del><ins>+ # database does not yet exist; if dump file exists, execute it,
+ # otherwise execute schema
</ins><span class="cx"> executeSQL = True
</span><span class="cx"> sqlToExecute = self.schema
</span><span class="cx"> if self.importFileName:
</span><span class="lines">@@ -367,7 +386,9 @@
</span><span class="cx">
</span><span class="cx"> if self.shutdownDeferred is None:
</span><span class="cx"> # Only continue startup if we've not begun shutdown
</span><del>- self.subServiceFactory(self.produceConnection, self).setServiceParent(self)
</del><ins>+ self.subServiceFactory(
+ self.produceConnection, self
+ ).setServiceParent(self)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def pauseMonitor(self):
</span><span class="lines">@@ -400,7 +421,7 @@
</span><span class="cx">
</span><span class="cx"> def createConnection():
</span><span class="cx"> createDatabaseConn = self.produceConnection(
</span><del>- 'schema creation', 'postgres'
</del><ins>+ "schema creation", "postgres"
</ins><span class="cx"> )
</span><span class="cx"> createDatabaseCursor = createDatabaseConn.cursor()
</span><span class="cx"> createDatabaseCursor.execute("commit")
</span><span class="lines">@@ -412,25 +433,42 @@
</span><span class="cx">
</span><span class="cx"> options = []
</span><span class="cx"> options.append(
</span><del>- "-c listen_addresses='%s'" % (",".join(self.listenAddresses))
</del><ins>+ "-c listen_addresses={}"
+ .format(shell_quote(",".join(self.listenAddresses)))
</ins><span class="cx"> )
</span><span class="cx"> if self.socketDir:
</span><del>- options.append("-k '%s'" % (self.socketDir.path,))
</del><ins>+ options.append(
+ "-k {}"
+ .format(shell_quote(self.socketDir.path))
+ )
</ins><span class="cx"> if self.port:
</span><del>- options.append("-c port=%s" % (self.port,))
- options.append("-c shared_buffers=%d" % (self.sharedBuffers,))
- options.append("-c max_connections=%d" % (self.maxConnections,))
</del><ins>+ options.append(
+ "-c port={}".format(shell_quote(self.port))
+ )
+ options.append(
+ "-c shared_buffers={:d}"
+ .format(shell_quote(self.sharedBuffers))
+ )
+ options.append(
+ "-c max_connections={:d}"
+ .format(shell_quote(self.maxConnections))
+ )
</ins><span class="cx"> options.append("-c standard_conforming_strings=on")
</span><span class="cx"> options.append("-c unix_socket_permissions=0770")
</span><span class="cx"> options.extend(self.options)
</span><span class="cx"> if self.logDirectory: # tell postgres to rotate logs
</span><del>- options.append("-c log_directory={}".format(self.logDirectory))
</del><ins>+ options.append(
+ "-c log_directory={}".format(shell_quote(self.logDirectory))
+ )
</ins><span class="cx"> options.append("-c log_truncate_on_rotation=on")
</span><span class="cx"> options.append("-c log_filename=postgresql_%w.log")
</span><span class="cx"> options.append("-c log_rotation_age=1440")
</span><span class="cx"> options.append("-c logging_collector=on")
</span><span class="cx">
</span><del>- log.warn("Requesting postgres start via {cmd} {opts}", cmd=pgCtl, opts=options)
</del><ins>+ log.warn(
+ "Requesting postgres start via {cmd} {opts}",
+ cmd=pgCtl, opts=options
+ )
</ins><span class="cx"> self.reactor.spawnProcess(
</span><span class="cx"> monitor, pgCtl,
</span><span class="cx"> [
</span><span class="lines">@@ -456,7 +494,7 @@
</span><span class="cx"> removed/renamed/unmounted.
</span><span class="cx"> """
</span><span class="cx"> reResult = re.search("PID: (\d+)\D", result)
</span><del>- if reResult != None:
</del><ins>+ if reResult is not None:
</ins><span class="cx"> self._postgresPid = int(reResult.group(1))
</span><span class="cx"> self.ready(*createConnection())
</span><span class="cx"> self.deactivateDelayedShutdown()
</span><span class="lines">@@ -515,20 +553,26 @@
</span><span class="cx"> PGHOST=self.host,
</span><span class="cx"> PGUSER=self.spawnedDBUser)
</span><span class="cx"> initdb = self.initdb()
</span><ins>+
</ins><span class="cx"> if self.socketDir:
</span><span class="cx"> if not self.socketDir.isdir():
</span><span class="cx"> self.socketDir.createDirectory()
</span><ins>+
</ins><span class="cx"> if self.uid and self.gid:
</span><span class="cx"> os.chown(self.socketDir.path, self.uid, self.gid)
</span><ins>+
</ins><span class="cx"> if self.dataStoreDirectory.isdir():
</span><span class="cx"> self.startDatabase()
</span><span class="cx"> else:
</span><span class="cx"> self.dataStoreDirectory.createDirectory()
</span><ins>+
</ins><span class="cx"> if not self.workingDir.isdir():
</span><span class="cx"> self.workingDir.createDirectory()
</span><ins>+
</ins><span class="cx"> if self.uid and self.gid:
</span><span class="cx"> os.chown(self.dataStoreDirectory.path, self.uid, self.gid)
</span><span class="cx"> os.chown(self.workingDir.path, self.uid, self.gid)
</span><ins>+
</ins><span class="cx"> dbInited = Deferred()
</span><span class="cx"> self.reactor.spawnProcess(
</span><span class="cx"> CapturingProcessProtocol(dbInited, None),
</span><span class="lines">@@ -536,11 +580,16 @@
</span><span class="cx"> env=env, path=self.workingDir.path,
</span><span class="cx"> uid=self.uid, gid=self.gid,
</span><span class="cx"> )
</span><ins>+
</ins><span class="cx"> def doCreate(result):
</span><span class="cx"> if result.find("FATAL:") != -1:
</span><span class="cx"> log.error(result)
</span><del>- raise RuntimeError("Unable to initialize postgres database: %s" % (result,))
</del><ins>+ raise RuntimeError(
+ "Unable to initialize postgres database: {}"
+ .format(result)
+ )
</ins><span class="cx"> self.startDatabase()
</span><ins>+
</ins><span class="cx"> dbInited.addCallback(doCreate)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -564,8 +613,9 @@
</span><span class="cx"> monitor = _PostgresMonitor()
</span><span class="cx"> pgCtl = self.pgCtl()
</span><span class="cx"> # FIXME: why is this 'logfile' and not self.logfile?
</span><del>- self.reactor.spawnProcess(monitor, pgCtl,
- [pgCtl, '-l', 'logfile', 'stop'],
</del><ins>+ self.reactor.spawnProcess(
+ monitor, pgCtl,
+ [pgCtl, "-l", "logfile", "stop"],
</ins><span class="cx"> env=self.env, path=self.workingDir.path,
</span><span class="cx"> uid=self.uid, gid=self.gid,
</span><span class="cx"> )
</span></span></pre>
</div>
</div>
</body>
</html>