mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-24 18:55:04 +08:00
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:
parent
b0b72c64a0
commit
a1a7bb8f16
@ -15,6 +15,7 @@ include $(top_builddir)/src/Makefile.global
|
|||||||
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
|
override CPPFLAGS := -I. -I$(srcdir) $(CPPFLAGS)
|
||||||
|
|
||||||
OBJS = \
|
OBJS = \
|
||||||
|
conffiles.o \
|
||||||
guc.o \
|
guc.o \
|
||||||
guc-file.o \
|
guc-file.o \
|
||||||
guc_funcs.o \
|
guc_funcs.o \
|
||||||
|
164
src/backend/utils/misc/conffiles.c
Normal file
164
src/backend/utils/misc/conffiles.c
Normal 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;
|
||||||
|
}
|
@ -17,6 +17,7 @@
|
|||||||
#include "mb/pg_wchar.h"
|
#include "mb/pg_wchar.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/fd.h"
|
#include "storage/fd.h"
|
||||||
|
#include "utils/conffiles.h"
|
||||||
#include "utils/memutils.h"
|
#include "utils/memutils.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,37 +156,6 @@ ProcessConfigFile(GucContext context)
|
|||||||
MemoryContextDelete(config_cxt);
|
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
|
* Read and parse a single configuration file. This function recurses
|
||||||
* to handle "include" directives.
|
* to handle "include" directives.
|
||||||
@ -605,127 +575,30 @@ ParseConfigDirectory(const char *includedir,
|
|||||||
ConfigVariable **head_p,
|
ConfigVariable **head_p,
|
||||||
ConfigVariable **tail_p)
|
ConfigVariable **tail_p)
|
||||||
{
|
{
|
||||||
char *directory;
|
char *err_msg;
|
||||||
DIR *d;
|
|
||||||
struct dirent *de;
|
|
||||||
char **filenames;
|
char **filenames;
|
||||||
int num_filenames;
|
int num_filenames;
|
||||||
int size_filenames;
|
|
||||||
bool status;
|
|
||||||
|
|
||||||
/*
|
filenames = GetConfFilesInDir(includedir, calling_file, elevel,
|
||||||
* Reject directory name that is all-blank (including empty), as that
|
&num_filenames, &err_msg);
|
||||||
* leads to confusion --- we'd read the containing directory, typically
|
|
||||||
* resulting in recursive inclusion of the same file(s).
|
if (!filenames)
|
||||||
*/
|
|
||||||
if (strspn(includedir, " \t\r\n") == strlen(includedir))
|
|
||||||
{
|
{
|
||||||
ereport(elevel,
|
record_config_file_error(err_msg, calling_file, calling_lineno, head_p,
|
||||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
tail_p);
|
||||||
errmsg("empty configuration directory name: \"%s\"",
|
|
||||||
includedir)));
|
|
||||||
record_config_file_error("empty configuration directory name",
|
|
||||||
calling_file, calling_lineno,
|
|
||||||
head_p, tail_p);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
for (int i = 0; i < num_filenames; i++)
|
||||||
* 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)
|
|
||||||
{
|
{
|
||||||
ereport(elevel,
|
if (!ParseConfigFile(filenames[i], true,
|
||||||
(errcode_for_file_access(),
|
calling_file, calling_lineno,
|
||||||
errmsg("could not open configuration directory \"%s\": %m",
|
depth, elevel,
|
||||||
directory)));
|
head_p, tail_p))
|
||||||
record_config_file_error(psprintf("could not open directory \"%s\"",
|
return false;
|
||||||
directory),
|
|
||||||
calling_file, calling_lineno,
|
|
||||||
head_p, tail_p);
|
|
||||||
status = false;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
return true;
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
backend_sources += files(
|
backend_sources += files(
|
||||||
|
'conffiles.c',
|
||||||
'guc.c',
|
'guc.c',
|
||||||
'guc_funcs.c',
|
'guc_funcs.c',
|
||||||
'guc_tables.c',
|
'guc_tables.c',
|
||||||
|
23
src/include/utils/conffiles.h
Normal file
23
src/include/utils/conffiles.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user