aboutsummaryrefslogtreecommitdiff
path: root/libpq/thread.c
blob: ed908bab36241f2ea114247d65e150d7ea94b99b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*-------------------------------------------------------------------------
 *
 * 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
 *
 * 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.
 */


/*
 * 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.
 *
 * 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
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)
	return getpwuid_r(uid, resultbuf, buffer, buflen, result);
#else
	/* no getpwuid_r() available, just use getpwuid() */
	errno = 0;
	*result = getpwuid(uid);
	/* 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,
				struct hostent * resultbuf,
				char *buffer, size_t buflen,
				struct hostent ** result,
				int *herrno)
{
#if defined(FRONTEND) && defined(ENABLE_THREAD_SAFETY) && defined(HAVE_GETHOSTBYNAME_R)

	/*
	 * 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;

	if (*result != NULL)
		return 0;
	else
		return -1;
#endif
}

#endif