diff --git a/doc/src/sgml/storage.sgml b/doc/src/sgml/storage.sgml index 430df4a843..746a48219f 100644 --- a/doc/src/sgml/storage.sgml +++ b/doc/src/sgml/storage.sgml @@ -117,8 +117,9 @@ last started with postmaster.pid A lock file recording the current postmaster process id (PID), - cluster data directory, port number, Unix domain socket directory, - and shared memory segment ID + postmaster start time, cluster data directory, port number, user-specified + Unix domain socket directory, first valid listen_address host, and + shared memory segment ID diff --git a/src/backend/port/ipc_test.c b/src/backend/port/ipc_test.c index a003dc9c20..461a7a65b2 100644 --- a/src/backend/port/ipc_test.c +++ b/src/backend/port/ipc_test.c @@ -104,7 +104,7 @@ on_exit_reset(void) } void -RecordSharedMemoryInLockFile(unsigned long id1, unsigned long id2) +AddToLockFile(int target_line, const char *str) { } diff --git a/src/backend/port/sysv_shmem.c b/src/backend/port/sysv_shmem.c index d970eb2996..ff77099885 100644 --- a/src/backend/port/sysv_shmem.c +++ b/src/backend/port/sysv_shmem.c @@ -198,9 +198,17 @@ InternalIpcMemoryCreate(IpcMemoryKey memKey, Size size) /* Register on-exit routine to detach new segment before deleting */ on_shmem_exit(IpcMemoryDetach, PointerGetDatum(memAddress)); - /* Record key and ID in lockfile for data directory. */ - RecordSharedMemoryInLockFile((unsigned long) memKey, - (unsigned long) shmid); + /* + * Append record key and ID in lockfile for data directory. Format + * to try to keep it the same length. + */ + { + char line[32]; + + sprintf(line, "%9lu %9lu\n", (unsigned long) memKey, + (unsigned long) shmid); + AddToLockFile(LOCK_FILE_LINES, line); + } return memAddress; } diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index a46a323881..c1e553a85b 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -483,7 +483,8 @@ PostmasterMain(int argc, char *argv[]) int status; char *userDoption = NULL; int i; - + bool connection_line_output = false; + MyProcPid = PostmasterPid = getpid(); MyStartTime = time(NULL); @@ -860,10 +861,22 @@ PostmasterMain(int argc, char *argv[]) UnixSocketDir, ListenSocket, MAXLISTEN); else + { status = StreamServerPort(AF_UNSPEC, curhost, (unsigned short) PostPortNumber, UnixSocketDir, ListenSocket, MAXLISTEN); + /* must supply a valid listen_address for PQping() */ + if (!connection_line_output) + { + char line[MAXPGPATH + 2]; + + sprintf(line, "%s\n", curhost); + AddToLockFile(LOCK_FILE_LINES - 1, line); + connection_line_output = true; + } + } + if (status == STATUS_OK) success++; else @@ -880,6 +893,10 @@ PostmasterMain(int argc, char *argv[]) pfree(rawstring); } + /* Supply an empty listen_address line for PQping() */ + if (!connection_line_output) + AddToLockFile(LOCK_FILE_LINES - 1, "\n"); + #ifdef USE_BONJOUR /* Register for Bonjour only if we opened TCP socket(s) */ if (enable_bonjour && ListenSocket[0] != PGINVALID_SOCKET) diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c index 0d5ffb0a8e..27679259e4 100644 --- a/src/backend/utils/init/miscinit.c +++ b/src/backend/utils/init/miscinit.c @@ -46,7 +46,20 @@ #define DIRECTORY_LOCK_FILE "postmaster.pid" - +/* + * The lock file contents are: + * + * line # + * 1 pid + * 2 postmaster start time + * 3 data directory + * 4 port # + * 5 user-specified socket directory + * (the lines below are added later) + * 6 first valid listen_address + * 7 shared memory key + */ + ProcessingMode Mode = InitProcessing; /* Note: we rely on this to initialize as zeroes */ @@ -678,7 +691,7 @@ CreateLockFile(const char *filename, bool amPostmaster, bool isDDLock, const char *refName) { int fd; - char buffer[MAXPGPATH * 2 + 256]; + char buffer[MAXPGPATH * 3 + 256]; int ntries; int len; int encoded_pid; @@ -845,11 +858,10 @@ CreateLockFile(const char *filename, bool amPostmaster, if (isDDLock) { char *ptr = buffer; - unsigned long id1, - id2; + unsigned long id1, id2; int lineno; - for (lineno = 1; lineno <= 4; lineno++) + for (lineno = 1; lineno <= LOCK_FILE_LINES - 1; lineno++) { if ((ptr = strchr(ptr, '\n')) == NULL) { @@ -893,9 +905,10 @@ CreateLockFile(const char *filename, bool amPostmaster, /* * Successfully created the file, now fill it. */ - snprintf(buffer, sizeof(buffer), "%d\n%s\n%d\n%s\n", + snprintf(buffer, sizeof(buffer), "%d\n%ld\n%s\n%d\n%s\n", amPostmaster ? (int) my_pid : -((int) my_pid), - DataDir, PostPortNumber, UnixSocketDir); + (long) MyStartTime, DataDir, PostPortNumber, + UnixSocketDir); errno = 0; if (write(fd, buffer, strlen(buffer)) != strlen(buffer)) { @@ -1004,24 +1017,19 @@ TouchSocketLockFile(void) } } + /* - * Append information about a shared memory segment to the data directory - * lock file. - * - * This may be called multiple times in the life of a postmaster, if we - * delete and recreate shmem due to backend crash. Therefore, be prepared - * to overwrite existing information. (As of 7.1, a postmaster only creates - * one shm seg at a time; but for the purposes here, if we did have more than - * one then any one of them would do anyway.) + * Add lines to the data directory lock file. This erases all following + * lines, but that is OK because lines are added in order. */ void -RecordSharedMemoryInLockFile(unsigned long id1, unsigned long id2) +AddToLockFile(int target_line, const char *str) { int fd; int len; int lineno; char *ptr; - char buffer[MAXPGPATH * 2 + 256]; + char buffer[MAXPGPATH * 3 + 256]; fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0); if (fd < 0) @@ -1048,7 +1056,7 @@ RecordSharedMemoryInLockFile(unsigned long id1, unsigned long id2) * Skip over first four lines (PID, pgdata, portnum, socketdir). */ ptr = buffer; - for (lineno = 1; lineno <= 4; lineno++) + for (lineno = 1; lineno < target_line; lineno++) { if ((ptr = strchr(ptr, '\n')) == NULL) { @@ -1059,11 +1067,7 @@ RecordSharedMemoryInLockFile(unsigned long id1, unsigned long id2) ptr++; } - /* - * Append key information. Format to try to keep it the same length - * always (trailing junk won't hurt, but might confuse humans). - */ - sprintf(ptr, "%9lu %9lu\n", id1, id2); + strlcat(buffer, str, sizeof(buffer)); /* * And rewrite the data. Since we write in a single kernel call, this diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c index 5e1b7728fc..92ea514f1d 100644 --- a/src/bin/pg_ctl/pg_ctl.c +++ b/src/bin/pg_ctl/pg_ctl.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -138,6 +139,7 @@ static void read_post_opts(void); static PGPing test_postmaster_connection(bool); static bool postmaster_is_alive(pid_t pid); +static time_t start_time; static char postopts_file[MAXPGPATH]; static char pid_file[MAXPGPATH]; @@ -404,13 +406,13 @@ static PGPing test_postmaster_connection(bool do_checkpoint) { int portnum = 0; - char socket_dir[MAXPGPATH]; + char host_str[MAXPGPATH]; char connstr[MAXPGPATH + 256]; PGPing ret = PQPING_OK; /* assume success for wait == zero */ char **optlines; int i; - socket_dir[0] = '\0'; + host_str[0] = '\0'; connstr[0] = '\0'; for (i = 0; i < wait_seconds; i++) @@ -425,13 +427,14 @@ test_postmaster_connection(bool do_checkpoint) * 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 + * 5 9.1+ server, listen host not created + * 6 9.1+ server, shared memory not created + * 7 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. + * written shmem key, so we fail. 9.1+ writes connection + * information in the file for us to use. * (PG_VERSION could also have told us the major version.) */ @@ -439,7 +442,10 @@ test_postmaster_connection(bool do_checkpoint) if ((optlines = readfile(pid_file)) != NULL && optlines[0] != NULL && optlines[1] != NULL && - optlines[2] != NULL) + optlines[2] != NULL && + /* pre-9.1 server or listen_address line is present? */ + (optlines[3] == NULL || + optlines[5] != NULL)) { /* A 3-line file? */ if (optlines[3] == NULL) @@ -459,31 +465,53 @@ test_postmaster_connection(bool do_checkpoint) return PQPING_NO_ATTEMPT; } } - else /* 9.1+ server */ + else { - portnum = atoi(optlines[2]); - - /* Get socket directory, if specified. */ - if (optlines[3][0] != '\n') + /* + * Easy check to see if we are looking at the right + * data directory: Is the postmaster older than this + * execution of pg_ctl? Subtract 2 seconds to account + * for possible clock skew between pg_ctl and the + * postmaster. + */ + if (atol(optlines[1]) < start_time - 2) { - /* - * 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'; + write_stderr(_("%s: this data directory is running an older postmaster\n"), + progname); + return PQPING_NO_ATTEMPT; } - } + + portnum = atoi(optlines[3]); + /* + * Determine the proper host string to use. + */ +#ifdef HAVE_UNIX_SOCKETS + /* + * Use socket directory, if specified. We assume if we + * have unix sockets, the server does too because we + * just started the postmaster. + */ + /* + * While unix_socket_directory can accept relative + * directories, libpq's host must have a leading slash + * to indicate a socket directory. + */ + if (optlines[4][0] != '\n' && optlines[4][0] != '/') + { + write_stderr(_("%s: -w option cannot use a relative socket directory specification\n"), + progname); + return PQPING_NO_ATTEMPT; + } + strlcpy(host_str, optlines[4], sizeof(host_str)); +#else + strlcpy(host_str, optlines[5], sizeof(host_str)); +#endif + /* remove newline */ + if (strchr(host_str, '\n') != NULL) + *strchr(host_str, '\n') = '\0'; + } + /* * We need to set connect_timeout otherwise on Windows the * Service Control Manager (SCM) will probably timeout first. @@ -491,9 +519,9 @@ test_postmaster_connection(bool do_checkpoint) snprintf(connstr, sizeof(connstr), "dbname=postgres port=%d connect_timeout=5", portnum); - if (socket_dir[0] != '\0') + if (host_str[0] != '\0') snprintf(connstr + strlen(connstr), sizeof(connstr) - strlen(connstr), - " host='%s'", socket_dir); + " host='%s'", host_str); } } @@ -1756,6 +1784,7 @@ main(int argc, char **argv) progname = get_progname(argv[0]); set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_ctl")); + start_time = time(NULL); /* * save argv[0] so do_start() can look for the postmaster if necessary. we diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h index ddba50cafa..b2a8d82079 100644 --- a/src/include/miscadmin.h +++ b/src/include/miscadmin.h @@ -348,11 +348,11 @@ extern PGDLLIMPORT bool process_shared_preload_libraries_in_progress; extern char *shared_preload_libraries_string; extern char *local_preload_libraries_string; +#define LOCK_FILE_LINES 7 extern void CreateDataDirLockFile(bool amPostmaster); extern void CreateSocketLockFile(const char *socketfile, bool amPostmaster); extern void TouchSocketLockFile(void); -extern void RecordSharedMemoryInLockFile(unsigned long id1, - unsigned long id2); +extern void AddToLockFile(int target_line, const char *str); extern void ValidatePgVersion(const char *path); extern void process_shared_preload_libraries(void); extern void process_local_preload_libraries(void);