1
1
Fork 0
mirror of https://github.com/NixOS/nix.git synced 2025-11-27 04:30:59 +01:00

Merged to R9561; Fixed initial snapshot bug.

This commit is contained in:
Wouter den Breejen 2007-10-31 15:08:22 +00:00
parent c28742f633
commit 1164d6a389
21 changed files with 1522 additions and 775 deletions

View file

@ -298,18 +298,19 @@ static Expr prim_getEnv(EvalState & state, const ATermVector & args)
return makeStr(getEnv(name));
}
/* for debugging purposes. print the first arg on stdout (perhaps stderr should be used?)
* and return the second
/* Evaluate the first expression, and print its abstract syntax tree
on standard error. Then return the second expression. Useful for
debugging.
*/
static Expr prim_trace(EvalState & state, const ATermVector & args)
{
//string str = evalStringNoCtx(state, args[0]);
Expr a = evalExpr(state, args[0]);
printf("traced value: %s\n", atPrint(a).c_str());
Expr e = evalExpr(state, args[0]);
printMsg(lvlError, format("trace: %1%") % e);
return evalExpr(state, args[1]);
}
static Expr prim_relativise(EvalState & state, const ATermVector & args)
{
PathSet context; /* !!! what to do? */

View file

@ -27,6 +27,19 @@
#include <grp.h>
/* Includes required for chroot support. */
#include "config.h"
#if HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#if HAVE_SYS_MOUNT_H
#include <sys/mount.h>
#endif
#define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND)
namespace nix {
using std::map;
@ -585,6 +598,89 @@ void deletePathWrapped(const Path & path)
//////////////////////////////////////////////////////////////////////
/* Helper RAII 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)
{
#if CHROOT_ENABLED
debug(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);
#endif
}
void unbind()
{
#if CHROOT_ENABLED
if (source == "") return;
debug(format("unmount bind-mount `%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 bind-mount `%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) {
debug(format("deleting `%1%'") % *i);
if (remove(i->c_str()) == -1)
throw SysError(format("cannot unlink `%1%'") % *i);
}
source = "";
#endif
}
};
//////////////////////////////////////////////////////////////////////
class DerivationGoal : public Goal
{
private:
@ -635,6 +731,17 @@ private:
Pipe toHook;
Pipe fromHook;
/* Whether we're currently doing a chroot build. */
bool useChroot;
/* A RAII object to delete the chroot directory. */
boost::shared_ptr<AutoDelete> autoDelChroot;
/* 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;
@ -690,7 +797,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. */
@ -1038,6 +1145,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();
@ -1208,7 +1318,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);
}
@ -1596,6 +1706,56 @@ void DerivationGoal::startBuilder()
% buildUser.getGID() % nixStore);
}
/* Are we doing a chroot build? */
useChroot = queryBoolSetting("build-use-chroot", false);
Path tmpRootDir;
if (useChroot) {
#if CHROOT_ENABLED
/* Create a temporary directory in which we set up the chroot
environment using bind-mounts.
!!! Big danger here: since we're doing this in /tmp, there
is a risk that the admin does something like "rm -rf
/tmp/chroot-nix-*" to clean up aborted builds, and if some
of the bind-mounts are still active, then "rm -rf" will
happily recurse into those mount points (thereby deleting,
say, /nix/store). Ideally, tmpRootDir should be created in
some special location (maybe in /nix/var/nix) where Nix
takes care of unmounting / deleting old chroots
automatically. */
tmpRootDir = createTempDir("", "chroot-nix");
/* Clean up the chroot directory automatically, but don't
recurse; that would be very very bad if the unmount of a
bind-mount fails. Instead BindMount::unbind() unmounts and
deletes exactly those directories that it created to
produce the mount point, so that after all the BindMount
destructors have run, tmpRootDir should be empty. */
autoDelChroot = boost::shared_ptr<AutoDelete>(new AutoDelete(tmpRootDir, false));
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)));
#else
throw Error("chroot builds are not supported on this platform");
#endif
}
/* Run the builder. */
printMsg(lvlChatty, format("executing builder `%1%'") %
@ -1621,6 +1781,17 @@ void DerivationGoal::startBuilder()
try { /* child */
#if CHROOT_ENABLED
/* 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);
#endif
initChild();
/* Fill in the environment. */
@ -1684,7 +1855,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);
}
@ -1916,13 +2087,6 @@ void DerivationGoal::computeClosure()
state_stateReferences,
drvPath, 0);
//Commit state (we only include our own state in the rivisionMapping (but other build component states might have been changed !!!! TODO)
RevisionClosure rivisionMapping;
rivisionMapping[statePath] = commitStatePathTxn(txn, statePath);
//Save the new revision
setStateRevisionsTxn(txn, rivisionMapping, statePath, "Initial build revision.");
//Convert stateInfo from drv to DB format
//And set all interval-ed paths to zero to begin with
DerivationStateOutputDirs stateOutputDirs = drv.stateOutputDirs;
@ -1953,6 +2117,13 @@ void DerivationGoal::computeClosure()
//register state options that may change
DerivationStateOutput drvso = drv.stateOutputs["state"];
setStateOptionsTxn(txn, statePath, queryCallingUsername(), "nixbld", 700, drvso.runtimeStateArgs);
//Commit state (we only include our own state in the rivisionMapping (but other build component states might have been changed !!!! TODO)
RevisionClosure rivisionMapping;
rivisionMapping[statePath] = commitStatePathTxn(txn, statePath);
//Save the new revision
setStateRevisionsTxn(txn, rivisionMapping, statePath, "Initial build revision.");
//Shared state
Path sharedState = drv.stateOutputs.find("state")->second.sharedState;
@ -2332,7 +2503,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);
}

