Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * pg_basebackup.c - receive a base backup using streaming replication protocol
4 : : *
5 : : * Author: Magnus Hagander <magnus@hagander.net>
6 : : *
7 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
8 : : *
9 : : * IDENTIFICATION
10 : : * src/bin/pg_basebackup/pg_basebackup.c
11 : : *-------------------------------------------------------------------------
12 : : */
13 : :
14 : : #include "postgres_fe.h"
15 : :
16 : : #include <unistd.h>
17 : : #include <dirent.h>
18 : : #include <limits.h>
19 : : #include <sys/select.h>
20 : : #include <sys/stat.h>
21 : : #include <sys/wait.h>
22 : : #include <signal.h>
23 : : #include <time.h>
24 : : #ifdef HAVE_LIBZ
25 : : #include <zlib.h>
26 : : #endif
27 : :
28 : : #include "access/xlog_internal.h"
29 : : #include "astreamer_inject.h"
30 : : #include "backup/basebackup.h"
31 : : #include "common/compression.h"
32 : : #include "common/file_perm.h"
33 : : #include "common/file_utils.h"
34 : : #include "common/logging.h"
35 : : #include "fe_utils/option_utils.h"
36 : : #include "fe_utils/recovery_gen.h"
37 : : #include "getopt_long.h"
38 : : #include "libpq/protocol.h"
39 : : #include "receivelog.h"
40 : : #include "streamutil.h"
41 : :
42 : : #define ERRCODE_DATA_CORRUPTED "XX001"
43 : :
44 : : typedef struct TablespaceListCell
45 : : {
46 : : struct TablespaceListCell *next;
47 : : char old_dir[MAXPGPATH];
48 : : char new_dir[MAXPGPATH];
49 : : } TablespaceListCell;
50 : :
51 : : typedef struct TablespaceList
52 : : {
53 : : TablespaceListCell *head;
54 : : TablespaceListCell *tail;
55 : : } TablespaceList;
56 : :
57 : : typedef struct ArchiveStreamState
58 : : {
59 : : int tablespacenum;
60 : : pg_compress_specification *compress;
61 : : astreamer *streamer;
62 : : astreamer *manifest_inject_streamer;
63 : : PQExpBuffer manifest_buffer;
64 : : char manifest_filename[MAXPGPATH];
65 : : FILE *manifest_file;
66 : : } ArchiveStreamState;
67 : :
68 : : typedef struct WriteTarState
69 : : {
70 : : int tablespacenum;
71 : : astreamer *streamer;
72 : : } WriteTarState;
73 : :
74 : : typedef struct WriteManifestState
75 : : {
76 : : char filename[MAXPGPATH];
77 : : FILE *file;
78 : : } WriteManifestState;
79 : :
80 : : typedef void (*WriteDataCallback) (size_t nbytes, char *buf,
81 : : void *callback_data);
82 : :
83 : : /*
84 : : * pg_xlog has been renamed to pg_wal in version 10. This version number
85 : : * should be compared with PQserverVersion().
86 : : */
87 : : #define MINIMUM_VERSION_FOR_PG_WAL 100000
88 : :
89 : : /*
90 : : * Temporary replication slots are supported from version 10.
91 : : */
92 : : #define MINIMUM_VERSION_FOR_TEMP_SLOTS 100000
93 : :
94 : : /*
95 : : * Backup manifests are supported from version 13.
96 : : */
97 : : #define MINIMUM_VERSION_FOR_MANIFESTS 130000
98 : :
99 : : /*
100 : : * Before v15, tar files received from the server will be improperly
101 : : * terminated.
102 : : */
103 : : #define MINIMUM_VERSION_FOR_TERMINATED_TARFILE 150000
104 : :
105 : : /*
106 : : * pg_wal/summaries exists beginning with version 17.
107 : : */
108 : : #define MINIMUM_VERSION_FOR_WAL_SUMMARIES 170000
109 : :
110 : : /*
111 : : * Different ways to include WAL
112 : : */
113 : : typedef enum
114 : : {
115 : : NO_WAL,
116 : : FETCH_WAL,
117 : : STREAM_WAL,
118 : : } IncludeWal;
119 : :
120 : : /*
121 : : * Different places to perform compression
122 : : */
123 : : typedef enum
124 : : {
125 : : COMPRESS_LOCATION_UNSPECIFIED,
126 : : COMPRESS_LOCATION_CLIENT,
127 : : COMPRESS_LOCATION_SERVER,
128 : : } CompressionLocation;
129 : :
130 : : /* Global options */
131 : : static char *basedir = NULL;
132 : : static TablespaceList tablespace_dirs = {NULL, NULL};
133 : : static char *xlog_dir = NULL;
134 : : static char format = '\0'; /* p(lain)/t(ar) */
135 : : static char *label = "pg_basebackup base backup";
136 : : static bool noclean = false;
137 : : static bool checksum_failure = false;
138 : : static bool showprogress = false;
139 : : static bool estimatesize = true;
140 : : static int verbose = 0;
141 : : static IncludeWal includewal = STREAM_WAL;
142 : : static bool fastcheckpoint = false;
143 : : static bool writerecoveryconf = false;
144 : : static bool do_sync = true;
145 : : static int standby_message_timeout = 10 * 1000; /* 10 sec = default */
146 : : static pg_time_t last_progress_report = 0;
147 : : static int32 maxrate = 0; /* no limit by default */
148 : : static char *replication_slot = NULL;
149 : : static bool temp_replication_slot = true;
150 : : static char *backup_target = NULL;
151 : : static bool create_slot = false;
152 : : static bool no_slot = false;
153 : : static bool verify_checksums = true;
154 : : static bool manifest = true;
155 : : static bool manifest_force_encode = false;
156 : : static char *manifest_checksums = NULL;
157 : : static DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
158 : :
159 : : static bool success = false;
160 : : static bool made_new_pgdata = false;
161 : : static bool found_existing_pgdata = false;
162 : : static bool made_new_xlogdir = false;
163 : : static bool found_existing_xlogdir = false;
164 : : static bool made_tablespace_dirs = false;
165 : : static bool found_tablespace_dirs = false;
166 : :
167 : : /* Progress indicators */
168 : : static uint64 totalsize_kb;
169 : : static uint64 totaldone;
170 : : static int tablespacecount;
171 : : static char *progress_filename = NULL;
172 : :
173 : : /* Pipe to communicate with background wal receiver process */
174 : : #ifndef WIN32
175 : : static int bgpipe[2] = {-1, -1};
176 : : #endif
177 : :
178 : : /* Handle to child process */
179 : : static pid_t bgchild = -1;
180 : : static bool in_log_streamer = false;
181 : :
182 : : /* Flag to indicate if child process exited unexpectedly */
183 : : static volatile sig_atomic_t bgchild_exited = false;
184 : :
185 : : /* End position for xlog streaming, empty string if unknown yet */
186 : : static XLogRecPtr xlogendptr;
187 : :
188 : : #ifndef WIN32
189 : : static int has_xlogendptr = 0;
190 : : #else
191 : : static volatile LONG has_xlogendptr = 0;
192 : : #endif
193 : :
194 : : /* Contents of configuration file to be generated */
195 : : static PQExpBuffer recoveryconfcontents = NULL;
196 : :
197 : : /* Function headers */
198 : : static void usage(void);
199 : : static void verify_dir_is_empty_or_create(char *dirname, bool *created, bool *found);
200 : : static void progress_update_filename(const char *filename);
201 : : static void progress_report(int tablespacenum, bool force, bool finished);
202 : :
203 : : static astreamer *CreateBackupStreamer(char *archive_name, char *spclocation,
204 : : astreamer **manifest_inject_streamer_p,
205 : : bool is_recovery_guc_supported,
206 : : bool expect_unterminated_tarfile,
207 : : pg_compress_specification *compress);
208 : : static void ReceiveArchiveStreamChunk(size_t r, char *copybuf,
209 : : void *callback_data);
210 : : static char GetCopyDataByte(size_t r, char *copybuf, size_t *cursor);
211 : : static char *GetCopyDataString(size_t r, char *copybuf, size_t *cursor);
212 : : static uint64 GetCopyDataUInt64(size_t r, char *copybuf, size_t *cursor);
213 : : static void GetCopyDataEnd(size_t r, char *copybuf, size_t cursor);
214 : : static void ReportCopyDataParseError(size_t r, char *copybuf);
215 : : static void ReceiveTarFile(PGconn *conn, char *archive_name, char *spclocation,
216 : : bool tablespacenum, pg_compress_specification *compress);
217 : : static void ReceiveTarCopyChunk(size_t r, char *copybuf, void *callback_data);
218 : : static void ReceiveBackupManifest(PGconn *conn);
219 : : static void ReceiveBackupManifestChunk(size_t r, char *copybuf,
220 : : void *callback_data);
221 : : static void ReceiveBackupManifestInMemory(PGconn *conn, PQExpBuffer buf);
222 : : static void ReceiveBackupManifestInMemoryChunk(size_t r, char *copybuf,
223 : : void *callback_data);
224 : : static void BaseBackup(char *compression_algorithm, char *compression_detail,
225 : : CompressionLocation compressloc,
226 : : pg_compress_specification *client_compress,
227 : : char *incremental_manifest);
228 : :
229 : : static bool reached_end_position(XLogRecPtr segendpos, uint32 timeline,
230 : : bool segment_finished);
231 : :
232 : : static const char *get_tablespace_mapping(const char *dir);
233 : : static void tablespace_list_append(const char *arg);
234 : :
235 : :
236 : : static void
237 : 385 : cleanup_directories_atexit(void)
238 : : {
239 [ + + + + ]: 385 : if (success || in_log_streamer)
240 : 328 : return;
241 : :
242 [ + + + + ]: 57 : if (!noclean && !checksum_failure)
243 : : {
244 [ + + ]: 53 : if (made_new_pgdata)
245 : : {
246 : 19 : pg_log_info("removing data directory \"%s\"", basedir);
247 [ - + ]: 19 : if (!rmtree(basedir, true))
248 : 0 : pg_log_error("failed to remove data directory");
249 : : }
250 [ - + ]: 34 : else if (found_existing_pgdata)
251 : : {
252 : 0 : pg_log_info("removing contents of data directory \"%s\"", basedir);
253 [ # # ]: 0 : if (!rmtree(basedir, false))
254 : 0 : pg_log_error("failed to remove contents of data directory");
255 : : }
256 : :
257 [ - + ]: 53 : if (made_new_xlogdir)
258 : : {
259 : 0 : pg_log_info("removing WAL directory \"%s\"", xlog_dir);
260 [ # # ]: 0 : if (!rmtree(xlog_dir, true))
261 : 0 : pg_log_error("failed to remove WAL directory");
262 : : }
263 [ - + ]: 53 : else if (found_existing_xlogdir)
264 : : {
265 : 0 : pg_log_info("removing contents of WAL directory \"%s\"", xlog_dir);
266 [ # # ]: 0 : if (!rmtree(xlog_dir, false))
267 : 0 : pg_log_error("failed to remove contents of WAL directory");
268 : : }
269 : : }
270 : : else
271 : : {
272 [ + + - + : 4 : if ((made_new_pgdata || found_existing_pgdata) && !checksum_failure)
- + ]
273 : 0 : pg_log_info("data directory \"%s\" not removed at user's request", basedir);
274 : :
275 [ + - - + ]: 4 : if (made_new_xlogdir || found_existing_xlogdir)
276 : 0 : pg_log_info("WAL directory \"%s\" not removed at user's request", xlog_dir);
277 : : }
278 : :
279 [ + - - + : 57 : if ((made_tablespace_dirs || found_tablespace_dirs) && !checksum_failure)
- - ]
280 : 0 : pg_log_info("changes to tablespace directories will not be undone");
281 : : }
282 : :
283 : : static void
284 : 355 : disconnect_atexit(void)
285 : : {
286 [ + + ]: 355 : if (conn != NULL)
287 : 180 : PQfinish(conn);
288 : 355 : }
289 : :
290 : : #ifndef WIN32
291 : : /*
292 : : * If the bgchild exits prematurely and raises a SIGCHLD signal, we can abort
293 : : * processing rather than wait until the backup has finished and error out at
294 : : * that time. On Windows, we use a background thread which can communicate
295 : : * without the need for a signal handler.
296 : : */
297 : : static void
298 : 155 : sigchld_handler(SIGNAL_ARGS)
299 : : {
300 : 155 : bgchild_exited = true;
301 : 155 : }
302 : :
303 : : /*
304 : : * On windows, our background thread dies along with the process. But on
305 : : * Unix, if we have started a subprocess, we want to kill it off so it
306 : : * doesn't remain running trying to stream data.
307 : : */
308 : : static void
309 : 157 : kill_bgchild_atexit(void)
310 : : {
311 [ + - + + ]: 157 : if (bgchild > 0 && !bgchild_exited)
312 : 4 : kill(bgchild, SIGTERM);
313 : 157 : }
314 : : #endif
315 : :
316 : : /*
317 : : * Split argument into old_dir and new_dir and append to tablespace mapping
318 : : * list.
319 : : */
320 : : static void
321 : 22 : tablespace_list_append(const char *arg)
322 : : {
323 : 22 : TablespaceListCell *cell = pg_malloc0_object(TablespaceListCell);
324 : : char *dst;
325 : : char *dst_ptr;
326 : : const char *arg_ptr;
327 : :
328 : 22 : dst_ptr = dst = cell->old_dir;
329 [ + + ]: 764 : for (arg_ptr = arg; *arg_ptr; arg_ptr++)
330 : : {
331 [ - + ]: 743 : if (dst_ptr - dst >= MAXPGPATH)
332 : 0 : pg_fatal("directory name too long");
333 : :
334 [ + + + - ]: 743 : if (*arg_ptr == '\\' && *(arg_ptr + 1) == '=')
335 : : ; /* skip backslash escaping = */
336 [ + + + + : 741 : else if (*arg_ptr == '=' && (arg_ptr == arg || *(arg_ptr - 1) != '\\'))
+ + ]
337 : : {
338 [ + + ]: 22 : if (*cell->new_dir)
339 : 1 : pg_fatal("multiple \"=\" signs in tablespace mapping");
340 : : else
341 : 21 : dst = dst_ptr = cell->new_dir;
342 : : }
343 : : else
344 : 719 : *dst_ptr++ = *arg_ptr;
345 : : }
346 : :
347 [ + + + + ]: 21 : if (!*cell->old_dir || !*cell->new_dir)
348 : 3 : pg_fatal("invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"", arg);
349 : :
350 : : /*
351 : : * All tablespaces are created with absolute directories, so specifying a
352 : : * non-absolute path here would just never match, possibly confusing
353 : : * users. Since we don't know whether the remote side is Windows or not,
354 : : * and it might be different than the local side, permit any path that
355 : : * could be absolute under either set of rules.
356 : : *
357 : : * (There is little practical risk of confusion here, because someone
358 : : * running entirely on Linux isn't likely to have a relative path that
359 : : * begins with a backslash or something that looks like a drive
360 : : * specification. If they do, and they also incorrectly believe that a
361 : : * relative path is acceptable here, we'll silently fail to warn them of
362 : : * their mistake, and the -T option will just not get applied, same as if
363 : : * they'd specified -T for a nonexistent tablespace.)
364 : : */
365 [ + + ]: 18 : if (!is_nonwindows_absolute_path(cell->old_dir) &&
366 [ + - + - : 1 : !is_windows_absolute_path(cell->old_dir))
+ - - + -
- - - ]
367 : 1 : pg_fatal("old directory is not an absolute path in tablespace mapping: %s",
368 : : cell->old_dir);
369 : :
370 [ + + ]: 17 : if (!is_absolute_path(cell->new_dir))
371 : 1 : pg_fatal("new directory is not an absolute path in tablespace mapping: %s",
372 : : cell->new_dir);
373 : :
374 : : /*
375 : : * Comparisons done with these values should involve similarly
376 : : * canonicalized path values. This is particularly sensitive on Windows
377 : : * where path values may not necessarily use Unix slashes.
378 : : */
379 : 16 : canonicalize_path(cell->old_dir);
380 : 16 : canonicalize_path(cell->new_dir);
381 : :
382 [ - + ]: 16 : if (tablespace_dirs.tail)
383 : 0 : tablespace_dirs.tail->next = cell;
384 : : else
385 : 16 : tablespace_dirs.head = cell;
386 : 16 : tablespace_dirs.tail = cell;
387 : 16 : }
388 : :
389 : :
390 : : static void
391 : 1 : usage(void)
392 : : {
393 : 1 : printf(_("%s takes a base backup of a running PostgreSQL server.\n\n"),
394 : : progname);
395 : 1 : printf(_("Usage:\n"));
396 : 1 : printf(_(" %s [OPTION]...\n"), progname);
397 : 1 : printf(_("\nOptions controlling the output:\n"));
398 : 1 : printf(_(" -D, --pgdata=DIRECTORY receive base backup into directory\n"));
399 : 1 : printf(_(" -F, --format=p|t output format (plain (default), tar)\n"));
400 : 1 : printf(_(" -i, --incremental=OLDMANIFEST\n"
401 : : " take incremental backup\n"));
402 : 1 : printf(_(" -r, --max-rate=RATE maximum transfer rate to transfer data directory\n"
403 : : " (in kB/s, or use suffix \"k\" or \"M\")\n"));
404 : 1 : printf(_(" -R, --write-recovery-conf\n"
405 : : " write configuration for replication\n"));
406 : 1 : printf(_(" -t, --target=TARGET[:DETAIL]\n"
407 : : " backup target (if other than client)\n"));
408 : 1 : printf(_(" -T, --tablespace-mapping=OLDDIR=NEWDIR\n"
409 : : " relocate tablespace in OLDDIR to NEWDIR\n"));
410 : 1 : printf(_(" --waldir=WALDIR location for the write-ahead log directory\n"));
411 : 1 : printf(_(" -X, --wal-method=none|fetch|stream\n"
412 : : " include required WAL files with specified method\n"));
413 : 1 : printf(_(" -z, --gzip compress tar output\n"));
414 : 1 : printf(_(" -Z, --compress=[{client|server}-]METHOD[:DETAIL]\n"
415 : : " compress on client or server as specified\n"));
416 : 1 : printf(_(" -Z, --compress=none do not compress tar output\n"));
417 : 1 : printf(_("\nGeneral options:\n"));
418 : 1 : printf(_(" -c, --checkpoint=fast|spread\n"
419 : : " set fast or spread (default) checkpointing\n"));
420 : 1 : printf(_(" -C, --create-slot create replication slot\n"));
421 : 1 : printf(_(" -l, --label=LABEL set backup label\n"));
422 : 1 : printf(_(" -n, --no-clean do not clean up after errors\n"));
423 : 1 : printf(_(" -N, --no-sync do not wait for changes to be written safely to disk\n"));
424 : 1 : printf(_(" -P, --progress show progress information\n"));
425 : 1 : printf(_(" -S, --slot=SLOTNAME replication slot to use\n"));
426 : 1 : printf(_(" -v, --verbose output verbose messages\n"));
427 : 1 : printf(_(" -V, --version output version information, then exit\n"));
428 : 1 : printf(_(" --manifest-checksums=SHA{224,256,384,512}|CRC32C|NONE\n"
429 : : " use algorithm for manifest checksums\n"));
430 : 1 : printf(_(" --manifest-force-encode\n"
431 : : " hex encode all file names in manifest\n"));
432 : 1 : printf(_(" --no-estimate-size do not estimate backup size in server side\n"));
433 : 1 : printf(_(" --no-manifest suppress generation of backup manifest\n"));
434 : 1 : printf(_(" --no-slot prevent creation of temporary replication slot\n"));
435 : 1 : printf(_(" --no-verify-checksums\n"
436 : : " do not verify checksums\n"));
437 : 1 : printf(_(" --sync-method=METHOD\n"
438 : : " set method for syncing files to disk\n"));
439 : 1 : printf(_(" -?, --help show this help, then exit\n"));
440 : 1 : printf(_("\nConnection options:\n"));
441 : 1 : printf(_(" -d, --dbname=CONNSTR connection string\n"));
442 : 1 : printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
443 : 1 : printf(_(" -p, --port=PORT database server port number\n"));
444 : 1 : printf(_(" -s, --status-interval=INTERVAL\n"
445 : : " time between status packets sent to server (in seconds)\n"));
446 : 1 : printf(_(" -U, --username=NAME connect as specified database user\n"));
447 : 1 : printf(_(" -w, --no-password never prompt for password\n"));
448 : 1 : printf(_(" -W, --password force password prompt (should happen automatically)\n"));
449 : 1 : printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
450 : 1 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
451 : 1 : }
452 : :
453 : :
454 : : /*
455 : : * Called in the background process every time data is received.
456 : : * On Unix, we check to see if there is any data on our pipe
457 : : * (which would mean we have a stop position), and if it is, check if
458 : : * it is time to stop.
459 : : * On Windows, we are in a single process, so we can just check if it's
460 : : * time to stop.
461 : : */
462 : : static bool
463 : 2352 : reached_end_position(XLogRecPtr segendpos, uint32 timeline,
464 : : bool segment_finished)
465 : : {
466 [ + + ]: 2352 : if (!has_xlogendptr)
467 : : {
468 : : #ifndef WIN32
469 : : fd_set fds;
470 : 2114 : struct timeval tv = {0};
471 : : int r;
472 : :
473 : : /*
474 : : * Don't have the end pointer yet - check our pipe to see if it has
475 : : * been sent yet.
476 : : */
477 [ + + ]: 35938 : FD_ZERO(&fds);
478 : 2114 : FD_SET(bgpipe[0], &fds);
479 : :
480 : 2114 : r = select(bgpipe[0] + 1, &fds, NULL, NULL, &tv);
481 [ + + ]: 2114 : if (r == 1)
482 : : {
483 : : ssize_t nread;
484 : 151 : char xlogend[64] = {0};
485 : : uint32 hi,
486 : : lo;
487 : :
488 : 151 : nread = read(bgpipe[0], xlogend, sizeof(xlogend) - 1);
489 [ - + ]: 151 : if (nread < 0)
490 : 0 : pg_fatal("could not read from ready pipe: %m");
491 : :
492 [ - + ]: 151 : if (sscanf(xlogend, "%X/%08X", &hi, &lo) != 2)
493 : 0 : pg_fatal("could not parse write-ahead log location \"%s\"",
494 : : xlogend);
495 : 151 : xlogendptr = ((uint64) hi) << 32 | lo;
496 : 151 : has_xlogendptr = 1;
497 : :
498 : : /*
499 : : * Fall through to check if we've reached the point further
500 : : * already.
501 : : */
502 : : }
503 : : else
504 : : {
505 : : /*
506 : : * No data received on the pipe means we don't know the end
507 : : * position yet - so just say it's not time to stop yet.
508 : : */
509 : 1963 : return false;
510 : : }
511 : : #else
512 : :
513 : : /*
514 : : * On win32, has_xlogendptr is set by the main thread, so if it's not
515 : : * set here, we just go back and wait until it shows up.
516 : : */
517 : : return false;
518 : : #endif
519 : : }
520 : :
521 : : /*
522 : : * At this point we have an end pointer, so compare it to the current
523 : : * position to figure out if it's time to stop.
524 : : */
525 [ + + ]: 389 : if (segendpos >= xlogendptr)
526 : 302 : return true;
527 : :
528 : : /*
529 : : * Have end pointer, but haven't reached it yet - so tell the caller to
530 : : * keep streaming.
531 : : */
532 : 87 : return false;
533 : : }
534 : :
535 : : typedef struct
536 : : {
537 : : PGconn *bgconn;
538 : : XLogRecPtr startptr;
539 : : char xlog[MAXPGPATH]; /* directory or tarfile depending on mode */
540 : : char *sysidentifier;
541 : : int timeline;
542 : : pg_compress_algorithm wal_compress_algorithm;
543 : : int wal_compress_level;
544 : : } logstreamer_param;
545 : :
546 : : static int
547 : 153 : LogStreamerMain(logstreamer_param *param)
548 : : {
549 : 153 : StreamCtl stream = {0};
550 : :
551 : 153 : in_log_streamer = true;
552 : :
553 : 153 : stream.startpos = param->startptr;
554 : 153 : stream.timeline = param->timeline;
555 : 153 : stream.sysidentifier = param->sysidentifier;
556 : 153 : stream.stream_stop = reached_end_position;
557 : : #ifndef WIN32
558 : 153 : stream.stop_socket = bgpipe[0];
559 : : #else
560 : : stream.stop_socket = PGINVALID_SOCKET;
561 : : #endif
562 : 153 : stream.standby_message_timeout = standby_message_timeout;
563 : 153 : stream.synchronous = false;
564 : : /* fsync happens at the end of pg_basebackup for all data */
565 : 153 : stream.do_sync = false;
566 : 153 : stream.mark_done = true;
567 : 153 : stream.partial_suffix = NULL;
568 : 153 : stream.replication_slot = replication_slot;
569 [ + + ]: 153 : if (format == 'p')
570 : 139 : stream.walmethod = CreateWalDirectoryMethod(param->xlog,
571 : : PG_COMPRESSION_NONE, 0,
572 : 139 : stream.do_sync);
573 : : else
574 : 14 : stream.walmethod = CreateWalTarMethod(param->xlog,
575 : : param->wal_compress_algorithm,
576 : : param->wal_compress_level,
577 : 14 : stream.do_sync);
578 : :
579 [ + + ]: 153 : if (!ReceiveXlogStream(param->bgconn, &stream))
580 : : {
581 : : /*
582 : : * Any errors will already have been reported in the function process,
583 : : * but we need to tell the parent that we didn't shutdown in a nice
584 : : * way.
585 : : */
586 : : #ifdef WIN32
587 : : /*
588 : : * In order to signal the main thread of an ungraceful exit we set the
589 : : * same flag that we use on Unix to signal SIGCHLD.
590 : : */
591 : : bgchild_exited = true;
592 : : #endif
593 : 2 : return 1;
594 : : }
595 : :
596 [ - + ]: 151 : if (!stream.walmethod->ops->finish(stream.walmethod))
597 : : {
598 : 0 : pg_log_error("could not finish writing WAL files: %m");
599 : : #ifdef WIN32
600 : : bgchild_exited = true;
601 : : #endif
602 : 0 : return 1;
603 : : }
604 : :
605 : 151 : PQfinish(param->bgconn);
606 : :
607 : 151 : stream.walmethod->ops->free(stream.walmethod);
608 : :
609 : 151 : return 0;
610 : : }
611 : :
612 : : /*
613 : : * Initiate background process for receiving xlog during the backup.
614 : : * The background stream will use its own database connection so we can
615 : : * stream the logfile in parallel with the backups.
616 : : */
617 : : static void
618 : 158 : StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier,
619 : : pg_compress_algorithm wal_compress_algorithm,
620 : : int wal_compress_level)
621 : : {
622 : : logstreamer_param *param;
623 : : uint32 hi,
624 : : lo;
625 : : char statusdir[MAXPGPATH];
626 : :
627 : 158 : param = pg_malloc0_object(logstreamer_param);
628 : 158 : param->timeline = timeline;
629 : 158 : param->sysidentifier = sysidentifier;
630 : 158 : param->wal_compress_algorithm = wal_compress_algorithm;
631 : 158 : param->wal_compress_level = wal_compress_level;
632 : :
633 : : /* Convert the starting position */
634 [ - + ]: 158 : if (sscanf(startpos, "%X/%08X", &hi, &lo) != 2)
635 : 0 : pg_fatal("could not parse write-ahead log location \"%s\"",
636 : : startpos);
637 : 158 : param->startptr = ((uint64) hi) << 32 | lo;
638 : : /* Round off to even segment position */
639 : 158 : param->startptr -= XLogSegmentOffset(param->startptr, WalSegSz);
640 : :
641 : : #ifndef WIN32
642 : : /* Create our background pipe */
643 [ - + ]: 158 : if (pipe(bgpipe) < 0)
644 : 0 : pg_fatal("could not create pipe for background process: %m");
645 : : #endif
646 : :
647 : : /* Get a second connection */
648 : 158 : param->bgconn = GetConnection();
649 [ - + ]: 158 : if (!param->bgconn)
650 : : /* Error message already written in GetConnection() */
651 : 0 : exit(1);
652 : :
653 : : /* In post-10 cluster, pg_xlog has been renamed to pg_wal */
654 [ - + ]: 158 : snprintf(param->xlog, sizeof(param->xlog), "%s/%s",
655 : : basedir,
656 : 158 : PQserverVersion(conn) < MINIMUM_VERSION_FOR_PG_WAL ?
657 : : "pg_xlog" : "pg_wal");
658 : :
659 : : /* Temporary replication slots are only supported in 10 and newer */
660 [ - + ]: 158 : if (PQserverVersion(conn) < MINIMUM_VERSION_FOR_TEMP_SLOTS)
661 : 0 : temp_replication_slot = false;
662 : :
663 : : /*
664 : : * Create replication slot if requested
665 : : */
666 [ + + + - ]: 158 : if (temp_replication_slot && !replication_slot)
667 : 151 : replication_slot = psprintf("pg_basebackup_%u",
668 : 151 : (unsigned int) PQbackendPID(param->bgconn));
669 [ + + + + ]: 158 : if (temp_replication_slot || create_slot)
670 : : {
671 [ + + ]: 154 : if (!CreateReplicationSlot(param->bgconn, replication_slot, NULL,
672 : : temp_replication_slot, true, true, false,
673 : : false, false))
674 : 1 : exit(1);
675 : :
676 [ - + ]: 153 : if (verbose)
677 : : {
678 [ # # ]: 0 : if (temp_replication_slot)
679 : 0 : pg_log_info("created temporary replication slot \"%s\"",
680 : : replication_slot);
681 : : else
682 : 0 : pg_log_info("created replication slot \"%s\"",
683 : : replication_slot);
684 : : }
685 : : }
686 : :
687 [ + + ]: 157 : if (format == 'p')
688 : : {
689 : : /*
690 : : * Create pg_wal/archive_status or pg_xlog/archive_status (and thus
691 : : * pg_wal or pg_xlog) depending on the target server so we can write
692 : : * to basedir/pg_wal or basedir/pg_xlog as the directory entry in the
693 : : * tar file may arrive later.
694 : : */
695 [ - + ]: 142 : snprintf(statusdir, sizeof(statusdir), "%s/%s/archive_status",
696 : : basedir,
697 : 142 : PQserverVersion(conn) < MINIMUM_VERSION_FOR_PG_WAL ?
698 : : "pg_xlog" : "pg_wal");
699 : :
700 [ - + - - ]: 142 : if (pg_mkdir_p(statusdir, pg_dir_create_mode) != 0 && errno != EEXIST)
701 : 0 : pg_fatal("could not create directory \"%s\": %m", statusdir);
702 : :
703 : : /*
704 : : * For newer server versions, likewise create pg_wal/summaries
705 : : */
706 [ + - ]: 142 : if (PQserverVersion(conn) >= MINIMUM_VERSION_FOR_WAL_SUMMARIES)
707 : : {
708 : : char summarydir[MAXPGPATH];
709 : :
710 : 142 : snprintf(summarydir, sizeof(summarydir), "%s/%s/summaries",
711 : : basedir, "pg_wal");
712 : :
713 [ - + ]: 142 : if (pg_mkdir_p(summarydir, pg_dir_create_mode) != 0 &&
714 [ # # ]: 0 : errno != EEXIST)
715 : 0 : pg_fatal("could not create directory \"%s\": %m", summarydir);
716 : : }
717 : : }
718 : :
719 : : /*
720 : : * Start a child process and tell it to start streaming. On Unix, this is
721 : : * a fork(). On Windows, we create a thread.
722 : : */
723 : : #ifndef WIN32
724 : 157 : bgchild = fork();
725 [ + + ]: 310 : if (bgchild == 0)
726 : : {
727 : : /* in child process */
728 : 153 : exit(LogStreamerMain(param));
729 : : }
730 [ - + ]: 157 : else if (bgchild < 0)
731 : 0 : pg_fatal("could not create background process: %m");
732 : :
733 : : /*
734 : : * Else we are in the parent process and all is well.
735 : : */
736 : 157 : atexit(kill_bgchild_atexit);
737 : : #else /* WIN32 */
738 : : bgchild = _beginthreadex(NULL, 0, (void *) LogStreamerMain, param, 0, NULL);
739 : : if (bgchild == 0)
740 : : pg_fatal("could not create background thread: %m");
741 : : #endif
742 : 157 : }
743 : :
744 : : /*
745 : : * Verify that the given directory exists and is empty. If it does not
746 : : * exist, it is created. If it exists but is not empty, an error will
747 : : * be given and the process ended.
748 : : */
749 : : static void
750 : 220 : verify_dir_is_empty_or_create(char *dirname, bool *created, bool *found)
751 : : {
752 [ + + + - : 220 : switch (pg_check_dir(dirname))
- ]
753 : : {
754 : 205 : case 0:
755 : :
756 : : /*
757 : : * Does not exist, so create
758 : : */
759 [ - + ]: 205 : if (pg_mkdir_p(dirname, pg_dir_create_mode) == -1)
760 : 0 : pg_fatal("could not create directory \"%s\": %m", dirname);
761 [ + - ]: 205 : if (created)
762 : 205 : *created = true;
763 : 205 : return;
764 : 14 : case 1:
765 : :
766 : : /*
767 : : * Exists, empty
768 : : */
769 [ + - ]: 14 : if (found)
770 : 14 : *found = true;
771 : 14 : return;
772 : 1 : case 2:
773 : : case 3:
774 : : case 4:
775 : :
776 : : /*
777 : : * Exists, not empty
778 : : */
779 : 1 : pg_fatal("directory \"%s\" exists but is not empty", dirname);
780 : 0 : case -1:
781 : :
782 : : /*
783 : : * Access problem
784 : : */
785 : 0 : pg_fatal("could not access directory \"%s\": %m", dirname);
786 : : }
787 : : }
788 : :
789 : : /*
790 : : * Callback to update our notion of the current filename.
791 : : *
792 : : * No other code should modify progress_filename!
793 : : */
794 : : static void
795 : 164690 : progress_update_filename(const char *filename)
796 : : {
797 : : /* We needn't maintain this variable if not doing verbose reports. */
798 [ - + - - ]: 164690 : if (showprogress && verbose)
799 : : {
800 : 0 : pg_free(progress_filename);
801 [ # # ]: 0 : if (filename)
802 : 0 : progress_filename = pg_strdup(filename);
803 : : else
804 : 0 : progress_filename = NULL;
805 : : }
806 : 164690 : }
807 : :
808 : : /*
809 : : * Print a progress report based on the global variables. If verbose output
810 : : * is enabled, also print the current file name.
811 : : *
812 : : * Progress report is written at maximum once per second, unless the force
813 : : * parameter is set to true.
814 : : *
815 : : * If finished is set to true, this is the last progress report. The cursor
816 : : * is moved to the next line.
817 : : */
818 : : static void
819 : 261 : progress_report(int tablespacenum, bool force, bool finished)
820 : : {
821 : : int percent;
822 : : char totaldone_str[32];
823 : : char totalsize_str[32];
824 : : pg_time_t now;
825 : :
826 [ + - ]: 261 : if (!showprogress)
827 : 261 : return;
828 : :
829 : 0 : now = time(NULL);
830 [ # # # # : 0 : if (now == last_progress_report && !force && !finished)
# # ]
831 : 0 : return; /* Max once per second */
832 : :
833 : 0 : last_progress_report = now;
834 [ # # ]: 0 : percent = totalsize_kb ? (int) ((totaldone / 1024) * 100 / totalsize_kb) : 0;
835 : :
836 : : /*
837 : : * Avoid overflowing past 100% or the full size. This may make the total
838 : : * size number change as we approach the end of the backup (the estimate
839 : : * will always be wrong if WAL is included), but that's better than having
840 : : * the done column be bigger than the total.
841 : : */
842 [ # # ]: 0 : if (percent > 100)
843 : 0 : percent = 100;
844 [ # # ]: 0 : if (totaldone / 1024 > totalsize_kb)
845 : 0 : totalsize_kb = totaldone / 1024;
846 : :
847 : 0 : snprintf(totaldone_str, sizeof(totaldone_str), UINT64_FORMAT,
848 : : totaldone / 1024);
849 : 0 : snprintf(totalsize_str, sizeof(totalsize_str), UINT64_FORMAT, totalsize_kb);
850 : :
851 : : #define VERBOSE_FILENAME_LENGTH 35
852 [ # # ]: 0 : if (verbose)
853 : : {
854 [ # # ]: 0 : if (!progress_filename)
855 : :
856 : : /*
857 : : * No filename given, so clear the status line (used for last
858 : : * call)
859 : : */
860 : 0 : fprintf(stderr,
861 : 0 : ngettext("%*s/%s kB (100%%), %d/%d tablespace %*s",
862 : : "%*s/%s kB (100%%), %d/%d tablespaces %*s",
863 : : tablespacecount),
864 : 0 : (int) strlen(totalsize_str),
865 : : totaldone_str, totalsize_str,
866 : : tablespacenum, tablespacecount,
867 : : VERBOSE_FILENAME_LENGTH + 5, "");
868 : : else
869 : : {
870 : 0 : bool truncate = (strlen(progress_filename) > VERBOSE_FILENAME_LENGTH);
871 : :
872 [ # # # # : 0 : fprintf(stderr,
# # # # ]
873 : 0 : ngettext("%*s/%s kB (%d%%), %d/%d tablespace (%s%-*.*s)",
874 : : "%*s/%s kB (%d%%), %d/%d tablespaces (%s%-*.*s)",
875 : : tablespacecount),
876 : 0 : (int) strlen(totalsize_str),
877 : : totaldone_str, totalsize_str, percent,
878 : : tablespacenum, tablespacecount,
879 : : /* Prefix with "..." if we do leading truncation */
880 : : truncate ? "..." : "",
881 : : truncate ? VERBOSE_FILENAME_LENGTH - 3 : VERBOSE_FILENAME_LENGTH,
882 : : truncate ? VERBOSE_FILENAME_LENGTH - 3 : VERBOSE_FILENAME_LENGTH,
883 : : /* Truncate filename at beginning if it's too long */
884 : 0 : truncate ? progress_filename + strlen(progress_filename) - VERBOSE_FILENAME_LENGTH + 3 : progress_filename);
885 : : }
886 : : }
887 : : else
888 : 0 : fprintf(stderr,
889 : 0 : ngettext("%*s/%s kB (%d%%), %d/%d tablespace",
890 : : "%*s/%s kB (%d%%), %d/%d tablespaces",
891 : : tablespacecount),
892 : 0 : (int) strlen(totalsize_str),
893 : : totaldone_str, totalsize_str, percent,
894 : : tablespacenum, tablespacecount);
895 : :
896 : : /*
897 : : * Stay on the same line if reporting to a terminal and we're not done
898 : : * yet.
899 : : */
900 [ # # # # ]: 0 : fputc((!finished && isatty(fileno(stderr))) ? '\r' : '\n', stderr);
901 : : }
902 : :
903 : : static int32
904 : 1 : parse_max_rate(char *src)
905 : : {
906 : : double result;
907 : : char *after_num;
908 : 1 : char *suffix = NULL;
909 : :
910 : 1 : errno = 0;
911 : 1 : result = strtod(src, &after_num);
912 [ - + ]: 1 : if (src == after_num)
913 : 0 : pg_fatal("transfer rate \"%s\" is not a valid value", src);
914 [ - + ]: 1 : if (errno != 0)
915 : 0 : pg_fatal("invalid transfer rate \"%s\": %m", src);
916 : :
917 [ - + ]: 1 : if (result <= 0)
918 : : {
919 : : /*
920 : : * Reject obviously wrong values here.
921 : : */
922 : 0 : pg_fatal("transfer rate must be greater than zero");
923 : : }
924 : :
925 : : /*
926 : : * Evaluate suffix, after skipping over possible whitespace. Lack of
927 : : * suffix means kilobytes.
928 : : */
929 [ - + - - ]: 1 : while (*after_num != '\0' && isspace((unsigned char) *after_num))
930 : 0 : after_num++;
931 : :
932 [ - + ]: 1 : if (*after_num != '\0')
933 : : {
934 : 0 : suffix = after_num;
935 [ # # ]: 0 : if (*after_num == 'k')
936 : : {
937 : : /* kilobyte is the expected unit. */
938 : 0 : after_num++;
939 : : }
940 [ # # ]: 0 : else if (*after_num == 'M')
941 : : {
942 : 0 : after_num++;
943 : 0 : result *= 1024.0;
944 : : }
945 : : }
946 : :
947 : : /* The rest can only consist of white space. */
948 [ - + - - ]: 1 : while (*after_num != '\0' && isspace((unsigned char) *after_num))
949 : 0 : after_num++;
950 : :
951 [ - + ]: 1 : if (*after_num != '\0')
952 : 0 : pg_fatal("invalid --max-rate unit: \"%s\"", suffix);
953 : :
954 : : /* Valid integer? */
955 [ - + ]: 1 : if ((uint64) result != (uint64) ((uint32) result))
956 : 0 : pg_fatal("transfer rate \"%s\" exceeds integer range", src);
957 : :
958 : : /*
959 : : * The range is checked on the server side too, but avoid the server
960 : : * connection if a nonsensical value was passed.
961 : : */
962 [ + - - + ]: 1 : if (result < MAX_RATE_LOWER || result > MAX_RATE_UPPER)
963 : 0 : pg_fatal("transfer rate \"%s\" is out of range", src);
964 : :
965 : 1 : return (int32) result;
966 : : }
967 : :
968 : : /*
969 : : * Basic parsing of a value specified for -Z/--compress.
970 : : *
971 : : * We're not concerned here with understanding exactly what behavior the
972 : : * user wants, but we do need to know whether the user is requesting client
973 : : * or server side compression or leaving it unspecified, and we need to
974 : : * separate the name of the compression algorithm from the detail string.
975 : : *
976 : : * For instance, if the user writes --compress client-lz4:6, we want to
977 : : * separate that into (a) client-side compression, (b) algorithm "lz4",
978 : : * and (c) detail "6". Note, however, that all the client/server prefix is
979 : : * optional, and so is the detail. The algorithm name is required, unless
980 : : * the whole string is an integer, in which case we assume "gzip" as the
981 : : * algorithm and use the integer as the detail.
982 : : *
983 : : * We're not concerned with validation at this stage, so if the user writes
984 : : * --compress client-turkey:sandwich, the requested algorithm is "turkey"
985 : : * and the detail string is "sandwich". We'll sort out whether that's legal
986 : : * at a later stage.
987 : : */
988 : : static void
989 : 32 : backup_parse_compress_options(char *option, char **algorithm, char **detail,
990 : : CompressionLocation *locationres)
991 : : {
992 : : /*
993 : : * Strip off any "client-" or "server-" prefix, calculating the location.
994 : : */
995 [ + + ]: 32 : if (strncmp(option, "server-", 7) == 0)
996 : : {
997 : 15 : *locationres = COMPRESS_LOCATION_SERVER;
998 : 15 : option += 7;
999 : : }
1000 [ + + ]: 17 : else if (strncmp(option, "client-", 7) == 0)
1001 : : {
1002 : 3 : *locationres = COMPRESS_LOCATION_CLIENT;
1003 : 3 : option += 7;
1004 : : }
1005 : : else
1006 : 14 : *locationres = COMPRESS_LOCATION_UNSPECIFIED;
1007 : :
1008 : : /* fallback to the common parsing for the algorithm and detail */
1009 : 32 : parse_compress_options(option, algorithm, detail);
1010 : 32 : }
1011 : :
1012 : : /*
1013 : : * Read a stream of COPY data and invoke the provided callback for each
1014 : : * chunk.
1015 : : */
1016 : : static void
1017 : 181 : ReceiveCopyData(PGconn *conn, WriteDataCallback callback,
1018 : : void *callback_data)
1019 : : {
1020 : : PGresult *res;
1021 : :
1022 : : /* Get the COPY data stream. */
1023 : 181 : res = PQgetResult(conn);
1024 [ - + ]: 181 : if (PQresultStatus(res) != PGRES_COPY_OUT)
1025 : 0 : pg_fatal("could not get COPY data stream: %s",
1026 : : PQerrorMessage(conn));
1027 : 181 : PQclear(res);
1028 : :
1029 : : /* Loop over chunks until done. */
1030 : : while (1)
1031 : 408191 : {
1032 : : int r;
1033 : : char *copybuf;
1034 : :
1035 : 408372 : r = PQgetCopyData(conn, ©buf, 0);
1036 [ + + ]: 408372 : if (r == -1)
1037 : : {
1038 : : /* End of chunk. */
1039 : 179 : break;
1040 : : }
1041 [ - + ]: 408193 : else if (r == -2)
1042 : 0 : pg_fatal("could not read COPY data: %s",
1043 : : PQerrorMessage(conn));
1044 : :
1045 [ + + ]: 408193 : if (bgchild_exited)
1046 : 2 : pg_fatal("background process terminated unexpectedly");
1047 : :
1048 : 408191 : (*callback) (r, copybuf, callback_data);
1049 : :
1050 : 408191 : PQfreemem(copybuf);
1051 : : }
1052 : 179 : }
1053 : :
1054 : : /*
1055 : : * Figure out what to do with an archive received from the server based on
1056 : : * the options selected by the user. We may just write the results directly
1057 : : * to a file, or we might compress first, or we might extract the tar file
1058 : : * and write each member separately. This function doesn't do any of that
1059 : : * directly, but it works out what kind of astreamer we need to create so
1060 : : * that the right stuff happens when, down the road, we actually receive
1061 : : * the data.
1062 : : */
1063 : : static astreamer *
1064 : 205 : CreateBackupStreamer(char *archive_name, char *spclocation,
1065 : : astreamer **manifest_inject_streamer_p,
1066 : : bool is_recovery_guc_supported,
1067 : : bool expect_unterminated_tarfile,
1068 : : pg_compress_specification *compress)
1069 : : {
1070 : 205 : astreamer *streamer = NULL;
1071 : 205 : astreamer *manifest_inject_streamer = NULL;
1072 : : bool inject_manifest;
1073 : : bool is_tar,
1074 : : is_compressed_tar;
1075 : : pg_compress_algorithm compressed_tar_algorithm;
1076 : : bool must_parse_archive;
1077 : :
1078 : : /*
1079 : : * Normally, we emit the backup manifest as a separate file, but when
1080 : : * we're writing a tarfile to stdout, we don't have that option, so
1081 : : * include it in the one tarfile we've got.
1082 : : */
1083 [ + + - + : 205 : inject_manifest = (format == 't' && strcmp(basedir, "-") == 0 && manifest);
- - ]
1084 : :
1085 : : /* Check whether it is a tar archive and its compression type */
1086 : 205 : is_tar = parse_tar_compress_algorithm(archive_name,
1087 : : &compressed_tar_algorithm);
1088 : :
1089 : : /* Is this any kind of compressed tar? */
1090 [ + - ]: 410 : is_compressed_tar = (is_tar &&
1091 [ + + ]: 205 : compressed_tar_algorithm != PG_COMPRESSION_NONE);
1092 : :
1093 : : /*
1094 : : * Injecting the manifest into a compressed tar file would be possible if
1095 : : * we decompressed it, parsed the tarfile, generated a new tarfile, and
1096 : : * recompressed it, but compressing and decompressing multiple times just
1097 : : * to inject the manifest seems inefficient enough that it's probably not
1098 : : * what the user wants. So, instead, reject the request and tell the user
1099 : : * to specify something more reasonable.
1100 : : */
1101 [ - + - - ]: 205 : if (inject_manifest && is_compressed_tar)
1102 : : {
1103 : 0 : pg_log_error("cannot inject manifest into a compressed tar file");
1104 : 0 : pg_log_error_hint("Use client-side compression, send the output to a directory rather than standard output, or use %s.",
1105 : : "--no-manifest");
1106 : 0 : exit(1);
1107 : : }
1108 : :
1109 : : /*
1110 : : * We have to parse the archive if (1) we're suppose to extract it, or if
1111 : : * (2) we need to inject backup_manifest or recovery configuration into
1112 : : * it. However, we only know how to parse tar archives.
1113 : : */
1114 [ + + + - : 225 : must_parse_archive = (format == 'p' || inject_manifest ||
+ + ]
1115 [ - + ]: 20 : (spclocation == NULL && writerecoveryconf));
1116 : :
1117 : : /* At present, we only know how to parse tar archives. */
1118 [ + + - + ]: 205 : if (must_parse_archive && !is_tar)
1119 : : {
1120 : 0 : pg_log_error("cannot parse archive \"%s\"", archive_name);
1121 : 0 : pg_log_error_detail("Only tar archives can be parsed.");
1122 [ # # ]: 0 : if (format == 'p')
1123 : 0 : pg_log_error_detail("Plain format requires pg_basebackup to parse the archive.");
1124 [ # # ]: 0 : if (inject_manifest)
1125 : 0 : pg_log_error_detail("Using - as the output directory requires pg_basebackup to parse the archive.");
1126 [ # # ]: 0 : if (writerecoveryconf)
1127 : 0 : pg_log_error_detail("The -R option requires pg_basebackup to parse the archive.");
1128 : 0 : exit(1);
1129 : : }
1130 : :
1131 [ + + ]: 205 : if (format == 'p')
1132 : : {
1133 : : const char *directory;
1134 : :
1135 : : /*
1136 : : * In plain format, we must extract the archive. The data for the main
1137 : : * tablespace will be written to the base directory, and the data for
1138 : : * other tablespaces will be written to the directory where they're
1139 : : * located on the server, after applying any user-specified tablespace
1140 : : * mappings.
1141 : : *
1142 : : * In the case of an in-place tablespace, spclocation will be a
1143 : : * relative path. We just convert it to an absolute path by prepending
1144 : : * basedir.
1145 : : */
1146 [ + + ]: 182 : if (spclocation == NULL)
1147 : 152 : directory = basedir;
1148 [ + + ]: 30 : else if (!is_absolute_path(spclocation))
1149 : 14 : directory = psprintf("%s/%s", basedir, spclocation);
1150 : : else
1151 : 16 : directory = get_tablespace_mapping(spclocation);
1152 : 182 : streamer = astreamer_extractor_new(directory,
1153 : : get_tablespace_mapping,
1154 : : progress_update_filename);
1155 : : }
1156 : : else
1157 : : {
1158 : : FILE *archive_file;
1159 : : char archive_filename[MAXPGPATH];
1160 : :
1161 : : /*
1162 : : * In tar format, we just write the archive without extracting it.
1163 : : * Normally, we write it to the archive name provided by the caller,
1164 : : * but when the base directory is "-" that means we need to write to
1165 : : * standard output.
1166 : : */
1167 [ - + ]: 23 : if (strcmp(basedir, "-") == 0)
1168 : : {
1169 : 0 : snprintf(archive_filename, sizeof(archive_filename), "-");
1170 : 0 : archive_file = stdout;
1171 : : }
1172 : : else
1173 : : {
1174 : 23 : snprintf(archive_filename, sizeof(archive_filename),
1175 : : "%s/%s", basedir, archive_name);
1176 : 23 : archive_file = NULL;
1177 : : }
1178 : :
1179 [ + + ]: 23 : if (compress->algorithm == PG_COMPRESSION_NONE)
1180 : 17 : streamer = astreamer_plain_writer_new(archive_filename,
1181 : : archive_file);
1182 [ + + ]: 6 : else if (compress->algorithm == PG_COMPRESSION_GZIP)
1183 : : {
1184 : 4 : strlcat(archive_filename, ".gz", sizeof(archive_filename));
1185 : 4 : streamer = astreamer_gzip_writer_new(archive_filename,
1186 : : archive_file, compress);
1187 : : }
1188 [ + - ]: 2 : else if (compress->algorithm == PG_COMPRESSION_LZ4)
1189 : : {
1190 : 2 : strlcat(archive_filename, ".lz4", sizeof(archive_filename));
1191 : 2 : streamer = astreamer_plain_writer_new(archive_filename,
1192 : : archive_file);
1193 : 2 : streamer = astreamer_lz4_compressor_new(streamer, compress);
1194 : : }
1195 [ # # ]: 0 : else if (compress->algorithm == PG_COMPRESSION_ZSTD)
1196 : : {
1197 : 0 : strlcat(archive_filename, ".zst", sizeof(archive_filename));
1198 : 0 : streamer = astreamer_plain_writer_new(archive_filename,
1199 : : archive_file);
1200 : 0 : streamer = astreamer_zstd_compressor_new(streamer, compress);
1201 : : }
1202 : : else
1203 : : {
1204 : : Assert(false); /* not reachable */
1205 : : }
1206 : :
1207 : : /*
1208 : : * If we need to parse the archive for whatever reason, then we'll
1209 : : * also need to re-archive, because, if the output format is tar, the
1210 : : * only point of parsing the archive is to be able to inject stuff
1211 : : * into it.
1212 : : */
1213 [ - + ]: 23 : if (must_parse_archive)
1214 : 0 : streamer = astreamer_tar_archiver_new(streamer);
1215 : 23 : progress_update_filename(archive_filename);
1216 : : }
1217 : :
1218 : : /*
1219 : : * If we're supposed to inject the backup manifest into the results, it
1220 : : * should be done here, so that the file content can be injected directly,
1221 : : * without worrying about the details of the tar format.
1222 : : */
1223 [ - + ]: 205 : if (inject_manifest)
1224 : 0 : manifest_inject_streamer = streamer;
1225 : :
1226 : : /*
1227 : : * If this is the main tablespace and we're supposed to write recovery
1228 : : * information, arrange to do that.
1229 : : */
1230 [ + + + + ]: 205 : if (spclocation == NULL && writerecoveryconf)
1231 : : {
1232 : : Assert(must_parse_archive);
1233 : 4 : streamer = astreamer_recovery_injector_new(streamer,
1234 : : is_recovery_guc_supported,
1235 : : recoveryconfcontents);
1236 : : }
1237 : :
1238 : : /*
1239 : : * If we're doing anything that involves understanding the contents of the
1240 : : * archive, we'll need to parse it. If not, we can skip parsing it, but
1241 : : * old versions of the server send improperly terminated tarfiles, so if
1242 : : * we're talking to such a server we'll need to add the terminator here.
1243 : : */
1244 [ + + ]: 205 : if (must_parse_archive)
1245 : 182 : streamer = astreamer_tar_parser_new(streamer);
1246 [ - + ]: 23 : else if (expect_unterminated_tarfile)
1247 : 0 : streamer = astreamer_tar_terminator_new(streamer);
1248 : :
1249 : : /*
1250 : : * If the user has requested a server compressed archive along with
1251 : : * archive extraction at client then we need to decompress it.
1252 : : */
1253 [ + + + + ]: 205 : if (format == 'p' && is_compressed_tar)
1254 : : {
1255 [ + + ]: 2 : if (compressed_tar_algorithm == PG_COMPRESSION_GZIP)
1256 : 1 : streamer = astreamer_gzip_decompressor_new(streamer);
1257 [ + - ]: 1 : else if (compressed_tar_algorithm == PG_COMPRESSION_LZ4)
1258 : 1 : streamer = astreamer_lz4_decompressor_new(streamer);
1259 [ # # ]: 0 : else if (compressed_tar_algorithm == PG_COMPRESSION_ZSTD)
1260 : 0 : streamer = astreamer_zstd_decompressor_new(streamer);
1261 : : }
1262 : :
1263 : : /* Return the results. */
1264 : 205 : *manifest_inject_streamer_p = manifest_inject_streamer;
1265 : 205 : return streamer;
1266 : : }
1267 : :
1268 : : /*
1269 : : * Receive all of the archives the server wants to send - and the backup
1270 : : * manifest if present - as a single COPY stream.
1271 : : */
1272 : : static void
1273 : 181 : ReceiveArchiveStream(PGconn *conn, pg_compress_specification *compress)
1274 : : {
1275 : : ArchiveStreamState state;
1276 : :
1277 : : /* Set up initial state. */
1278 : 181 : memset(&state, 0, sizeof(state));
1279 : 181 : state.tablespacenum = -1;
1280 : 181 : state.compress = compress;
1281 : :
1282 : : /* All the real work happens in ReceiveArchiveStreamChunk. */
1283 : 181 : ReceiveCopyData(conn, ReceiveArchiveStreamChunk, &state);
1284 : :
1285 : : /* If we wrote the backup manifest to a file, close the file. */
1286 [ + + ]: 179 : if (state.manifest_file != NULL)
1287 : : {
1288 : 168 : fclose(state.manifest_file);
1289 : 168 : state.manifest_file = NULL;
1290 : : }
1291 : :
1292 : : /*
1293 : : * If we buffered the backup manifest in order to inject it into the
1294 : : * output tarfile, do that now.
1295 : : */
1296 [ - + ]: 179 : if (state.manifest_inject_streamer != NULL &&
1297 [ # # ]: 0 : state.manifest_buffer != NULL)
1298 : : {
1299 : 0 : astreamer_inject_file(state.manifest_inject_streamer,
1300 : : "backup_manifest",
1301 : 0 : state.manifest_buffer->data,
1302 : 0 : state.manifest_buffer->len);
1303 : 0 : destroyPQExpBuffer(state.manifest_buffer);
1304 : 0 : state.manifest_buffer = NULL;
1305 : : }
1306 : :
1307 : : /* If there's still an archive in progress, end processing. */
1308 [ + + ]: 179 : if (state.streamer != NULL)
1309 : : {
1310 : 170 : astreamer_finalize(state.streamer);
1311 : 170 : astreamer_free(state.streamer);
1312 : 170 : state.streamer = NULL;
1313 : : }
1314 : 179 : }
1315 : :
1316 : : /*
1317 : : * Receive one chunk of data sent by the server as part of a single COPY
1318 : : * stream that includes all archives and the manifest.
1319 : : */
1320 : : static void
1321 : 408191 : ReceiveArchiveStreamChunk(size_t r, char *copybuf, void *callback_data)
1322 : : {
1323 : 408191 : ArchiveStreamState *state = callback_data;
1324 : 408191 : size_t cursor = 0;
1325 : :
1326 : : /* Each CopyData message begins with a type byte. */
1327 [ + + + + : 408191 : switch (GetCopyDataByte(r, copybuf, &cursor))
- ]
1328 : : {
1329 : 218 : case PqBackupMsg_NewArchive:
1330 : : {
1331 : : /* New archive. */
1332 : : char *archive_name;
1333 : : char *spclocation;
1334 : :
1335 : : /*
1336 : : * We force a progress report at the end of each tablespace. A
1337 : : * new tablespace starts when the previous one ends, except in
1338 : : * the case of the very first one.
1339 : : */
1340 [ + + ]: 218 : if (++state->tablespacenum > 0)
1341 : 37 : progress_report(state->tablespacenum, true, false);
1342 : :
1343 : : /* Sanity check. */
1344 [ + - ]: 218 : if (state->manifest_buffer != NULL ||
1345 [ - + ]: 218 : state->manifest_file != NULL)
1346 : 0 : pg_fatal("archives must precede manifest");
1347 : :
1348 : : /* Parse the rest of the CopyData message. */
1349 : 218 : archive_name = GetCopyDataString(r, copybuf, &cursor);
1350 : 218 : spclocation = GetCopyDataString(r, copybuf, &cursor);
1351 : 218 : GetCopyDataEnd(r, copybuf, cursor);
1352 : :
1353 : : /*
1354 : : * Basic sanity checks on the archive name: it shouldn't be
1355 : : * empty, it shouldn't start with a dot, and it shouldn't
1356 : : * contain a path separator.
1357 : : */
1358 [ + - + - ]: 218 : if (archive_name[0] == '\0' || archive_name[0] == '.' ||
1359 [ + - ]: 218 : strchr(archive_name, '/') != NULL ||
1360 [ - + ]: 218 : strchr(archive_name, '\\') != NULL)
1361 : 0 : pg_fatal("invalid archive name: \"%s\"",
1362 : : archive_name);
1363 : :
1364 : : /*
1365 : : * An empty spclocation is treated as NULL. We expect this
1366 : : * case to occur for the data directory itself, but not for
1367 : : * any archives that correspond to tablespaces.
1368 : : */
1369 [ + + ]: 218 : if (spclocation[0] == '\0')
1370 : 181 : spclocation = NULL;
1371 : :
1372 : : /* End processing of any prior archive. */
1373 [ + + ]: 218 : if (state->streamer != NULL)
1374 : : {
1375 : 33 : astreamer_finalize(state->streamer);
1376 : 33 : astreamer_free(state->streamer);
1377 : 33 : state->streamer = NULL;
1378 : : }
1379 : :
1380 : : /*
1381 : : * Create an appropriate backup streamer, unless a backup
1382 : : * target was specified. In that case, it's up to the server
1383 : : * to put the backup wherever it needs to go.
1384 : : */
1385 [ + + ]: 218 : if (backup_target == NULL)
1386 : : {
1387 : : /*
1388 : : * We know that recovery GUCs are supported, because this
1389 : : * protocol can only be used on v15+.
1390 : : */
1391 : 205 : state->streamer =
1392 : 205 : CreateBackupStreamer(archive_name,
1393 : : spclocation,
1394 : : &state->manifest_inject_streamer,
1395 : : true, false,
1396 : : state->compress);
1397 : : }
1398 : 218 : break;
1399 : : }
1400 : :
1401 : 407572 : case PqMsg_CopyData:
1402 : : {
1403 : : /* Archive or manifest data. */
1404 [ - + ]: 407572 : if (state->manifest_buffer != NULL)
1405 : : {
1406 : : /* Manifest data, buffer in memory. */
1407 : 0 : appendPQExpBuffer(state->manifest_buffer, copybuf + 1,
1408 : : r - 1);
1409 : : }
1410 [ + + ]: 407572 : else if (state->manifest_file != NULL)
1411 : : {
1412 : : /* Manifest data, write to disk. */
1413 [ - + ]: 883 : if (fwrite(copybuf + 1, r - 1, 1,
1414 : : state->manifest_file) != 1)
1415 : : {
1416 : : /*
1417 : : * If fwrite() didn't set errno, assume that the
1418 : : * problem is that we're out of disk space.
1419 : : */
1420 [ # # ]: 0 : if (errno == 0)
1421 : 0 : errno = ENOSPC;
1422 : 0 : pg_fatal("could not write to file \"%s\": %m",
1423 : : state->manifest_filename);
1424 : : }
1425 : : }
1426 [ + - ]: 406689 : else if (state->streamer != NULL)
1427 : : {
1428 : : /* Archive data. */
1429 : 406689 : astreamer_content(state->streamer, NULL, copybuf + 1,
1430 : 406689 : r - 1, ASTREAMER_UNKNOWN);
1431 : : }
1432 : : else
1433 : 0 : pg_fatal("unexpected payload data");
1434 : 407572 : break;
1435 : : }
1436 : :
1437 : 224 : case PqBackupMsg_ProgressReport:
1438 : : {
1439 : : /*
1440 : : * Progress report.
1441 : : *
1442 : : * The remainder of the message is expected to be an 8-byte
1443 : : * count of bytes completed.
1444 : : */
1445 : 224 : totaldone = GetCopyDataUInt64(r, copybuf, &cursor);
1446 : 224 : GetCopyDataEnd(r, copybuf, cursor);
1447 : :
1448 : : /*
1449 : : * The server shouldn't send progress report messages too
1450 : : * often, so we force an update each time we receive one.
1451 : : */
1452 : 224 : progress_report(state->tablespacenum, true, false);
1453 : 224 : break;
1454 : : }
1455 : :
1456 : 177 : case PqBackupMsg_Manifest:
1457 : : {
1458 : : /*
1459 : : * Manifest data will be sent next. This message is not
1460 : : * expected to have any further payload data.
1461 : : */
1462 : 177 : GetCopyDataEnd(r, copybuf, cursor);
1463 : :
1464 : : /*
1465 : : * If a backup target was specified, figuring out where to put
1466 : : * the manifest is the server's problem. Otherwise, we need to
1467 : : * deal with it.
1468 : : */
1469 [ + + ]: 177 : if (backup_target == NULL)
1470 : : {
1471 : : /*
1472 : : * If we're supposed inject the manifest into the archive,
1473 : : * we prepare to buffer it in memory; otherwise, we
1474 : : * prepare to write it to a temporary file.
1475 : : */
1476 [ - + ]: 168 : if (state->manifest_inject_streamer != NULL)
1477 : 0 : state->manifest_buffer = createPQExpBuffer();
1478 : : else
1479 : : {
1480 : 168 : snprintf(state->manifest_filename,
1481 : : sizeof(state->manifest_filename),
1482 : : "%s/backup_manifest.tmp", basedir);
1483 : 168 : state->manifest_file =
1484 : 168 : fopen(state->manifest_filename, "wb");
1485 [ - + ]: 168 : if (state->manifest_file == NULL)
1486 : 0 : pg_fatal("could not create file \"%s\": %m",
1487 : : state->manifest_filename);
1488 : : }
1489 : : }
1490 : 177 : break;
1491 : : }
1492 : :
1493 : 0 : default:
1494 : 0 : ReportCopyDataParseError(r, copybuf);
1495 : 0 : break;
1496 : : }
1497 : 408191 : }
1498 : :
1499 : : /*
1500 : : * Get a single byte from a CopyData message.
1501 : : *
1502 : : * Bail out if none remain.
1503 : : */
1504 : : static char
1505 : 408191 : GetCopyDataByte(size_t r, char *copybuf, size_t *cursor)
1506 : : {
1507 [ - + ]: 408191 : if (*cursor >= r)
1508 : 0 : ReportCopyDataParseError(r, copybuf);
1509 : :
1510 : 408191 : return copybuf[(*cursor)++];
1511 : : }
1512 : :
1513 : : /*
1514 : : * Get a NUL-terminated string from a CopyData message.
1515 : : *
1516 : : * Bail out if the terminating NUL cannot be found.
1517 : : */
1518 : : static char *
1519 : 436 : GetCopyDataString(size_t r, char *copybuf, size_t *cursor)
1520 : : {
1521 : 436 : size_t startpos = *cursor;
1522 : 436 : size_t endpos = startpos;
1523 : :
1524 : : while (1)
1525 : : {
1526 [ - + ]: 2979 : if (endpos >= r)
1527 : 0 : ReportCopyDataParseError(r, copybuf);
1528 [ + + ]: 2979 : if (copybuf[endpos] == '\0')
1529 : 436 : break;
1530 : 2543 : ++endpos;
1531 : : }
1532 : :
1533 : 436 : *cursor = endpos + 1;
1534 : 436 : return ©buf[startpos];
1535 : : }
1536 : :
1537 : : /*
1538 : : * Get an unsigned 64-bit integer from a CopyData message.
1539 : : *
1540 : : * Bail out if there are not at least 8 bytes remaining.
1541 : : */
1542 : : static uint64
1543 : 224 : GetCopyDataUInt64(size_t r, char *copybuf, size_t *cursor)
1544 : : {
1545 : : uint64 result;
1546 : :
1547 [ - + ]: 224 : if (*cursor + sizeof(uint64) > r)
1548 : 0 : ReportCopyDataParseError(r, copybuf);
1549 : 224 : memcpy(&result, ©buf[*cursor], sizeof(uint64));
1550 : 224 : *cursor += sizeof(uint64);
1551 : 224 : return pg_ntoh64(result);
1552 : : }
1553 : :
1554 : : /*
1555 : : * Bail out if we didn't parse the whole message.
1556 : : */
1557 : : static void
1558 : 619 : GetCopyDataEnd(size_t r, char *copybuf, size_t cursor)
1559 : : {
1560 [ - + ]: 619 : if (r != cursor)
1561 : 0 : ReportCopyDataParseError(r, copybuf);
1562 : 619 : }
1563 : :
1564 : : /*
1565 : : * Report failure to parse a CopyData message from the server. Then exit.
1566 : : *
1567 : : * As a debugging aid, we try to give some hint about what kind of message
1568 : : * provoked the failure. Perhaps this is not detailed enough, but it's not
1569 : : * clear that it's worth expending any more code on what should be a
1570 : : * can't-happen case.
1571 : : */
1572 : : static void
1573 : 0 : ReportCopyDataParseError(size_t r, char *copybuf)
1574 : : {
1575 [ # # ]: 0 : if (r == 0)
1576 : 0 : pg_fatal("empty COPY message");
1577 : : else
1578 : 0 : pg_fatal("malformed COPY message of type %d, length %zu",
1579 : : copybuf[0], r);
1580 : : }
1581 : :
1582 : : /*
1583 : : * Receive raw tar data from the server, and stream it to the appropriate
1584 : : * location. If we're writing a single tarfile to standard output, also
1585 : : * receive the backup manifest and inject it into that tarfile.
1586 : : */
1587 : : static void
1588 : 0 : ReceiveTarFile(PGconn *conn, char *archive_name, char *spclocation,
1589 : : bool tablespacenum, pg_compress_specification *compress)
1590 : : {
1591 : : WriteTarState state;
1592 : : astreamer *manifest_inject_streamer;
1593 : : bool is_recovery_guc_supported;
1594 : : bool expect_unterminated_tarfile;
1595 : :
1596 : : /* Pass all COPY data through to the backup streamer. */
1597 : 0 : memset(&state, 0, sizeof(state));
1598 : 0 : is_recovery_guc_supported =
1599 : 0 : PQserverVersion(conn) >= MINIMUM_VERSION_FOR_RECOVERY_GUC;
1600 : 0 : expect_unterminated_tarfile =
1601 : 0 : PQserverVersion(conn) < MINIMUM_VERSION_FOR_TERMINATED_TARFILE;
1602 : 0 : state.streamer = CreateBackupStreamer(archive_name, spclocation,
1603 : : &manifest_inject_streamer,
1604 : : is_recovery_guc_supported,
1605 : : expect_unterminated_tarfile,
1606 : : compress);
1607 : 0 : state.tablespacenum = tablespacenum;
1608 : 0 : ReceiveCopyData(conn, ReceiveTarCopyChunk, &state);
1609 : 0 : progress_update_filename(NULL);
1610 : :
1611 : : /*
1612 : : * The decision as to whether we need to inject the backup manifest into
1613 : : * the output at this stage is made by CreateBackupStreamer; if that is
1614 : : * needed, manifest_inject_streamer will be non-NULL; otherwise, it will
1615 : : * be NULL.
1616 : : */
1617 [ # # ]: 0 : if (manifest_inject_streamer != NULL)
1618 : : {
1619 : : PQExpBufferData buf;
1620 : :
1621 : : /* Slurp the entire backup manifest into a buffer. */
1622 : 0 : initPQExpBuffer(&buf);
1623 : 0 : ReceiveBackupManifestInMemory(conn, &buf);
1624 [ # # ]: 0 : if (PQExpBufferDataBroken(buf))
1625 : 0 : pg_fatal("out of memory");
1626 : :
1627 : : /* Inject it into the output tarfile. */
1628 : 0 : astreamer_inject_file(manifest_inject_streamer, "backup_manifest",
1629 : 0 : buf.data, buf.len);
1630 : :
1631 : : /* Free memory. */
1632 : 0 : termPQExpBuffer(&buf);
1633 : : }
1634 : :
1635 : : /* Cleanup. */
1636 : 0 : astreamer_finalize(state.streamer);
1637 : 0 : astreamer_free(state.streamer);
1638 : :
1639 : 0 : progress_report(tablespacenum, true, false);
1640 : :
1641 : : /*
1642 : : * Do not sync the resulting tar file yet, all files are synced once at
1643 : : * the end.
1644 : : */
1645 : 0 : }
1646 : :
1647 : : /*
1648 : : * Receive one chunk of tar-format data from the server.
1649 : : */
1650 : : static void
1651 : 0 : ReceiveTarCopyChunk(size_t r, char *copybuf, void *callback_data)
1652 : : {
1653 : 0 : WriteTarState *state = callback_data;
1654 : :
1655 : 0 : astreamer_content(state->streamer, NULL, copybuf, r, ASTREAMER_UNKNOWN);
1656 : :
1657 : 0 : totaldone += r;
1658 : 0 : progress_report(state->tablespacenum, false, false);
1659 : 0 : }
1660 : :
1661 : :
1662 : : /*
1663 : : * Retrieve tablespace path, either relocated or original depending on whether
1664 : : * -T was passed or not.
1665 : : */
1666 : : static const char *
1667 : 49 : get_tablespace_mapping(const char *dir)
1668 : : {
1669 : : TablespaceListCell *cell;
1670 : : char canon_dir[MAXPGPATH];
1671 : :
1672 : : /* Canonicalize path for comparison consistency */
1673 : 49 : strlcpy(canon_dir, dir, sizeof(canon_dir));
1674 : 49 : canonicalize_path(canon_dir);
1675 : :
1676 [ + + ]: 49 : for (cell = tablespace_dirs.head; cell; cell = cell->next)
1677 [ + - ]: 48 : if (strcmp(canon_dir, cell->old_dir) == 0)
1678 : 48 : return cell->new_dir;
1679 : :
1680 : 1 : return dir;
1681 : : }
1682 : :
1683 : : /*
1684 : : * Receive the backup manifest file and write it out to a file.
1685 : : */
1686 : : static void
1687 : 0 : ReceiveBackupManifest(PGconn *conn)
1688 : : {
1689 : : WriteManifestState state;
1690 : :
1691 : 0 : snprintf(state.filename, sizeof(state.filename),
1692 : : "%s/backup_manifest.tmp", basedir);
1693 : 0 : state.file = fopen(state.filename, "wb");
1694 [ # # ]: 0 : if (state.file == NULL)
1695 : 0 : pg_fatal("could not create file \"%s\": %m", state.filename);
1696 : :
1697 : 0 : ReceiveCopyData(conn, ReceiveBackupManifestChunk, &state);
1698 : :
1699 : 0 : fclose(state.file);
1700 : 0 : }
1701 : :
1702 : : /*
1703 : : * Receive one chunk of the backup manifest file and write it out to a file.
1704 : : */
1705 : : static void
1706 : 0 : ReceiveBackupManifestChunk(size_t r, char *copybuf, void *callback_data)
1707 : : {
1708 : 0 : WriteManifestState *state = callback_data;
1709 : :
1710 : 0 : errno = 0;
1711 [ # # ]: 0 : if (fwrite(copybuf, r, 1, state->file) != 1)
1712 : : {
1713 : : /* if write didn't set errno, assume problem is no disk space */
1714 [ # # ]: 0 : if (errno == 0)
1715 : 0 : errno = ENOSPC;
1716 : 0 : pg_fatal("could not write to file \"%s\": %m", state->filename);
1717 : : }
1718 : 0 : }
1719 : :
1720 : : /*
1721 : : * Receive the backup manifest file and write it out to a file.
1722 : : */
1723 : : static void
1724 : 0 : ReceiveBackupManifestInMemory(PGconn *conn, PQExpBuffer buf)
1725 : : {
1726 : 0 : ReceiveCopyData(conn, ReceiveBackupManifestInMemoryChunk, buf);
1727 : 0 : }
1728 : :
1729 : : /*
1730 : : * Receive one chunk of the backup manifest file and write it out to a file.
1731 : : */
1732 : : static void
1733 : 0 : ReceiveBackupManifestInMemoryChunk(size_t r, char *copybuf,
1734 : : void *callback_data)
1735 : : {
1736 : 0 : PQExpBuffer buf = callback_data;
1737 : :
1738 : 0 : appendPQExpBuffer(buf, copybuf, r);
1739 : 0 : }
1740 : :
1741 : : static void
1742 : 202 : BaseBackup(char *compression_algorithm, char *compression_detail,
1743 : : CompressionLocation compressloc,
1744 : : pg_compress_specification *client_compress,
1745 : : char *incremental_manifest)
1746 : : {
1747 : : PGresult *res;
1748 : : char *sysidentifier;
1749 : : TimeLineID latesttli;
1750 : : TimeLineID starttli;
1751 : : char *basebkp;
1752 : : int i;
1753 : : char xlogstart[64];
1754 : 202 : char xlogend[64] = {0};
1755 : : int minServerMajor,
1756 : : maxServerMajor;
1757 : : int serverVersion,
1758 : : serverMajor;
1759 : : int writing_to_stdout;
1760 : 202 : bool use_new_option_syntax = false;
1761 : : PQExpBufferData buf;
1762 : :
1763 : : Assert(conn != NULL);
1764 : 202 : initPQExpBuffer(&buf);
1765 : :
1766 : : /*
1767 : : * Check server version. BASE_BACKUP command was introduced in 9.1, so we
1768 : : * can't work with servers older than 9.1.
1769 : : */
1770 : 202 : minServerMajor = 901;
1771 : 202 : maxServerMajor = PG_VERSION_NUM / 100;
1772 : 202 : serverVersion = PQserverVersion(conn);
1773 : 202 : serverMajor = serverVersion / 100;
1774 [ + - - + ]: 202 : if (serverMajor < minServerMajor || serverMajor > maxServerMajor)
1775 : : {
1776 : 0 : const char *serverver = PQparameterStatus(conn, "server_version");
1777 : :
1778 [ # # ]: 0 : pg_fatal("incompatible server version %s",
1779 : : serverver ? serverver : "'unknown'");
1780 : : }
1781 [ + - ]: 202 : if (serverMajor >= 1500)
1782 : 202 : use_new_option_syntax = true;
1783 : :
1784 : : /*
1785 : : * If WAL streaming was requested, also check that the server is new
1786 : : * enough for that.
1787 : : */
1788 [ + + - + ]: 202 : if (includewal == STREAM_WAL && !CheckServerVersionForStreaming(conn))
1789 : : {
1790 : : /*
1791 : : * Error message already written in CheckServerVersionForStreaming(),
1792 : : * but add a hint about using -X none.
1793 : : */
1794 : 0 : pg_log_error_hint("Use -X none or -X fetch to disable log streaming.");
1795 : 0 : exit(1);
1796 : : }
1797 : :
1798 : : /*
1799 : : * Build contents of configuration file if requested.
1800 : : *
1801 : : * Note that we don't use the dbname from key-value pair in conn as that
1802 : : * would have been filled by the default dbname (dbname=replication) in
1803 : : * case the user didn't specify the one. The dbname written in the config
1804 : : * file as part of primary_conninfo would be used by slotsync worker which
1805 : : * doesn't use a replication connection so the default won't work for it.
1806 : : */
1807 [ + + ]: 202 : if (writerecoveryconf)
1808 : 4 : recoveryconfcontents = GenerateRecoveryConfig(conn,
1809 : : replication_slot,
1810 : : GetDbnameFromConnectionOptions(connection_string));
1811 : :
1812 : : /*
1813 : : * Run IDENTIFY_SYSTEM so we can get the timeline
1814 : : */
1815 [ - + ]: 202 : if (!RunIdentifySystem(conn, &sysidentifier, &latesttli, NULL, NULL))
1816 : 0 : exit(1);
1817 : :
1818 : : /*
1819 : : * If the user wants an incremental backup, we must upload the manifest
1820 : : * for the previous backup upon which it is to be based.
1821 : : */
1822 [ + + ]: 202 : if (incremental_manifest != NULL)
1823 : : {
1824 : : int fd;
1825 : : char mbuf[65536];
1826 : : ssize_t nbytes;
1827 : :
1828 : : /* Reject if server is too old. */
1829 [ - + ]: 14 : if (serverVersion < MINIMUM_VERSION_FOR_WAL_SUMMARIES)
1830 : 0 : pg_fatal("server does not support incremental backup");
1831 : :
1832 : : /* Open the file. */
1833 : 14 : fd = open(incremental_manifest, O_RDONLY | PG_BINARY, 0);
1834 [ - + ]: 14 : if (fd < 0)
1835 : 0 : pg_fatal("could not open file \"%s\": %m", incremental_manifest);
1836 : :
1837 : : /* Tell the server what we want to do. */
1838 [ - + ]: 14 : if (PQsendQuery(conn, "UPLOAD_MANIFEST") == 0)
1839 : 0 : pg_fatal("could not send replication command \"%s\": %s",
1840 : : "UPLOAD_MANIFEST", PQerrorMessage(conn));
1841 : 14 : res = PQgetResult(conn);
1842 [ - + ]: 14 : if (PQresultStatus(res) != PGRES_COPY_IN)
1843 : : {
1844 [ # # ]: 0 : if (PQresultStatus(res) == PGRES_FATAL_ERROR)
1845 : 0 : pg_fatal("could not upload manifest: %s",
1846 : : PQerrorMessage(conn));
1847 : : else
1848 : 0 : pg_fatal("could not upload manifest: unexpected status %s",
1849 : : PQresStatus(PQresultStatus(res)));
1850 : : }
1851 : :
1852 : : /* Loop, reading from the file and sending the data to the server. */
1853 [ + + ]: 57 : while ((nbytes = read(fd, mbuf, sizeof mbuf)) > 0)
1854 : : {
1855 [ - + ]: 43 : if (PQputCopyData(conn, mbuf, nbytes) < 0)
1856 : 0 : pg_fatal("could not send COPY data: %s",
1857 : : PQerrorMessage(conn));
1858 : : }
1859 : :
1860 : : /* Bail out if we exited the loop due to an error. */
1861 [ - + ]: 14 : if (nbytes < 0)
1862 : 0 : pg_fatal("could not read file \"%s\": %m", incremental_manifest);
1863 : :
1864 : : /* End the COPY operation. */
1865 [ - + ]: 14 : if (PQputCopyEnd(conn, NULL) < 0)
1866 : 0 : pg_fatal("could not send end-of-COPY: %s",
1867 : : PQerrorMessage(conn));
1868 : :
1869 : : /* See whether the server is happy with what we sent. */
1870 : 14 : res = PQgetResult(conn);
1871 [ + + ]: 14 : if (PQresultStatus(res) == PGRES_FATAL_ERROR)
1872 : 1 : pg_fatal("could not upload manifest: %s",
1873 : : PQerrorMessage(conn));
1874 [ - + ]: 13 : else if (PQresultStatus(res) != PGRES_COMMAND_OK)
1875 : 0 : pg_fatal("could not upload manifest: unexpected status %s",
1876 : : PQresStatus(PQresultStatus(res)));
1877 : :
1878 : : /* Consume ReadyForQuery message from server. */
1879 : 13 : res = PQgetResult(conn);
1880 [ - + ]: 13 : if (res != NULL)
1881 : 0 : pg_fatal("unexpected extra result while sending manifest");
1882 : :
1883 : : /* Add INCREMENTAL option to BASE_BACKUP command. */
1884 : 13 : AppendPlainCommandOption(&buf, use_new_option_syntax, "INCREMENTAL");
1885 : : }
1886 : :
1887 : : /*
1888 : : * Continue building up the options list for the BASE_BACKUP command.
1889 : : */
1890 : 201 : AppendStringCommandOption(&buf, use_new_option_syntax, "LABEL", label);
1891 [ + - ]: 201 : if (estimatesize)
1892 : 201 : AppendPlainCommandOption(&buf, use_new_option_syntax, "PROGRESS");
1893 [ + + ]: 201 : if (includewal == FETCH_WAL)
1894 : 19 : AppendPlainCommandOption(&buf, use_new_option_syntax, "WAL");
1895 [ + + ]: 201 : if (fastcheckpoint)
1896 : : {
1897 [ + - ]: 191 : if (use_new_option_syntax)
1898 : 191 : AppendStringCommandOption(&buf, use_new_option_syntax,
1899 : : "CHECKPOINT", "fast");
1900 : : else
1901 : 0 : AppendPlainCommandOption(&buf, use_new_option_syntax, "FAST");
1902 : : }
1903 [ + + ]: 201 : if (includewal != NO_WAL)
1904 : : {
1905 [ + - ]: 191 : if (use_new_option_syntax)
1906 : 191 : AppendIntegerCommandOption(&buf, use_new_option_syntax, "WAIT", 0);
1907 : : else
1908 : 0 : AppendPlainCommandOption(&buf, use_new_option_syntax, "NOWAIT");
1909 : : }
1910 [ + + ]: 201 : if (maxrate > 0)
1911 : 1 : AppendIntegerCommandOption(&buf, use_new_option_syntax, "MAX_RATE",
1912 : : maxrate);
1913 [ + + ]: 201 : if (format == 't')
1914 : 21 : AppendPlainCommandOption(&buf, use_new_option_syntax, "TABLESPACE_MAP");
1915 [ + + ]: 201 : if (!verify_checksums)
1916 : : {
1917 [ + - ]: 1 : if (use_new_option_syntax)
1918 : 1 : AppendIntegerCommandOption(&buf, use_new_option_syntax,
1919 : : "VERIFY_CHECKSUMS", 0);
1920 : : else
1921 : 0 : AppendPlainCommandOption(&buf, use_new_option_syntax,
1922 : : "NOVERIFY_CHECKSUMS");
1923 : : }
1924 : :
1925 [ + + ]: 201 : if (manifest)
1926 : : {
1927 : 200 : AppendStringCommandOption(&buf, use_new_option_syntax, "MANIFEST",
1928 [ + + ]: 200 : manifest_force_encode ? "force-encode" : "yes");
1929 [ + + ]: 200 : if (manifest_checksums != NULL)
1930 : 14 : AppendStringCommandOption(&buf, use_new_option_syntax,
1931 : : "MANIFEST_CHECKSUMS", manifest_checksums);
1932 : : }
1933 : :
1934 [ + + ]: 201 : if (backup_target != NULL)
1935 : : {
1936 : : char *colon;
1937 : :
1938 [ - + ]: 14 : if (serverMajor < 1500)
1939 : 0 : pg_fatal("backup targets are not supported by this server version");
1940 : :
1941 [ - + ]: 14 : if (writerecoveryconf)
1942 : 0 : pg_fatal("recovery configuration cannot be written when a backup target is used");
1943 : :
1944 : 14 : AppendPlainCommandOption(&buf, use_new_option_syntax, "TABLESPACE_MAP");
1945 : :
1946 [ + + ]: 14 : if ((colon = strchr(backup_target, ':')) == NULL)
1947 : : {
1948 : 6 : AppendStringCommandOption(&buf, use_new_option_syntax,
1949 : : "TARGET", backup_target);
1950 : : }
1951 : : else
1952 : : {
1953 : : char *target;
1954 : :
1955 : 8 : target = pnstrdup(backup_target, colon - backup_target);
1956 : 8 : AppendStringCommandOption(&buf, use_new_option_syntax,
1957 : : "TARGET", target);
1958 : 8 : AppendStringCommandOption(&buf, use_new_option_syntax,
1959 : 8 : "TARGET_DETAIL", colon + 1);
1960 : : }
1961 : : }
1962 [ + - ]: 187 : else if (serverMajor >= 1500)
1963 : 187 : AppendStringCommandOption(&buf, use_new_option_syntax,
1964 : : "TARGET", "client");
1965 : :
1966 [ + + ]: 201 : if (compressloc == COMPRESS_LOCATION_SERVER)
1967 : : {
1968 [ - + ]: 26 : if (!use_new_option_syntax)
1969 : 0 : pg_fatal("server does not support server-side compression");
1970 : 26 : AppendStringCommandOption(&buf, use_new_option_syntax,
1971 : : "COMPRESSION", compression_algorithm);
1972 [ + + ]: 26 : if (compression_detail != NULL)
1973 : 12 : AppendStringCommandOption(&buf, use_new_option_syntax,
1974 : : "COMPRESSION_DETAIL",
1975 : : compression_detail);
1976 : : }
1977 : :
1978 [ - + ]: 201 : if (verbose)
1979 : 0 : pg_log_info("initiating base backup, waiting for checkpoint to complete");
1980 : :
1981 [ - + - - ]: 201 : if (showprogress && !verbose)
1982 : : {
1983 : 0 : fprintf(stderr, _("waiting for checkpoint"));
1984 [ # # ]: 0 : if (isatty(fileno(stderr)))
1985 : 0 : fprintf(stderr, "\r");
1986 : : else
1987 : 0 : fprintf(stderr, "\n");
1988 : : }
1989 : :
1990 [ + - + - ]: 201 : if (use_new_option_syntax && buf.len > 0)
1991 : 201 : basebkp = psprintf("BASE_BACKUP (%s)", buf.data);
1992 : : else
1993 : 0 : basebkp = psprintf("BASE_BACKUP %s", buf.data);
1994 : :
1995 : : /* OK, try to start the backup. */
1996 [ - + ]: 201 : if (PQsendQuery(conn, basebkp) == 0)
1997 : 0 : pg_fatal("could not send replication command \"%s\": %s",
1998 : : "BASE_BACKUP", PQerrorMessage(conn));
1999 : :
2000 : : /*
2001 : : * Get the starting WAL location
2002 : : */
2003 : 201 : res = PQgetResult(conn);
2004 [ + + ]: 201 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
2005 : 18 : pg_fatal("could not initiate base backup: %s",
2006 : : PQerrorMessage(conn));
2007 [ - + ]: 183 : if (PQntuples(res) != 1)
2008 : 0 : pg_fatal("server returned unexpected response to BASE_BACKUP command; got %d rows and %d fields, expected %d rows and %d fields",
2009 : : PQntuples(res), PQnfields(res), 1, 2);
2010 : :
2011 : 183 : strlcpy(xlogstart, PQgetvalue(res, 0, 0), sizeof(xlogstart));
2012 : :
2013 [ - + ]: 183 : if (verbose)
2014 : 0 : pg_log_info("checkpoint completed");
2015 : :
2016 : : /*
2017 : : * 9.3 and later sends the TLI of the starting point. With older servers,
2018 : : * assume it's the same as the latest timeline reported by
2019 : : * IDENTIFY_SYSTEM.
2020 : : */
2021 [ + - ]: 183 : if (PQnfields(res) >= 2)
2022 : 183 : starttli = atoi(PQgetvalue(res, 0, 1));
2023 : : else
2024 : 0 : starttli = latesttli;
2025 : 183 : PQclear(res);
2026 : :
2027 [ - + - - ]: 183 : if (verbose && includewal != NO_WAL)
2028 : 0 : pg_log_info("write-ahead log start point: %s on timeline %u",
2029 : : xlogstart, starttli);
2030 : :
2031 : : /*
2032 : : * Get the header
2033 : : */
2034 : 183 : res = PQgetResult(conn);
2035 [ - + ]: 183 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
2036 : 0 : pg_fatal("could not get backup header: %s",
2037 : : PQerrorMessage(conn));
2038 [ - + ]: 183 : if (PQntuples(res) < 1)
2039 : 0 : pg_fatal("no data returned from server");
2040 : :
2041 : : /*
2042 : : * Sum up the total size, for progress reporting
2043 : : */
2044 : 183 : totalsize_kb = totaldone = 0;
2045 : 183 : tablespacecount = PQntuples(res);
2046 [ + + ]: 402 : for (i = 0; i < PQntuples(res); i++)
2047 : : {
2048 : 220 : totalsize_kb += atoll(PQgetvalue(res, i, 2));
2049 : :
2050 : : /*
2051 : : * Verify tablespace directories are empty. Don't bother with the
2052 : : * first once since it can be relocated, and it will be checked before
2053 : : * we do anything anyway.
2054 : : *
2055 : : * Note that this is skipped for tar format backups and backups that
2056 : : * the server is storing to a target location, since in that case we
2057 : : * won't be storing anything into these directories and thus should
2058 : : * not create them.
2059 : : */
2060 [ + + + + : 220 : if (backup_target == NULL && format == 'p' && !PQgetisnull(res, i, 1))
+ + ]
2061 : : {
2062 : 31 : char *path = PQgetvalue(res, i, 1);
2063 : :
2064 [ + + ]: 31 : if (is_absolute_path(path))
2065 : 17 : path = unconstify(char *, get_tablespace_mapping(path));
2066 : : else
2067 : : {
2068 : : /* This is an in-place tablespace, so prepend basedir. */
2069 : 14 : path = psprintf("%s/%s", basedir, path);
2070 : : }
2071 : :
2072 : 31 : verify_dir_is_empty_or_create(path, &made_tablespace_dirs, &found_tablespace_dirs);
2073 : : }
2074 : : }
2075 : :
2076 : : /*
2077 : : * When writing to stdout, require a single tablespace
2078 : : */
2079 [ + + + - ]: 202 : writing_to_stdout = format == 't' && basedir != NULL &&
2080 [ - + ]: 20 : strcmp(basedir, "-") == 0;
2081 [ - + - - ]: 182 : if (writing_to_stdout && PQntuples(res) > 1)
2082 : 0 : pg_fatal("can only write single tablespace to stdout, database has %d",
2083 : : PQntuples(res));
2084 : :
2085 : : /*
2086 : : * If we're streaming WAL, start the streaming session before we start
2087 : : * receiving the actual data chunks.
2088 : : */
2089 [ + + ]: 182 : if (includewal == STREAM_WAL)
2090 : : {
2091 : : pg_compress_algorithm wal_compress_algorithm;
2092 : : int wal_compress_level;
2093 : :
2094 [ - + ]: 158 : if (verbose)
2095 : 0 : pg_log_info("starting background WAL receiver");
2096 : :
2097 [ + + ]: 158 : if (client_compress->algorithm == PG_COMPRESSION_GZIP)
2098 : : {
2099 : 3 : wal_compress_algorithm = PG_COMPRESSION_GZIP;
2100 : 3 : wal_compress_level = client_compress->level;
2101 : : }
2102 : : else
2103 : : {
2104 : 155 : wal_compress_algorithm = PG_COMPRESSION_NONE;
2105 : 155 : wal_compress_level = 0;
2106 : : }
2107 : :
2108 : 158 : StartLogStreamer(xlogstart, starttli, sysidentifier,
2109 : : wal_compress_algorithm,
2110 : : wal_compress_level);
2111 : : }
2112 : :
2113 [ + - ]: 181 : if (serverMajor >= 1500)
2114 : : {
2115 : : /* Receive a single tar stream with everything. */
2116 : 181 : ReceiveArchiveStream(conn, client_compress);
2117 : : }
2118 : : else
2119 : : {
2120 : : /* Receive a tar file for each tablespace in turn */
2121 [ # # ]: 0 : for (i = 0; i < PQntuples(res); i++)
2122 : : {
2123 : : char archive_name[MAXPGPATH];
2124 : : char *spclocation;
2125 : :
2126 : : /*
2127 : : * If we write the data out to a tar file, it will be named
2128 : : * base.tar if it's the main data directory or <tablespaceoid>.tar
2129 : : * if it's for another tablespace. CreateBackupStreamer() will
2130 : : * arrange to add an extension to the archive name if
2131 : : * pg_basebackup is performing compression, depending on the
2132 : : * compression type.
2133 : : */
2134 [ # # ]: 0 : if (PQgetisnull(res, i, 0))
2135 : : {
2136 : 0 : strlcpy(archive_name, "base.tar", sizeof(archive_name));
2137 : 0 : spclocation = NULL;
2138 : : }
2139 : : else
2140 : : {
2141 : 0 : snprintf(archive_name, sizeof(archive_name),
2142 : : "%s.tar", PQgetvalue(res, i, 0));
2143 : 0 : spclocation = PQgetvalue(res, i, 1);
2144 : : }
2145 : :
2146 : 0 : ReceiveTarFile(conn, archive_name, spclocation, i,
2147 : : client_compress);
2148 : : }
2149 : :
2150 : : /*
2151 : : * Now receive backup manifest, if appropriate.
2152 : : *
2153 : : * If we're writing a tarfile to stdout, ReceiveTarFile will have
2154 : : * already processed the backup manifest and included it in the output
2155 : : * tarfile. Such a configuration doesn't allow for writing multiple
2156 : : * files.
2157 : : *
2158 : : * If we're talking to an older server, it won't send a backup
2159 : : * manifest, so don't try to receive one.
2160 : : */
2161 [ # # # # ]: 0 : if (!writing_to_stdout && manifest)
2162 : 0 : ReceiveBackupManifest(conn);
2163 : : }
2164 : :
2165 [ - + ]: 179 : if (showprogress)
2166 : : {
2167 : 0 : progress_update_filename(NULL);
2168 : 0 : progress_report(PQntuples(res), true, true);
2169 : : }
2170 : :
2171 : 179 : PQclear(res);
2172 : :
2173 : : /*
2174 : : * Get the stop position
2175 : : */
2176 : 179 : res = PQgetResult(conn);
2177 [ + + ]: 179 : if (PQresultStatus(res) != PGRES_TUPLES_OK)
2178 : 1 : pg_fatal("backup failed: %s",
2179 : : PQerrorMessage(conn));
2180 [ - + ]: 178 : if (PQntuples(res) != 1)
2181 : 0 : pg_fatal("no write-ahead log end position returned from server");
2182 : 178 : strlcpy(xlogend, PQgetvalue(res, 0, 0), sizeof(xlogend));
2183 [ - + - - ]: 178 : if (verbose && includewal != NO_WAL)
2184 : 0 : pg_log_info("write-ahead log end point: %s", xlogend);
2185 : 178 : PQclear(res);
2186 : :
2187 : 178 : res = PQgetResult(conn);
2188 [ + + ]: 178 : if (PQresultStatus(res) != PGRES_COMMAND_OK)
2189 : : {
2190 : 3 : const char *sqlstate = PQresultErrorField(res, PG_DIAG_SQLSTATE);
2191 : :
2192 [ + - ]: 3 : if (sqlstate &&
2193 [ + - ]: 3 : strcmp(sqlstate, ERRCODE_DATA_CORRUPTED) == 0)
2194 : : {
2195 : 3 : pg_log_error("checksum error occurred");
2196 : 3 : checksum_failure = true;
2197 : : }
2198 : : else
2199 : : {
2200 : 0 : pg_log_error("final receive failed: %s",
2201 : : PQerrorMessage(conn));
2202 : : }
2203 : 3 : exit(1);
2204 : : }
2205 : :
2206 [ + + ]: 175 : if (bgchild > 0)
2207 : : {
2208 : : #ifndef WIN32
2209 : : int status;
2210 : : pid_t r;
2211 : : #else
2212 : : DWORD status;
2213 : :
2214 : : /*
2215 : : * get a pointer sized version of bgchild to avoid warnings about
2216 : : * casting to a different size on WIN64.
2217 : : */
2218 : : intptr_t bgchild_handle = bgchild;
2219 : : uint32 hi,
2220 : : lo;
2221 : : #endif
2222 : :
2223 [ - + ]: 151 : if (verbose)
2224 : 0 : pg_log_info("waiting for background process to finish streaming ...");
2225 : :
2226 : : #ifndef WIN32
2227 [ - + ]: 151 : if (write(bgpipe[1], xlogend, strlen(xlogend)) != strlen(xlogend))
2228 : 0 : pg_fatal("could not send command to background pipe: %m");
2229 : :
2230 : : /* Just wait for the background process to exit */
2231 : 151 : r = waitpid(bgchild, &status, 0);
2232 [ - + ]: 151 : if (r == (pid_t) -1)
2233 : 0 : pg_fatal("could not wait for child process: %m");
2234 [ - + ]: 151 : if (r != bgchild)
2235 : 0 : pg_fatal("child %d died, expected %d", (int) r, (int) bgchild);
2236 [ - + ]: 151 : if (status != 0)
2237 : 0 : pg_fatal("%s", wait_result_to_str(status));
2238 : : /* Exited normally, we're happy! */
2239 : : #else /* WIN32 */
2240 : :
2241 : : /*
2242 : : * On Windows, since we are in the same process, we can just store the
2243 : : * value directly in the variable, and then set the flag that says
2244 : : * it's there.
2245 : : */
2246 : : if (sscanf(xlogend, "%X/%08X", &hi, &lo) != 2)
2247 : : pg_fatal("could not parse write-ahead log location \"%s\"",
2248 : : xlogend);
2249 : : xlogendptr = ((uint64) hi) << 32 | lo;
2250 : : InterlockedIncrement(&has_xlogendptr);
2251 : :
2252 : : /* First wait for the thread to exit */
2253 : : if (WaitForSingleObjectEx((HANDLE) bgchild_handle, INFINITE, FALSE) !=
2254 : : WAIT_OBJECT_0)
2255 : : {
2256 : : _dosmaperr(GetLastError());
2257 : : pg_fatal("could not wait for child thread: %m");
2258 : : }
2259 : : if (GetExitCodeThread((HANDLE) bgchild_handle, &status) == 0)
2260 : : {
2261 : : _dosmaperr(GetLastError());
2262 : : pg_fatal("could not get child thread exit status: %m");
2263 : : }
2264 : : if (status != 0)
2265 : : pg_fatal("child thread exited with error %u",
2266 : : (unsigned int) status);
2267 : : /* Exited normally, we're happy */
2268 : : #endif
2269 : : }
2270 : :
2271 : : /* Free the configuration file contents */
2272 : 175 : destroyPQExpBuffer(recoveryconfcontents);
2273 : :
2274 : : /*
2275 : : * End of copy data. Final result is already checked inside the loop.
2276 : : */
2277 : 175 : PQclear(res);
2278 : 175 : PQfinish(conn);
2279 : 175 : conn = NULL;
2280 : :
2281 : : /*
2282 : : * Make data persistent on disk once backup is completed. For tar format
2283 : : * sync the parent directory and all its contents as each tar file was not
2284 : : * synced after being completed. In plain format, all the data of the
2285 : : * base directory is synced, taking into account all the tablespaces.
2286 : : * Errors are not considered fatal.
2287 : : *
2288 : : * If, however, there's a backup target, we're not writing anything
2289 : : * locally, so in that case we skip this step.
2290 : : */
2291 [ - + - - ]: 175 : if (do_sync && backup_target == NULL)
2292 : : {
2293 [ # # ]: 0 : if (verbose)
2294 : 0 : pg_log_info("syncing data to disk ...");
2295 [ # # ]: 0 : if (format == 't')
2296 : : {
2297 [ # # ]: 0 : if (strcmp(basedir, "-") != 0)
2298 : 0 : (void) sync_dir_recurse(basedir, sync_method);
2299 : : }
2300 : : else
2301 : : {
2302 : 0 : (void) sync_pgdata(basedir, serverVersion, sync_method, true);
2303 : : }
2304 : : }
2305 : :
2306 : : /*
2307 : : * After synchronizing data to disk, perform a durable rename of
2308 : : * backup_manifest.tmp to backup_manifest, if we wrote such a file. This
2309 : : * way, a failure or system crash before we reach this point will leave us
2310 : : * without a backup_manifest file, decreasing the chances that a directory
2311 : : * we leave behind will be mistaken for a valid backup.
2312 : : */
2313 [ + - + + : 175 : if (!writing_to_stdout && manifest && backup_target == NULL)
+ + ]
2314 : : {
2315 : : char tmp_filename[MAXPGPATH];
2316 : : char filename[MAXPGPATH];
2317 : :
2318 [ - + ]: 165 : if (verbose)
2319 : 0 : pg_log_info("renaming backup_manifest.tmp to backup_manifest");
2320 : :
2321 : 165 : snprintf(tmp_filename, MAXPGPATH, "%s/backup_manifest.tmp", basedir);
2322 : 165 : snprintf(filename, MAXPGPATH, "%s/backup_manifest", basedir);
2323 : :
2324 [ - + ]: 165 : if (do_sync)
2325 : : {
2326 : : /* durable_rename emits its own log message in case of failure */
2327 [ # # ]: 0 : if (durable_rename(tmp_filename, filename) != 0)
2328 : 0 : exit(1);
2329 : : }
2330 : : else
2331 : : {
2332 [ - + ]: 165 : if (rename(tmp_filename, filename) != 0)
2333 : 0 : pg_fatal("could not rename file \"%s\" to \"%s\": %m",
2334 : : tmp_filename, filename);
2335 : : }
2336 : : }
2337 : :
2338 [ - + ]: 175 : if (verbose)
2339 : 0 : pg_log_info("base backup completed");
2340 : 175 : }
2341 : :
2342 : :
2343 : : int
2344 : 234 : main(int argc, char **argv)
2345 : : {
2346 : : static struct option long_options[] = {
2347 : : {"help", no_argument, NULL, '?'},
2348 : : {"version", no_argument, NULL, 'V'},
2349 : : {"pgdata", required_argument, NULL, 'D'},
2350 : : {"format", required_argument, NULL, 'F'},
2351 : : {"incremental", required_argument, NULL, 'i'},
2352 : : {"checkpoint", required_argument, NULL, 'c'},
2353 : : {"create-slot", no_argument, NULL, 'C'},
2354 : : {"max-rate", required_argument, NULL, 'r'},
2355 : : {"write-recovery-conf", no_argument, NULL, 'R'},
2356 : : {"slot", required_argument, NULL, 'S'},
2357 : : {"target", required_argument, NULL, 't'},
2358 : : {"tablespace-mapping", required_argument, NULL, 'T'},
2359 : : {"wal-method", required_argument, NULL, 'X'},
2360 : : {"gzip", no_argument, NULL, 'z'},
2361 : : {"compress", required_argument, NULL, 'Z'},
2362 : : {"label", required_argument, NULL, 'l'},
2363 : : {"no-clean", no_argument, NULL, 'n'},
2364 : : {"no-sync", no_argument, NULL, 'N'},
2365 : : {"dbname", required_argument, NULL, 'd'},
2366 : : {"host", required_argument, NULL, 'h'},
2367 : : {"port", required_argument, NULL, 'p'},
2368 : : {"username", required_argument, NULL, 'U'},
2369 : : {"no-password", no_argument, NULL, 'w'},
2370 : : {"password", no_argument, NULL, 'W'},
2371 : : {"status-interval", required_argument, NULL, 's'},
2372 : : {"verbose", no_argument, NULL, 'v'},
2373 : : {"progress", no_argument, NULL, 'P'},
2374 : : {"waldir", required_argument, NULL, 1},
2375 : : {"no-slot", no_argument, NULL, 2},
2376 : : {"no-verify-checksums", no_argument, NULL, 3},
2377 : : {"no-estimate-size", no_argument, NULL, 4},
2378 : : {"no-manifest", no_argument, NULL, 5},
2379 : : {"manifest-force-encode", no_argument, NULL, 6},
2380 : : {"manifest-checksums", required_argument, NULL, 7},
2381 : : {"sync-method", required_argument, NULL, 8},
2382 : : {NULL, 0, NULL, 0}
2383 : : };
2384 : : int c;
2385 : :
2386 : : int option_index;
2387 : 234 : char *compression_algorithm = "none";
2388 : 234 : char *compression_detail = NULL;
2389 : 234 : char *incremental_manifest = NULL;
2390 : 234 : CompressionLocation compressloc = COMPRESS_LOCATION_UNSPECIFIED;
2391 : : pg_compress_specification client_compress;
2392 : :
2393 : 234 : pg_logging_init(argv[0]);
2394 : 234 : progname = get_progname(argv[0]);
2395 : 234 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_basebackup"));
2396 : :
2397 [ + + ]: 234 : if (argc > 1)
2398 : : {
2399 [ + + - + ]: 233 : if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
2400 : : {
2401 : 1 : usage();
2402 : 1 : exit(0);
2403 : : }
2404 [ + - ]: 232 : else if (strcmp(argv[1], "-V") == 0
2405 [ + + ]: 232 : || strcmp(argv[1], "--version") == 0)
2406 : : {
2407 : 1 : puts("pg_basebackup (PostgreSQL) " PG_VERSION);
2408 : 1 : exit(0);
2409 : : }
2410 : : }
2411 : :
2412 : 232 : atexit(cleanup_directories_atexit);
2413 : :
2414 : 1237 : while ((c = getopt_long(argc, argv, "c:Cd:D:F:h:i:l:nNp:Pr:Rs:S:t:T:U:vwWX:zZ:",
2415 [ + + ]: 1237 : long_options, &option_index)) != -1)
2416 : : {
2417 [ + + + + : 1012 : switch (c)
+ + + - +
+ + - + +
- + + + +
- - - + +
+ + + + -
+ + + -
+ ]
2418 : : {
2419 : 208 : case 'c':
2420 [ + - ]: 208 : if (pg_strcasecmp(optarg, "fast") == 0)
2421 : 208 : fastcheckpoint = true;
2422 [ # # ]: 0 : else if (pg_strcasecmp(optarg, "spread") == 0)
2423 : 0 : fastcheckpoint = false;
2424 : : else
2425 : 0 : pg_fatal("invalid checkpoint argument \"%s\", must be \"fast\" or \"spread\"",
2426 : : optarg);
2427 : 208 : break;
2428 : 5 : case 'C':
2429 : 5 : create_slot = true;
2430 : 5 : break;
2431 : 3 : case 'd':
2432 : 3 : connection_string = pg_strdup(optarg);
2433 : 3 : break;
2434 : 213 : case 'D':
2435 : 213 : basedir = pg_strdup(optarg);
2436 : 213 : break;
2437 : 34 : case 'F':
2438 [ + - + + ]: 34 : if (strcmp(optarg, "p") == 0 || strcmp(optarg, "plain") == 0)
2439 : 12 : format = 'p';
2440 [ + + + - ]: 22 : else if (strcmp(optarg, "t") == 0 || strcmp(optarg, "tar") == 0)
2441 : 22 : format = 't';
2442 : : else
2443 : 0 : pg_fatal("invalid output format \"%s\", must be \"plain\" or \"tar\"",
2444 : : optarg);
2445 : 34 : break;
2446 : 85 : case 'h':
2447 : 85 : dbhost = pg_strdup(optarg);
2448 : 85 : break;
2449 : 14 : case 'i':
2450 : 14 : incremental_manifest = pg_strdup(optarg);
2451 : 14 : break;
2452 : 0 : case 'l':
2453 : 0 : label = pg_strdup(optarg);
2454 : 0 : break;
2455 : 1 : case 'n':
2456 : 1 : noclean = true;
2457 : 1 : break;
2458 : 208 : case 'N':
2459 : 208 : do_sync = false;
2460 : 208 : break;
2461 : 85 : case 'p':
2462 : 85 : dbport = pg_strdup(optarg);
2463 : 85 : break;
2464 : 0 : case 'P':
2465 : 0 : showprogress = true;
2466 : 0 : break;
2467 : 1 : case 'r':
2468 : 1 : maxrate = parse_max_rate(optarg);
2469 : 1 : break;
2470 : 4 : case 'R':
2471 : 4 : writerecoveryconf = true;
2472 : 4 : break;
2473 : 0 : case 's':
2474 [ # # ]: 0 : if (!option_parse_int(optarg, "-s/--status-interval", 0,
2475 : : INT_MAX / 1000,
2476 : : &standby_message_timeout))
2477 : 0 : exit(1);
2478 : 0 : standby_message_timeout *= 1000;
2479 : 0 : break;
2480 : 8 : case 'S':
2481 : :
2482 : : /*
2483 : : * When specifying replication slot name, use a permanent
2484 : : * slot.
2485 : : */
2486 : 8 : replication_slot = pg_strdup(optarg);
2487 : 8 : temp_replication_slot = false;
2488 : 8 : break;
2489 : 19 : case 't':
2490 : 19 : backup_target = pg_strdup(optarg);
2491 : 19 : break;
2492 : 22 : case 'T':
2493 : 22 : tablespace_list_append(optarg);
2494 : 16 : break;
2495 : 7 : case 'U':
2496 : 7 : dbuser = pg_strdup(optarg);
2497 : 7 : break;
2498 : 0 : case 'v':
2499 : 0 : verbose++;
2500 : 0 : break;
2501 : 0 : case 'w':
2502 : 0 : dbgetpassword = -1;
2503 : 0 : break;
2504 : 0 : case 'W':
2505 : 0 : dbgetpassword = 1;
2506 : 0 : break;
2507 : 41 : case 'X':
2508 [ + - ]: 41 : if (strcmp(optarg, "n") == 0 ||
2509 [ + + ]: 41 : strcmp(optarg, "none") == 0)
2510 : : {
2511 : 13 : includewal = NO_WAL;
2512 : : }
2513 [ + - ]: 28 : else if (strcmp(optarg, "f") == 0 ||
2514 [ + + ]: 28 : strcmp(optarg, "fetch") == 0)
2515 : : {
2516 : 19 : includewal = FETCH_WAL;
2517 : : }
2518 [ + - ]: 9 : else if (strcmp(optarg, "s") == 0 ||
2519 [ + - ]: 9 : strcmp(optarg, "stream") == 0)
2520 : : {
2521 : 9 : includewal = STREAM_WAL;
2522 : : }
2523 : : else
2524 : 0 : pg_fatal("invalid wal-method option \"%s\", must be \"fetch\", \"stream\", or \"none\"",
2525 : : optarg);
2526 : 41 : break;
2527 : 1 : case 'z':
2528 : 1 : compression_algorithm = "gzip";
2529 : 1 : compression_detail = NULL;
2530 : 1 : compressloc = COMPRESS_LOCATION_UNSPECIFIED;
2531 : 1 : break;
2532 : 32 : case 'Z':
2533 : 32 : backup_parse_compress_options(optarg, &compression_algorithm,
2534 : : &compression_detail, &compressloc);
2535 : 32 : break;
2536 : 1 : case 1:
2537 : 1 : xlog_dir = pg_strdup(optarg);
2538 : 1 : break;
2539 : 2 : case 2:
2540 : 2 : no_slot = true;
2541 : 2 : break;
2542 : 1 : case 3:
2543 : 1 : verify_checksums = false;
2544 : 1 : break;
2545 : 0 : case 4:
2546 : 0 : estimatesize = false;
2547 : 0 : break;
2548 : 1 : case 5:
2549 : 1 : manifest = false;
2550 : 1 : break;
2551 : 1 : case 6:
2552 : 1 : manifest_force_encode = true;
2553 : 1 : break;
2554 : 14 : case 7:
2555 : 14 : manifest_checksums = pg_strdup(optarg);
2556 : 14 : break;
2557 : 0 : case 8:
2558 [ # # ]: 0 : if (!parse_sync_method(optarg, &sync_method))
2559 : 0 : exit(1);
2560 : 0 : break;
2561 : 1 : default:
2562 : : /* getopt_long already emitted a complaint */
2563 : 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2564 : 1 : exit(1);
2565 : : }
2566 : : }
2567 : :
2568 : : /*
2569 : : * Any non-option arguments?
2570 : : */
2571 [ - + ]: 225 : if (optind < argc)
2572 : : {
2573 : 0 : pg_log_error("too many command-line arguments (first is \"%s\")",
2574 : : argv[optind]);
2575 : 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2576 : 0 : exit(1);
2577 : : }
2578 : :
2579 : : /*
2580 : : * Setting the backup target to 'client' is equivalent to leaving out the
2581 : : * option. This logic allows us to assume elsewhere that the backup is
2582 : : * being stored locally if and only if backup_target == NULL.
2583 : : */
2584 [ + + - + ]: 225 : if (backup_target != NULL && strcmp(backup_target, "client") == 0)
2585 : : {
2586 : 0 : pg_free(backup_target);
2587 : 0 : backup_target = NULL;
2588 : : }
2589 : :
2590 : : /*
2591 : : * Can't use --format with --target. Without --target, default format is
2592 : : * tar.
2593 : : */
2594 [ + + + + ]: 225 : if (backup_target != NULL && format != '\0')
2595 : : {
2596 : 1 : pg_log_error("cannot specify both format and backup target");
2597 : 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2598 : 1 : exit(1);
2599 : : }
2600 [ + + ]: 224 : if (format == '\0')
2601 : 197 : format = 'p';
2602 : :
2603 : : /*
2604 : : * Either directory or backup target should be specified, but not both
2605 : : */
2606 [ + + + + ]: 224 : if (basedir == NULL && backup_target == NULL)
2607 : : {
2608 : 1 : pg_log_error("must specify output directory or backup target");
2609 : 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2610 : 1 : exit(1);
2611 : : }
2612 [ + + + + ]: 223 : if (basedir != NULL && backup_target != NULL)
2613 : : {
2614 : 2 : pg_log_error("cannot specify both output directory and backup target");
2615 : 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2616 : 2 : exit(1);
2617 : : }
2618 : :
2619 : : /*
2620 : : * If the user has not specified where to perform backup compression,
2621 : : * default to the client, unless the user specified --target, in which
2622 : : * case the server is the only choice.
2623 : : */
2624 [ + + ]: 221 : if (compressloc == COMPRESS_LOCATION_UNSPECIFIED)
2625 : : {
2626 [ + + ]: 203 : if (backup_target == NULL)
2627 : 190 : compressloc = COMPRESS_LOCATION_CLIENT;
2628 : : else
2629 : 13 : compressloc = COMPRESS_LOCATION_SERVER;
2630 : : }
2631 : :
2632 : : /*
2633 : : * If any compression that we're doing is happening on the client side, we
2634 : : * must try to parse the compression algorithm and detail, but if it's all
2635 : : * on the server side, then we're just going to pass through whatever was
2636 : : * requested and let the server decide what to do.
2637 : : */
2638 [ + + ]: 221 : if (compressloc == COMPRESS_LOCATION_CLIENT)
2639 : : {
2640 : : pg_compress_algorithm alg;
2641 : : char *error_detail;
2642 : :
2643 [ + + ]: 193 : if (!parse_compress_algorithm(compression_algorithm, &alg))
2644 : 2 : pg_fatal("unrecognized compression algorithm: \"%s\"",
2645 : : compression_algorithm);
2646 : :
2647 : 191 : parse_compress_specification(alg, compression_detail, &client_compress);
2648 : 191 : error_detail = validate_compress_specification(&client_compress);
2649 [ + + ]: 191 : if (error_detail != NULL)
2650 : 10 : pg_fatal("invalid compression specification: %s",
2651 : : error_detail);
2652 : : }
2653 : : else
2654 : : {
2655 : : Assert(compressloc == COMPRESS_LOCATION_SERVER);
2656 : 28 : client_compress.algorithm = PG_COMPRESSION_NONE;
2657 : 28 : client_compress.options = 0;
2658 : : }
2659 : :
2660 : : /*
2661 : : * Can't perform client-side compression if the backup is not being sent
2662 : : * to the client.
2663 : : */
2664 [ + + - + ]: 209 : if (backup_target != NULL && compressloc == COMPRESS_LOCATION_CLIENT)
2665 : : {
2666 : 0 : pg_log_error("client-side compression is not possible when a backup target is specified");
2667 : 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2668 : 0 : exit(1);
2669 : : }
2670 : :
2671 : : /*
2672 : : * Client-side compression doesn't make sense unless tar format is in use.
2673 : : */
2674 [ + + + + ]: 209 : if (format == 'p' && compressloc == COMPRESS_LOCATION_CLIENT &&
2675 [ - + ]: 160 : client_compress.algorithm != PG_COMPRESSION_NONE)
2676 : : {
2677 : 0 : pg_log_error("only tar mode backups can be compressed");
2678 : 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2679 : 0 : exit(1);
2680 : : }
2681 : :
2682 : : /*
2683 : : * Sanity checks for WAL method.
2684 : : */
2685 [ + + + + ]: 209 : if (backup_target != NULL && includewal == STREAM_WAL)
2686 : : {
2687 : 2 : pg_log_error("WAL cannot be streamed when a backup target is specified");
2688 : 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2689 : 2 : exit(1);
2690 : : }
2691 [ + + + + : 207 : if (format == 't' && includewal == STREAM_WAL && strcmp(basedir, "-") == 0)
- + ]
2692 : : {
2693 : 0 : pg_log_error("cannot stream write-ahead logs in tar mode to stdout");
2694 : 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2695 : 0 : exit(1);
2696 : : }
2697 : :
2698 [ + + + + ]: 207 : if (replication_slot && includewal != STREAM_WAL)
2699 : : {
2700 : 1 : pg_log_error("replication slots can only be used with WAL streaming");
2701 : 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2702 : 1 : exit(1);
2703 : : }
2704 : :
2705 : : /*
2706 : : * Sanity checks for replication slot options.
2707 : : */
2708 [ + + ]: 206 : if (no_slot)
2709 : : {
2710 [ + + ]: 2 : if (replication_slot)
2711 : : {
2712 : 1 : pg_log_error("--no-slot cannot be used with slot name");
2713 : 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2714 : 1 : exit(1);
2715 : : }
2716 : 1 : temp_replication_slot = false;
2717 : : }
2718 : :
2719 [ + + ]: 205 : if (create_slot)
2720 : : {
2721 [ + + ]: 4 : if (!replication_slot)
2722 : : {
2723 : 1 : pg_log_error("%s needs a slot to be specified using --slot",
2724 : : "--create-slot");
2725 : 1 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2726 : 1 : exit(1);
2727 : : }
2728 : :
2729 [ - + ]: 3 : if (no_slot)
2730 : : {
2731 : 0 : pg_log_error("%s and %s are incompatible options",
2732 : : "--create-slot", "--no-slot");
2733 : 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2734 : 0 : exit(1);
2735 : : }
2736 : : }
2737 : :
2738 : : /*
2739 : : * Sanity checks on WAL directory.
2740 : : */
2741 [ + + ]: 204 : if (xlog_dir)
2742 : : {
2743 [ - + ]: 1 : if (backup_target != NULL)
2744 : : {
2745 : 0 : pg_log_error("WAL directory location cannot be specified along with a backup target");
2746 : 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2747 : 0 : exit(1);
2748 : : }
2749 [ - + ]: 1 : if (format != 'p')
2750 : : {
2751 : 0 : pg_log_error("WAL directory location can only be specified in plain mode");
2752 : 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2753 : 0 : exit(1);
2754 : : }
2755 : :
2756 : : /* clean up xlog directory name, check it's absolute */
2757 : 1 : canonicalize_path(xlog_dir);
2758 [ - + ]: 1 : if (!is_absolute_path(xlog_dir))
2759 : : {
2760 : 0 : pg_log_error("WAL directory location must be an absolute path");
2761 : 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2762 : 0 : exit(1);
2763 : : }
2764 : : }
2765 : :
2766 : : /*
2767 : : * Sanity checks for progress reporting options.
2768 : : */
2769 [ - + - - ]: 204 : if (showprogress && !estimatesize)
2770 : : {
2771 : 0 : pg_log_error("%s and %s are incompatible options",
2772 : : "--progress", "--no-estimate-size");
2773 : 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2774 : 0 : exit(1);
2775 : : }
2776 : :
2777 : : /*
2778 : : * Sanity checks for backup manifest options.
2779 : : */
2780 [ + + - + ]: 204 : if (!manifest && manifest_checksums != NULL)
2781 : : {
2782 : 0 : pg_log_error("%s and %s are incompatible options",
2783 : : "--no-manifest", "--manifest-checksums");
2784 : 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2785 : 0 : exit(1);
2786 : : }
2787 : :
2788 [ + + - + ]: 204 : if (!manifest && manifest_force_encode)
2789 : : {
2790 : 0 : pg_log_error("%s and %s are incompatible options",
2791 : : "--no-manifest", "--manifest-force-encode");
2792 : 0 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
2793 : 0 : exit(1);
2794 : : }
2795 : :
2796 : : /* connection in replication mode to server */
2797 : 204 : conn = GetConnection();
2798 [ + + ]: 204 : if (!conn)
2799 : : {
2800 : : /* Error message already written in GetConnection() */
2801 : 2 : exit(1);
2802 : : }
2803 : 202 : atexit(disconnect_atexit);
2804 : :
2805 : : #ifndef WIN32
2806 : :
2807 : : /*
2808 : : * Trap SIGCHLD to be able to handle the WAL stream process exiting. There
2809 : : * is no SIGCHLD on Windows, there we rely on the background thread
2810 : : * setting the signal variable on unexpected but graceful exit. If the WAL
2811 : : * stream thread crashes on Windows it will bring down the entire process
2812 : : * as it's a thread, so there is nothing to catch should that happen. A
2813 : : * crash on UNIX will be caught by the signal handler.
2814 : : */
2815 : 202 : pqsignal(SIGCHLD, sigchld_handler);
2816 : : #endif
2817 : :
2818 : : /*
2819 : : * Set umask so that directories/files are created with the same
2820 : : * permissions as directories/files in the source data directory.
2821 : : *
2822 : : * pg_mode_mask is set to owner-only by default and then updated in
2823 : : * GetConnection() where we get the mode from the server-side with
2824 : : * RetrieveDataDirCreatePerm() and then call SetDataDirectoryCreatePerm().
2825 : : */
2826 : 202 : umask(pg_mode_mask);
2827 : :
2828 : : /* Backup manifests are supported in 13 and newer versions */
2829 [ - + ]: 202 : if (PQserverVersion(conn) < MINIMUM_VERSION_FOR_MANIFESTS)
2830 : 0 : manifest = false;
2831 : :
2832 : : /*
2833 : : * If an output directory was specified, verify that it exists, or create
2834 : : * it. Note that for a tar backup, an output directory of "-" means we are
2835 : : * writing to stdout, so do nothing in that case.
2836 : : */
2837 [ + + + + : 202 : if (basedir != NULL && (format == 'p' || strcmp(basedir, "-") != 0))
+ - ]
2838 : 188 : verify_dir_is_empty_or_create(basedir, &made_new_pgdata, &found_existing_pgdata);
2839 : :
2840 : : /* determine remote server's xlog segment size */
2841 [ - + ]: 202 : if (!RetrieveWalSegSize(conn))
2842 : 0 : exit(1);
2843 : :
2844 : : /* Create pg_wal symlink, if required */
2845 [ + + ]: 202 : if (xlog_dir)
2846 : : {
2847 : : char *linkloc;
2848 : :
2849 : 1 : verify_dir_is_empty_or_create(xlog_dir, &made_new_xlogdir, &found_existing_xlogdir);
2850 : :
2851 : : /*
2852 : : * Form name of the place where the symlink must go. pg_xlog has been
2853 : : * renamed to pg_wal in post-10 clusters.
2854 : : */
2855 [ - + ]: 1 : linkloc = psprintf("%s/%s", basedir,
2856 : 1 : PQserverVersion(conn) < MINIMUM_VERSION_FOR_PG_WAL ?
2857 : : "pg_xlog" : "pg_wal");
2858 : :
2859 [ - + ]: 1 : if (symlink(xlog_dir, linkloc) != 0)
2860 : 0 : pg_fatal("could not create symbolic link \"%s\": %m", linkloc);
2861 : 1 : pfree(linkloc);
2862 : : }
2863 : :
2864 : 202 : BaseBackup(compression_algorithm, compression_detail, compressloc,
2865 : : &client_compress, incremental_manifest);
2866 : :
2867 : 175 : success = true;
2868 : 175 : return 0;
2869 : : }
|