LCOV - differential code coverage report
Current view: top level - src/bin/pg_archivecleanup - pg_archivecleanup.c (source / functions) Coverage Total Hit UBC GNC CBC DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 96.8 % 125 121 4 1 120 1
Current Date: 2023-04-08 17:13:01 Functions: 100.0 % 6 6 1 5
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (60,120] days: 100.0 % 1 1 1
Legend: Lines: hit not hit (240..) days: 96.8 % 124 120 4 120
Function coverage date bins:
(240..) days: 100.0 % 6 6 1 5

 Age         Owner                  TLA  Line data    Source code
                                  1                 : /*
                                  2                 :  * pg_archivecleanup.c
                                  3                 :  *
                                  4                 :  * To be used as archive_cleanup_command to clean an archive when using
                                  5                 :  * standby mode.
                                  6                 :  *
                                  7                 :  * src/bin/pg_archivecleanup/pg_archivecleanup.c
                                  8                 :  */
                                  9                 : #include "postgres_fe.h"
                                 10                 : 
                                 11                 : #include <ctype.h>
                                 12                 : #include <dirent.h>
                                 13                 : #include <sys/stat.h>
                                 14                 : #include <fcntl.h>
                                 15                 : #include <signal.h>
                                 16                 : #include <sys/time.h>
                                 17                 : 
                                 18                 : #include "access/xlog_internal.h"
                                 19                 : #include "common/logging.h"
                                 20                 : #include "pg_getopt.h"
                                 21                 : 
                                 22                 : const char *progname;
                                 23                 : 
                                 24                 : /* Options and defaults */
                                 25                 : bool        dryrun = false;     /* are we performing a dry-run operation? */
                                 26                 : char       *additional_ext = NULL;  /* Extension to remove from filenames */
                                 27                 : 
                                 28                 : char       *archiveLocation;    /* where to find the archive? */
                                 29                 : char       *restartWALFileName; /* the file from which we can restart restore */
                                 30                 : char        exclusiveCleanupFileName[MAXFNAMELEN];  /* the oldest file we want
                                 31                 :                                                      * to remain in archive */
                                 32                 : 
                                 33                 : 
                                 34                 : /* =====================================================================
                                 35                 :  *
                                 36                 :  *        Customizable section
                                 37                 :  *
                                 38                 :  * =====================================================================
                                 39                 :  *
                                 40                 :  *  Currently, this section assumes that the Archive is a locally
                                 41                 :  *  accessible directory. If you want to make other assumptions,
                                 42                 :  *  such as using a vendor-specific archive and access API, these
                                 43                 :  *  routines are the ones you'll need to change. You're
                                 44                 :  *  encouraged to submit any changes to pgsql-hackers@lists.postgresql.org
                                 45                 :  *  or personally to the current maintainer. Those changes may be
                                 46                 :  *  folded in to later versions of this program.
                                 47                 :  */
                                 48                 : 
                                 49                 : /*
                                 50                 :  *  Initialize allows customized commands into the archive cleanup program.
                                 51                 :  *
                                 52                 :  *  You may wish to add code to check for tape libraries, etc..
                                 53                 :  */
                                 54                 : static void
 4682 simon                      55 CBC           6 : Initialize(void)
                                 56                 : {
                                 57                 :     /*
                                 58                 :      * This code assumes that archiveLocation is a directory, so we use stat
                                 59                 :      * to test if it's accessible.
                                 60                 :      */
                                 61                 :     struct stat stat_buf;
                                 62                 : 
 4679 tgl                        63               6 :     if (stat(archiveLocation, &stat_buf) != 0 ||
                                 64               5 :         !S_ISDIR(stat_buf.st_mode))
                                 65                 :     {
 1469 peter                      66               1 :         pg_log_error("archive location \"%s\" does not exist",
                                 67                 :                      archiveLocation);
 4682 simon                      68               1 :         exit(2);
                                 69                 :     }
                                 70               5 : }
                                 71                 : 
                                 72                 : static void
 4021 rhaas                      73              33 : TrimExtension(char *filename, char *extension)
                                 74                 : {
                                 75                 :     int         flen;
                                 76                 :     int         elen;
                                 77                 : 
                                 78              33 :     if (extension == NULL)
                                 79               9 :         return;
                                 80                 : 
                                 81              24 :     elen = strlen(extension);
                                 82              24 :     flen = strlen(filename);
                                 83                 : 
                                 84              24 :     if (flen > elen && strcmp(filename + flen - elen, extension) == 0)
                                 85               3 :         filename[flen - elen] = '\0';
                                 86                 : }
                                 87                 : 
                                 88                 : static void
 4682 simon                      89               4 : CleanupPriorWALFiles(void)
                                 90                 : {
                                 91                 :     int         rc;
                                 92                 :     DIR        *xldir;
                                 93                 :     struct dirent *xlde;
                                 94                 :     char        walfile[MAXPGPATH];
                                 95                 : 
                                 96               4 :     if ((xldir = opendir(archiveLocation)) != NULL)
                                 97                 :     {
 3306 bruce                      98              32 :         while (errno = 0, (xlde = readdir(xldir)) != NULL)
                                 99                 :         {
                                100                 :             /*
                                101                 :              * Truncation is essentially harmless, because we skip names of
                                102                 :              * length other than XLOG_FNAME_LEN.  (In principle, one could use
                                103                 :              * a 1000-character additional_ext and get trouble.)
                                104                 :              */
 3156 noah                      105              28 :             strlcpy(walfile, xlde->d_name, MAXPGPATH);
 4021 rhaas                     106              28 :             TrimExtension(walfile, additional_ext);
                                107                 : 
                                108                 :             /*
                                109                 :              * We ignore the timeline part of the XLOG segment identifiers in
                                110                 :              * deciding whether a segment is still needed.  This ensures that
                                111                 :              * we won't prematurely remove a segment from a parent timeline.
                                112                 :              * We could probably be a little more proactive about removing
                                113                 :              * segments of non-parent timelines, but that would be a whole lot
                                114                 :              * more complicated.
                                115                 :              *
                                116                 :              * We use the alphanumeric sorting property of the filenames to
                                117                 :              * decide which ones are earlier than the exclusiveCleanupFileName
                                118                 :              * file. Note that this means files are not removed in the order
                                119                 :              * they were originally written, in case this worries you.
                                120                 :              */
 2837 fujii                     121              28 :             if ((IsXLogFileName(walfile) || IsPartialXLogFileName(walfile)) &&
 4021 rhaas                     122              15 :                 strcmp(walfile + 8, exclusiveCleanupFileName + 8) < 0)
                                123                 :             {
                                124                 :                 char        WALFilePath[MAXPGPATH * 2]; /* the file path
                                125                 :                                                          * including archive */
                                126                 : 
                                127                 :                 /*
                                128                 :                  * Use the original file name again now, including any
                                129                 :                  * extension that might have been chopped off before testing
                                130                 :                  * the sequence.
                                131                 :                  */
 2189 peter_e                   132               7 :                 snprintf(WALFilePath, sizeof(WALFilePath), "%s/%s",
 4612 tgl                       133               7 :                          archiveLocation, xlde->d_name);
                                134                 : 
 4085 alvherre                  135               7 :                 if (dryrun)
                                136                 :                 {
                                137                 :                     /*
                                138                 :                      * Prints the name of the file to be removed and skips the
                                139                 :                      * actual removal.  The regular printout is so that the
                                140                 :                      * user can pipe the output into some other program.
                                141                 :                      */
                                142               1 :                     printf("%s\n", WALFilePath);
 1469 peter                     143               1 :                     pg_log_debug("file \"%s\" would be removed", WALFilePath);
 4085 alvherre                  144               1 :                     continue;
                                145                 :                 }
                                146                 : 
 1469 peter                     147               6 :                 pg_log_debug("removing file \"%s\"", WALFilePath);
                                148                 : 
 4682 simon                     149               6 :                 rc = unlink(WALFilePath);
                                150               6 :                 if (rc != 0)
  366 tgl                       151 UBC           0 :                     pg_fatal("could not remove file \"%s\": %m",
                                152                 :                              WALFilePath);
                                153                 :             }
                                154                 :         }
                                155                 : 
 3306 bruce                     156 CBC           4 :         if (errno)
  366 tgl                       157 UBC           0 :             pg_fatal("could not read archive location \"%s\": %m",
                                158                 :                      archiveLocation);
 3306 bruce                     159 CBC           4 :         if (closedir(xldir))
  366 tgl                       160 UBC           0 :             pg_fatal("could not close archive location \"%s\": %m",
                                161                 :                      archiveLocation);
                                162                 :     }
                                163                 :     else
                                164               0 :         pg_fatal("could not open archive location \"%s\": %m",
                                165                 :                  archiveLocation);
 4682 simon                     166 CBC           4 : }
                                167                 : 
                                168                 : /*
                                169                 :  * SetWALFileNameForCleanup()
                                170                 :  *
                                171                 :  *    Set the earliest WAL filename that we want to keep on the archive
                                172                 :  *    and decide whether we need cleanup
                                173                 :  */
                                174                 : static void
                                175               5 : SetWALFileNameForCleanup(void)
                                176                 : {
 4660 bruce                     177               5 :     bool        fnameOK = false;
                                178                 : 
 4021 rhaas                     179               5 :     TrimExtension(restartWALFileName, additional_ext);
                                180                 : 
                                181                 :     /*
                                182                 :      * If restartWALFileName is a WAL file name then just use it directly. If
                                183                 :      * restartWALFileName is a .partial or .backup filename, make sure we use
                                184                 :      * the prefix of the filename, otherwise we will remove wrong files since
                                185                 :      * 000000010000000000000010.partial and
                                186                 :      * 000000010000000000000010.00000020.backup are after
                                187                 :      * 000000010000000000000010.
                                188                 :      */
 2838 fujii                     189               5 :     if (IsXLogFileName(restartWALFileName))
                                190                 :     {
 4682 simon                     191               2 :         strcpy(exclusiveCleanupFileName, restartWALFileName);
                                192               2 :         fnameOK = true;
                                193                 :     }
 2837 fujii                     194               3 :     else if (IsPartialXLogFileName(restartWALFileName))
                                195                 :     {
                                196                 :         int         args;
                                197               1 :         uint32      tli = 1,
                                198               1 :                     log = 0,
                                199               1 :                     seg = 0;
                                200                 : 
                                201               1 :         args = sscanf(restartWALFileName, "%08X%08X%08X.partial",
                                202                 :                       &tli, &log, &seg);
                                203               1 :         if (args == 3)
                                204                 :         {
                                205               1 :             fnameOK = true;
                                206                 : 
                                207                 :             /*
                                208                 :              * Use just the prefix of the filename, ignore everything after
                                209                 :              * first period
                                210                 :              */
                                211               1 :             XLogFileNameById(exclusiveCleanupFileName, tli, log, seg);
                                212                 :         }
                                213                 :     }
 2838                           214               2 :     else if (IsBackupHistoryFileName(restartWALFileName))
                                215                 :     {
                                216                 :         int         args;
 4682 simon                     217               1 :         uint32      tli = 1,
                                218               1 :                     log = 0,
                                219               1 :                     seg = 0,
                                220               1 :                     offset = 0;
                                221                 : 
                                222               1 :         args = sscanf(restartWALFileName, "%08X%08X%08X.%08X.backup", &tli, &log, &seg, &offset);
                                223               1 :         if (args == 4)
                                224                 :         {
                                225               1 :             fnameOK = true;
                                226                 : 
                                227                 :             /*
                                228                 :              * Use just the prefix of the filename, ignore everything after
                                229                 :              * first period
                                230                 :              */
 2838 fujii                     231               1 :             XLogFileNameById(exclusiveCleanupFileName, tli, log, seg);
                                232                 :         }
                                233                 :     }
                                234                 : 
 4682 simon                     235               5 :     if (!fnameOK)
                                236                 :     {
 1469 peter                     237               1 :         pg_log_error("invalid file name argument");
  366 tgl                       238               1 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 4682 simon                     239               1 :         exit(2);
                                240                 :     }
                                241               4 : }
                                242                 : 
                                243                 : /* =====================================================================
                                244                 :  *        End of Customizable section
                                245                 :  * =====================================================================
                                246                 :  */
                                247                 : 
                                248                 : static void
                                249               1 : usage(void)
                                250                 : {
 2368 peter_e                   251               1 :     printf(_("%s removes older WAL files from PostgreSQL archives.\n\n"), progname);
                                252               1 :     printf(_("Usage:\n"));
                                253               1 :     printf(_("  %s [OPTION]... ARCHIVELOCATION OLDESTKEPTWALFILE\n"), progname);
                                254               1 :     printf(_("\nOptions:\n"));
                                255               1 :     printf(_("  -d             generate debug output (verbose mode)\n"));
                                256               1 :     printf(_("  -n             dry run, show the names of the files that would be removed\n"));
                                257               1 :     printf(_("  -V, --version  output version information, then exit\n"));
                                258               1 :     printf(_("  -x EXT         clean up files if they have this extension\n"));
                                259               1 :     printf(_("  -?, --help     show this help, then exit\n"));
                                260               1 :     printf(_("\n"
                                261                 :              "For use as archive_cleanup_command in postgresql.conf:\n"
                                262                 :              "  archive_cleanup_command = 'pg_archivecleanup [OPTION]... ARCHIVELOCATION %%r'\n"
                                263                 :              "e.g.\n"
                                264                 :              "  archive_cleanup_command = 'pg_archivecleanup /mnt/server/archiverdir %%r'\n"));
                                265               1 :     printf(_("\n"
                                266                 :              "Or for use as a standalone archive cleaner:\n"
                                267                 :              "e.g.\n"
                                268                 :              "  pg_archivecleanup /mnt/server/archiverdir 000000010000000000000010.00000020.backup\n"));
 1136 peter                     269               1 :     printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
                                270               1 :     printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
 4682 simon                     271               1 : }
                                272                 : 
                                273                 : /*------------ MAIN ----------------------------------------*/
                                274                 : int
                                275              12 : main(int argc, char **argv)
                                276                 : {
                                277                 :     int         c;
                                278                 : 
 1469 peter                     279              12 :     pg_logging_init(argv[0]);
 2368 peter_e                   280              12 :     set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_archivecleanup"));
 4682 simon                     281              12 :     progname = get_progname(argv[0]);
                                282                 : 
                                283              12 :     if (argc > 1)
                                284                 :     {
                                285              11 :         if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
                                286                 :         {
                                287               1 :             usage();
                                288               1 :             exit(0);
                                289                 :         }
                                290              10 :         if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
                                291                 :         {
                                292               1 :             puts("pg_archivecleanup (PostgreSQL) " PG_VERSION);
                                293               1 :             exit(0);
                                294                 :         }
                                295                 :     }
                                296                 : 
  118 peter                     297 GNC          15 :     while ((c = getopt(argc, argv, "dnx:")) != -1)
                                298                 :     {
 4682 simon                     299 CBC           6 :         switch (c)
                                300                 :         {
                                301               1 :             case 'd':           /* Debug mode */
  934 tgl                       302               1 :                 pg_logging_increase_verbosity();
 4682 simon                     303               1 :                 break;
 4085 alvherre                  304               1 :             case 'n':           /* Dry-Run mode */
                                305               1 :                 dryrun = true;
                                306               1 :                 break;
 4021 rhaas                     307               3 :             case 'x':
 2118 tgl                       308               3 :                 additional_ext = pg_strdup(optarg); /* Extension to remove
                                309                 :                                                      * from xlogfile names */
 4021 rhaas                     310               3 :                 break;
 4682 simon                     311               1 :             default:
                                312                 :                 /* getopt already emitted a complaint */
  366 tgl                       313               1 :                 pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 4682 simon                     314               1 :                 exit(2);
                                315                 :         }
                                316                 :     }
                                317                 : 
                                318                 :     /*
                                319                 :      * We will go to the archiveLocation to check restartWALFileName.
                                320                 :      * restartWALFileName may not exist anymore, which would not be an error,
                                321                 :      * so we separate the archiveLocation and restartWALFileName so we can
                                322                 :      * check separately whether archiveLocation exists, if not that is an
                                323                 :      * error
                                324                 :      */
                                325               9 :     if (optind < argc)
                                326                 :     {
                                327               8 :         archiveLocation = argv[optind];
                                328               8 :         optind++;
                                329                 :     }
                                330                 :     else
                                331                 :     {
 1469 peter                     332               1 :         pg_log_error("must specify archive location");
  366 tgl                       333               1 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 4682 simon                     334               1 :         exit(2);
                                335                 :     }
                                336                 : 
                                337               8 :     if (optind < argc)
                                338                 :     {
                                339               7 :         restartWALFileName = argv[optind];
                                340               7 :         optind++;
                                341                 :     }
                                342                 :     else
                                343                 :     {
 1469 peter                     344               1 :         pg_log_error("must specify oldest kept WAL file");
  366 tgl                       345               1 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 4682 simon                     346               1 :         exit(2);
                                347                 :     }
                                348                 : 
                                349               7 :     if (optind < argc)
                                350                 :     {
 1469 peter                     351               1 :         pg_log_error("too many command-line arguments");
  366 tgl                       352               1 :         pg_log_error_hint("Try \"%s --help\" for more information.", progname);
 4682 simon                     353               1 :         exit(2);
                                354                 :     }
                                355                 : 
                                356                 :     /*
                                357                 :      * Check archive exists and other initialization if required.
                                358                 :      */
                                359               6 :     Initialize();
                                360                 : 
                                361                 :     /*
                                362                 :      * Check filename is a valid name, then process to find cut-off
                                363                 :      */
                                364               5 :     SetWALFileNameForCleanup();
                                365                 : 
 1469 peter                     366               4 :     pg_log_debug("keeping WAL file \"%s/%s\" and later",
                                367                 :                  archiveLocation, exclusiveCleanupFileName);
                                368                 : 
                                369                 :     /*
                                370                 :      * Remove WAL files older than cut-off
                                371                 :      */
 4682 simon                     372               4 :     CleanupPriorWALFiles();
                                373                 : 
                                374               4 :     exit(0);
                                375                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a