311 lines
6.6 KiB
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 */
|