| 1 | /* strerror_r.c --- POSIX compatible system error routine |
| 2 | |
| 3 | Copyright (C) 2010-2020 Free Software Foundation, Inc. |
| 4 | |
| 5 | This program is free software: you can redistribute it and/or modify |
| 6 | it under the terms of the GNU General Public License as published by |
| 7 | the Free Software Foundation; either version 3 of the License, or |
| 8 | (at your option) any later version. |
| 9 | |
| 10 | This program 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 General Public License for more details. |
| 14 | |
| 15 | You should have received a copy of the GNU 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 | # define snprintf local_snprintf |
| 124 | #endif |
| 125 | |
| 126 | /* Copy as much of MSG into BUF as possible, without corrupting errno. |
| 127 | Return 0 if MSG fit in BUFLEN, otherwise return ERANGE. */ |
| 128 | static int |
| 129 | safe_copy (char *buf, size_t buflen, const char *msg) |
| 130 | { |
| 131 | size_t len = strlen (msg); |
| 132 | size_t moved = len < buflen ? len : buflen - 1; |
| 133 | |
| 134 | /* Although POSIX lets memmove corrupt errno, we don't |
| 135 | know of any implementation where this is a real problem. */ |
| 136 | memmove (buf, msg, moved); |
| 137 | buf[moved] = '\0'; |
| 138 | return len < buflen ? 0 : ERANGE; |
| 139 | } |
| 140 | |
| 141 | |
| 142 | int |
| 143 | strerror_r (int errnum, char *buf, size_t buflen) |
| 144 | #undef strerror_r |
| 145 | { |
| 146 | /* Filter this out now, so that rest of this replacement knows that |
| 147 | there is room for a non-empty message and trailing NUL. */ |
| 148 | if (buflen <= 1) |
| 149 | { |
| 150 | if (buflen) |
| 151 | *buf = '\0'; |
| 152 | return ERANGE; |
| 153 | } |
| 154 | *buf = '\0'; |
| 155 | |
| 156 | /* Check for gnulib overrides. */ |
| 157 | { |
| 158 | char const *msg = strerror_override (errnum); |
| 159 | |
| 160 | if (msg) |
| 161 | return safe_copy (buf, buflen, msg); |
| 162 | } |
| 163 | |
| 164 | { |
| 165 | int ret; |
| 166 | int saved_errno = errno; |
| 167 | |
| 168 | #if USE_XPG_STRERROR_R |
| 169 | |
| 170 | { |
| 171 | ret = __xpg_strerror_r (errnum, buf, buflen); |
| 172 | if (ret < 0) |
| 173 | ret = errno; |
| 174 | if (!*buf) |
| 175 | { |
| 176 | /* glibc 2.13 would not touch buf on err, so we have to fall |
| 177 | back to GNU strerror_r which always returns a thread-safe |
| 178 | untruncated string to (partially) copy into our buf. */ |
| 179 | safe_copy (buf, buflen, strerror_r (errnum, buf, buflen)); |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | #elif USE_SYSTEM_STRERROR_R |
| 184 | |
| 185 | if (buflen > INT_MAX) |
| 186 | buflen = INT_MAX; |
| 187 | |
| 188 | # ifdef __hpux |
| 189 | /* On HP-UX 11.31, strerror_r always fails when buflen < 80; it |
| 190 | also fails to change buf on EINVAL. */ |
| 191 | { |
| 192 | char stackbuf[80]; |
| 193 | |
| 194 | if (buflen < sizeof stackbuf) |
| 195 | { |
| 196 | ret = strerror_r (errnum, stackbuf, sizeof stackbuf); |
| 197 | if (ret == 0) |
| 198 | ret = safe_copy (buf, buflen, stackbuf); |
| 199 | } |
| 200 | else |
| 201 | ret = strerror_r (errnum, buf, buflen); |
| 202 | } |
| 203 | # else |
| 204 | ret = strerror_r (errnum, buf, buflen); |
| 205 | |
| 206 | /* Some old implementations may return (-1, EINVAL) instead of EINVAL. |
| 207 | But on Haiku, valid error numbers are negative. */ |
| 208 | # if !defined __HAIKU__ |
| 209 | if (ret < 0) |
| 210 | ret = errno; |
| 211 | # endif |
| 212 | # endif |
| 213 | |
| 214 | # if defined _AIX || defined __HAIKU__ |
| 215 | /* AIX and Haiku return 0 rather than ERANGE when truncating strings; try |
| 216 | again until we are sure we got the entire string. */ |
| 217 | if (!ret && strlen (buf) == buflen - 1) |
| 218 | { |
| 219 | char stackbuf[STACKBUF_LEN]; |
| 220 | size_t len; |
| 221 | strerror_r (errnum, stackbuf, sizeof stackbuf); |
| 222 | len = strlen (stackbuf); |
| 223 | /* STACKBUF_LEN should have been large enough. */ |
| 224 | if (len + 1 == sizeof stackbuf) |
| 225 | abort (); |
| 226 | if (buflen <= len) |
| 227 | ret = ERANGE; |
| 228 | } |
| 229 | # else |
| 230 | /* Solaris 10 does not populate buf on ERANGE. OpenBSD 4.7 |
| 231 | truncates early on ERANGE rather than return a partial integer. |
| 232 | We prefer the maximal string. We set buf[0] earlier, and we |
| 233 | know of no implementation that modifies buf to be an |
| 234 | unterminated string, so this strlen should be portable in |
| 235 | practice (rather than pulling in a safer strnlen). */ |
| 236 | if (ret == ERANGE && strlen (buf) < buflen - 1) |
| 237 | { |
| 238 | char stackbuf[STACKBUF_LEN]; |
| 239 | |
| 240 | /* STACKBUF_LEN should have been large enough. */ |
| 241 | if (strerror_r (errnum, stackbuf, sizeof stackbuf) == ERANGE) |
| 242 | abort (); |
| 243 | safe_copy (buf, buflen, stackbuf); |
| 244 | } |
| 245 | # endif |
| 246 | |
| 247 | #else /* USE_SYSTEM_STRERROR */ |
| 248 | |
| 249 | /* Try to do what strerror (errnum) does, but without clobbering the |
| 250 | buffer used by strerror(). */ |
| 251 | |
| 252 | # if defined __NetBSD__ || defined __hpux || (defined _WIN32 && !defined __CYGWIN__) || defined __CYGWIN__ /* NetBSD, HP-UX, native Windows, Cygwin */ |
| 253 | |
| 254 | /* NetBSD: sys_nerr, sys_errlist are declared through _NETBSD_SOURCE |
| 255 | and <errno.h> above. |
| 256 | HP-UX: sys_nerr, sys_errlist are declared explicitly above. |
| 257 | native Windows: sys_nerr, sys_errlist are declared in <stdlib.h>. |
| 258 | Cygwin: sys_nerr, sys_errlist are declared in <errno.h>. */ |
| 259 | if (errnum >= 0 && errnum < sys_nerr) |
| 260 | { |
| 261 | # if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux) |
| 262 | # if defined __NetBSD__ |
| 263 | nl_catd catd = catopen ("libc", NL_CAT_LOCALE); |
| 264 | const char *errmsg = |
| 265 | (catd != (nl_catd)-1 |
| 266 | ? catgets (catd, 1, errnum, sys_errlist[errnum]) |
| 267 | : sys_errlist[errnum]); |
| 268 | # endif |
| 269 | # if defined __hpux |
| 270 | nl_catd catd = catopen ("perror", NL_CAT_LOCALE); |
| 271 | const char *errmsg = |
| 272 | (catd != (nl_catd)-1 |
| 273 | ? catgets (catd, 1, 1 + errnum, sys_errlist[errnum]) |
| 274 | : sys_errlist[errnum]); |
| 275 | # endif |
| 276 | # else |
| 277 | const char *errmsg = sys_errlist[errnum]; |
| 278 | # endif |
| 279 | if (errmsg == NULL || *errmsg == '\0') |
| 280 | ret = EINVAL; |
| 281 | else |
| 282 | ret = safe_copy (buf, buflen, errmsg); |
| 283 | # if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux) |
| 284 | if (catd != (nl_catd)-1) |
| 285 | catclose (catd); |
| 286 | # endif |
| 287 | } |
| 288 | else |
| 289 | ret = EINVAL; |
| 290 | |
| 291 | # elif defined __sgi || (defined __sun && !defined _LP64) /* IRIX, Solaris <= 9 32-bit */ |
| 292 | |
| 293 | /* For a valid error number, the system's strerror() function returns |
| 294 | a pointer to a not copied string, not to a buffer. */ |
| 295 | if (errnum >= 0 && errnum < sys_nerr) |
| 296 | { |
| 297 | char *errmsg = strerror (errnum); |
| 298 | |
| 299 | if (errmsg == NULL || *errmsg == '\0') |
| 300 | ret = EINVAL; |
| 301 | else |
| 302 | ret = safe_copy (buf, buflen, errmsg); |
| 303 | } |
| 304 | else |
| 305 | ret = EINVAL; |
| 306 | |
| 307 | # else |
| 308 | |
| 309 | gl_lock_lock (strerror_lock); |
| 310 | |
| 311 | { |
| 312 | char *errmsg = strerror (errnum); |
| 313 | |
| 314 | /* For invalid error numbers, strerror() on |
| 315 | - IRIX 6.5 returns NULL, |
| 316 | - HP-UX 11 returns an empty string. */ |
| 317 | if (errmsg == NULL || *errmsg == '\0') |
| 318 | ret = EINVAL; |
| 319 | else |
| 320 | ret = safe_copy (buf, buflen, errmsg); |
| 321 | } |
| 322 | |
| 323 | gl_lock_unlock (strerror_lock); |
| 324 | |
| 325 | # endif |
| 326 | |
| 327 | #endif |
| 328 | |
| 329 | #if defined _WIN32 && !defined __CYGWIN__ |
| 330 | /* MSVC 14 defines names for many error codes in the range 100..140, |
| 331 | but _sys_errlist contains strings only for the error codes |
| 332 | < _sys_nerr = 43. */ |
| 333 | if (ret == EINVAL) |
| 334 | { |
| 335 | const char *errmsg; |
| 336 | |
| 337 | switch (errnum) |
| 338 | { |
| 339 | case 100 /* EADDRINUSE */: |
| 340 | errmsg = "Address already in use"; |
| 341 | break; |
| 342 | case 101 /* EADDRNOTAVAIL */: |
| 343 | errmsg = "Cannot assign requested address"; |
| 344 | break; |
| 345 | case 102 /* EAFNOSUPPORT */: |
| 346 | errmsg = "Address family not supported by protocol"; |
| 347 | break; |
| 348 | case 103 /* EALREADY */: |
| 349 | errmsg = "Operation already in progress"; |
| 350 | break; |
| 351 | case 105 /* ECANCELED */: |
| 352 | errmsg = "Operation canceled"; |
| 353 | break; |
| 354 | case 106 /* ECONNABORTED */: |
| 355 | errmsg = "Software caused connection abort"; |
| 356 | break; |
| 357 | case 107 /* ECONNREFUSED */: |
| 358 | errmsg = "Connection refused"; |
| 359 | break; |
| 360 | case 108 /* ECONNRESET */: |
| 361 | errmsg = "Connection reset by peer"; |
| 362 | break; |
| 363 | case 109 /* EDESTADDRREQ */: |
| 364 | errmsg = "Destination address required"; |
| 365 | break; |
| 366 | case 110 /* EHOSTUNREACH */: |
| 367 | errmsg = "No route to host"; |
| 368 | break; |
| 369 | case 112 /* EINPROGRESS */: |
| 370 | errmsg = "Operation now in progress"; |
| 371 | break; |
| 372 | case 113 /* EISCONN */: |
| 373 | errmsg = "Transport endpoint is already connected"; |
| 374 | break; |
| 375 | case 114 /* ELOOP */: |
| 376 | errmsg = "Too many levels of symbolic links"; |
| 377 | break; |
| 378 | case 115 /* EMSGSIZE */: |
| 379 | errmsg = "Message too long"; |
| 380 | break; |
| 381 | case 116 /* ENETDOWN */: |
| 382 | errmsg = "Network is down"; |
| 383 | break; |
| 384 | case 117 /* ENETRESET */: |
| 385 | errmsg = "Network dropped connection on reset"; |
| 386 | break; |
| 387 | case 118 /* ENETUNREACH */: |
| 388 | errmsg = "Network is unreachable"; |
| 389 | break; |
| 390 | case 119 /* ENOBUFS */: |
| 391 | errmsg = "No buffer space available"; |
| 392 | break; |
| 393 | case 123 /* ENOPROTOOPT */: |
| 394 | errmsg = "Protocol not available"; |
| 395 | break; |
| 396 | case 126 /* ENOTCONN */: |
| 397 | errmsg = "Transport endpoint is not connected"; |
| 398 | break; |
| 399 | case 128 /* ENOTSOCK */: |
| 400 | errmsg = "Socket operation on non-socket"; |
| 401 | break; |
| 402 | case 129 /* ENOTSUP */: |
| 403 | errmsg = "Not supported"; |
| 404 | break; |
| 405 | case 130 /* EOPNOTSUPP */: |
| 406 | errmsg = "Operation not supported"; |
| 407 | break; |
| 408 | case 132 /* EOVERFLOW */: |
| 409 | errmsg = "Value too large for defined data type"; |
| 410 | break; |
| 411 | case 133 /* EOWNERDEAD */: |
| 412 | errmsg = "Owner died"; |
| 413 | break; |
| 414 | case 134 /* EPROTO */: |
| 415 | errmsg = "Protocol error"; |
| 416 | break; |
| 417 | case 135 /* EPROTONOSUPPORT */: |
| 418 | errmsg = "Protocol not supported"; |
| 419 | break; |
| 420 | case 136 /* EPROTOTYPE */: |
| 421 | errmsg = "Protocol wrong type for socket"; |
| 422 | break; |
| 423 | case 138 /* ETIMEDOUT */: |
| 424 | errmsg = "Connection timed out"; |
| 425 | break; |
| 426 | case 140 /* EWOULDBLOCK */: |
| 427 | errmsg = "Operation would block"; |
| 428 | break; |
| 429 | default: |
| 430 | errmsg = NULL; |
| 431 | break; |
| 432 | } |
| 433 | if (errmsg != NULL) |
| 434 | ret = safe_copy (buf, buflen, errmsg); |
| 435 | } |
| 436 | #endif |
| 437 | |
| 438 | if (ret == EINVAL && !*buf) |
| 439 | { |
| 440 | #if defined __HAIKU__ |
| 441 | /* For consistency with perror(). */ |
| 442 | snprintf (buf, buflen, "Unknown Application Error (%d)", errnum); |
| 443 | #else |
| 444 | snprintf (buf, buflen, "Unknown error %d", errnum); |
| 445 | #endif |
| 446 | } |
| 447 | |
| 448 | errno = saved_errno; |
| 449 | return ret; |
| 450 | } |
| 451 | } |