* Yes, this really is the only way of getting the errno
* from the dlopen API.
*/
- if (!strstr(dlerror_txt, fr_syserror(ENOENT))) {
+ if (strstr(dlerror_txt, fr_syserror_simple(ENOENT)) != NULL) {
#ifndef __linux__
int access_mode = R_OK | X_OK;
-#ifdef AT_ACCESS
+# ifdef AT_ACCESS
access_mode |= AT_ACCESS;
-#endif
+# endif
if (access(path, access_mode) < 0 && errno == ENOENT) continue;
#endif
- fr_strerror_printf("Access check failed: %s", dlerror_txt);
+ fr_strerror_printf_push("Access check failed: %s", dlerror_txt);
break;
}
+
+ /*
+ * We're reliant on dlerror_txt from the loop,
+ * because once dlerror() is called the error
+ * is cleared.
+ *
+ * We need to push every error because we
+ * don't know which one would be useful
+ * in diagnosing the underlying cause of the
+ * load failure.
+ */
+ fr_strerror_printf_push("%s", dlerror_txt ? dlerror_txt : "unknown dlopen error");
}
/*
*/
if (!handle) {
talloc_free(ctx);
- /*
- * We're reliant on dlerror_txt from the loop,
- * because once dlerror() is called the error
- * is cleared.
- */
- fr_strerror_printf("%s", dlerror_txt ? dlerror_txt : "unknown dlopen error");
return NULL;
}
+ fr_strerror_clear(); /* Don't leave spurious errors in the buffer */
+
talloc_free(ctx);
} else {
char buffer[2048];
/*
* Append the error
*/
- fr_strerror_printf("%s", error);
+ fr_strerror_printf_push("%s", error);
return NULL;
}
}
[EXDEV] = "EXDEV"
};
-/** Guaranteed to be thread-safe version of strerror
- *
- * @param num errno as returned by function or from global errno.
- * @return local specific error string relating to errno.
- *
- * @hidecallergraph
- */
-char const *fr_syserror(int num)
+static inline CC_HINT(always_inline)
+ssize_t _fr_syserror(int num, char *buffer, size_t buff_len)
{
- char *buffer, *p, *end;
-
- buffer = fr_syserror_buffer;
- if (!buffer) {
- /*
- * Try and produce something useful,
- * even if the thread is exiting.
- */
- if (logging_stop) {
- if (HAVE_DEFINITION(num)) return fr_syserror_macro_names[num];
- return "";
- }
-
- buffer = talloc_array(NULL, char, FR_SYSERROR_BUFSIZE);
- if (!buffer) {
- fr_perror("Failed allocating memory for system error buffer");
- return NULL;
- }
- fr_atexit_thread_local(fr_syserror_buffer, _fr_logging_free, buffer);
- }
-
- if (num == 0) return "No additional error information";
-
- p = buffer;
- end = p + FR_SYSERROR_BUFSIZE;
-
- /*
- * Prefix system errors with the macro name and number
- * if we're debugging.
- */
- if (HAVE_DEFINITION(num)) {
- p += snprintf(p, end - p, "%s: ", fr_syserror_macro_names[num]);
- } else {
- p += snprintf(p, end - p, "errno %i: ", num);
- }
- if (p >= end) return p;
-
/*
* XSI-Compliant version
*/
{
int ret;
- ret = strerror_r(num, p, end - p);
+ ret = strerror_r(num, buffer, buff_len);
if (ret != 0) {
# ifndef NDEBUG
fprintf(stderr, "strerror_r() failed to write error for errno %i to buffer %p (%zu bytes), "
"returned %i: %s\n", num, buffer, (size_t)FR_SYSERROR_BUFSIZE, ret, strerror(ret));
# endif
buffer[0] = '\0';
+ return -1;
}
}
- return buffer;
+ return strlen(buffer);
+#else
/*
* GNU Specific version
*
* The GNU Specific version returns a char pointer. That pointer may point
* the buffer you just passed in, or to an immutable static string.
*/
-#else
{
char *q;
- q = strerror_r(num, p, end - p);
+ q = strerror_r(num, buffer, buff_len);
if (!q) {
# ifndef NDEBUG
fprintf(stderr, "strerror_r() failed to write error for errno %i to buffer %p "
"(%zu bytes): %s\n", num, buffer, (size_t)FR_SYSERROR_BUFSIZE, strerror(errno));
# endif
buffer[0] = '\0';
- return buffer;
+ return -1;
}
/*
* If strerror_r used a static string, copy it to the buffer
*/
- if (q != p) {
+ if (q != buffer) {
size_t len;
len = strlen(q) + 1;
- if (len >= (size_t)(end - p)) len = end - p;
-
- strlcpy(p, q, len);
+ if (len >= buff_len) len = buff_len; /* Truncate */
+ return strlcpy(buffer, q, len);
}
- return buffer;
+ return strlen(q);
}
#endif
}
+
+static inline CC_HINT(always_inline)
+char *_fr_syserror_buffer(void)
+{
+ char *buffer;
+
+ buffer = fr_syserror_buffer;
+ if (!buffer) {
+ buffer = talloc_array(NULL, char, FR_SYSERROR_BUFSIZE);
+ if (!buffer) {
+ fr_perror("Failed allocating memory for system error buffer");
+ return NULL;
+ }
+ fr_atexit_thread_local(fr_syserror_buffer, _fr_logging_free, buffer);
+ }
+ return buffer;
+}
+
+/** Guaranteed to be thread-safe version of strerror
+ *
+ * @param num errno as returned by function or from global errno.
+ * @return Error string relating to errno, with the macro name added as a prefix.
+ *
+ * @hidecallergraph
+ */
+char const *fr_syserror(int num)
+{
+ char *buffer, *p, *end;
+
+ /*
+ * Try and produce something useful,
+ * even if the thread is exiting.
+ */
+ if (logging_stop) {
+ error:
+ if (HAVE_DEFINITION(num)) return fr_syserror_macro_names[num];
+ return "";
+ }
+
+ if (num == 0) return "No additional error information";
+
+ /*
+ * Grab our thread local buffer
+ */
+ buffer = _fr_syserror_buffer();
+
+ p = buffer;
+ end = p + FR_SYSERROR_BUFSIZE;
+
+ /*
+ * Prefix system errors with the macro name and number
+ * if we're debugging.
+ */
+ if (HAVE_DEFINITION(num)) {
+ p += snprintf(p, end - p, "%s: ", fr_syserror_macro_names[num]);
+ } else {
+ p += snprintf(p, end - p, "errno %i: ", num);
+ }
+ if (p >= end) return p;
+
+ if (_fr_syserror(num, p, end - p) < 0) goto error;
+
+ return buffer;
+}
+
+/** Guaranteed to be thread-safe version of strerror
+ *
+ * @param num errno as returned by function or from global errno.
+ * @return Error string relating to errno with no decoration.
+ *
+ * @hidecallergraph
+ */
+char const *fr_syserror_simple(int num)
+{
+ char *buffer;
+
+ if (logging_stop) return "";
+
+ /*
+ * Grab our thread local buffer
+ */
+ buffer = _fr_syserror_buffer();
+ if (_fr_syserror(num, buffer, FR_SYSERROR_BUFSIZE) < 0) return "Failed retrieving error";
+
+ return buffer;
+}