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 *
35 0 : pg_strerror(int errnum)
36 : {
37 : static char errorstr_buf[PG_STRERROR_R_BUFLEN];
38 :
39 0 : 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 728 : 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 728 : 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 728 : if (str == NULL || *str == '\0' || *str == '?')
68 0 : str = get_errno_symbol(errnum);
69 :
70 728 : if (str == NULL)
71 : {
72 0 : snprintf(buf, buflen, _("operating system error %d"), errnum);
73 0 : str = buf;
74 : }
75 :
76 728 : 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 728 : 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 728 : 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 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";
161 0 : case EHOSTDOWN:
162 0 : return "EHOSTDOWN";
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";
191 0 : case ENETDOWN:
192 0 : return "ENETDOWN";
193 0 : case ENETRESET:
194 0 : return "ENETRESET";
195 0 : case ENETUNREACH:
196 0 : return "ENETUNREACH";
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 */
|