Merge tag 'release_3_0_12' into branch moonshot-fr-3.0.12-upgrade.
[freeradius.git] / src / lib / log.c
1 /*
2  * log.c        Functions in the library call radlib_log() which
3  *              does internal logging.
4  *
5  * Version:     $Id$
6  *
7  *   This library is free software; you can redistribute it and/or
8  *   modify it under the terms of the GNU Lesser General Public
9  *   License as published by the Free Software Foundation; either
10  *   version 2.1 of the License, or (at your option) any later version.
11  *
12  *   This library is distributed in the hope that it will be useful,
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  *   Lesser General Public License for more details.
16  *
17  *   You should have received a copy of the GNU Lesser General Public
18  *   License along with this library; if not, write to the Free Software
19  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  *
21  * Copyright 2000,2006  The FreeRADIUS server project
22  */
23
24 RCSID("$Id$")
25
26 #include <freeradius-devel/libradius.h>
27
28 /*
29  *      Are we using glibc or a close relative?
30  */
31 #ifdef HAVE_FEATURES_H
32 #  include <features.h>
33 #endif
34
35 #define FR_STRERROR_BUFSIZE (2048)
36
37 fr_thread_local_setup(char *, fr_strerror_buffer)       /* macro */
38 fr_thread_local_setup(char *, fr_syserror_buffer)       /* macro */
39
40 #ifndef NDEBUG
41 /** POSIX-2008 errno macros
42  *
43  * Non-POSIX macros may be added, but you must check they're defined.
44  */
45 char const *fr_errno_macro_names[] = {
46         [E2BIG] = "E2BIG",
47         [EACCES] = "EACCES",
48         [EADDRINUSE] = "EADDRINUSE",
49         [EADDRNOTAVAIL] = "EADDRNOTAVAIL",
50         [EAFNOSUPPORT] = "EAFNOSUPPORT",
51 #if EWOULDBLOCK == EAGAIN
52         [EWOULDBLOCK] = "EWOULDBLOCK or EAGAIN",
53 #else
54         [EAGAIN] = "EAGAIN",
55         [EWOULDBLOCK] = "EWOULDBLOCK",
56 #endif
57         [EALREADY] = "EALREADY",
58         [EBADF] = "EBADF",
59         [EBADMSG] = "EBADMSG",
60         [EBUSY] = "EBUSY",
61         [ECANCELED] = "ECANCELED",
62         [ECHILD] = "ECHILD",
63         [ECONNABORTED] = "ECONNABORTED",
64         [ECONNREFUSED] = "ECONNREFUSED",
65         [ECONNRESET] = "ECONNRESET",
66         [EDEADLK] = "EDEADLK",
67         [EDESTADDRREQ] = "EDESTADDRREQ",
68         [EDOM] = "EDOM",
69         [EDQUOT] = "EDQUOT",
70         [EEXIST] = "EEXIST",
71         [EFAULT] = "EFAULT",
72         [EFBIG] = "EFBIG",
73         [EHOSTUNREACH] = "EHOSTUNREACH",
74         [EIDRM] = "EIDRM",
75         [EILSEQ] = "EILSEQ",
76         [EINPROGRESS] = "EINPROGRESS",
77         [EINTR] = "EINTR",
78         [EINVAL] = "EINVAL",
79         [EIO] = "EIO",
80         [EISCONN] = "EISCONN",
81         [EISDIR] = "EISDIR",
82         [ELOOP] = "ELOOP",
83         [EMFILE] = "EMFILE",
84         [EMLINK] = "EMLINK",
85         [EMSGSIZE] = "EMSGSIZE",
86         [EMULTIHOP] = "EMULTIHOP",
87         [ENAMETOOLONG] = "ENAMETOOLONG",
88         [ENETDOWN] = "ENETDOWN",
89         [ENETRESET] = "ENETRESET",
90         [ENETUNREACH] = "ENETUNREACH",
91         [ENFILE] = "ENFILE",
92         [ENOBUFS] = "ENOBUFS",
93 #ifdef ENODATA
94         [ENODATA] = "ENODATA",
95 #endif
96         [ENODEV] = "ENODEV",
97         [ENOENT] = "ENOENT",
98         [ENOEXEC] = "ENOEXEC",
99         [ENOLCK] = "ENOLCK",
100         [ENOLINK] = "ENOLINK",
101         [ENOMEM] = "ENOMEM",
102         [ENOMSG] = "ENOMSG",
103         [ENOPROTOOPT] = "ENOPROTOOPT",
104         [ENOSPC] = "ENOSPC",
105 #ifdef ENOSR
106         [ENOSR] = "ENOSR",
107 #endif
108 #ifdef ENOSTR
109         [ENOSTR] = "ENOSTR",
110 #endif
111         [ENOSYS] = "ENOSYS",
112         [ENOTCONN] = "ENOTCONN",
113         [ENOTDIR] = "ENOTDIR",
114         [ENOTEMPTY] = "ENOTEMPTY",
115 #ifdef ENOTRECOVERABLE
116         [ENOTRECOVERABLE] = "ENOTRECOVERABLE",
117 #endif
118         [ENOTSOCK] = "ENOTSOCK",
119         [ENOTSUP] = "ENOTSUP",
120 #if ENOTSUP != EOPNOTSUPP
121         [EOPNOTSUPP] = "EOPNOTSUPP",
122 #endif
123         [ENOTTY] = "ENOTTY",
124         [ENXIO] = "ENXIO",
125         [EOVERFLOW] = "EOVERFLOW",
126 #ifdef EOWNERDEAD
127         [EOWNERDEAD] = "EOWNERDEAD",
128 #endif
129         [EPERM] = "EPERM",
130         [EPIPE] = "EPIPE",
131         [EPROTO] = "EPROTO",
132         [EPROTONOSUPPORT] = "EPROTONOSUPPORT",
133         [EPROTOTYPE] = "EPROTOTYPE",
134         [ERANGE] = "ERANGE",
135         [EROFS] = "EROFS",
136         [ESPIPE] = "ESPIPE",
137         [ESRCH] = "ESRCH",
138         [ESTALE] = "ESTALE",
139 #ifdef ETIME
140         [ETIME] = "ETIME",
141 #endif
142         [ETIMEDOUT] = "ETIMEDOUT",
143         [ETXTBSY] = "ETXTBSY",
144         [EXDEV] = "EXDEV"
145 };
146 #endif
147
148 /*
149  *      Explicitly cleanup the memory allocated to the error buffer,
150  *      just in case valgrind complains about it.
151  */
152 static void _fr_logging_free(void *arg)
153 {
154         free(arg);
155 }
156
157 /** Log to thread local error buffer
158  *
159  * @param fmt printf style format string. If NULL sets the 'new' byte to false,
160  *        effectively clearing the last message.
161  */
162 void fr_strerror_printf(char const *fmt, ...)
163 {
164         va_list ap;
165
166         char *buffer;
167
168         buffer = fr_thread_local_init(fr_strerror_buffer, _fr_logging_free);
169         if (!buffer) {
170                 int ret;
171
172                 /*
173                  *      malloc is thread safe, talloc is not
174                  */
175                 buffer = calloc((FR_STRERROR_BUFSIZE * 2) + 1, sizeof(char));   /* One byte extra for status */
176                 if (!buffer) {
177                         fr_perror("Failed allocating memory for libradius error buffer");
178                         return;
179                 }
180
181                 ret = fr_thread_local_set(fr_strerror_buffer, buffer);
182                 if (ret != 0) {
183                         fr_perror("Failed setting up TLS for libradius error buffer: %s", fr_syserror(ret));
184                         free(buffer);
185                         return;
186                 }
187         }
188
189         /*
190          *      NULL has a special meaning, setting the new bit to false.
191          */
192         if (!fmt) {
193                 buffer[FR_STRERROR_BUFSIZE * 2] &= 0x06;
194                 return;
195         }
196
197         va_start(ap, fmt);
198         /*
199          *      Alternate where we write the message, so we can do:
200          *      fr_strerror_printf("Additional error: %s", fr_strerror());
201          */
202         switch (buffer[FR_STRERROR_BUFSIZE * 2] & 0x06) {
203         default:
204                 vsnprintf(buffer + FR_STRERROR_BUFSIZE, FR_STRERROR_BUFSIZE, fmt, ap);
205                 buffer[FR_STRERROR_BUFSIZE * 2] = 0x05;                 /* Flip the 'new' bit to true */
206                 break;
207
208         case 0x04:
209                 vsnprintf(buffer, FR_STRERROR_BUFSIZE, fmt, ap);
210                 buffer[FR_STRERROR_BUFSIZE * 2] = 0x03;                 /* Flip the 'new' bit to true */
211                 break;
212         }
213         va_end(ap);
214 }
215
216 /** Get the last library error
217  *
218  * Will only return the last library error once, after which it will return a zero length string.
219  *
220  * @return library error or zero length string
221  */
222 char const *fr_strerror(void)
223 {
224         char *buffer;
225
226         buffer = fr_thread_local_get(fr_strerror_buffer);
227         if (!buffer) return "";
228
229         switch (buffer[FR_STRERROR_BUFSIZE * 2]) {
230         default:
231                 return "";
232
233         case 0x03:
234                 buffer[FR_STRERROR_BUFSIZE * 2] &= 0x06;                /* Flip the 'new' bit to false */
235                 return buffer;
236
237         case 0x05:
238                 buffer[FR_STRERROR_BUFSIZE * 2] &= 0x06;                /* Flip the 'new' bit to false */
239                 return buffer + FR_STRERROR_BUFSIZE;
240         }
241 }
242
243 /** Guaranteed to be thread-safe version of strerror
244  *
245  * @param num errno as returned by function or from global errno.
246  * @return local specific error string relating to errno.
247  */
248 char const *fr_syserror(int num)
249 {
250         char *buffer, *p, *end;
251         int ret;
252
253         buffer = fr_thread_local_init(fr_syserror_buffer, _fr_logging_free);
254         if (!buffer) {
255                 /*
256                  *      malloc is thread safe, talloc is not
257                  */
258                 buffer = malloc(sizeof(char) * FR_STRERROR_BUFSIZE);
259                 if (!buffer) {
260                         fr_perror("Failed allocating memory for system error buffer");
261                         return NULL;
262                 }
263
264                 ret = fr_thread_local_set(fr_syserror_buffer, buffer);
265                 if (ret != 0) {
266                         fr_perror("Failed setting up TLS for system error buffer: %s", fr_syserror(ret));
267                         free(buffer);
268                         return NULL;
269                 }
270         }
271
272         if (!num) return "No error";
273
274         p = buffer;
275         end = p + FR_STRERROR_BUFSIZE;
276
277 #ifndef NDEBUG
278         /*
279          *      Prefix system errors with the macro name and number
280          *      if we're debugging.
281          */
282         if (num < (int)(sizeof(fr_errno_macro_names) / sizeof(*fr_errno_macro_names))) {
283                 p += snprintf(p, end - p, "%s: ", fr_errno_macro_names[num]);
284         } else {
285                 p += snprintf(p, end - p, "errno %i: ", num);
286         }
287         if (p >= end) return p;
288 #endif
289
290         /*
291          *      XSI-Compliant version
292          */
293 #if !defined(HAVE_FEATURES_H) || !defined(__GLIBC__) || ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 500) && ! _GNU_SOURCE)
294         ret = strerror_r(num, p, end - p);
295         if (ret != 0) {
296 #  ifndef NDEBUG
297                 fprintf(stderr, "strerror_r() failed to write error for errno %i to buffer %p (%zu bytes), "
298                         "returned %i: %s\n", num, buffer, (size_t) FR_STRERROR_BUFSIZE, ret, strerror(ret));
299 #  endif
300                 buffer[0] = '\0';
301         }
302         return buffer;
303         /*
304          *      GNU Specific version
305          *
306          *      The GNU Specific version returns a char pointer. That pointer may point
307          *      the buffer you just passed in, or to an immutable static string.
308          */
309 #else
310         {
311                 p = strerror_r(num, p, end - p);
312                 if (!p) {
313 #  ifndef NDEBUG
314                         fprintf(stderr, "strerror_r() failed to write error for errno %i to buffer %p "
315                                 "(%zu bytes): %s\n", num, buffer, (size_t) FR_STRERROR_BUFSIZE, strerror(errno));
316 #  endif
317                         buffer[0] = '\0';
318                         return buffer;
319                 }
320
321                 return p;
322         }
323 #endif
324
325 }
326
327 void fr_perror(char const *fmt, ...)
328 {
329         char const *error;
330         va_list ap;
331
332         va_start(ap, fmt);
333         vfprintf(stderr, fmt, ap);
334
335         error = fr_strerror();
336         if (error && (error[0] != '\0')) {
337                 fprintf(stderr, ": %s\n", error);
338         } else {
339                 fputs("\n", stderr);
340         }
341
342         va_end(ap);
343 }