/*-------------------------------------------------------------------- * 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-2023, 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) { if (is_absolute_path(location)) return pstrdup(location); else { char abs_path[MAXPGPATH]; 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; }