The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
syserror.c
Go to the documentation of this file.
1/*
2 * This library is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2.1 of the License, or (at your option) any later version.
6 *
7 * This library is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with this library; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/** Support functions to allow libraries to get system errors in a threadsafe and easily debuggable way
18 *
19 * @file src/lib/util/syserror.c
20 *
21 * @copyright 2017 The FreeRADIUS server project
22 * @copyright 2017 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23 */
24RCSID("$Id: 253b4d36a0657a0ba72bb31dea7fd462a637d718 $")
25
26#include <freeradius-devel/util/log.h>
27#include <freeradius-devel/util/strerror.h>
28#include <freeradius-devel/util/syserror.h>
29#include <freeradius-devel/util/atexit.h>
30
31
32#define FR_SYSERROR_BUFSIZE (2048)
33
34static _Thread_local char *fr_syserror_buffer;
35static _Thread_local bool logging_stop; //!< Due to ordering issues we may get errors being
36 ///< logged from within other thread local destructors
37 ///< which cause a crash on exit if the logging buffer
38 ///< has already been freed.
39
40#define HAVE_DEFINITION(_errno) ((_errno) < (int)(NUM_ELEMENTS(fr_syserror_macro_names)))
41
42/*
43 * Explicitly cleanup the memory allocated to the error buffer,
44 * just in case valgrind complains about it.
45 */
46static int _fr_logging_free(UNUSED void *arg)
47{
48 if (talloc_free(fr_syserror_buffer) < 0) return -1;
49 fr_syserror_buffer = NULL;
50 logging_stop = true;
51 return 0;
52}
53
54/** POSIX-2008 errno macros
55 *
56 * Non-POSIX macros may be added, but you must check they're defined.
57 */
58static char const *fr_syserror_macro_names[] = {
59 [E2BIG] = "E2BIG",
60 [EACCES] = "EACCES",
61 [EADDRINUSE] = "EADDRINUSE",
62 [EADDRNOTAVAIL] = "EADDRNOTAVAIL",
63 [EAFNOSUPPORT] = "EAFNOSUPPORT",
64#if EWOULDBLOCK == EAGAIN
65 [EWOULDBLOCK] = "EWOULDBLOCK or EAGAIN",
66#else
67 [EAGAIN] = "EAGAIN",
68 [EWOULDBLOCK] = "EWOULDBLOCK",
69#endif
70 [EALREADY] = "EALREADY",
71 [EBADF] = "EBADF",
72 [EBADMSG] = "EBADMSG",
73 [EBUSY] = "EBUSY",
74 [ECANCELED] = "ECANCELED",
75 [ECHILD] = "ECHILD",
76 [ECONNABORTED] = "ECONNABORTED",
77 [ECONNREFUSED] = "ECONNREFUSED",
78 [ECONNRESET] = "ECONNRESET",
79 [EDEADLK] = "EDEADLK",
80 [EDESTADDRREQ] = "EDESTADDRREQ",
81 [EDOM] = "EDOM",
82 [EDQUOT] = "EDQUOT",
83 [EEXIST] = "EEXIST",
84 [EFAULT] = "EFAULT",
85 [EFBIG] = "EFBIG",
86 [EHOSTUNREACH] = "EHOSTUNREACH",
87 [EIDRM] = "EIDRM",
88 [EILSEQ] = "EILSEQ",
89 [EINPROGRESS] = "EINPROGRESS",
90 [EINTR] = "EINTR",
91 [EINVAL] = "EINVAL",
92 [EIO] = "EIO",
93 [EISCONN] = "EISCONN",
94 [EISDIR] = "EISDIR",
95 [ELOOP] = "ELOOP",
96 [EMFILE] = "EMFILE",
97 [EMLINK] = "EMLINK",
98 [EMSGSIZE] = "EMSGSIZE",
99 [EMULTIHOP] = "EMULTIHOP",
100 [ENAMETOOLONG] = "ENAMETOOLONG",
101 [ENETDOWN] = "ENETDOWN",
102 [ENETRESET] = "ENETRESET",
103 [ENETUNREACH] = "ENETUNREACH",
104 [ENFILE] = "ENFILE",
105 [ENOBUFS] = "ENOBUFS",
106#ifdef ENODATA
107 [ENODATA] = "ENODATA",
108#endif
109 [ENODEV] = "ENODEV",
110 [ENOENT] = "ENOENT",
111 [ENOEXEC] = "ENOEXEC",
112 [ENOLCK] = "ENOLCK",
113 [ENOLINK] = "ENOLINK",
114 [ENOMEM] = "ENOMEM",
115 [ENOMSG] = "ENOMSG",
116 [ENOPROTOOPT] = "ENOPROTOOPT",
117 [ENOSPC] = "ENOSPC",
118#ifdef ENOSR
119 [ENOSR] = "ENOSR",
120#endif
121#ifdef ENOSTR
122 [ENOSTR] = "ENOSTR",
123#endif
124 [ENOSYS] = "ENOSYS",
125 [ENOTCONN] = "ENOTCONN",
126 [ENOTDIR] = "ENOTDIR",
127 [ENOTEMPTY] = "ENOTEMPTY",
128#ifdef ENOTRECOVERABLE
129 [ENOTRECOVERABLE] = "ENOTRECOVERABLE",
130#endif
131 [ENOTSOCK] = "ENOTSOCK",
132#if ENOTSUP == EOPNOTSUPP
133 [ENOTSUP] = "ENOTSUP or EOPNOTSUPP",
134#else
135 [ENOTSUP] = "ENOTSUP",
136 [EOPNOTSUPP] = "EOPNOTSUPP",
137#endif
138 [ENOTTY] = "ENOTTY",
139 [ENXIO] = "ENXIO",
140 [EOVERFLOW] = "EOVERFLOW",
141#ifdef EOWNERDEAD
142 [EOWNERDEAD] = "EOWNERDEAD",
143#endif
144 [EPERM] = "EPERM",
145 [EPIPE] = "EPIPE",
146 [EPROTO] = "EPROTO",
147 [EPROTONOSUPPORT] = "EPROTONOSUPPORT",
148 [EPROTOTYPE] = "EPROTOTYPE",
149 [ERANGE] = "ERANGE",
150 [EROFS] = "EROFS",
151 [ESPIPE] = "ESPIPE",
152 [ESRCH] = "ESRCH",
153 [ESTALE] = "ESTALE",
154#ifdef ETIME
155 [ETIME] = "ETIME",
156#endif
157 [ETIMEDOUT] = "ETIMEDOUT",
158 [ETXTBSY] = "ETXTBSY",
159 [EXDEV] = "EXDEV"
160};
161
162static inline CC_HINT(always_inline)
163ssize_t _fr_syserror(int num, char *buffer, size_t buff_len)
164{
165 /*
166 * XSI-Compliant version
167 */
168#if !defined(HAVE_FEATURES_H) || !defined(__GLIBC__) || ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 500) && ! _GNU_SOURCE)
169 {
170 int ret;
171
172 ret = strerror_r(num, buffer, buff_len);
173 if (ret != 0) {
174# ifndef NDEBUG
175 fprintf(stderr, "strerror_r() failed to write error for errno %i to buffer %p (%zu bytes), "
176 "returned %i: %s\n", num, buffer, (size_t)FR_SYSERROR_BUFSIZE, ret, strerror(ret));
177# endif
178 buffer[0] = '\0';
179 return -1;
180 }
181 }
182 return strlen(buffer);
183#else
184 /*
185 * GNU Specific version
186 *
187 * The GNU Specific version returns a char pointer. That pointer may point
188 * the buffer you just passed in, or to an immutable static string.
189 */
190 {
191 char *q;
192
193 q = strerror_r(num, buffer, buff_len);
194 if (!q) {
195# ifndef NDEBUG
196 fprintf(stderr, "strerror_r() failed to write error for errno %i to buffer %p "
197 "(%zu bytes): %s\n", num, buffer, (size_t)FR_SYSERROR_BUFSIZE, strerror(errno));
198# endif
199 buffer[0] = '\0';
200 return -1;
201 }
202
203 /*
204 * If strerror_r used a static string, copy it to the buffer
205 */
206 if (q != buffer) {
207 size_t len;
208
209 len = strlen(q) + 1;
210 if (len >= buff_len) len = buff_len; /* Truncate */
211 return strlcpy(buffer, q, len);
212 }
213
214 return strlen(q);
215 }
216#endif
217}
218
219static inline CC_HINT(always_inline)
221{
222 char *buffer;
223
225 if (!buffer) {
226 buffer = talloc_array(NULL, char, FR_SYSERROR_BUFSIZE);
227 if (!buffer) {
228 fr_perror("Failed allocating memory for system error buffer");
229 return NULL;
230 }
232 }
233 return buffer;
234}
235
236/** Guaranteed to be thread-safe version of strerror
237 *
238 * @param num errno as returned by function or from global errno.
239 * @return Error string relating to errno, with the macro name added as a prefix.
240 *
241 * @hidecallergraph
242 */
243char const *fr_syserror(int num)
244{
245 char *buffer, *p, *end;
246
247 /*
248 * Try and produce something useful,
249 * even if the thread is exiting.
250 */
251 if (logging_stop) {
252 error:
253 if (HAVE_DEFINITION(num)) return fr_syserror_macro_names[num];
254 return "";
255 }
256
257 if (num == 0) return "No additional error information";
258
259 /*
260 * Grab our thread local buffer
261 */
263 if (!buffer) goto error;
264
265 p = buffer;
266 end = p + FR_SYSERROR_BUFSIZE;
267
268 /*
269 * Prefix system errors with the macro name and number
270 * if we're debugging.
271 */
272 if (HAVE_DEFINITION(num)) {
273 p += snprintf(p, end - p, "%s: ", fr_syserror_macro_names[num]);
274 } else {
275 p += snprintf(p, end - p, "errno %i: ", num);
276 }
277 if (p >= end) return p;
278
279 if (_fr_syserror(num, p, end - p) < 0) goto error;
280
281 return buffer;
282}
283
284/** Guaranteed to be thread-safe version of strerror
285 *
286 * @param num errno as returned by function or from global errno.
287 * @return Error string relating to errno with no decoration.
288 *
289 * @hidecallergraph
290 */
291char const *fr_syserror_simple(int num)
292{
293 char *buffer;
294
295 if (logging_stop) return "";
296
297 /*
298 * Grab our thread local buffer
299 */
301 if (!buffer || (_fr_syserror(num, buffer, FR_SYSERROR_BUFSIZE) < 0)) return "Failed retrieving error";
302
303 return buffer;
304}
static int const char char buffer[256]
Definition acutest.h:576
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition atexit.h:221
#define RCSID(id)
Definition build.h:483
#define UNUSED
Definition build.h:315
talloc_free(reap)
long int ssize_t
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition snprintf.c:689
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition strlcpy.c:34
static int _fr_logging_free(UNUSED void *arg)
Definition syserror.c:46
#define FR_SYSERROR_BUFSIZE
Definition syserror.c:32
static _Thread_local bool logging_stop
Due to ordering issues we may get errors being logged from within other thread local destructors whic...
Definition syserror.c:35
#define HAVE_DEFINITION(_errno)
Definition syserror.c:40
char const * fr_syserror_simple(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:291
static ssize_t _fr_syserror(int num, char *buffer, size_t buff_len)
Definition syserror.c:163
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition syserror.c:243
static _Thread_local char * fr_syserror_buffer
Definition syserror.c:34
static char const * fr_syserror_macro_names[]
POSIX-2008 errno macros.
Definition syserror.c:58
static char * _fr_syserror_buffer(void)
Definition syserror.c:220
void fr_perror(char const *fmt,...)
Print the current error to stderr with a prefix.
Definition strerror.c:733