Commit 30b5ede7 authored by Heikki Linnakangas's avatar Heikki Linnakangas

Fix escaping in generated recovery.conf file.

In the primary_conninfo line that "pg_basebackup -R" generates, single
quotes in parameter values need to be escaped into \\'; the libpq parser
requires the quotes to be escaped into \', and recovery.conf parser requires
the \ to be escaped into \\.

Also, don't quote parameter values unnecessarily, to make the connection
string prettier. Most options in a libpq connection string don't need
quoting.

Reported by Hari Babu, closer analysis by Zoltan Boszormenyi, although I
didn't use his patch.
parent 2af0971f
......@@ -1107,7 +1107,71 @@ ReceiveAndUnpackTarFile(PGconn *conn, PGresult *res, int rownum)
}
/*
* Escape single quotes used in connection parameters
* Escape a parameter value so that it can be used as part of a libpq
* connection string, e.g. in:
*
* application_name=<value>
*
* The returned string is malloc'd. Return NULL on out-of-memory.
*/
static char *
escapeConnectionParameter(const char *src)
{
bool need_quotes = false;
bool need_escaping = false;
const char *p;
char *dstbuf;
char *dst;
/*
* First check if quoting is needed. Any quote (') or backslash (\)
* characters need to be escaped. Parameters are separated by whitespace,
* so any string containing whitespace characters need to be quoted. An
* empty string is represented by ''.
*/
if (strchr(src, '\'') != NULL || strchr(src, '\\') != NULL)
need_escaping = true;
for (p = src; *p; p++)
{
if (isspace(*p))
{
need_quotes = true;
break;
}
}
if (*src == '\0')
return pg_strdup("''");
if (!need_quotes && !need_escaping)
return pg_strdup(src); /* no quoting or escaping needed */
/*
* Allocate a buffer large enough for the worst case that all the source
* characters need to be escaped, plus quotes.
*/
dstbuf = pg_malloc(strlen(src) * 2 + 2 + 1);
dst = dstbuf;
if (need_quotes)
*(dst++) = '\'';
for (; *src; src++)
{
if (*src == '\'' || *src == '\\')
*(dst++) = '\\';
*(dst++) = *src;
}
if (need_quotes)
*(dst++) = '\'';
*dst = '\0';
return dstbuf;
}
/*
* Escape a string so that it can be used as a value in a key-value pair
* a configuration file.
*/
static char *
escape_quotes(const char *src)
......@@ -1130,6 +1194,8 @@ GenerateRecoveryConf(PGconn *conn)
{
PQconninfoOption *connOptions;
PQconninfoOption *option;
PQExpBufferData conninfo_buf;
char *escaped;
recoveryconfcontents = createPQExpBuffer();
if (!recoveryconfcontents)
......@@ -1146,12 +1212,10 @@ GenerateRecoveryConf(PGconn *conn)
}
appendPQExpBufferStr(recoveryconfcontents, "standby_mode = 'on'\n");
appendPQExpBufferStr(recoveryconfcontents, "primary_conninfo = '");
initPQExpBuffer(&conninfo_buf);
for (option = connOptions; option && option->keyword; option++)
{
char *escaped;
/*
* Do not emit this setting if: - the setting is "replication",
* "dbname" or "fallback_application_name", since these would be
......@@ -1165,24 +1229,37 @@ GenerateRecoveryConf(PGconn *conn)
(option->val != NULL && option->val[0] == '\0'))
continue;
/* Separate key-value pairs with spaces */
if (conninfo_buf.len != 0)
appendPQExpBufferStr(&conninfo_buf, " ");
/*
* Write "keyword='value'" pieces, the value string is escaped if
* necessary and doubled single quotes around the value string.
* Write "keyword=value" pieces, the value string is escaped and/or
* quoted if necessary.
*/
escaped = escape_quotes(option->val);
appendPQExpBuffer(recoveryconfcontents, "%s=''%s'' ", option->keyword, escaped);
escaped = escapeConnectionParameter(option->val);
appendPQExpBuffer(&conninfo_buf, "%s=%s", option->keyword, escaped);
free(escaped);
}
appendPQExpBufferStr(recoveryconfcontents, "'\n");
if (PQExpBufferBroken(recoveryconfcontents))
/*
* Escape the connection string, so that it can be put in the config file.
* Note that this is different from the escaping of individual connection
* options above!
*/
escaped = escape_quotes(conninfo_buf.data);
appendPQExpBuffer(recoveryconfcontents, "primary_conninfo = '%s'\n", escaped);
free(escaped);
if (PQExpBufferBroken(recoveryconfcontents) ||
PQExpBufferDataBroken(conninfo_buf))
{
fprintf(stderr, _("%s: out of memory\n"), progname);
disconnect_and_exit(1);
}
termPQExpBuffer(&conninfo_buf);
PQconninfoFree(connOptions);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment