#include <unistd.h>
#include <ust-fd.h>
#include <dlfcn.h>
+#include <errno.h>
#include <helper.h>
#include "usterr-signal-safe.h"
-static int (*__lttng_ust_fd_plibc_close)(int fd);
-static int (*__lttng_ust_fd_plibc_fclose)(FILE *stream);
+#define LTTNG_UST_DLSYM_FAILED_PTR 0x1
+static int (*__lttng_ust_fd_plibc_close)(int fd) = NULL;
+static int (*__lttng_ust_fd_plibc_fclose)(FILE *stream) = NULL;
+
+/*
+ * Use dlsym to find the original libc close() symbol and store it in
+ * __lttng_ust_fd_plibc_close.
+ */
static
-int _lttng_ust_fd_libc_close(int fd)
+void *_lttng_ust_fd_init_plibc_close(void)
{
- if (!__lttng_ust_fd_plibc_close) {
+ if (__lttng_ust_fd_plibc_close == NULL) {
__lttng_ust_fd_plibc_close = dlsym(RTLD_NEXT, "close");
- if (!__lttng_ust_fd_plibc_close) {
+
+ if (__lttng_ust_fd_plibc_close == NULL) {
+ __lttng_ust_fd_plibc_close = (void *) LTTNG_UST_DLSYM_FAILED_PTR;
fprintf(stderr, "%s\n", dlerror());
- return -1;
}
}
- return lttng_ust_safe_close_fd(fd, __lttng_ust_fd_plibc_close);
+
+ return __lttng_ust_fd_plibc_close;
}
+/*
+ * Use dlsym to find the original libc fclose() symbol and store it in
+ * __lttng_ust_fd_plibc_fclose.
+ */
static
-int _lttng_ust_fd_libc_fclose(FILE *stream)
+void *_lttng_ust_fd_init_plibc_fclose(void)
{
- if (!__lttng_ust_fd_plibc_fclose) {
+ if (__lttng_ust_fd_plibc_fclose == NULL) {
__lttng_ust_fd_plibc_fclose = dlsym(RTLD_NEXT, "fclose");
- if (!__lttng_ust_fd_plibc_fclose) {
+
+ if (__lttng_ust_fd_plibc_fclose == NULL) {
+ __lttng_ust_fd_plibc_fclose = (void *) LTTNG_UST_DLSYM_FAILED_PTR;
fprintf(stderr, "%s\n", dlerror());
- return -1;
}
}
- return lttng_ust_safe_fclose_stream(stream,
- __lttng_ust_fd_plibc_fclose);
+
+ return __lttng_ust_fd_plibc_fclose;
+}
+
+static
+void _lttng_ust_fd_ctor(void)
+ __attribute__((constructor));
+static
+void _lttng_ust_fd_ctor(void)
+{
+ lttng_ust_common_ctor();
+
+ /*
+ * Initialize the function pointers to the original libc symbols in the
+ * constructor since close() has to stay async-signal-safe and as such,
+ * we can't call dlsym() in the override functions.
+ */
+ (void) _lttng_ust_fd_init_plibc_close();
+ (void) _lttng_ust_fd_init_plibc_fclose();
}
+/*
+ * Override the libc close() symbol with our own, allowing applications to
+ * close arbitrary file descriptors. If the fd is owned by lttng-ust, return
+ * -1, errno=EBADF instead of closing it.
+ *
+ * If dlsym failed to find the original libc close() symbol, return -1,
+ * errno=ENOSYS.
+ *
+ * There is a short window before the library constructor has executed where
+ * this wrapper could call dlsym() and thus not be async-signal-safe.
+ */
int close(int fd)
{
- return _lttng_ust_fd_libc_close(fd);
+ /*
+ * We can't retry dlsym here since close is async-signal-safe.
+ */
+ if (_lttng_ust_fd_init_plibc_close() == (void *) LTTNG_UST_DLSYM_FAILED_PTR) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return lttng_ust_safe_close_fd(fd, __lttng_ust_fd_plibc_close);
}
/*
- * Note: fcloseall() is not an issue because it fcloses only the
- * streams it knows about, which differs from the problems caused by
- * gnulib close_stdout(), which does an explicit fclose(stdout).
+ * Override the libc fclose() symbol with our own, allowing applications to
+ * close arbitrary streams. If the fd is owned by lttng-ust, return -1,
+ * errno=EBADF instead of closing it.
+ *
+ * If dlsym failed to find the original libc close() symbol, return -1,
+ * errno=ENOSYS.
+ *
+ * There is a short window before the library constructor has executed where
+ * this wrapper could call dlsym() and thus not be async-signal-safe.
+ *
+ * Note: fcloseall() is not an issue because it closes only the streams it
+ * knows about, which differs from the problems caused by gnulib
+ * close_stdout(), which does an explicit fclose(stdout).
*/
int fclose(FILE *stream)
{
- return _lttng_ust_fd_libc_fclose(stream);
+ if (_lttng_ust_fd_init_plibc_fclose() == (void *) LTTNG_UST_DLSYM_FAILED_PTR) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return lttng_ust_safe_fclose_stream(stream,
+ __lttng_ust_fd_plibc_fclose);
}
#if defined(__sun__) || defined(__FreeBSD__)
/* Solaris and FreeBSD. */
void closefrom(int lowfd)
{
- (void) lttng_ust_safe_closefrom(lowfd, __lttng_ust_fd_plibc_close);
+ if (_lttng_ust_fd_init_plibc_close() == (void *) LTTNG_UST_DLSYM_FAILED_PTR) {
+ return;
+ }
+
+ (void) lttng_ust_safe_closefrom_fd(lowfd, __lttng_ust_fd_plibc_close);
}
#elif defined(__NetBSD__) || defined(__OpenBSD__)
/* NetBSD and OpenBSD. */
int closefrom(int lowfd)
{
- return lttng_ust_safe_closefrom(lowfd, __lttng_ust_fd_plibc_close);
+ if (_lttng_ust_fd_init_plibc_close() == (void *) LTTNG_UST_DLSYM_FAILED_PTR) {
+ errno = ENOSYS;
+ return -1;
+ }
+
+ return lttng_ust_safe_closefrom_fd(lowfd, __lttng_ust_fd_plibc_close);
}
#else
/* As far as we know, this OS does not implement closefrom. */