Add user-specific .pg_service.conf file
authorPeter Eisentraut <peter_e@gmx.net>
Wed, 20 Jan 2010 21:15:21 +0000 (21:15 +0000)
committerPeter Eisentraut <peter_e@gmx.net>
Wed, 20 Jan 2010 21:15:21 +0000 (21:15 +0000)
This extends the existing pg_service.conf facility to first look for a
service definition file in the user's home directory.

doc/src/sgml/libpq.sgml
src/interfaces/libpq/fe-connect.c

index cfa87d0adaec3f77c13e2fbd1085ff75cf9d5540..a5386cf333d92470583a1bd655cfc3c918c232d5 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.293 2010/01/20 00:42:28 rhaas Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/libpq.sgml,v 1.294 2010/01/20 21:15:21 petere Exp $ -->
 
 <chapter id="libpq">
  <title><application>libpq</application> - C Library</title>
@@ -5791,6 +5791,18 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
      </para>
     </listitem>
 
+    <listitem>
+     <para>
+      <indexterm>
+       <primary><envar>PGSERVICEFILE</envar></primary>
+      </indexterm>
+      <envar>PGSERVICEFILE</envar> specifies the name of the per-user
+      connection service file.  If not set, it defaults
+      to <filename>~/.pg_service.conf</>
+      (see <xref linkend="libpq-pgservice">).
+     </para>
+    </listitem>
+
     <listitem>
      <para>
       <indexterm>
@@ -5987,7 +5999,8 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
        <primary><envar>PGSYSCONFDIR</envar></primary>
       </indexterm>
       <envar>PGSYSCONFDIR</envar> sets the directory containing the
-      <filename>pg_service.conf</> file.
+      <filename>pg_service.conf</> file and in a future version
+      possibly other system-wide configuration files.
      </para>
     </listitem>
 
@@ -6063,6 +6076,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</primary>
+  </indexterm>
 
   <para>
    The connection service file allows libpq connection parameters to be
@@ -6074,12 +6090,31 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
   </para>
 
   <para>
-   To use this feature, copy
-   <filename>share/pg_service.conf.sample</filename> to
-   <filename>etc/pg_service.conf</filename> and edit the file to add
-   service names and parameters. This file can be used for client-only
-   installs too. The file's location can also be specified by the
-   <envar>PGSYSCONFDIR</envar> environment variable.
+   The connection service file can be a per-user service file
+   at <filename>~/.pg_service.conf</filename> or the location
+   specified by the environment variable <envar>PGSERVICEFILE</envar>,
+   or it can be a system-wide file
+   at <filename>etc/pg_service.conf</filename> or in the directory
+   specified by the environment variable
+   <envar>PGSYSCONFDIR</envar>.  If service definitions with the same
+   name exist in the user and the system file, the user file takes
+   precedence.
+  </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-connect"> for a list.  For
+   example:
+<programlisting>
+# comment
+[mydb]
+host=somehost
+port=5433
+user=admin
+</programlisting>
+   An example file is provided at
+   <filename>share/pg_service.conf.sample</filename>.
   </para>
  </sect1>
 
index 125d93cf0b1eb0f6cadd40de101f728191cf6932..4d7be078c0725994ef240cb98b14f3ce02b49665 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.383 2010/01/15 09:19:10 heikki Exp $
+ *   $PostgreSQL: pgsql/src/interfaces/libpq/fe-connect.c,v 1.384 2010/01/20 21:15:21 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -272,6 +272,11 @@ 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 parseServiceFile(const char *serviceFile,
+                           const char *service,
+                           PQconninfoOption *options,
+                           PQExpBuffer errorMessage,
+                           bool *group_found);
 static char *pwdfMatchesString(char *buf, char *token);
 static char *PasswordFromFile(char *hostname, char *port, char *dbname,
                 char *username);
