All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
log.c
Go to the documentation of this file.
1 /*
2  * log.c Functions in the library call radlib_log() which
3  * does internal logging.
4  *
5  * Version: $Id: 48bf1efe588476e41c3c6a006876809e428c7ca3 $
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: 48bf1efe588476e41c3c6a006876809e428c7ca3 $")
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  [ENODATA] = "ENODATA",
94  [ENODEV] = "ENODEV",
95  [ENOENT] = "ENOENT",
96  [ENOEXEC] = "ENOEXEC",
97  [ENOLCK] = "ENOLCK",
98  [ENOLINK] = "ENOLINK",
99  [ENOMEM] = "ENOMEM",
100  [ENOMSG] = "ENOMSG",
101  [ENOPROTOOPT] = "ENOPROTOOPT",
102  [ENOSPC] = "ENOSPC",
103  [ENOSR] = "ENOSR",
104  [ENOSTR] = "ENOSTR",
105  [ENOSYS] = "ENOSYS",
106  [ENOTCONN] = "ENOTCONN",
107  [ENOTDIR] = "ENOTDIR",
108  [ENOTEMPTY] = "ENOTEMPTY",
109  [ENOTRECOVERABLE] = "ENOTRECOVERABLE",
110  [ENOTSOCK] = "ENOTSOCK",
111 #if ENOTSUP == EOPNOTSUPP
112  [ENOTSUP] = "ENOTSUP or EOPNOTSUPP",
113 #else
114  [ENOTSUP] = "ENOTSUP",
115  [EOPNOTSUPP] = "EOPNOTSUPP",
116 #endif
117  [ENOTTY] = "ENOTTY",
118  [ENXIO] = "ENXIO",
119  [EOVERFLOW] = "EOVERFLOW",
120  [EOWNERDEAD] = "EOWNERDEAD",
121  [EPERM] = "EPERM",
122  [EPIPE] = "EPIPE",
123  [EPROTO] = "EPROTO",
124  [EPROTONOSUPPORT] = "EPROTONOSUPPORT",
125  [EPROTOTYPE] = "EPROTOTYPE",
126  [ERANGE] = "ERANGE",
127  [EROFS] = "EROFS",
128  [ESPIPE] = "ESPIPE",
129  [ESRCH] = "ESRCH",
130  [ESTALE] = "ESTALE",
131  [ETIME] = "ETIME",
132  [ETIMEDOUT] = "ETIMEDOUT",
133  [ETXTBSY] = "ETXTBSY",
134  [EXDEV] = "EXDEV"
135 };
136 #endif
137 
138 /*
139  * Explicitly cleanup the memory allocated to the error buffer,
140  * just in case valgrind complains about it.
141  */
142 static void _fr_logging_free(void *arg)
143 {
144  free(arg);
145 }
146 
147 /** Log to thread local error buffer
148  *
149  * @param fmt printf style format string. If NULL sets the 'new' byte to false,
150  * effectively clearing the last message.
151  */
152 void fr_strerror_printf(char const *fmt, ...)
153 {
154  va_list ap;
155 
156  char *buffer;
157 
158  buffer = fr_thread_local_init(fr_strerror_buffer, _fr_logging_free);
159  if (!buffer) {
160  int ret;
161 
162  /*
163  * malloc is thread safe, talloc is not
164  */
165  buffer = calloc((FR_STRERROR_BUFSIZE * 2) + 1, sizeof(char)); /* One byte extra for status */
166  if (!buffer) {
167  fr_perror("Failed allocating memory for libradius error buffer");
168  return;
169  }
170 
171  ret = fr_thread_local_set(fr_strerror_buffer, buffer);
172  if (ret != 0) {
173  fr_perror("Failed setting up TLS for libradius error buffer: %s", fr_syserror(ret));
174  free(buffer);
175  return;
176  }
177  }
178 
179  /*
180  * NULL has a special meaning, setting the new bit to false.
181  */
182  if (!fmt) {
183  buffer[FR_STRERROR_BUFSIZE * 2] &= 0x06;
184  return;
185  }
186 
187  va_start(ap, fmt);
188  /*
189  * Alternate where we write the message, so we can do:
190  * fr_strerror_printf("Additional error: %s", fr_strerror());
191  */
192  switch (buffer[FR_STRERROR_BUFSIZE * 2] & 0x06) {
193  default:
195  buffer[FR_STRERROR_BUFSIZE * 2] = 0x05; /* Flip the 'new' bit to true */
196  break;
197 
198  case 0x04:
199  vsnprintf(buffer, FR_STRERROR_BUFSIZE, fmt, ap);
200  buffer[FR_STRERROR_BUFSIZE * 2] = 0x03; /* Flip the 'new' bit to true */
201  break;
202  }
203  va_end(ap);
204 }
205 
206 /** Get the last library error
207  *
208  * Will only return the last library error once, after which it will return a zero length string.
209  *
210  * @return library error or zero length string.
211  */
212 char const *fr_strerror(void)
213 {
214  char *buffer;
215 
216  buffer = fr_thread_local_get(fr_strerror_buffer);
217  if (!buffer) return "";
218 
219  switch (buffer[FR_STRERROR_BUFSIZE * 2]) {
220  default:
221  return "";
222 
223  case 0x03:
224  buffer[FR_STRERROR_BUFSIZE * 2] &= 0x06; /* Flip the 'new' bit to false */
225  return buffer;
226 
227  case 0x05:
228  buffer[FR_STRERROR_BUFSIZE * 2] &= 0x06; /* Flip the 'new' bit to false */
229  return buffer + FR_STRERROR_BUFSIZE;
230  }
231 }
232 
233 /** Guaranteed to be thread-safe version of strerror
234  *
235  * @param num errno as returned by function or from global errno.
236  * @return local specific error string relating to errno.
237  */
238 char const *fr_syserror(int num)
239 {
240  char *buffer, *p, *end;
241  int ret;
242 
243  buffer = fr_thread_local_init(fr_syserror_buffer, _fr_logging_free);
244  if (!buffer) {
245  /*
246  * malloc is thread safe, talloc is not
247  */
248  buffer = malloc(sizeof(char) * FR_STRERROR_BUFSIZE);
249  if (!buffer) {
250  fr_perror("Failed allocating memory for system error buffer");
251  return NULL;
252  }
253 
254  ret = fr_thread_local_set(fr_syserror_buffer, buffer);
255  if (ret != 0) {
256  fr_perror("Failed setting up TLS for system error buffer: %s", fr_syserror(ret));
257  free(buffer);
258  return NULL;
259  }
260  }
261 
262  if (!num) return "No error";
263 
264  p = buffer;
265  end = p + FR_STRERROR_BUFSIZE;
266 
267 #ifndef NDEBUG
268  /*
269  * Prefix system errors with the macro name and number
270  * if we're debugging.
271  */
272  if (num < (int)(sizeof(fr_errno_macro_names) / sizeof(*fr_errno_macro_names))) {
273  p += snprintf(p, end - p, "%s: ", fr_errno_macro_names[num]);
274  } else {
275  p += snprintf(p, end - p, "errno %i: ", num);
276  }
277  if (p >= end) return p;
278 #endif
279 
280  /*
281  * XSI-Compliant version
282  */
283 #if !defined(HAVE_FEATURES_H) || !defined(__GLIBC__) || ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 500) && ! _GNU_SOURCE)
284  ret = strerror_r(num, p, end - p);
285  if (ret != 0) {
286 # ifndef NDEBUG
287  fprintf(stderr, "strerror_r() failed to write error for errno %i to buffer %p (%zu bytes), "
288  "returned %i: %s\n", num, buffer, (size_t)FR_STRERROR_BUFSIZE, ret, strerror(ret));
289 # endif
290  buffer[0] = '\0';
291  }
292  return buffer;
293  /*
294  * GNU Specific version
295  *
296  * The GNU Specific version returns a char pointer. That pointer may point
297  * the buffer you just passed in, or to an immutable static string.
298  */
299 #else
300  {
301  p = strerror_r(num, p, end - p);
302  if (!p) {
303 # ifndef NDEBUG
304  fprintf(stderr, "strerror_r() failed to write error for errno %i to buffer %p "
305  "(%zu bytes): %s\n", num, buffer, (size_t)FR_STRERROR_BUFSIZE, strerror(errno));
306 # endif
307  buffer[0] = '\0';
308  return buffer;
309  }
310 
311  return p;
312  }
313 #endif
314 
315 }
316 
317 void fr_perror(char const *fmt, ...)
318 {
319  char const *error;
320  va_list ap;
321 
322  va_start(ap, fmt);
323  vfprintf(stderr, fmt, ap);
324 
325  error = fr_strerror();
326  if (error && (error[0] != '\0')) {
327  fprintf(stderr, ": %s\n", error);
328  } else {
329  fputs("\n", stderr);
330  }
331 
332  va_end(ap);
333 }
334 
335 /** Canonicalize error strings, removing tabs, and generate spaces for error marker
336  *
337  * @note talloc_free must be called on the buffer returned in spaces and text
338  *
339  * Used to produce error messages such as this:
340  @verbatim
341  I'm a string with a parser # error
342  ^ Unexpected character in string
343  @endverbatim
344  *
345  * With code resembling this:
346  @code{.c}
347  ERROR("%s", parsed_str);
348  ERROR("%s^ %s", space, text);
349  @endcode
350  *
351  * @todo merge with above function (radlog_request_marker)
352  *
353  * @param sp Where to write a dynamically allocated buffer of spaces used to indent the error text.
354  * @param text Where to write the canonicalized version of msg (the error text).
355  * @param ctx to allocate the spaces and text buffers in.
356  * @param slen of error marker. Expects negative integer value, as returned by parse functions.
357  * @param msg to canonicalize.
358  */
359 void fr_canonicalize_error(TALLOC_CTX *ctx, char **sp, char **text, ssize_t slen, char const *msg)
360 {
361  size_t offset, skip = 0;
362  char *spbuf, *p;
363  char *value;
364 
365  offset = -slen;
366 
367  /*
368  * Ensure that the error isn't indented
369  * too far.
370  */
371  if (offset > 45) {
372  skip = offset - 40;
373  offset -= skip;
374  value = talloc_strdup(ctx, msg + skip);
375  memcpy(value, "...", 3);
376 
377  } else {
378  value = talloc_strdup(ctx, msg);
379  }
380 
381  spbuf = talloc_array(ctx, char, offset + 1);
382  memset(spbuf, ' ', offset);
383  spbuf[offset] = '\0';
384 
385  /*
386  * Smash tabs to spaces for the input string.
387  */
388  for (p = value; *p != '\0'; p++) {
389  if (*p == '\t') *p = ' ';
390  }
391 
392 
393  /*
394  * Ensure that there isn't too much text after the error.
395  */
396  if (strlen(value) > 100) {
397  memcpy(value + 95, "... ", 5);
398  }
399 
400  *sp = spbuf;
401  *text = value;
402 }
403 
PUBLIC int vsnprintf(char *string, size_t length, char *format, va_list args)
Definition: snprintf.c:503
void fr_canonicalize_error(TALLOC_CTX *ctx, char **sp, char **text, ssize_t slen, char const *msg)
Canonicalize error strings, removing tabs, and generate spaces for error marker.
Definition: log.c:359
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:686
void fr_strerror_printf(char const *fmt,...)
Log to thread local error buffer.
Definition: log.c:152
#define FR_STRERROR_BUFSIZE
Definition: log.c:35
fr_thread_local_setup(char *, fr_strerror_buffer)
POSIX-2008 errno macros.
Definition: log.c:37
static void _fr_logging_free(void *arg)
Definition: log.c:142
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
void fr_perror(char const *fmt,...)
Definition: log.c:317
char const * fr_syserror(int num)
Guaranteed to be thread-safe version of strerror.
Definition: log.c:238
#define RCSID(id)
Definition: build.h:135