1 | /* strerror_r.c --- POSIX compatible system error routine
|
---|
2 |
|
---|
3 | Copyright (C) 2010-2021 Free Software Foundation, Inc.
|
---|
4 |
|
---|
5 | This file is free software: you can redistribute it and/or modify
|
---|
6 | it under the terms of the GNU Lesser General Public License as
|
---|
7 | published by the Free Software Foundation; either version 2.1 of the
|
---|
8 | License, or (at your option) any later version.
|
---|
9 |
|
---|
10 | This file is distributed in the hope that it will be useful,
|
---|
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
13 | GNU Lesser General Public License for more details.
|
---|
14 |
|
---|
15 | You should have received a copy of the GNU Lesser General Public License
|
---|
16 | along with this program. If not, see <https://www.gnu.org/licenses/>. */
|
---|
17 |
|
---|
18 | /* Written by Bruno Haible <bruno@clisp.org>, 2010. */
|
---|
19 |
|
---|
20 | #include <config.h>
|
---|
21 |
|
---|
22 | /* Enable declaration of sys_nerr and sys_errlist in <errno.h> on NetBSD. */
|
---|
23 | #define _NETBSD_SOURCE 1
|
---|
24 |
|
---|
25 | /* Specification. */
|
---|
26 | #include <string.h>
|
---|
27 |
|
---|
28 | #include <errno.h>
|
---|
29 | #include <stdio.h>
|
---|
30 | #include <stdlib.h>
|
---|
31 | #if !HAVE_SNPRINTF
|
---|
32 | # include <stdarg.h>
|
---|
33 | #endif
|
---|
34 |
|
---|
35 | #include "strerror-override.h"
|
---|
36 |
|
---|
37 | #if (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__) && HAVE___XPG_STRERROR_R /* glibc >= 2.3.4, cygwin >= 1.7.9 */
|
---|
38 |
|
---|
39 | # define USE_XPG_STRERROR_R 1
|
---|
40 | extern
|
---|
41 | #ifdef __cplusplus
|
---|
42 | "C"
|
---|
43 | #endif
|
---|
44 | int __xpg_strerror_r (int errnum, char *buf, size_t buflen);
|
---|
45 |
|
---|
46 | #elif HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__)
|
---|
47 |
|
---|
48 | /* The system's strerror_r function is OK, except that its third argument
|
---|
49 | is 'int', not 'size_t', or its return type is wrong. */
|
---|
50 |
|
---|
51 | # include <limits.h>
|
---|
52 |
|
---|
53 | # define USE_SYSTEM_STRERROR_R 1
|
---|
54 |
|
---|
55 | #else /* (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) */
|
---|
56 |
|
---|
57 | /* Use the system's strerror(). Exclude glibc and cygwin because the
|
---|
58 | system strerror_r has the wrong return type, and cygwin 1.7.9
|
---|
59 | strerror_r clobbers strerror. */
|
---|
60 | # undef strerror
|
---|
61 |
|
---|
62 | # define USE_SYSTEM_STRERROR 1
|
---|
63 |
|
---|
64 | # if defined __NetBSD__ || defined __hpux || (defined _WIN32 && !defined __CYGWIN__) || defined __sgi || (defined __sun && !defined _LP64) || defined __CYGWIN__
|
---|
65 |
|
---|
66 | /* No locking needed. */
|
---|
67 |
|
---|
68 | /* Get catgets internationalization functions. */
|
---|
69 | # if HAVE_CATGETS
|
---|
70 | # include <nl_types.h>
|
---|
71 | # endif
|
---|
72 |
|
---|
73 | #ifdef __cplusplus
|
---|
74 | extern "C" {
|
---|
75 | #endif
|
---|
76 |
|
---|
77 | /* Get sys_nerr, sys_errlist on HP-UX (otherwise only declared in C++ mode).
|
---|
78 | Get sys_nerr, sys_errlist on IRIX (otherwise only declared with _SGIAPI). */
|
---|
79 | # if defined __hpux || defined __sgi
|
---|
80 | extern int sys_nerr;
|
---|
81 | extern char *sys_errlist[];
|
---|
82 | # endif
|
---|
83 |
|
---|
84 | /* Get sys_nerr on Solaris. */
|
---|
85 | # if defined __sun && !defined _LP64
|
---|
86 | extern int sys_nerr;
|
---|
87 | # endif
|
---|
88 |
|
---|
89 | #ifdef __cplusplus
|
---|
90 | }
|
---|
91 | #endif
|
---|
92 |
|
---|
93 | # else
|
---|
94 |
|
---|
95 | # include "glthread/lock.h"
|
---|
96 |
|
---|
97 | /* This lock protects the buffer returned by strerror(). We assume that
|
---|
98 | no other uses of strerror() exist in the program. */
|
---|
99 | gl_lock_define_initialized(static, strerror_lock)
|
---|
100 |
|
---|
101 | # endif
|
---|
102 |
|
---|
103 | #endif
|
---|
104 |
|
---|
105 | /* On MSVC, there is no snprintf() function, just a _snprintf().
|
---|
106 | It is of lower quality, but sufficient for the simple use here.
|
---|
107 | We only have to make sure to NUL terminate the result (_snprintf
|
---|
108 | does not NUL terminate, like strncpy). */
|
---|
109 | #if !HAVE_SNPRINTF
|
---|
110 | static int
|
---|
111 | local_snprintf (char *buf, size_t buflen, const char *format, ...)
|
---|
112 | {
|
---|
113 | va_list args;
|
---|
114 | int result;
|
---|
115 |
|
---|
116 | va_start (args, format);
|
---|
117 | result = _vsnprintf (buf, buflen, format, args);
|
---|
118 | va_end (args);
|
---|
119 | if (buflen > 0 && (result < 0 || result >= buflen))
|
---|
120 | buf[buflen - 1] = '\0';
|
---|
121 | return result;
|
---|
122 | }
|
---|
123 | # undef snprintf
|
---|
124 | # define snprintf local_snprintf
|
---|
125 | #endif
|
---|
126 |
|
---|
127 | /* Copy as much of MSG into BUF as possible, without corrupting errno.
|
---|
128 | Return 0 if MSG fit in BUFLEN, otherwise return ERANGE. */
|
---|
129 | static int
|
---|
130 | safe_copy (char *buf, size_t buflen, const char *msg)
|
---|
131 | {
|
---|
132 | size_t len = strlen (msg);
|
---|
133 | size_t moved = len < buflen ? len : buflen - 1;
|
---|
134 |
|
---|
135 | /* Although POSIX lets memmove corrupt errno, we don't
|
---|
136 | know of any implementation where this is a real problem. */
|
---|
137 | memmove (buf, msg, moved);
|
---|
138 | buf[moved] = '\0';
|
---|
139 | return len < buflen ? 0 : ERANGE;
|
---|
140 | }
|
---|
141 |
|
---|
142 |
|
---|
143 | int
|
---|
144 | strerror_r (int errnum, char *buf, size_t buflen)
|
---|
145 | #undef strerror_r
|
---|
146 | {
|
---|
147 | /* Filter this out now, so that rest of this replacement knows that
|
---|
148 | there is room for a non-empty message and trailing NUL. */
|
---|
149 | if (buflen <= 1)
|
---|
150 | {
|
---|
151 | if (buflen)
|
---|
152 | *buf = '\0';
|
---|
153 | return ERANGE;
|
---|
154 | }
|
---|
155 | *buf = '\0';
|
---|
156 |
|
---|
157 | /* Check for gnulib overrides. */
|
---|
158 | {
|
---|
159 | char const *msg = strerror_override (errnum);
|
---|
160 |
|
---|
161 | if (msg)
|
---|
162 | return safe_copy (buf, buflen, msg);
|
---|
163 | }
|
---|
164 |
|
---|
165 | {
|
---|
166 | int ret;
|
---|
167 | int saved_errno = errno;
|
---|
168 |
|
---|
169 | #if USE_XPG_STRERROR_R
|
---|
170 |
|
---|
171 | {
|
---|
172 | ret = __xpg_strerror_r (errnum, buf, buflen);
|
---|
173 | if (ret < 0)
|
---|
174 | ret = errno;
|
---|
175 | if (!*buf)
|
---|
176 | {
|
---|
177 | /* glibc 2.13 would not touch buf on err, so we have to fall
|
---|
178 | back to GNU strerror_r which always returns a thread-safe
|
---|
179 | untruncated string to (partially) copy into our buf. */
|
---|
180 | safe_copy (buf, buflen, strerror_r (errnum, buf, buflen));
|
---|
181 | }
|
---|
182 | }
|
---|
183 |
|
---|
184 | #elif USE_SYSTEM_STRERROR_R
|
---|
185 |
|
---|
186 | if (buflen > INT_MAX)
|
---|
187 | buflen = INT_MAX;
|
---|
188 |
|
---|
189 | # ifdef __hpux
|
---|
190 | /* On HP-UX 11.31, strerror_r always fails when buflen < 80; it
|
---|
191 | also fails to change buf on EINVAL. */
|
---|
192 | {
|
---|
193 | char stackbuf[80];
|
---|
194 |
|
---|
195 | if (buflen < sizeof stackbuf)
|
---|
196 | {
|
---|
197 | ret = strerror_r (errnum, stackbuf, sizeof stackbuf);
|
---|
198 | if (ret == 0)
|
---|
199 | ret = safe_copy (buf, buflen, stackbuf);
|
---|
200 | }
|
---|
201 | else
|
---|
202 | ret = strerror_r (errnum, buf, buflen);
|
---|
203 | }
|
---|
204 | # else
|
---|
205 | ret = strerror_r (errnum, buf, buflen);
|
---|
206 |
|
---|
207 | /* Some old implementations may return (-1, EINVAL) instead of EINVAL.
|
---|
208 | But on Haiku, valid error numbers are negative. */
|
---|
209 | # if !defined __HAIKU__
|
---|
210 | if (ret < 0)
|
---|
211 | ret = errno;
|
---|
212 | # endif
|
---|
213 | # endif
|
---|
214 |
|
---|
215 | # if defined _AIX || defined __HAIKU__
|
---|
216 | /* AIX and Haiku return 0 rather than ERANGE when truncating strings; try
|
---|
217 | again until we are sure we got the entire string. */
|
---|
218 | if (!ret && strlen (buf) == buflen - 1)
|
---|
219 | {
|
---|
220 | char stackbuf[STACKBUF_LEN];
|
---|
221 | size_t len;
|
---|
222 | strerror_r (errnum, stackbuf, sizeof stackbuf);
|
---|
223 | len = strlen (stackbuf);
|
---|
224 | /* STACKBUF_LEN should have been large enough. */
|
---|
225 | if (len + 1 == sizeof stackbuf)
|
---|
226 | abort ();
|
---|
227 | if (buflen <= len)
|
---|
228 | ret = ERANGE;
|
---|
229 | }
|
---|
230 | # else
|
---|
231 | /* Solaris 10 does not populate buf on ERANGE. OpenBSD 4.7
|
---|
232 | truncates early on ERANGE rather than return a partial integer.
|
---|
233 | We prefer the maximal string. We set buf[0] earlier, and we
|
---|
234 | know of no implementation that modifies buf to be an
|
---|
235 | unterminated string, so this strlen should be portable in
|
---|
236 | practice (rather than pulling in a safer strnlen). */
|
---|
237 | if (ret == ERANGE && strlen (buf) < buflen - 1)
|
---|
238 | {
|
---|
239 | char stackbuf[STACKBUF_LEN];
|
---|
240 |
|
---|
241 | /* STACKBUF_LEN should have been large enough. */
|
---|
242 | if (strerror_r (errnum, stackbuf, sizeof stackbuf) == ERANGE)
|
---|
243 | abort ();
|
---|
244 | safe_copy (buf, buflen, stackbuf);
|
---|
245 | }
|
---|
246 | # endif
|
---|
247 |
|
---|
248 | #else /* USE_SYSTEM_STRERROR */
|
---|
249 |
|
---|
250 | /* Try to do what strerror (errnum) does, but without clobbering the
|
---|
251 | buffer used by strerror(). */
|
---|
252 |
|
---|
253 | # if defined __NetBSD__ || defined __hpux || (defined _WIN32 && !defined __CYGWIN__) || defined __CYGWIN__ /* NetBSD, HP-UX, native Windows, Cygwin */
|
---|
254 |
|
---|
255 | /* NetBSD: sys_nerr, sys_errlist are declared through _NETBSD_SOURCE
|
---|
256 | and <errno.h> above.
|
---|
257 | HP-UX: sys_nerr, sys_errlist are declared explicitly above.
|
---|
258 | native Windows: sys_nerr, sys_errlist are declared in <stdlib.h>.
|
---|
259 | Cygwin: sys_nerr, sys_errlist are declared in <errno.h>. */
|
---|
260 | if (errnum >= 0 && errnum < sys_nerr)
|
---|
261 | {
|
---|
262 | # if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
|
---|
263 | # if defined __NetBSD__
|
---|
264 | nl_catd catd = catopen ("libc", NL_CAT_LOCALE);
|
---|
265 | const char *errmsg =
|
---|
266 | (catd != (nl_catd)-1
|
---|
267 | ? catgets (catd, 1, errnum, sys_errlist[errnum])
|
---|
268 | : sys_errlist[errnum]);
|
---|
269 | # endif
|
---|
270 | # if defined __hpux
|
---|
271 | nl_catd catd = catopen ("perror", NL_CAT_LOCALE);
|
---|
272 | const char *errmsg =
|
---|
273 | (catd != (nl_catd)-1
|
---|
274 | ? catgets (catd, 1, 1 + errnum, sys_errlist[errnum])
|
---|
275 | : sys_errlist[errnum]);
|
---|
276 | # endif
|
---|
277 | # else
|
---|
278 | const char *errmsg = sys_errlist[errnum];
|
---|
279 | # endif
|
---|
280 | if (errmsg == NULL || *errmsg == '\0')
|
---|
281 | ret = EINVAL;
|
---|
282 | else
|
---|
283 | ret = safe_copy (buf, buflen, errmsg);
|
---|
284 | # if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
|
---|
285 | if (catd != (nl_catd)-1)
|
---|
286 | catclose (catd);
|
---|
287 | # endif
|
---|
288 | }
|
---|
289 | else
|
---|
290 | ret = EINVAL;
|
---|
291 |
|
---|
292 | # elif defined __sgi || (defined __sun && !defined _LP64) /* IRIX, Solaris <= 9 32-bit */
|
---|
293 |
|
---|
294 | /* For a valid error number, the system's strerror() function returns
|
---|
295 | a pointer to a not copied string, not to a buffer. */
|
---|
296 | if (errnum >= 0 && errnum < sys_nerr)
|
---|
297 | {
|
---|
298 | char *errmsg = strerror (errnum);
|
---|
299 |
|
---|
300 | if (errmsg == NULL || *errmsg == '\0')
|
---|
301 | ret = EINVAL;
|
---|
302 | else
|
---|
303 | ret = safe_copy (buf, buflen, errmsg);
|
---|
304 | }
|
---|
305 | else
|
---|
306 | ret = EINVAL;
|
---|
307 |
|
---|
308 | # else
|
---|
309 |
|
---|
310 | gl_lock_lock (strerror_lock);
|
---|
311 |
|
---|
312 | {
|
---|
313 | char *errmsg = strerror (errnum);
|
---|
314 |
|
---|
315 | /* For invalid error numbers, strerror() on
|
---|
316 | - IRIX 6.5 returns NULL,
|
---|
317 | - HP-UX 11 returns an empty string. */
|
---|
318 | if (errmsg == NULL || *errmsg == '\0')
|
---|
319 | ret = EINVAL;
|
---|
320 | else
|
---|
321 | ret = safe_copy (buf, buflen, errmsg);
|
---|
322 | }
|
---|
323 |
|
---|
324 | gl_lock_unlock (strerror_lock);
|
---|
325 |
|
---|
326 | # endif
|
---|
327 |
|
---|
328 | #endif
|
---|
329 |
|
---|
330 | #if defined _WIN32 && !defined __CYGWIN__
|
---|
331 | /* MSVC 14 defines names for many error codes in the range 100..140,
|
---|
332 | but _sys_errlist contains strings only for the error codes
|
---|
333 | < _sys_nerr = 43. */
|
---|
334 | if (ret == EINVAL)
|
---|
335 | {
|
---|
336 | const char *errmsg;
|
---|
337 |
|
---|
338 | switch (errnum)
|
---|
339 | {
|
---|
340 | case 100 /* EADDRINUSE */:
|
---|
341 | errmsg = "Address already in use";
|
---|
342 | break;
|
---|
343 | case 101 /* EADDRNOTAVAIL */:
|
---|
344 | errmsg = "Cannot assign requested address";
|
---|
345 | break;
|
---|
346 | case 102 /* EAFNOSUPPORT */:
|
---|
347 | errmsg = "Address family not supported by protocol";
|
---|
348 | break;
|
---|
349 | case 103 /* EALREADY */:
|
---|
350 | errmsg = "Operation already in progress";
|
---|
351 | break;
|
---|
352 | case 105 /* ECANCELED */:
|
---|
353 | errmsg = "Operation canceled";
|
---|
354 | break;
|
---|
355 | case 106 /* ECONNABORTED */:
|
---|
356 | errmsg = "Software caused connection abort";
|
---|
357 | break;
|
---|
358 | case 107 /* ECONNREFUSED */:
|
---|
359 | errmsg = "Connection refused";
|
---|
360 | break;
|
---|
361 | case 108 /* ECONNRESET */:
|
---|
362 | errmsg = "Connection reset by peer";
|
---|
363 | break;
|
---|
364 | case 109 /* EDESTADDRREQ */:
|
---|
365 | errmsg = "Destination address required";
|
---|
366 | break;
|
---|
367 | case 110 /* EHOSTUNREACH */:
|
---|
368 | errmsg = "No route to host";
|
---|
369 | break;
|
---|
370 | case 112 /* EINPROGRESS */:
|
---|
371 | errmsg = "Operation now in progress";
|
---|
372 | break;
|
---|
373 | case 113 /* EISCONN */:
|
---|
374 | errmsg = "Transport endpoint is already connected";
|
---|
375 | break;
|
---|
376 | case 114 /* ELOOP */:
|
---|
377 | errmsg = "Too many levels of symbolic links";
|
---|
378 | break;
|
---|
379 | case 115 /* EMSGSIZE */:
|
---|
380 | errmsg = "Message too long";
|
---|
381 | break;
|
---|
382 | case 116 /* ENETDOWN */:
|
---|
383 | errmsg = "Network is down";
|
---|
384 | break;
|
---|
385 | case 117 /* ENETRESET */:
|
---|
386 | errmsg = "Network dropped connection on reset";
|
---|
387 | break;
|
---|
388 | case 118 /* ENETUNREACH */:
|
---|
389 | errmsg = "Network is unreachable";
|
---|
390 | break;
|
---|
391 | case 119 /* ENOBUFS */:
|
---|
392 | errmsg = "No buffer space available";
|
---|
393 | break;
|
---|
394 | case 123 /* ENOPROTOOPT */:
|
---|
395 | errmsg = "Protocol not available";
|
---|
396 | break;
|
---|
397 | case 126 /* ENOTCONN */:
|
---|
398 | errmsg = "Transport endpoint is not connected";
|
---|
399 | break;
|
---|
400 | case 128 /* ENOTSOCK */:
|
---|
401 | errmsg = "Socket operation on non-socket";
|
---|
402 | break;
|
---|
403 | case 129 /* ENOTSUP */:
|
---|
404 | errmsg = "Not supported";
|
---|
405 | break;
|
---|
406 | case 130 /* EOPNOTSUPP */:
|
---|
407 | errmsg = "Operation not supported";
|
---|
408 | break;
|
---|
409 | case 132 /* EOVERFLOW */:
|
---|
410 | errmsg = "Value too large for defined data type";
|
---|
411 | break;
|
---|
412 | case 133 /* EOWNERDEAD */:
|
---|
413 | errmsg = "Owner died";
|
---|
414 | break;
|
---|
415 | case 134 /* EPROTO */:
|
---|
416 | errmsg = "Protocol error";
|
---|
417 | break;
|
---|
418 | case 135 /* EPROTONOSUPPORT */:
|
---|
419 | errmsg = "Protocol not supported";
|
---|
420 | break;
|
---|
421 | case 136 /* EPROTOTYPE */:
|
---|
422 | errmsg = "Protocol wrong type for socket";
|
---|
423 | break;
|
---|
424 | case 138 /* ETIMEDOUT */:
|
---|
425 | errmsg = "Connection timed out";
|
---|
426 | break;
|
---|
427 | case 140 /* EWOULDBLOCK */:
|
---|
428 | errmsg = "Operation would block";
|
---|
429 | break;
|
---|
430 | default:
|
---|
431 | errmsg = NULL;
|
---|
432 | break;
|
---|
433 | }
|
---|
434 | if (errmsg != NULL)
|
---|
435 | ret = safe_copy (buf, buflen, errmsg);
|
---|
436 | }
|
---|
437 | #endif
|
---|
438 |
|
---|
439 | if (ret == EINVAL && !*buf)
|
---|
440 | {
|
---|
441 | #if defined __HAIKU__
|
---|
442 | /* For consistency with perror(). */
|
---|
443 | snprintf (buf, buflen, "Unknown Application Error (%d)", errnum);
|
---|
444 | #else
|
---|
445 | snprintf (buf, buflen, "Unknown error %d", errnum);
|
---|
446 | #endif
|
---|
447 | }
|
---|
448 |
|
---|
449 | errno = saved_errno;
|
---|
450 | return ret;
|
---|
451 | }
|
---|
452 | }
|
---|