LCOV - code coverage report
Current view: top level - src/bin/pg_basebackup - pg_basebackup.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 69.1 % 1021 706
Test Date: 2026-07-26 10:15:44 Functions: 78.1 % 32 25
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 60.6 % 756 458

             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, &copybuf, 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 &copybuf[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, &copybuf[*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                 :             : }
        

Generated by: LCOV version 2.0-1