Move code related to configuration files in directories to new file

The code in charge of listing and classifying a set of configuration
files in a directory was located in guc-file.l, being used currently for
GUCs under "include_dir".  This code is planned to be used for an
upcoming feature able to include configuration files for ident and HBA
files from a directory, similarly to GUCs.  In both cases, the file
names, suffixed by ".conf", have to be ordered alphabetically.  This
logic is moved to a new file, called conffiles.c, so as it is easier to
share this facility between GUCs and the HBA/ident parsing logic.

Author: Julien Rouhaud, Michael Paquier
Discussion: https://postgr.es/m/Y2IgaH5YzIq2b+iR@paquier.xyz
This commit is contained in:
Michael Paquier 2022-11-07 12:31:38 +09:00
parent b0b72c64a0
commit a1a7bb8f16
5 changed files with 204 additions and 142 deletions

View File

@ -15,6 +15,7 @@ include $(top_builddir)/src/Makefile.global
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
OBJS = \
conffiles.o \
guc.o \
guc-file.o \
guc_funcs.o \

View File

@ -0,0 +1,164 @@
/*--------------------------------------------------------------------
* conffiles.c
*
* Utilities related to the handling of configuration files.
*
* This file contains some generic tools to work on configuration files
* used by PostgreSQL, be they related to GUCs or authentication.
*
*
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/backend/utils/misc/conffiles.c
*
*--------------------------------------------------------------------
*/
#include "postgres.h"
#include <dirent.h>
#include "common/file_utils.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "utils/conffiles.h"
/*
* AbsoluteConfigLocation
*
* Given a configuration file or directory location that may be a relative
* path, return an absolute one. We consider the location to be relative to
* the directory holding the calling file, or to DataDir if no calling file.
*/
char *
AbsoluteConfigLocation(const char *location, const char *calling_file)
{
char abs_path[MAXPGPATH];
if (is_absolute_path(location))
return pstrdup(location);
else
{
if (calling_file != NULL)
{
strlcpy(abs_path, calling_file, sizeof(abs_path));
get_parent_directory(abs_path);
join_path_components(abs_path, abs_path, location);
canonicalize_path(abs_path);
}
else
{
Assert(DataDir);
join_path_components(abs_path, DataDir, location);
canonicalize_path(abs_path);
}
return pstrdup(abs_path);
}
}
/*
* GetConfFilesInDir
*
* Returns the list of config files located in a directory, in alphabetical
* order. On error, returns NULL with details about the error stored in
* "err_msg".
*/
char **
GetConfFilesInDir(const char *includedir, const char *calling_file,
int elevel, int *num_filenames, char **err_msg)
{
char *directory;
DIR *d;
struct dirent *de;
char **filenames = NULL;
int size_filenames;
/*
* Reject directory name that is all-blank (including empty), as that
* leads to confusion --- we'd read the containing directory, typically
* resulting in recursive inclusion of the same file(s).
*/
if (strspn(includedir, " \t\r\n") == strlen(includedir))
{
ereport(elevel,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("empty configuration directory name: \"%s\"",
includedir)));
*err_msg = "empty configuration directory name";
return NULL;
}
directory = AbsoluteConfigLocation(includedir, calling_file);
d = AllocateDir(directory);
if (d == NULL)
{
ereport(elevel,
(errcode_for_file_access(),
errmsg("could not open configuration directory \"%s\": %m",
directory)));
*err_msg = psprintf("could not open directory \"%s\"", directory);
goto cleanup;
}
/*
* Read the directory and put the filenames in an array, so we can sort
* them prior to caller processing the contents.
*/
size_filenames = 32;
filenames = (char **) palloc(size_filenames * sizeof(char *));
*num_filenames = 0;
while ((de = ReadDir(d, directory)) != NULL)
{
PGFileType de_type;
char filename[MAXPGPATH];
/*
* Only parse files with names ending in ".conf". Explicitly reject
* files starting with ".". This excludes things like "." and "..",
* as well as typical hidden files, backup files, and editor debris.
*/
if (strlen(de->d_name) < 6)
continue;
if (de->d_name[0] == '.')
continue;
if (strcmp(de->d_name + strlen(de->d_name) - 5, ".conf") != 0)
continue;
join_path_components(filename, directory, de->d_name);
canonicalize_path(filename);
de_type = get_dirent_type(filename, de, true, elevel);
if (de_type == PGFILETYPE_ERROR)
{
*err_msg = psprintf("could not stat file \"%s\"", filename);
pfree(filenames);
filenames = NULL;
goto cleanup;
}
else if (de_type != PGFILETYPE_DIR)
{
/* Add file to array, increasing its size in blocks of 32 */
if (*num_filenames >= size_filenames)
{
size_filenames += 32;
filenames = (char **) repalloc(filenames,
size_filenames * sizeof(char *));
}
filenames[*num_filenames] = pstrdup(filename);
(*num_filenames)++;
}
}
/* Sort the files by name before leaving */
if (*num_filenames > 0)
qsort(filenames, *num_filenames, sizeof(char *), pg_qsort_strcmp);
cleanup:
if (d)
FreeDir(d);
pfree(directory);
return filenames;
}

