From a1a7bb8f16cd5529d76850c85625039ad7aa348a Mon Sep 17 00:00:00 2001 From: Michael Paquier Date: Mon, 7 Nov 2022 12:31:38 +0900 Subject: [PATCH] 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 --- src/backend/utils/misc/Makefile | 1 + src/backend/utils/misc/conffiles.c | 164 +++++++++++++++++++++++++++++ src/backend/utils/misc/guc-file.l | 157 +++------------------------ src/backend/utils/misc/meson.build | 1 + src/include/utils/conffiles.h | 23 ++++ 5 files changed, 204 insertions(+), 142 deletions(-) create mode 100644 src/backend/utils/misc/conffiles.c create mode 100644 src/include/utils/conffiles.h diff --git a/src/backend/utils/misc/Makefile b/src/backend/utils/misc/Makefile index 6097309033..b9ee4eb48a 100644 --- a/src/backend/utils/misc/Makefile +++ b/src/backend/utils/misc/Makefile @@ -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 \ diff --git a/src/backend/utils/misc/conffiles.c b/src/backend/utils/misc/conffiles.c new file mode 100644 index 0000000000..4a99a1961e --- /dev/null +++ b/src/backend/utils/misc/conffiles.c @@ -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 + +#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; +} diff --git a/src/backend/utils/misc/guc-file.l b/src/backend/utils/misc/guc-file.l index 2aa48ec969..88245475d1 100644 --- a/src/backend/utils/misc/guc-file.l +++ b/src/backend/utils/misc/guc-file.l @@ -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; } /* diff --git a/src/backend/utils/misc/meson.build b/src/backend/utils/misc/meson.build index db4de225e1..e7a9730229 100644 --- a/src/backend/utils/misc/meson.build +++ b/src/backend/utils/misc/meson.build @@ -1,4 +1,5 @@ backend_sources += files( + 'conffiles.c', 'guc.c', 'guc_funcs.c', 'guc_tables.c', diff --git a/src/include/utils/conffiles.h b/src/include/utils/conffiles.h new file mode 100644 index 0000000000..3f23a2a011 --- /dev/null +++ b/src/include/utils/conffiles.h @@ -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 */