From 075354ad1bc0496c30dbd4dafb0f88a4d3e54cbc Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Fri, 24 Dec 2010 09:45:15 -0500 Subject: [PATCH] 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(). --- doc/src/sgml/ref/pg_ctl-ref.sgml | 51 +------- src/backend/utils/init/miscinit.c | 30 +++-- src/bin/pg_ctl/pg_ctl.c | 189 ++++++++++++++---------------- 3 files changed, 112 insertions(+), 158 deletions(-) diff --git a/doc/src/sgml/ref/pg_ctl-ref.sgml b/doc/src/sgml/ref/pg_ctl-ref.sgml index 99f0b8f00b..28f415da24 100644 --- a/doc/src/sgml/ref/pg_ctl-ref.sgml +++ b/doc/src/sgml/ref/pg_ctl-ref.sgml @@ -348,21 +348,12 @@ PostgreSQL documentation Wait for the startup or shutdown to complete. Waiting is the default option for shutdowns, but not startups. + When waiting for startup, pg_ctl repeatedly + attempts to connect to the server. When waiting for shutdown, pg_ctl waits for the server to remove its PID file. - When waiting for startup, pg_ctl repeatedly - attempts to connect to the server via psql, and - reports success when this is successful. - pg_ctl will attempt to use the proper port for - psql. If the environment variable - PGPORT exists, that is used. Otherwise, - pg_ctl will see if a port has been set in the - postgresql.conf file. If not, it will use the - default port that PostgreSQL was compiled - with (5432 by default). - When waiting, pg_ctl will - return an exit code based on the success of the startup - or shutdown. + pg_ctl returns an exit code based on the + success of the startup or shutdown. @@ -442,28 +433,6 @@ PostgreSQL documentation - - PGHOST - - - - Default host name or Unix-domain socket location for (used when waiting for startup). - - - - - - PGPORT - - - - Default port number for - (used when waiting for startup). - - - - @@ -506,18 +475,6 @@ PostgreSQL documentation - - postgresql.conf - - - - This file, located in the data directory, is parsed to find the - proper port to use with psql - when waiting for startup. - - - - diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 14ed9147a1..deb2d582b2 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -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). diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index c5f855e063..5e1b7728fc 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -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); } -- 2.40.0