@@ -3095,9 +3100,10 @@ parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
 {
    char       *service = conninfo_getval(options, "service");
    char        serviceFile[MAXPGPATH];
+   char       *env;
    bool        group_found = false;
-   int         linenr = 0,
-               i;
+   int         status;
+   struct stat stat_buf;
 
    /*
     * We have to special-case the environment variable PGSERVICE here, since
@@ -3107,154 +3113,196 @@ parseServiceInfo(PQconninfoOption *options, PQExpBuffer errorMessage)
    if (service == NULL)
        service = getenv("PGSERVICE");
 
+   if (service == NULL)
+       return 0;
+
+   if ((env = getenv("PGSERVICEFILE")) != NULL)
+       strlcpy(serviceFile, env, sizeof(serviceFile));
+   else
+   {
+       char        homedir[MAXPGPATH];
+
+       if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
+       {
+           printfPQExpBuffer(errorMessage, libpq_gettext("could not get home directory to locate service definition file"));
+           return 1;
+       }
+       snprintf(serviceFile, MAXPGPATH, "%s/%s", homedir, ".pg_service.conf");
+       errno = 0;
+       if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT)
+           goto next_file;
+   }
+
+   status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found);
+   if (group_found || status != 0)
+       return status;
+
+next_file:
    /*
     * This could be used by any application so we can't use the binary
     * location to find our config files.
     */
    snprintf(serviceFile, MAXPGPATH, "%s/pg_service.conf",
             getenv("PGSYSCONFDIR") ? getenv("PGSYSCONFDIR") : SYSCONFDIR);
+   errno = 0;
+   if (stat(serviceFile, &stat_buf) != 0 && errno == ENOENT)
+       goto last_file;
+
+   status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found);
+   if (status != 0)
+       return status;
+
+last_file:
+   if (!group_found)
+   {
+       printfPQExpBuffer(errorMessage,
+                         libpq_gettext("definition of service \"%s\" not found\n"), service);
+       return 3;
+   }
+
+   return 0;
+}
 
