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