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-2024, 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 {
21  bool skip_file;
28 
30  astreamer_member *member,
31  const char *data, int len,
33 static void astreamer_recovery_injector_finalize(astreamer *streamer);
34 static 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  */
64 astreamer *
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  */
84 static void
86  astreamer_member *member,
87  const char *data, int len,
89 {
90  astreamer_recovery_injector *mystreamer;
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)
166  astreamer_inject_file(mystreamer->base.bbs_next,
167  "postgresql.auto.conf",
168  mystreamer->recoveryconfcontents->data,
169  mystreamer->recoveryconfcontents->len);
170 
171  /* Inject empty standby.signal file. */
172  astreamer_inject_file(mystreamer->base.bbs_next,
173  "standby.signal", "", 0);
174  }
175  else
176  {
177  /* Inject recovery.conf file with specified contents. */
178  astreamer_inject_file(mystreamer->base.bbs_next,
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  */
199 static void
201 {
202  astreamer_finalize(streamer->bbs_next);
203 }
204 
205 /*
206  * Free memory associated with this astreamer.
207  */
208 static 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  */
218 void
219 astreamer_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:222
#define Assert(condition)
Definition: c.h:849
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
tree context
Definition: radixtree.h:1835
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