forked from lix-project/lix
WIP: restore /nix/var/nix as source of truth for profiles
See-also: https://github.com/NixOS/nix/pull/5226
Change-Id: I35082c9a59148f7fb132232659c3a254d514d190
This commit is contained in:
parent
a93667433f
commit
f789808a6c
|
@ -269,7 +269,15 @@ void MixProfile::updateProfile(const BuiltPaths & buildables)
|
|||
|
||||
MixDefaultProfile::MixDefaultProfile()
|
||||
{
|
||||
profile = getDefaultProfile();
|
||||
static bool haveWarned = false;
|
||||
try {
|
||||
// `nix profile remove` at the least seems to expect this.
|
||||
this->profile = ensureDefaultProfile();
|
||||
} catch (SysError const & e) {
|
||||
// However we get called in __dumpCli too, so we should not do fatal IO.
|
||||
warnOnce(haveWarned, "ignoring error initializing default profile %s", e.what());
|
||||
this->profile = getDefaultProfile();
|
||||
}
|
||||
}
|
||||
|
||||
MixEnvironment::MixEnvironment() : ignoreEnvironment(false)
|
||||
|
|
|
@ -216,6 +216,10 @@ LocalStore::LocalStore(const Params & params)
|
|||
}
|
||||
}
|
||||
|
||||
if (!readOnly) {
|
||||
createUser(getUserName(), getuid());
|
||||
}
|
||||
|
||||
/* Optionally, create directories and set permissions for a
|
||||
multi-user install. */
|
||||
if (getuid() == 0 && settings.buildUsersGroup != "") {
|
||||
|
@ -1788,6 +1792,27 @@ void LocalStore::signRealisation(Realisation & realisation)
|
|||
}
|
||||
}
|
||||
|
||||
void LocalStore::createUser(std::string_view userName, uid_t userId)
|
||||
{
|
||||
using namespace std::literals::string_view_literals;
|
||||
|
||||
// FIXME(Qyriad): is /nix/var/nix/gcroots/per-user used anywhere...?
|
||||
for (auto const & dirPart : {"profiles"sv, "gcroots"sv}) {
|
||||
|
||||
auto const fullPerUserDir = fmt("%s/%s/per-user/%s", stateDir, dirPart, userName);
|
||||
createDirs(fullPerUserDir);
|
||||
|
||||
if (chmod(fullPerUserDir.c_str(), 0755) == -1) {
|
||||
throw SysError(errno, "changing permissions of directory '%s'", fullPerUserDir);
|
||||
}
|
||||
|
||||
if (chown(fullPerUserDir.c_str(), userId, getgid()) == -1) {
|
||||
throw SysError(errno, "changing owner of directory '%s'", fullPerUserDir);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void LocalStore::signPathInfo(ValidPathInfo & info)
|
||||
{
|
||||
// FIXME: keep secret keys in memory.
|
||||
|
|
|
@ -364,6 +364,8 @@ private:
|
|||
void signPathInfo(ValidPathInfo & info);
|
||||
void signRealisation(Realisation &);
|
||||
|
||||
void createUser(std::string_view userName, uid_t userId) override;
|
||||
|
||||
// XXX: Make a generic `Store` method
|
||||
ContentAddress hashCAPath(
|
||||
const ContentAddressMethod & method,
|
||||
|
|
|
@ -307,12 +307,7 @@ std::string optimisticLockProfile(const Path & profile)
|
|||
|
||||
Path profilesDir()
|
||||
{
|
||||
auto profileRoot =
|
||||
(getuid() == 0)
|
||||
? rootProfilesDir()
|
||||
: createNixStateDir() + "/profiles";
|
||||
createDirs(profileRoot);
|
||||
return profileRoot;
|
||||
return fmt("%s/profiles/per-user/%s", settings.nixStateDir, getUserName());
|
||||
}
|
||||
|
||||
Path rootProfilesDir()
|
||||
|
@ -332,24 +327,40 @@ Path getDefaultProfileLink()
|
|||
return getHome() + "/.nix-profile";
|
||||
}
|
||||
|
||||
Path ensureDefaultProfile()
|
||||
{
|
||||
Path const profileLink = getDefaultProfileLink();
|
||||
Path const defaultProfile = profilesDir() + "/profile";
|
||||
|
||||
if (!pathExists(profileLink)) {
|
||||
replaceSymlink(defaultProfile, profileLink);
|
||||
}
|
||||
|
||||
// Backwards compatibiliy measure: Make root's profile available as
|
||||
// `.../default` as it's what NixOS and most of the init scripts expect
|
||||
Path const globalProfileLink = settings.nixStateDir + "/profiles/default";
|
||||
if (getuid() == 0 && !pathExists(globalProfileLink)) {
|
||||
replaceSymlink(defaultProfile, globalProfileLink);
|
||||
}
|
||||
|
||||
return absPath(readLink(profileLink), dirOf(profileLink));
|
||||
}
|
||||
|
||||
Path getDefaultProfile()
|
||||
{
|
||||
Path profileLink = settings.useXDGBaseDirectories ? createNixStateDir() + "/profile" : getHome() + "/.nix-profile";
|
||||
Path const profileLink = getDefaultProfileLink();
|
||||
|
||||
try {
|
||||
auto profile = profilesDir() + "/profile";
|
||||
if (!pathExists(profileLink)) {
|
||||
replaceSymlink(profile, profileLink);
|
||||
if (pathExists(profileLink)) {
|
||||
return absPath(readLink(profileLink), dirOf(profileLink));
|
||||
}
|
||||
// Backwards compatibiliy measure: Make root's profile available as
|
||||
// `.../default` as it's what NixOS and most of the init scripts expect
|
||||
Path globalProfileLink = settings.nixStateDir + "/profiles/default";
|
||||
if (getuid() == 0 && !pathExists(globalProfileLink)) {
|
||||
replaceSymlink(profile, globalProfileLink);
|
||||
}
|
||||
return absPath(readLink(profileLink), dirOf(profileLink));
|
||||
} catch (Error &) {
|
||||
return profileLink;
|
||||
} catch (SysError const & e) {
|
||||
printTalkative("ignoring error resolving default profile '%s': %s", profileLink, e.what());
|
||||
} catch (Error const & e) {
|
||||
printError("ignoring error resolving default profile '%s': %s", profileLink, e.what());
|
||||
}
|
||||
|
||||
return profileLink;
|
||||
}
|
||||
|
||||
Path defaultChannelsDir()
|
||||
|
|
|
@ -235,9 +235,14 @@ Path getDefaultProfileLink();
|
|||
|
||||
/**
|
||||
* Resolve the default profile (~/.nix-profile by default,
|
||||
* $XDG_STATE_HOME/nix/profile if XDG Base Directory Support is enabled),
|
||||
* and create if doesn't exist
|
||||
* $XDG_STATE_HOME/nix/profile if XDG Base Directory Support is enabled).
|
||||
*/
|
||||
Path getDefaultProfile();
|
||||
|
||||
/**
|
||||
* Resolve the default profile (see getDefaultProfile()),
|
||||
* and create it if it doesn't exist.
|
||||
*/
|
||||
Path ensureDefaultProfile();
|
||||
|
||||
}
|
||||
|
|
|
@ -845,6 +845,11 @@ public:
|
|||
return toRealPath(printStorePath(storePath));
|
||||
}
|
||||
|
||||
virtual void createUser(std::string_view userName, uid_t userId)
|
||||
{
|
||||
warn("base class Store called unimplemented createUser()");
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronises the options of the client with those of the daemon
|
||||
* (a no-op when there’s no daemon)
|
||||
|
|
|
@ -357,6 +357,13 @@ static void daemonLoop(std::optional<TrustedFlag> forceTrustClientOpt)
|
|||
// Restore normal handling of SIGCHLD.
|
||||
setSigChldAction(false);
|
||||
|
||||
auto store = openUncachedStore();
|
||||
try {
|
||||
store->createUser(user, peer.uid);
|
||||
} catch (SysError const & e) {
|
||||
printError("ignoring error while creating store per-user state: %s", e.what());
|
||||
}
|
||||
|
||||
// For debugging, stuff the pid into argv[1].
|
||||
if (peer.pidKnown && savedArgv[1]) {
|
||||
auto processName = std::to_string(peer.pid);
|
||||
|
@ -366,7 +373,7 @@ static void daemonLoop(std::optional<TrustedFlag> forceTrustClientOpt)
|
|||
// Handle the connection.
|
||||
FdSource from(remote.get());
|
||||
FdSink to(remote.get());
|
||||
processConnection(openUncachedStore(), from, to, trusted, NotRecursive);
|
||||
processConnection(store, from, to, trusted, NotRecursive);
|
||||
|
||||
exit(0);
|
||||
}, options);
|
||||
|
|
|
@ -66,7 +66,7 @@ readLink() {
|
|||
}
|
||||
|
||||
clearProfiles() {
|
||||
profiles="$HOME"/.local/state/nix/profiles
|
||||
profiles="$NIX_STATE_DIR/profiles/per-user/$(whoami || echo -n nixbld)"
|
||||
rm -rf "$profiles"
|
||||
}
|
||||
|
||||
|
|
|
@ -143,6 +143,8 @@ in
|
|||
|
||||
nix-upgrade-nix = runNixOSTestFor "x86_64-linux" ./nix-upgrade-nix.nix;
|
||||
|
||||
profiles = runNixOSTestFor "x86_64-linux" ./profiles.nix;
|
||||
|
||||
nssPreload = runNixOSTestFor "x86_64-linux" ./nss-preload.nix;
|
||||
|
||||
githubFlakes = runNixOSTestFor "x86_64-linux" ./github-flakes.nix;
|
||||
|
|
128
tests/nixos/profiles.nix
Normal file
128
tests/nixos/profiles.nix
Normal file
|
@ -0,0 +1,128 @@
|
|||
{ lib, config, ... }:
|
||||
|
||||
let
|
||||
pkgs = config.nodes.machine.nixpkgs.pkgs;
|
||||
eza = "${lib.getExe pkgs.eza} -laa --group-directories-first --classify --color=always";
|
||||
ezaTree = "${lib.getExe pkgs.eza} -la --tree --color=always";
|
||||
|
||||
oldNix = pkgs.nixVersions.nix_2_18;
|
||||
|
||||
nix23-daemon = lib.getExe' oldNix "nix-daemon";
|
||||
nix23-env = lib.getExe' oldNix "nix-env";
|
||||
|
||||
in {
|
||||
name = "nix-profiles";
|
||||
|
||||
nodes = {
|
||||
machine = { config, lib, pkgs, ... }: {
|
||||
virtualisation.writableStore = true;
|
||||
virtualisation.additionalPaths = [
|
||||
pkgs.hello
|
||||
pkgs.cowsay.out
|
||||
pkgs.cowsay.man
|
||||
pkgs.cowsay.drvPath
|
||||
pkgs.eza
|
||||
(lib.getBin pkgs.nixVersions.nix_2_3)
|
||||
];
|
||||
users.users.alice.isNormalUser = true;
|
||||
nix.settings = {
|
||||
substituters = lib.mkForce [ ];
|
||||
experimental-features = [ "nix-command" "flakes" ];
|
||||
allowed-users = [ "alice" ];
|
||||
use-xdg-base-directories = true;
|
||||
};
|
||||
nix.nixPath = [ "nixpkgs=${pkgs.path}" ];
|
||||
#nix.package = pkgs.nixVersions.nix_2_18;
|
||||
#nix.package = pkgs.nixVersions.nix_2_3;
|
||||
};
|
||||
};
|
||||
|
||||
testScript = { nodes }: ''
|
||||
# fmt: off
|
||||
|
||||
start_all()
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
|
||||
machine.succeed("nix -vv --version >&2")
|
||||
print(machine.succeed("${eza} ~ >&2"))
|
||||
print(machine.succeed("realpath ~ >&2"))
|
||||
|
||||
machine.succeed("nix --version -vv >&2")
|
||||
|
||||
# Initial state.
|
||||
#machine.succeed("""
|
||||
# su --login alice -c '
|
||||
# set -euxo pipefail
|
||||
# ${ezaTree} ~
|
||||
# ${ezaTree} /nix/var/nix
|
||||
# :' >&2
|
||||
#""")
|
||||
|
||||
# Create some client-side profiles for us to worry about.
|
||||
machine.succeed("""
|
||||
export NIX_DAEMON_SOCKET_PATH=/tmp/nix23-socket
|
||||
${nix23-daemon} >&2 &
|
||||
export _NIX_DAEMON_PID=$!
|
||||
su --login alice -c '
|
||||
export NIX_DAEMON_SOCKET_PATH=/tmp/nix23-socket
|
||||
${nix23-env} --version -vv
|
||||
${nix23-env} --file "<nixpkgs>" --install -A hello
|
||||
${ezaTree} ~
|
||||
${ezaTree} /nix/var/nix/profiles
|
||||
:' >&2
|
||||
kill "$_NIX_DAEMON_PID" >&2
|
||||
""")
|
||||
|
||||
# This one is as root.
|
||||
#machine.succeed("""
|
||||
# nix-env --store local --file "<nixpkgs>" --install -A hello >&2
|
||||
# ${ezaTree} ~ >&2
|
||||
# ${ezaTree} /nix/var/nix/profiles >&2
|
||||
#""")
|
||||
|
||||
machine.succeed("""
|
||||
su --login alice -c '
|
||||
set -euxo pipefail
|
||||
nix-env --file "<nixpkgs>" --install -A hello
|
||||
${ezaTree} ~
|
||||
${ezaTree} /nix/var/nix/profiles
|
||||
:' >&2
|
||||
""")
|
||||
|
||||
machine.succeed("""
|
||||
su --login alice -c '
|
||||
set -euxo pipefail
|
||||
nix-env --file "<nixpkgs>" --install -A cowsay
|
||||
${ezaTree} ~
|
||||
${ezaTree} /nix/var/nix/profiles
|
||||
:' >&2
|
||||
""")
|
||||
|
||||
machine.succeed("""
|
||||
su --login alice -c '
|
||||
set -euxo pipefail
|
||||
PAGER=cat nix-env --option use-xdg-base-directories false --query '*'
|
||||
${ezaTree} ~
|
||||
${ezaTree} /nix/var/nix/profiles
|
||||
:' >&2
|
||||
""")
|
||||
|
||||
machine.succeed("""
|
||||
su --login alice -c '
|
||||
set -euxo pipefail
|
||||
nix-env --uninstall cowsay
|
||||
${ezaTree} ~
|
||||
${ezaTree} /nix/var/nix/profiles
|
||||
:' >&2
|
||||
""")
|
||||
|
||||
machine.succeed("""
|
||||
su --login alice -c '
|
||||
set -euxo pipefail
|
||||
nix profile install -f "<nixpkgs>" cowsay
|
||||
${ezaTree} ~
|
||||
${ezaTree} /nix/var/nix/profiles
|
||||
:' >&2
|
||||
""")
|
||||
'';
|
||||
}
|
Loading…
Reference in a new issue