Age Owner TLA 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-2023, 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 *
153 michael 36 GNC 6267 : AbsoluteConfigLocation(const char *location, const char *calling_file)
37 : {
38 6267 : if (is_absolute_path(location))
39 4015 : return pstrdup(location);
40 : else
41 : {
42 : char abs_path[MAXPGPATH];
43 :
44 2252 : if (calling_file != NULL)
45 : {
46 78 : strlcpy(abs_path, calling_file, sizeof(abs_path));
47 78 : get_parent_directory(abs_path);
48 78 : join_path_components(abs_path, abs_path, location);
49 78 : canonicalize_path(abs_path);
50 : }
51 : else
52 : {
53 2174 : Assert(DataDir);
54 2174 : join_path_components(abs_path, DataDir, location);
55 2174 : canonicalize_path(abs_path);
56 : }
57 2252 : 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 4 : 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 4 : 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 4 : if (strspn(includedir, " \t\r\n") == strlen(includedir))
85 : {
153 michael 86 UNC 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 :
153 michael 94 GNC 4 : directory = AbsoluteConfigLocation(includedir, calling_file);
95 4 : d = AllocateDir(directory);
96 4 : if (d == NULL)
97 : {
153 michael 98 UNC 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 : */
153 michael 110 GNC 4 : size_filenames = 32;
111 4 : filenames = (char **) palloc(size_filenames * sizeof(char *));
112 4 : *num_filenames = 0;
113 :
114 24 : 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 20 : if (strlen(de->d_name) < 6)
125 12 : continue;
126 12 : if (de->d_name[0] == '.')
153 michael 127 UNC 0 : continue;
153 michael 128 GNC 12 : if (strcmp(de->d_name + strlen(de->d_name) - 5, ".conf") != 0)
129 4 : continue;
130 :
131 8 : join_path_components(filename, directory, de->d_name);
132 8 : canonicalize_path(filename);
133 8 : de_type = get_dirent_type(filename, de, true, elevel);
134 8 : if (de_type == PGFILETYPE_ERROR)
135 : {
153 michael 136 UNC 0 : *err_msg = psprintf("could not stat file \"%s\"", filename);
137 0 : pfree(filenames);
138 0 : filenames = NULL;
139 0 : goto cleanup;
140 : }
153 michael 141 GNC 8 : else if (de_type != PGFILETYPE_DIR)
142 : {
143 : /* Add file to array, increasing its size in blocks of 32 */
144 8 : if (*num_filenames >= size_filenames)
145 : {
153 michael 146 UNC 0 : size_filenames += 32;
147 0 : filenames = (char **) repalloc(filenames,
148 : size_filenames * sizeof(char *));
149 : }
153 michael 150 GNC 8 : filenames[*num_filenames] = pstrdup(filename);
151 8 : (*num_filenames)++;
152 : }
153 : }
154 :
155 : /* Sort the files by name before leaving */
156 4 : if (*num_filenames > 0)
157 4 : qsort(filenames, *num_filenames, sizeof(char *), pg_qsort_strcmp);
158 :
153 michael 159 UNC 0 : cleanup:
153 michael 160 GNC 4 : if (d)
161 4 : FreeDir(d);
162 4 : pfree(directory);
163 4 : return filenames;
164 : }
|