On ons, 2010-01-13 at 23:49 +0200, Peter Eisentraut wrote: > I was surprised/annoyed to find out that there is no way to have > per-user pg_service.conf, something like ~/.pg_service.conf (well, > except by export PGSYSCONFDIR). That would be easy to add. Comments?
Here's a patch. Perhaps those who had said they would like that can validate the behavior.
*** doc/src/sgml/libpq.sgml 2 Dec 2009 14:07:25 -0000 1.292 --- doc/src/sgml/libpq.sgml 14 Jan 2010 17:02:59 -0000 *************** *** 5786,5791 **** myEventProc(PGEventId evtId, void *evtIn --- 5786,5803 ---- <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> <primary><envar>PGREALM</envar></primary> </indexterm> <envar>PGREALM</envar> sets the Kerberos realm to use with *************** *** 5979,5985 **** myEventProc(PGEventId evtId, void *evtIn <primary><envar>PGSYSCONFDIR</envar></primary> </indexterm> <envar>PGSYSCONFDIR</envar> sets the directory containing the ! <filename>pg_service.conf</> file. </para> </listitem> --- 5991,5998 ---- <primary><envar>PGSYSCONFDIR</envar></primary> </indexterm> <envar>PGSYSCONFDIR</envar> sets the directory containing the ! <filename>pg_service.conf</> file and in a future version ! possibly other system-wide configuration files. </para> </listitem> *************** *** 6055,6060 **** myEventProc(PGEventId evtId, void *evtIn --- 6068,6076 ---- <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 *************** *** 6066,6077 **** myEventProc(PGEventId evtId, void *evtIn </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. </para> </sect1> --- 6082,6111 ---- </para> <para> ! 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. 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> *** src/interfaces/libpq/fe-connect.c 2 Jan 2010 16:58:11 -0000 1.382 --- src/interfaces/libpq/fe-connect.c 14 Jan 2010 17:02:59 -0000 *************** *** 269,274 **** static void defaultNoticeReceiver(void * --- 269,279 ---- 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); *************** *** 3088,3096 **** parseServiceInfo(PQconninfoOption *optio { char *service = conninfo_getval(options, "service"); char serviceFile[MAXPGPATH]; bool group_found = false; ! int linenr = 0, ! i; /* * We have to special-case the environment variable PGSERVICE here, since --- 3093,3102 ---- { char *service = conninfo_getval(options, "service"); char serviceFile[MAXPGPATH]; + char *env; bool group_found = false; ! int status; ! struct stat stat_buf; /* * We have to special-case the environment variable PGSERVICE here, since *************** *** 3100,3253 **** parseServiceInfo(PQconninfoOption *optio if (service == NULL) service = getenv("PGSERVICE"); /* * 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); ! if (service != NULL) { ! 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) ! { ! 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 leading blanks */ ! while (*line && isspace((unsigned char) line[0])) ! line++; ! /* ignore comments and empty lines */ ! if (strlen(line) == 0 || line[0] == '#') ! continue; ! /* Check for right groupname */ ! if (line[0] == '[') { ! 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; } else { ! if (group_found) ! { ! /* ! * 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 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'; ! /* ! * 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 (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; ! } } } } - - fclose(f); - - if (!group_found) - { - printfPQExpBuffer(errorMessage, - libpq_gettext("definition of service \"%s\" not found\n"), service); - return 3; - } } return 0; } --- 3106,3301 ---- 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; ! } ! ! 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) ! { ! 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 leading blanks */ ! while (*line && isspace((unsigned char) line[0])) ! line++; ! /* ignore comments and empty lines */ ! if (strlen(line) == 0 || line[0] == '#') ! continue; ! /* Check for right groupname */ ! if (line[0] == '[') ! { ! 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; + } + else + { + if (*group_found) { ! /* ! * 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 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'; ! /* ! * 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 (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; } } } } + fclose(f); + return 0; }
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers