forked from lix-project/lix
20ce2642fc
* Set the references for the user environment manifest properly. * Don't copy the manifest (this was accidental). * Don't store derivation paths in the manifest (maybe this should be made optional). This cleans up the semantics of nix-env, which were weird. * Hash on the output paths of activated components, not on derivation paths. This is because we don't know the derivation path of already installed components anymore, and it allows the installation of components by store path (skipping Nix expressions entirely). * Query options `--out-path' and `--drv-path' to show the output and derivation paths of components, respectively (the latter replaces the `--expr' query).
133 lines
3.7 KiB
C++
133 lines
3.7 KiB
C++
#include "profiles.hh"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
|
|
|
|
static bool cmpGensByNumber(const Generation & a, const Generation & b)
|
|
{
|
|
return a.number < b.number;
|
|
}
|
|
|
|
|
|
/* Parse a generation name of the format
|
|
`<profilename>-<number>-link'. */
|
|
static int parseName(const string & profileName, const string & name)
|
|
{
|
|
if (string(name, 0, profileName.size() + 1) != profileName + "-") return -1;
|
|
string s = string(name, profileName.size() + 1);
|
|
unsigned int p = s.find("-link");
|
|
if (p == string::npos) return -1;
|
|
int n;
|
|
if (string2Int(string(s, 0, p), n) && n >= 0)
|
|
return n;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
|
|
|
|
Generations findGenerations(Path profile, int & curGen)
|
|
{
|
|
Generations gens;
|
|
|
|
Path profileDir = dirOf(profile);
|
|
string profileName = baseNameOf(profile);
|
|
|
|
Strings names = readDirectory(profileDir);
|
|
for (Strings::iterator i = names.begin(); i != names.end(); ++i) {
|
|
int n;
|
|
if ((n = parseName(profileName, *i)) != -1) {
|
|
Generation gen;
|
|
gen.path = profileDir + "/" + *i;
|
|
gen.number = n;
|
|
struct stat st;
|
|
if (lstat(gen.path.c_str(), &st) != 0)
|
|
throw SysError(format("statting `%1%'") % gen.path);
|
|
gen.creationTime = st.st_mtime;
|
|
gens.push_back(gen);
|
|
}
|
|
}
|
|
|
|
gens.sort(cmpGensByNumber);
|
|
|
|
curGen = pathExists(profile)
|
|
? parseName(profileName, readLink(profile))
|
|
: -1;
|
|
|
|
return gens;
|
|
}
|
|
|
|
|
|
static void makeNames(const Path & profile, unsigned int num,
|
|
Path & outLink, Path & drvLink)
|
|
{
|
|
Path prefix = (format("%1%-%2%") % profile % num).str();
|
|
outLink = prefix + "-link";
|
|
drvLink = prefix + "-drv";
|
|
}
|
|
|
|
|
|
Path createGeneration(Path profile, Path outPath, Path drvPath)
|
|
{
|
|
/* The new generation number should be higher than old the
|
|
previous ones. */
|
|
int dummy;
|
|
Generations gens = findGenerations(profile, dummy);
|
|
unsigned int num = gens.size() > 0 ? gens.front().number : 0;
|
|
|
|
/* Create the new generation. */
|
|
Path outLink, drvLink;
|
|
|
|
while (1) {
|
|
makeNames(profile, num, outLink, drvLink);
|
|
if (symlink(outPath.c_str(), outLink.c_str()) == 0) break;
|
|
if (errno != EEXIST)
|
|
throw SysError(format("creating symlink `%1%'") % outLink);
|
|
/* Somebody beat us to it, retry with a higher number. */
|
|
num++;
|
|
}
|
|
|
|
if (symlink(drvPath.c_str(), drvLink.c_str()) != 0)
|
|
throw SysError(format("creating symlink `%1%'") % drvLink);
|
|
|
|
return outLink;
|
|
}
|
|
|
|
|
|
static void removeFile(const Path & path)
|
|
{
|
|
if (remove(path.c_str()) == -1)
|
|
throw SysError(format("cannot unlink `%1%'") % path);
|
|
}
|
|
|
|
|
|
void deleteGeneration(const Path & profile, unsigned int gen)
|
|
{
|
|
Path generation, gcrootDrv;
|
|
makeNames(profile, gen, generation, gcrootDrv);
|
|
removeFile(generation);
|
|
if (pathExists(gcrootDrv)) removeFile(gcrootDrv);
|
|
}
|
|
|
|
|
|
void switchLink(Path link, Path target)
|
|
{
|
|
/* Hacky. */
|
|
if (dirOf(target) == dirOf(link)) target = baseNameOf(target);
|
|
|
|
Path tmp = canonPath(dirOf(link) + "/.new_" + baseNameOf(link));
|
|
if (symlink(target.c_str(), tmp.c_str()) != 0)
|
|
throw SysError(format("creating symlink `%1%'") % tmp);
|
|
/* The rename() system call is supposed to be essentially atomic
|
|
on Unix. That is, if we have links `current -> X' and
|
|
`new_current -> Y', and we rename new_current to current, a
|
|
process accessing current will see X or Y, but never a
|
|
file-not-found or other error condition. This is sufficient to
|
|
atomically switch user environments. */
|
|
if (rename(tmp.c_str(), link.c_str()) != 0)
|
|
throw SysError(format("renaming `%1%' to `%2%'") % tmp % link);
|
|
}
|