<!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(&quot;postgres process ended with status {status}&quot;, status=reason.value.status)
</del><ins>+        log.warn(
+            &quot;postgres process ended with status {status}&quot;,
+            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=&quot;cluster&quot;,
-                 logFile=&quot;postgres.log&quot;,
-                 logDirectory=&quot;&quot;,
-                 socketDir=&quot;&quot;,
-                 listenAddresses=[], sharedBuffers=30,
-                 maxConnections=20, options=[],
-                 testMode=False,
-                 uid=None, gid=None,
-                 spawnedDBUser=&quot;caldav&quot;,
-                 importFileName=None,
-                 pgCtl=&quot;pg_ctl&quot;,
-                 initDB=&quot;initdb&quot;,
-                 reactor=None):
</del><ins>+    def __init__(
+        self, dataStoreDirectory, subServiceFactory,
+        schema, resetSchema=False, databaseName=&quot;subpostgres&quot;,
+        clusterName=&quot;cluster&quot;,
+        logFile=&quot;postgres.log&quot;,
+        logDirectory=&quot;&quot;,
+        socketDir=&quot;&quot;,
+        listenAddresses=[], sharedBuffers=30,
+        maxConnections=20, options=[],
+        testMode=False,
+        uid=None, gid=None,
+        spawnedDBUser=&quot;caldav&quot;,
+        importFileName=None,
+        pgCtl=&quot;pg_ctl&quot;,
+        initDB=&quot;initdb&quot;,
+        reactor=None,
+    ):
</ins><span class="cx">         &quot;&quot;&quot;
</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">         &quot;&quot;&quot;
</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 &quot;&quot;
</del><ins>+        if logDirectory:
+            self.logDirectory = os.path.abspath(logDirectory)
+        else:
+            self.logDirectory = &quot;&quot;
</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(&quot;:&quot;) if &quot;:&quot; in listenAddresses[0] else (listenAddresses[0], None,)
-            self.listenAddresses = [addr.split(&quot;:&quot;)[0] for addr in listenAddresses]
</del><ins>+            if &quot;:&quot; in listenAddresses[0]:
+                self.host, self.port = listenAddresses[0].split(&quot;:&quot;)
+            else:
+                self.host, self.port = (listenAddresses[0], None)
+
+            self.listenAddresses = [
+                addr.split(&quot;:&quot;)[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 = &quot;%s:dbname=%s:%s&quot; % (self.host, databaseName, self.spawnedDBUser)
</del><ins>+            dsn = &quot;{}:dbname={}:{}&quot;.format(
+                self.host, databaseName, self.spawnedDBUser
+            )
</ins><span class="cx">         elif self.uid is not None:
</span><del>-            dsn = &quot;%s:dbname=%s:%s&quot; % (self.host, databaseName,
-                pwd.getpwuid(self.uid).pw_name)
</del><ins>+            dsn = &quot;{}:dbname={}:{}&quot;.format(
+                self.host, databaseName, pwd.getpwuid(self.uid).pw_name
+            )
</ins><span class="cx">         else:
</span><del>-            dsn = &quot;%s:dbname=%s&quot; % (self.host, databaseName)
</del><ins>+            dsn = &quot;{}:dbname={}&quot;.format(self.host, databaseName)
</ins><span class="cx"> 
</span><span class="cx">         kwargs = {}
</span><span class="cx">         if self.port:
</span><del>-            kwargs[&quot;host&quot;] = &quot;%s:%s&quot; % (self.host, self.port,)
</del><ins>+            kwargs[&quot;host&quot;] = &quot;{}:{}&quot;.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>-                    &quot;drop database %s&quot; % (self.databaseName)
</del><ins>+                    &quot;drop database {}&quot;.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>-                &quot;create database %s with encoding 'UTF8'&quot; % (self.databaseName)
</del><ins>+                &quot;create database {} with encoding 'UTF8'&quot;
+                .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>+                &quot;schema creation&quot;, &quot;postgres&quot;
</ins><span class="cx">             )
</span><span class="cx">             createDatabaseCursor = createDatabaseConn.cursor()
</span><span class="cx">             createDatabaseCursor.execute(&quot;commit&quot;)
</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>-            &quot;-c listen_addresses='%s'&quot; % (&quot;,&quot;.join(self.listenAddresses))
</del><ins>+            &quot;-c listen_addresses={}&quot;
+            .format(shell_quote(&quot;,&quot;.join(self.listenAddresses)))
</ins><span class="cx">         )
</span><span class="cx">         if self.socketDir:
</span><del>-            options.append(&quot;-k '%s'&quot; % (self.socketDir.path,))
</del><ins>+            options.append(
+                &quot;-k {}&quot;
+                .format(shell_quote(self.socketDir.path))
+            )
</ins><span class="cx">         if self.port:
</span><del>-            options.append(&quot;-c port=%s&quot; % (self.port,))
-        options.append(&quot;-c shared_buffers=%d&quot; % (self.sharedBuffers,))
-        options.append(&quot;-c max_connections=%d&quot; % (self.maxConnections,))
</del><ins>+            options.append(
+                &quot;-c port={}&quot;.format(shell_quote(self.port))
+            )
+        options.append(
+            &quot;-c shared_buffers={:d}&quot;
+            .format(shell_quote(self.sharedBuffers))
+        )
+        options.append(
+            &quot;-c max_connections={:d}&quot;
+            .format(shell_quote(self.maxConnections))
+        )
</ins><span class="cx">         options.append(&quot;-c standard_conforming_strings=on&quot;)
</span><span class="cx">         options.append(&quot;-c unix_socket_permissions=0770&quot;)
</span><span class="cx">         options.extend(self.options)
</span><span class="cx">         if self.logDirectory:  # tell postgres to rotate logs
</span><del>-            options.append(&quot;-c log_directory={}&quot;.format(self.logDirectory))
</del><ins>+            options.append(
+                &quot;-c log_directory={}&quot;.format(shell_quote(self.logDirectory))
+            )
</ins><span class="cx">             options.append(&quot;-c log_truncate_on_rotation=on&quot;)
</span><span class="cx">             options.append(&quot;-c log_filename=postgresql_%w.log&quot;)
</span><span class="cx">             options.append(&quot;-c log_rotation_age=1440&quot;)
</span><span class="cx">             options.append(&quot;-c logging_collector=on&quot;)
</span><span class="cx"> 
</span><del>-        log.warn(&quot;Requesting postgres start via {cmd} {opts}&quot;, cmd=pgCtl, opts=options)
</del><ins>+        log.warn(
+            &quot;Requesting postgres start via {cmd} {opts}&quot;,
+            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">             &quot;&quot;&quot;
</span><span class="cx">             reResult = re.search(&quot;PID: (\d+)\D&quot;, 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(&quot;FATAL:&quot;) != -1:
</span><span class="cx">                     log.error(result)
</span><del>-                    raise RuntimeError(&quot;Unable to initialize postgres database: %s&quot; % (result,))
</del><ins>+                    raise RuntimeError(
+                        &quot;Unable to initialize postgres database: {}&quot;
+                        .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, &quot;-l&quot;, &quot;logfile&quot;, &quot;stop&quot;],
</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>