summaryrefslogtreecommitdiff
path: root/src/backend/utils/misc/conffiles.c
blob: 376a5c885bbcb971db8d16157f8c4bcd3869eed6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
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-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 <dirent.h>

#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;
}