Age Owner Branch data TLA Line data Source code
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 : :
18 : : typedef struct bbstreamer_recovery_injector
19 : : {
20 : : bbstreamer base;
21 : : bool skip_file;
22 : : bool is_recovery_guc_supported;
23 : : bool is_postgresql_auto_conf;
24 : : bool found_postgresql_auto_conf;
25 : : PQExpBuffer recoveryconfcontents;
26 : : bbstreamer_member member;
27 : : } bbstreamer_recovery_injector;
28 : :
29 : : static void bbstreamer_recovery_injector_content(bbstreamer *streamer,
30 : : bbstreamer_member *member,
31 : : const char *data, int len,
32 : : bbstreamer_archive_context context);
33 : : static void bbstreamer_recovery_injector_finalize(bbstreamer *streamer);
34 : : static void bbstreamer_recovery_injector_free(bbstreamer *streamer);
35 : :
36 : : const bbstreamer_ops bbstreamer_recovery_injector_ops = {
37 : : .content = bbstreamer_recovery_injector_content,
38 : : .finalize = bbstreamer_recovery_injector_finalize,
39 : : .free = bbstreamer_recovery_injector_free
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 *
891 rhaas@postgresql.org 65 :CBC 3 : bbstreamer_recovery_injector_new(bbstreamer *next,
66 : : bool is_recovery_guc_supported,
67 : : PQExpBuffer recoveryconfcontents)
68 : : {
69 : : bbstreamer_recovery_injector *streamer;
70 : :
71 : 3 : streamer = palloc0(sizeof(bbstreamer_recovery_injector));
72 : 3 : *((const bbstreamer_ops **) &streamer->base.bbs_ops) =
73 : : &bbstreamer_recovery_injector_ops;
74 : 3 : streamer->base.bbs_next = next;
75 : 3 : streamer->is_recovery_guc_supported = is_recovery_guc_supported;
76 : 3 : streamer->recoveryconfcontents = recoveryconfcontents;
77 : :
78 : 3 : return &streamer->base;
79 : : }
80 : :
81 : : /*
82 : : * Handle each chunk of tar content while injecting recovery configuration.
83 : : */
84 : : static void
85 : 9498 : bbstreamer_recovery_injector_content(bbstreamer *streamer,
86 : : bbstreamer_member *member,
87 : : const char *data, int len,
88 : : bbstreamer_archive_context context)
89 : : {
90 : : bbstreamer_recovery_injector *mystreamer;
91 : :
92 : 9498 : mystreamer = (bbstreamer_recovery_injector *) streamer;
93 [ + + - + ]: 9498 : Assert(member != NULL || context == BBSTREAMER_ARCHIVE_TRAILER);
94 : :
95 [ + + + + : 9498 : switch (context)
- ]
96 : : {
97 : 2982 : case BBSTREAMER_MEMBER_HEADER:
98 : : /* Must copy provided data so we have the option to modify it. */
99 : 2982 : 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 [ + - ]: 2982 : if (mystreamer->is_recovery_guc_supported)
106 : : {
107 : 2982 : mystreamer->skip_file =
108 : 2982 : (strcmp(member->pathname, "standby.signal") == 0);
109 : 2982 : mystreamer->is_postgresql_auto_conf =
110 : 2982 : (strcmp(member->pathname, "postgresql.auto.conf") == 0);
111 [ + + ]: 2982 : if (mystreamer->is_postgresql_auto_conf)
112 : : {
113 : : /* Remember we saw it so we don't add it again. */
114 : 3 : mystreamer->found_postgresql_auto_conf = true;
115 : :
116 : : /* Increment length by data to be injected. */
117 : 3 : mystreamer->member.size +=
118 : 3 : 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 : 3 : data = NULL;
126 : 3 : len = 0;
127 : : }
128 : : }
129 : : else
891 rhaas@postgresql.org 130 :UBC 0 : mystreamer->skip_file =
131 : 0 : (strcmp(member->pathname, "recovery.conf") == 0);
132 : :
133 : : /* Do not forward if the file is to be skipped. */
891 rhaas@postgresql.org 134 [ - + ]:CBC 2982 : if (mystreamer->skip_file)
891 rhaas@postgresql.org 135 :UBC 0 : return;
891 rhaas@postgresql.org 136 :CBC 2982 : break;
137 : :
138 : 3531 : case BBSTREAMER_MEMBER_CONTENTS:
139 : : /* Do not forward if the file is to be skipped. */
140 [ - + ]: 3531 : if (mystreamer->skip_file)
891 rhaas@postgresql.org 141 :UBC 0 : return;
891 rhaas@postgresql.org 142 :CBC 3531 : break;
143 : :
144 : 2982 : case BBSTREAMER_MEMBER_TRAILER:
145 : : /* Do not forward it the file is to be skipped. */
146 [ - + ]: 2982 : if (mystreamer->skip_file)
891 rhaas@postgresql.org 147 :UBC 0 : return;
148 : :
149 : : /* Append provided content to whatever we already sent. */
891 rhaas@postgresql.org 150 [ + + ]:CBC 2982 : if (mystreamer->is_postgresql_auto_conf)
151 : 3 : bbstreamer_content(mystreamer->base.bbs_next, member,
152 : 3 : mystreamer->recoveryconfcontents->data,
153 : 3 : mystreamer->recoveryconfcontents->len,
154 : : BBSTREAMER_MEMBER_CONTENTS);
155 : 2982 : break;
156 : :
157 : 3 : case BBSTREAMER_ARCHIVE_TRAILER:
158 [ + - ]: 3 : 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 [ - + ]: 3 : if (!mystreamer->found_postgresql_auto_conf)
891 rhaas@postgresql.org 166 :UBC 0 : bbstreamer_inject_file(mystreamer->base.bbs_next,
167 : : "postgresql.auto.conf",
168 : 0 : mystreamer->recoveryconfcontents->data,
169 : 0 : mystreamer->recoveryconfcontents->len);
170 : :
171 : : /* Inject empty standby.signal file. */
891 rhaas@postgresql.org 172 :CBC 3 : bbstreamer_inject_file(mystreamer->base.bbs_next,
173 : : "standby.signal", "", 0);
174 : : }
175 : : else
176 : : {
177 : : /* Inject recovery.conf file with specified contents. */
891 rhaas@postgresql.org 178 :UBC 0 : bbstreamer_inject_file(mystreamer->base.bbs_next,
179 : : "recovery.conf",
180 : 0 : mystreamer->recoveryconfcontents->data,
181 : 0 : mystreamer->recoveryconfcontents->len);
182 : : }
183 : :
184 : : /* Nothing to do here. */
891 rhaas@postgresql.org 185 :CBC 3 : break;
186 : :
891 rhaas@postgresql.org 187 :UBC 0 : default:
188 : : /* Shouldn't happen. */
737 tgl@sss.pgh.pa.us 189 : 0 : pg_fatal("unexpected state while injecting recovery settings");
190 : : }
191 : :
891 rhaas@postgresql.org 192 :CBC 9498 : 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
200 : 3 : bbstreamer_recovery_injector_finalize(bbstreamer *streamer)
201 : : {
202 : 3 : bbstreamer_finalize(streamer->bbs_next);
203 : 3 : }
204 : :
205 : : /*
206 : : * Free memory associated with this bbstreamer.
207 : : */
208 : : static void
209 : 3 : bbstreamer_recovery_injector_free(bbstreamer *streamer)
210 : : {
211 : 3 : bbstreamer_free(streamer->bbs_next);
212 : 3 : pfree(streamer);
213 : 3 : }
214 : :
215 : : /*
216 : : * Inject a member into the archive with specified contents.
217 : : */
218 : : void
219 : 3 : bbstreamer_inject_file(bbstreamer *streamer, char *pathname, char *data,
220 : : int len)
221 : : {
222 : : bbstreamer_member member;
223 : :
224 : 3 : strlcpy(member.pathname, pathname, MAXPGPATH);
225 : 3 : member.size = len;
226 : 3 : member.mode = pg_file_create_mode;
227 : 3 : member.is_directory = false;
228 : 3 : member.is_link = false;
229 : 3 : 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 : 3 : member.uid = 04000;
236 : 3 : 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 : 3 : bbstreamer_content(streamer, &member, NULL, 0,
244 : : BBSTREAMER_MEMBER_HEADER);
245 : 3 : bbstreamer_content(streamer, &member, data, len,
246 : : BBSTREAMER_MEMBER_CONTENTS);
247 : 3 : bbstreamer_content(streamer, &member, NULL, 0,
248 : : BBSTREAMER_MEMBER_TRAILER);
249 : 3 : }
|