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-2016, 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 <sys/stat.h>
19 #include <sys/types.h>
20 #include <sys/wait.h>
21 #include <signal.h>
22 #include <time.h>
23 #ifdef HAVE_SYS_SELECT_H
24 #include <sys/select.h>
25 #endif
26 #ifdef HAVE_LIBZ
27 #include <zlib.h>
28 #endif
29
30 #include "common/string.h"
31 #include "getopt_long.h"
32 #include "libpq-fe.h"
33 #include "pqexpbuffer.h"
34 #include "pgtar.h"
35 #include "pgtime.h"
36 #include "receivelog.h"
37 #include "replication/basebackup.h"
38 #include "streamutil.h"
39
40
41 #define atooid(x) ((Oid) strtoul((x), NULL, 10))
42
43 typedef struct TablespaceListCell
44 {
45 struct TablespaceListCell *next;
46 char old_dir[MAXPGPATH];
47 char new_dir[MAXPGPATH];
48 } TablespaceListCell;
49
50 typedef struct TablespaceList
51 {
52 TablespaceListCell *head;
53 TablespaceListCell *tail;
54 } TablespaceList;
55
56 /* Global options */
57 static char *basedir = NULL;
58 static TablespaceList tablespace_dirs = {NULL, NULL};
59 static char *xlog_dir = "";
60 static char format = 'p'; /* p(lain)/t(ar) */
61 static char *label = "pg_basebackup base backup";
62 static bool showprogress = false;
63 static int verbose = 0;
64 static int compresslevel = 0;
65 static bool includewal = false;
66 static bool streamwal = false;
67 static bool fastcheckpoint = false;
68 static bool writerecoveryconf = false;
69 static int standby_message_timeout = 10 * 1000; /* 10 sec = default */
70 static pg_time_t last_progress_report = 0;
71 static int32 maxrate = 0; /* no limit by default */
72
73
74 /* Progress counters */
75 static uint64 totalsize;
76 static uint64 totaldone;
77 static int tablespacecount;
78
79 /* Pipe to communicate with background wal receiver process */
80 #ifndef WIN32
81 static int bgpipe[2] = {-1, -1};
82 #endif
83
84 /* Handle to child process */
85 static pid_t bgchild = -1;
86
87 /* End position for xlog streaming, empty string if unknown yet */
88 static XLogRecPtr xlogendptr;
89
90 #ifndef WIN32
91 static int has_xlogendptr = 0;
92 #else
93 static volatile LONG has_xlogendptr = 0;
94 #endif
95
96 /* Contents of recovery.conf to be generated */
97 static PQExpBuffer recoveryconfcontents = NULL;
98
99 /* Function headers */
100 static void usage(void);
101 static void disconnect_and_exit(int code);
102 static void verify_dir_is_empty_or_create(char *dirname);
103 static void progress_report(int tablespacenum, const char *filename, bool force);
104
105 static void ReceiveTarFile(PGconn *conn, PGresult *res, int rownum);
106 static void ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum);
107 static void GenerateRecoveryConf(PGconn *conn);
108 static void WriteRecoveryConf(void);
109 static void BaseBackup(void);
110
111 static bool reached_end_position(XLogRecPtr segendpos, uint32 timeline,
112 bool segment_finished);
113
114 static const char *get_tablespace_mapping(const char *dir);
115 static void tablespace_list_append(const char *arg);
116
117
118 static void
disconnect_and_exit(int code)119 disconnect_and_exit(int code)
120 {
121 if (conn != NULL)
122 PQfinish(conn);
123
124 #ifndef WIN32
125
126 /*
127 * On windows, our background thread dies along with the process. But on
128 * Unix, if we have started a subprocess, we want to kill it off so it
129 * doesn't remain running trying to stream data.
130 */
131 if (bgchild > 0)
132 kill(bgchild, SIGTERM);
133 #endif
134
135 exit(code);
136 }
137
138
139 /*
140 * Split argument into old_dir and new_dir and append to tablespace mapping
141 * list.
142 */
143 static void
tablespace_list_append(const char * arg)144 tablespace_list_append(const char *arg)
145 {
146 TablespaceListCell *cell = (TablespaceListCell *) pg_malloc0(sizeof(TablespaceListCell));
147 char *dst;
148 char *dst_ptr;
149 const char *arg_ptr;
150
151 dst_ptr = dst = cell->old_dir;
152 for (arg_ptr = arg; *arg_ptr; arg_ptr++)
153 {
154 if (dst_ptr - dst >= MAXPGPATH)
155 {
156 fprintf(stderr, _("%s: directory name too long\n"), progname);
157 exit(1);
158 }
159
160 if (*arg_ptr == '\\' && *(arg_ptr + 1) == '=')
161 ; /* skip backslash escaping = */
162 else if (*arg_ptr == '=' && (arg_ptr == arg || *(arg_ptr - 1) != '\\'))
163 {
164 if (*cell->new_dir)
165 {
166 fprintf(stderr, _("%s: multiple \"=\" signs in tablespace mapping\n"), progname);
167 exit(1);
168 }
169 else
170 dst = dst_ptr = cell->new_dir;
171 }
172 else
173 *dst_ptr++ = *arg_ptr;
174 }
175
176 if (!*cell->old_dir || !*cell->new_dir)
177 {
178 fprintf(stderr,
179 _("%s: invalid tablespace mapping format \"%s\", must be \"OLDDIR=NEWDIR\"\n"),
180 progname, arg);
181 exit(1);
182 }
183
184 /*
185 * This check isn't absolutely necessary. But all tablespaces are created
186 * with absolute directories, so specifying a non-absolute path here would
187 * just never match, possibly confusing users. It's also good to be
188 * consistent with the new_dir check.
189 */
190 if (!is_absolute_path(cell->old_dir))
191 {
192 fprintf(stderr, _("%s: old directory is not an absolute path in tablespace mapping: %s\n"),
193 progname, cell->old_dir);
194 exit(1);
195 }
196
197 if (!is_absolute_path(cell->new_dir))
198 {
199 fprintf(stderr, _("%s: new directory is not an absolute path in tablespace mapping: %s\n"),
200 progname, cell->new_dir);
201 exit(1);
202 }
203
204 /*
205 * Comparisons done with these values should involve similarly
206 * canonicalized path values. This is particularly sensitive on Windows
207 * where path values may not necessarily use Unix slashes.
208 */
209 canonicalize_path(cell->old_dir);
210 canonicalize_path(cell->new_dir);
211
212 if (tablespace_dirs.tail)
213 tablespace_dirs.tail->next = cell;
214 else
215 tablespace_dirs.head = cell;
216 tablespace_dirs.tail = cell;
217 }
218
219
220 #ifdef HAVE_LIBZ
221 static const char *
get_gz_error(gzFile gzf)222 get_gz_error(gzFile gzf)
223 {
224 int errnum;
225 const char *errmsg;
226
227 errmsg = gzerror(gzf, &errnum);
228 if (errnum == Z_ERRNO)
229 return strerror(errno);
230 else
231 return errmsg;
232 }
233 #endif
234
235 static void
usage(void)236 usage(void)
237 {
238 printf(_("%s takes a base backup of a running PostgreSQL server.\n\n"),
239 progname);
240 printf(_("Usage:\n"));
241 printf(_(" %s [OPTION]...\n"), progname);
242 printf(_("\nOptions controlling the output:\n"));
243 printf(_(" -D, --pgdata=DIRECTORY receive base backup into directory\n"));
244 printf(_(" -F, --format=p|t output format (plain (default), tar)\n"));
245 printf(_(" -r, --max-rate=RATE maximum transfer rate to transfer data directory\n"
246 " (in kB/s, or use suffix \"k\" or \"M\")\n"));
247 printf(_(" -R, --write-recovery-conf\n"
248 " write recovery.conf for replication\n"));
249 printf(_(" -S, --slot=SLOTNAME replication slot to use\n"));
250 printf(_(" -T, --tablespace-mapping=OLDDIR=NEWDIR\n"
251 " relocate tablespace in OLDDIR to NEWDIR\n"));
252 printf(_(" -x, --xlog include required WAL files in backup (fetch mode)\n"));
253 printf(_(" -X, --xlog-method=fetch|stream\n"
254 " include required WAL files with specified method\n"));
255 printf(_(" --xlogdir=XLOGDIR location for the transaction log directory\n"));
256 printf(_(" -z, --gzip compress tar output\n"));
257 printf(_(" -Z, --compress=0-9 compress tar output with given compression level\n"));
258 printf(_("\nGeneral options:\n"));
259 printf(_(" -c, --checkpoint=fast|spread\n"
260 " set fast or spread checkpointing\n"));
261 printf(_(" -l, --label=LABEL set backup label\n"));
262 printf(_(" -P, --progress show progress information\n"));
263 printf(_(" -v, --verbose output verbose messages\n"));
264 printf(_(" -V, --version output version information, then exit\n"));
265 printf(_(" -?, --help show this help, then exit\n"));
266 printf(_("\nConnection options:\n"));
267 printf(_(" -d, --dbname=CONNSTR connection string\n"));
268 printf(_(" -h, --host=HOSTNAME database server host or socket directory\n"));
269 printf(_(" -p, --port=PORT database server port number\n"));
270 printf(_(" -s, --status-interval=INTERVAL\n"
271 " time between status packets sent to server (in seconds)\n"));
272 printf(_(" -U, --username=NAME connect as specified database user\n"));
273 printf(_(" -w, --no-password never prompt for password\n"));
274 printf(_(" -W, --password force password prompt (should happen automatically)\n"));
275 printf(_("\nReport bugs to <pgsql-bugs@postgresql.org>.\n"));
276 }
277
278
279 /*
280 * Called in the background process every time data is received.
281 * On Unix, we check to see if there is any data on our pipe
282 * (which would mean we have a stop position), and if it is, check if
283 * it is time to stop.
284 * On Windows, we are in a single process, so we can just check if it's
285 * time to stop.
286 */
287 static bool
reached_end_position(XLogRecPtr segendpos,uint32 timeline,bool segment_finished)288 reached_end_position(XLogRecPtr segendpos, uint32 timeline,
289 bool segment_finished)
290 {
291 if (!has_xlogendptr)
292 {
293 #ifndef WIN32
294 fd_set fds;
295 struct timeval tv;
296 int r;
297
298 /*
299 * Don't have the end pointer yet - check our pipe to see if it has
300 * been sent yet.
301 */
302 FD_ZERO(&fds);
303 FD_SET(bgpipe[0], &fds);
304
305 MemSet(&tv, 0, sizeof(tv));
306
307 r = select(bgpipe[0] + 1, &fds, NULL, NULL, &tv);
308 if (r == 1)
309 {
310 char xlogend[64];
311 uint32 hi,
312 lo;
313
314 MemSet(xlogend, 0, sizeof(xlogend));
315 r = read(bgpipe[0], xlogend, sizeof(xlogend) - 1);
316 if (r < 0)
317 {
318 fprintf(stderr, _("%s: could not read from ready pipe: %s\n"),
319 progname, strerror(errno));
320 exit(1);
321 }
322
323 if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2)
324 {
325 fprintf(stderr,
326 _("%s: could not parse transaction log location \"%s\"\n"),
327 progname, xlogend);
328 exit(1);
329 }
330 xlogendptr = ((uint64) hi) << 32 | lo;
331 has_xlogendptr = 1;
332
333 /*
334 * Fall through to check if we've reached the point further
335 * already.
336 */
337 }
338 else
339 {
340 /*
341 * No data received on the pipe means we don't know the end
342 * position yet - so just say it's not time to stop yet.
343 */
344 return false;
345 }
346 #else
347
348 /*
349 * On win32, has_xlogendptr is set by the main thread, so if it's not
350 * set here, we just go back and wait until it shows up.
351 */
352 return false;
353 #endif
354 }
355
356 /*
357 * At this point we have an end pointer, so compare it to the current
358 * position to figure out if it's time to stop.
359 */
360 if (segendpos >= xlogendptr)
361 return true;
362
363 /*
364 * Have end pointer, but haven't reached it yet - so tell the caller to
365 * keep streaming.
366 */
367 return false;
368 }
369
370 typedef struct
371 {
372 PGconn *bgconn;
373 XLogRecPtr startptr;
374 char xlogdir[MAXPGPATH];
375 char *sysidentifier;
376 int timeline;
377 } logstreamer_param;
378
379 static int
LogStreamerMain(logstreamer_param * param)380 LogStreamerMain(logstreamer_param *param)
381 {
382 StreamCtl stream;
383
384 MemSet(&stream, 0, sizeof(stream));
385 stream.startpos = param->startptr;
386 stream.timeline = param->timeline;
387 stream.sysidentifier = param->sysidentifier;
388 stream.stream_stop = reached_end_position;
389 stream.standby_message_timeout = standby_message_timeout;
390 stream.synchronous = false;
391 stream.mark_done = true;
392 stream.basedir = param->xlogdir;
393 stream.partial_suffix = NULL;
394
395 if (!ReceiveXlogStream(param->bgconn, &stream))
396
397 /*
398 * Any errors will already have been reported in the function process,
399 * but we need to tell the parent that we didn't shutdown in a nice
400 * way.
401 */
402 return 1;
403
404 PQfinish(param->bgconn);
405 return 0;
406 }
407
408 /*
409 * Initiate background process for receiving xlog during the backup.
410 * The background stream will use its own database connection so we can
411 * stream the logfile in parallel with the backups.
412 */
413 static void
StartLogStreamer(char * startpos,uint32 timeline,char * sysidentifier)414 StartLogStreamer(char *startpos, uint32 timeline, char *sysidentifier)
415 {
416 logstreamer_param *param;
417 uint32 hi,
418 lo;
419 char statusdir[MAXPGPATH];
420
421 param = pg_malloc0(sizeof(logstreamer_param));
422 param->timeline = timeline;
423 param->sysidentifier = sysidentifier;
424
425 /* Convert the starting position */
426 if (sscanf(startpos, "%X/%X", &hi, &lo) != 2)
427 {
428 fprintf(stderr,
429 _("%s: could not parse transaction log location \"%s\"\n"),
430 progname, startpos);
431 disconnect_and_exit(1);
432 }
433 param->startptr = ((uint64) hi) << 32 | lo;
434 /* Round off to even segment position */
435 param->startptr -= param->startptr % XLOG_SEG_SIZE;
436
437 #ifndef WIN32
438 /* Create our background pipe */
439 if (pipe(bgpipe) < 0)
440 {
441 fprintf(stderr,
442 _("%s: could not create pipe for background process: %s\n"),
443 progname, strerror(errno));
444 disconnect_and_exit(1);
445 }
446 #endif
447
448 /* Get a second connection */
449 param->bgconn = GetConnection();
450 if (!param->bgconn)
451 /* Error message already written in GetConnection() */
452 exit(1);
453
454 snprintf(param->xlogdir, sizeof(param->xlogdir), "%s/pg_xlog", basedir);
455
456 /*
457 * Create pg_xlog/archive_status (and thus pg_xlog) so we can write to
458 * basedir/pg_xlog as the directory entry in the tar file may arrive
459 * later.
460 */
461 snprintf(statusdir, sizeof(statusdir), "%s/pg_xlog/archive_status",
462 basedir);
463
464 if (pg_mkdir_p(statusdir, S_IRWXU) != 0 && errno != EEXIST)
465 {
466 fprintf(stderr,
467 _("%s: could not create directory \"%s\": %s\n"),
468 progname, statusdir, strerror(errno));
469 disconnect_and_exit(1);
470 }
471
472 /*
473 * Start a child process and tell it to start streaming. On Unix, this is
474 * a fork(). On Windows, we create a thread.
475 */
476 #ifndef WIN32
477 bgchild = fork();
478 if (bgchild == 0)
479 {
480 /* in child process */
481 exit(LogStreamerMain(param));
482 }
483 else if (bgchild < 0)
484 {
485 fprintf(stderr, _("%s: could not create background process: %s\n"),
486 progname, strerror(errno));
487 disconnect_and_exit(1);
488 }
489
490 /*
491 * Else we are in the parent process and all is well.
492 */
493 #else /* WIN32 */
494 bgchild = _beginthreadex(NULL, 0, (void *) LogStreamerMain, param, 0, NULL);
495 if (bgchild == 0)
496 {
497 fprintf(stderr, _("%s: could not create background thread: %s\n"),
498 progname, strerror(errno));
499 disconnect_and_exit(1);
500 }
501 #endif
502 }
503
504 /*
505 * Verify that the given directory exists and is empty. If it does not
506 * exist, it is created. If it exists but is not empty, an error will
507 * be give and the process ended.
508 */
509 static void
verify_dir_is_empty_or_create(char * dirname)510 verify_dir_is_empty_or_create(char *dirname)
511 {
512 switch (pg_check_dir(dirname))
513 {
514 case 0:
515
516 /*
517 * Does not exist, so create
518 */
519 if (pg_mkdir_p(dirname, S_IRWXU) == -1)
520 {
521 fprintf(stderr,
522 _("%s: could not create directory \"%s\": %s\n"),
523 progname, dirname, strerror(errno));
524 disconnect_and_exit(1);
525 }
526 return;
527 case 1:
528
529 /*
530 * Exists, empty
531 */
532 return;
533 case 2:
534 case 3:
535 case 4:
536
537 /*
538 * Exists, not empty
539 */
540 fprintf(stderr,
541 _("%s: directory \"%s\" exists but is not empty\n"),
542 progname, dirname);
543 disconnect_and_exit(1);
544 case -1:
545
546 /*
547 * Access problem
548 */
549 fprintf(stderr, _("%s: could not access directory \"%s\": %s\n"),
550 progname, dirname, strerror(errno));
551 disconnect_and_exit(1);
552 }
553 }
554
555
556 /*
557 * Print a progress report based on the global variables. If verbose output
558 * is enabled, also print the current file name.
559 *
560 * Progress report is written at maximum once per second, unless the
561 * force parameter is set to true.
562 */
563 static void
progress_report(int tablespacenum,const char * filename,bool force)564 progress_report(int tablespacenum, const char *filename, bool force)
565 {
566 int percent;
567 char totaldone_str[32];
568 char totalsize_str[32];
569 pg_time_t now;
570
571 if (!showprogress)
572 return;
573
574 now = time(NULL);
575 if (now == last_progress_report && !force)
576 return; /* Max once per second */
577
578 last_progress_report = now;
579 percent = totalsize ? (int) ((totaldone / 1024) * 100 / totalsize) : 0;
580
581 /*
582 * Avoid overflowing past 100% or the full size. This may make the total
583 * size number change as we approach the end of the backup (the estimate
584 * will always be wrong if WAL is included), but that's better than having
585 * the done column be bigger than the total.
586 */
587 if (percent > 100)
588 percent = 100;
589 if (totaldone / 1024 > totalsize)
590 totalsize = totaldone / 1024;
591
592 /*
593 * Separate step to keep platform-dependent format code out of
594 * translatable strings. And we only test for INT64_FORMAT availability
595 * in snprintf, not fprintf.
596 */
597 snprintf(totaldone_str, sizeof(totaldone_str), INT64_FORMAT,
598 totaldone / 1024);
599 snprintf(totalsize_str, sizeof(totalsize_str), INT64_FORMAT, totalsize);
600
601 #define VERBOSE_FILENAME_LENGTH 35
602 if (verbose)
603 {
604 if (!filename)
605
606 /*
607 * No filename given, so clear the status line (used for last
608 * call)
609 */
610 fprintf(stderr,
611 ngettext("%*s/%s kB (100%%), %d/%d tablespace %*s",
612 "%*s/%s kB (100%%), %d/%d tablespaces %*s",
613 tablespacecount),
614 (int) strlen(totalsize_str),
615 totaldone_str, totalsize_str,
616 tablespacenum, tablespacecount,
617 VERBOSE_FILENAME_LENGTH + 5, "");
618 else
619 {
620 bool truncate = (strlen(filename) > VERBOSE_FILENAME_LENGTH);
621
622 fprintf(stderr,
623 ngettext("%*s/%s kB (%d%%), %d/%d tablespace (%s%-*.*s)",
624 "%*s/%s kB (%d%%), %d/%d tablespaces (%s%-*.*s)",
625 tablespacecount),
626 (int) strlen(totalsize_str),
627 totaldone_str, totalsize_str, percent,
628 tablespacenum, tablespacecount,
629 /* Prefix with "..." if we do leading truncation */
630 truncate ? "..." : "",
631 truncate ? VERBOSE_FILENAME_LENGTH - 3 : VERBOSE_FILENAME_LENGTH,
632 truncate ? VERBOSE_FILENAME_LENGTH - 3 : VERBOSE_FILENAME_LENGTH,
633 /* Truncate filename at beginning if it's too long */
634 truncate ? filename + strlen(filename) - VERBOSE_FILENAME_LENGTH + 3 : filename);
635 }
636 }
637 else
638 fprintf(stderr,
639 ngettext("%*s/%s kB (%d%%), %d/%d tablespace",
640 "%*s/%s kB (%d%%), %d/%d tablespaces",
641 tablespacecount),
642 (int) strlen(totalsize_str),
643 totaldone_str, totalsize_str, percent,
644 tablespacenum, tablespacecount);
645
646 fprintf(stderr, "\r");
647 }
648
649 static int32
parse_max_rate(char * src)650 parse_max_rate(char *src)
651 {
652 double result;
653 char *after_num;
654 char *suffix = NULL;
655
656 errno = 0;
657 result = strtod(src, &after_num);
658 if (src == after_num)
659 {
660 fprintf(stderr,
661 _("%s: transfer rate \"%s\" is not a valid value\n"),
662 progname, src);
663 exit(1);
664 }
665 if (errno != 0)
666 {
667 fprintf(stderr,
668 _("%s: invalid transfer rate \"%s\": %s\n"),
669 progname, src, strerror(errno));
670 exit(1);
671 }
672
673 if (result <= 0)
674 {
675 /*
676 * Reject obviously wrong values here.
677 */
678 fprintf(stderr, _("%s: transfer rate must be greater than zero\n"),
679 progname);
680 exit(1);
681 }
682
683 /*
684 * Evaluate suffix, after skipping over possible whitespace. Lack of
685 * suffix means kilobytes.
686 */
687 while (*after_num != '\0' && isspace((unsigned char) *after_num))
688 after_num++;
689
690 if (*after_num != '\0')
691 {
692 suffix = after_num;
693 if (*after_num == 'k')
694 {
695 /* kilobyte is the expected unit. */
696 after_num++;
697 }
698 else if (*after_num == 'M')
699 {
700 after_num++;
701 result *= 1024.0;
702 }
703 }
704
705 /* The rest can only consist of white space. */
706 while (*after_num != '\0' && isspace((unsigned char) *after_num))
707 after_num++;
708
709 if (*after_num != '\0')
710 {
711 fprintf(stderr,
712 _("%s: invalid --max-rate unit: \"%s\"\n"),
713 progname, suffix);
714 exit(1);
715 }
716
717 /* Valid integer? */
718 if ((uint64) result != (uint64) ((uint32) result))
719 {
720 fprintf(stderr,
721 _("%s: transfer rate \"%s\" exceeds integer range\n"),
722 progname, src);
723 exit(1);
724 }
725
726 /*
727 * The range is checked on the server side too, but avoid the server
728 * connection if a nonsensical value was passed.
729 */
730 if (result < MAX_RATE_LOWER || result > MAX_RATE_UPPER)
731 {
732 fprintf(stderr,
733 _("%s: transfer rate \"%s\" is out of range\n"),
734 progname, src);
735 exit(1);
736 }
737
738 return (int32) result;
739 }
740
741 /*
742 * Write a piece of tar data
743 */
744 static void
writeTarData(gzFile ztarfile,FILE * tarfile,char * buf,int r,char * current_file)745 writeTarData(
746 #ifdef HAVE_LIBZ
747 gzFile ztarfile,
748 #endif
749 FILE *tarfile, char *buf, int r, char *current_file)
750 {
751 #ifdef HAVE_LIBZ
752 if (ztarfile != NULL)
753 {
754 errno = 0;
755 if (gzwrite(ztarfile, buf, r) != r)
756 {
757 /* if write didn't set errno, assume problem is no disk space */
758 if (errno == 0)
759 errno = ENOSPC;
760 fprintf(stderr,
761 _("%s: could not write to compressed file \"%s\": %s\n"),
762 progname, current_file, get_gz_error(ztarfile));
763 disconnect_and_exit(1);
764 }
765 }
766 else
767 #endif
768 {
769 errno = 0;
770 if (fwrite(buf, r, 1, tarfile) != 1)
771 {
772 /* if write didn't set errno, assume problem is no disk space */
773 if (errno == 0)
774 errno = ENOSPC;
775 fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"),
776 progname, current_file, strerror(errno));
777 disconnect_and_exit(1);
778 }
779 }
780 }
781
782 #ifdef HAVE_LIBZ
783 #define WRITE_TAR_DATA(buf, sz) writeTarData(ztarfile, tarfile, buf, sz, filename)
784 #else
785 #define WRITE_TAR_DATA(buf, sz) writeTarData(tarfile, buf, sz, filename)
786 #endif
787
788 /*
789 * Receive a tar format file from the connection to the server, and write
790 * the data from this file directly into a tar file. If compression is
791 * enabled, the data will be compressed while written to the file.
792 *
793 * The file will be named base.tar[.gz] if it's for the main data directory
794 * or <tablespaceoid>.tar[.gz] if it's for another tablespace.
795 *
796 * No attempt to inspect or validate the contents of the file is done.
797 */
798 static void
ReceiveTarFile(PGconn * conn,PGresult * res,int rownum)799 ReceiveTarFile(PGconn *conn, PGresult *res, int rownum)
800 {
801 char filename[MAXPGPATH];
802 char *copybuf = NULL;
803 FILE *tarfile = NULL;
804 char tarhdr[512];
805 bool basetablespace = PQgetisnull(res, rownum, 0);
806 bool in_tarhdr = true;
807 bool skip_file = false;
808 size_t tarhdrsz = 0;
809 pgoff_t filesz = 0;
810
811 #ifdef HAVE_LIBZ
812 gzFile ztarfile = NULL;
813 #endif
814
815 if (basetablespace)
816 {
817 /*
818 * Base tablespaces
819 */
820 if (strcmp(basedir, "-") == 0)
821 {
822 #ifdef WIN32
823 _setmode(fileno(stdout), _O_BINARY);
824 #endif
825
826 #ifdef HAVE_LIBZ
827 if (compresslevel != 0)
828 {
829 ztarfile = gzdopen(dup(fileno(stdout)), "wb");
830 if (gzsetparams(ztarfile, compresslevel,
831 Z_DEFAULT_STRATEGY) != Z_OK)
832 {
833 fprintf(stderr,
834 _("%s: could not set compression level %d: %s\n"),
835 progname, compresslevel, get_gz_error(ztarfile));
836 disconnect_and_exit(1);
837 }
838 }
839 else
840 #endif
841 tarfile = stdout;
842 strcpy(filename, "-");
843 }
844 else
845 {
846 #ifdef HAVE_LIBZ
847 if (compresslevel != 0)
848 {
849 snprintf(filename, sizeof(filename), "%s/base.tar.gz", basedir);
850 ztarfile = gzopen(filename, "wb");
851 if (gzsetparams(ztarfile, compresslevel,
852 Z_DEFAULT_STRATEGY) != Z_OK)
853 {
854 fprintf(stderr,
855 _("%s: could not set compression level %d: %s\n"),
856 progname, compresslevel, get_gz_error(ztarfile));
857 disconnect_and_exit(1);
858 }
859 }
860 else
861 #endif
862 {
863 snprintf(filename, sizeof(filename), "%s/base.tar", basedir);
864 tarfile = fopen(filename, "wb");
865 }
866 }
867 }
868 else
869 {
870 /*
871 * Specific tablespace
872 */
873 #ifdef HAVE_LIBZ
874 if (compresslevel != 0)
875 {
876 snprintf(filename, sizeof(filename), "%s/%s.tar.gz", basedir,
877 PQgetvalue(res, rownum, 0));
878 ztarfile = gzopen(filename, "wb");
879 if (gzsetparams(ztarfile, compresslevel,
880 Z_DEFAULT_STRATEGY) != Z_OK)
881 {
882 fprintf(stderr,
883 _("%s: could not set compression level %d: %s\n"),
884 progname, compresslevel, get_gz_error(ztarfile));
885 disconnect_and_exit(1);
886 }
887 }
888 else
889 #endif
890 {
891 snprintf(filename, sizeof(filename), "%s/%s.tar", basedir,
892 PQgetvalue(res, rownum, 0));
893 tarfile = fopen(filename, "wb");
894 }
895 }
896
897 #ifdef HAVE_LIBZ
898 if (compresslevel != 0)
899 {
900 if (!ztarfile)
901 {
902 /* Compression is in use */
903 fprintf(stderr,
904 _("%s: could not create compressed file \"%s\": %s\n"),
905 progname, filename, get_gz_error(ztarfile));
906 disconnect_and_exit(1);
907 }
908 }
909 else
910 #endif
911 {
912 /* Either no zlib support, or zlib support but compresslevel = 0 */
913 if (!tarfile)
914 {
915 fprintf(stderr, _("%s: could not create file \"%s\": %s\n"),
916 progname, filename, strerror(errno));
917 disconnect_and_exit(1);
918 }
919 }
920
921 /*
922 * Get the COPY data stream
923 */
924 res = PQgetResult(conn);
925 if (PQresultStatus(res) != PGRES_COPY_OUT)
926 {
927 fprintf(stderr, _("%s: could not get COPY data stream: %s"),
928 progname, PQerrorMessage(conn));
929 disconnect_and_exit(1);
930 }
931
932 while (1)
933 {
934 int r;
935
936 if (copybuf != NULL)
937 {
938 PQfreemem(copybuf);
939 copybuf = NULL;
940 }
941
942 r = PQgetCopyData(conn, ©buf, 0);
943 if (r == -1)
944 {
945 /*
946 * End of chunk. If requested, and this is the base tablespace,
947 * write recovery.conf into the tarfile. When done, close the file
948 * (but not stdout).
949 *
950 * Also, write two completely empty blocks at the end of the tar
951 * file, as required by some tar programs.
952 */
953 char zerobuf[1024];
954
955 MemSet(zerobuf, 0, sizeof(zerobuf));
956
957 if (basetablespace && writerecoveryconf)
958 {
959 char header[512];
960 int padding;
961
962 tarCreateHeader(header, "recovery.conf", NULL,
963 recoveryconfcontents->len,
964 0600, 04000, 02000,
965 time(NULL));
966
967 padding = ((recoveryconfcontents->len + 511) & ~511) - recoveryconfcontents->len;
968
969 WRITE_TAR_DATA(header, sizeof(header));
970 WRITE_TAR_DATA(recoveryconfcontents->data, recoveryconfcontents->len);
971 if (padding)
972 WRITE_TAR_DATA(zerobuf, padding);
973 }
974
975 /* 2 * 512 bytes empty data at end of file */
976 WRITE_TAR_DATA(zerobuf, sizeof(zerobuf));
977
978 #ifdef HAVE_LIBZ
979 if (ztarfile != NULL)
980 {
981 if (gzclose(ztarfile) != 0)
982 {
983 fprintf(stderr,
984 _("%s: could not close compressed file \"%s\": %s\n"),
985 progname, filename, get_gz_error(ztarfile));
986 disconnect_and_exit(1);
987 }
988 }
989 else
990 #endif
991 {
992 if (strcmp(basedir, "-") != 0)
993 {
994 if (fclose(tarfile) != 0)
995 {
996 fprintf(stderr,
997 _("%s: could not close file \"%s\": %s\n"),
998 progname, filename, strerror(errno));
999 disconnect_and_exit(1);
1000 }
1001 }
1002 }
1003
1004 break;
1005 }
1006 else if (r == -2)
1007 {
1008 fprintf(stderr, _("%s: could not read COPY data: %s"),
1009 progname, PQerrorMessage(conn));
1010 disconnect_and_exit(1);
1011 }
1012
1013 if (!writerecoveryconf || !basetablespace)
1014 {
1015 /*
1016 * When not writing recovery.conf, or when not working on the base
1017 * tablespace, we never have to look for an existing recovery.conf
1018 * file in the stream.
1019 */
1020 WRITE_TAR_DATA(copybuf, r);
1021 }
1022 else
1023 {
1024 /*
1025 * Look for a recovery.conf in the existing tar stream. If it's
1026 * there, we must skip it so we can later overwrite it with our
1027 * own version of the file.
1028 *
1029 * To do this, we have to process the individual files inside the
1030 * TAR stream. The stream consists of a header and zero or more
1031 * chunks, all 512 bytes long. The stream from the server is
1032 * broken up into smaller pieces, so we have to track the size of
1033 * the files to find the next header structure.
1034 */
1035 int rr = r;
1036 int pos = 0;
1037
1038 while (rr > 0)
1039 {
1040 if (in_tarhdr)
1041 {
1042 /*
1043 * We're currently reading a header structure inside the
1044 * TAR stream, i.e. the file metadata.
1045 */
1046 if (tarhdrsz < 512)
1047 {
1048 /*
1049 * Copy the header structure into tarhdr in case the
1050 * header is not aligned to 512 bytes or it's not
1051 * returned in whole by the last PQgetCopyData call.
1052 */
1053 int hdrleft;
1054 int bytes2copy;
1055
1056 hdrleft = 512 - tarhdrsz;
1057 bytes2copy = (rr > hdrleft ? hdrleft : rr);
1058
1059 memcpy(&tarhdr[tarhdrsz], copybuf + pos, bytes2copy);
1060
1061 rr -= bytes2copy;
1062 pos += bytes2copy;
1063 tarhdrsz += bytes2copy;
1064 }
1065 else
1066 {
1067 /*
1068 * We have the complete header structure in tarhdr,
1069 * look at the file metadata: - the subsequent file
1070 * contents have to be skipped if the filename is
1071 * recovery.conf - find out the size of the file
1072 * padded to the next multiple of 512
1073 */
1074 int padding;
1075
1076 skip_file = (strcmp(&tarhdr[0], "recovery.conf") == 0);
1077
1078 filesz = read_tar_number(&tarhdr[124], 12);
1079
1080 padding = ((filesz + 511) & ~511) - filesz;
1081 filesz += padding;
1082
1083 /* Next part is the file, not the header */
1084 in_tarhdr = false;
1085
1086 /*
1087 * If we're not skipping the file, write the tar
1088 * header unmodified.
1089 */
1090 if (!skip_file)
1091 WRITE_TAR_DATA(tarhdr, 512);
1092 }
1093 }
1094 else
1095 {
1096 /*
1097 * We're processing a file's contents.
1098 */
1099 if (filesz > 0)
1100 {
1101 /*
1102 * We still have data to read (and possibly write).
1103 */
1104 int bytes2write;
1105
1106 bytes2write = (filesz > rr ? rr : filesz);
1107
1108 if (!skip_file)
1109 WRITE_TAR_DATA(copybuf + pos, bytes2write);
1110
1111 rr -= bytes2write;
1112 pos += bytes2write;
1113 filesz -= bytes2write;
1114 }
1115 else
1116 {
1117 /*
1118 * No more data in the current file, the next piece of
1119 * data (if any) will be a new file header structure.
1120 */
1121 in_tarhdr = true;
1122 skip_file = false;
1123 tarhdrsz = 0;
1124 filesz = 0;
1125 }
1126 }
1127 }
1128 }
1129 totaldone += r;
1130 progress_report(rownum, filename, false);
1131 } /* while (1) */
1132 progress_report(rownum, filename, true);
1133
1134 if (copybuf != NULL)
1135 PQfreemem(copybuf);
1136 }
1137
1138
1139 /*
1140 * Retrieve tablespace path, either relocated or original depending on whether
1141 * -T was passed or not.
1142 */
1143 static const char *
get_tablespace_mapping(const char * dir)1144 get_tablespace_mapping(const char *dir)
1145 {
1146 TablespaceListCell *cell;
1147 char canon_dir[MAXPGPATH];
1148
1149 /* Canonicalize path for comparison consistency */
1150 strlcpy(canon_dir, dir, sizeof(canon_dir));
1151 canonicalize_path(canon_dir);
1152
1153 for (cell = tablespace_dirs.head; cell; cell = cell->next)
1154 if (strcmp(canon_dir, cell->old_dir) == 0)
1155 return cell->new_dir;
1156
1157 return dir;
1158 }
1159
1160
1161 /*
1162 * Receive a tar format stream from the connection to the server, and unpack
1163 * the contents of it into a directory. Only files, directories and
1164 * symlinks are supported, no other kinds of special files.
1165 *
1166 * If the data is for the main data directory, it will be restored in the
1167 * specified directory. If it's for another tablespace, it will be restored
1168 * in the original or mapped directory.
1169 */
1170 static void
ReceiveAndUnpackTarFile(PGconn * conn,PGresult * res,int rownum)1171 ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum)
1172 {
1173 char current_path[MAXPGPATH];
1174 char filename[MAXPGPATH];
1175 const char *mapped_tblspc_path;
1176 pgoff_t current_len_left = 0;
1177 int current_padding = 0;
1178 bool basetablespace;
1179 char *copybuf = NULL;
1180 FILE *file = NULL;
1181
1182 basetablespace = PQgetisnull(res, rownum, 0);
1183 if (basetablespace)
1184 strlcpy(current_path, basedir, sizeof(current_path));
1185 else
1186 strlcpy(current_path,
1187 get_tablespace_mapping(PQgetvalue(res, rownum, 1)),
1188 sizeof(current_path));
1189
1190 /*
1191 * Get the COPY data
1192 */
1193 res = PQgetResult(conn);
1194 if (PQresultStatus(res) != PGRES_COPY_OUT)
1195 {
1196 fprintf(stderr, _("%s: could not get COPY data stream: %s"),
1197 progname, PQerrorMessage(conn));
1198 disconnect_and_exit(1);
1199 }
1200
1201 while (1)
1202 {
1203 int r;
1204
1205 if (copybuf != NULL)
1206 {
1207 PQfreemem(copybuf);
1208 copybuf = NULL;
1209 }
1210
1211 r = PQgetCopyData(conn, ©buf, 0);
1212
1213 if (r == -1)
1214 {
1215 /*
1216 * End of chunk
1217 */
1218 if (file)
1219 fclose(file);
1220
1221 break;
1222 }
1223 else if (r == -2)
1224 {
1225 fprintf(stderr, _("%s: could not read COPY data: %s"),
1226 progname, PQerrorMessage(conn));
1227 disconnect_and_exit(1);
1228 }
1229
1230 if (file == NULL)
1231 {
1232 int filemode;
1233
1234 /*
1235 * No current file, so this must be the header for a new file
1236 */
1237 if (r != 512)
1238 {
1239 fprintf(stderr, _("%s: invalid tar block header size: %d\n"),
1240 progname, r);
1241 disconnect_and_exit(1);
1242 }
1243 totaldone += 512;
1244
1245 current_len_left = read_tar_number(©buf[124], 12);
1246
1247 /* Set permissions on the file */
1248 filemode = read_tar_number(©buf[100], 8);
1249
1250 /*
1251 * All files are padded up to 512 bytes
1252 */
1253 current_padding =
1254 ((current_len_left + 511) & ~511) - current_len_left;
1255
1256 /*
1257 * First part of header is zero terminated filename
1258 */
1259 snprintf(filename, sizeof(filename), "%s/%s", current_path,
1260 copybuf);
1261 if (filename[strlen(filename) - 1] == '/')
1262 {
1263 /*
1264 * Ends in a slash means directory or symlink to directory
1265 */
1266 if (copybuf[156] == '5')
1267 {
1268 /*
1269 * Directory
1270 */
1271 filename[strlen(filename) - 1] = '\0'; /* Remove trailing slash */
1272 if (mkdir(filename, S_IRWXU) != 0)
1273 {
1274 /*
1275 * When streaming WAL, pg_xlog will have been created
1276 * by the wal receiver process. Also, when transaction
1277 * log directory location was specified, pg_xlog has
1278 * already been created as a symbolic link before
1279 * starting the actual backup. So just ignore creation
1280 * failures on related directories.
1281 */
1282 if (!((pg_str_endswith(filename, "/pg_xlog") ||
1283 pg_str_endswith(filename, "/archive_status")) &&
1284 errno == EEXIST))
1285 {
1286 fprintf(stderr,
1287 _("%s: could not create directory \"%s\": %s\n"),
1288 progname, filename, strerror(errno));
1289 disconnect_and_exit(1);
1290 }
1291 }
1292 #ifndef WIN32
1293 if (chmod(filename, (mode_t) filemode))
1294 fprintf(stderr,
1295 _("%s: could not set permissions on directory \"%s\": %s\n"),
1296 progname, filename, strerror(errno));
1297 #endif
1298 }
1299 else if (copybuf[156] == '2')
1300 {
1301 /*
1302 * Symbolic link
1303 *
1304 * It's most likely a link in pg_tblspc directory, to the
1305 * location of a tablespace. Apply any tablespace mapping
1306 * given on the command line (--tablespace-mapping). (We
1307 * blindly apply the mapping without checking that the
1308 * link really is inside pg_tblspc. We don't expect there
1309 * to be other symlinks in a data directory, but if there
1310 * are, you can call it an undocumented feature that you
1311 * can map them too.)
1312 */
1313 filename[strlen(filename) - 1] = '\0'; /* Remove trailing slash */
1314
1315 mapped_tblspc_path = get_tablespace_mapping(©buf[157]);
1316 if (symlink(mapped_tblspc_path, filename) != 0)
1317 {
1318 fprintf(stderr,
1319 _("%s: could not create symbolic link from \"%s\" to \"%s\": %s\n"),
1320 progname, filename, mapped_tblspc_path,
1321 strerror(errno));
1322 disconnect_and_exit(1);
1323 }
1324 }
1325 else
1326 {
1327 fprintf(stderr,
1328 _("%s: unrecognized link indicator \"%c\"\n"),
1329 progname, copybuf[156]);
1330 disconnect_and_exit(1);
1331 }
1332 continue; /* directory or link handled */
1333 }
1334
1335 /*
1336 * regular file
1337 */
1338 file = fopen(filename, "wb");
1339 if (!file)
1340 {
1341 fprintf(stderr, _("%s: could not create file \"%s\": %s\n"),
1342 progname, filename, strerror(errno));
1343 disconnect_and_exit(1);
1344 }
1345
1346 #ifndef WIN32
1347 if (chmod(filename, (mode_t) filemode))
1348 fprintf(stderr, _("%s: could not set permissions on file \"%s\": %s\n"),
1349 progname, filename, strerror(errno));
1350 #endif
1351
1352 if (current_len_left == 0)
1353 {
1354 /*
1355 * Done with this file, next one will be a new tar header
1356 */
1357 fclose(file);
1358 file = NULL;
1359 continue;
1360 }
1361 } /* new file */
1362 else
1363 {
1364 /*
1365 * Continuing blocks in existing file
1366 */
1367 if (current_len_left == 0 && r == current_padding)
1368 {
1369 /*
1370 * Received the padding block for this file, ignore it and
1371 * close the file, then move on to the next tar header.
1372 */
1373 fclose(file);
1374 file = NULL;
1375 totaldone += r;
1376 continue;
1377 }
1378
1379 errno = 0;
1380 if (fwrite(copybuf, r, 1, file) != 1)
1381 {
1382 /* if write didn't set errno, assume problem is no disk space */
1383 if (errno == 0)
1384 errno = ENOSPC;
1385 fprintf(stderr, _("%s: could not write to file \"%s\": %s\n"),
1386 progname, filename, strerror(errno));
1387 disconnect_and_exit(1);
1388 }
1389 totaldone += r;
1390 progress_report(rownum, filename, false);
1391
1392 current_len_left -= r;
1393 if (current_len_left == 0 && current_padding == 0)
1394 {
1395 /*
1396 * Received the last block, and there is no padding to be
1397 * expected. Close the file and move on to the next tar
1398 * header.
1399 */
1400 fclose(file);
1401 file = NULL;
1402 continue;
1403 }
1404 } /* continuing data in existing file */
1405 } /* loop over all data blocks */
1406 progress_report(rownum, filename, true);
1407
1408 if (file != NULL)
1409 {
1410 fprintf(stderr,
1411 _("%s: COPY stream ended before last file was finished\n"),
1412 progname);
1413 disconnect_and_exit(1);
1414 }
1415
1416 if (copybuf != NULL)
1417 PQfreemem(copybuf);
1418
1419 if (basetablespace && writerecoveryconf)
1420 WriteRecoveryConf();
1421 }
1422
1423 /*
1424 * Escape a parameter value so that it can be used as part of a libpq
1425 * connection string, e.g. in:
1426 *
1427 * application_name=<value>
1428 *
1429 * The returned string is malloc'd. Return NULL on out-of-memory.
1430 */
1431 static char *
escapeConnectionParameter(const char * src)1432 escapeConnectionParameter(const char *src)
1433 {
1434 bool need_quotes = false;
1435 bool need_escaping = false;
1436 const char *p;
1437 char *dstbuf;
1438 char *dst;
1439
1440 /*
1441 * First check if quoting is needed. Any quote (') or backslash (\)
1442 * characters need to be escaped. Parameters are separated by whitespace,
1443 * so any string containing whitespace characters need to be quoted. An
1444 * empty string is represented by ''.
1445 */
1446 if (strchr(src, '\'') != NULL || strchr(src, '\\') != NULL)
1447 need_escaping = true;
1448
1449 for (p = src; *p; p++)
1450 {
1451 if (isspace((unsigned char) *p))
1452 {
1453 need_quotes = true;
1454 break;
1455 }
1456 }
1457
1458 if (*src == '\0')
1459 return pg_strdup("''");
1460
1461 if (!need_quotes && !need_escaping)
1462 return pg_strdup(src); /* no quoting or escaping needed */
1463
1464 /*
1465 * Allocate a buffer large enough for the worst case that all the source
1466 * characters need to be escaped, plus quotes.
1467 */
1468 dstbuf = pg_malloc(strlen(src) * 2 + 2 + 1);
1469
1470 dst = dstbuf;
1471 if (need_quotes)
1472 *(dst++) = '\'';
1473 for (; *src; src++)
1474 {
1475 if (*src == '\'' || *src == '\\')
1476 *(dst++) = '\\';
1477 *(dst++) = *src;
1478 }
1479 if (need_quotes)
1480 *(dst++) = '\'';
1481 *dst = '\0';
1482
1483 return dstbuf;
1484 }
1485
1486 /*
1487 * Escape a string so that it can be used as a value in a key-value pair
1488 * a configuration file.
1489 */
1490 static char *
escape_quotes(const char * src)1491 escape_quotes(const char *src)
1492 {
1493 char *result = escape_single_quotes_ascii(src);
1494
1495 if (!result)
1496 {
1497 fprintf(stderr, _("%s: out of memory\n"), progname);
1498 exit(1);
1499 }
1500 return result;
1501 }
1502
1503 /*
1504 * Create a recovery.conf file in memory using a PQExpBuffer
1505 */
1506 static void
GenerateRecoveryConf(PGconn * conn)1507 GenerateRecoveryConf(PGconn *conn)
1508 {
1509 PQconninfoOption *connOptions;
1510 PQconninfoOption *option;
1511 PQExpBufferData conninfo_buf;
1512 char *escaped;
1513
1514 recoveryconfcontents = createPQExpBuffer();
1515 if (!recoveryconfcontents)
1516 {
1517 fprintf(stderr, _("%s: out of memory\n"), progname);
1518 disconnect_and_exit(1);
1519 }
1520
1521 connOptions = PQconninfo(conn);
1522 if (connOptions == NULL)
1523 {
1524 fprintf(stderr, _("%s: out of memory\n"), progname);
1525 disconnect_and_exit(1);
1526 }
1527
1528 appendPQExpBufferStr(recoveryconfcontents, "standby_mode = 'on'\n");
1529
1530 initPQExpBuffer(&conninfo_buf);
1531 for (option = connOptions; option && option->keyword; option++)
1532 {
1533 /*
1534 * Do not emit this setting if: - the setting is "replication",
1535 * "dbname" or "fallback_application_name", since these would be
1536 * overridden by the libpqwalreceiver module anyway. - not set or
1537 * empty.
1538 */
1539 if (strcmp(option->keyword, "replication") == 0 ||
1540 strcmp(option->keyword, "dbname") == 0 ||
1541 strcmp(option->keyword, "fallback_application_name") == 0 ||
1542 (option->val == NULL) ||
1543 (option->val != NULL && option->val[0] == '\0'))
1544 continue;
1545
1546 /* Separate key-value pairs with spaces */
1547 if (conninfo_buf.len != 0)
1548 appendPQExpBufferChar(&conninfo_buf, ' ');
1549
1550 /*
1551 * Write "keyword=value" pieces, the value string is escaped and/or
1552 * quoted if necessary.
1553 */
1554 escaped = escapeConnectionParameter(option->val);
1555 appendPQExpBuffer(&conninfo_buf, "%s=%s", option->keyword, escaped);
1556 free(escaped);
1557 }
1558
1559 /*
1560 * Escape the connection string, so that it can be put in the config file.
1561 * Note that this is different from the escaping of individual connection
1562 * options above!
1563 */
1564 escaped = escape_quotes(conninfo_buf.data);
1565 appendPQExpBuffer(recoveryconfcontents, "primary_conninfo = '%s'\n", escaped);
1566 free(escaped);
1567
1568 if (replication_slot)
1569 {
1570 /* unescaped: ReplicationSlotValidateName allows [a-z0-9_] only */
1571 appendPQExpBuffer(recoveryconfcontents, "primary_slot_name = '%s'\n",
1572 replication_slot);
1573 }
1574
1575 if (PQExpBufferBroken(recoveryconfcontents) ||
1576 PQExpBufferDataBroken(conninfo_buf))
1577 {
1578 fprintf(stderr, _("%s: out of memory\n"), progname);
1579 disconnect_and_exit(1);
1580 }
1581
1582 termPQExpBuffer(&conninfo_buf);
1583
1584 PQconninfoFree(connOptions);
1585 }
1586
1587
1588 /*
1589 * Write a recovery.conf file into the directory specified in basedir,
1590 * with the contents already collected in memory.
1591 */
1592 static void
WriteRecoveryConf(void)1593 WriteRecoveryConf(void)
1594 {
1595 char filename[MAXPGPATH];
1596 FILE *cf;
1597
1598 sprintf(filename, "%s/recovery.conf", basedir);
1599
1600 cf = fopen(filename, "w");
1601 if (cf == NULL)
1602 {
1603 fprintf(stderr, _("%s: could not create file \"%s\": %s\n"), progname, filename, strerror(errno));
1604 disconnect_and_exit(1);
1605 }
1606
1607 if (fwrite(recoveryconfcontents->data, recoveryconfcontents->len, 1, cf) != 1)
1608 {
1609 fprintf(stderr,
1610 _("%s: could not write to file \"%s\": %s\n"),
1611 progname, filename, strerror(errno));
1612 disconnect_and_exit(1);
1613 }
1614
1615 fclose(cf);
1616 }
1617
1618
1619 static void
BaseBackup(void)1620 BaseBackup(void)
1621 {
1622 PGresult *res;
1623 char *sysidentifier;
1624 TimeLineID latesttli;
1625 TimeLineID starttli;
1626 char *basebkp;
1627 char escaped_label[MAXPGPATH];
1628 char *maxrate_clause = NULL;
1629 int i;
1630 char xlogstart[64];
1631 char xlogend[64];
1632 int minServerMajor,
1633 maxServerMajor;
1634 int serverMajor;
1635
1636 /*
1637 * Connect in replication mode to the server
1638 */
1639 conn = GetConnection();
1640 if (!conn)
1641 /* Error message already written in GetConnection() */
1642 exit(1);
1643
1644 /*
1645 * Check server version. BASE_BACKUP command was introduced in 9.1, so we
1646 * can't work with servers older than 9.1.
1647 */
1648 minServerMajor = 901;
1649 maxServerMajor = PG_VERSION_NUM / 100;
1650 serverMajor = PQserverVersion(conn) / 100;
1651 if (serverMajor < minServerMajor || serverMajor > maxServerMajor)
1652 {
1653 const char *serverver = PQparameterStatus(conn, "server_version");
1654
1655 fprintf(stderr, _("%s: incompatible server version %s\n"),
1656 progname, serverver ? serverver : "'unknown'");
1657 disconnect_and_exit(1);
1658 }
1659
1660 /*
1661 * If WAL streaming was requested, also check that the server is new
1662 * enough for that.
1663 */
1664 if (streamwal && !CheckServerVersionForStreaming(conn))
1665 {
1666 /* Error message already written in CheckServerVersionForStreaming() */
1667 disconnect_and_exit(1);
1668 }
1669
1670 /*
1671 * Build contents of recovery.conf if requested
1672 */
1673 if (writerecoveryconf)
1674 GenerateRecoveryConf(conn);
1675
1676 /*
1677 * Run IDENTIFY_SYSTEM so we can get the timeline
1678 */
1679 if (!RunIdentifySystem(conn, &sysidentifier, &latesttli, NULL, NULL))
1680 disconnect_and_exit(1);
1681
1682 /*
1683 * Start the actual backup
1684 */
1685 PQescapeStringConn(conn, escaped_label, label, sizeof(escaped_label), &i);
1686
1687 if (maxrate > 0)
1688 maxrate_clause = psprintf("MAX_RATE %u", maxrate);
1689
1690 if (verbose)
1691 fprintf(stderr,
1692 _("%s: initiating base backup, waiting for checkpoint to complete\n"),
1693 progname);
1694
1695 if (showprogress && !verbose)
1696 fprintf(stderr, "waiting for checkpoint\r");
1697
1698 basebkp =
1699 psprintf("BASE_BACKUP LABEL '%s' %s %s %s %s %s %s",
1700 escaped_label,
1701 showprogress ? "PROGRESS" : "",
1702 includewal && !streamwal ? "WAL" : "",
1703 fastcheckpoint ? "FAST" : "",
1704 includewal ? "NOWAIT" : "",
1705 maxrate_clause ? maxrate_clause : "",
1706 format == 't' ? "TABLESPACE_MAP" : "");
1707
1708 if (PQsendQuery(conn, basebkp) == 0)
1709 {
1710 fprintf(stderr, _("%s: could not send replication command \"%s\": %s"),
1711 progname, "BASE_BACKUP", PQerrorMessage(conn));
1712 disconnect_and_exit(1);
1713 }
1714
1715 /*
1716 * Get the starting xlog position
1717 */
1718 res = PQgetResult(conn);
1719 if (PQresultStatus(res) != PGRES_TUPLES_OK)
1720 {
1721 fprintf(stderr, _("%s: could not initiate base backup: %s"),
1722 progname, PQerrorMessage(conn));
1723 disconnect_and_exit(1);
1724 }
1725 if (PQntuples(res) != 1)
1726 {
1727 fprintf(stderr,
1728 _("%s: server returned unexpected response to BASE_BACKUP command; got %d rows and %d fields, expected %d rows and %d fields\n"),
1729 progname, PQntuples(res), PQnfields(res), 1, 2);
1730 disconnect_and_exit(1);
1731 }
1732
1733 strlcpy(xlogstart, PQgetvalue(res, 0, 0), sizeof(xlogstart));
1734
1735 if (verbose)
1736 fprintf(stderr, _("%s: checkpoint completed\n"), progname);
1737
1738 /*
1739 * 9.3 and later sends the TLI of the starting point. With older servers,
1740 * assume it's the same as the latest timeline reported by
1741 * IDENTIFY_SYSTEM.
1742 */
1743 if (PQnfields(res) >= 2)
1744 starttli = atoi(PQgetvalue(res, 0, 1));
1745 else
1746 starttli = latesttli;
1747 PQclear(res);
1748 MemSet(xlogend, 0, sizeof(xlogend));
1749
1750 if (verbose && includewal)
1751 fprintf(stderr, _("transaction log start point: %s on timeline %u\n"),
1752 xlogstart, starttli);
1753
1754 /*
1755 * Get the header
1756 */
1757 res = PQgetResult(conn);
1758 if (PQresultStatus(res) != PGRES_TUPLES_OK)
1759 {
1760 fprintf(stderr, _("%s: could not get backup header: %s"),
1761 progname, PQerrorMessage(conn));
1762 disconnect_and_exit(1);
1763 }
1764 if (PQntuples(res) < 1)
1765 {
1766 fprintf(stderr, _("%s: no data returned from server\n"), progname);
1767 disconnect_and_exit(1);
1768 }
1769
1770 /*
1771 * Sum up the total size, for progress reporting
1772 */
1773 totalsize = totaldone = 0;
1774 tablespacecount = PQntuples(res);
1775 for (i = 0; i < PQntuples(res); i++)
1776 {
1777 totalsize += atol(PQgetvalue(res, i, 2));
1778
1779 /*
1780 * Verify tablespace directories are empty. Don't bother with the
1781 * first once since it can be relocated, and it will be checked before
1782 * we do anything anyway.
1783 */
1784 if (format == 'p' && !PQgetisnull(res, i, 1))
1785 {
1786 char *path = (char *) get_tablespace_mapping(PQgetvalue(res, i, 1));
1787
1788 verify_dir_is_empty_or_create(path);
1789 }
1790 }
1791
1792 /*
1793 * When writing to stdout, require a single tablespace
1794 */
1795 if (format == 't' && strcmp(basedir, "-") == 0 && PQntuples(res) > 1)
1796 {
1797 fprintf(stderr,
1798 _("%s: can only write single tablespace to stdout, database has %d\n"),
1799 progname, PQntuples(res));
1800 disconnect_and_exit(1);
1801 }
1802
1803 /*
1804 * If we're streaming WAL, start the streaming session before we start
1805 * receiving the actual data chunks.
1806 */
1807 if (streamwal)
1808 {
1809 if (verbose)
1810 fprintf(stderr, _("%s: starting background WAL receiver\n"),
1811 progname);
1812 StartLogStreamer(xlogstart, starttli, sysidentifier);
1813 }
1814
1815 /*
1816 * Start receiving chunks
1817 */
1818 for (i = 0; i < PQntuples(res); i++)
1819 {
1820 if (format == 't')
1821 ReceiveTarFile(conn, res, i);
1822 else
1823 ReceiveAndUnpackTarFile(conn, res, i);
1824 } /* Loop over all tablespaces */
1825
1826 if (showprogress)
1827 {
1828 progress_report(PQntuples(res), NULL, true);
1829 fprintf(stderr, "\n"); /* Need to move to next line */
1830 }
1831
1832 PQclear(res);
1833
1834 /*
1835 * Get the stop position
1836 */
1837 res = PQgetResult(conn);
1838 if (PQresultStatus(res) != PGRES_TUPLES_OK)
1839 {
1840 fprintf(stderr,
1841 _("%s: could not get transaction log end position from server: %s"),
1842 progname, PQerrorMessage(conn));
1843 disconnect_and_exit(1);
1844 }
1845 if (PQntuples(res) != 1)
1846 {
1847 fprintf(stderr,
1848 _("%s: no transaction log end position returned from server\n"),
1849 progname);
1850 disconnect_and_exit(1);
1851 }
1852 strlcpy(xlogend, PQgetvalue(res, 0, 0), sizeof(xlogend));
1853 if (verbose && includewal)
1854 fprintf(stderr, "transaction log end point: %s\n", xlogend);
1855 PQclear(res);
1856
1857 res = PQgetResult(conn);
1858 if (PQresultStatus(res) != PGRES_COMMAND_OK)
1859 {
1860 fprintf(stderr, _("%s: final receive failed: %s"),
1861 progname, PQerrorMessage(conn));
1862 disconnect_and_exit(1);
1863 }
1864
1865 if (bgchild > 0)
1866 {
1867 #ifndef WIN32
1868 int status;
1869 pid_t r;
1870 #else
1871 DWORD status;
1872
1873 /*
1874 * get a pointer sized version of bgchild to avoid warnings about
1875 * casting to a different size on WIN64.
1876 */
1877 intptr_t bgchild_handle = bgchild;
1878 uint32 hi,
1879 lo;
1880 #endif
1881
1882 if (verbose)
1883 fprintf(stderr,
1884 _("%s: waiting for background process to finish streaming ...\n"), progname);
1885
1886 #ifndef WIN32
1887 if (write(bgpipe[1], xlogend, strlen(xlogend)) != strlen(xlogend))
1888 {
1889 fprintf(stderr,
1890 _("%s: could not send command to background pipe: %s\n"),
1891 progname, strerror(errno));
1892 disconnect_and_exit(1);
1893 }
1894
1895 /* Just wait for the background process to exit */
1896 r = waitpid(bgchild, &status, 0);
1897 if (r == (pid_t) -1)
1898 {
1899 fprintf(stderr, _("%s: could not wait for child process: %s\n"),
1900 progname, strerror(errno));
1901 disconnect_and_exit(1);
1902 }
1903 if (r != bgchild)
1904 {
1905 fprintf(stderr, _("%s: child %d died, expected %d\n"),
1906 progname, (int) r, (int) bgchild);
1907 disconnect_and_exit(1);
1908 }
1909 if (status != 0)
1910 {
1911 fprintf(stderr, "%s: %s\n",
1912 progname, wait_result_to_str(status));
1913 disconnect_and_exit(1);
1914 }
1915 /* Exited normally, we're happy! */
1916 #else /* WIN32 */
1917
1918 /*
1919 * On Windows, since we are in the same process, we can just store the
1920 * value directly in the variable, and then set the flag that says
1921 * it's there.
1922 */
1923 if (sscanf(xlogend, "%X/%X", &hi, &lo) != 2)
1924 {
1925 fprintf(stderr,
1926 _("%s: could not parse transaction log location \"%s\"\n"),
1927 progname, xlogend);
1928 disconnect_and_exit(1);
1929 }
1930 xlogendptr = ((uint64) hi) << 32 | lo;
1931 InterlockedIncrement(&has_xlogendptr);
1932
1933 /* First wait for the thread to exit */
1934 if (WaitForSingleObjectEx((HANDLE) bgchild_handle, INFINITE, FALSE) !=
1935 WAIT_OBJECT_0)
1936 {
1937 _dosmaperr(GetLastError());
1938 fprintf(stderr, _("%s: could not wait for child thread: %s\n"),
1939 progname, strerror(errno));
1940 disconnect_and_exit(1);
1941 }
1942 if (GetExitCodeThread((HANDLE) bgchild_handle, &status) == 0)
1943 {
1944 _dosmaperr(GetLastError());
1945 fprintf(stderr, _("%s: could not get child thread exit status: %s\n"),
1946 progname, strerror(errno));
1947 disconnect_and_exit(1);
1948 }
1949 if (status != 0)
1950 {
1951 fprintf(stderr, _("%s: child thread exited with error %u\n"),
1952 progname, (unsigned int) status);
1953 disconnect_and_exit(1);
1954 }
1955 /* Exited normally, we're happy */
1956 #endif
1957 }
1958
1959 /* Free the recovery.conf contents */
1960 destroyPQExpBuffer(recoveryconfcontents);
1961
1962 /*
1963 * End of copy data. Final result is already checked inside the loop.
1964 */
1965 PQclear(res);
1966 PQfinish(conn);
1967
1968 if (verbose)
1969 fprintf(stderr, "%s: base backup completed\n", progname);
1970 }
1971
1972
1973 int
main(int argc,char ** argv)1974 main(int argc, char **argv)
1975 {
1976 static struct option long_options[] = {
1977 {"help", no_argument, NULL, '?'},
1978 {"version", no_argument, NULL, 'V'},
1979 {"pgdata", required_argument, NULL, 'D'},
1980 {"format", required_argument, NULL, 'F'},
1981 {"checkpoint", required_argument, NULL, 'c'},
1982 {"max-rate", required_argument, NULL, 'r'},
1983 {"write-recovery-conf", no_argument, NULL, 'R'},
1984 {"slot", required_argument, NULL, 'S'},
1985 {"tablespace-mapping", required_argument, NULL, 'T'},
1986 {"xlog", no_argument, NULL, 'x'},
1987 {"xlog-method", required_argument, NULL, 'X'},
1988 {"gzip", no_argument, NULL, 'z'},
1989 {"compress", required_argument, NULL, 'Z'},
1990 {"label", required_argument, NULL, 'l'},
1991 {"dbname", required_argument, NULL, 'd'},
1992 {"host", required_argument, NULL, 'h'},
1993 {"port", required_argument, NULL, 'p'},
1994 {"username", required_argument, NULL, 'U'},
1995 {"no-password", no_argument, NULL, 'w'},
1996 {"password", no_argument, NULL, 'W'},
1997 {"status-interval", required_argument, NULL, 's'},
1998 {"verbose", no_argument, NULL, 'v'},
1999 {"progress", no_argument, NULL, 'P'},
2000 {"xlogdir", required_argument, NULL, 1},
2001 {NULL, 0, NULL, 0}
2002 };
2003 int c;
2004
2005 int option_index;
2006
2007 progname = get_progname(argv[0]);
2008 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_basebackup"));
2009
2010 if (argc > 1)
2011 {
2012 if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
2013 {
2014 usage();
2015 exit(0);
2016 }
2017 else if (strcmp(argv[1], "-V") == 0
2018 || strcmp(argv[1], "--version") == 0)
2019 {
2020 puts("pg_basebackup (PostgreSQL) " PG_VERSION);
2021 exit(0);
2022 }
2023 }
2024
2025 while ((c = getopt_long(argc, argv, "D:F:r:RT:xX:l:zZ:d:c:h:p:U:s:S:wWvP",
2026 long_options, &option_index)) != -1)
2027 {
2028 switch (c)
2029 {
2030 case 'D':
2031 basedir = pg_strdup(optarg);
2032 break;
2033 case 'F':
2034 if (strcmp(optarg, "p") == 0 || strcmp(optarg, "plain") == 0)
2035 format = 'p';
2036 else if (strcmp(optarg, "t") == 0 || strcmp(optarg, "tar") == 0)
2037 format = 't';
2038 else
2039 {
2040 fprintf(stderr,
2041 _("%s: invalid output format \"%s\", must be \"plain\" or \"tar\"\n"),
2042 progname, optarg);
2043 exit(1);
2044 }
2045 break;
2046 case 'r':
2047 maxrate = parse_max_rate(optarg);
2048 break;
2049 case 'R':
2050 writerecoveryconf = true;
2051 break;
2052 case 'S':
2053 replication_slot = pg_strdup(optarg);
2054 break;
2055 case 'T':
2056 tablespace_list_append(optarg);
2057 break;
2058 case 'x':
2059 if (includewal)
2060 {
2061 fprintf(stderr,
2062 _("%s: cannot specify both --xlog and --xlog-method\n"),
2063 progname);
2064 exit(1);
2065 }
2066
2067 includewal = true;
2068 streamwal = false;
2069 break;
2070 case 'X':
2071 if (includewal)
2072 {
2073 fprintf(stderr,
2074 _("%s: cannot specify both --xlog and --xlog-method\n"),
2075 progname);
2076 exit(1);
2077 }
2078
2079 includewal = true;
2080 if (strcmp(optarg, "f") == 0 ||
2081 strcmp(optarg, "fetch") == 0)
2082 streamwal = false;
2083 else if (strcmp(optarg, "s") == 0 ||
2084 strcmp(optarg, "stream") == 0)
2085 streamwal = true;
2086 else
2087 {
2088 fprintf(stderr,
2089 _("%s: invalid xlog-method option \"%s\", must be \"fetch\" or \"stream\"\n"),
2090 progname, optarg);
2091 exit(1);
2092 }
2093 break;
2094 case 1:
2095 xlog_dir = pg_strdup(optarg);
2096 break;
2097 case 'l':
2098 label = pg_strdup(optarg);
2099 break;
2100 case 'z':
2101 #ifdef HAVE_LIBZ
2102 compresslevel = Z_DEFAULT_COMPRESSION;
2103 #else
2104 compresslevel = 1; /* will be rejected below */
2105 #endif
2106 break;
2107 case 'Z':
2108 compresslevel = atoi(optarg);
2109 if (compresslevel < 0 || compresslevel > 9)
2110 {
2111 fprintf(stderr, _("%s: invalid compression level \"%s\"\n"),
2112 progname, optarg);
2113 exit(1);
2114 }
2115 break;
2116 case 'c':
2117 if (pg_strcasecmp(optarg, "fast") == 0)
2118 fastcheckpoint = true;
2119 else if (pg_strcasecmp(optarg, "spread") == 0)
2120 fastcheckpoint = false;
2121 else
2122 {
2123 fprintf(stderr, _("%s: invalid checkpoint argument \"%s\", must be \"fast\" or \"spread\"\n"),
2124 progname, optarg);
2125 exit(1);
2126 }
2127 break;
2128 case 'd':
2129 connection_string = pg_strdup(optarg);
2130 break;
2131 case 'h':
2132 dbhost = pg_strdup(optarg);
2133 break;
2134 case 'p':
2135 dbport = pg_strdup(optarg);
2136 break;
2137 case 'U':
2138 dbuser = pg_strdup(optarg);
2139 break;
2140 case 'w':
2141 dbgetpassword = -1;
2142 break;
2143 case 'W':
2144 dbgetpassword = 1;
2145 break;
2146 case 's':
2147 standby_message_timeout = atoi(optarg) * 1000;
2148 if (standby_message_timeout < 0)
2149 {
2150 fprintf(stderr, _("%s: invalid status interval \"%s\"\n"),
2151 progname, optarg);
2152 exit(1);
2153 }
2154 break;
2155 case 'v':
2156 verbose++;
2157 break;
2158 case 'P':
2159 showprogress = true;
2160 break;
2161 default:
2162
2163 /*
2164 * getopt_long already emitted a complaint
2165 */
2166 fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
2167 progname);
2168 exit(1);
2169 }
2170 }
2171
2172 /*
2173 * Any non-option arguments?
2174 */
2175 if (optind < argc)
2176 {
2177 fprintf(stderr,
2178 _("%s: too many command-line arguments (first is \"%s\")\n"),
2179 progname, argv[optind]);
2180 fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
2181 progname);
2182 exit(1);
2183 }
2184
2185 /*
2186 * Required arguments
2187 */
2188 if (basedir == NULL)
2189 {
2190 fprintf(stderr, _("%s: no target directory specified\n"), progname);
2191 fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
2192 progname);
2193 exit(1);
2194 }
2195
2196 /*
2197 * Mutually exclusive arguments
2198 */
2199 if (format == 'p' && compresslevel != 0)
2200 {
2201 fprintf(stderr,
2202 _("%s: only tar mode backups can be compressed\n"),
2203 progname);
2204 fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
2205 progname);
2206 exit(1);
2207 }
2208
2209 if (format != 'p' && streamwal)
2210 {
2211 fprintf(stderr,
2212 _("%s: WAL streaming can only be used in plain mode\n"),
2213 progname);
2214 fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
2215 progname);
2216 exit(1);
2217 }
2218
2219 if (replication_slot && !streamwal)
2220 {
2221 fprintf(stderr,
2222 _("%s: replication slots can only be used with WAL streaming\n"),
2223 progname);
2224 fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
2225 progname);
2226 exit(1);
2227 }
2228
2229 if (strcmp(xlog_dir, "") != 0)
2230 {
2231 if (format != 'p')
2232 {
2233 fprintf(stderr,
2234 _("%s: transaction log directory location can only be specified in plain mode\n"),
2235 progname);
2236 fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
2237 progname);
2238 exit(1);
2239 }
2240
2241 /* clean up xlog directory name, check it's absolute */
2242 canonicalize_path(xlog_dir);
2243 if (!is_absolute_path(xlog_dir))
2244 {
2245 fprintf(stderr, _("%s: transaction log directory location must be "
2246 "an absolute path\n"), progname);
2247 fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
2248 progname);
2249 exit(1);
2250 }
2251 }
2252
2253 #ifndef HAVE_LIBZ
2254 if (compresslevel != 0)
2255 {
2256 fprintf(stderr,
2257 _("%s: this build does not support compression\n"),
2258 progname);
2259 exit(1);
2260 }
2261 #endif
2262
2263 /*
2264 * Verify that the target directory exists, or create it. For plaintext
2265 * backups, always require the directory. For tar backups, require it
2266 * unless we are writing to stdout.
2267 */
2268 if (format == 'p' || strcmp(basedir, "-") != 0)
2269 verify_dir_is_empty_or_create(basedir);
2270
2271 /* Create transaction log symlink, if required */
2272 if (strcmp(xlog_dir, "") != 0)
2273 {
2274 char *linkloc;
2275
2276 verify_dir_is_empty_or_create(xlog_dir);
2277
2278 /* form name of the place where the symlink must go */
2279 linkloc = psprintf("%s/pg_xlog", basedir);
2280
2281 #ifdef HAVE_SYMLINK
2282 if (symlink(xlog_dir, linkloc) != 0)
2283 {
2284 fprintf(stderr, _("%s: could not create symbolic link \"%s\": %s\n"),
2285 progname, linkloc, strerror(errno));
2286 exit(1);
2287 }
2288 #else
2289 fprintf(stderr, _("%s: symlinks are not supported on this platform\n"), progname);
2290 exit(1);
2291 #endif
2292 free(linkloc);
2293 }
2294
2295 BaseBackup();
2296
2297 return 0;
2298 }
2299