Different bits needed for alternation and consumed
[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
41 /*
42  *      Explicitly cleanup the memory allocated to the error buffer,
43  *      just in case valgrind complains about it.
44  */
45 static void _fr_logging_free(void *arg)
46 {
47         free(arg);
48 }
49
50 /** Log to thread local error buffer
51  *
52  * @param fmt printf style format string. If NULL sets the 'new' byte to false,
53  *        effectively clearing the last message.
54  */
55 void fr_strerror_printf(char const *fmt, ...)
56 {
57         va_list ap;
58
59         char *buffer;
60
61         buffer = fr_thread_local_init(fr_strerror_buffer, _fr_logging_free);
62         if (!buffer) {
63                 int ret;
64
65                 /*
66                  *      malloc is thread safe, talloc is not
67                  */
68                 buffer = calloc((FR_STRERROR_BUFSIZE * 2) + 1, sizeof(char));   /* One byte extra for status */
69                 if (!buffer) {
70                         fr_perror("Failed allocating memory for libradius error buffer");
71                         return;
72                 }
73
74                 ret = fr_thread_local_set(fr_strerror_buffer, buffer);
75                 if (ret != 0) {
76                         fr_perror("Failed setting up TLS for libradius error buffer: %s", fr_syserror(ret));
77                         free(buffer);
78                         return;
79                 }
80         }
81
82         /*
83          *      NULL has a special meaning, setting the new bit to false.
84          */
85         if (!fmt) {
86                 buffer[FR_STRERROR_BUFSIZE * 2] &= 0x06;
87                 return;
88         }
89
90         va_start(ap, fmt);
91         /*
92          *      Alternate where we write the message, so we can do:
93          *      fr_strerror_printf("Additional error: %s", fr_strerror());
94          */
95         switch (buffer[FR_STRERROR_BUFSIZE * 2] & 0x06) {
96         default:
97                 vsnprintf(buffer + FR_STRERROR_BUFSIZE, FR_STRERROR_BUFSIZE, fmt, ap);
98                 buffer[FR_STRERROR_BUFSIZE * 2] = 0x05;                 /* Flip the 'new' bit to true */
99                 break;
100
101         case 0x04:
102                 vsnprintf(buffer, FR_STRERROR_BUFSIZE, fmt, ap);
103                 buffer[FR_STRERROR_BUFSIZE * 2] = 0x03;                 /* Flip the 'new' bit to true */
104                 break;
105         }
106         va_end(ap);
107 }
108
109 /** Get the last library error
110  *
111  * Will only return the last library error once, after which it will return a zero length string.
112  *
113  * @return library error or zero length string
114  */
115 char const *fr_strerror(void)
116 {
117         char *buffer;
118
119         buffer = fr_thread_local_get(fr_strerror_buffer);
120         if (!buffer) return "";
121
122         switch (buffer[FR_STRERROR_BUFSIZE * 2]) {
123         default:
124                 return "";
125
126         case 0x03:
127                 buffer[FR_STRERROR_BUFSIZE * 2] &= 0x06;                /* Flip the 'new' bit to false */
128                 return buffer;
129
130         case 0x05:
131                 buffer[FR_STRERROR_BUFSIZE * 2] &= 0x06;                /* Flip the 'new' bit to false */
132                 return buffer + FR_STRERROR_BUFSIZE;
133         }
134 }
135
136 /** Guaranteed to be thread-safe version of strerror
137  *
138  * @param num errno as returned by function or from global errno.
139  * @return local specific error string relating to errno.
140  */
141 char const *fr_syserror(int num)
142 {
143         char *buffer;
144         int ret;
145
146         buffer = fr_thread_local_init(fr_syserror_buffer, _fr_logging_free);
147         if (!buffer) {
148                 /*
149                  *      malloc is thread safe, talloc is not
150                  */
151                 buffer = malloc(sizeof(char) * FR_STRERROR_BUFSIZE);
152                 if (!buffer) {
153                         fr_perror("Failed allocating memory for system error buffer");
154                         return NULL;
155                 }
156
157                 ret = fr_thread_local_set(fr_syserror_buffer, buffer);
158                 if (ret != 0) {
159                         fr_perror("Failed setting up TLS for system error buffer: %s", fr_syserror(ret));
160                         free(buffer);
161                         return NULL;
162                 }
163         }
164
165         if (!num) {
166                 return "No error";
167         }
168
169         /*
170          *      XSI-Compliant version
171          */
172 #if !defined(HAVE_FEATURES_H) || !defined(__GLIBC__) || ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 500) && ! _GNU_SOURCE)
173         if ((ret = strerror_r(num, buffer, (size_t)FR_STRERROR_BUFSIZE) != 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_STRERROR_BUFSIZE, ret, strerror(ret));
177 #  endif
178                 buffer[0] = '\0';
179         }
180         return buffer;
181         /*
182          *      GNU Specific version
183          *
184          *      The GNU Specific version returns a char pointer. That pointer may point
185          *      the buffer you just passed in, or to an immutable static string.
186          */
187 #else
188         {
189                 char const *p;
190                 p = strerror_r(num, buffer, (size_t)FR_STRERROR_BUFSIZE);
191                 if (!p) {
192 #  ifndef NDEBUG
193                         fprintf(stderr, "strerror_r() failed to write error for errno %i to buffer %p "
194                                 "(%zu bytes): %s\n", num, buffer, (size_t) FR_STRERROR_BUFSIZE, strerror(errno));
195 #  endif
196                         buffer[0] = '\0';
197                         return buffer;
198                 }
199                 return p;
200         }
201 #endif
202
203 }
204
205 void fr_perror(char const *fmt, ...)
206 {
207         char const *error;
208         va_list ap;
209
210         va_start(ap, fmt);
211         vfprintf(stderr, fmt, ap);
212
213         error = fr_strerror();
214         if (error && (error[0] != '\0')) {
215                 fprintf(stderr, ": %s\n", error);
216         } else {
217                 fputs("\n", stderr);
218         }
219
220         va_end(ap);
221 }