Repair memory leaks in plpython.
[pgsql.git] / contrib / basebackup_to_shell / basebackup_to_shell.c
blobd91366b06d23ac24e2f11f51429c53dfdbcc4563
1 /*-------------------------------------------------------------------------
3 * basebackup_to_shell.c
4 * target base backup files to a shell command
6 * Copyright (c) 2016-2025, PostgreSQL Global Development Group
8 * contrib/basebackup_to_shell/basebackup_to_shell.c
9 *-------------------------------------------------------------------------
11 #include "postgres.h"
13 #include "access/xact.h"
14 #include "backup/basebackup_target.h"
15 #include "common/percentrepl.h"
16 #include "miscadmin.h"
17 #include "storage/fd.h"
18 #include "utils/acl.h"
19 #include "utils/guc.h"
21 PG_MODULE_MAGIC;
23 typedef struct bbsink_shell
25 /* Common information for all types of sink. */
26 bbsink base;
28 /* User-supplied target detail string. */
29 char *target_detail;
31 /* Shell command pattern being used for this backup. */
32 char *shell_command;
34 /* The command that is currently running. */
35 char *current_command;
37 /* Pipe to the running command. */
38 FILE *pipe;
39 } bbsink_shell;
41 static void *shell_check_detail(char *target, char *target_detail);
42 static bbsink *shell_get_sink(bbsink *next_sink, void *detail_arg);
44 static void bbsink_shell_begin_archive(bbsink *sink,
45 const char *archive_name);
46 static void bbsink_shell_archive_contents(bbsink *sink, size_t len);
47 static void bbsink_shell_end_archive(bbsink *sink);
48 static void bbsink_shell_begin_manifest(bbsink *sink);
49 static void bbsink_shell_manifest_contents(bbsink *sink, size_t len);
50 static void bbsink_shell_end_manifest(bbsink *sink);
52 static const bbsink_ops bbsink_shell_ops = {
53 .begin_backup = bbsink_forward_begin_backup,
54 .begin_archive = bbsink_shell_begin_archive,
55 .archive_contents = bbsink_shell_archive_contents,
56 .end_archive = bbsink_shell_end_archive,
57 .begin_manifest = bbsink_shell_begin_manifest,
58 .manifest_contents = bbsink_shell_manifest_contents,
59 .end_manifest = bbsink_shell_end_manifest,
60 .end_backup = bbsink_forward_end_backup,
61 .cleanup = bbsink_forward_cleanup
64 static char *shell_command = "";
65 static char *shell_required_role = "";
67 void
68 _PG_init(void)
70 DefineCustomStringVariable("basebackup_to_shell.command",
71 "Shell command to be executed for each backup file.",
72 NULL,
73 &shell_command,
74 "",
75 PGC_SIGHUP,
77 NULL, NULL, NULL);
79 DefineCustomStringVariable("basebackup_to_shell.required_role",
80 "Backup user must be a member of this role to use shell backup target.",
81 NULL,
82 &shell_required_role,
83 "",
84 PGC_SIGHUP,
86 NULL, NULL, NULL);
88 MarkGUCPrefixReserved("basebackup_to_shell");
90 BaseBackupAddTarget("shell", shell_check_detail, shell_get_sink);
94 * We choose to defer sanity checking until shell_get_sink(), and so
95 * just pass the target detail through without doing anything. However, we do
96 * permissions checks here, before any real work has been done.
98 static void *
99 shell_check_detail(char *target, char *target_detail)
101 if (shell_required_role[0] != '\0')
103 Oid roleid;
105 StartTransactionCommand();
106 roleid = get_role_oid(shell_required_role, true);
107 if (!has_privs_of_role(GetUserId(), roleid))
108 ereport(ERROR,
109 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
110 errmsg("permission denied to use basebackup_to_shell")));
111 CommitTransactionCommand();
114 return target_detail;
118 * Set up a bbsink to implement this base backup target.
120 * This is also a convenient place to sanity check that a target detail was
121 * given if and only if %d is present.
123 static bbsink *
124 shell_get_sink(bbsink *next_sink, void *detail_arg)
126 bbsink_shell *sink;
127 bool has_detail_escape = false;
128 char *c;
131 * Set up the bbsink.
133 * We remember the current value of basebackup_to_shell.shell_command to
134 * be certain that it can't change under us during the backup.
136 sink = palloc0(sizeof(bbsink_shell));
137 *((const bbsink_ops **) &sink->base.bbs_ops) = &bbsink_shell_ops;
138 sink->base.bbs_next = next_sink;
139 sink->target_detail = detail_arg;
140 sink->shell_command = pstrdup(shell_command);
142 /* Reject an empty shell command. */
143 if (sink->shell_command[0] == '\0')
144 ereport(ERROR,
145 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
146 errmsg("shell command for backup is not configured"));
148 /* Determine whether the shell command we're using contains %d. */
149 for (c = sink->shell_command; *c != '\0'; ++c)
151 if (c[0] == '%' && c[1] != '\0')
153 if (c[1] == 'd')
154 has_detail_escape = true;
155 ++c;
159 /* There should be a target detail if %d was used, and not otherwise. */
160 if (has_detail_escape && sink->target_detail == NULL)
161 ereport(ERROR,
162 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
163 errmsg("a target detail is required because the configured command includes %%d"),
164 errhint("Try \"pg_basebackup --target shell:DETAIL ...\"")));
165 else if (!has_detail_escape && sink->target_detail != NULL)
166 ereport(ERROR,
167 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
168 errmsg("a target detail is not permitted because the configured command does not include %%d")));
171 * Since we're passing the string provided by the user to popen(), it will
172 * be interpreted by the shell, which is a potential security
173 * vulnerability, since the user invoking this module is not necessarily a
174 * superuser. To stay out of trouble, we must disallow any shell
175 * metacharacters here; to be conservative and keep things simple, we
176 * allow only alphanumerics.
178 if (sink->target_detail != NULL)
180 char *d;
181 bool scary = false;
183 for (d = sink->target_detail; *d != '\0'; ++d)
185 if (*d >= 'a' && *d <= 'z')
186 continue;
187 if (*d >= 'A' && *d <= 'Z')
188 continue;
189 if (*d >= '0' && *d <= '9')
190 continue;
191 scary = true;
192 break;
195 if (scary)
196 ereport(ERROR,
197 errcode(ERRCODE_INVALID_PARAMETER_VALUE),
198 errmsg("target detail must contain only alphanumeric characters"));
201 return &sink->base;
205 * Construct the exact shell command that we're actually going to run,
206 * making substitutions as appropriate for escape sequences.
208 static char *
209 shell_construct_command(const char *base_command, const char *filename,
210 const char *target_detail)
212 return replace_percent_placeholders(base_command, "basebackup_to_shell.command",
213 "df", target_detail, filename);
217 * Finish executing the shell command once all data has been written.
219 static void
220 shell_finish_command(bbsink_shell *sink)
222 int pclose_rc;
224 /* There should be a command running. */
225 Assert(sink->current_command != NULL);
226 Assert(sink->pipe != NULL);
228 /* Close down the pipe we opened. */
229 pclose_rc = ClosePipeStream(sink->pipe);
230 if (pclose_rc == -1)
231 ereport(ERROR,
232 (errcode_for_file_access(),
233 errmsg("could not close pipe to external command: %m")));
234 else if (pclose_rc != 0)
236 ereport(ERROR,
237 (errcode(ERRCODE_EXTERNAL_ROUTINE_EXCEPTION),
238 errmsg("shell command \"%s\" failed",
239 sink->current_command),
240 errdetail_internal("%s", wait_result_to_str(pclose_rc))));
243 /* Clean up. */
244 sink->pipe = NULL;
245 pfree(sink->current_command);
246 sink->current_command = NULL;
250 * Start up the shell command, substituting %f in for the current filename.
252 static void
253 shell_run_command(bbsink_shell *sink, const char *filename)
255 /* There should not be anything already running. */
256 Assert(sink->current_command == NULL);
257 Assert(sink->pipe == NULL);
259 /* Construct a suitable command. */
260 sink->current_command = shell_construct_command(sink->shell_command,
261 filename,
262 sink->target_detail);
264 /* Run it. */
265 sink->pipe = OpenPipeStream(sink->current_command, PG_BINARY_W);
266 if (sink->pipe == NULL)
267 ereport(ERROR,
268 (errcode_for_file_access(),
269 errmsg("could not execute command \"%s\": %m",
270 sink->current_command)));
274 * Send accumulated data to the running shell command.
276 static void
277 shell_send_data(bbsink_shell *sink, size_t len)
279 /* There should be a command running. */
280 Assert(sink->current_command != NULL);
281 Assert(sink->pipe != NULL);
283 /* Try to write the data. */
284 if (fwrite(sink->base.bbs_buffer, len, 1, sink->pipe) != 1 ||
285 ferror(sink->pipe))
287 if (errno == EPIPE)
290 * The error we're about to throw would shut down the command
291 * anyway, but we may get a more meaningful error message by doing
292 * this. If not, we'll fall through to the generic error below.
294 shell_finish_command(sink);
295 errno = EPIPE;
297 ereport(ERROR,
298 (errcode_for_file_access(),
299 errmsg("could not write to shell backup program: %m")));
304 * At start of archive, start up the shell command and forward to next sink.
306 static void
307 bbsink_shell_begin_archive(bbsink *sink, const char *archive_name)
309 bbsink_shell *mysink = (bbsink_shell *) sink;
311 shell_run_command(mysink, archive_name);
312 bbsink_forward_begin_archive(sink, archive_name);
316 * Send archive contents to command's stdin and forward to next sink.
318 static void
319 bbsink_shell_archive_contents(bbsink *sink, size_t len)
321 bbsink_shell *mysink = (bbsink_shell *) sink;
323 shell_send_data(mysink, len);
324 bbsink_forward_archive_contents(sink, len);
328 * At end of archive, shut down the shell command and forward to next sink.
330 static void
331 bbsink_shell_end_archive(bbsink *sink)
333 bbsink_shell *mysink = (bbsink_shell *) sink;
335 shell_finish_command(mysink);
336 bbsink_forward_end_archive(sink);
340 * At start of manifest, start up the shell command and forward to next sink.
342 static void
343 bbsink_shell_begin_manifest(bbsink *sink)
345 bbsink_shell *mysink = (bbsink_shell *) sink;
347 shell_run_command(mysink, "backup_manifest");
348 bbsink_forward_begin_manifest(sink);
352 * Send manifest contents to command's stdin and forward to next sink.
354 static void
355 bbsink_shell_manifest_contents(bbsink *sink, size_t len)
357 bbsink_shell *mysink = (bbsink_shell *) sink;
359 shell_send_data(mysink, len);
360 bbsink_forward_manifest_contents(sink, len);
364 * At end of manifest, shut down the shell command and forward to next sink.
366 static void
367 bbsink_shell_end_manifest(bbsink *sink)
369 bbsink_shell *mysink = (bbsink_shell *) sink;
371 shell_finish_command(mysink);
372 bbsink_forward_end_manifest(sink);