mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-28 11:55:03 +03:00 
			
		
		
		
	Add user-specific .pg_service.conf file
This extends the existing pg_service.conf facility to first look for a service definition file in the user's home directory.
This commit is contained in:
		| @@ -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> | ||||
|  | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user