Improve "pg_ctl -w start" server detection by writing the postmaster
authorBruce Momjian <bruce@momjian.us>
Fri, 24 Dec 2010 14:45:15 +0000 (09:45 -0500)
committerBruce Momjian <bruce@momjian.us>
Fri, 24 Dec 2010 14:45:52 +0000 (09:45 -0500)
port and socket directory into postmaster.pid, and have pg_ctl read from
that file, for use by PQping().

doc/src/sgml/ref/pg_ctl-ref.sgml
src/backend/utils/init/miscinit.c
src/bin/pg_ctl/pg_ctl.c

index 99f0b8f00bf2c87b4f6e78ecd71b4a5625f83134..28f415da24b00528ca422595732af8ceca547666 100644 (file)
@@ -348,21 +348,12 @@ PostgreSQL documentation
        <para>
         Wait for the startup or shutdown to complete.
         Waiting is the default option for shutdowns, but not startups.
+        When waiting for startup, <command>pg_ctl</command> repeatedly
+        attempts to connect to the server.
         When waiting for shutdown, <command>pg_ctl</command> waits for
         the server to remove its <acronym>PID</acronym> file.
-        When waiting for startup, <command>pg_ctl</command> repeatedly
-        attempts to connect to the server via <application>psql</>, and
-        reports success when this is successful.
-        <command>pg_ctl</command> will attempt to use the proper port for
-        <application>psql</>. If the environment variable
-        <envar>PGPORT</envar> exists, that is used.  Otherwise,
-        <command>pg_ctl</command> will see if a port has been set in the
-        <filename>postgresql.conf</filename> file.  If not, it will use the
-        default port that <productname>PostgreSQL</productname> was compiled
-        with (5432 by default).
-        When waiting, <command>pg_ctl</command> will
-        return an exit code based on the success of the startup
-        or shutdown.
+        <command>pg_ctl</command> returns an exit code based on the
+        success of the startup or shutdown.
        </para>
       </listitem>
      </varlistentry>
@@ -442,28 +433,6 @@ PostgreSQL documentation
     </listitem>
    </varlistentry>
 
-   <varlistentry>
-    <term><envar>PGHOST</envar></term>
-
-    <listitem>
-     <para>
-      Default host name or Unix-domain socket location for <xref
-      linkend="app-psql"> (used when waiting for startup).
-     </para>
-    </listitem>
-   </varlistentry>
-
-   <varlistentry>
-    <term><envar>PGPORT</envar></term>
-
-    <listitem>
-     <para>
-      Default port number for <xref linkend="app-psql">
-      (used when waiting for startup).
-     </para>
-    </listitem>
-   </varlistentry>
-
   </variablelist>
 
   <para>
@@ -506,18 +475,6 @@ PostgreSQL documentation
     </listitem>
    </varlistentry>
 
-   <varlistentry>
-    <term><filename>postgresql.conf</filename></term>
-
-    <listitem>
-     <para>
-      This file, located in the data directory, is parsed to find the
-      proper port to use with <application>psql</application>
-      when waiting for startup.
-     </para>
-    </listitem>
-   </varlistentry>
-
   </variablelist>
  </refsect1>
 
index 14ed9147a19c3620ced5e0efc9e6d7ea59d2ad7e..deb2d582b2a09974015bbf488a6a2c2841a38edb 100644 (file)
@@ -33,6 +33,7 @@
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "postmaster/autovacuum.h"
+#include "postmaster/postmaster.h"
 #include "storage/fd.h"
 #include "storage/ipc.h"
 #include "storage/pg_shmem.h"
