@ -47,6 +47,12 @@ static sigjmp_buf *GUC_flex_fatal_jmp;
static void FreeConfigVariable(ConfigVariable *item);
static void record_config_file_error(const char *errmsg,
const char *config_file,
int lineno,
ConfigVariable **head_p,
ConfigVariable **tail_p);
static int GUC_flex_fatal(const char *msg);
static char *GUC_scanstr(const char *s);
@ -107,20 +113,15 @@ STRING \'([^'\\\n]|\\.|\'\')*\'
* parameter indicates in what context the file is being read --- either
* postmaster startup (including standalone-backend startup) or SIGHUP.
* All options mentioned in the configuration file are set to new values.
* If an error occurs, no values will be changed.
* If a hard error occurs, no values will be changed. (There can also be
* errors that prevent just one value from being changed.)
*/
void
ProcessConfigFile(GucContext context)
{
bool error = false;
bool apply = false;
int elevel;
const char *ConfFileWithError;
ConfigVariable *item,
*head,
*tail;
int i;
int file_variables_count = 0;
MemoryContext config_cxt;
MemoryContext caller_cxt;
/*
* Config files are processed on startup (by the postmaster only)
@ -135,15 +136,58 @@ ProcessConfigFile(GucContext context)
*/
elevel = IsUnderPostmaster ? DEBUG2 : LOG;
/*
* This function is usually called within a process-lifespan memory
* context. To ensure that any memory leaked during GUC processing does
* not accumulate across repeated SIGHUP cycles, do the work in a private
* context that we can free at exit.
*/
config_cxt = AllocSetContextCreate(CurrentMemoryContext,
"config file processing",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
caller_cxt = MemoryContextSwitchTo(config_cxt);
/*
* Read and apply the config file. We don't need to examine the result.
*/
(void) ProcessConfigFileInternal(context, true, elevel);
/* Clean up */
MemoryContextSwitchTo(caller_cxt);
MemoryContextDelete(config_cxt);
}
/*
* This function handles both actual config file (re)loads and execution of
* show_all_file_settings() (i.e., the pg_file_settings view). In the latter
* case we don't apply any of the settings, but we make all the usual validity
* checks, and we return the ConfigVariable list so that it can be printed out
* by show_all_file_settings().
*/
static ConfigVariable *
ProcessConfigFileInternal(GucContext context, bool applySettings, int elevel)
{
bool error = false;
bool applying = false;
const char *ConfFileWithError;
ConfigVariable *item,
*head,
*tail;
int i;
/* Parse the main config file into a list of option names and values */
ConfFileWithError = ConfigFileName;
head = tail = NULL;
if (!ParseConfigFile(ConfigFileName, NULL, true, 0, elevel, &head, &tail))
if (!ParseConfigFile(ConfigFileName, true,
NULL, 0, 0, elevel,
&head, &tail))
{
/* Syntax error(s) detected in the file, so bail out */
error = true;
goto cleanup_list;
goto bail_ou t;
}
/*
@ -154,13 +198,14 @@ ProcessConfigFile(GucContext context)
*/
if (DataDir)
{
if (!ParseConfigFile(PG_AUTOCONF_FILENAME, NULL, false, 0, elevel,
if (!ParseConfigFile(PG_AUTOCONF_FILENAME, false,
NULL, 0, 0, elevel,
&head, &tail))
{
/* Syntax error(s) detected in the file, so bail out */
error = true;
ConfFileWithError = PG_AUTOCONF_FILENAME;
goto cleanup_lis t;
goto bail_ou t;
}
}
else
@ -173,28 +218,22 @@ ProcessConfigFile(GucContext context)
* will be read later. OTOH, since data_directory isn't allowed in the
* PG_AUTOCONF_FILENAME file, it will never be overwritten later.
*/
ConfigVariable *prev = NULL;
ConfigVariable *newlist = NULL;
/* Prune all items except "data_directory" from the list */
for (item = head; item;)
/*
* Prune all items except the last "data_directory" from the list.
*/
for (item = head; item; item = item->next)
{
ConfigVariable *ptr = item;
item = item->next;
if (strcmp(ptr->name, "data_directory") != 0)
{
if (prev == NULL)
head = ptr->next;
else
prev->next = ptr->next;
if (ptr->next == NULL)
tail = prev;
FreeConfigVariable(ptr);
}
else
prev = ptr;
if (!item->ignore &&
strcmp(item->name, "data_directory") == 0)
newlist = item;
}
if (newlist)
newlist->next = NULL;
head = tail = newlist;
/*
* Quick exit if data_directory is not present in file.
*
@ -203,7 +242,7 @@ ProcessConfigFile(GucContext context)
* the config file.
*/
if (head == NULL)
return ;
goto bail_out ;
}
/*
@ -228,12 +267,17 @@ ProcessConfigFile(GucContext context)
* same reason, we don't attempt to validate the options' values here.
*
* In addition, the GUC_IS_IN_FILE flag is set on each existing GUC
* variable mentioned in the file.
* variable mentioned in the file; and we detect duplicate entries in
* the file and mark the earlier occurrences as ignorable.
*/
for (item = head; item; item = item->next)
{
struct config_generic *record;
/* Ignore anything already marked as ignorable */
if (item->ignore)
continue;
/*
* Try to find the variable; but do not create a custom placeholder
* if it's not there already.
@ -242,7 +286,24 @@ ProcessConfigFile(GucContext context)
if (record)
{
/* Found, so mark it as present in file */
/* If it's already marked, then this is a duplicate entry */
if (record->status & GUC_IS_IN_FILE)
{
/*
* Mark the earlier occurrence(s) as dead/ignorable. We could
* avoid the O(N^2) behavior here with some additional state,
* but it seems unlikely to be worth the trouble.
*/
ConfigVariable *pitem;
for (pitem = head; pitem != item; pitem = pitem->next)
{
if (!pitem->ignore &&
strcmp(pitem->name, item->name) == 0)
pitem->ignore = true;
}
}
/* Now mark it as present in file */
record->status |= GUC_IS_IN_FILE;
}
else if (strchr(item->name, GUC_QUALIFIER_SEPARATOR) == NULL)
@ -253,10 +314,10 @@ ProcessConfigFile(GucContext context)
errmsg("unrecognized configuration parameter \"%s\" in file \"%s\" line %u",
item->name,
item->filename, item->sourceline)));
item->errmsg = pstrdup("unrecognized configuration parameter");
error = true;
ConfFileWithError = item->filename;
}
file_variables_count++;
}
/*
@ -264,10 +325,10 @@ ProcessConfigFile(GucContext context)
* any changes.
*/
if (error)
goto cleanup_lis t;
goto bail_ou t;
/* Otherwise, set flag that we're beginning to apply changes */
apply = true;
applying = true;
/*
* Check for variables having been removed from the config file, and
@ -289,10 +350,18 @@ ProcessConfigFile(GucContext context)
(errcode(ERRCODE_CANT_CHANGE_RUNTIME_PARAM),
errmsg("parameter \"%s\" cannot be changed without restarting the server",
gconf->name)));
record_config_file_error(psprintf("parameter \"%s\" cannot be changed without restarting the server",
gconf->name),
NULL, 0,
&head, &tail);
error = true;
continue;
}
/* No more to do if we're just doing show_all_file_settings() */
if (!applySettings)
continue;
/*
* Reset any "file" sources to "default", else set_config_option
* will not override those settings.
@ -334,7 +403,7 @@ ProcessConfigFile(GucContext context)
* potentially have PGC_S_DYNAMIC_DEFAULT or PGC_S_ENV_VAR source.
* However, there's no time to redesign it for 9.1.
*/
if (context == PGC_SIGHUP)
if (context == PGC_SIGHUP && applySettings )
{
InitializeGUCOptionsFromEnvironment();
pg_timezone_abbrev_initialize();
@ -343,54 +412,6 @@ ProcessConfigFile(GucContext context)
PGC_BACKEND, PGC_S_DYNAMIC_DEFAULT);
}
/*
* Check if we have allocated the array yet.
*
* If not, allocate it based on the number of file variables we have seen.
*/
if (!guc_file_variables)
{
/* For the first call */
num_guc_file_variables = file_variables_count;
guc_file_variables = (ConfigFileVariable *) guc_malloc(FATAL,
num_guc_file_variables * sizeof(struct ConfigFileVariable));
}
else
{
int i;
/* Free all of the previously allocated entries */
for (i = 0; i < num_guc_file_variables; i++)
{
free(guc_file_variables[i].name);
free(guc_file_variables[i].value);
free(guc_file_variables[i].filename);
}
/* Update the global count and realloc based on the new size */
num_guc_file_variables = file_variables_count;
guc_file_variables = (ConfigFileVariable *) guc_realloc(FATAL,
guc_file_variables,
num_guc_file_variables * sizeof(struct ConfigFileVariable));
}
/*
* Copy the settings which came from the files read into the
* guc_file_variables array which backs the pg_show_file_settings()
* function.
*/
for (item = head, i = 0; item && i < num_guc_file_variables;
item = item->next, i++)
{
guc_file_variables[i].name = guc_strdup(FATAL, item->name);
guc_file_variables[i].value = guc_strdup(FATAL, item->value);
guc_file_variables[i].filename = guc_strdup(FATAL, item->filename);
guc_file_variables[i].sourceline = item->sourceline;
}
/* We had better have made it through the loop above to a clean ending. */
Assert(!item && i == num_guc_file_variables);
/*
* Now apply the values from the config file.
*/
@ -399,8 +420,12 @@ ProcessConfigFile(GucContext context)
char *pre_value = NULL;
int scres;
/* Ignore anything marked as ignorable */
if (item->ignore)
continue;
/* In SIGHUP cases in the postmaster, we want to report changes */
if (context == PGC_SIGHUP && !IsUnderPostmaster)
if (context == PGC_SIGHUP && applySettings && !IsUnderPostmaster)
{
const char *preval = GetConfigOption(item->name, true, false);
@ -413,7 +438,7 @@ ProcessConfigFile(GucContext context)
scres = set_config_option(item->name, item->value,
context, PGC_S_FILE,
GUC_ACTION_SET, true , 0, false);
GUC_ACTION_SET, applySettings , 0, false);
if (scres > 0)
{
/* variable was updated, so log the change if appropriate */
@ -428,13 +453,19 @@ ProcessConfigFile(GucContext context)
(errmsg("parameter \"%s\" changed to \"%s\"",
item->name, item->value)));
}
item->applied = true;
}
else if (scres == 0)
{
error = true;
item->errmsg = pstrdup("setting could not be applied");
ConfFileWithError = item->filename;
}
/* else no error but variable's active value was not changed */
else
{
/* no error, but variable's active value was not changed */
item->applied = true;
}
/*
* We should update source location unless there was an error, since
@ -442,7 +473,7 @@ ProcessConfigFile(GucContext context)
* (In the postmaster, there won't be a difference, but it does matter
* in backends.)
*/
if (scres != 0)
if (scres != 0 && applySettings )
set_config_sourcefile(item->name, item->filename,
item->sourceline);
@ -451,10 +482,11 @@ ProcessConfigFile(GucContext context)
}
/* Remember when we last successfully loaded the config file. */
PgReloadTime = GetCurrentTimestamp();
if (applySettings)
PgReloadTime = GetCurrentTimestamp();
cleanup_lis t:
if (error)
bail_ou t:
if (error && applySettings )
{
/* During postmaster startup, any error is fatal */
if (context == PGC_POSTMASTER)
@ -462,7 +494,7 @@ ProcessConfigFile(GucContext context)
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("configuration file \"%s\" contains errors",
ConfFileWithError)));
else if (apply)
else if (applying )
ereport(elevel,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("configuration file \"%s\" contains errors; unaffected changes were applied",
@ -474,12 +506,8 @@ ProcessConfigFile(GucContext context)
ConfFileWithError)));
}
/*
* Calling FreeConfigVariables() any earlier than this can cause problems,
* because ConfFileWithError could be pointing to a string that will be
* freed here.
*/
FreeConfigVariables(head);
/* Successful or otherwise, return the collected data list */
return head;
}
/*
@ -520,12 +548,16 @@ AbsoluteConfigLocation(const char *location, const char *calling_file)
* If "strict" is true, treat failure to open the config file as an error,
* otherwise just skip the file.
*
* calling_file/calling_lineno identify the source of the request.
* Pass NULL/0 if not recursing from an inclusion request.
*
* See ParseConfigFp for further details. This one merely adds opening the
* config file rather than working from a caller-supplied file descriptor,
* and absolute-ifying the path name if necessary.
*/
bool
ParseConfigFile(const char *config_file, const char *calling_file, bool strict,
ParseConfigFile(const char *config_file, bool strict,
const char *calling_file, int calling_lineno,
int depth, int elevel,
ConfigVariable **head_p,
ConfigVariable **tail_p)
@ -545,6 +577,9 @@ ParseConfigFile(const char *config_file, const char *calling_file, bool strict,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
errmsg("could not open configuration file \"%s\": maximum nesting depth exceeded",
config_file)));
record_config_file_error("nesting depth exceeded",
calling_file, calling_lineno,
head_p, tail_p);
return false;
}
@ -558,6 +593,10 @@ ParseConfigFile(const char *config_file, const char *calling_file, bool strict,
(errcode_for_file_access(),
errmsg("could not open configuration file \"%s\": %m",
abs_path)));
record_config_file_error(psprintf("could not open file \"%s\"",
abs_path),
calling_file, calling_lineno,
head_p, tail_p);
OK = false;
}
else
@ -579,6 +618,35 @@ cleanup:
return OK;
}
/*
* Capture an error message in the ConfigVariable list returned by
* config file parsing.
*/
static void
record_config_file_error(const char *errmsg,
const char *config_file,
int lineno,
ConfigVariable **head_p,
ConfigVariable **tail_p)
{
ConfigVariable *item;
item = palloc(sizeof *item);
item->name = NULL;
item->value = NULL;
item->errmsg = pstrdup(errmsg);
item->filename = config_file ? pstrdup(config_file) : NULL;
item->sourceline = lineno;
item->ignore = true;
item->applied = false;
item->next = NULL;
if (*head_p == NULL)
*head_p = item;
else
(*tail_p)->next = item;
*tail_p = item;
}
/*
* Flex fatal errors bring us here. Stash the error message and jump back to
* ParseConfigFp(). Assume all msg arguments point to string constants; this
@ -607,9 +675,10 @@ GUC_flex_fatal(const char *msg)
* Input/Output parameters:
* head_p, tail_p: head and tail of linked list of name/value pairs
*
* *head_p and *tail_p must either be initialized to NULL or valid pointers
* to a ConfigVariable list before calling the outer recursion level. Any
* name-value pairs read from the input file(s) will be added to the list.
* *head_p and *tail_p must be initialized, either to NULL or valid pointers
* to a ConfigVariable list, before calling the outer recursion level. Any
* name-value pairs read from the input file(s) will be appended to the list.
* Error reports will also be appended to the list, if elevel < ERROR.
*
* 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
@ -617,6 +686,12 @@ GUC_flex_fatal(const char *msg)
*
* Note: if elevel >= ERROR then an error will not return control to the
* caller, so there is no need to check the return value in that case.
*
* Note: this function is used to parse not only postgresql.conf, but
* various other configuration files that use the same "name = value"
* syntax. Hence, do not do anything here or in the subsidiary routines
* ParseConfigFile/ParseConfigDirectory that assumes we are processing
* GUCs specifically.
*/
bool
ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
@ -641,7 +716,9 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
*/
elog(elevel, "%s at file \"%s\" line %u",
GUC_flex_fatal_errmsg, config_file, ConfigFileLineno);
record_config_file_error(GUC_flex_fatal_errmsg,
config_file, ConfigFileLineno,
head_p, tail_p);
OK = false;
goto cleanup;
}
@ -704,12 +781,12 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
* An include_dir directive isn't a variable and should be
* processed immediately.
*/
if (!ParseConfigDirectory(opt_value, config_file,
depth + 1, elevel,
head_p, tail_p))
if (!ParseConfigDirectory(opt_value,
config_file, ConfigFileLineno - 1,
depth + 1, elevel,
head_p, tail_p))
OK = false;
yy_switch_to_buffer(lex_buffer);
ConfigFileLineno = save_ConfigFileLineno;
pfree(opt_name);
pfree(opt_value);
}
@ -719,7 +796,8 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
* An include_if_exists directive isn't a variable and should be
* processed immediately.
*/
if (!ParseConfigFile(opt_value, config_file, false,
if (!ParseConfigFile(opt_value, false,
config_file, ConfigFileLineno - 1,
depth + 1, elevel,
head_p, tail_p))
OK = false;
@ -733,7 +811,8 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
* An include directive isn't a variable and should be processed
* immediately.
*/
if (!ParseConfigFile(opt_value, config_file, true,
if (!ParseConfigFile(opt_value, true,
config_file, ConfigFileLineno - 1,
depth + 1, elevel,
head_p, tail_p))
OK = false;
@ -747,8 +826,11 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
item = palloc(sizeof *item);
item->name = opt_name;
item->value = opt_value;
item->errmsg = NULL;
item->filename = pstrdup(config_file);
item->sourceline = ConfigFileLineno-1;
item->ignore = false;
item->applied = false;
item->next = NULL;
if (*head_p == NULL)
*head_p = item;
@ -771,15 +853,25 @@ ParseConfigFp(FILE *fp, const char *config_file, int depth, int elevel,
/* report the error */
if (token == GUC_EOL || token == 0)
{
ereport(elevel,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("syntax error in file \"%s\" line %u, near end of line",
config_file, ConfigFileLineno - 1)));
record_config_file_error("syntax error",
config_file, ConfigFileLineno - 1,
head_p, tail_p);
}
else
{
ereport(elevel,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("syntax error in file \"%s\" line %u, near token \"%s\"",
config_file, ConfigFileLineno, yytext)));
record_config_file_error("syntax error",
config_file, ConfigFileLineno,
head_p, tail_p);
}
OK = false;
errorcount++;
@ -817,10 +909,17 @@ cleanup:
/*
* Read and parse all config files in a subdirectory in alphabetical order
*
* includedir is the absolute or relative path to the subdirectory to scan.
*
* calling_file/calling_lineno identify the source of the request.
* Pass NULL/0 if not recursing from an inclusion request.
*
* See ParseConfigFp for further details.
*/
bool
ParseConfigDirectory(const char *includedir,
const char *calling_file,
const char *calling_file, int calling_lineno,
int depth, int elevel,
ConfigVariable **head_p,
ConfigVariable **tail_p)
@ -828,9 +927,9 @@ ParseConfigDirectory(const char *includedir,
char *directory;
DIR *d;
struct dirent *de;
char **filenames = NULL ;
int num_filenames = 0 ;
int size_filenames = 0 ;
char **filenames;
int num_filenames;
int size_filenames;
bool status;
directory = AbsoluteConfigLocation(includedir, calling_file);
@ -841,6 +940,10 @@ ParseConfigDirectory(const char *includedir,
(errcode_for_file_access(),
errmsg("could not open configuration directory \"%s\": %m",
directory)));
record_config_file_error(psprintf("could not open directory \"%s\"",
directory),
calling_file, calling_lineno,
head_p, tail_p);
status = false;
goto cleanup;
}
@ -849,6 +952,10 @@ ParseConfigDirectory(const char *includedir,
* Read the directory and put the filenames in an array, so we can sort
* them prior to processing the contents.
*/
size_filenames = 32;
filenames = (char **) palloc(size_filenames * sizeof(char *));
num_filenames = 0;
while ((de = ReadDir(d, directory)) != NULL)
{
struct stat st;
@ -872,15 +979,12 @@ ParseConfigDirectory(const char *includedir,
{
if (!S_ISDIR(st.st_mode))
{
/* Add file to list , increasing its size in blocks of 32 */
if (num_filenames = = size_filenames)
/* Add file to array , increasing its size in blocks of 32 */
if (num_filenames > = size_filenames)
{
size_filenames += 32;
if (num_filenames == 0)
/* Must initialize, repalloc won't take NULL input */
filenames = palloc(size_filenames * sizeof(char *));
else
filenames = repalloc(filenames, size_filenames * sizeof(char *));
filenames = (char **) repalloc(filenames,
size_filenames * sizeof(char *));
}
filenames[num_filenames] = pstrdup(filename);
num_filenames++;
@ -897,6 +1001,10 @@ ParseConfigDirectory(const char *includedir,
(errcode_for_file_access(),
errmsg("could not stat file \"%s\": %m",
filename)));
record_config_file_error(psprintf("could not stat file \"%s\"",
filename),
calling_file, calling_lineno,
head_p, tail_p);
status = false;
goto cleanup;
}
@ -908,8 +1016,10 @@ ParseConfigDirectory(const char *includedir,
qsort(filenames, num_filenames, sizeof(char *), pg_qsort_strcmp);
for (i = 0; i < num_filenames; i++)
{
if (!ParseConfigFile(filenames[i], NULL, true,
depth, elevel, head_p, tail_p))
if (!ParseConfigFile(filenames[i], true,
calling_file, calling_lineno,
depth, elevel,
head_p, tail_p))
{
status = false;
goto cleanup;
@ -949,9 +1059,14 @@ FreeConfigVariables(ConfigVariable *list)
static void
FreeConfigVariable(ConfigVariable *item)
{
pfree(item->name);
pfree(item->value);
pfree(item->filename);
if (item->name)
pfree(item->name);
if (item->value)
pfree(item->value);
if (item->errmsg)
pfree(item->errmsg);
if (item->filename)
pfree(item->filename);
pfree(item);
}