Coverage Report

Created: 2025-08-12 06:43

/src/postgres/src/backend/archive/shell_archive.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * shell_archive.c
4
 *
5
 * This archiving function uses a user-specified shell command (the
6
 * archive_command GUC) to copy write-ahead log files.  It is used as the
7
 * default, but other modules may define their own custom archiving logic.
8
 *
9
 * Copyright (c) 2022-2025, PostgreSQL Global Development Group
10
 *
11
 * IDENTIFICATION
12
 *    src/backend/archive/shell_archive.c
13
 *
14
 *-------------------------------------------------------------------------
15
 */
16
#include "postgres.h"
17
18
#include <sys/wait.h>
19
20
#include "access/xlog.h"
21
#include "archive/archive_module.h"
22
#include "archive/shell_archive.h"
23
#include "common/percentrepl.h"
24
#include "pgstat.h"
25
26
static bool shell_archive_configured(ArchiveModuleState *state);
27
static bool shell_archive_file(ArchiveModuleState *state,
28
                 const char *file,
29
                 const char *path);
30
static void shell_archive_shutdown(ArchiveModuleState *state);
31
32
static const ArchiveModuleCallbacks shell_archive_callbacks = {
33
  .startup_cb = NULL,
34
  .check_configured_cb = shell_archive_configured,
35
  .archive_file_cb = shell_archive_file,
36
  .shutdown_cb = shell_archive_shutdown
37
};
38
39
const ArchiveModuleCallbacks *
40
shell_archive_init(void)
41
0
{
42
0
  return &shell_archive_callbacks;
43
0
}
44
45
static bool
46
shell_archive_configured(ArchiveModuleState *state)
47
0
{
48
0
  if (XLogArchiveCommand[0] != '\0')
49
0
    return true;
50
51
0
  arch_module_check_errdetail("\"%s\" is not set.",
52
0
                "archive_command");
53
0
  return false;
54
0
}
55
56
static bool
57
shell_archive_file(ArchiveModuleState *state, const char *file,
58
           const char *path)
59
0
{
60
0
  char     *xlogarchcmd;
61
0
  char     *nativePath = NULL;
62
0
  int     rc;
63
64
0
  if (path)
65
0
  {
66
0
    nativePath = pstrdup(path);
67
0
    make_native_path(nativePath);
68
0
  }
69
70
0
  xlogarchcmd = replace_percent_placeholders(XLogArchiveCommand,
71
0
                         "archive_command", "fp",
72
0
                         file, nativePath);
73
74
0
  ereport(DEBUG3,
75
0
      (errmsg_internal("executing archive command \"%s\"",
76
0
               xlogarchcmd)));
77
78
0
  fflush(NULL);
79
0
  pgstat_report_wait_start(WAIT_EVENT_ARCHIVE_COMMAND);
80
0
  rc = system(xlogarchcmd);
81
0
  pgstat_report_wait_end();
82
83
0
  if (rc != 0)
84
0
  {
85
    /*
86
     * If either the shell itself, or a called command, died on a signal,
87
     * abort the archiver.  We do this because system() ignores SIGINT and
88
     * SIGQUIT while waiting; so a signal is very likely something that
89
     * should have interrupted us too.  Also die if the shell got a hard
90
     * "command not found" type of error.  If we overreact it's no big
91
     * deal, the postmaster will just start the archiver again.
92
     */
93
0
    int     lev = wait_result_is_any_signal(rc, true) ? FATAL : LOG;
94
95
0
    if (WIFEXITED(rc))
96
0
    {
97
0
      ereport(lev,
98
0
          (errmsg("archive command failed with exit code %d",
99
0
              WEXITSTATUS(rc)),
100
0
           errdetail("The failed archive command was: %s",
101
0
                 xlogarchcmd)));
102
0
    }
103
0
    else if (WIFSIGNALED(rc))
104
0
    {
105
#if defined(WIN32)
106
      ereport(lev,
107
          (errmsg("archive command was terminated by exception 0x%X",
108
              WTERMSIG(rc)),
109
           errhint("See C include file \"ntstatus.h\" for a description of the hexadecimal value."),
110
           errdetail("The failed archive command was: %s",
111
                 xlogarchcmd)));
112
#else
113
0
      ereport(lev,
114
0
          (errmsg("archive command was terminated by signal %d: %s",
115
0
              WTERMSIG(rc), pg_strsignal(WTERMSIG(rc))),
116
0
           errdetail("The failed archive command was: %s",
117
0
                 xlogarchcmd)));
118
0
#endif
119
0
    }
120
0
    else
121
0
    {
122
0
      ereport(lev,
123
0
          (errmsg("archive command exited with unrecognized status %d",
124
0
              rc),
125
0
           errdetail("The failed archive command was: %s",
126
0
                 xlogarchcmd)));
127
0
    }
128
0
    pfree(xlogarchcmd);
129
130
0
    return false;
131
0
  }
132
0
  pfree(xlogarchcmd);
133
134
0
  elog(DEBUG1, "archived write-ahead log file \"%s\"", file);
135
0
  return true;
136
0
}
137
138
static void
139
shell_archive_shutdown(ArchiveModuleState *state)
140
0
{
141
0
  elog(DEBUG1, "archiver process shutting down");
142
0
}