Skip to content

Commit a33e17f

Browse files
committed
Rework internal command generation of pg_rewind
pg_rewind generates and executes internally up to two commands to work on the target cluster, depending on the options given by its caller: - postgres -C to retrieve the value of restore_command, when using -c/--restore-target-wal. - postgres --single to enforce recovery once and get the target cluster in a clean shutdown state. Both commands have been applying incorrect quoting rules, which could lead to failures when for example using a target data directory with unexpected characters like CRLFs. Those commands are now generated with PQExpBuffer, making use of string_utils.h to quote those commands as they should. We may extend those commands in the future with more options, so this makes any upcoming additions easier. This is arguably a bug fix, but nobody has complained about the existing code being a problem either, so no backpatch is done. Extracted from a larger patch by the same author. Author: Gunnar "Nick" Bluth Discussion: https://postgr.es/m/7c59265d-ac50-b0aa-ca1e-65e8bd27642a@pro-open.de
1 parent 7a85073 commit a33e17f

File tree

1 file changed

+33
-10
lines changed

1 file changed

+33
-10
lines changed

src/bin/pg_rewind/pg_rewind.c

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "common/restricted_token.h"
2424
#include "common/string.h"
2525
#include "fe_utils/recovery_gen.h"
26+
#include "fe_utils/string_utils.h"
2627
#include "file_ops.h"
2728
#include "filemap.h"
2829
#include "getopt_long.h"
@@ -1016,8 +1017,8 @@ getRestoreCommand(const char *argv0)
10161017
{
10171018
int rc;
10181019
char postgres_exec_path[MAXPGPATH],
1019-
postgres_cmd[MAXPGPATH],
10201020
cmd_output[MAXPGPATH];
1021+
PQExpBuffer postgres_cmd;
10211022

10221023
if (!restore_wal)
10231024
return;
@@ -1051,11 +1052,19 @@ getRestoreCommand(const char *argv0)
10511052
* Build a command able to retrieve the value of GUC parameter
10521053
* restore_command, if set.
10531054
*/
1054-
snprintf(postgres_cmd, sizeof(postgres_cmd),
1055-
"\"%s\" -D \"%s\" -C restore_command",
1056-
postgres_exec_path, datadir_target);
1055+
postgres_cmd = createPQExpBuffer();
10571056

1058-
if (!pipe_read_line(postgres_cmd, cmd_output, sizeof(cmd_output)))
1057+
/* path to postgres, properly quoted */
1058+
appendShellString(postgres_cmd, postgres_exec_path);
1059+
1060+
/* add -D switch, with properly quoted data directory */
1061+
appendPQExpBufferStr(postgres_cmd, " -D ");
1062+
appendShellString(postgres_cmd, datadir_target);
1063+
1064+
/* add -C switch, for restore_command */
1065+
appendPQExpBufferStr(postgres_cmd, " -C restore_command");
1066+
1067+
if (!pipe_read_line(postgres_cmd->data, cmd_output, sizeof(cmd_output)))
10591068
exit(1);
10601069

10611070
(void) pg_strip_crlf(cmd_output);
@@ -1067,6 +1076,8 @@ getRestoreCommand(const char *argv0)
10671076

10681077
pg_log_debug("using for rewind restore_command = \'%s\'",
10691078
restore_command);
1079+
1080+
destroyPQExpBuffer(postgres_cmd);
10701081
}
10711082

10721083

@@ -1080,7 +1091,7 @@ ensureCleanShutdown(const char *argv0)
10801091
int ret;
10811092
#define MAXCMDLEN (2 * MAXPGPATH)
10821093
char exec_path[MAXPGPATH];
1083-
char cmd[MAXCMDLEN];
1094+
PQExpBuffer postgres_cmd;
10841095

10851096
/* locate postgres binary */
10861097
if ((ret = find_other_exec(argv0, "postgres",
@@ -1119,14 +1130,26 @@ ensureCleanShutdown(const char *argv0)
11191130
* fsync here. This makes the recovery faster, and the target data folder
11201131
* is synced at the end anyway.
11211132
*/
1122-
snprintf(cmd, MAXCMDLEN, "\"%s\" --single -F -D \"%s\" template1 < \"%s\"",
1123-
exec_path, datadir_target, DEVNULL);
1133+
postgres_cmd = createPQExpBuffer();
11241134

1125-
if (system(cmd) != 0)
1135+
/* path to postgres, properly quoted */
1136+
appendShellString(postgres_cmd, exec_path);
1137+
1138+
/* add set of options with properly quoted data directory */
1139+
appendPQExpBufferStr(postgres_cmd, " --single -F -D ");
1140+
appendShellString(postgres_cmd, datadir_target);
1141+
1142+
/* finish with the database name, and a properly quoted redirection */
1143+
appendPQExpBufferStr(postgres_cmd, " template1 < ");
1144+
appendShellString(postgres_cmd, DEVNULL);
1145+
1146+
if (system(postgres_cmd->data) != 0)
11261147
{
11271148
pg_log_error("postgres single-user mode in target cluster failed");
1128-
pg_fatal("Command was: %s", cmd);
1149+
pg_fatal("Command was: %s", postgres_cmd->data);
11291150
}
1151+
1152+
destroyPQExpBuffer(postgres_cmd);
11301153
}
11311154

11321155
static void

0 commit comments

Comments
 (0)