PostgreSQL Source Code git master
astreamer_inject.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * astreamer_inject.c
4 *
5 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
6 *
7 * IDENTIFICATION
8 * src/bin/pg_basebackup/astreamer_inject.c
9 *-------------------------------------------------------------------------
10 */
11
12#include "postgres_fe.h"
13
14#include "astreamer_inject.h"
15#include "common/file_perm.h"
16#include "common/logging.h"
17
19{
28
30 astreamer_member *member,
31 const char *data, int len,
34static void astreamer_recovery_injector_free(astreamer *streamer);
35
40};
41
42/*
43 * Create a astreamer that can edit recoverydata into an archive stream.
44 *
45 * The input should be a series of typed chunks (not ASTREAMER_UNKNOWN) as
46 * per the conventions described in astreamer.h; the chunks forwarded to
47 * the next astreamer will be similarly typed, but the
48 * ASTREAMER_MEMBER_HEADER chunks may be zero-length in cases where we've
49 * edited the archive stream.
50 *
51 * Our goal is to do one of the following three things with the content passed
52 * via recoveryconfcontents: (1) if is_recovery_guc_supported is false, then
53 * put the content into recovery.conf, replacing any existing archive member
54 * by that name; (2) if is_recovery_guc_supported is true and
55 * postgresql.auto.conf exists in the archive, then append the content
56 * provided to the existing file; and (3) if is_recovery_guc_supported is
57 * true but postgresql.auto.conf does not exist in the archive, then create
58 * it with the specified content.
59 *
60 * In addition, if is_recovery_guc_supported is true, then we create a
61 * zero-length standby.signal file, dropping any file with that name from
62 * the archive.
63 */
66 bool is_recovery_guc_supported,
68{
70
71 streamer = palloc0(sizeof(astreamer_recovery_injector));
72 *((const astreamer_ops **) &streamer->base.bbs_ops) =
74 streamer->base.bbs_next = next;
75 streamer->is_recovery_guc_supported = is_recovery_guc_supported;
77
78 return &streamer->base;
79}
80
81/*
82 * Handle each chunk of tar content while injecting recovery configuration.
83 */
84static void
86 astreamer_member *member,
87 const char *data, int len,
89{
91
92 mystreamer = (astreamer_recovery_injector *) streamer;
93 Assert(member != NULL || context == ASTREAMER_ARCHIVE_TRAILER);
94
95 switch (context)
96 {
98 /* Must copy provided data so we have the option to modify it. */
99 memcpy(&mystreamer->member, member, sizeof(astreamer_member));
100
101 /*
102 * On v12+, skip standby.signal and edit postgresql.auto.conf; on
103 * older versions, skip recovery.conf.
104 */
105 if (mystreamer->is_recovery_guc_supported)
106 {
107 mystreamer->skip_file =
108 (strcmp(member->pathname, "standby.signal") == 0);
109 mystreamer->is_postgresql_auto_conf =
110 (strcmp(member->pathname, "postgresql.auto.conf") == 0);
111 if (mystreamer->is_postgresql_auto_conf)
112 {
113 /* Remember we saw it so we don't add it again. */
114 mystreamer->found_postgresql_auto_conf = true;
115
116 /* Increment length by data to be injected. */
117 mystreamer->member.size +=
118 mystreamer->recoveryconfcontents->len;
119
120 /*
121 * Zap data and len because the archive header is no
122 * longer valid; some subsequent astreamer must regenerate
123 * it if it's necessary.
124 */
125 data = NULL;
126 len = 0;
127 }
128 }
129 else
130 mystreamer->skip_file =
131 (strcmp(member->pathname, "recovery.conf") == 0);
132
133 /* Do not forward if the file is to be skipped. */
134 if (mystreamer->skip_file)
135 return;
136 break;
137
139 /* Do not forward if the file is to be skipped. */
140 if (mystreamer->skip_file)
141 return;
142 break;
143
145 /* Do not forward it the file is to be skipped. */
146 if (mystreamer->skip_file)
147 return;
148
149 /* Append provided content to whatever we already sent. */
150 if (mystreamer->is_postgresql_auto_conf)
151 astreamer_content(mystreamer->base.bbs_next, member,
152 mystreamer->recoveryconfcontents->data,
153 mystreamer->recoveryconfcontents->len,
155 break;
156
158 if (mystreamer->is_recovery_guc_supported)
159 {
160 /*
161 * If we didn't already find (and thus modify)
162 * postgresql.auto.conf, inject it as an additional archive
163 * member now.
164 */
165 if (!mystreamer->found_postgresql_auto_conf)
167 "postgresql.auto.conf",
168 mystreamer->recoveryconfcontents->data,
169 mystreamer->recoveryconfcontents->len);
170
171 /* Inject empty standby.signal file. */
173 "standby.signal", "", 0);
174 }
175 else
176 {
177 /* Inject recovery.conf file with specified contents. */
179 "recovery.conf",
180 mystreamer->recoveryconfcontents->data,
181 mystreamer->recoveryconfcontents->len);
182 }
183
184 /* Nothing to do here. */
185 break;
186
187 default:
188 /* Shouldn't happen. */
189 pg_fatal("unexpected state while injecting recovery settings");
190 }
191
192 astreamer_content(mystreamer->base.bbs_next, &mystreamer->member,
193 data, len, context);
194}
195
196/*
197 * End-of-stream processing for this astreamer.
198 */
199static void
201{
202 astreamer_finalize(streamer->bbs_next);
203}
204
205/*
206 * Free memory associated with this astreamer.
207 */
208static void
210{
211 astreamer_free(streamer->bbs_next);
212 pfree(streamer);
213}
214
215/*
216 * Inject a member into the archive with specified contents.
217 */
218void
219astreamer_inject_file(astreamer *streamer, char *pathname, char *data,
220 int len)
221{
222 astreamer_member member;
223
224 strlcpy(member.pathname, pathname, MAXPGPATH);
225 member.size = len;
226 member.mode = pg_file_create_mode;
227 member.is_directory = false;
228 member.is_link = false;
229 member.linktarget[0] = '\0';
230
231 /*
232 * There seems to be no principled argument for these values, but they are
233 * what PostgreSQL has historically used.
234 */
235 member.uid = 04000;
236 member.gid = 02000;
237
238 /*
239 * We don't know here how to generate valid member headers and trailers
240 * for the archiving format in use, so if those are needed, some successor
241 * astreamer will have to generate them using the data from 'member'.
242 */
243 astreamer_content(streamer, &member, NULL, 0,
245 astreamer_content(streamer, &member, data, len,
247 astreamer_content(streamer, &member, NULL, 0,
249}
static void astreamer_free(astreamer *streamer)
Definition: astreamer.h:153
static void astreamer_content(astreamer *streamer, astreamer_member *member, const char *data, int len, astreamer_archive_context context)
Definition: astreamer.h:135
static void astreamer_finalize(astreamer *streamer)
Definition: astreamer.h:145
astreamer_archive_context
Definition: astreamer.h:63
@ ASTREAMER_MEMBER_HEADER
Definition: astreamer.h:65
@ ASTREAMER_MEMBER_CONTENTS
Definition: astreamer.h:66
@ ASTREAMER_MEMBER_TRAILER
Definition: astreamer.h:67
@ ASTREAMER_ARCHIVE_TRAILER
Definition: astreamer.h:68
static void astreamer_recovery_injector_content(astreamer *streamer, astreamer_member *member, const char *data, int len, astreamer_archive_context context)
astreamer * astreamer_recovery_injector_new(astreamer *next, bool is_recovery_guc_supported, PQExpBuffer recoveryconfcontents)
struct astreamer_recovery_injector astreamer_recovery_injector
static void astreamer_recovery_injector_finalize(astreamer *streamer)
static void astreamer_recovery_injector_free(astreamer *streamer)
static const astreamer_ops astreamer_recovery_injector_ops
void astreamer_inject_file(astreamer *streamer, char *pathname, char *data, int len)
static int32 next
Definition: blutils.c:219
#define Assert(condition)
Definition: c.h:815
int pg_file_create_mode
Definition: file_perm.c:19
void pfree(void *pointer)
Definition: mcxt.c:1521
void * palloc0(Size size)
Definition: mcxt.c:1347
#define pg_fatal(...)
static PQExpBuffer recoveryconfcontents
#define MAXPGPATH
const void size_t len
const void * data
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
char linktarget[MAXPGPATH]
Definition: astreamer.h:88
char pathname[MAXPGPATH]
Definition: astreamer.h:81
pgoff_t size
Definition: astreamer.h:82
void(* content)(astreamer *streamer, astreamer_member *member, const char *data, int len, astreamer_archive_context context)
Definition: astreamer.h:126
const astreamer_ops * bbs_ops
Definition: astreamer.h:109
astreamer * bbs_next
Definition: astreamer.h:110