PostgreSQL Source Code  git master
bbstreamer_inject.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * bbstreamer_inject.c
4  *
5  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
6  *
7  * IDENTIFICATION
8  * src/bin/pg_basebackup/bbstreamer_inject.c
9  *-------------------------------------------------------------------------
10  */
11 
12 #include "postgres_fe.h"
13 
14 #include "bbstreamer.h"
15 #include "common/file_perm.h"
16 #include "common/logging.h"
17 
19 {
21  bool skip_file;
28 
30  bbstreamer_member *member,
31  const char *data, int len,
34 static void bbstreamer_recovery_injector_free(bbstreamer *streamer);
35 
40 };
41 
42 /*
43  * Create a bbstreamer that can edit recoverydata into an archive stream.
44  *
45  * The input should be a series of typed chunks (not BBSTREAMER_UNKNOWN) as
46  * per the conventions described in bbstreamer.h; the chunks forwarded to
47  * the next bbstreamer will be similarly typed, but the
48  * BBSTREAMER_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 extern bbstreamer *
66  bool is_recovery_guc_supported,
68 {
70 
71  streamer = palloc0(sizeof(bbstreamer_recovery_injector));
72  *((const bbstreamer_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  bbstreamer_member *member,
87  const char *data, int len,
89 {
90  bbstreamer_recovery_injector *mystreamer;
91 
92  mystreamer = (bbstreamer_recovery_injector *) streamer;
93  Assert(member != NULL || context == BBSTREAMER_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(bbstreamer_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 bbstreamer must
123  * regenerate 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  bbstreamer_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  bbstreamer_content(mystreamer->base.bbs_next, &mystreamer->member,
193  data, len, context);
194 }
195 
196 /*
197  * End-of-stream processing for this bbstreamer.
198  */
199 static void
201 {
202  bbstreamer_finalize(streamer->bbs_next);
203 }
204 
205 /*
206  * Free memory associated with this bbstreamer.
207  */
208 static void
210 {
211  bbstreamer_free(streamer->bbs_next);
212  pfree(streamer);
213 }
214 
215 /*
216  * Inject a member into the archive with specified contents.
217  */
218 void
219 bbstreamer_inject_file(bbstreamer *streamer, char *pathname, char *data,
220  int len)
221 {
222  bbstreamer_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  * bbstreamer will have to generate them using the data from 'member'.
242  */
243  bbstreamer_content(streamer, &member, NULL, 0,
245  bbstreamer_content(streamer, &member, data, len,
247  bbstreamer_content(streamer, &member, NULL, 0,
249 }
static void bbstreamer_content(bbstreamer *streamer, bbstreamer_member *member, const char *data, int len, bbstreamer_archive_context context)
Definition: bbstreamer.h:126
static void bbstreamer_finalize(bbstreamer *streamer)
Definition: bbstreamer.h:136
bbstreamer_archive_context
Definition: bbstreamer.h:54
@ BBSTREAMER_ARCHIVE_TRAILER
Definition: bbstreamer.h:59
@ BBSTREAMER_MEMBER_HEADER
Definition: bbstreamer.h:56
@ BBSTREAMER_MEMBER_TRAILER
Definition: bbstreamer.h:58
@ BBSTREAMER_MEMBER_CONTENTS
Definition: bbstreamer.h:57
static void bbstreamer_free(bbstreamer *streamer)
Definition: bbstreamer.h:144
const bbstreamer_ops bbstreamer_recovery_injector_ops
static void bbstreamer_recovery_injector_finalize(bbstreamer *streamer)
bbstreamer * bbstreamer_recovery_injector_new(bbstreamer *next, bool is_recovery_guc_supported, PQExpBuffer recoveryconfcontents)
static void bbstreamer_recovery_injector_free(bbstreamer *streamer)
struct bbstreamer_recovery_injector bbstreamer_recovery_injector
void bbstreamer_inject_file(bbstreamer *streamer, char *pathname, char *data, int len)
static void bbstreamer_recovery_injector_content(bbstreamer *streamer, bbstreamer_member *member, const char *data, int len, bbstreamer_archive_context context)
static int32 next
Definition: blutils.c:221
#define Assert(condition)
Definition: c.h:858
int pg_file_create_mode
Definition: file_perm.c:19
void pfree(void *pointer)
Definition: mcxt.c:1520
void * palloc0(Size size)
Definition: mcxt.c:1346
#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:1829
char pathname[MAXPGPATH]
Definition: bbstreamer.h:72
char linktarget[MAXPGPATH]
Definition: bbstreamer.h:79
void(* content)(bbstreamer *streamer, bbstreamer_member *member, const char *data, int len, bbstreamer_archive_context context)
Definition: bbstreamer.h:117
const bbstreamer_ops * bbs_ops
Definition: bbstreamer.h:100
bbstreamer * bbs_next
Definition: bbstreamer.h:101