PostgreSQL Source Code  git master
strerror.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * strerror.c
4  * Replacements for standard strerror() and strerror_r() functions
5  *
6  * Portions Copyright (c) 1996-2023, 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  case EADDRINUSE:
122  return "EADDRINUSE";
123  case EADDRNOTAVAIL:
124  return "EADDRNOTAVAIL";
125  case EAFNOSUPPORT:
126  return "EAFNOSUPPORT";
127 #ifdef EAGAIN
128  case EAGAIN:
129  return "EAGAIN";
130 #endif
131 #ifdef EALREADY
132  case EALREADY:
133  return "EALREADY";
134 #endif
135  case EBADF:
136  return "EBADF";
137 #ifdef EBADMSG
138  case EBADMSG:
139  return "EBADMSG";
140 #endif
141  case EBUSY:
142  return "EBUSY";
143  case ECHILD:
144  return "ECHILD";
145  case ECONNABORTED:
146  return "ECONNABORTED";
147  case ECONNREFUSED:
148  return "ECONNREFUSED";
149  case ECONNRESET:
150  return "ECONNRESET";
151  case EDEADLK:
152  return "EDEADLK";
153  case EDOM:
154  return "EDOM";
155  case EEXIST:
156  return "EEXIST";
157  case EFAULT:
158  return "EFAULT";
159  case EFBIG:
160  return "EFBIG";
161  case EHOSTDOWN:
162  return "EHOSTDOWN";
163  case EHOSTUNREACH:
164  return "EHOSTUNREACH";
165  case EIDRM:
166  return "EIDRM";
167  case EINPROGRESS:
168  return "EINPROGRESS";
169  case EINTR:
170  return "EINTR";
171  case EINVAL:
172  return "EINVAL";
173  case EIO:
174  return "EIO";
175  case EISCONN:
176  return "EISCONN";
177  case EISDIR:
178  return "EISDIR";
179 #ifdef ELOOP
180  case ELOOP:
181  return "ELOOP";
182 #endif
183  case EMFILE:
184  return "EMFILE";
185  case EMLINK:
186  return "EMLINK";
187  case EMSGSIZE:
188  return "EMSGSIZE";
189  case ENAMETOOLONG:
190  return "ENAMETOOLONG";
191  case ENETDOWN:
192  return "ENETDOWN";
193  case ENETRESET:
194  return "ENETRESET";
195  case ENETUNREACH:
196  return "ENETUNREACH";
197  case ENFILE:
198  return "ENFILE";
199  case ENOBUFS:
200  return "ENOBUFS";
201  case ENODEV:
202  return "ENODEV";
203  case ENOENT:
204  return "ENOENT";
205  case ENOEXEC:
206  return "ENOEXEC";
207  case ENOMEM:
208  return "ENOMEM";
209  case ENOSPC:
210  return "ENOSPC";
211  case ENOSYS:
212  return "ENOSYS";
213  case ENOTCONN:
214  return "ENOTCONN";
215  case ENOTDIR:
216  return "ENOTDIR";
217 #if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
218  case ENOTEMPTY:
219  return "ENOTEMPTY";
220 #endif
221  case ENOTSOCK:
222  return "ENOTSOCK";
223 #ifdef ENOTSUP
224  case ENOTSUP:
225  return "ENOTSUP";
226 #endif
227  case ENOTTY:
228  return "ENOTTY";
229  case ENXIO:
230  return "ENXIO";
231 #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
232  case EOPNOTSUPP:
233  return "EOPNOTSUPP";
234 #endif
235 #ifdef EOVERFLOW
236  case EOVERFLOW:
237  return "EOVERFLOW";
238 #endif
239  case EPERM:
240  return "EPERM";
241  case EPIPE:
242  return "EPIPE";
243  case EPROTONOSUPPORT:
244  return "EPROTONOSUPPORT";
245  case ERANGE:
246  return "ERANGE";
247 #ifdef EROFS
248  case EROFS:
249  return "EROFS";
250 #endif
251  case ESRCH:
252  return "ESRCH";
253  case ETIMEDOUT:
254  return "ETIMEDOUT";
255 #ifdef ETXTBSY
256  case ETXTBSY:
257  return "ETXTBSY";
258 #endif
259 #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
260  case EWOULDBLOCK:
261  return "EWOULDBLOCK";
262 #endif
263  case EXDEV:
264  return "EXDEV";
265  }
266 
267  return NULL;
268 }
269 
270 
271 #ifdef WIN32
272 
273 /*
274  * Windows' strerror() doesn't know the Winsock codes, so handle them this way
275  */
276 static char *
277 win32_socket_strerror(int errnum, char *buf, size_t buflen)
278 {
279  static HANDLE handleDLL = INVALID_HANDLE_VALUE;
280 
281  if (handleDLL == INVALID_HANDLE_VALUE)
282  {
283  handleDLL = LoadLibraryEx("netmsg.dll", NULL,
284  DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
285  if (handleDLL == NULL)
286  {
287  snprintf(buf, buflen,
288  "winsock error %d (could not load netmsg.dll to translate: error code %lu)",
289  errnum, GetLastError());
290  return buf;
291  }
292  }
293 
294  ZeroMemory(buf, buflen);
295  if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
296  FORMAT_MESSAGE_FROM_SYSTEM |
297  FORMAT_MESSAGE_FROM_HMODULE,
298  handleDLL,
299  errnum,
300  MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
301  buf,
302  buflen - 1,
303  NULL) == 0)
304  {
305  /* Failed to get id */
306  snprintf(buf, buflen, "unrecognized winsock error %d", errnum);
307  }
308 
309  return buf;
310 }
311 
312 #endif /* WIN32 */
#define _(x)
Definition: elog.c:91
static char * buf
Definition: pg_test_fsync.c:73
#define PG_STRERROR_R_BUFLEN
Definition: port.h:256
#define strerror
Definition: port.h:251
#define snprintf
Definition: port.h:238
#define strerror_r
Definition: port.h:255
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
#define EOVERFLOW
Definition: private.h:41
#define ENOTSUP
Definition: private.h:38
static char * get_errno_symbol(int errnum)
Definition: strerror.c:113
char * pg_strerror_r(int errnum, char *buf, size_t buflen)
Definition: strerror.c:46
static char * gnuish_strerror_r(int errnum, char *buf, size_t buflen)
Definition: strerror.c:85
char * pg_strerror(int errnum)
Definition: strerror.c:35
#define EISCONN
Definition: win32_port.h:388
#define ENETUNREACH
Definition: win32_port.h:412
#define ECONNABORTED
Definition: win32_port.h:382
#define EINTR
Definition: win32_port.h:374
#define EWOULDBLOCK
Definition: win32_port.h:380
#define EOPNOTSUPP
Definition: win32_port.h:398
#define EAFNOSUPPORT
Definition: win32_port.h:378
#define EHOSTUNREACH
Definition: win32_port.h:406
#define EADDRNOTAVAIL
Definition: win32_port.h:402
#define ETIMEDOUT
Definition: win32_port.h:416
#define EADDRINUSE
Definition: win32_port.h:400
#define EINPROGRESS
Definition: win32_port.h:386
#define ENETRESET
Definition: win32_port.h:410
#define ENOBUFS
Definition: win32_port.h:390
#define EHOSTDOWN
Definition: win32_port.h:404
#define ENETDOWN
Definition: win32_port.h:408
#define ECONNREFUSED
Definition: win32_port.h:394
#define EPROTONOSUPPORT
Definition: win32_port.h:392
#define EIDRM
Definition: win32_port.h:104
#define ECONNRESET
Definition: win32_port.h:384
#define ENOTSOCK
Definition: win32_port.h:396
#define EMSGSIZE
Definition: win32_port.h:376
#define EAGAIN
Definition: win32_port.h:372
#define ENOTCONN
Definition: win32_port.h:414