postgresql/src/port/thread.c

147 lines
4.4 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
2003-08-08 05:09:56 +02:00
* thread.c
*
* Prototypes and macros around system calls, used to help make
* threaded libraries reentrant and safe to use from threaded applications.
*
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
*
2010-09-20 22:08:53 +02:00
* src/port/thread.c
*
*-------------------------------------------------------------------------
*/
#include "c.h"
#include <pwd.h>
/*
* Threading sometimes requires specially-named versions of functions
* that return data in static buffers, like strerror_r() instead of
* strerror(). Other operating systems use pthread_setspecific()
* and pthread_getspecific() internally to allow standard library
* functions to return static data to threaded applications. And some
* operating systems have neither.
*
* Additional confusion exists because many operating systems that
* use pthread_setspecific/pthread_getspecific() also have *_r versions
* of standard library functions for compatibility with operating systems
* that require them. However, internally, these *_r functions merely
* call the thread-safe standard library functions.
*
* For example, BSD/OS 4.3 uses Bind 8.2.3 for getpwuid(). Internally,
* getpwuid() calls pthread_setspecific/pthread_getspecific() to return
* static data to the caller in a thread-safe manner. However, BSD/OS
* also has getpwuid_r(), which merely calls getpwuid() and shifts
* around the arguments to match the getpwuid_r() function declaration.
* Therefore, while BSD/OS has getpwuid_r(), it isn't required. It also
* doesn't have strerror_r(), so we can't fall back to only using *_r
* functions for threaded programs.
*
* The current setup is to try threading in this order:
*
* use *_r function names if they exit
* (*_THREADSAFE=yes)
* use non-*_r functions if they are thread-safe
*
* One thread-safe solution for gethostbyname() might be to use getaddrinfo().
*
* Run src/test/thread to test if your operating system has thread-safe
* non-*_r functions.
*/
2004-08-29 07:07:03 +02:00
/*
* Wrapper around strerror and strerror_r to use the former if it is
* available and also return a more useful value (the error string).
*/
char *
pqStrerror(int errnum, char *strerrbuf, size_t buflen)
{
#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(HAVE_STRERROR_R)
/* reentrant strerror_r is available */
#ifdef STRERROR_R_INT
/* SUSv3 version */
if (strerror_r(errnum, strerrbuf, buflen) == 0)
return strerrbuf;
else
return "Unknown error";
#else
/* GNU libc */
return strerror_r(errnum, strerrbuf, buflen);
#endif
#else
/* no strerror_r() available, just use strerror */
strlcpy(strerrbuf, strerror(errnum), buflen);
return strerrbuf;
#endif
}
/*
* Wrapper around getpwuid() or getpwuid_r() to mimic POSIX getpwuid_r()
* behaviour, if that function is not available or required.
Fix libpq's behavior when /etc/passwd isn't readable. Some users run their applications in chroot environments that lack an /etc/passwd file. This means that the current UID's user name and home directory are not obtainable. libpq used to be all right with that, so long as the database role name to use was specified explicitly. But commit a4c8f14364c27508233f8a31ac4b10a4c90235a9 broke such cases by causing any failure of pg_fe_getauthname() to be treated as a hard error. In any case it did little to advance its nominal goal of causing errors in pg_fe_getauthname() to be reported better. So revert that and instead put some real error-reporting code in place. This requires changes to the APIs of pg_fe_getauthname() and pqGetpwuid(), since the latter had departed from the POSIX-specified API of getpwuid_r() in a way that made it impossible to distinguish actual lookup errors from "no such user". To allow such failures to be reported, while not failing if the caller supplies a role name, add a second call of pg_fe_getauthname() in connectOptions2(). This is a tad ugly, and could perhaps be avoided with some refactoring of PQsetdbLogin(), but I'll leave that idea for later. (Note that the complained-of misbehavior only occurs in PQsetdbLogin, not when using the PQconnect functions, because in the latter we will never bother to call pg_fe_getauthname() if the user gives a role name.) In passing also clean up the Windows-side usage of GetUserName(): the recommended buffer size is 257 bytes, the passed buffer length should be the buffer size not buffer size less 1, and any error is reported by GetLastError() not errno. Per report from Christoph Berg. Back-patch to 9.4 where the chroot failure case was introduced. The generally poor reporting of errors here is of very long standing, of course, but given the lack of field complaints about it we won't risk changing these APIs further back (even though they're theoretically internal to libpq).
2015-01-11 18:35:44 +01:00
*
* Per POSIX, the possible cases are:
* success: returns zero, *result is non-NULL
* uid not found: returns zero, *result is NULL
* error during lookup: returns an errno code, *result is NULL
* (caller should *not* assume that the errno variable is set)
*/
#ifndef WIN32
int
2004-08-29 07:07:03 +02:00
pqGetpwuid(uid_t uid, struct passwd * resultbuf, char *buffer,
size_t buflen, struct passwd ** result)
{
#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(HAVE_GETPWUID_R)
Fix libpq's behavior when /etc/passwd isn't readable. Some users run their applications in chroot environments that lack an /etc/passwd file. This means that the current UID's user name and home directory are not obtainable. libpq used to be all right with that, so long as the database role name to use was specified explicitly. But commit a4c8f14364c27508233f8a31ac4b10a4c90235a9 broke such cases by causing any failure of pg_fe_getauthname() to be treated as a hard error. In any case it did little to advance its nominal goal of causing errors in pg_fe_getauthname() to be reported better. So revert that and instead put some real error-reporting code in place. This requires changes to the APIs of pg_fe_getauthname() and pqGetpwuid(), since the latter had departed from the POSIX-specified API of getpwuid_r() in a way that made it impossible to distinguish actual lookup errors from "no such user". To allow such failures to be reported, while not failing if the caller supplies a role name, add a second call of pg_fe_getauthname() in connectOptions2(). This is a tad ugly, and could perhaps be avoided with some refactoring of PQsetdbLogin(), but I'll leave that idea for later. (Note that the complained-of misbehavior only occurs in PQsetdbLogin, not when using the PQconnect functions, because in the latter we will never bother to call pg_fe_getauthname() if the user gives a role name.) In passing also clean up the Windows-side usage of GetUserName(): the recommended buffer size is 257 bytes, the passed buffer length should be the buffer size not buffer size less 1, and any error is reported by GetLastError() not errno. Per report from Christoph Berg. Back-patch to 9.4 where the chroot failure case was introduced. The generally poor reporting of errors here is of very long standing, of course, but given the lack of field complaints about it we won't risk changing these APIs further back (even though they're theoretically internal to libpq).
2015-01-11 18:35:44 +01:00
return getpwuid_r(uid, resultbuf, buffer, buflen, result);
#else
/* no getpwuid_r() available, just use getpwuid() */
Fix libpq's behavior when /etc/passwd isn't readable. Some users run their applications in chroot environments that lack an /etc/passwd file. This means that the current UID's user name and home directory are not obtainable. libpq used to be all right with that, so long as the database role name to use was specified explicitly. But commit a4c8f14364c27508233f8a31ac4b10a4c90235a9 broke such cases by causing any failure of pg_fe_getauthname() to be treated as a hard error. In any case it did little to advance its nominal goal of causing errors in pg_fe_getauthname() to be reported better. So revert that and instead put some real error-reporting code in place. This requires changes to the APIs of pg_fe_getauthname() and pqGetpwuid(), since the latter had departed from the POSIX-specified API of getpwuid_r() in a way that made it impossible to distinguish actual lookup errors from "no such user". To allow such failures to be reported, while not failing if the caller supplies a role name, add a second call of pg_fe_getauthname() in connectOptions2(). This is a tad ugly, and could perhaps be avoided with some refactoring of PQsetdbLogin(), but I'll leave that idea for later. (Note that the complained-of misbehavior only occurs in PQsetdbLogin, not when using the PQconnect functions, because in the latter we will never bother to call pg_fe_getauthname() if the user gives a role name.) In passing also clean up the Windows-side usage of GetUserName(): the recommended buffer size is 257 bytes, the passed buffer length should be the buffer size not buffer size less 1, and any error is reported by GetLastError() not errno. Per report from Christoph Berg. Back-patch to 9.4 where the chroot failure case was introduced. The generally poor reporting of errors here is of very long standing, of course, but given the lack of field complaints about it we won't risk changing these APIs further back (even though they're theoretically internal to libpq).
2015-01-11 18:35:44 +01:00
errno = 0;
*result = getpwuid(uid);
Fix libpq's behavior when /etc/passwd isn't readable. Some users run their applications in chroot environments that lack an /etc/passwd file. This means that the current UID's user name and home directory are not obtainable. libpq used to be all right with that, so long as the database role name to use was specified explicitly. But commit a4c8f14364c27508233f8a31ac4b10a4c90235a9 broke such cases by causing any failure of pg_fe_getauthname() to be treated as a hard error. In any case it did little to advance its nominal goal of causing errors in pg_fe_getauthname() to be reported better. So revert that and instead put some real error-reporting code in place. This requires changes to the APIs of pg_fe_getauthname() and pqGetpwuid(), since the latter had departed from the POSIX-specified API of getpwuid_r() in a way that made it impossible to distinguish actual lookup errors from "no such user". To allow such failures to be reported, while not failing if the caller supplies a role name, add a second call of pg_fe_getauthname() in connectOptions2(). This is a tad ugly, and could perhaps be avoided with some refactoring of PQsetdbLogin(), but I'll leave that idea for later. (Note that the complained-of misbehavior only occurs in PQsetdbLogin, not when using the PQconnect functions, because in the latter we will never bother to call pg_fe_getauthname() if the user gives a role name.) In passing also clean up the Windows-side usage of GetUserName(): the recommended buffer size is 257 bytes, the passed buffer length should be the buffer size not buffer size less 1, and any error is reported by GetLastError() not errno. Per report from Christoph Berg. Back-patch to 9.4 where the chroot failure case was introduced. The generally poor reporting of errors here is of very long standing, of course, but given the lack of field complaints about it we won't risk changing these APIs further back (even though they're theoretically internal to libpq).
2015-01-11 18:35:44 +01:00
/* paranoia: ensure we return zero on success */
return (*result == NULL) ? errno : 0;
#endif
}
#endif
/*
* Wrapper around gethostbyname() or gethostbyname_r() to mimic
* POSIX gethostbyname_r() behaviour, if it is not available or required.
* This function is called _only_ by our getaddinfo() portability function.
*/
#ifndef HAVE_GETADDRINFO
int
pqGethostbyname(const char *name,
2004-08-29 07:07:03 +02:00
struct hostent * resultbuf,
char *buffer, size_t buflen,
2004-08-29 07:07:03 +02:00
struct hostent ** result,
int *herrno)
{
#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(HAVE_GETHOSTBYNAME_R)
2004-08-29 07:07:03 +02:00
/*
2005-10-15 04:49:52 +02:00
* broken (well early POSIX draft) gethostbyname_r() which returns 'struct
* hostent *'
*/
*result = gethostbyname_r(name, resultbuf, buffer, buflen, herrno);
return (*result == NULL) ? -1 : 0;
#else
/* no gethostbyname_r(), just use gethostbyname() */
*result = gethostbyname(name);
if (*result != NULL)
*herrno = h_errno;
2004-08-29 07:07:03 +02:00
if (*result != NULL)
return 0;
else
return -1;
#endif
}
2004-08-29 07:07:03 +02:00
#endif