return buf;
}
-/*
- * Parse one line from recovery.conf. 'cmdline' is the raw line from the
- * file. If the line is parsed successfully, returns true, false indicates
- * syntax error. On success, *key_p and *value_p are set to the parameter
- * name and value on the line, respectively. If the line is an empty line,
- * consisting entirely of whitespace and comments, function returns true
- * and *keyp_p and *value_p are set to NULL.
- *
- * The pointers returned in *key_p and *value_p point to an internal buffer
- * that is valid only until the next call of parseRecoveryCommandFile().
- */
-static bool
-parseRecoveryCommandFileLine(char *cmdline, char **key_p, char **value_p)
-{
- char *ptr;
- char *bufp;
- char *key;
- char *value;
- static char *buf = NULL;
-
- *key_p = *value_p = NULL;
-
- /*
- * Allocate the buffer on first use. It's used to hold both the parameter
- * name and value.
- */
- if (buf == NULL)
- buf = malloc(MAXPGPATH + 1);
- bufp = buf;
-
- /* Skip any whitespace at the beginning of line */
- for (ptr = cmdline; *ptr; ptr++)
- {
- if (!isspace((unsigned char) *ptr))
- break;
- }
- /* Ignore empty lines */
- if (*ptr == '\0' || *ptr == '#')
- return true;
-
- /* Read the parameter name */
- key = bufp;
- while (*ptr && !isspace((unsigned char) *ptr) &&
- *ptr != '=' && *ptr != '\'')
- *(bufp++) = *(ptr++);
- *(bufp++) = '\0';
-
- /* Skip to the beginning quote of the parameter value */
- ptr = strchr(ptr, '\'');
- if (!ptr)
- return false;
- ptr++;
-
- /* Read the parameter value to *bufp. Collapse any '' escapes as we go. */
- value = bufp;
- for (;;)
- {
- if (*ptr == '\'')
- {
- ptr++;
- if (*ptr == '\'')
- *(bufp++) = '\'';
- else
- {
- /* end of parameter */
- *bufp = '\0';
- break;
- }
- }
- else if (*ptr == '\0')
- return false; /* unterminated quoted string */
- else
- *(bufp++) = *ptr;
-
- ptr++;
- }
- *(bufp++) = '\0';
-
- /* Check that there's no garbage after the value */
- while (*ptr)
- {
- if (*ptr == '#')
- break;
- if (!isspace((unsigned char) *ptr))
- return false;
- ptr++;
- }
-
- /* Success! */
- *key_p = key;
- *value_p = value;
- return true;
-}
-
/*
* See if there is a recovery command file (recovery.conf), and if so
* read in parameters for archive recovery and XLOG streaming.
*
- * XXX longer term intention is to expand this to
- * cater for additional parameters and controls
- * possibly use a flex lexer similar to the GUC one
+ * The file is parsed using the main configuration parser.
*/
static void
readRecoveryCommandFile(void)
{
FILE *fd;
- char cmdline[MAXPGPATH];
TimeLineID rtli = 0;
bool rtliGiven = false;
- bool syntaxError = false;
+ ConfigVariable *item,
+ *head = NULL,
+ *tail = NULL;
fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
if (fd == NULL)
}
/*
- * Parse the file...
- */
- while (fgets(cmdline, sizeof(cmdline), fd) != NULL)
- {
- char *tok1;
- char *tok2;
+ * Since we're asking ParseConfigFp() to error out at FATAL, there's no
+ * need to check the return value.
+ */
+ ParseConfigFp(fd, RECOVERY_COMMAND_FILE, 0, FATAL, &head, &tail);
- if (!parseRecoveryCommandFileLine(cmdline, &tok1, &tok2))
- {
- syntaxError = true;
- break;
- }
- if (tok1 == NULL)
- continue;
-
- if (strcmp(tok1, "restore_command") == 0)
+ for (item = head; item; item = item->next)
+ {
+ if (strcmp(item->name, "restore_command") == 0)
{
- recoveryRestoreCommand = pstrdup(tok2);
+ recoveryRestoreCommand = pstrdup(item->value);
ereport(DEBUG2,
(errmsg("restore_command = '%s'",
recoveryRestoreCommand)));
}
- else if (strcmp(tok1, "recovery_end_command") == 0)
+ else if (strcmp(item->name, "recovery_end_command") == 0)
{
- recoveryEndCommand = pstrdup(tok2);
+ recoveryEndCommand = pstrdup(item->value);
ereport(DEBUG2,
(errmsg("recovery_end_command = '%s'",
recoveryEndCommand)));
}
- else if (strcmp(tok1, "archive_cleanup_command") == 0)
+ else if (strcmp(item->name, "archive_cleanup_command") == 0)
{
- archiveCleanupCommand = pstrdup(tok2);
+ archiveCleanupCommand = pstrdup(item->value);
ereport(DEBUG2,
(errmsg("archive_cleanup_command = '%s'",
archiveCleanupCommand)));
}
- else if (strcmp(tok1, "recovery_target_timeline") == 0)
+ else if (strcmp(item->name, "recovery_target_timeline") == 0)
{
rtliGiven = true;
- if (strcmp(tok2, "latest") == 0)
+ if (strcmp(item->value, "latest") == 0)
rtli = 0;
else
{
errno = 0;
- rtli = (TimeLineID) strtoul(tok2, NULL, 0);
+ rtli = (TimeLineID) strtoul(item->value, NULL, 0);
if (errno == EINVAL || errno == ERANGE)
ereport(FATAL,
(errmsg("recovery_target_timeline is not a valid number: \"%s\"",
- tok2)));
+ item->value)));
}
if (rtli)
ereport(DEBUG2,
ereport(DEBUG2,
(errmsg("recovery_target_timeline = latest")));
}
- else if (strcmp(tok1, "recovery_target_xid") == 0)
+ else if (strcmp(item->name, "recovery_target_xid") == 0)
{
errno = 0;
- recoveryTargetXid = (TransactionId) strtoul(tok2, NULL, 0);
+ recoveryTargetXid = (TransactionId) strtoul(item->value, NULL, 0);
if (errno == EINVAL || errno == ERANGE)
ereport(FATAL,
(errmsg("recovery_target_xid is not a valid number: \"%s\"",
- tok2)));
+ item->value)));
ereport(DEBUG2,
(errmsg("recovery_target_xid = %u",
recoveryTargetXid)));
recoveryTarget = RECOVERY_TARGET_XID;
}
- else if (strcmp(tok1, "recovery_target_time") == 0)
+ else if (strcmp(item->name, "recovery_target_time") == 0)
{
/*
* if recovery_target_xid specified, then this overrides
*/
recoveryTargetTime =
DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
- CStringGetDatum(tok2),
+ CStringGetDatum(item->value),
ObjectIdGetDatum(InvalidOid),
Int32GetDatum(-1)));
ereport(DEBUG2,
(errmsg("recovery_target_time = '%s'",
timestamptz_to_str(recoveryTargetTime))));
}
- else if (strcmp(tok1, "recovery_target_inclusive") == 0)
+ else if (strcmp(item->name, "recovery_target_inclusive") == 0)
{
/*
* does nothing if a recovery_target is not also set
*/
- if (!parse_bool(tok2, &recoveryTargetInclusive))
+ if (!parse_bool(item->value, &recoveryTargetInclusive))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("parameter \"%s\" requires a Boolean value", "recovery_target_inclusive")));
ereport(DEBUG2,
- (errmsg("recovery_target_inclusive = %s", tok2)));
+ (errmsg("recovery_target_inclusive = %s", item->value)));
}
- else if (strcmp(tok1, "standby_mode") == 0)
+ else if (strcmp(item->name, "standby_mode") == 0)
{
- if (!parse_bool(tok2, &StandbyMode))
+ if (!parse_bool(item->value, &StandbyMode))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("parameter \"%s\" requires a Boolean value", "standby_mode")));
ereport(DEBUG2,
- (errmsg("standby_mode = '%s'", tok2)));
+ (errmsg("standby_mode = '%s'", item->value)));
}
- else if (strcmp(tok1, "primary_conninfo") == 0)
+ else if (strcmp(item->name, "primary_conninfo") == 0)
{
- PrimaryConnInfo = pstrdup(tok2);
+ PrimaryConnInfo = pstrdup(item->value);
ereport(DEBUG2,
(errmsg("primary_conninfo = '%s'",
PrimaryConnInfo)));
}
- else if (strcmp(tok1, "trigger_file") == 0)
+ else if (strcmp(item->name, "trigger_file") == 0)
{
- TriggerFile = pstrdup(tok2);
+ TriggerFile = pstrdup(item->value);
ereport(DEBUG2,
(errmsg("trigger_file = '%s'",
TriggerFile)));
else
ereport(FATAL,
(errmsg("unrecognized recovery parameter \"%s\"",
- tok1)));
+ item->name)));
}
- FreeFile(fd);
-
- if (syntaxError)
- ereport(FATAL,
- (errmsg("syntax error in recovery command file: %s",
- cmdline),
- errhint("Lines should have the format parameter = 'value'.")));
-
/*
* Check for compulsory parameters
*/
recoveryTargetTLI = findNewestTimeLine(recoveryTargetTLI);
}
}
+
+ FreeConfigVariables(head);
+ FreeFile(fd);
}
/*
GUC_ERROR = 100
};
-struct name_value_pair
-{
- char *name;
- char *value;
- char *filename;
- int sourceline;
- struct name_value_pair *next;
-};
-
static unsigned int ConfigFileLineno;
/* flex fails to supply a prototype for yylex, so provide one */
int GUC_yylex(void);
-static bool ParseConfigFile(const char *config_file, const char *calling_file,
- int depth, int elevel,
- struct name_value_pair **head_p,
- struct name_value_pair **tail_p);
-static void free_name_value_list(struct name_value_pair * list);
static char *GUC_scanstr(const char *s);
%}
ProcessConfigFile(GucContext context)
{
int elevel;
- struct name_value_pair *item, *head, *tail;
+ ConfigVariable *item,
+ *head,
+ *tail;
char *cvc = NULL;
struct config_string *cvc_struct;
const char *envvar;
PgReloadTime = GetCurrentTimestamp();
cleanup_list:
- free_name_value_list(head);
+ FreeConfigVariables(head);
if (cvc)
free(cvc);
}
-
/*
- * Read and parse a single configuration file. This function recurses
- * to handle "include" directives.
- *
- * Input parameters:
- * config_file: absolute or relative path of file to read
- * calling_file: absolute path of file containing the "include" directive,
- * or NULL at outer level (config_file must be absolute at outer level)
- * depth: recursion depth (used only to prevent infinite recursion)
- * elevel: error logging level determined by ProcessConfigFile()
- * Output parameters:
- * head_p, tail_p: head and tail of linked list of name/value pairs
- *
- * *head_p and *tail_p must be initialized to NULL before calling the outer
- * recursion level. On exit, they contain a list of name-value pairs read
- * from the input file(s).
- *
- * Returns TRUE if successful, FALSE if an error occurred. The error has
- * already been ereport'd, it is only necessary for the caller to clean up
- * its own state and release the name/value pairs list.
- *
- * Note: if elevel >= ERROR then an error will not return control to the
- * caller, and internal state such as open files will not be cleaned up.
- * This case occurs only during postmaster or standalone-backend startup,
- * where an error will lead to immediate process exit anyway; so there is
- * no point in contorting the code so it can clean up nicely.
+ * See next function for details. This one will just work with a config_file
+ * name rather than an already opened File Descriptor
*/
-static bool
+bool
ParseConfigFile(const char *config_file, const char *calling_file,
int depth, int elevel,
- struct name_value_pair **head_p,
- struct name_value_pair **tail_p)
+ ConfigVariable **head_p,
+ ConfigVariable **tail_p)
{
bool OK = true;
- char abs_path[MAXPGPATH];
FILE *fp;
- YY_BUFFER_STATE lex_buffer;
- int token;
+ char abs_path[MAXPGPATH];
/*
* Reject too-deep include nesting depth. This is just a safety check
*/
if (!is_absolute_path(config_file))
{
- Assert(calling_file != NULL);
- strlcpy(abs_path, calling_file, sizeof(abs_path));
- get_parent_directory(abs_path);
- join_path_components(abs_path, abs_path, config_file);
- canonicalize_path(abs_path);
- config_file = abs_path;
+ if (calling_file != NULL)
+ {
+ strlcpy(abs_path, calling_file, sizeof(abs_path));
+ get_parent_directory(abs_path);
+ join_path_components(abs_path, abs_path, config_file);
+ canonicalize_path(abs_path);
+ config_file = abs_path;
+ }
+ else
+ {
+ /*
+ * calling_file is NULL, we make an absolute path from $PGDATA
+ */
+ join_path_components(abs_path, data_directory, config_file);
+ canonicalize_path(abs_path);
+ config_file = abs_path;
+ }
}
fp = AllocateFile(config_file, "r");
return false;
}
+ OK = ParseConfigFp(fp, config_file, depth, elevel, head_p, tail_p);
+
+ FreeFile(fp);
+
+ return OK;
+}
+
+/*
+ * Read and parse a single configuration file. This function recurses
+ * to handle "include" directives.
+ *
+ * Input parameters:
+ * fp: file pointer from AllocateFile for the configuration file to parse
+ * config_file: absolute or relative path of file to read
+ * depth: recursion depth (used only to prevent infinite recursion)
+ * elevel: error logging level determined by ProcessConfigFile()
+ * Output parameters:
+ * head_p, tail_p: head and tail of linked list of name/value pairs
+ *
+ * *head_p and *tail_p must be initialized to NULL before calling the outer
+ * recursion level. On exit, they contain a list of name-value pairs read
+ * from the input file(s).
+ *
+ * Returns TRUE if successful, FALSE if an error occurred. The error has
+ * already been ereport'd, it is only necessary for the caller to clean up
+ * its own state and release the name/value pairs list.
+ *
+ * Note: if elevel >= ERROR then an error will not return control to the
+ * caller, and internal state such as open files will not be cleaned up.
+ * This case occurs only during postmaster or standalone-backend startup,
+ * where an error will lead to immediate process exit anyway; so there is
+ * no point in contorting the code so it can clean up nicely.
+ */
+bool
+ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
+ ConfigVariable **head_p, ConfigVariable **tail_p)
+{
+ bool OK = true;
+ YY_BUFFER_STATE lex_buffer;
+ int token;
+
/*
* Parse
*/
while ((token = yylex()))
{
char *opt_name, *opt_value;
- struct name_value_pair *item;
+ ConfigVariable *item;
if (token == GUC_EOL) /* empty or comment line */
continue;
cleanup_exit:
yy_delete_buffer(lex_buffer);
- FreeFile(fp);
return OK;
}
/*
- * Free a list of name/value pairs, including the names and the values
+ * Free a list of ConfigVariables, including the names and the values
*/
-static void
-free_name_value_list(struct name_value_pair *list)
+void
+FreeConfigVariables(ConfigVariable *list)
{
- struct name_value_pair *item;
+ ConfigVariable *item;
item = list;
while (item)
{
- struct name_value_pair *next = item->next;
+ ConfigVariable *next = item->next;
pfree(item->name);
pfree(item->value);