| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * strerror.c |
| 4 | * Replacements for standard strerror() and strerror_r() functions |
| 5 | * |
| 6 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
| 7 | * Portions Copyright (c) 1994, Regents of the University of California |
| 8 | * |
| 9 | * |
| 10 | * IDENTIFICATION |
| 11 | * src/port/strerror.c |
| 12 | * |
| 13 | *------------------------------------------------------------------------- |
| 14 | */ |
| 15 | #include "c.h" |
| 16 | |
| 17 | /* |
| 18 | * Within this file, "strerror" means the platform's function not pg_strerror, |
| 19 | * and likewise for "strerror_r" |
| 20 | */ |
| 21 | #undef strerror |
| 22 | #undef strerror_r |
| 23 | |
| 24 | static char *gnuish_strerror_r(int errnum, char *buf, size_t buflen); |
| 25 | static char *get_errno_symbol(int errnum); |
| 26 | #ifdef WIN32 |
| 27 | static char *win32_socket_strerror(int errnum, char *buf, size_t buflen); |
| 28 | #endif |
| 29 | |
| 30 | |
| 31 | /* |
| 32 | * A slightly cleaned-up version of strerror() |
| 33 | */ |
| 34 | char * |
| 35 | pg_strerror(int errnum) |
| 36 | { |
| 37 | static char errorstr_buf[PG_STRERROR_R_BUFLEN]; |
| 38 | |
| 39 | return pg_strerror_r(errnum, errorstr_buf, sizeof(errorstr_buf)); |
| 40 | } |
| 41 | |
| 42 | /* |
| 43 | * A slightly cleaned-up version of strerror_r() |
| 44 | */ |
| 45 | char * |
| 46 | pg_strerror_r(int errnum, char *buf, size_t buflen) |
| 47 | { |
| 48 | char *str; |
| 49 | |
| 50 | /* If it's a Windows Winsock error, that needs special handling */ |
| 51 | #ifdef WIN32 |
| 52 | /* Winsock error code range, per WinError.h */ |
| 53 | if (errnum >= 10000 && errnum <= 11999) |
| 54 | return win32_socket_strerror(errnum, buf, buflen); |
| 55 | #endif |
| 56 | |
| 57 | /* Try the platform's strerror_r(), or maybe just strerror() */ |
| 58 | str = gnuish_strerror_r(errnum, buf, buflen); |
| 59 | |
| 60 | /* |
| 61 | * Some strerror()s return an empty string for out-of-range errno. This |
| 62 | * is ANSI C spec compliant, but not exactly useful. Also, we may get |
| 63 | * back strings of question marks if libc cannot transcode the message to |
| 64 | * the codeset specified by LC_CTYPE. If we get nothing useful, first try |
| 65 | * get_errno_symbol(), and if that fails, print the numeric errno. |
| 66 | */ |
| 67 | if (str == NULL || *str == '\0' || *str == '?') |
| 68 | str = get_errno_symbol(errnum); |
| 69 | |
| 70 | if (str == NULL) |
| 71 | { |
| 72 | snprintf(buf, buflen, _("operating system error %d" ), errnum); |
| 73 | str = buf; |
| 74 | } |
| 75 | |
| 76 | return str; |
| 77 | } |
| 78 | |
| 79 | /* |
| 80 | * Simple wrapper to emulate GNU strerror_r if what the platform provides is |
| 81 | * POSIX. Also, if platform lacks strerror_r altogether, fall back to plain |
| 82 | * strerror; it might not be very thread-safe, but tough luck. |
| 83 | */ |
| 84 | static char * |
| 85 | gnuish_strerror_r(int errnum, char *buf, size_t buflen) |
| 86 | { |
| 87 | #ifdef HAVE_STRERROR_R |
| 88 | #ifdef STRERROR_R_INT |
| 89 | /* POSIX API */ |
| 90 | if (strerror_r(errnum, buf, buflen) == 0) |
| 91 | return buf; |
| 92 | return NULL; /* let caller deal with failure */ |
| 93 | #else |
| 94 | /* GNU API */ |
| 95 | return strerror_r(errnum, buf, buflen); |
| 96 | #endif |
| 97 | #else /* !HAVE_STRERROR_R */ |
| 98 | char *sbuf = strerror(errnum); |
| 99 | |
| 100 | if (sbuf == NULL) /* can this still happen anywhere? */ |
| 101 | return NULL; |
| 102 | /* To minimize thread-unsafety hazard, copy into caller's buffer */ |
| 103 | strlcpy(buf, sbuf, buflen); |
| 104 | return buf; |
| 105 | #endif |
| 106 | } |
| 107 | |
| 108 | /* |
| 109 | * Returns a symbol (e.g. "ENOENT") for an errno code. |
| 110 | * Returns NULL if the code is unrecognized. |
| 111 | */ |
| 112 | static char * |
| 113 | get_errno_symbol(int errnum) |
| 114 | { |
| 115 | switch (errnum) |
| 116 | { |
| 117 | case E2BIG: |
| 118 | return "E2BIG" ; |
| 119 | case EACCES: |
| 120 | return "EACCES" ; |
| 121 | #ifdef EADDRINUSE |
| 122 | case EADDRINUSE: |
| 123 | return "EADDRINUSE" ; |
| 124 | #endif |
| 125 | #ifdef EADDRNOTAVAIL |
| 126 | case EADDRNOTAVAIL: |
| 127 | return "EADDRNOTAVAIL" ; |
| 128 | #endif |
| 129 | case EAFNOSUPPORT: |
| 130 | return "EAFNOSUPPORT" ; |
| 131 | #ifdef EAGAIN |
| 132 | case EAGAIN: |
| 133 | return "EAGAIN" ; |
| 134 | #endif |
| 135 | #ifdef EALREADY |
| 136 | case EALREADY: |
| 137 | return "EALREADY" ; |
| 138 | #endif |
| 139 | case EBADF: |
| 140 | return "EBADF" ; |
| 141 | #ifdef EBADMSG |
| 142 | case EBADMSG: |
| 143 | return "EBADMSG" ; |
| 144 | #endif |
| 145 | case EBUSY: |
| 146 | return "EBUSY" ; |
| 147 | case ECHILD: |
| 148 | return "ECHILD" ; |
| 149 | #ifdef ECONNABORTED |
| 150 | case ECONNABORTED: |
| 151 | return "ECONNABORTED" ; |
| 152 | #endif |
| 153 | case ECONNREFUSED: |
| 154 | return "ECONNREFUSED" ; |
| 155 | #ifdef ECONNRESET |
| 156 | case ECONNRESET: |
| 157 | return "ECONNRESET" ; |
| 158 | #endif |
| 159 | case EDEADLK: |
| 160 | return "EDEADLK" ; |
| 161 | case EDOM: |
| 162 | return "EDOM" ; |
| 163 | case EEXIST: |
| 164 | return "EEXIST" ; |
| 165 | case EFAULT: |
| 166 | return "EFAULT" ; |
| 167 | case EFBIG: |
| 168 | return "EFBIG" ; |
| 169 | #ifdef EHOSTUNREACH |
| 170 | case EHOSTUNREACH: |
| 171 | return "EHOSTUNREACH" ; |
| 172 | #endif |
| 173 | case EIDRM: |
| 174 | return "EIDRM" ; |
| 175 | case EINPROGRESS: |
| 176 | return "EINPROGRESS" ; |
| 177 | case EINTR: |
| 178 | return "EINTR" ; |
| 179 | case EINVAL: |
| 180 | return "EINVAL" ; |
| 181 | case EIO: |
| 182 | return "EIO" ; |
| 183 | #ifdef EISCONN |
| 184 | case EISCONN: |
| 185 | return "EISCONN" ; |
| 186 | #endif |
| 187 | case EISDIR: |
| 188 | return "EISDIR" ; |
| 189 | #ifdef ELOOP |
| 190 | case ELOOP: |
| 191 | return "ELOOP" ; |
| 192 | #endif |
| 193 | case EMFILE: |
| 194 | return "EMFILE" ; |
| 195 | case EMLINK: |
| 196 | return "EMLINK" ; |
| 197 | case EMSGSIZE: |
| 198 | return "EMSGSIZE" ; |
| 199 | case ENAMETOOLONG: |
| 200 | return "ENAMETOOLONG" ; |
| 201 | case ENFILE: |
| 202 | return "ENFILE" ; |
| 203 | case ENOBUFS: |
| 204 | return "ENOBUFS" ; |
| 205 | case ENODEV: |
| 206 | return "ENODEV" ; |
| 207 | case ENOENT: |
| 208 | return "ENOENT" ; |
| 209 | case ENOEXEC: |
| 210 | return "ENOEXEC" ; |
| 211 | case ENOMEM: |
| 212 | return "ENOMEM" ; |
| 213 | case ENOSPC: |
| 214 | return "ENOSPC" ; |
| 215 | case ENOSYS: |
| 216 | return "ENOSYS" ; |
| 217 | #ifdef ENOTCONN |
| 218 | case ENOTCONN: |
| 219 | return "ENOTCONN" ; |
| 220 | #endif |
| 221 | case ENOTDIR: |
| 222 | return "ENOTDIR" ; |
| 223 | #if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */ |
| 224 | case ENOTEMPTY: |
| 225 | return "ENOTEMPTY" ; |
| 226 | #endif |
| 227 | #ifdef ENOTSOCK |
| 228 | case ENOTSOCK: |
| 229 | return "ENOTSOCK" ; |
| 230 | #endif |
| 231 | #ifdef ENOTSUP |
| 232 | case ENOTSUP: |
| 233 | return "ENOTSUP" ; |
| 234 | #endif |
| 235 | case ENOTTY: |
| 236 | return "ENOTTY" ; |
| 237 | case ENXIO: |
| 238 | return "ENXIO" ; |
| 239 | #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP)) |
| 240 | case EOPNOTSUPP: |
| 241 | return "EOPNOTSUPP" ; |
| 242 | #endif |
| 243 | #ifdef EOVERFLOW |
| 244 | case EOVERFLOW: |
| 245 | return "EOVERFLOW" ; |
| 246 | #endif |
| 247 | case EPERM: |
| 248 | return "EPERM" ; |
| 249 | case EPIPE: |
| 250 | return "EPIPE" ; |
| 251 | case EPROTONOSUPPORT: |
| 252 | return "EPROTONOSUPPORT" ; |
| 253 | case ERANGE: |
| 254 | return "ERANGE" ; |
| 255 | #ifdef EROFS |
| 256 | case EROFS: |
| 257 | return "EROFS" ; |
| 258 | #endif |
| 259 | case ESRCH: |
| 260 | return "ESRCH" ; |
| 261 | #ifdef ETIMEDOUT |
| 262 | case ETIMEDOUT: |
| 263 | return "ETIMEDOUT" ; |
| 264 | #endif |
| 265 | #ifdef ETXTBSY |
| 266 | case ETXTBSY: |
| 267 | return "ETXTBSY" ; |
| 268 | #endif |
| 269 | #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN)) |
| 270 | case EWOULDBLOCK: |
| 271 | return "EWOULDBLOCK" ; |
| 272 | #endif |
| 273 | case EXDEV: |
| 274 | return "EXDEV" ; |
| 275 | } |
| 276 | |
| 277 | return NULL; |
| 278 | } |
| 279 | |
| 280 | |
| 281 | #ifdef WIN32 |
| 282 | |
| 283 | /* |
| 284 | * Windows' strerror() doesn't know the Winsock codes, so handle them this way |
| 285 | */ |
| 286 | static char * |
| 287 | win32_socket_strerror(int errnum, char *buf, size_t buflen) |
| 288 | { |
| 289 | static HANDLE handleDLL = INVALID_HANDLE_VALUE; |
| 290 | |
| 291 | if (handleDLL == INVALID_HANDLE_VALUE) |
| 292 | { |
| 293 | handleDLL = LoadLibraryEx("netmsg.dll" , NULL, |
| 294 | DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE); |
| 295 | if (handleDLL == NULL) |
| 296 | { |
| 297 | snprintf(buf, buflen, |
| 298 | "winsock error %d (could not load netmsg.dll to translate: error code %lu)" , |
| 299 | errnum, GetLastError()); |
| 300 | return buf; |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | ZeroMemory(buf, buflen); |
| 305 | if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | |
| 306 | FORMAT_MESSAGE_FROM_SYSTEM | |
| 307 | FORMAT_MESSAGE_FROM_HMODULE, |
| 308 | handleDLL, |
| 309 | errnum, |
| 310 | MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT), |
| 311 | buf, |
| 312 | buflen - 1, |
| 313 | NULL) == 0) |
| 314 | { |
| 315 | /* Failed to get id */ |
| 316 | snprintf(buf, buflen, "unrecognized winsock error %d" , errnum); |
| 317 | } |
| 318 | |
| 319 | return buf; |
| 320 | } |
| 321 | |
| 322 | #endif /* WIN32 */ |
| 323 | |