aboutsummaryrefslogtreecommitdiffstats
path: root/tools/libxl/libxl_fork.c
diff options
context:
space:
mode:
authorIan Jackson <ian.jackson@eu.citrix.com>2012-04-11 14:14:18 +0100
committerIan Jackson <ian.jackson@eu.citrix.com>2012-04-11 14:14:18 +0100
commitda31ffa712dbbe9540a7b9c2b7bee4d3315c1cd1 (patch)
treed9134fca3659e8a649f433d03f8aacc620101675 /tools/libxl/libxl_fork.c
parent88d3e27049298a53d9f9f5c8600d2799dc88ef7b (diff)
downloadxen-da31ffa712dbbe9540a7b9c2b7bee4d3315c1cd1.tar.gz
xen-da31ffa712dbbe9540a7b9c2b7bee4d3315c1cd1.tar.bz2
xen-da31ffa712dbbe9540a7b9c2b7bee4d3315c1cd1.zip
libxl: Protect fds with CLOEXEC even with forking threads
We introduce a new "carefd" concept, which relates to fds that we care about not being inherited by long-lived children. As yet we do not use this anywhere in libxl. Until all locations in libxl which make such fds are converted, libxl__postfork may not work entirely properly. If these locations do not use O_CLOEXEC (or use calls for which there is no O_CLOEXEC) then multithreaded programs may not work properly. This introduces a new API call libxl_postfork_child_noexec which must be called by applications which make long-running non-execing children. Add the appropriate call to xl's postfork function. Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com> Acked-by: Ian Campbell <ian.campbell@citrix.com> Committed-by: Ian Jackson <Ian.Jackson@eu.citrix.com>
Diffstat (limited to 'tools/libxl/libxl_fork.c')
-rw-r--r--tools/libxl/libxl_fork.c149
1 files changed, 149 insertions, 0 deletions
diff --git a/tools/libxl/libxl_fork.c b/tools/libxl/libxl_fork.c
new file mode 100644
index 0000000000..dce88ad54b
--- /dev/null
+++ b/tools/libxl/libxl_fork.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2012 Citrix Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; version 2.1 only. with the special
+ * exception on linking described in file LICENSE.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ */
+/*
+ * Internal child process machinery for use by other parts of libxl
+ */
+
+#include "libxl_osdeps.h" /* must come before any other headers */
+
+#include "libxl_internal.h"
+
+/*
+ * carefd arrangements
+ *
+ * carefd_begin and _unlock take out the no_forking lock, which we
+ * also take and release in our pthread_atfork handlers. So when this
+ * lock is held the whole process cannot fork. We therefore protect
+ * our fds from leaking into children made by other threads.
+ *
+ * We maintain a list of all the carefds, so that if the application
+ * wants to fork a long-running but non-execing child, we can close
+ * them all.
+ *
+ * So the record function sets CLOEXEC for the benefit of execing
+ * children, and makes a note of the fd for the benefit of non-execing
+ * ones.
+ */
+
+struct libxl__carefd {
+ LIBXL_LIST_ENTRY(libxl__carefd) entry;
+ int fd;
+};
+
+static pthread_mutex_t no_forking = PTHREAD_MUTEX_INITIALIZER;
+static int atfork_registered;
+static LIBXL_LIST_HEAD(, libxl__carefd) carefds =
+ LIBXL_LIST_HEAD_INITIALIZER(carefds);
+
+static void atfork_lock(void)
+{
+ int r = pthread_mutex_lock(&no_forking);
+ assert(!r);
+}
+
+static void atfork_unlock(void)
+{
+ int r = pthread_mutex_unlock(&no_forking);
+ assert(!r);
+}
+
+int libxl__atfork_init(libxl_ctx *ctx)
+{
+ int r, rc;
+
+ atfork_lock();
+ if (atfork_registered) { rc = 0; goto out; }
+
+ r = pthread_atfork(atfork_lock, atfork_unlock, atfork_unlock);
+ if (r) {
+ assert(r == ENOMEM);
+ libxl__alloc_failed(ctx, __func__, 0,0);
+ }
+
+ atfork_registered = 1;
+ rc = 0;
+ out:
+ atfork_unlock();
+ return rc;
+}
+
+void libxl__carefd_begin(void) { atfork_lock(); }
+void libxl__carefd_unlock(void) { atfork_unlock(); }
+
+libxl__carefd *libxl__carefd_record(libxl_ctx *ctx, int fd)
+{
+ libxl__carefd *cf = 0;
+
+ libxl_fd_set_cloexec(ctx, fd, 1);
+ cf = libxl__zalloc(NULL, sizeof(*cf));
+ cf->fd = fd;
+ LIBXL_LIST_INSERT_HEAD(&carefds, cf, entry);
+ return cf;
+}
+
+libxl__carefd *libxl__carefd_opened(libxl_ctx *ctx, int fd)
+{
+ libxl__carefd *cf = 0;
+
+ cf = libxl__carefd_record(ctx, fd);
+ libxl__carefd_unlock();
+ return cf;
+}
+
+void libxl_postfork_child_noexec(libxl_ctx *ctx)
+{
+ libxl__carefd *cf, *cf_tmp;
+ int r;
+
+ atfork_lock();
+ LIBXL_LIST_FOREACH_SAFE(cf, &carefds, entry, cf_tmp) {
+ if (cf->fd >= 0) {
+ r = close(cf->fd);
+ if (r)
+ LIBXL__LOG_ERRNO(ctx, LIBXL__LOG_WARNING,
+ "failed to close fd=%d"
+ " (perhaps of another libxl ctx)", cf->fd);
+ }
+ free(cf);
+ }
+ LIBXL_LIST_INIT(&carefds);
+ atfork_unlock();
+}
+
+int libxl__carefd_close(libxl__carefd *cf)
+{
+ if (!cf) return 0;
+ int r = cf->fd < 0 ? 0 : close(cf->fd);
+ int esave = errno;
+ atfork_lock();
+ LIBXL_LIST_REMOVE(cf, entry);
+ atfork_unlock();
+ free(cf);
+ errno = esave;
+ return r;
+}
+
+int libxl__carefd_fd(const libxl__carefd *cf)
+{
+ if (!cf) return -1;
+ return cf->fd;
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ */