diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index cfa87d0ada..a5386cf333 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -1,4 +1,4 @@ - + <application>libpq</application> - C Library @@ -5791,6 +5791,18 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) + + + + PGSERVICEFILE + + PGSERVICEFILE specifies the name of the per-user + connection service file. If not set, it defaults + to ~/.pg_service.conf + (see ). + + + @@ -5987,7 +5999,8 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) PGSYSCONFDIR PGSYSCONFDIR sets the directory containing the - pg_service.conf file. + pg_service.conf file and in a future version + possibly other system-wide configuration files. @@ -6063,6 +6076,9 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) pg_service.conf + + .pg_service.conf + The connection service file allows libpq connection parameters to be @@ -6074,12 +6090,31 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough) - To use this feature, copy - share/pg_service.conf.sample to - etc/pg_service.conf 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 - PGSYSCONFDIR environment variable. + The connection service file can be a per-user service file + at ~/.pg_service.conf or the location + specified by the environment variable PGSERVICEFILE, + or it can be a system-wide file + at etc/pg_service.conf or in the directory + specified by the environment variable + PGSYSCONFDIR. If service definitions with the same + name exist in the user and the system file, the user file takes + precedence. + + + + 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 + example: + +# comment +[mydb] +host=somehost +port=5433 +user=admin + + An example file is provided at + share/pg_service.conf.sample. diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 125d93cf0b..4d7be078c0 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -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; - if (service != NULL) + status = parseServiceFile(serviceFile, service, options, errorMessage, &group_found); + if (status != 0) + return status; + +last_file: + if (!group_found) { - FILE *f; - char buf[MAXBUFSIZE], - *line; + printfPQExpBuffer(errorMessage, + libpq_gettext("definition of service \"%s\" not found\n"), service); + return 3; + } - f = fopen(serviceFile, "r"); - if (f == NULL) + 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) { - 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) + /* 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] == '[') { - linenr++; - - if (strlen(line) >= sizeof(buf) - 1) + if (*group_found) { + /* group info already read */ fclose(f); - printfPQExpBuffer(errorMessage, - libpq_gettext("line %d too long in service file \"%s\"\n"), - linenr, - serviceFile); - return 2; + return 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 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; - } + 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; }