diff --git a/src/bin/pg_basebackup/pg_basebackup.c b/src/bin/pg_basebackup/pg_basebackup.c index 669aa207a3..4cf875de45 100644 --- a/src/bin/pg_basebackup/pg_basebackup.c +++ b/src/bin/pg_basebackup/pg_basebackup.c @@ -173,6 +173,7 @@ static int bgpipe[2] = {-1, -1}; /* Handle to child process */ static pid_t bgchild = -1; +static bool bgchild_exited = false; static bool in_log_streamer = false; /* End position for xlog streaming, empty string if unknown yet */ @@ -211,6 +212,7 @@ static bool reached_end_position(XLogRecPtr segendpos, uint32 timeline, static const char *get_tablespace_mapping(const char *dir); static void tablespace_list_append(const char *arg); +static void WaitForBGChild(const char *xlogend); static void @@ -967,6 +969,22 @@ ReceiveCopyData(PGconn *conn, WriteDataCallback callback, int r; char *copybuf; + if (bgchild > 0) + { + /* Check to see if our background process is still running fine. */ + int status; + pid_t p; + + p = waitpid(bgchild, &status, WNOHANG); + + /* if our child process exited unexpectedly, we stop */ + if (WIFEXITED(p)) + { + bgchild_exited = true; + break; + } + } + r = PQgetCopyData(conn, ©buf, 0); if (r == -1) { @@ -1167,6 +1185,11 @@ ReceiveTarFile(PGconn *conn, PGresult *res, int rownum) ReceiveCopyData(conn, ReceiveTarCopyChunk, &state); + if (bgchild_exited) + { + return; + } + /* * End of copy data. If requested, and this is the base tablespace, write * configuration file into the tarfile. When done, close the file (but not @@ -1531,6 +1554,11 @@ ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum) if (state.file) fclose(state.file); + if (bgchild_exited) + { + return; + } + progress_report(rownum, state.filename, true, false); if (state.file != NULL) @@ -1796,6 +1824,95 @@ ReceiveBackupManifestInMemoryChunk(size_t r, char *copybuf, appendPQExpBuffer(buf, copybuf, r); } +static void +WaitForBGChild(const char *xlogend) +{ + if (bgchild > 0) + { +#ifndef WIN32 + int status; + pid_t r; +#else + DWORD status; + + /* + * get a pointer sized version of bgchild to avoid warnings about + * casting to a different size on WIN64. + */ + intptr_t bgchild_handle = bgchild; + uint32 hi, + lo; +#endif + + if (verbose) + pg_log_info("waiting for background process to finish streaming ..."); + +#ifndef WIN32 + if (write(bgpipe[1], xlogend, strlen(xlogend)) != strlen(xlogend)) + { + pg_log_info("could not send command to background pipe: %m"); + exit(1); + } + + /* Just wait for the background process to exit */ + r = waitpid(bgchild, &status, 0); + if (r == (pid_t) -1) + { + pg_log_error("could not wait for child process: %m"); + exit(1); + } + if (r != bgchild) + { + pg_log_error("child %d died, expected %d", (int) r, (int) bgchild); + exit(1); + } + if (status != 0) + { + pg_log_error("%s", wait_result_to_str(status)); + exit(1); + } + /* Exited normally, we're happy! */ +#else /* WIN32 */ + + /* + * On Windows, since we are in the same process, we can just store the + * value directly in the variable, and then set the flag that says + * it's there. + */ + if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2) + { + pg_log_error("could not parse write-ahead log location \"%s\"", + xlogend); + exit(1); + } + xlogendptr = ((uint64) hi) << 32 | lo; + InterlockedIncrement(&has_xlogendptr); + + /* First wait for the thread to exit */ + if (WaitForSingleObjectEx((HANDLE) bgchild_handle, INFINITE, FALSE) != + WAIT_OBJECT_0) + { + _dosmaperr(GetLastError()); + pg_log_error("could not wait for child thread: %m"); + exit(1); + } + if (GetExitCodeThread((HANDLE) bgchild_handle, &status) == 0) + { + _dosmaperr(GetLastError()); + pg_log_error("could not get child thread exit status: %m"); + exit(1); + } + if (status != 0) + { + pg_log_error("child thread exited with error %u", + (unsigned int) status); + exit(1); + } + /* Exited normally, we're happy */ +#endif + } +} + static void BaseBackup(void) { @@ -2020,8 +2137,26 @@ BaseBackup(void) ReceiveTarFile(conn, res, i); else ReceiveAndUnpackTarFile(conn, res, i); + + if (bgchild_exited) + break; } /* Loop over all tablespaces */ + + /* if bgchild exited early, we need to stop now. */ + if (bgchild_exited) + { + PQclear(res); + PQfinish(conn); + conn = NULL; + + destroyPQExpBuffer(recoveryconfcontents); + + /* wait for the child process to be finished entirely */ + (void) WaitForBGChild(xlogend); + exit(1); + } + /* * Now receive backup manifest, if appropriate. * @@ -2079,90 +2214,7 @@ BaseBackup(void) exit(1); } - if (bgchild > 0) - { -#ifndef WIN32 - int status; - pid_t r; -#else - DWORD status; - - /* - * get a pointer sized version of bgchild to avoid warnings about - * casting to a different size on WIN64. - */ - intptr_t bgchild_handle = bgchild; - uint32 hi, - lo; -#endif - - if (verbose) - pg_log_info("waiting for background process to finish streaming ..."); - -#ifndef WIN32 - if (write(bgpipe[1], xlogend, strlen(xlogend)) != strlen(xlogend)) - { - pg_log_info("could not send command to background pipe: %m"); - exit(1); - } - - /* Just wait for the background process to exit */ - r = waitpid(bgchild, &status, 0); - if (r == (pid_t) -1) - { - pg_log_error("could not wait for child process: %m"); - exit(1); - } - if (r != bgchild) - { - pg_log_error("child %d died, expected %d", (int) r, (int) bgchild); - exit(1); - } - if (status != 0) - { - pg_log_error("%s", wait_result_to_str(status)); - exit(1); - } - /* Exited normally, we're happy! */ -#else /* WIN32 */ - - /* - * On Windows, since we are in the same process, we can just store the - * value directly in the variable, and then set the flag that says - * it's there. - */ - if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2) - { - pg_log_error("could not parse write-ahead log location \"%s\"", - xlogend); - exit(1); - } - xlogendptr = ((uint64) hi) << 32 | lo; - InterlockedIncrement(&has_xlogendptr); - - /* First wait for the thread to exit */ - if (WaitForSingleObjectEx((HANDLE) bgchild_handle, INFINITE, FALSE) != - WAIT_OBJECT_0) - { - _dosmaperr(GetLastError()); - pg_log_error("could not wait for child thread: %m"); - exit(1); - } - if (GetExitCodeThread((HANDLE) bgchild_handle, &status) == 0) - { - _dosmaperr(GetLastError()); - pg_log_error("could not get child thread exit status: %m"); - exit(1); - } - if (status != 0) - { - pg_log_error("child thread exited with error %u", - (unsigned int) status); - exit(1); - } - /* Exited normally, we're happy */ -#endif - } + (void) WaitForBGChild(xlogend); /* Free the configuration file contents */ destroyPQExpBuffer(recoveryconfcontents);