View File

@ -17,6 +17,7 @@
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "storage/fd.h"
#include "utils/conffiles.h"
#include "utils/memutils.h"
}
@ -155,37 +156,6 @@ ProcessConfigFile(GucContext context)
MemoryContextDelete(config_cxt);
}
/*
* Given a configuration file or directory location that may be a relative
* path, return an absolute one. We consider the location to be relative to
* the directory holding the calling file, or to DataDir if no calling file.
*/
static char *
AbsoluteConfigLocation(const char *location, const char *calling_file)
{
char abs_path[MAXPGPATH];
if (is_absolute_path(location))
return pstrdup(location);
else
{
if (calling_file != NULL)
{
strlcpy(abs_path, calling_file, sizeof(abs_path));
get_parent_directory(abs_path);
join_path_components(abs_path, abs_path, location);
canonicalize_path(abs_path);
}
else
{
Assert(DataDir);
join_path_components(abs_path, DataDir, location);
canonicalize_path(abs_path);
}
return pstrdup(abs_path);
}
}
/*
* Read and parse a single configuration file. This function recurses
* to handle "include" directives.
@ -605,127 +575,30 @@ ParseConfigDirectory(const char *includedir,
ConfigVariable **head_p,
ConfigVariable **tail_p)
{
char *directory;
DIR *d;
struct dirent *de;
char *err_msg;
char **filenames;
int num_filenames;
int size_filenames;
bool status;
/*
* Reject directory name that is all-blank (including empty), as that
* leads to confusion --- we'd read the containing directory, typically
* resulting in recursive inclusion of the same file(s).
*/
if (strspn(includedir, " \t\r\n") == strlen(includedir))
filenames = GetConfFilesInDir(includedir, calling_file, elevel,
&num_filenames, &err_msg);
if (!filenames)
{
ereport(elevel,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("empty configuration directory name: \"%s\"",
includedir)));
record_config_file_error("empty configuration directory name",
calling_file, calling_lineno,
head_p, tail_p);
record_config_file_error(err_msg, calling_file, calling_lineno, head_p,
tail_p);
return false;
}
/*
* We don't check for recursion or too-deep nesting depth here; the
* subsequent calls to ParseConfigFile will take care of that.
*/
directory = AbsoluteConfigLocation(includedir, calling_file);
d = AllocateDir(directory);
if (d == NULL)
for (int i = 0; i < num_filenames; i++)
{
ereport(elevel,
(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;
if (!ParseConfigFile(filenames[i], true,
calling_file, calling_lineno,
depth, elevel,
head_p, tail_p))
return false;
}
/*
* 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)
{
PGFileType de_type;
char filename[MAXPGPATH];
/*
* Only parse files with names ending in ".conf". Explicitly reject
* files starting with ".". This excludes things like "." and "..",
* as well as typical hidden files, backup files, and editor debris.
*/
if (strlen(de->d_name) < 6)
continue;
if (de->d_name[0] == '.')
continue;
if (strcmp(de->d_name + strlen(de->d_name) - 5, ".conf") != 0)
continue;
join_path_components(filename, directory, de->d_name);
canonicalize_path(filename);
de_type = get_dirent_type(filename, de, true, elevel);
if (de_type == PGFILETYPE_ERROR)
{
record_config_file_error(psprintf("could not stat file \"%s\"",
filename),
calling_file, calling_lineno,
head_p, tail_p);
status = false;
goto cleanup;
}
else if (de_type != PGFILETYPE_DIR)
{
/* Add file to array, increasing its size in blocks of 32 */
if (num_filenames >= size_filenames)
{
size_filenames += 32;
filenames = (char **) repalloc(filenames,
size_filenames * sizeof(char *));
}
filenames[num_filenames] = pstrdup(filename);
num_filenames++;
}
}
if (num_filenames > 0)
{
int i;
qsort(filenames, num_filenames, sizeof(char *), pg_qsort_strcmp);
for (i = 0; i < num_filenames; i++)
{
if (!ParseConfigFile(filenames[i], true,
calling_file, calling_lineno,
depth, elevel,
head_p, tail_p))
{
status = false;
goto cleanup;
}
}
}
status = true;
cleanup:
if (d)
FreeDir(d);
pfree(directory);
return status;
return true;
}
/*

View File

@ -1,4 +1,5 @@
backend_sources += files(
'conffiles.c',
'guc.c',
'guc_funcs.c',
'guc_tables.c',

View File

@ -0,0 +1,23 @@
/*--------------------------------------------------------------------
* conffiles.h
*
* Utilities related to configuration files.
*
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/utils/conffiles.h
*
*--------------------------------------------------------------------
*/
#ifndef CONFFILES_H
#define CONFFILES_H
extern char *AbsoluteConfigLocation(const char *location,
const char *calling_file);
extern char **GetConfFilesInDir(const char *includedir,
const char *calling_file,
int elevel, int *num_filenames,
char **err_msg);
#endif /* CONFFILES_H */