/src/postgres/src/port/strerror.c
Line | Count | Source (jump to first uncovered line) |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * strerror.c |
4 | | * Replacements for standard strerror() and strerror_r() functions |
5 | | * |
6 | | * Portions Copyright (c) 1996-2025, 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 | 0 | { |
37 | 0 | static char errorstr_buf[PG_STRERROR_R_BUFLEN]; |
38 | |
|
39 | 0 | return pg_strerror_r(errnum, errorstr_buf, sizeof(errorstr_buf)); |
40 | 0 | } |
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 | 0 | { |
48 | 0 | 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 | 0 | 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 | 0 | if (str == NULL || *str == '\0' || *str == '?') |
68 | 0 | str = get_errno_symbol(errnum); |
69 | |
|
70 | 0 | if (str == NULL) |
71 | 0 | { |
72 | 0 | snprintf(buf, buflen, _("operating system error %d"), errnum); |
73 | 0 | str = buf; |
74 | 0 | } |
75 | |
|
76 | 0 | return str; |
77 | 0 | } |
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 | 0 | { |
87 | 0 | #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 | 0 | return strerror_r(errnum, buf, buflen); |
96 | 0 | #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 | 0 | } |
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 | 0 | { |
115 | 0 | switch (errnum) |
116 | 0 | { |
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 | 0 | #ifdef EAGAIN |
128 | 0 | case EAGAIN: |
129 | 0 | return "EAGAIN"; |
130 | 0 | #endif |
131 | 0 | #ifdef EALREADY |
132 | 0 | case EALREADY: |
133 | 0 | return "EALREADY"; |
134 | 0 | #endif |
135 | 0 | case EBADF: |
136 | 0 | return "EBADF"; |
137 | 0 | #ifdef EBADMSG |
138 | 0 | case EBADMSG: |
139 | 0 | return "EBADMSG"; |
140 | 0 | #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 | 0 | #ifdef ELOOP |
180 | 0 | case ELOOP: |
181 | 0 | return "ELOOP"; |
182 | 0 | #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 | 0 | #ifdef ENOTSUP |
222 | 0 | case ENOTSUP: |
223 | 0 | return "ENOTSUP"; |
224 | 0 | #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 | 0 | #ifdef EOVERFLOW |
234 | 0 | case EOVERFLOW: |
235 | 0 | return "EOVERFLOW"; |
236 | 0 | #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 | 0 | #ifdef EROFS |
246 | 0 | case EROFS: |
247 | 0 | return "EROFS"; |
248 | 0 | #endif |
249 | 0 | case ESRCH: |
250 | 0 | return "ESRCH"; |
251 | 0 | case ETIMEDOUT: |
252 | 0 | return "ETIMEDOUT"; |
253 | 0 | #ifdef ETXTBSY |
254 | 0 | case ETXTBSY: |
255 | 0 | return "ETXTBSY"; |
256 | 0 | #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 | 0 | } |
264 | | |
265 | 0 | return NULL; |
266 | 0 | } |
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 */ |