diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 4e46451..5c00e2a 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -7421,6 +7421,9 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) .pg_service.conf + + pg_service.conf.d + The connection service file allows libpq connection parameters to be @@ -7444,6 +7447,17 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) + Additional connection service files can be placed in the system-wide + directory `pg_config --sysconfdir`/pg_service.conf.d + or in the subdirectory pg_service.conf.d below the + directory specified by the environment variable + PGSYSCONFDIR. These will have the lowest precedence, + following the files described above. Files must end in '.conf' and + must not begin with '.'. Note if a single service is defined in + multiple files in this directory, which one gets used is not predictable. + + + The file uses an INI file format where the section name is the service name and the parameters are connection parameters; see for a list. For diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 8d54333..9bd742f 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "libpq-fe.h" #include "libpq-int.h" @@ -400,6 +401,10 @@ static void defaultNoticeReceiver(void *arg, const PGresult *res); static void defaultNoticeProcessor(void *arg, const char *message); static int parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage); +static int searchServiceFileDirectory(const char *service, + PQconninfoOption *options, + PQExpBuffer errorMessage, + bool *group_found); static int parseServiceFile(const char *serviceFile, const char *service, PQconninfoOption *options, @@ -4542,10 +4547,15 @@ next_file: goto last_file; status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found); - if (status != 0) + if (group_found || status != 0) return status; last_file: + + status = searchServiceFileDirectory(service, options, errorMessage, &group_found); + if (status != 0) + return status; + if (!group_found) { printfPQExpBuffer(errorMessage, @@ -4556,6 +4566,64 @@ last_file: return 0; } +/* + * searchServiceFileDirectory: Try to parse every file in pg_service.conf.d + * as a pg_service.conf style file. + * + * Returns 0 on success, nonzero on failure. + */ +static int +searchServiceFileDirectory(const char *service, + PQconninfoOption *options, + PQExpBuffer errorMessage, + bool *group_found) +{ + char serviceDirPath[MAXPGPATH]; + char serviceFile[MAXPGPATH]; + int status; + int filenamelen; + struct stat stat_buf; + struct dirent *direntry; + DIR *serviceDir; + + snprintf(serviceDirPath, MAXPGPATH, "%s/pg_service.conf.d", + getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR); + + if (stat(serviceDirPath, &stat_buf) != 0 || !S_ISDIR(stat_buf.st_mode)) + return 0; + + serviceDir = opendir(serviceDirPath); + if (serviceDir == NULL) + return 0; + + while ((direntry = readdir(serviceDir)) != NULL) + { + if (direntry->d_name[0] == '.') + continue; + + filenamelen = strlen(direntry->d_name); + if (filenamelen < 6 + || strcmp(direntry->d_name + filenamelen - 5, ".conf")) + continue; + + snprintf(serviceFile, MAXPGPATH, "%s/%s", serviceDirPath, + direntry->d_name); + + if (stat(serviceFile, &stat_buf) != 0 || !S_ISREG(stat_buf.st_mode) + || access(serviceFile, R_OK)) + continue; + + status = parseServiceFile(serviceFile, service, options, errorMessage, + group_found); + if (*group_found || status != 0) + break; + } + + closedir(serviceDir); + + return status; +} + static int parseServiceFile(const char *serviceFile, const char *service,