aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/libstore/build.cc138
-rw-r--r--src/libutil/util.cc32
-rw-r--r--src/libutil/util.hh8
3 files changed, 162 insertions, 16 deletions
diff --git a/src/libstore/build.cc b/src/libstore/build.cc
index 85933e84f..3c988ea42 100644
--- a/src/libstore/build.cc
+++ b/src/libstore/build.cc
@@ -583,6 +583,84 @@ void deletePathWrapped(const Path & path)
//////////////////////////////////////////////////////////////////////
+#include <sys/mount.h>
+
+
+/* Helper class for automatically unmounting bind-mounts in chroots. */
+struct BindMount
+{
+ Path source, target;
+ Paths created;
+
+ BindMount()
+ {
+ }
+
+ BindMount(const Path & source, const Path & target)
+ {
+ bind(source, target);
+ }
+
+ ~BindMount()
+ {
+ try {
+ unbind();
+ } catch (...) {
+ ignoreException();
+ }
+ }
+
+ void bind(const Path & source, const Path & target)
+ {
+ printMsg(lvlError, format("bind mounting `%1%' to `%2%'") % source % target);
+
+ this->source = source;
+ this->target = target;
+
+ created = createDirs(target);
+
+ if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1)
+ throw SysError(format("bind mount from `%1%' to `%2%' failed") % source % target);
+ }
+
+ void unbind()
+ {
+ if (source == "") return;
+ printMsg(lvlError, format("umount `%1%'") % target);
+
+ /* Urgh. Unmount sometimes doesn't succeed right away because
+ the mount point is still busy. It shouldn't be, because
+ we've killed all the build processes by now (at least when
+ using a build user; see the check in killUser()). But
+ maybe this is because those processes are still zombies and
+ are keeping some kernel structures busy (open files,
+ current directories, etc.). So retry a few times
+ (actually, a 1 second sleep is almost certainly enough for
+ the zombies to be cleaned up). */
+ unsigned int tries = 0;
+ while (umount(target.c_str()) == -1) {
+ if (errno == EBUSY && ++tries < 10) {
+ printMsg(lvlError, format("unmounting `%1%' failed, retrying after 1 second...") % target);
+ sleep(1);
+ }
+ else
+ throw SysError(format("unmounting `%1%' failed") % target);
+ }
+
+ /* Get rid of the directories for the mount point created in
+ bind(). */
+ for (Paths::reverse_iterator i = created.rbegin(); i != created.rend(); ++i) {
+ printMsg(lvlError, format("delete `%1%'") % *i);
+ if (remove(i->c_str()) == -1)
+ throw SysError(format("cannot unlink `%1%'") % *i);
+ }
+ }
+};
+
+
+//////////////////////////////////////////////////////////////////////
+
+
class DerivationGoal : public Goal
{
private:
@@ -623,6 +701,14 @@ private:
Pipe toHook;
Pipe fromHook;
+ /* Whether we're currently doing a chroot build. */
+ bool useChroot;
+
+ /* In chroot builds, the list of bind mounts currently active.
+ The destructor of BindMount will cause the binds to be
+ unmounted. */
+ list<boost::shared_ptr<BindMount> > bindMounts;
+
typedef void (DerivationGoal::*GoalState)();
GoalState state;
@@ -678,7 +764,7 @@ private:
void openLogFile();
/* Common initialisation to be performed in child processes (i.e.,
- both in builders and in build hooks. */
+ both in builders and in build hooks). */
void initChild();
/* Delete the temporary directory, if we have one. */
@@ -711,11 +797,18 @@ DerivationGoal::~DerivationGoal()
/* Careful: we should never ever throw an exception from a
destructor. */
try {
+ printMsg(lvlError, "DESTROY");
killChild();
deleteTmpDir(false);
} catch (...) {
ignoreException();
}
+ try {
+ //sleep(1);
+ bindMounts.clear();
+ } catch (...) {
+ ignoreException();
+ }
}
@@ -1024,6 +1117,9 @@ void DerivationGoal::buildDone()
deleteTmpDir(true);
+ /* In chroot builds, unmount the bind mounts ASAP. */
+ bindMounts.clear(); /* the destructors will do the rest */
+
/* Compute the FS closure of the outputs and register them as
being valid. */
computeClosure();
@@ -1173,7 +1269,7 @@ DerivationGoal::HookReply DerivationGoal::tryBuildHook()
throw SysError(format("executing `%1%'") % buildHook);
} catch (std::exception & e) {
- std::cerr << format("build hook error: %1%\n") % e.what();
+ std::cerr << format("build hook error: %1%") % e.what() << std::endl;
}
quickExit(1);
}
@@ -1544,6 +1640,31 @@ void DerivationGoal::startBuilder()
% buildUser.getGID() % nixStore);
}
+
+ /* Are we doing a chroot build? */
+ useChroot = queryBoolSetting("build-use-chroot", false);
+ Path tmpRootDir;
+
+ if (useChroot) {
+ tmpRootDir = createTempDir();
+
+ printMsg(lvlChatty, format("setting up chroot environment in `%1%'") % tmpRootDir);
+
+ Paths defaultDirs;
+ defaultDirs.push_back("/dev");
+ defaultDirs.push_back("/proc");
+ Paths dirsInChroot = querySetting("build-chroot-dirs", defaultDirs);
+
+ dirsInChroot.push_front(nixStore);
+ dirsInChroot.push_front(tmpDir);
+
+ /* Push BindMounts at the front of the list so that they get
+ unmounted in LIFO order. (!!! Does the C++ standard
+ guarantee that list elements are destroyed in order?) */
+ for (Paths::iterator i = dirsInChroot.begin(); i != dirsInChroot.end(); ++i)
+ bindMounts.push_front(boost::shared_ptr<BindMount>(new BindMount(*i, tmpRootDir + *i)));
+ }
+
/* Run the builder. */
printMsg(lvlChatty, format("executing builder `%1%'") %
@@ -1569,6 +1690,15 @@ void DerivationGoal::startBuilder()
try { /* child */
+ /* If building in a chroot, do the chroot right away.
+ initChild() will do a chdir() to the temporary build
+ directory to make sure the current directory is in the
+ chroot. (Actually the order doesn't matter, since due
+ to the bind mount tmpDir and tmpRootDit/tmpDir are the
+ same directories.) */
+ if (useChroot && chroot(tmpRootDir.c_str()) == -1)
+ throw SysError(format("cannot change root directory to `%1%'") % tmpRootDir);
+
initChild();
/* Fill in the environment. */
@@ -1632,7 +1762,7 @@ void DerivationGoal::startBuilder()
% drv.builder);
} catch (std::exception & e) {
- std::cerr << format("build error: %1%\n") % e.what();
+ std::cerr << format("build error: %1%") % e.what() << std::endl;
}
quickExit(1);
}
@@ -2143,7 +2273,7 @@ void SubstitutionGoal::tryToRun()
throw SysError(format("executing `%1%'") % sub);
} catch (std::exception & e) {
- std::cerr << format("substitute error: %1%\n") % e.what();
+ std::cerr << format("substitute error: %1%") % e.what() << std::endl;
}
quickExit(1);
}
diff --git a/src/libutil/util.cc b/src/libutil/util.cc
index d8d3751a1..428b1ff9a 100644
--- a/src/libutil/util.cc
+++ b/src/libutil/util.cc
@@ -350,13 +350,16 @@ Path createTempDir(const Path & tmpRoot)
}
-void createDirs(const Path & path)
+Paths createDirs(const Path & path)
{
- if (path == "/") return;
- createDirs(dirOf(path));
- if (!pathExists(path))
+ if (path == "/") return Paths();
+ Paths created = createDirs(dirOf(path));
+ if (!pathExists(path)) {
if (mkdir(path.c_str(), 0777) == -1)
throw SysError(format("creating directory `%1%'") % path);
+ created.push_back(path);
+ }
+ return created;
}
@@ -509,14 +512,25 @@ string drainFD(int fd)
//////////////////////////////////////////////////////////////////////
-AutoDelete::AutoDelete(const string & p) : path(p)
+AutoDelete::AutoDelete(const string & p, bool recursive) : path(p)
{
del = true;
+ this->recursive = recursive;
}
AutoDelete::~AutoDelete()
{
- if (del) deletePath(path);
+ try {
+ if (del)
+ if (recursive)
+ deletePath(path);
+ else {
+ if (remove(path.c_str()) == -1)
+ throw SysError(format("cannot unlink `%1%'") % path);
+ }
+ } catch (...) {
+ ignoreException();
+ }
}
void AutoDelete::cancel()
@@ -752,10 +766,10 @@ void killUser(uid_t uid)
if (errno != EINTR)
throw SysError(format("cannot kill processes for uid `%1%'") % uid);
}
-
+
} catch (std::exception & e) {
- std::cerr << format("killing processes beloging to uid `%1%': %1%\n")
- % uid % e.what();
+ std::cerr << format("killing processes beloging to uid `%1%': %1%")
+ % uid % e.what() << std::endl;
quickExit(1);
}
quickExit(0);
diff --git a/src/libutil/util.hh b/src/libutil/util.hh
index f72a6f820..0ed98118c 100644
--- a/src/libutil/util.hh
+++ b/src/libutil/util.hh
@@ -72,8 +72,9 @@ void makePathReadOnly(const Path & path);
/* Create a temporary directory. */
Path createTempDir(const Path & tmpRoot = "");
-/* Create a directory and all its parents, if necessary. */
-void createDirs(const Path & path);
+/* Create a directory and all its parents, if necessary. Returns the
+ list of created directories, in order of creation. */
+Paths createDirs(const Path & path);
/* Create a file and write the given text to it. The file is written
in binary mode (i.e., no end-of-line conversions). The path should
@@ -166,8 +167,9 @@ class AutoDelete
{
Path path;
bool del;
+ bool recursive;
public:
- AutoDelete(const Path & p);
+ AutoDelete(const Path & p, bool recursive = true);
~AutoDelete();
void cancel();
};