1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * strerror.c |
4 | * Replacements for standard strerror() and strerror_r() functions |
5 | * |
6 | * Portions Copyright (c) 1996-2019, 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 * |
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 * |
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 * |
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 * |
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 * |
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 | |