| /*------------------------------------------------------------------------- |
| * |
| * strerror.c |
| * Replacements for standard strerror() and strerror_r() functions |
| * |
| * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * |
| * IDENTIFICATION |
| * src/port/strerror.c |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #include "c.h" |
| |
| /* |
| * Within this file, "strerror" means the platform's function not pg_strerror, |
| * and likewise for "strerror_r" |
| */ |
| #undef strerror |
| #undef strerror_r |
| |
| static char *gnuish_strerror_r(int errnum, char *buf, size_t buflen); |
| static char *get_errno_symbol(int errnum); |
| #ifdef WIN32 |
| static char *win32_socket_strerror(int errnum, char *buf, size_t buflen); |
| #endif |
| |
| |
| /* |
| * A slightly cleaned-up version of strerror() |
| */ |
| char * |
| pg_strerror(int errnum) |
| { |
| static char errorstr_buf[PG_STRERROR_R_BUFLEN]; |
| |
| return pg_strerror_r(errnum, errorstr_buf, sizeof(errorstr_buf)); |
| } |
| |
| /* |
| * A slightly cleaned-up version of strerror_r() |
| */ |
| char * |
| pg_strerror_r(int errnum, char *buf, size_t buflen) |
| { |
| char *str; |
| |
| /* If it's a Windows Winsock error, that needs special handling */ |
| #ifdef WIN32 |
| /* Winsock error code range, per WinError.h */ |
| if (errnum >= 10000 && errnum <= 11999) |
| return win32_socket_strerror(errnum, buf, buflen); |
| #endif |
| |
| /* Try the platform's strerror_r(), or maybe just strerror() */ |
| str = gnuish_strerror_r(errnum, buf, buflen); |
| |
| /* |
| * Some strerror()s return an empty string for out-of-range errno. This |
| * is ANSI C spec compliant, but not exactly useful. Also, we may get |
| * back strings of question marks if libc cannot transcode the message to |
| * the codeset specified by LC_CTYPE. If we get nothing useful, first try |
| * get_errno_symbol(), and if that fails, print the numeric errno. |
| */ |
| if (str == NULL || *str == '\0' || *str == '?') |
| str = get_errno_symbol(errnum); |
| |
| if (str == NULL) |
| { |
| snprintf(buf, buflen, _("operating system error %d"), errnum); |
| str = buf; |
| } |
| |
| return str; |
| } |
| |
| /* |
| * Simple wrapper to emulate GNU strerror_r if what the platform provides is |
| * POSIX. Also, if platform lacks strerror_r altogether, fall back to plain |
| * strerror; it might not be very thread-safe, but tough luck. |
| */ |
| static char * |
| gnuish_strerror_r(int errnum, char *buf, size_t buflen) |
| { |
| #ifdef HAVE_STRERROR_R |
| #ifdef STRERROR_R_INT |
| /* POSIX API */ |
| if (strerror_r(errnum, buf, buflen) == 0) |
| return buf; |
| return NULL; /* let caller deal with failure */ |
| #else |
| /* GNU API */ |
| return strerror_r(errnum, buf, buflen); |
| #endif |
| #else /* !HAVE_STRERROR_R */ |
| char *sbuf = strerror(errnum); |
| |
| if (sbuf == NULL) /* can this still happen anywhere? */ |
| return NULL; |
| /* To minimize thread-unsafety hazard, copy into caller's buffer */ |
| strlcpy(buf, sbuf, buflen); |
| return buf; |
| #endif |
| } |
| |
| /* |
| * Returns a symbol (e.g. "ENOENT") for an errno code. |
| * Returns NULL if the code is unrecognized. |
| */ |
| static char * |
| get_errno_symbol(int errnum) |
| { |
| switch (errnum) |
| { |
| case E2BIG: |
| return "E2BIG"; |
| case EACCES: |
| return "EACCES"; |
| case EADDRINUSE: |
| return "EADDRINUSE"; |
| case EADDRNOTAVAIL: |
| return "EADDRNOTAVAIL"; |
| case EAFNOSUPPORT: |
| return "EAFNOSUPPORT"; |
| #ifdef EAGAIN |
| case EAGAIN: |
| return "EAGAIN"; |
| #endif |
| #ifdef EALREADY |
| case EALREADY: |
| return "EALREADY"; |
| #endif |
| case EBADF: |
| return "EBADF"; |
| #ifdef EBADMSG |
| case EBADMSG: |
| return "EBADMSG"; |
| #endif |
| case EBUSY: |
| return "EBUSY"; |
| case ECHILD: |
| return "ECHILD"; |
| case ECONNABORTED: |
| return "ECONNABORTED"; |
| case ECONNREFUSED: |
| return "ECONNREFUSED"; |
| case ECONNRESET: |
| return "ECONNRESET"; |
| case EDEADLK: |
| return "EDEADLK"; |
| case EDOM: |
| return "EDOM"; |
| case EEXIST: |
| return "EEXIST"; |
| case EFAULT: |
| return "EFAULT"; |
| case EFBIG: |
| return "EFBIG"; |
| case EHOSTDOWN: |
| return "EHOSTDOWN"; |
| case EHOSTUNREACH: |
| return "EHOSTUNREACH"; |
| case EIDRM: |
| return "EIDRM"; |
| case EINPROGRESS: |
| return "EINPROGRESS"; |
| case EINTR: |
| return "EINTR"; |
| case EINVAL: |
| return "EINVAL"; |
| case EIO: |
| return "EIO"; |
| case EISCONN: |
| return "EISCONN"; |
| case EISDIR: |
| return "EISDIR"; |
| #ifdef ELOOP |
| case ELOOP: |
| return "ELOOP"; |
| #endif |
| case EMFILE: |
| return "EMFILE"; |
| case EMLINK: |
| return "EMLINK"; |
| case EMSGSIZE: |
| return "EMSGSIZE"; |
| case ENAMETOOLONG: |
| return "ENAMETOOLONG"; |
| case ENETDOWN: |
| return "ENETDOWN"; |
| case ENETRESET: |
| return "ENETRESET"; |
| case ENETUNREACH: |
| return "ENETUNREACH"; |
| case ENFILE: |
| return "ENFILE"; |
| case ENOBUFS: |
| return "ENOBUFS"; |
| case ENODEV: |
| return "ENODEV"; |
| case ENOENT: |
| return "ENOENT"; |
| case ENOEXEC: |
| return "ENOEXEC"; |
| case ENOMEM: |
| return "ENOMEM"; |
| case ENOSPC: |
| return "ENOSPC"; |
| case ENOSYS: |
| return "ENOSYS"; |
| case ENOTCONN: |
| return "ENOTCONN"; |
| case ENOTDIR: |
| return "ENOTDIR"; |
| #if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */ |
| case ENOTEMPTY: |
| return "ENOTEMPTY"; |
| #endif |
| case ENOTSOCK: |
| return "ENOTSOCK"; |
| #ifdef ENOTSUP |
| case ENOTSUP: |
| return "ENOTSUP"; |
| #endif |
| case ENOTTY: |
| return "ENOTTY"; |
| case ENXIO: |
| return "ENXIO"; |
| #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP)) |
| case EOPNOTSUPP: |
| return "EOPNOTSUPP"; |
| #endif |
| #ifdef EOVERFLOW |
| case EOVERFLOW: |
| return "EOVERFLOW"; |
| #endif |
| case EPERM: |
| return "EPERM"; |
| case EPIPE: |
| return "EPIPE"; |
| case EPROTONOSUPPORT: |
| return "EPROTONOSUPPORT"; |
| case ERANGE: |
| return "ERANGE"; |
| #ifdef EROFS |
| case EROFS: |
| return "EROFS"; |
| #endif |
| case ESRCH: |
| return "ESRCH"; |
| case ETIMEDOUT: |
| return "ETIMEDOUT"; |
| #ifdef ETXTBSY |
| case ETXTBSY: |
| return "ETXTBSY"; |
| #endif |
| #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) |
| case EWOULDBLOCK: |
| return "EWOULDBLOCK"; |
| #endif |
| case EXDEV: |
| return "EXDEV"; |
| } |
| |
| return NULL; |
| } |
| |
| |
| #ifdef WIN32 |
| |
| /* |
| * Windows' strerror() doesn't know the Winsock codes, so handle them this way |
| */ |
| static char * |
| win32_socket_strerror(int errnum, char *buf, size_t buflen) |
| { |
| static HANDLE handleDLL = INVALID_HANDLE_VALUE; |
| |
| if (handleDLL == INVALID_HANDLE_VALUE) |
| { |
| handleDLL = LoadLibraryEx("netmsg.dll", NULL, |
| DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); |
| if (handleDLL == NULL) |
| { |
| snprintf(buf, buflen, |
| "winsock error %d (could not load netmsg.dll to translate: error code %lu)", |
| errnum, GetLastError()); |
| return buf; |
| } |
| } |
| |
| ZeroMemory(buf, buflen); |
| if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | |
| FORMAT_MESSAGE_FROM_SYSTEM | |
| FORMAT_MESSAGE_FROM_HMODULE, |
| handleDLL, |
| errnum, |
| MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), |
| buf, |
| buflen - 1, |
| NULL) == 0) |
| { |
| /* Failed to get id */ |
| snprintf(buf, buflen, "unrecognized winsock error %d", errnum); |
| } |
| |
| return buf; |
| } |
| |
| #endif /* WIN32 */ |