Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * strerror.c
4 : : * Replacements for standard strerror() and strerror_r() functions
5 : : *
6 : : * Portions Copyright (c) 1996-2024, 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 *
2027 tgl@sss.pgh.pa.us 35 :CBC 4 : pg_strerror(int errnum)
36 : : {
37 : : static char errorstr_buf[PG_STRERROR_R_BUFLEN];
38 : :
39 : 4 : 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 : 476 : 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 : 476 : 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 [ + - + - : 476 : if (str == NULL || *str == '\0' || *str == '?')
- + ]
2027 tgl@sss.pgh.pa.us 68 :UBC 0 : str = get_errno_symbol(errnum);
69 : :
2027 tgl@sss.pgh.pa.us 70 [ - + ]:CBC 476 : if (str == NULL)
71 : : {
2027 tgl@sss.pgh.pa.us 72 :UBC 0 : snprintf(buf, buflen, _("operating system error %d"), errnum);
73 : 0 : str = buf;
74 : : }
75 : :
2027 tgl@sss.pgh.pa.us 76 :CBC 476 : 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 : 476 : 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 : 476 : 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 *
2027 tgl@sss.pgh.pa.us 113 :UBC 0 : get_errno_symbol(int errnum)
114 : : {
115 [ # # # # : 0 : switch (errnum)
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # ]
116 : : {
117 : 0 : case E2BIG:
118 : 0 : return "E2BIG";
119 : 0 : case EACCES:
120 : 0 : return "EACCES";
121 : 0 : case EADDRINUSE:
122 : 0 : return "EADDRINUSE";
123 : 0 : case EADDRNOTAVAIL:
124 : 0 : return "EADDRNOTAVAIL";
125 : 0 : case EAFNOSUPPORT:
126 : 0 : return "EAFNOSUPPORT";
127 : : #ifdef EAGAIN
128 : 0 : case EAGAIN:
129 : 0 : return "EAGAIN";
130 : : #endif
131 : : #ifdef EALREADY
132 : 0 : case EALREADY:
133 : 0 : return "EALREADY";
134 : : #endif
135 : 0 : case EBADF:
136 : 0 : return "EBADF";
137 : : #ifdef EBADMSG
138 : 0 : case EBADMSG:
139 : 0 : return "EBADMSG";
140 : : #endif
141 : 0 : case EBUSY:
142 : 0 : return "EBUSY";
143 : 0 : case ECHILD:
144 : 0 : return "ECHILD";
145 : 0 : case ECONNABORTED:
146 : 0 : return "ECONNABORTED";
147 : 0 : case ECONNREFUSED:
148 : 0 : return "ECONNREFUSED";
149 : 0 : case ECONNRESET:
150 : 0 : return "ECONNRESET";
151 : 0 : case EDEADLK:
152 : 0 : return "EDEADLK";
153 : 0 : case EDOM:
154 : 0 : return "EDOM";
155 : 0 : case EEXIST:
156 : 0 : return "EEXIST";
157 : 0 : case EFAULT:
158 : 0 : return "EFAULT";
159 : 0 : case EFBIG:
160 : 0 : return "EFBIG";
1282 161 : 0 : case EHOSTDOWN:
162 : 0 : return "EHOSTDOWN";
2027 163 : 0 : case EHOSTUNREACH:
164 : 0 : return "EHOSTUNREACH";
165 : 0 : case EIDRM:
166 : 0 : return "EIDRM";
167 : 0 : case EINPROGRESS:
168 : 0 : return "EINPROGRESS";
169 : 0 : case EINTR:
170 : 0 : return "EINTR";
171 : 0 : case EINVAL:
172 : 0 : return "EINVAL";
173 : 0 : case EIO:
174 : 0 : return "EIO";
175 : 0 : case EISCONN:
176 : 0 : return "EISCONN";
177 : 0 : case EISDIR:
178 : 0 : return "EISDIR";
179 : : #ifdef ELOOP
180 : 0 : case ELOOP:
181 : 0 : return "ELOOP";
182 : : #endif
183 : 0 : case EMFILE:
184 : 0 : return "EMFILE";
185 : 0 : case EMLINK:
186 : 0 : return "EMLINK";
187 : 0 : case EMSGSIZE:
188 : 0 : return "EMSGSIZE";
189 : 0 : case ENAMETOOLONG:
190 : 0 : return "ENAMETOOLONG";
1282 191 : 0 : case ENETDOWN:
192 : 0 : return "ENETDOWN";
193 : 0 : case ENETRESET:
194 : 0 : return "ENETRESET";
195 : 0 : case ENETUNREACH:
196 : 0 : return "ENETUNREACH";
2027 197 : 0 : case ENFILE:
198 : 0 : return "ENFILE";
199 : 0 : case ENOBUFS:
200 : 0 : return "ENOBUFS";
201 : 0 : case ENODEV:
202 : 0 : return "ENODEV";
203 : 0 : case ENOENT:
204 : 0 : return "ENOENT";
205 : 0 : case ENOEXEC:
206 : 0 : return "ENOEXEC";
207 : 0 : case ENOMEM:
208 : 0 : return "ENOMEM";
209 : 0 : case ENOSPC:
210 : 0 : return "ENOSPC";
211 : 0 : case ENOSYS:
212 : 0 : return "ENOSYS";
213 : 0 : case ENOTCONN:
214 : 0 : return "ENOTCONN";
215 : 0 : case ENOTDIR:
216 : 0 : return "ENOTDIR";
217 : 0 : case ENOTEMPTY:
218 : 0 : return "ENOTEMPTY";
219 : 0 : case ENOTSOCK:
220 : 0 : return "ENOTSOCK";
221 : : #ifdef ENOTSUP
222 : 0 : case ENOTSUP:
223 : 0 : return "ENOTSUP";
224 : : #endif
225 : 0 : case ENOTTY:
226 : 0 : return "ENOTTY";
227 : 0 : case ENXIO:
228 : 0 : return "ENXIO";
229 : : #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
230 : : case EOPNOTSUPP:
231 : : return "EOPNOTSUPP";
232 : : #endif
233 : : #ifdef EOVERFLOW
234 : 0 : case EOVERFLOW:
235 : 0 : return "EOVERFLOW";
236 : : #endif
237 : 0 : case EPERM:
238 : 0 : return "EPERM";
239 : 0 : case EPIPE:
240 : 0 : return "EPIPE";
241 : 0 : case EPROTONOSUPPORT:
242 : 0 : return "EPROTONOSUPPORT";
243 : 0 : case ERANGE:
244 : 0 : return "ERANGE";
245 : : #ifdef EROFS
246 : 0 : case EROFS:
247 : 0 : return "EROFS";
248 : : #endif
249 : 0 : case ESRCH:
250 : 0 : return "ESRCH";
251 : 0 : case ETIMEDOUT:
252 : 0 : return "ETIMEDOUT";
253 : : #ifdef ETXTBSY
254 : 0 : case ETXTBSY:
255 : 0 : return "ETXTBSY";
256 : : #endif
257 : : #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
258 : : case EWOULDBLOCK:
259 : : return "EWOULDBLOCK";
260 : : #endif
261 : 0 : case EXDEV:
262 : 0 : return "EXDEV";
263 : : }
264 : :
265 : 0 : return NULL;
266 : : }
267 : :
268 : :
269 : : #ifdef WIN32
270 : :
271 : : /*
272 : : * Windows' strerror() doesn't know the Winsock codes, so handle them this way
273 : : */
274 : : static char *
275 : : win32_socket_strerror(int errnum, char *buf, size_t buflen)
276 : : {
277 : : static HANDLE handleDLL = INVALID_HANDLE_VALUE;
278 : :
279 : : if (handleDLL == INVALID_HANDLE_VALUE)
280 : : {
281 : : handleDLL = LoadLibraryEx("netmsg.dll", NULL,
282 : : DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
283 : : if (handleDLL == NULL)
284 : : {
285 : : snprintf(buf, buflen,
286 : : "winsock error %d (could not load netmsg.dll to translate: error code %lu)",
287 : : errnum, GetLastError());
288 : : return buf;
289 : : }
290 : : }
291 : :
292 : : ZeroMemory(buf, buflen);
293 : : if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
294 : : FORMAT_MESSAGE_FROM_SYSTEM |
295 : : FORMAT_MESSAGE_FROM_HMODULE,
296 : : handleDLL,
297 : : errnum,
298 : : MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
299 : : buf,
300 : : buflen - 1,
301 : : NULL) == 0)
302 : : {
303 : : /* Failed to get id */
304 : : snprintf(buf, buflen, "unrecognized winsock error %d", errnum);
305 : : }
306 : :
307 : : return buf;
308 : : }
309 : :
310 : : #endif /* WIN32 */
|