Line data Source code
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 16 : shell_archive_init(void)
42 : {
43 16 : return &shell_archive_callbacks;
44 : }
45 :
46 : static bool
47 374 : shell_archive_configured(ArchiveModuleState *state)
48 : {
49 374 : if (XLogArchiveCommand[0] != '\0')
50 371 : return true;
51 :
52 3 : arch_module_check_errdetail("\"%s\" is not set.",
53 : "archive_command");
54 3 : return false;
55 : }
56 :
57 : static bool
58 371 : shell_archive_file(ArchiveModuleState *state, const char *file,
59 : const char *path)
60 : {
61 : char *xlogarchcmd;
62 371 : char *nativePath = NULL;
63 : int rc;
64 :
65 371 : if (path)
66 : {
67 371 : nativePath = pstrdup(path);
68 371 : make_native_path(nativePath);
69 : }
70 :
71 371 : xlogarchcmd = replace_percent_placeholders(XLogArchiveCommand,
72 : "archive_command", "fp",
73 : file, nativePath);
74 :
75 371 : ereport(DEBUG3,
76 : (errmsg_internal("executing archive command \"%s\"",
77 : xlogarchcmd)));
78 :
79 371 : fflush(NULL);
80 371 : pgstat_report_wait_start(WAIT_EVENT_ARCHIVE_COMMAND);
81 371 : rc = system(xlogarchcmd);
82 371 : pgstat_report_wait_end();
83 :
84 371 : 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 7 : int lev = wait_result_is_any_signal(rc, true) ? FATAL : LOG;
95 :
96 7 : if (WIFEXITED(rc))
97 : {
98 7 : 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 0 : 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 0 : 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 0 : 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 7 : pfree(xlogarchcmd);
130 :
131 7 : return false;
132 : }
133 364 : pfree(xlogarchcmd);
134 :
135 364 : elog(DEBUG1, "archived write-ahead log file \"%s\"", file);
136 364 : return true;
137 : }
138 :
139 : static void
140 16 : shell_archive_shutdown(ArchiveModuleState *state)
141 : {
142 16 : elog(DEBUG1, "archiver process shutting down");
143 16 : }
|