postgresql/src/port/strerror.c

311 lines
6.6 KiB
C

/*-------------------------------------------------------------------------
*
* strerror.c
* Replacements for standard strerror() and strerror_r() functions
*
* Portions Copyright (c) 1996-2024, 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";
case ENOTEMPTY:
return "ENOTEMPTY";
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 */