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-2026, 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 *
2727 tgl@sss.pgh.pa.us 35 :UBC 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 *
2727 tgl@sss.pgh.pa.us 46 :CBC 440 : 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 : 440 : 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 [ + - + - : 440 : if (str == NULL || *str == '\0' || *str == '?')
- + ]
2727 tgl@sss.pgh.pa.us 68 :UBC 0 : str = get_errno_symbol(errnum);
69 : :
2727 tgl@sss.pgh.pa.us 70 [ - + ]:CBC 440 : if (str == NULL)
71 : : {
2727 tgl@sss.pgh.pa.us 72 :UBC 0 : snprintf(buf, buflen, _("operating system error %d"), errnum);
73 : 0 : str = buf;
74 : : }
75 : :
2727 tgl@sss.pgh.pa.us 76 :CBC 440 : 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 : 440 : 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 : 440 : 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 *
2727 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";
1982 161 : 0 : case EHOSTDOWN:
162 : 0 : return "EHOSTDOWN";
2727 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";
1982 191 : 0 : case ENETDOWN:
192 : 0 : return "ENETDOWN";
193 : 0 : case ENETRESET:
194 : 0 : return "ENETRESET";
195 : 0 : case ENETUNREACH:
196 : 0 : return "ENETUNREACH";
2727 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 : : #if defined(ENOTEMPTY) && (ENOTEMPTY != EEXIST) /* same code on AIX */
218 : 0 : case ENOTEMPTY:
219 : 0 : return "ENOTEMPTY";
220 : : #endif
221 : 0 : case ENOTSOCK:
222 : 0 : return "ENOTSOCK";
223 : : #ifdef ENOTSUP
224 : 0 : case ENOTSUP:
225 : 0 : return "ENOTSUP";
226 : : #endif
227 : 0 : case ENOTTY:
228 : 0 : return "ENOTTY";
229 : 0 : case ENXIO:
230 : 0 : return "ENXIO";
231 : : #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
232 : : case EOPNOTSUPP:
233 : : return "EOPNOTSUPP";
234 : : #endif
235 : : #ifdef EOVERFLOW
236 : 0 : case EOVERFLOW:
237 : 0 : return "EOVERFLOW";
238 : : #endif
239 : 0 : case EPERM:
240 : 0 : return "EPERM";
241 : 0 : case EPIPE:
242 : 0 : return "EPIPE";
243 : 0 : case EPROTONOSUPPORT:
244 : 0 : return "EPROTONOSUPPORT";
245 : 0 : case ERANGE:
246 : 0 : return "ERANGE";
247 : : #ifdef EROFS
248 : 0 : case EROFS:
249 : 0 : return "EROFS";
250 : : #endif
251 : 0 : case ESRCH:
252 : 0 : return "ESRCH";
253 : 0 : case ETIMEDOUT:
254 : 0 : return "ETIMEDOUT";
255 : : #ifdef ETXTBSY
256 : 0 : case ETXTBSY:
257 : 0 : return "ETXTBSY";
258 : : #endif
259 : : #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
260 : : case EWOULDBLOCK:
261 : : return "EWOULDBLOCK";
262 : : #endif
263 : 0 : case EXDEV:
264 : 0 : return "EXDEV";
265 : : }
266 : :
267 : 0 : 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 */
|