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