#include "access/xact.h"
#include "backup/basebackup_target.h"
+#include "common/percentrepl.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "utils/acl.h"
shell_construct_command(const char *base_command, const char *filename,
const char *target_detail)
{
- StringInfoData buf;
- const char *c;
-
- initStringInfo(&buf);
- for (c = base_command; *c != '\0'; ++c)
- {
- /* Anything other than '%' is copied verbatim. */
- if (*c != '%')
- {
- appendStringInfoChar(&buf, *c);
- continue;
- }
-
- /* Any time we see '%' we eat the following character as well. */
- ++c;
-
- /*
- * The following character determines what we insert here, or may
- * cause us to throw an error.
- */
- if (*c == '%')
- {
- /* '%%' is replaced by a single '%' */
- appendStringInfoChar(&buf, '%');
- }
- else if (*c == 'f')
- {
- /* '%f' is replaced by the filename */
- appendStringInfoString(&buf, filename);
- }
- else if (*c == 'd')
- {
- /* '%d' is replaced by the target detail */
- appendStringInfoString(&buf, target_detail);
- }
- else if (*c == '\0')
- {
- /* Incomplete escape sequence, expected a character afterward */
- ereport(ERROR,
- errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("shell command ends unexpectedly after escape character \"%%\""));
- }
- else
- {
- /* Unknown escape sequence */
- ereport(ERROR,
- errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("shell command contains unexpected escape sequence \"%c\"",
- *c));
- }
- }
-
- return buf.data;
+ return replace_percent_placeholders(base_command, "basebackup_to_shell.command",
+ "df", target_detail, filename);
}
/*
#include "access/xlog_internal.h"
#include "access/xlogarchive.h"
#include "common/archive.h"
+#include "common/percentrepl.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "postmaster/startup.h"
ExecuteRecoveryCommand(const char *command, const char *commandName,
bool failOnSignal, uint32 wait_event_info)
{
- char xlogRecoveryCmd[MAXPGPATH];
+ char *xlogRecoveryCmd;
char lastRestartPointFname[MAXPGPATH];
- char *dp;
- char *endp;
- const char *sp;
int rc;
XLogSegNo restartSegNo;
XLogRecPtr restartRedoPtr;
/*
* construct the command to be executed
*/
- dp = xlogRecoveryCmd;
- endp = xlogRecoveryCmd + MAXPGPATH - 1;
- *endp = '\0';
-
- for (sp = command; *sp; sp++)
- {
- if (*sp == '%')
- {
- switch (sp[1])
- {
- case 'r':
- /* %r: filename of last restartpoint */
- sp++;
- strlcpy(dp, lastRestartPointFname, endp - dp);
- dp += strlen(dp);
- break;
- case '%':
- /* convert %% to a single % */
- sp++;
- if (dp < endp)
- *dp++ = *sp;
- break;
- default:
- /* otherwise treat the % as not special */
- if (dp < endp)
- *dp++ = *sp;
- break;
- }
- }
- else
- {
- if (dp < endp)
- *dp++ = *sp;
- }
- }
- *dp = '\0';
+ xlogRecoveryCmd = replace_percent_placeholders(command, commandName, "r", lastRestartPointFname);
ereport(DEBUG3,
(errmsg_internal("executing %s \"%s\"", commandName, command)));
rc = system(xlogRecoveryCmd);
pgstat_report_wait_end();
+ pfree(xlogRecoveryCmd);
+
if (rc != 0)
{
/*
#include <sys/stat.h>
#include <unistd.h>
+#include "common/percentrepl.h"
#include "common/string.h"
#include "libpq/libpq.h"
#include "storage/fd.h"
run_ssl_passphrase_command(const char *prompt, bool is_server_start, char *buf, int size)
{
int loglevel = is_server_start ? ERROR : LOG;
- StringInfoData command;
- char *p;
+ char *command;
FILE *fh;
int pclose_rc;
size_t len = 0;
Assert(size > 0);
buf[0] = '\0';
- initStringInfo(&command);
+ command = replace_percent_placeholders(ssl_passphrase_command, "ssl_passphrase_command", "p", prompt);
- for (p = ssl_passphrase_command; *p; p++)
- {
- if (p[0] == '%')
- {
- switch (p[1])
- {
- case 'p':
- appendStringInfoString(&command, prompt);
- p++;
- break;
- case '%':
- appendStringInfoChar(&command, '%');
- p++;
- break;
- default:
- appendStringInfoChar(&command, p[0]);
- }
- }
- else
- appendStringInfoChar(&command, p[0]);
- }
-
- fh = OpenPipeStream(command.data, "r");
+ fh = OpenPipeStream(command, "r");
if (fh == NULL)
{
ereport(loglevel,
(errcode_for_file_access(),
errmsg("could not execute command \"%s\": %m",
- command.data)));
+ command)));
goto error;
}
ereport(loglevel,
(errcode_for_file_access(),
errmsg("could not read from command \"%s\": %m",
- command.data)));
+ command)));
goto error;
}
}
ereport(loglevel,
(errcode_for_file_access(),
errmsg("command \"%s\" failed",
- command.data),
+ command),
errdetail_internal("%s", wait_result_to_str(pclose_rc))));
goto error;
}
len = pg_strip_crlf(buf);
error:
- pfree(command.data);
+ pfree(command);
return len;
}
#include <sys/wait.h>
#include "access/xlog.h"
+#include "common/percentrepl.h"
#include "pgstat.h"
#include "postmaster/pgarch.h"
static bool
shell_archive_file(const char *file, const char *path)
{
- char xlogarchcmd[MAXPGPATH];
- char *dp;
- char *endp;
- const char *sp;
+ char *xlogarchcmd;
+ char *nativePath = NULL;
int rc;
- /*
- * construct the command to be executed
- */
- dp = xlogarchcmd;
- endp = xlogarchcmd + MAXPGPATH - 1;
- *endp = '\0';
-
- for (sp = XLogArchiveCommand; *sp; sp++)
+ if (path)
{
- if (*sp == '%')
- {
- switch (sp[1])
- {
- case 'p':
- /* %p: relative path of source file */
- sp++;
- strlcpy(dp, path, endp - dp);
- make_native_path(dp);
- dp += strlen(dp);
- break;
- case 'f':
- /* %f: filename of source file */
- sp++;
- strlcpy(dp, file, endp - dp);
- dp += strlen(dp);
- break;
- case '%':
- /* convert %% to a single % */
- sp++;
- if (dp < endp)
- *dp++ = *sp;
- break;
- default:
- /* otherwise treat the % as not special */
- if (dp < endp)
- *dp++ = *sp;
- break;
- }
- }
- else
- {
- if (dp < endp)
- *dp++ = *sp;
- }
+ nativePath = pstrdup(path);
+ make_native_path(nativePath);
}
- *dp = '\0';
+
+ xlogarchcmd = replace_percent_placeholders(XLogArchiveCommand, "archive_command", "fp", file, nativePath);
+
+ if (nativePath)
+ pfree(nativePath);
ereport(DEBUG3,
(errmsg_internal("executing archive command \"%s\"",
return false;
}
+ pfree(xlogarchcmd);
+
elog(DEBUG1, "archived write-ahead log file \"%s\"", file);
return true;
}
kwlookup.o \
link-canary.o \
md5_common.o \
+ percentrepl.o \
pg_get_line.o \
pg_lzcompress.o \
pg_prng.o \
#endif
#include "common/archive.h"
-#include "lib/stringinfo.h"
+#include "common/percentrepl.h"
/*
* BuildRestoreCommand
const char *xlogfname,
const char *lastRestartPointFname)
{
- StringInfoData result;
- const char *sp;
+ char *nativePath = NULL;
+ char *result;
- /*
- * Build the command to be executed.
- */
- initStringInfo(&result);
-
- for (sp = restoreCommand; *sp; sp++)
+ if (xlogpath)
{
- if (*sp == '%')
- {
- switch (sp[1])
- {
- case 'p':
- {
- char *nativePath;
+ nativePath = pstrdup(xlogpath);
+ make_native_path(nativePath);
+ }
- /* %p: relative path of target file */
- if (xlogpath == NULL)
- {
- pfree(result.data);
- return NULL;
- }
- sp++;
+ result = replace_percent_placeholders(restoreCommand, "restore_command", "frp",
+ xlogfname, lastRestartPointFname, nativePath);
- /*
- * This needs to use a placeholder to not modify the
- * input with the conversion done via
- * make_native_path().
- */
- nativePath = pstrdup(xlogpath);
- make_native_path(nativePath);
- appendStringInfoString(&result,
- nativePath);
- pfree(nativePath);
- break;
- }
- case 'f':
- /* %f: filename of desired file */
- if (xlogfname == NULL)
- {
- pfree(result.data);
- return NULL;
- }
- sp++;
- appendStringInfoString(&result, xlogfname);
- break;
- case 'r':
- /* %r: filename of last restartpoint */
- if (lastRestartPointFname == NULL)
- {
- pfree(result.data);
- return NULL;
- }
- sp++;
- appendStringInfoString(&result,
- lastRestartPointFname);
- break;
- case '%':
- /* convert %% to a single % */
- sp++;
- appendStringInfoChar(&result, *sp);
- break;
- default:
- /* otherwise treat the % as not special */
- appendStringInfoChar(&result, *sp);
- break;
- }
- }
- else
- {
- appendStringInfoChar(&result, *sp);
- }
- }
+ if (nativePath)
+ pfree(nativePath);
- return result.data;
+ return result;
}
'kwlookup.c',
'link-canary.c',
'md5_common.c',
+ 'percentrepl.c',
'pg_get_line.c',
'pg_lzcompress.c',
'pg_prng.c',
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * percentrepl.c
+ * Common routines to replace percent placeholders in strings
+ *
+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/common/percentrepl.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef FRONTEND
+#include "postgres.h"
+#else
+#include "postgres_fe.h"
+#include "common/logging.h"
+#endif
+
+#include "common/percentrepl.h"
+#include "lib/stringinfo.h"
+
+/*
+ * replace_percent_placeholders
+ *
+ * Replace percent-letter placeholders in input string with the supplied
+ * values. For example, to replace %f with foo and %b with bar, call
+ *
+ * replace_percent_placeholders(instr, "param_name", "bf", bar, foo);
+ *
+ * The return value is palloc'd.
+ *
+ * "%%" is replaced by a single "%".
+ *
+ * This throws an error for an unsupported placeholder or a "%" at the end of
+ * the input string.
+ *
+ * A value may be NULL. If the corresponding placeholder is found in the
+ * input string, it will be treated as if an unsupported placeholder was used.
+ * This allows callers to share a "letters" specification but vary the
+ * actually supported placeholders at run time.
+ *
+ * This functions is meant for cases where all the values are readily
+ * available or cheap to compute and most invocations will use most values
+ * (for example for archive_command). Also, it requires that all values are
+ * strings. It won't be a good match for things like log prefixes or prompts
+ * that use a mix of data types and any invocation will only use a few of the
+ * possible values.
+ *
+ * param_name is the name of the underlying GUC parameter, for error
+ * reporting. At the moment, this function is only used for GUC parameters.
+ * If other kinds of uses were added, the error reporting would need to be
+ * revised.
+ */
+char *
+replace_percent_placeholders(const char *instr, const char *param_name, const char *letters,...)
+{
+ StringInfoData result;
+
+ initStringInfo(&result);
+
+ for (const char *sp = instr; *sp; sp++)
+ {
+ if (*sp == '%')
+ {
+ if (sp[1] == '%')
+ {
+ /* Convert %% to a single % */
+ sp++;
+ appendStringInfoChar(&result, *sp);
+ }
+ else if (sp[1] == '\0')
+ {
+ /* Incomplete escape sequence, expected a character afterward */
+#ifdef FRONTEND
+ pg_log_error("invalid value for parameter \"%s\": \"%s\"", param_name, instr);
+ pg_log_error_detail("String ends unexpectedly after escape character \"%%\".");
+ exit(1);
+#else
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid value for parameter \"%s\": \"%s\"", param_name, instr),
+ errdetail("String ends unexpectedly after escape character \"%%\"."));
+#endif
+ }
+ else
+ {
+ /* Look up placeholder character */
+ bool found = false;
+ va_list ap;
+
+ sp++;
+
+ va_start(ap, letters);
+ for (const char *lp = letters; *lp; lp++)
+ {
+ char *val = va_arg(ap, char *);
+
+ if (*sp == *lp)
+ {
+ if (val)
+ {
+ appendStringInfoString(&result, val);
+ found = true;
+ }
+ /* If val is NULL, we will report an error. */
+ break;
+ }
+ }
+ va_end(ap);
+ if (!found)
+ {
+ /* Unknown escape sequence */
+#ifdef FRONTEND
+ pg_log_error("invalid value for parameter \"%s\": \"%s\"", param_name, instr);
+ pg_log_error_detail("String contains unexpected escape sequence \"%c\".", *sp);
+ exit(1);
+#else
+ ereport(ERROR,
+ errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("invalid value for parameter \"%s\": \"%s\"", param_name, instr),
+ errdetail("String contains unexpected escape sequence \"%c\".", *sp));
+#endif
+ }
+ }
+ }
+ else
+ {
+ appendStringInfoChar(&result, *sp);
+ }
+ }
+
+ return result.data;
+}
xlogRestoreCmd = BuildRestoreCommand(restoreCommand, xlogpath,
xlogfname, NULL);
- if (xlogRestoreCmd == NULL)
- pg_fatal("cannot use restore_command with %%r placeholder");
/*
* Execute restore_command, which should copy the missing file from
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * percentrepl.h
+ * Common routines to replace percent placeholders in strings
+ *
+ * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/common/percentrepl.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PERCENTREPL_H
+#define PERCENTREPL_H
+
+extern char *replace_percent_placeholders(const char *instr, const char *param_name, const char *letters,...);
+
+#endif /* PERCENTREPL_H */