View file

@ -185,13 +185,15 @@ Snapshots commitStatePathTxn(const Transaction & txn, const Path & statePath)
CommitIntervals intervals = getStatePathsIntervalTxn(txn, statePath);
Snapshots revisions_list;
for (StateInfos::const_iterator i = infos.begin(); i != infos.end(); ++i){
string thisdir = (*i).path;
string type = (*i).type;
unsigned int interval = (*i).interval;
//printMsg(lvlError, format("maybe ssing %1% %2%") % thisdir % type);
if(type == "none"){
continue;
}

View file

@ -319,19 +319,19 @@ void makePathReadOnly(const Path & path)
}
static Path tempName(const Path & tmpRoot)
static Path tempName(const Path & tmpRoot, const Path & prefix)
{
static int counter = 0;
Path tmpRoot2 = canonPath(tmpRoot.empty() ? getEnv("TMPDIR", "/tmp") : tmpRoot, true);
return (format("%1%/nix-%2%-%3%") % tmpRoot2 % getpid() % counter++).str();
return (format("%1%/%2%-%3%-%4%") % tmpRoot2 % prefix % getpid() % counter++).str();
}
Path createTempDir(const Path & tmpRoot)
Path createTempDir(const Path & tmpRoot, const Path & prefix)
{
while (1) {
checkInterrupt();
Path tmpDir = tempName(tmpRoot);
Path tmpDir = tempName(tmpRoot, prefix);
if (mkdir(tmpDir.c_str(), 0777) == 0) {
/* Explicitly set the group of the directory. This is to
work around around problems caused by BSD's group
@ -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;
}
@ -511,14 +514,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()
@ -754,10 +768,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);
@ -812,7 +826,7 @@ string runProgram(Path program, bool searchPath, const Strings & args)
throw SysError(format("executing `%1%'") % program);
} catch (std::exception & e) {
std::cerr << "error: " << e.what() << std::endl;
std::cerr << "error: " << e.what() << std::endl; //TODO does not give the full error message
}
quickExit(1);
}
@ -1201,21 +1215,6 @@ bool IsSymlink(const string FileName)
return (S_ISLNK(my_stat.st_mode) != 0);
}
/*
string getCallingUserName()
{
//Linux
Strings empty;
string username = runProgram("whoami", true, empty); //the username of the user that is trying to build the component
//TODO Can be faked, so this is clearly unsafe ... :(
//Remove the trailing \n
int pos = username.find("\n",0);
username.erase(pos,1);
return username;
}
*/
/* adds the second PathSet after the first, but removing doubles from the second (union)
* (We assume the first PathSet has no duplicates)
* UNTESTED !!!!!!!!!!!!!!
@ -1321,8 +1320,15 @@ void symlinkPath(const Path & existingDir, const Path & newLinkName) //TODO bool
* We do -snf for:
* -s : symlinking
* -f : To remove existing destination files (this does NOT always overwrite the newLinkName !!!!)
* -n : Treat destination that is a symlink to a directory as if it were a normal file (This makes sure
* that newLinkName is really overwritten)
* -n : Treat destination that is a symlink to a directory as if it were a normal file:
* When the destination is an actual directory (not a symlink to one), there is no ambiguity.
* The link is created in that directory. But when the specified destination is a symlink to a directory,
* there are two ways to treat the user's request. ln can treat the destination just as it would a normal
* directory and create the link in it. On the other hand, the destination can be viewed as a non-directory
* - as the symlink itself. In that case, ln must delete or backup that symlink before creating the new link.
* The default is to treat a destination that is a symlink to a directory just like a directory.
*
* ((This makes sure that newLinkName is really overwritten)
*/

View file

@ -79,10 +79,11 @@ void deletePath(const Path & path, unsigned long long & bytesFreed);
void makePathReadOnly(const Path & path);
/* Create a temporary directory. */
Path createTempDir(const Path & tmpRoot = "");
Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix");
/* 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
@ -175,8 +176,9 @@ class AutoDelete
{
Path path;
bool del;
bool recursive;
public:
AutoDelete(const Path & p);
AutoDelete(const Path & p, bool recursive = true);
~AutoDelete();
void cancel();
};

View file

@ -63,8 +63,6 @@ Query flags:
--out-path: print path of derivation output
--description: print description
--meta: print all meta attributes (only with --xml)
--prebuilt-only: only show derivations whose prebuilt binaries are
available on this machine or are downloadable
Options:
@ -74,3 +72,5 @@ Options:
--keep-failed / -K: keep temporary directories of failed builds
--preserve-installed: do not replace currently installed versions in `-i'
--system-filter SYSTEM: only use derivations for specified platform
--prebuilt-only / -b: only use derivations whose prebuilt binaries are
available on this machine or are downloadable

View file

@ -49,8 +49,9 @@ struct InstallSourceInfo
Path nixExprPath; /* for srcNixExprDrvs, srcNixExprs */
Path profile; /* for srcProfile */
string systemFilter; /* for srcNixExprDrvs */
bool prebuiltOnly;
ATermMap autoArgs;
InstallSourceInfo() : autoArgs() { };
InstallSourceInfo() : prebuiltOnly(false) { };
};
@ -96,6 +97,8 @@ static bool parseInstallSourceOptions(Globals & globals,
}
else if (arg == "--attr" || arg == "-A")
globals.instSource.type = srcAttrPath;
else if (arg == "--prebuilt-only" || arg == "-b")
globals.instSource.prebuiltOnly = true;
else return false;
return true;
}
@ -339,9 +342,16 @@ static int comparePriorities(EvalState & state,
}
static DrvInfos filterBySelector(EvalState & state,
const DrvInfos & allElems,
const Strings & args, bool newestOnly)
static bool isPrebuilt(EvalState & state, const DrvInfo & elem)
{
return
store->isValidPath(elem.queryOutPath(state)) ||
store->hasSubstitutes(elem.queryOutPath(state));
}
static DrvInfos filterBySelector(EvalState & state, const DrvInfos & allElems,
const Strings & args, bool newestOnly, bool prebuiltOnly)
{
DrvNames selectors = drvNamesFromArgs(args);
@ -360,7 +370,8 @@ static DrvInfos filterBySelector(EvalState & state,
DrvName drvName(j->name);
if (i->matches(drvName)) {
i->hits++;
matches.push_back(std::pair<DrvInfo, unsigned int>(*j, n));
if (!prebuiltOnly || isPrebuilt(state, *j))
matches.push_back(std::pair<DrvInfo, unsigned int>(*j, n));
}
}
@ -450,7 +461,8 @@ static void queryInstSources(EvalState & state,
loadDerivations(state, instSource.nixExprPath,
instSource.systemFilter, instSource.autoArgs, "", allElems);
elems = filterBySelector(state, allElems, args, newestOnly);
elems = filterBySelector(state, allElems, args,
newestOnly, instSource.prebuiltOnly);
break;
}
@ -518,7 +530,7 @@ static void queryInstSources(EvalState & state,
case srcProfile: {
elems = filterBySelector(state,
queryInstalled(state, instSource.profile),
args, newestOnly);
args, newestOnly, instSource.prebuiltOnly);
break;
}
@ -1152,7 +1164,7 @@ static void opQuery(Globals & globals,
DrvInfos elems = filterBySelector(globals.state,
source == sInstalled ? installedElems : availElems,
remaining, false);
remaining, false, prebuiltOnly);
DrvInfos & otherElems(source == sInstalled ? availElems : installedElems);
@ -1193,12 +1205,6 @@ static void opQuery(Globals & globals,
/* For XML output. */
XMLAttrs attrs;
if (prebuiltOnly) {
if (!store->isValidPath(i->queryOutPath(globals.state)) &&
!store->hasSubstitutes(i->queryOutPath(globals.state)))
continue;
}
if (printStatus) {
bool hasSubs = store->hasSubstitutes(i->queryOutPath(globals.state));
bool isInstalled = installed.find(i->queryOutPath(globals.state)) != installed.end();

View file

@ -197,7 +197,6 @@ static void revertToRevision(Strings opFlags, Strings opArgs)
static void queryAvailableStateRevisions(Strings opFlags, Strings opArgs)
{
Path statePath;
if(store->isValidStatePath(*(opArgs.begin())))
statePath = *(opArgs.begin());
else{
@ -292,7 +291,18 @@ static void opShowSharedPaths(Strings opFlags, Strings opArgs)
static void opUnshare(Strings opFlags, Strings opArgs)
{
Path statePath = *(opArgs.begin());
Path statePath;
if(store->isValidStatePath(*(opArgs.begin())))
statePath = *(opArgs.begin());
else{
Path componentPath;
string binary;
string derivationPath;
bool isStateComponent;
Strings program_args;
getPathInfo_andCheckArgs(opFlags, opArgs, componentPath, statePath, binary, derivationPath, isStateComponent, program_args);
}
if(!store->isValidStatePath(statePath))
throw UsageError(format("Path '%1%' is not a valid state path.") % statePath);
@ -784,10 +794,10 @@ void run(Strings args)
*/
//Manipulate options....
//Manipulate options TODO only allow for root user ...
else if (arg.substr(0,13) == "--identifier=")
stateIdentifier = arg.substr(13,arg.length());
else if (arg.substr(0,7) == "--user=")
else if (arg.substr(0,7) == "--nix-user=")
username = arg.substr(7,arg.length());

View file

@ -240,8 +240,8 @@ static void performOp(Source & from, Sink & to, unsigned int op)
}
#endif
case wopIsValidPath: {
Path path = readStorePath(from);
case wopIsValidPath: { //we do a readString at isValidXXXX
Path path = readString(from);
startWork();
bool result = store->isValidPath(path);
stopWork();
@ -250,7 +250,7 @@ static void performOp(Source & from, Sink & to, unsigned int op)
}
case wopIsValidStatePath: {
Path path = readStatePath(from);
Path path = readString(from);
startWork();
bool result = store->isValidStatePath(path);
stopWork();
@ -259,7 +259,7 @@ static void performOp(Source & from, Sink & to, unsigned int op)
}
case wopIsValidComponentOrStatePath: {
Path path = readStoreOrStatePath(from);
Path path = readString(from);
startWork();
bool result = store->isValidComponentOrStatePath(path);
stopWork();