@@ -658,7 +659,7 @@ CreateLockFile(const char *filename, bool amPostmaster,
                           bool isDDLock, const char *refName)
 {
        int                     fd;
-       char            buffer[MAXPGPATH + 100];
+       char            buffer[MAXPGPATH * 2 + 256];
        int                     ntries;
        int                     len;
        int                     encoded_pid;
@@ -868,9 +869,9 @@ CreateLockFile(const char *filename, bool amPostmaster,
        /*
         * Successfully created the file, now fill it.
         */
-       snprintf(buffer, sizeof(buffer), "%d\n%s\n",
+       snprintf(buffer, sizeof(buffer), "%d\n%s\n%d\n%s\n",
                         amPostmaster ? (int) my_pid : -((int) my_pid),
-                        DataDir);
+                        DataDir, PostPortNumber, UnixSocketDir);
        errno = 0;
        if (write(fd, buffer, strlen(buffer)) != strlen(buffer))
        {
@@ -994,8 +995,9 @@ RecordSharedMemoryInLockFile(unsigned long id1, unsigned long id2)
 {
        int                     fd;
        int                     len;
+       int                     lineno;
        char       *ptr;
-       char            buffer[BLCKSZ];
+       char            buffer[MAXPGPATH * 2 + 256];
 
        fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
        if (fd < 0)
@@ -1019,18 +1021,20 @@ RecordSharedMemoryInLockFile(unsigned long id1, unsigned long id2)
        buffer[len] = '\0';
 
        /*
-        * Skip over first two lines (PID and path).
+        * Skip over first four lines (PID, pgdata, portnum, socketdir).
         */
-       ptr = strchr(buffer, '\n');
-       if (ptr == NULL ||
-               (ptr = strchr(ptr + 1, '\n')) == NULL)
+       ptr = buffer;
+       for (lineno = 1; lineno <= 4; lineno++)
        {
-               elog(LOG, "bogus data in \"%s\"", DIRECTORY_LOCK_FILE);
-               close(fd);
-               return;
+               if ((ptr = strchr(ptr, '\n')) == NULL)
+               {
+                       elog(LOG, "bogus data in \"%s\"", DIRECTORY_LOCK_FILE);
+                       close(fd);
+                       return;
+               }
+               ptr++;
        }
-       ptr++;
-
+       
        /*
         * Append key information.      Format to try to keep it the same length
         * always (trailing junk won't hurt, but might confuse humans).
index c5f855e063fe90fa7fc4abd48c1905bd46019367..5e1b7728fcd3d9d829b8cf8c4f56d20184085697 100644 (file)
@@ -141,7 +141,6 @@ static bool postmaster_is_alive(pid_t pid);
 
 static char postopts_file[MAXPGPATH];
 static char pid_file[MAXPGPATH];
-static char conf_file[MAXPGPATH];
 static char backup_file[MAXPGPATH];
 static char recovery_file[MAXPGPATH];
 
@@ -404,113 +403,108 @@ start_postmaster(void)
 static PGPing
 test_postmaster_connection(bool do_checkpoint)
 {
+       int                     portnum = 0;
+       char            socket_dir[MAXPGPATH];
+       char            connstr[MAXPGPATH + 256];
        PGPing          ret = PQPING_OK;        /* assume success for wait == zero */
+       char      **optlines;
        int                     i;
-       char            portstr[32];
-       char       *p;
-       char       *q;
-       char            connstr[128];   /* Should be way more than enough! */
 
-       portstr[0] = '\0';
-
-       /*
-        * Look in post_opts for a -p switch.
-        *
-        * This parsing code is not amazingly bright; it could for instance get
-        * fooled if ' -p' occurs within a quoted argument value.  Given that few
-        * people pass complicated settings in post_opts, it's probably good
-        * enough.
-        */
-       for (p = post_opts; *p;)
+       socket_dir[0] = '\0';
+       connstr[0] = '\0';
+       
+       for (i = 0; i < wait_seconds; i++)
        {
-               /* advance past whitespace */
-               while (isspace((unsigned char) *p))
-                       p++;
-
-               if (strncmp(p, "-p", 2) == 0)
+               /* Do we need a connection string? */
+               if (connstr[0] == '\0')
                {
-                       p += 2;
-                       /* advance past any whitespace/quoting */
-                       while (isspace((unsigned char) *p) || *p == '\'' || *p == '"')
-                               p++;
-                       /* find end of value (not including any ending quote!) */
-                       q = p;
-                       while (*q &&
-                                  !(isspace((unsigned char) *q) || *q == '\'' || *q == '"'))
-                               q++;
-                       /* and save the argument value */
-                       strlcpy(portstr, p, Min((q - p) + 1, sizeof(portstr)));
-                       /* keep looking, maybe there is another -p */
-                       p = q;
-               }
-               /* Advance to next whitespace */
-               while (*p && !isspace((unsigned char) *p))
-                       p++;
-       }
+                       /*
+                        *      The number of lines in postmaster.pid tells us several things:
+                        *
+                        *      # of lines
+                        *              0       lock file created but status not written
+                        *              2       pre-9.1 server, shared memory not created
+                        *              3       pre-9.1 server, shared memory created
+                        *              4       9.1+ server, shared memory not created
+                        *              5       9.1+ server, shared memory created
+                        *
+                        *      For pre-9.1 Unix servers, we grab the port number from the
+                        *      shmem key (first value on line 3).  Pre-9.1 Win32 has no
+                        *      written shmem key, so we fail.  9.1+ writes both the port
+                        *      number and socket address in the file for us to use.
+                        *      (PG_VERSION could also have told us the major version.)
+                        */
+               
+                       /* Try to read a completed postmaster.pid file */
+                       if ((optlines = readfile(pid_file)) != NULL &&
+                               optlines[0] != NULL &&
+                               optlines[1] != NULL &&
+                               optlines[2] != NULL)
+                       {                               
+                               /* A 3-line file? */
+                               if (optlines[3] == NULL)
+                               {
+                                       /*
+                                        *      Pre-9.1:  on Unix, we get the port number by
+                                        *      deriving it from the shmem key (the first number on
+                                        *      on the line);  see
+                                        *      miscinit.c::RecordSharedMemoryInLockFile().
+                                        */
+                                       portnum = atoi(optlines[2]) / 1000;
+                                       /* Win32 does not give us a shmem key, so we fail. */
+                                       if (portnum == 0)
+                                       {
+                                               write_stderr(_("%s: -w option is not supported on this platform\nwhen connecting to a pre-9.1 server\n"),
+                                                                        progname);
+                                               return PQPING_NO_ATTEMPT;
+                                       }
+                               }
+                               else    /* 9.1+ server */
+                               {
+                                       portnum = atoi(optlines[2]);
+       
+                                       /* Get socket directory, if specified. */
+                                       if (optlines[3][0] != '\n')
+                                       {
+                                               /*
+                                                *      While unix_socket_directory can accept relative
+                                                *      directories, libpq's host must have a leading slash
+                                                *      to indicate a socket directory.
+                                                */
+                                               if (optlines[3][0] != '/')
+                                               {
+                                                       write_stderr(_("%s: -w option cannot use a relative socket directory specification\n"),
+                                                                                progname);
+                                                       return PQPING_NO_ATTEMPT;
+                                               }
+                                               strlcpy(socket_dir, optlines[3], MAXPGPATH);
+                                               /* remove newline */
+                                               if (strchr(socket_dir, '\n') != NULL)
+                                                       *strchr(socket_dir, '\n') = '\0';
+                                       }
+                               }
 
-       /*
-        * Search config file for a 'port' option.
-        *
-        * This parsing code isn't amazingly bright either, but it should be okay
-        * for valid port settings.
-        */
-       if (!portstr[0])
-       {
-               char      **optlines;
+                               /*
+                                * We need to set connect_timeout otherwise on Windows the
+                                * Service Control Manager (SCM) will probably timeout first.
+                                */
+                               snprintf(connstr, sizeof(connstr),
+                                                "dbname=postgres port=%d connect_timeout=5", portnum);
 
-               optlines = readfile(conf_file);
-               if (optlines != NULL)
-               {
-                       for (; *optlines != NULL; optlines++)
-                       {
-                               p = *optlines;
-
-                               while (isspace((unsigned char) *p))
-                                       p++;
-                               if (strncmp(p, "port", 4) != 0)
-                                       continue;
-                               p += 4;
-                               while (isspace((unsigned char) *p))
-                                       p++;
-                               if (*p != '=')
-                                       continue;
-                               p++;
-                               /* advance past any whitespace/quoting */
-                               while (isspace((unsigned char) *p) || *p == '\'' || *p == '"')
-                                       p++;
-                               /* find end of value (not including any ending quote/comment!) */
-                               q = p;
-                               while (*q &&
-                                          !(isspace((unsigned char) *q) ||
-                                                *q == '\'' || *q == '"' || *q == '#'))
-                                       q++;
-                               /* and save the argument value */
-                               strlcpy(portstr, p, Min((q - p) + 1, sizeof(portstr)));
-                               /* keep looking, maybe there is another */
+                               if (socket_dir[0] != '\0')
+                                       snprintf(connstr + strlen(connstr), sizeof(connstr) - strlen(connstr),
+                                               " host='%s'", socket_dir);
                        }
                }
-       }
 
-       /* Check environment */
-       if (!portstr[0] && getenv("PGPORT") != NULL)
-               strlcpy(portstr, getenv("PGPORT"), sizeof(portstr));
-
-       /* Else use compiled-in default */
-       if (!portstr[0])
-               snprintf(portstr, sizeof(portstr), "%d", DEF_PGPORT);
-
-       /*
-        * We need to set a connect timeout otherwise on Windows the SCM will
-        * probably timeout first
-        */
-       snprintf(connstr, sizeof(connstr),
-                        "dbname=postgres port=%s connect_timeout=5", portstr);
+               /* If we have a connection string, ping the server */
+               if (connstr[0] != '\0')
+               {
+                       ret = PQping(connstr);
+                       if (ret == PQPING_OK || ret == PQPING_NO_ATTEMPT)
+                               break;
+               }
 
-       for (i = 0; i < wait_seconds; i++)
-       {
-               ret = PQping(connstr);
-               if (ret == PQPING_OK || ret == PQPING_NO_ATTEMPT)
-                       break;
                /* No response, or startup still in process; wait */
 #if defined(WIN32)
                if (do_checkpoint)
@@ -2009,7 +2003,6 @@ main(int argc, char **argv)
        {
                snprintf(postopts_file, MAXPGPATH, "%s/postmaster.opts", pg_data);
                snprintf(pid_file, MAXPGPATH, "%s/postmaster.pid", pg_data);
-               snprintf(conf_file, MAXPGPATH, "%s/postgresql.conf", pg_data);
                snprintf(backup_file, MAXPGPATH, "%s/backup_label", pg_data);
                snprintf(recovery_file, MAXPGPATH, "%s/recovery.conf", pg_data);
        }