gsasl  1.10.0
About: GNU SASL is an implementation of the Simple Authentication and Security Layer (SASL). Development version.
  Fossies Dox: gsasl-1.10.0.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

strerror_r.c
Go to the documentation of this file.
1 /* strerror_r.c --- POSIX compatible system error routine
2 
3  Copyright (C) 2010-2021 Free Software Foundation, Inc.
4 
5  This program is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 3 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program. If not, see <https://www.gnu.org/licenses/>. */
17 
18 /* Written by Bruno Haible <bruno@clisp.org>, 2010. */
19 
20 #include <config.h>
21 
22 /* Enable declaration of sys_nerr and sys_errlist in <errno.h> on NetBSD. */
23 #define _NETBSD_SOURCE 1
24 
25 /* Specification. */
26 #include <string.h>
27 
28 #include <errno.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #if !HAVE_SNPRINTF
32 # include <stdarg.h>
33 #endif
34 
35 #include "strerror-override.h"
36 
37 #if (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__) && HAVE___XPG_STRERROR_R /* glibc >= 2.3.4, cygwin >= 1.7.9 */
38 
39 # define USE_XPG_STRERROR_R 1
40 extern
41 #ifdef __cplusplus
42 "C"
43 #endif
44 int __xpg_strerror_r (int errnum, char *buf, size_t buflen);
45 
46 #elif HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__)
47 
48 /* The system's strerror_r function is OK, except that its third argument
49  is 'int', not 'size_t', or its return type is wrong. */
50 
51 # include <limits.h>
52 
53 # define USE_SYSTEM_STRERROR_R 1
54 
55 #else /* (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) */
56 
57 /* Use the system's strerror(). Exclude glibc and cygwin because the
58  system strerror_r has the wrong return type, and cygwin 1.7.9
59  strerror_r clobbers strerror. */
60 # undef strerror
61 
62 # define USE_SYSTEM_STRERROR 1
63 
64 # if defined __NetBSD__ || defined __hpux || (defined _WIN32 && !defined __CYGWIN__) || defined __sgi || (defined __sun && !defined _LP64) || defined __CYGWIN__
65 
66 /* No locking needed. */
67 
68 /* Get catgets internationalization functions. */
69 # if HAVE_CATGETS
70 # include <nl_types.h>
71 # endif
72 
73 #ifdef __cplusplus
74 extern "C" {
75 #endif
76 
77 /* Get sys_nerr, sys_errlist on HP-UX (otherwise only declared in C++ mode).
78  Get sys_nerr, sys_errlist on IRIX (otherwise only declared with _SGIAPI). */
79 # if defined __hpux || defined __sgi
80 extern int sys_nerr;
81 extern char *sys_errlist[];
82 # endif
83 
84 /* Get sys_nerr on Solaris. */
85 # if defined __sun && !defined _LP64
86 extern int sys_nerr;
87 # endif
88 
89 #ifdef __cplusplus
90 }
91 #endif
92 
93 # else
94 
95 # include "glthread/lock.h"
96 
97 /* This lock protects the buffer returned by strerror(). We assume that
98  no other uses of strerror() exist in the program. */
99 gl_lock_define_initialized(static, strerror_lock)
100 
101 # endif
102 
103 #endif
104 
105 /* On MSVC, there is no snprintf() function, just a _snprintf().
106  It is of lower quality, but sufficient for the simple use here.
107  We only have to make sure to NUL terminate the result (_snprintf
108  does not NUL terminate, like strncpy). */
109 #if !HAVE_SNPRINTF
110 static int
111 local_snprintf (char *buf, size_t buflen, const char *format, ...)
112 {
113  va_list args;
114  int result;
115 
116  va_start (args, format);
117  result = _vsnprintf (buf, buflen, format, args);
118  va_end (args);
119  if (buflen > 0 && (result < 0 || result >= buflen))
120  buf[buflen - 1] = '\0';
121  return result;
122 }
123 # undef snprintf
124 # define snprintf local_snprintf
125 #endif
126 
127 /* Copy as much of MSG into BUF as possible, without corrupting errno.
128  Return 0 if MSG fit in BUFLEN, otherwise return ERANGE. */
129 static int
130 safe_copy (char *buf, size_t buflen, const char *msg)
131 {
132  size_t len = strlen (msg);
133  size_t moved = len < buflen ? len : buflen - 1;
134 
135  /* Although POSIX lets memmove corrupt errno, we don't
136  know of any implementation where this is a real problem. */
137  memmove (buf, msg, moved);
138  buf[moved] = '\0';
139  return len < buflen ? 0 : ERANGE;
140 }
141 
142 
143 int
144 strerror_r (int errnum, char *buf, size_t buflen)
145 #undef strerror_r
146 {
147  /* Filter this out now, so that rest of this replacement knows that
148  there is room for a non-empty message and trailing NUL. */
149  if (buflen <= 1)
150  {
151  if (buflen)
152  *buf = '\0';
153  return ERANGE;
154  }
155  *buf = '\0';
156 
157  /* Check for gnulib overrides. */
158  {
159  char const *msg = strerror_override (errnum);
160 
161  if (msg)
162  return safe_copy (buf, buflen, msg);
163  }
164 
165  {
166  int ret;
167  int saved_errno = errno;
168 
169 #if USE_XPG_STRERROR_R
170 
171  {
172  ret = __xpg_strerror_r (errnum, buf, buflen);
173  if (ret < 0)
174  ret = errno;
175  if (!*buf)
176  {
177  /* glibc 2.13 would not touch buf on err, so we have to fall
178  back to GNU strerror_r which always returns a thread-safe
179  untruncated string to (partially) copy into our buf. */
180  safe_copy (buf, buflen, strerror_r (errnum, buf, buflen));
181  }
182  }
183 
184 #elif USE_SYSTEM_STRERROR_R
185 
186  if (buflen > INT_MAX)
187  buflen = INT_MAX;
188 
189 # ifdef __hpux
190  /* On HP-UX 11.31, strerror_r always fails when buflen < 80; it
191  also fails to change buf on EINVAL. */
192  {
193  char stackbuf[80];
194 
195  if (buflen < sizeof stackbuf)
196  {
197  ret = strerror_r (errnum, stackbuf, sizeof stackbuf);
198  if (ret == 0)
199  ret = safe_copy (buf, buflen, stackbuf);
200  }
201  else
202  ret = strerror_r (errnum, buf, buflen);
203  }
204 # else
205  ret = strerror_r (errnum, buf, buflen);
206 
207  /* Some old implementations may return (-1, EINVAL) instead of EINVAL.
208  But on Haiku, valid error numbers are negative. */
209 # if !defined __HAIKU__
210  if (ret < 0)
211  ret = errno;
212 # endif
213 # endif
214 
215 # if defined _AIX || defined __HAIKU__
216  /* AIX and Haiku return 0 rather than ERANGE when truncating strings; try
217  again until we are sure we got the entire string. */
218  if (!ret && strlen (buf) == buflen - 1)
219  {
220  char stackbuf[STACKBUF_LEN];
221  size_t len;
222  strerror_r (errnum, stackbuf, sizeof stackbuf);
223  len = strlen (stackbuf);
224  /* STACKBUF_LEN should have been large enough. */
225  if (len + 1 == sizeof stackbuf)
226  abort ();
227  if (buflen <= len)
228  ret = ERANGE;
229  }
230 # else
231  /* Solaris 10 does not populate buf on ERANGE. OpenBSD 4.7
232  truncates early on ERANGE rather than return a partial integer.
233  We prefer the maximal string. We set buf[0] earlier, and we
234  know of no implementation that modifies buf to be an
235  unterminated string, so this strlen should be portable in
236  practice (rather than pulling in a safer strnlen). */
237  if (ret == ERANGE && strlen (buf) < buflen - 1)
238  {
239  char stackbuf[STACKBUF_LEN];
240 
241  /* STACKBUF_LEN should have been large enough. */
242  if (strerror_r (errnum, stackbuf, sizeof stackbuf) == ERANGE)
243  abort ();
244  safe_copy (buf, buflen, stackbuf);
245  }
246 # endif
247 
248 #else /* USE_SYSTEM_STRERROR */
249 
250  /* Try to do what strerror (errnum) does, but without clobbering the
251  buffer used by strerror(). */
252 
253 # if defined __NetBSD__ || defined __hpux || (defined _WIN32 && !defined __CYGWIN__) || defined __CYGWIN__ /* NetBSD, HP-UX, native Windows, Cygwin */
254 
255  /* NetBSD: sys_nerr, sys_errlist are declared through _NETBSD_SOURCE
256  and <errno.h> above.
257  HP-UX: sys_nerr, sys_errlist are declared explicitly above.
258  native Windows: sys_nerr, sys_errlist are declared in <stdlib.h>.
259  Cygwin: sys_nerr, sys_errlist are declared in <errno.h>. */
260  if (errnum >= 0 && errnum < sys_nerr)
261  {
262 # if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
263 # if defined __NetBSD__
264  nl_catd catd = catopen ("libc", NL_CAT_LOCALE);
265  const char *errmsg =
266  (catd != (nl_catd)-1
267  ? catgets (catd, 1, errnum, sys_errlist[errnum])
268  : sys_errlist[errnum]);
269 # endif
270 # if defined __hpux
271  nl_catd catd = catopen ("perror", NL_CAT_LOCALE);
272  const char *errmsg =
273  (catd != (nl_catd)-1
274  ? catgets (catd, 1, 1 + errnum, sys_errlist[errnum])
275  : sys_errlist[errnum]);
276 # endif
277 # else
278  const char *errmsg = sys_errlist[errnum];
279 # endif
280  if (errmsg == NULL || *errmsg == '\0')
281  ret = EINVAL;
282  else
283  ret = safe_copy (buf, buflen, errmsg);
284 # if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux)
285  if (catd != (nl_catd)-1)
286  catclose (catd);
287 # endif
288  }
289  else
290  ret = EINVAL;
291 
292 # elif defined __sgi || (defined __sun && !defined _LP64) /* IRIX, Solaris <= 9 32-bit */
293 
294  /* For a valid error number, the system's strerror() function returns
295  a pointer to a not copied string, not to a buffer. */
296  if (errnum >= 0 && errnum < sys_nerr)
297  {
298  char *errmsg = strerror (errnum);
299 
300  if (errmsg == NULL || *errmsg == '\0')
301  ret = EINVAL;
302  else
303  ret = safe_copy (buf, buflen, errmsg);
304  }
305  else
306  ret = EINVAL;
307 
308 # else
309 
310  gl_lock_lock (strerror_lock);
311 
312  {
313  char *errmsg = strerror (errnum);
314 
315  /* For invalid error numbers, strerror() on
316  - IRIX 6.5 returns NULL,
317  - HP-UX 11 returns an empty string. */
318  if (errmsg == NULL || *errmsg == '\0')
319  ret = EINVAL;
320  else
321  ret = safe_copy (buf, buflen, errmsg);
322  }
323 
324  gl_lock_unlock (strerror_lock);
325 
326 # endif
327 
328 #endif
329 
330 #if defined _WIN32 && !defined __CYGWIN__
331  /* MSVC 14 defines names for many error codes in the range 100..140,
332  but _sys_errlist contains strings only for the error codes
333  < _sys_nerr = 43. */
334  if (ret == EINVAL)
335  {
336  const char *errmsg;
337 
338  switch (errnum)
339  {
340  case 100 /* EADDRINUSE */:
341  errmsg = "Address already in use";
342  break;
343  case 101 /* EADDRNOTAVAIL */:
344  errmsg = "Cannot assign requested address";
345  break;
346  case 102 /* EAFNOSUPPORT */:
347  errmsg = "Address family not supported by protocol";
348  break;
349  case 103 /* EALREADY */:
350  errmsg = "Operation already in progress";
351  break;
352  case 105 /* ECANCELED */:
353  errmsg = "Operation canceled";
354  break;
355  case 106 /* ECONNABORTED */:
356  errmsg = "Software caused connection abort";
357  break;
358  case 107 /* ECONNREFUSED */:
359  errmsg = "Connection refused";
360  break;
361  case 108 /* ECONNRESET */:
362  errmsg = "Connection reset by peer";
363  break;
364  case 109 /* EDESTADDRREQ */:
365  errmsg = "Destination address required";
366  break;
367  case 110 /* EHOSTUNREACH */:
368  errmsg = "No route to host";
369  break;
370  case 112 /* EINPROGRESS */:
371  errmsg = "Operation now in progress";
372  break;
373  case 113 /* EISCONN */:
374  errmsg = "Transport endpoint is already connected";
375  break;
376  case 114 /* ELOOP */:
377  errmsg = "Too many levels of symbolic links";
378  break;
379  case 115 /* EMSGSIZE */:
380  errmsg = "Message too long";
381  break;
382  case 116 /* ENETDOWN */:
383  errmsg = "Network is down";
384  break;
385  case 117 /* ENETRESET */:
386  errmsg = "Network dropped connection on reset";
387  break;
388  case 118 /* ENETUNREACH */:
389  errmsg = "Network is unreachable";
390  break;
391  case 119 /* ENOBUFS */:
392  errmsg = "No buffer space available";
393  break;
394  case 123 /* ENOPROTOOPT */:
395  errmsg = "Protocol not available";
396  break;
397  case 126 /* ENOTCONN */:
398  errmsg = "Transport endpoint is not connected";
399  break;
400  case 128 /* ENOTSOCK */:
401  errmsg = "Socket operation on non-socket";
402  break;
403  case 129 /* ENOTSUP */:
404  errmsg = "Not supported";
405  break;
406  case 130 /* EOPNOTSUPP */:
407  errmsg = "Operation not supported";
408  break;
409  case 132 /* EOVERFLOW */:
410  errmsg = "Value too large for defined data type";
411  break;
412  case 133 /* EOWNERDEAD */:
413  errmsg = "Owner died";
414  break;
415  case 134 /* EPROTO */:
416  errmsg = "Protocol error";
417  break;
418  case 135 /* EPROTONOSUPPORT */:
419  errmsg = "Protocol not supported";
420  break;
421  case 136 /* EPROTOTYPE */:
422  errmsg = "Protocol wrong type for socket";
423  break;
424  case 138 /* ETIMEDOUT */:
425  errmsg = "Connection timed out";
426  break;
427  case 140 /* EWOULDBLOCK */:
428  errmsg = "Operation would block";
429  break;
430  default:
431  errmsg = NULL;
432  break;
433  }
434  if (errmsg != NULL)
435  ret = safe_copy (buf, buflen, errmsg);
436  }
437 #endif
438 
439  if (ret == EINVAL && !*buf)
440  {
441 #if defined __HAIKU__
442  /* For consistency with perror(). */
443  snprintf (buf, buflen, "Unknown Application Error (%d)", errnum);
444 #else
445  snprintf (buf, buflen, "Unknown error %d", errnum);
446 #endif
447  }
448 
449  errno = saved_errno;
450  return ret;
451  }
452 }
const char * msg
Definition: gai_strerror.c:53
#define NULL
Definition: stddef.in.h:72
#define strerror_override(ignored)
#define STACKBUF_LEN
char * strerror(int n)
Definition: strerror.c:36
#define gl_lock_lock(NAME)
Definition: lock.h:686
#define gl_lock_unlock(NAME)
Definition: lock.h:693
#define gl_lock_define_initialized(STORAGECLASS, NAME)
Definition: lock.h:636
char buf[4]
Definition: mbrtowc-impl.h:39
int snprintf(char *str, size_t size, const char *format,...)
Definition: snprintf.c:37
int strerror_r(int errnum, char *buf, size_t buflen)
Definition: strerror_r.c:144
static int safe_copy(char *buf, size_t buflen, const char *msg)
Definition: strerror_r.c:130