I love centralizing connection service definitions in
<sysconfdir>/pg_service.conf, but for a
large enterprise, sometimes we have multiple sets of connection
service definitions
independently managed.

The convention widely adopted for this type of thing is to allow
multiple config files to be in
a directory, usually the '.d' version of the config filename. See, for example:

    http://blog.siphos.be/2013/05/the-linux-d-approach/
    https://lists.debian.org/debian-devel/2010/04/msg00352.html

This patch adds an extra search for service connection definitions if
the current places fail
to find the service (~/.pg_service.conf <sysconfdir>/pg_service.conf).
It will then search
every readable file in the directory <sysconfdir>/pg_service.conf.d.

What do you think?

Curt
diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index 4e46451..ec4ba77 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -7421,6 +7421,9 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
   <indexterm zone="libpq-pgservice">
    <primary>.pg_service.conf</primary>
   </indexterm>
+  <indexterm zone="libpq-pgservice">
+   <primary>pg_service.conf.d</primary>
+  </indexterm>
 
   <para>
    The connection service file allows libpq connection parameters to be
@@ -7444,6 +7447,15 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
   </para>
 
   <para>
+    Additional connection service files can be placed in the system-wide
+    directory <filename>`pg_config --sysconfdir`/pg_service.conf.d</filename>
+    or in the subdirectory <filename>pg_service.conf.d</filename> below the
+    directory specified by the environment variable
+    <envar>PGSYSCONFDIR</envar>.  These will have the lowest precedence,
+    following the files described above.
+  </para>
+
+  <para>
    The file uses an <quote>INI file</quote> format where the section
    name is the service name and the parameters are connection
    parameters; see <xref linkend="libpq-paramkeywords"/> for a list.  For
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 8d54333..43ec0d5 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -20,6 +20,7 @@
 #include <ctype.h>
 #include <time.h>
 #include <unistd.h>
+#include <dirent.h>
 
 #include "libpq-fe.h"
 #include "libpq-int.h"
@@ -4492,10 +4493,14 @@ parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
 {
 	const char *service = conninfo_getval(options, "service");
 	char		serviceFile[MAXPGPATH];
+	char		serviceDirPath[MAXPGPATH];
 	char	   *env;
 	bool		group_found = false;
 	int			status;
 	struct stat stat_buf;
+	DIR		   *serviceDir;
+	struct dirent *direntry;
+
 
 	/*
 	 * We have to special-case the environment variable PGSERVICE here, since
@@ -4539,21 +4544,45 @@ next_file:
 	snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf",
 			 getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR);
 	if (stat(serviceFile, &stat_buf) != 0)
+		goto conf_dir;
+
+	status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found);
+	if (group_found || status != 0)
+		return status;
+
+conf_dir:
+
+	/*
+	 * Try every file in pg_service.conf.d/*
+	 */
+	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) ||
+		(serviceDir = opendir(serviceDirPath)) == NULL)
 		goto last_file;
 
-	status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found);
-	if (status != 0)
-		return status;
+	while ((direntry = readdir(serviceDir)) != NULL)
+	{
+		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)
+		{
+			closedir(serviceDir);
+			return status;
+		}
+	}
+	closedir(serviceDir);
 
 last_file:
-	if (!group_found)
-	{
-		printfPQExpBuffer(errorMessage,
-						  libpq_gettext("definition of service \"%s\" not found\n"), service);
-		return 3;
-	}
-
-	return 0;
+	printfPQExpBuffer(errorMessage,
+					  libpq_gettext("definition of service \"%s\" not found\n"), service);
+	return 3;
 }
 
 static int

Reply via email to