-   if (service != NULL)
+static int
+parseServiceFile(const char *serviceFile,
+                const char *service,
+                PQconninfoOption *options,
+                PQExpBuffer errorMessage,
+                bool *group_found)
+{  
+   int         linenr = 0,
+               i;
+   FILE       *f;
+   char        buf[MAXBUFSIZE],
+              *line;
+
+   f = fopen(serviceFile, "r");
+   if (f == NULL)
+   {
+       printfPQExpBuffer(errorMessage, libpq_gettext("service file \"%s\" not found\n"),
+                         serviceFile);
+       return 1;
+   }
+
+   while ((line = fgets(buf, sizeof(buf), f)) != NULL)
    {
-       FILE       *f;
-       char        buf[MAXBUFSIZE],
-                  *line;
+       linenr++;
 
-       f = fopen(serviceFile, "r");
-       if (f == NULL)
+       if (strlen(line) >= sizeof(buf) - 1)
        {
-           printfPQExpBuffer(errorMessage, libpq_gettext("service file \"%s\" not found\n"),
+           fclose(f);
+           printfPQExpBuffer(errorMessage,
+                             libpq_gettext("line %d too long in service file \"%s\"\n"),
+                             linenr,
                              serviceFile);
-           return 1;
+           return 2;
        }
 
-       while ((line = fgets(buf, sizeof(buf), f)) != NULL)
-       {
-           linenr++;
-
-           if (strlen(line) >= sizeof(buf) - 1)
-           {
-               fclose(f);
-               printfPQExpBuffer(errorMessage,
-                                 libpq_gettext("line %d too long in service file \"%s\"\n"),
-                                 linenr,
-                                 serviceFile);
-               return 2;
-           }
+       /* ignore EOL at end of line */
+       if (strlen(line) && line[strlen(line) - 1] == '\n')
+           line[strlen(line) - 1] = 0;
 
-           /* ignore EOL at end of line */
-           if (strlen(line) && line[strlen(line) - 1] == '\n')
-               line[strlen(line) - 1] = 0;
+       /* ignore leading blanks */
+       while (*line && isspace((unsigned char) line[0]))
+           line++;
 
-           /* ignore leading blanks */
-           while (*line && isspace((unsigned char) line[0]))
-               line++;
-
-           /* ignore comments and empty lines */
-           if (strlen(line) == 0 || line[0] == '#')
-               continue;
+       /* ignore comments and empty lines */
+       if (strlen(line) == 0 || line[0] == '#')
+           continue;
 
-           /* Check for right groupname */
-           if (line[0] == '[')
+       /* Check for right groupname */
+       if (line[0] == '[')
+       {
+           if (*group_found)
            {
-               if (group_found)
-               {
-                   /* group info already read */
-                   fclose(f);
-                   return 0;
-               }
-
-               if (strncmp(line + 1, service, strlen(service)) == 0 &&
-                   line[strlen(service) + 1] == ']')
-                   group_found = true;
-               else
-                   group_found = false;
+               /* group info already read */
+               fclose(f);
+               return 0;
            }
+
+           if (strncmp(line + 1, service, strlen(service)) == 0 &&
+               line[strlen(service) + 1] == ']')
+               *group_found = true;
            else
+               *group_found = false;
+       }
+       else
+       {
+           if (*group_found)
            {
-               if (group_found)
-               {
-                   /*
-                    * Finally, we are in the right group and can parse the
-                    * line
-                    */
-                   char       *key,
-                              *val;
-                   bool        found_keyword;
+               /*
+                * Finally, we are in the right group and can parse
+                * the line
+                */
+               char       *key,
+                          *val;
+               bool        found_keyword;
 
 #ifdef USE_LDAP
-                   if (strncmp(line, "ldap", 4) == 0)
-                   {
-                       int         rc = ldapServiceLookup(line, options, errorMessage);
+               if (strncmp(line, "ldap", 4) == 0)
+               {
+                   int         rc = ldapServiceLookup(line, options, errorMessage);
 
-                       /* if rc = 2, go on reading for fallback */
-                       switch (rc)
-                       {
-                           case 0:
-                               fclose(f);
-                               return 0;
-                           case 1:
-                           case 3:
-                               fclose(f);
-                               return 3;
-                           case 2:
-                               continue;
-                       }
+                   /* if rc = 2, go on reading for fallback */
+                   switch (rc)
+                   {
+                       case 0:
+                           fclose(f);
+                           return 0;
+                       case 1:
+                       case 3:
+                           fclose(f);
+                           return 3;
+                       case 2:
+                           continue;
                    }
+               }
 #endif
 
-                   key = line;
-                   val = strchr(line, '=');
-                   if (val == NULL)
-                   {
-                       printfPQExpBuffer(errorMessage,
-                                         libpq_gettext("syntax error in service file \"%s\", line %d\n"),
-                                         serviceFile,
-                                         linenr);
-                       fclose(f);
-                       return 3;
-                   }
-                   *val++ = '\0';
+               key = line;
+               val = strchr(line, '=');
+               if (val == NULL)
+               {
+                   printfPQExpBuffer(errorMessage,
+                                     libpq_gettext("syntax error in service file \"%s\", line %d\n"),
+                                     serviceFile,
+                                     linenr);
+                   fclose(f);
+                   return 3;
+               }
+               *val++ = '\0';
 
-                   /*
-                    * Set the parameter --- but don't override any previous
-                    * explicit setting.
-                    */
-                   found_keyword = false;
-                   for (i = 0; options[i].keyword; i++)
+               /*
+                * Set the parameter --- but don't override any previous
+                * explicit setting.
+                */
+               found_keyword = false;
+               for (i = 0; options[i].keyword; i++)
+               {
+                   if (strcmp(options[i].keyword, key) == 0)
                    {
-                       if (strcmp(options[i].keyword, key) == 0)
-                       {
-                           if (options[i].val == NULL)
-                               options[i].val = strdup(val);
-                           found_keyword = true;
-                           break;
-                       }
+                       if (options[i].val == NULL)
+                           options[i].val = strdup(val);
+                       found_keyword = true;
+                       break;
                    }
+               }
 
-                   if (!found_keyword)
-                   {
-                       printfPQExpBuffer(errorMessage,
-                                         libpq_gettext("syntax error in service file \"%s\", line %d\n"),
-                                         serviceFile,
-                                         linenr);
-                       fclose(f);
-                       return 3;
-                   }
+               if (!found_keyword)
+               {
+                   printfPQExpBuffer(errorMessage,
+                                     libpq_gettext("syntax error in service file \"%s\", line %d\n"),
+                                     serviceFile,
+                                     linenr);
+                   fclose(f);
+                   return 3;
                }
            }
        }
-
-       fclose(f);
-
-       if (!group_found)
-       {
-           printfPQExpBuffer(errorMessage,
-                             libpq_gettext("definition of service \"%s\" not found\n"), service);
-           return 3;
-       }
    }
 
+   fclose(f);
+
    return 0;
 }