/src/postgres/src/backend/utils/misc/conffiles.c
Line | Count | Source |
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 | | AbsoluteConfigLocation(const char *location, const char *calling_file) |
37 | 0 | { |
38 | 0 | if (is_absolute_path(location)) |
39 | 0 | return pstrdup(location); |
40 | 0 | else |
41 | 0 | { |
42 | 0 | char abs_path[MAXPGPATH]; |
43 | |
|
44 | 0 | if (calling_file != NULL) |
45 | 0 | { |
46 | 0 | strlcpy(abs_path, calling_file, sizeof(abs_path)); |
47 | 0 | get_parent_directory(abs_path); |
48 | 0 | join_path_components(abs_path, abs_path, location); |
49 | 0 | canonicalize_path(abs_path); |
50 | 0 | } |
51 | 0 | else |
52 | 0 | { |
53 | 0 | Assert(DataDir); |
54 | 0 | join_path_components(abs_path, DataDir, location); |
55 | 0 | canonicalize_path(abs_path); |
56 | 0 | } |
57 | 0 | return pstrdup(abs_path); |
58 | 0 | } |
59 | 0 | } |
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 | | GetConfFilesInDir(const char *includedir, const char *calling_file, |
71 | | int elevel, int *num_filenames, char **err_msg) |
72 | 0 | { |
73 | 0 | char *directory; |
74 | 0 | DIR *d; |
75 | 0 | struct dirent *de; |
76 | 0 | char **filenames = NULL; |
77 | 0 | 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 | 0 | if (strspn(includedir, " \t\r\n") == strlen(includedir)) |
85 | 0 | { |
86 | 0 | ereport(elevel, |
87 | 0 | (errcode(ERRCODE_INVALID_PARAMETER_VALUE), |
88 | 0 | errmsg("empty configuration directory name: \"%s\"", |
89 | 0 | includedir))); |
90 | 0 | *err_msg = "empty configuration directory name"; |
91 | 0 | return NULL; |
92 | 0 | } |
93 | | |
94 | 0 | directory = AbsoluteConfigLocation(includedir, calling_file); |
95 | 0 | d = AllocateDir(directory); |
96 | 0 | if (d == NULL) |
97 | 0 | { |
98 | 0 | ereport(elevel, |
99 | 0 | (errcode_for_file_access(), |
100 | 0 | errmsg("could not open configuration directory \"%s\": %m", |
101 | 0 | directory))); |
102 | 0 | *err_msg = psprintf("could not open directory \"%s\"", directory); |
103 | 0 | goto cleanup; |
104 | 0 | } |
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 | 0 | size_filenames = 32; |
111 | 0 | filenames = (char **) palloc(size_filenames * sizeof(char *)); |
112 | 0 | *num_filenames = 0; |
113 | |
|
114 | 0 | while ((de = ReadDir(d, directory)) != NULL) |
115 | 0 | { |
116 | 0 | PGFileType de_type; |
117 | 0 | 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 | 0 | if (strlen(de->d_name) < 6) |
125 | 0 | continue; |
126 | 0 | if (de->d_name[0] == '.') |
127 | 0 | continue; |
128 | 0 | if (strcmp(de->d_name + strlen(de->d_name) - 5, ".conf") != 0) |
129 | 0 | continue; |
130 | | |
131 | 0 | join_path_components(filename, directory, de->d_name); |
132 | 0 | canonicalize_path(filename); |
133 | 0 | de_type = get_dirent_type(filename, de, true, elevel); |
134 | 0 | if (de_type == PGFILETYPE_ERROR) |
135 | 0 | { |
136 | 0 | *err_msg = psprintf("could not stat file \"%s\"", filename); |
137 | 0 | pfree(filenames); |
138 | 0 | filenames = NULL; |
139 | 0 | goto cleanup; |
140 | 0 | } |
141 | 0 | else if (de_type != PGFILETYPE_DIR) |
142 | 0 | { |
143 | | /* Add file to array, increasing its size in blocks of 32 */ |
144 | 0 | if (*num_filenames >= size_filenames) |
145 | 0 | { |
146 | 0 | size_filenames += 32; |
147 | 0 | filenames = (char **) repalloc(filenames, |
148 | 0 | size_filenames * sizeof(char *)); |
149 | 0 | } |
150 | 0 | filenames[*num_filenames] = pstrdup(filename); |
151 | 0 | (*num_filenames)++; |
152 | 0 | } |
153 | 0 | } |
154 | | |
155 | | /* Sort the files by name before leaving */ |
156 | 0 | if (*num_filenames > 0) |
157 | 0 | qsort(filenames, *num_filenames, sizeof(char *), pg_qsort_strcmp); |
158 | |
|
159 | 0 | cleanup: |
160 | 0 | if (d) |
161 | 0 | FreeDir(d); |
162 | 0 | pfree(directory); |
163 | 0 | return filenames; |
164 | 0 | } |