1 /*-------------------------------------------------------------------------
2  *
3  * strerror.c
4  *	  Replacements for standard strerror() and strerror_r() functions
5  *
6  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *	  src/port/strerror.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "c.h"
16 
17 /*
18  * Within this file, "strerror" means the platform's function not pg_strerror,
19  * and likewise for "strerror_r"
20  */
21 #undef strerror
22 #undef strerror_r
23 
24 static char *gnuish_strerror_r(int errnum, char *buf, size_t buflen);
25 static char *get_errno_symbol(int errnum);
26 #ifdef WIN32
27 static char *win32_socket_strerror(int errnum, char *buf, size_t buflen);
28 #endif
29 
30 
31 /*
32  * A slightly cleaned-up version of strerror()
33  */
34 char *
pg_strerror(int errnum)35 pg_strerror(int errnum)
36 {
37 	static char errorstr_buf[PG_STRERROR_R_BUFLEN];
38 
39 	return pg_strerror_r(errnum, errorstr_buf, sizeof(errorstr_buf));
40 }
41 
42 /*
43  * A slightly cleaned-up version of strerror_r()
44  */
45 char *
pg_strerror_r(int errnum,char * buf,size_t buflen)46 pg_strerror_r(int errnum, char *buf, size_t buflen)
47 {
48 	char	   *str;
49 
50 	/* If it's a Windows Winsock error, that needs special handling */
51 #ifdef WIN32
52 	/* Winsock error code range, per WinError.h */
53 	if (errnum >= 10000 && errnum <= 11999)
54 		return win32_socket_strerror(errnum, buf, buflen);
55 #endif
56 
57 	/* Try the platform's strerror_r(), or maybe just strerror() */
58 	str = gnuish_strerror_r(errnum, buf, buflen);
59 
60 	/*
61 	 * Some strerror()s return an empty string for out-of-range errno.  This
62 	 * is ANSI C spec compliant, but not exactly useful.  Also, we may get
63 	 * back strings of question marks if libc cannot transcode the message to
64 	 * the codeset specified by LC_CTYPE.  If we get nothing useful, first try
65 	 * get_errno_symbol(), and if that fails, print the numeric errno.
66 	 */
67 	if (str == NULL || *str == '\0' || *str == '?')
68 		str = get_errno_symbol(errnum);
69 
70 	if (str == NULL)
71 	{
72 		snprintf(buf, buflen, _("operating system error %d"), errnum);
73 		str = buf;
74 	}
75 
76 	return str;
77 }
78 
79 /*
80  * Simple wrapper to emulate GNU strerror_r if what the platform provides is
81  * POSIX.  Also, if platform lacks strerror_r altogether, fall back to plain
82  * strerror; it might not be very thread-safe, but tough luck.
83  */
84 static char *
gnuish_strerror_r(int errnum,char * buf,size_t buflen)85 gnuish_strerror_r(int errnum, char *buf, size_t buflen)
86 {
87 #ifdef HAVE_STRERROR_R
88 #ifdef STRERROR_R_INT
89 	/* POSIX API */
90 	if (strerror_r(errnum, buf, buflen) == 0)
91 		return buf;
92 	return NULL;				/* let caller deal with failure */
93 #else
94 	/* GNU API */
95 	return strerror_r(errnum, buf, buflen);
96 #endif
97 #else							/* !HAVE_STRERROR_R */
98 	char	   *sbuf = strerror(errnum);
99 
100 	if (sbuf == NULL)			/* can this still happen anywhere? */
101 		return NULL;
102 	/* To minimize thread-unsafety hazard, copy into caller's buffer */
103 	strlcpy(buf, sbuf, buflen);
104 	return buf;
105 #endif
106 }
107 
108 /*
109  * Returns a symbol (e.g. "ENOENT") for an errno code.
110  * Returns NULL if the code is unrecognized.
111  */
112 static char *
get_errno_symbol(int errnum)113 get_errno_symbol(int errnum)
114 {
115 	switch (errnum)
116 	{
117 		case E2BIG:
118 			return "E2BIG";
119 		case EACCES:
120 			return "EACCES";
121 #ifdef EADDRINUSE
122 		case EADDRINUSE:
123 			return "EADDRINUSE";
124 #endif
125 #ifdef EADDRNOTAVAIL
126 		case EADDRNOTAVAIL:
127 			return "EADDRNOTAVAIL";
128 #endif
129 		case EAFNOSUPPORT:
130 			return "EAFNOSUPPORT";
131 #ifdef EAGAIN
132 		case EAGAIN:
133 			return "EAGAIN";
134 #endif
135 #ifdef EALREADY
136 		case EALREADY:
137 			return "EALREADY";
138 #endif
139 		case EBADF:
140 			return "EBADF";
141 #ifdef EBADMSG
142 		case EBADMSG:
143 			return "EBADMSG";
144 #endif
145 		case EBUSY:
146 			return "EBUSY";
147 		case ECHILD:
148 			return "ECHILD";
149 #ifdef ECONNABORTED
150 		case ECONNABORTED:
151 			return "ECONNABORTED";
152 #endif
153 		case ECONNREFUSED:
154 			return "ECONNREFUSED";
155 #ifdef ECONNRESET
156 		case ECONNRESET:
157 			return "ECONNRESET";
158 #endif
159 		case EDEADLK:
160 			return "EDEADLK";
161 		case EDOM:
162 			return "EDOM";
163 		case EEXIST:
164 			return "EEXIST";
165 		case EFAULT:
166 			return "EFAULT";
167 		case EFBIG:
168 			return "EFBIG";
169 #ifdef EHOSTUNREACH
170 		case EHOSTUNREACH:
171 			return "EHOSTUNREACH";
172 #endif
173 		case EIDRM:
174 			return "EIDRM";
175 		case EINPROGRESS:
176 			return "EINPROGRESS";
177 		case EINTR:
178 			return "EINTR";
179 		case EINVAL:
180 			return "EINVAL";
181 		case EIO:
182 			return "EIO";
183 #ifdef EISCONN
184 		case EISCONN:
185 			return "EISCONN";
186 #endif
187 		case EISDIR:
188 			return "EISDIR";
189 #ifdef ELOOP
190 		case ELOOP:
191 			return "ELOOP";
192 #endif
193 		case EMFILE:
194 			return "EMFILE";
195 		case EMLINK:
196 			return "EMLINK";
197 		case EMSGSIZE:
198 			return "EMSGSIZE";
199 		case ENAMETOOLONG:
200 			return "ENAMETOOLONG";
201 		case ENFILE:
202 			return "ENFILE";
203 		case ENOBUFS:
204 			return "ENOBUFS";
205 		case ENODEV:
206 			return "ENODEV";
207 		case ENOENT:
208 			return "ENOENT";
209 		case ENOEXEC:
210 			return "ENOEXEC";
211 		case ENOMEM:
212 			return "ENOMEM";
213 		case ENOSPC:
214 			return "ENOSPC";
215 		case ENOSYS:
216 			return "ENOSYS";
217 #ifdef ENOTCONN
218 		case ENOTCONN:
219 			return "ENOTCONN";
220 #endif
221 		case ENOTDIR:
222 			return "ENOTDIR";
223 #if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
224 		case ENOTEMPTY:
225 			return "ENOTEMPTY";
226 #endif
227 #ifdef ENOTSOCK
228 		case ENOTSOCK:
229 			return "ENOTSOCK";
230 #endif
231 #ifdef ENOTSUP
232 		case ENOTSUP:
233 			return "ENOTSUP";
234 #endif
235 		case ENOTTY:
236 			return "ENOTTY";
237 		case ENXIO:
238 			return "ENXIO";
239 #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
240 		case EOPNOTSUPP:
241 			return "EOPNOTSUPP";
242 #endif
243 #ifdef EOVERFLOW
244 		case EOVERFLOW:
245 			return "EOVERFLOW";
246 #endif
247 		case EPERM:
248 			return "EPERM";
249 		case EPIPE:
250 			return "EPIPE";
251 		case EPROTONOSUPPORT:
252 			return "EPROTONOSUPPORT";
253 		case ERANGE:
254 			return "ERANGE";
255 #ifdef EROFS
256 		case EROFS:
257 			return "EROFS";
258 #endif
259 		case ESRCH:
260 			return "ESRCH";
261 #ifdef ETIMEDOUT
262 		case ETIMEDOUT:
263 			return "ETIMEDOUT";
264 #endif
265 #ifdef ETXTBSY
266 		case ETXTBSY:
267 			return "ETXTBSY";
268 #endif
269 #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
270 		case EWOULDBLOCK:
271 			return "EWOULDBLOCK";
272 #endif
273 		case EXDEV:
274 			return "EXDEV";
275 	}
276 
277 	return NULL;
278 }
279 
280 
281 #ifdef WIN32
282 
283 /*
284  * Windows' strerror() doesn't know the Winsock codes, so handle them this way
285  */
286 static char *
win32_socket_strerror(int errnum,char * buf,size_t buflen)287 win32_socket_strerror(int errnum, char *buf, size_t buflen)
288 {
289 	static HANDLE handleDLL = INVALID_HANDLE_VALUE;
290 
291 	if (handleDLL == INVALID_HANDLE_VALUE)
292 	{
293 		handleDLL = LoadLibraryEx("netmsg.dll", NULL,
294 								  DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
295 		if (handleDLL == NULL)
296 		{
297 			snprintf(buf, buflen,
298 					 "winsock error %d (could not load netmsg.dll to translate: error code %lu)",
299 					 errnum, GetLastError());
300 			return buf;
301 		}
302 	}
303 
304 	ZeroMemory(buf, buflen);
305 	if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
306 					  FORMAT_MESSAGE_FROM_SYSTEM |
307 					  FORMAT_MESSAGE_FROM_HMODULE,
308 					  handleDLL,
309 					  errnum,
310 					  MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
311 					  buf,
312 					  buflen - 1,
313 					  NULL) == 0)
314 	{
315 		/* Failed to get id */
316 		snprintf(buf, buflen, "unrecognized winsock error %d", errnum);
317 	}
318 
319 	return buf;
320 }
321 
322 #endif							/* WIN32 */
323