#include <pthread.h>
#include <urcu/compiler.h>
#include <urcu/tls-compat.h>
+#include <urcu/system.h>
#include <ust-fd.h>
#include <helper.h>
#define IS_FD_VALID(fd) ((fd) >= 0 && (fd) < lttng_ust_max_fd)
#define GET_FD_SET_FOR_FD(fd, fd_sets) (&((fd_sets)[(fd) / FD_SETSIZE]))
#define CALC_INDEX_TO_SET(fd) ((fd) % FD_SETSIZE)
+#define IS_FD_STD(fd) (IS_FD_VALID(fd) && (fd) <= STDERR_FILENO)
/* Check fd validity before calling these. */
#define ADD_FD_TO_SET(fd, fd_sets) \
static fd_set *lttng_fd_set;
static int lttng_ust_max_fd;
static int num_fd_sets;
+static int init_done;
/*
* Force a read (imply TLS fixup for dlopen) of TLS variables.
struct rlimit rlim;
int i;
+ if (CMM_LOAD_SHARED(init_done))
+ return;
+
memset(&rlim, 0, sizeof(rlim));
/* Get the current possible max number of fd for this process. */
if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
abort();
for (i = 0; i < num_fd_sets; i++)
FD_ZERO((<tng_fd_set[i]));
+ CMM_STORE_SHARED(init_done, 1);
}
void lttng_ust_lock_fd_tracker(void)
URCU_TLS(thread_fd_tracking) = 0;
}
+static int dup_std_fd(int fd)
+{
+ int ret, i;
+ int fd_to_close[STDERR_FILENO + 1];
+ int fd_to_close_count = 0;
+ int dup_cmd = F_DUPFD; /* Default command */
+ int fd_valid = -1;
+
+ if (!(IS_FD_STD(fd))) {
+ /* Should not be here */
+ ret = -1;
+ goto error;
+ }
+
+ /* Check for FD_CLOEXEC flag */
+ ret = fcntl(fd, F_GETFD);
+ if (ret < 0) {
+ PERROR("fcntl on f_getfd");
+ ret = -1;
+ goto error;
+ }
+
+ if (ret & FD_CLOEXEC) {
+ dup_cmd = F_DUPFD_CLOEXEC;
+ }
+
+ /* Perform dup */
+ for (i = 0; i < STDERR_FILENO + 1; i++) {
+ ret = fcntl(fd, dup_cmd, 0);
+ if (ret < 0) {
+ PERROR("fcntl dup fd");
+ goto error;
+ }
+
+ if (!(IS_FD_STD(ret))) {
+ /* fd is outside of STD range, use it. */
+ fd_valid = ret;
+ /* Close fd received as argument. */
+ fd_to_close[i] = fd;
+ fd_to_close_count++;
+ break;
+ }
+
+ fd_to_close[i] = ret;
+ fd_to_close_count++;
+ }
+
+ /* Close intermediary fds */
+ for (i = 0; i < fd_to_close_count; i++) {
+ ret = close(fd_to_close[i]);
+ if (ret) {
+ PERROR("close on temporary fd: %d.", fd_to_close[i]);
+ /*
+ * Not using an abort here would yield a complicated
+ * error handling for the caller. If a failure occurs
+ * here, the system is already in a bad state.
+ */
+ abort();
+ }
+ }
+
+ ret = fd_valid;
+error:
+ return ret;
+}
+
/*
* Needs to be called with ust_safe_guard_fd_mutex held when opening the fd.
* Has strict checking of fd validity.
+ *
+ * If fd <= 2, dup the fd until fd > 2. This enables us to bypass
+ * problems that can be encountered if UST uses stdin, stdout, stderr
+ * fds for internal use (daemon etc.). This can happen if the
+ * application closes either of those file descriptors. Intermediary fds
+ * are closed as needed.
+ *
+ * Return -1 on error.
+ *
*/
-void lttng_ust_add_fd_to_tracker(int fd)
+int lttng_ust_add_fd_to_tracker(int fd)
{
+ int ret;
+ /*
+ * Ensure the tracker is initialized when called from
+ * constructors.
+ */
+ lttng_ust_init_fd_tracker();
assert(URCU_TLS(thread_fd_tracking));
+
+ if (IS_FD_STD(fd)) {
+ ret = dup_std_fd(fd);
+ if (ret < 0) {
+ goto error;
+ }
+ fd = ret;
+ }
+
/* Trying to add an fd which we can not accommodate. */
assert(IS_FD_VALID(fd));
/* Setting an fd thats already set. */
assert(!IS_FD_SET(fd, lttng_fd_set));
ADD_FD_TO_SET(fd, lttng_fd_set);
+ return fd;
+error:
+ return ret;
}
/*
*/
void lttng_ust_delete_fd_from_tracker(int fd)
{
+ /*
+ * Ensure the tracker is initialized when called from
+ * constructors.
+ */
+ lttng_ust_init_fd_tracker();
+
assert(URCU_TLS(thread_fd_tracking));
/* Not a valid fd. */
assert(IS_FD_VALID(fd));
lttng_ust_fixup_fd_tracker_tls();
+ /*
+ * Ensure the tracker is initialized when called from
+ * constructors.
+ */
+ lttng_ust_init_fd_tracker();
+
/*
* If called from lttng-ust, we directly call close without
* validating whether the FD is part of the tracked set.
return ret;
}
+/*
+ * Interface allowing applications to close arbitrary streams.
+ * We check if it is owned by lttng-ust, and return -1, errno=EBADF
+ * instead of closing it if it is the case.
+ */
+int lttng_ust_safe_fclose_stream(FILE *stream, int (*fclose_cb)(FILE *stream))
+{
+ int ret = 0, fd;
+
+ lttng_ust_fixup_fd_tracker_tls();
+
+ /*
+ * Ensure the tracker is initialized when called from
+ * constructors.
+ */
+ lttng_ust_init_fd_tracker();
+
+ /*
+ * If called from lttng-ust, we directly call fclose without
+ * validating whether the FD is part of the tracked set.
+ */
+ if (URCU_TLS(thread_fd_tracking))
+ return fclose_cb(stream);
+
+ fd = fileno(stream);
+
+ lttng_ust_lock_fd_tracker();
+ if (IS_FD_VALID(fd) && IS_FD_SET(fd, lttng_fd_set)) {
+ ret = -1;
+ errno = EBADF;
+ } else {
+ ret = fclose_cb(stream);
+ }
+ lttng_ust_unlock_fd_tracker();
+
+ return ret;
+}
+
#ifdef __OpenBSD__
static void set_close_success(int *p)
{
lttng_ust_fixup_fd_tracker_tls();
+ /*
+ * Ensure the tracker is initialized when called from
+ * constructors.
+ */
+ lttng_ust_init_fd_tracker();
+
if (lowfd < 0) {
/*
* NetBSD return EBADF if fd is invalid.