Commit 075354ad authored by Bruce Momjian's avatar Bruce Momjian

Improve "pg_ctl -w start" server detection by writing the postmaster

port and socket directory into postmaster.pid, and have pg_ctl read from
that file, for use by PQping().
parent 4b1742a1
...@@ -348,21 +348,12 @@ PostgreSQL documentation ...@@ -348,21 +348,12 @@ PostgreSQL documentation
<para> <para>
Wait for the startup or shutdown to complete. Wait for the startup or shutdown to complete.
Waiting is the default option for shutdowns, but not startups. 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 When waiting for shutdown, <command>pg_ctl</command> waits for
the server to remove its <acronym>PID</acronym> file. the server to remove its <acronym>PID</acronym> file.
When waiting for startup, <command>pg_ctl</command> repeatedly <command>pg_ctl</command> returns an exit code based on the
attempts to connect to the server via <application>psql</>, and success of the startup or shutdown.
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.
</para> </para>
</listitem> </listitem>
</varlistentry> </varlistentry>
...@@ -442,28 +433,6 @@ PostgreSQL documentation ...@@ -442,28 +433,6 @@ PostgreSQL documentation
</listitem> </listitem>
</varlistentry> </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> </variablelist>
<para> <para>
...@@ -506,18 +475,6 @@ PostgreSQL documentation ...@@ -506,18 +475,6 @@ PostgreSQL documentation
</listitem> </listitem>
</varlistentry> </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> </variablelist>
</refsect1> </refsect1>
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "mb/pg_wchar.h" #include "mb/pg_wchar.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "postmaster/autovacuum.h" #include "postmaster/autovacuum.h"
#include "postmaster/postmaster.h"
#include "storage/fd.h" #include "storage/fd.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/pg_shmem.h" #include "storage/pg_shmem.h"
...@@ -658,7 +659,7 @@ CreateLockFile(const char *filename, bool amPostmaster, ...@@ -658,7 +659,7 @@ CreateLockFile(const char *filename, bool amPostmaster,
bool isDDLock, const char *refName) bool isDDLock, const char *refName)
{ {
int fd; int fd;
char buffer[MAXPGPATH + 100]; char buffer[MAXPGPATH * 2 + 256];
int ntries; int ntries;
int len; int len;
int encoded_pid; int encoded_pid;
...@@ -868,9 +869,9 @@ CreateLockFile(const char *filename, bool amPostmaster, ...@@ -868,9 +869,9 @@ CreateLockFile(const char *filename, bool amPostmaster,
/* /*
* Successfully created the file, now fill it. * 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), amPostmaster ? (int) my_pid : -((int) my_pid),
DataDir); DataDir, PostPortNumber, UnixSocketDir);
errno = 0; errno = 0;
if (write(fd, buffer, strlen(buffer)) != strlen(buffer)) if (write(fd, buffer, strlen(buffer)) != strlen(buffer))
{ {
...@@ -994,8 +995,9 @@ RecordSharedMemoryInLockFile(unsigned long id1, unsigned long id2) ...@@ -994,8 +995,9 @@ RecordSharedMemoryInLockFile(unsigned long id1, unsigned long id2)
{ {
int fd; int fd;
int len; int len;
int lineno;
char *ptr; char *ptr;
char buffer[BLCKSZ]; char buffer[MAXPGPATH * 2 + 256];
fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0); fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
if (fd < 0) if (fd < 0)
...@@ -1019,18 +1021,20 @@ RecordSharedMemoryInLockFile(unsigned long id1, unsigned long id2) ...@@ -1019,18 +1021,20 @@ RecordSharedMemoryInLockFile(unsigned long id1, unsigned long id2)
buffer[len] = '\0'; buffer[len] = '\0';
/* /*
* Skip over first two lines (PID and path). * Skip over first four lines (PID, pgdata, portnum, socketdir).
*/ */
ptr = strchr(buffer, '\n'); ptr = buffer;
if (ptr == NULL || for (lineno = 1; lineno <= 4; lineno++)
(ptr = strchr(ptr + 1, '\n')) == NULL)
{ {
elog(LOG, "bogus data in \"%s\"", DIRECTORY_LOCK_FILE); if ((ptr = strchr(ptr, '\n')) == NULL)
close(fd); {
return; 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 * Append key information. Format to try to keep it the same length
* always (trailing junk won't hurt, but might confuse humans). * always (trailing junk won't hurt, but might confuse humans).
......
...@@ -141,7 +141,6 @@ static bool postmaster_is_alive(pid_t pid); ...@@ -141,7 +141,6 @@ static bool postmaster_is_alive(pid_t pid);
static char postopts_file[MAXPGPATH]; static char postopts_file[MAXPGPATH];
static char pid_file[MAXPGPATH]; static char pid_file[MAXPGPATH];
static char conf_file[MAXPGPATH];
static char backup_file[MAXPGPATH]; static char backup_file[MAXPGPATH];
static char recovery_file[MAXPGPATH]; static char recovery_file[MAXPGPATH];
...@@ -404,113 +403,108 @@ start_postmaster(void) ...@@ -404,113 +403,108 @@ start_postmaster(void)
static PGPing static PGPing
test_postmaster_connection(bool do_checkpoint) 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 */ PGPing ret = PQPING_OK; /* assume success for wait == zero */
char **optlines;
int i; int i;
char portstr[32];
char *p;
char *q;
char connstr[128]; /* Should be way more than enough! */
portstr[0] = '\0'; socket_dir[0] = '\0';
connstr[0] = '\0';
/*
* Look in post_opts for a -p switch. for (i = 0; i < wait_seconds; i++)
*
* 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;)
{ {
/* advance past whitespace */ /* Do we need a connection string? */
while (isspace((unsigned char) *p)) if (connstr[0] == '\0')
p++;
if (strncmp(p, "-p", 2) == 0)
{ {
p += 2; /*
/* advance past any whitespace/quoting */ * The number of lines in postmaster.pid tells us several things:
while (isspace((unsigned char) *p) || *p == '\'' || *p == '"') *
p++; * # of lines
/* find end of value (not including any ending quote!) */ * 0 lock file created but status not written
q = p; * 2 pre-9.1 server, shared memory not created
while (*q && * 3 pre-9.1 server, shared memory created
!(isspace((unsigned char) *q) || *q == '\'' || *q == '"')) * 4 9.1+ server, shared memory not created
q++; * 5 9.1+ server, shared memory created
/* and save the argument value */ *
strlcpy(portstr, p, Min((q - p) + 1, sizeof(portstr))); * For pre-9.1 Unix servers, we grab the port number from the
/* keep looking, maybe there is another -p */ * shmem key (first value on line 3). Pre-9.1 Win32 has no
p = q; * written shmem key, so we fail. 9.1+ writes both the port
} * number and socket address in the file for us to use.
/* Advance to next whitespace */ * (PG_VERSION could also have told us the major version.)
while (*p && !isspace((unsigned char) *p)) */
p++;
} /* 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. * We need to set connect_timeout otherwise on Windows the
* * Service Control Manager (SCM) will probably timeout first.
* This parsing code isn't amazingly bright either, but it should be okay */
* for valid port settings. snprintf(connstr, sizeof(connstr),
*/ "dbname=postgres port=%d connect_timeout=5", portnum);
if (!portstr[0])
{
char **optlines;
optlines = readfile(conf_file); if (socket_dir[0] != '\0')
if (optlines != NULL) snprintf(connstr + strlen(connstr), sizeof(connstr) - strlen(connstr),
{ " host='%s'", socket_dir);
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 */
} }
} }
}
/* Check environment */ /* If we have a connection string, ping the server */
if (!portstr[0] && getenv("PGPORT") != NULL) if (connstr[0] != '\0')
strlcpy(portstr, getenv("PGPORT"), sizeof(portstr)); {
ret = PQping(connstr);
/* Else use compiled-in default */ if (ret == PQPING_OK || ret == PQPING_NO_ATTEMPT)
if (!portstr[0]) break;
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);
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 */ /* No response, or startup still in process; wait */
#if defined(WIN32) #if defined(WIN32)
if (do_checkpoint) if (do_checkpoint)
...@@ -2009,7 +2003,6 @@ main(int argc, char **argv) ...@@ -2009,7 +2003,6 @@ main(int argc, char **argv)
{ {
snprintf(postopts_file, MAXPGPATH, "%s/postmaster.opts", pg_data); snprintf(postopts_file, MAXPGPATH, "%s/postmaster.opts", pg_data);
snprintf(pid_file, MAXPGPATH, "%s/postmaster.pid", 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(backup_file, MAXPGPATH, "%s/backup_label", pg_data);
snprintf(recovery_file, MAXPGPATH, "%s/recovery.conf", pg_data); snprintf(recovery_file, MAXPGPATH, "%s/recovery.conf", pg_data);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment