Line data Source code
1 : /*--------------------------------------------------------------------
2 : * conffiles.c
3 : *
4 : * Utilities related to the handling of configuration files.
5 : *
6 : * This file contains some generic tools to work on configuration files
7 : * used by PostgreSQL, be they related to GUCs or authentication.
8 : *
9 : *
10 : * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
11 : * Portions Copyright (c) 1994, Regents of the University of California
12 : *
13 : * IDENTIFICATION
14 : * src/backend/utils/misc/conffiles.c
15 : *
16 : *--------------------------------------------------------------------
17 : */
18 :
19 : #include "postgres.h"
20 :
21 : #include <dirent.h>
22 :
23 : #include "common/file_utils.h"
24 : #include "miscadmin.h"
25 : #include "storage/fd.h"
26 : #include "utils/conffiles.h"
27 :
28 : /*
29 : * AbsoluteConfigLocation
30 : *
31 : * Given a configuration file or directory location that may be a relative
32 : * path, return an absolute one. We consider the location to be relative to
33 : * the directory holding the calling file, or to DataDir if no calling file.
34 : */
35 : char *
36 7718 : AbsoluteConfigLocation(const char *location, const char *calling_file)
37 : {
38 7718 : if (is_absolute_path(location))
39 4770 : return pstrdup(location);
40 : else
41 : {
42 : char abs_path[MAXPGPATH];
43 :
44 2948 : if (calling_file != NULL)
45 : {
46 152 : strlcpy(abs_path, calling_file, sizeof(abs_path));
47 152 : get_parent_directory(abs_path);
48 152 : join_path_components(abs_path, abs_path, location);
49 152 : canonicalize_path(abs_path);
50 : }
51 : else
52 : {
53 : Assert(DataDir);
54 2796 : join_path_components(abs_path, DataDir, location);
55 2796 : canonicalize_path(abs_path);
56 : }
57 2948 : return pstrdup(abs_path);
58 : }
59 : }
60 :
61 :
62 : /*
63 : * GetConfFilesInDir
64 : *
65 : * Returns the list of config files located in a directory, in alphabetical
66 : * order. On error, returns NULL with details about the error stored in
67 : * "err_msg".
68 : */
69 : char **
70 6 : GetConfFilesInDir(const char *includedir, const char *calling_file,
71 : int elevel, int *num_filenames, char **err_msg)
72 : {
73 : char *directory;
74 : DIR *d;
75 : struct dirent *de;
76 6 : char **filenames = NULL;
77 : int size_filenames;
78 :
79 : /*
80 : * Reject directory name that is all-blank (including empty), as that
81 : * leads to confusion --- we'd read the containing directory, typically
82 : * resulting in recursive inclusion of the same file(s).
83 : */
84 6 : if (strspn(includedir, " \t\r\n") == strlen(includedir))
85 : {
86 0 : ereport(elevel,
87 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
88 : errmsg("empty configuration directory name: \"%s\"",
89 : includedir)));
90 0 : *err_msg = "empty configuration directory name";
91 0 : return NULL;
92 : }
93 :
94 6 : directory = AbsoluteConfigLocation(includedir, calling_file);
95 6 : d = AllocateDir(directory);
96 6 : if (d == NULL)
97 : {
98 0 : ereport(elevel,
99 : (errcode_for_file_access(),
100 : errmsg("could not open configuration directory \"%s\": %m",
101 : directory)));
102 0 : *err_msg = psprintf("could not open directory \"%s\"", directory);
103 0 : goto cleanup;
104 : }
105 :
106 : /*
107 : * Read the directory and put the filenames in an array, so we can sort
108 : * them prior to caller processing the contents.
109 : */
110 6 : size_filenames = 32;
111 6 : filenames = (char **) palloc(size_filenames * sizeof(char *));
112 6 : *num_filenames = 0;
113 :
114 36 : while ((de = ReadDir(d, directory)) != NULL)
115 : {
116 : PGFileType de_type;
117 : char filename[MAXPGPATH];
118 :
119 : /*
120 : * Only parse files with names ending in ".conf". Explicitly reject
121 : * files starting with ".". This excludes things like "." and "..",
122 : * as well as typical hidden files, backup files, and editor debris.
123 : */
124 30 : if (strlen(de->d_name) < 6)
125 18 : continue;
126 18 : if (de->d_name[0] == '.')
127 0 : continue;
128 18 : if (strcmp(de->d_name + strlen(de->d_name) - 5, ".conf") != 0)
129 6 : continue;
130 :
131 12 : join_path_components(filename, directory, de->d_name);
132 12 : canonicalize_path(filename);
133 12 : de_type = get_dirent_type(filename, de, true, elevel);
134 12 : if (de_type == PGFILETYPE_ERROR)
135 : {
136 0 : *err_msg = psprintf("could not stat file \"%s\"", filename);
137 0 : pfree(filenames);
138 0 : filenames = NULL;
139 0 : goto cleanup;
140 : }
141 12 : else if (de_type != PGFILETYPE_DIR)
142 : {
143 : /* Add file to array, increasing its size in blocks of 32 */
144 12 : if (*num_filenames >= size_filenames)
145 : {
146 0 : size_filenames += 32;
147 0 : filenames = (char **) repalloc(filenames,
148 : size_filenames * sizeof(char *));
149 : }
150 12 : filenames[*num_filenames] = pstrdup(filename);
151 12 : (*num_filenames)++;
152 : }
153 : }
154 :
155 : /* Sort the files by name before leaving */
156 6 : if (*num_filenames > 0)
157 6 : qsort(filenames, *num_filenames, sizeof(char *), pg_qsort_strcmp);
158 :
159 0 : cleanup:
160 6 : if (d)
161 6 : FreeDir(d);
162 6 : pfree(directory);
163 6 : return filenames;
164 : }
|