Coverage Report

Created: 2026-09-28 06:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/archive/shell_archive.c
Line
Count
Source
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-2026, 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
#include "utils/wait_event.h"
26
27
static bool shell_archive_configured(ArchiveModuleState *state);
28
static bool shell_archive_file(ArchiveModuleState *state,
29
                 const char *file,
30
                 const char *path);
31
static void shell_archive_shutdown(ArchiveModuleState *state);
32
33
static const ArchiveModuleCallbacks shell_archive_callbacks = {
34
  .startup_cb = NULL,
35
  .check_configured_cb = shell_archive_configured,
36
  .archive_file_cb = shell_archive_file,
37
  .shutdown_cb = shell_archive_shutdown
38
};
39
40
const ArchiveModuleCallbacks *
41
shell_archive_init(void)
42
0
{
43
0
  return &shell_archive_callbacks;
44
0
}
45
46
static bool
47
shell_archive_configured(ArchiveModuleState *state)
48
0
{
49
0
  if (XLogArchiveCommand[0] != '\0')
50
0
    return true;
51
52
0
  arch_module_check_errdetail("\"%s\" is not set.",
53
0
                "archive_command");
54
  return false;
55
0
}
56
57
static bool
58
shell_archive_file(ArchiveModuleState *state, const char *file,
59
           const char *path)
60
{
61
  char     *xlogarchcmd;
62
  char     *nativePath = NULL;
63
  int     rc;
64
65
  if (path)
66
  {
67
    nativePath = pstrdup(path);
68
    make_native_path(nativePath);
69
  }
70
71
  xlogarchcmd = replace_percent_placeholders(XLogArchiveCommand,
72
                         "archive_command", "fp",
73
                         file, nativePath);
74
75
  ereport(DEBUG3,
76
      (errmsg_internal("executing archive command \"%s\"",
77
               xlogarchcmd)));
78
79
  fflush(NULL);
80
  pgstat_report_wait_start(WAIT_EVENT_ARCHIVE_COMMAND);
81
  rc = system(xlogarchcmd);
82
  pgstat_report_wait_end();
83
84
  if (rc != 0)
85
  {
86
    /*
87
     * If either the shell itself, or a called command, died on a signal,
88
     * abort the archiver.  We do this because system() ignores SIGINT and
89
     * SIGQUIT while waiting; so a signal is very likely something that
90
     * should have interrupted us too.  Also die if the shell got a hard
91
     * "command not found" type of error.  If we overreact it's no big
92
     * deal, the postmaster will just start the archiver again.
93
     */
94
    int     lev = wait_result_is_any_signal(rc, true) ? FATAL : LOG;
95
96
    if (WIFEXITED(rc))
97
    {
98
      ereport(lev,
99
          (errmsg("archive command failed with exit code %d",
100
              WEXITSTATUS(rc)),
101
           errdetail("The failed archive command was: %s",
102
                 xlogarchcmd)));
103
    }
104
    else if (WIFSIGNALED(rc))
105
    {
106
#if defined(WIN32)
107
      ereport(lev,
108
          (errmsg("archive command was terminated by exception 0x%X",
109
              WTERMSIG(rc)),
110
           errhint("See C include file \"ntstatus.h\" for a description of the hexadecimal value."),
111
           errdetail("The failed archive command was: %s",
112
                 xlogarchcmd)));
113
#else
114
      ereport(lev,
115
          (errmsg("archive command was terminated by signal %d: %s",
116
              WTERMSIG(rc), pg_strsignal(WTERMSIG(rc))),
117
           errdetail("The failed archive command was: %s",
118
                 xlogarchcmd)));
119
#endif
120
    }
121
    else
122
    {
123
      ereport(lev,
124
          (errmsg("archive command exited with unrecognized status %d",
125
              rc),
126
           errdetail("The failed archive command was: %s",
127
                 xlogarchcmd)));
128
    }
129
    pfree(xlogarchcmd);
130
131
    return false;
132
  }
133
  pfree(xlogarchcmd);
134
135
  elog(DEBUG1, "archived write-ahead log file \"%s\"", file);
136
  return true;
137
}
138
139
static void
140
shell_archive_shutdown(ArchiveModuleState *state)
141
{
142
  elog(DEBUG1, "archiver process shutting down");
143
}