diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml index e519ff96b90a4bee5c1476df2af46917bfeaa8d7..a8a09e4f0ec63f5fe586ccfc1f4caaa996e54baa 100644 --- a/doc/src/sgml/protocol.sgml +++ b/doc/src/sgml/protocol.sgml @@ -4734,7 +4734,10 @@ StartupMessage (F) set at backend start time might be listed. Such settings will be applied during backend start (after parsing the command-line options if any). The values will act as - session defaults. + session defaults. Spaces in option values need to be escaped + with a backslash (<literal>\</>). A literal backslash can be + passed by escaping it with another backslash + (i.e <literal>\\</>). </para> </listitem> </varlistentry> diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index b190cf51136f4a9c753695c5c988ceb9946fb286..14535c8b35a6cf07011e41531ebd238aa41de5fe 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -4083,8 +4083,7 @@ BackendRun(Port *port) /* * Pass any backend switches specified with -o on the postmaster's own - * command line. We assume these are secure. (It's OK to mangle - * ExtraOptions now, since we're safely inside a subprocess.) + * command line. We assume these are secure. */ pg_split_opts(av, &ac, ExtraOptions); diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c index a5b98217739e7bc1a7d331aa0169829ac1d93e14..304be047892c663f665477bbb3f69e71a22a124a 100644 --- a/src/backend/utils/init/postinit.c +++ b/src/backend/utils/init/postinit.c @@ -409,32 +409,57 @@ InitCommunication(void) /* * pg_split_opts -- split a string of options and append it to an argv array * - * NB: the input string is destructively modified! Also, caller is responsible - * for ensuring the argv array is large enough. The maximum possible number - * of arguments added by this routine is (strlen(optstr) + 1) / 2. + * The caller is responsible for ensuring the argv array is large enough. The + * maximum possible number of arguments added by this routine is + * (strlen(optstr) + 1) / 2. * - * Since no current POSTGRES arguments require any quoting characters, - * we can use the simple-minded tactic of assuming each set of space- - * delimited characters is a separate argv element. - * - * If you don't like that, well, we *used* to pass the whole option string - * as ONE argument to execl(), which was even less intelligent... + * Because some option values can contain spaces we allow escaping using + * backslashes, with \\ representing a literal backslash. */ void pg_split_opts(char **argv, int *argcp, char *optstr) { + StringInfoData s; + + initStringInfo(&s); + while (*optstr) { + bool last_was_escape = false; + + resetStringInfo(&s); + + /* skip over leading space */ while (isspace((unsigned char) *optstr)) optstr++; + if (*optstr == '\0') break; - argv[(*argcp)++] = optstr; - while (*optstr && !isspace((unsigned char) *optstr)) + + /* + * Parse a single option + value, stopping at the first space, unless + * it's escaped. + */ + while (*optstr) + { + if (isspace(*optstr) && !last_was_escape) + break; + + if (!last_was_escape && *optstr == '\\') + last_was_escape = true; + else + { + last_was_escape = false; + appendStringInfoChar(&s, *optstr); + } + optstr++; - if (*optstr) - *optstr++ = '\0'; + } + + /* now store the option */ + argv[(*argcp)++] = pstrdup(s.data); } + resetStringInfo(&s); } /* @@ -981,7 +1006,6 @@ process_startup_options(Port *port, bool am_superuser) av[ac++] = "postgres"; - /* Note this mangles port->cmdline_options */ pg_split_opts(av, &ac, port->cmdline_options); av[ac] = NULL;