diff --git a/nixos/modules/security/ca.nix b/nixos/modules/security/ca.nix
index f430a5a6339..e070ffc95e4 100644
--- a/nixos/modules/security/ca.nix
+++ b/nixos/modules/security/ca.nix
@@ -4,10 +4,53 @@ with lib;
{
+ options = {
+
+ security.pki.certificateFiles = mkOption {
+ type = types.listOf types.path;
+ default = [];
+ example = literalExample "[ \"\${pkgs.cacert}/etc/ca-bundle.crt\" ]";
+ description = ''
+ A list of files containing trusted root certificates in PEM
+ format. These are concatenated to form
+ /etc/ssl/certs/ca-bundle.crt, which is
+ used by many programs that use OpenSSL, such as
+ curl and git.
+ '';
+ };
+
+ security.pki.certificates = mkOption {
+ type = types.listOf types.string;
+ default = [];
+ example = singleton ''
+ NixOS.org
+ =========
+ -----BEGIN CERTIFICATE-----
+ MIIGUDCCBTigAwIBAgIDD8KWMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYDVQQGEwJJ
+ TDEWMBQGA1UEChMNU3RhcnRDb20gTHRkLjErMCkGA1UECxMiU2VjdXJlIERpZ2l0
+ ...
+ -----END CERTIFICATE-----
+ '';
+ description = ''
+ A list of trusted root certificates in PEM format.
+ '';
+ };
+
+ };
+
config = {
+ security.pki.certificateFiles = [ "${pkgs.cacert}/etc/ca-bundle.crt" ];
+
environment.etc =
- [ { source = "${pkgs.cacert}/etc/ca-bundle.crt";
+ [ { source = pkgs.runCommand "ca-bundle.crt"
+ { files =
+ config.security.pki.certificateFiles ++
+ [ (builtins.toFile "extra.crt" (concatStringsSep "\n" config.security.pki.certificates)) ];
+ }
+ ''
+ cat $files > $out
+ '';
target = "ssl/certs/ca-bundle.crt";
}
];
diff --git a/pkgs/build-support/build-fhs-chrootenv/default.nix b/pkgs/build-support/build-fhs-chrootenv/default.nix
index a4ba456d7a5..461f4762aba 100644
--- a/pkgs/build-support/build-fhs-chrootenv/default.nix
+++ b/pkgs/build-support/build-fhs-chrootenv/default.nix
@@ -1,97 +1,6 @@
-{ buildEnv, nixpkgs, nixpkgs_i686, system
-, stdenv, glibc, glibc_multi, glibcLocales
-, bashInteractive, coreutils, less, shadow, su
-, gawk, gcc, gcc_multi, diffutils, findutils, gnused, gnugrep
-, gnutar, gzip, bzip2, xz
-} :
-{ name, pkgs ? [], profile ? ""
-, targetPkgs ? null, multiPkgs ? null
-, extraBuildCommands ? "", extraBuildCommandsMulti ? ""
-}:
-
-assert pkgs != [] -> targetPkgs == null && multiPkgs == null;
-assert targetPkgs != null -> multiPkgs != null;
-assert multiPkgs != null -> targetPkgs != null;
-assert targetPkgs != null -> pkgs == [];
-
-
-# HOWTO:
-# If pkgs is defined buildFHSChrootEnv will run in legacy mode. This means
-# it will build all pkgs contained in pkgs and basePkgs and then just merge
-# all of their contents together via buildEnv.
-#
-# The new way is to define both targetPkgs and multiPkgs. These two are
-# functions which get a pkgs environment supplied and should then return a list
-# of packages based this environment.
-# For example: targetPkgs = pkgs: [ pkgs.nmap ];
-#
-# All packages (most likeley programs) placed in targetPkgs will only be
-# installed once--matching the hosts architecture (64bit on x86_64 and 32bit on
-# x86). These packages will populate the chroot directory tree.
-#
-# Packages (most likeley libraries) defined in multiPkgs will be installed once
-# on x86 systems and twice on x86_64 systems.
-# On x86 they will just be merge with the packages defined in targetPkgs.
-# On x86_64 they will be added to targetPkgs and in addition their 32bit
-# versions will also be installed. The final directory should look as follows:
-# /lib will include 32bit libraries from multiPkgs
-# /lib32 will link to /lib
-# /lib64 will include 64bit libraries from multiPkgs and targetPkgs
-# /x86 will contain a complete 32bit environment composed by multiPkgs
+{ stdenv } : { env } :
let
- is64Bit = system == "x86_64-linux";
- # enable multi builds on x86_64 hosts if pakgs_target/multi are defined
- isMultiBuild = is64Bit && targetPkgs != null;
- isTargetBuild = !isMultiBuild;
-
- # list of packages (usually programs) which will only be installed for the
- # hosts architecture
- targetPaths = if targetPkgs == null
- then pkgs
- else targetPkgs nixpkgs ++ multiPkgs nixpkgs;
-
- # list of pckages which should be build for both x86 and x86_64 on x86_64
- # systems
- multiPaths = if isMultiBuild
- then multiPkgs nixpkgs_i686
- else [];
-
- # base packages of the chroot
- # these match the hosts architecture, gcc/glibc_multi will be choosen
- # on multi builds
- choosenGcc = if isMultiBuild then gcc_multi else gcc;
- basePkgs =
- [ (if isMultiBuild then glibc_multi else glibc)
- choosenGcc
- bashInteractive coreutils less shadow su
- gawk diffutils findutils gnused gnugrep
- gnutar gzip bzip2 xz
- ];
-
- # Compose a global profile for the chroot environment
- profilePkg = nixpkgs.stdenv.mkDerivation {
- name = "${name}-chrootenv-profile";
- buildCommand = ''
- mkdir -p $out/etc
- cat >> $out/etc/profile << "EOF"
- export PS1='${name}-chrootenv:\u@\h:\w\$ '
- ${profile}
- EOF
- '';
- };
-
- # Composes a /usr like directory structure
- staticUsrProfileTarget = buildEnv {
- name = "system-profile-target";
- paths = basePkgs ++ [ profilePkg ] ++ targetPaths;
- };
-
- staticUsrProfileMulti = buildEnv {
- name = "system-profile-multi";
- paths = multiPaths;
- };
-
# References to shell scripts that set up or tear down the environment
initSh = ./init.sh.in;
mountSh = ./mount.sh.in;
@@ -99,89 +8,16 @@ let
umountSh = ./umount.sh.in;
destroySh = ./destroy.sh.in;
- linkProfile = profile: ''
- for i in ${profile}/{etc,bin,sbin,share,var}; do
- if [ -x "$i" ]
- then
- ln -s "$i"
- fi
- done
- '';
-
- # the target profile is the actual profile that will be used for the chroot
- setupTargetProfile = ''
- ${linkProfile staticUsrProfileTarget}
- ${setupLibDirs}
-
- mkdir -m0755 usr
- cd usr
- ${linkProfile staticUsrProfileTarget}
- ${setupLibDirs}
- cd ..
- '';
-
- # this will happen on x86_64 host:
- # /x86 -> links to the whole profile defined by multiPaths
- # /lib, /lib32 -> links to 32bit binaries
- # /lib64 -> links to 64bit binaries
- # /usr/lib* -> same as above
- setupMultiProfile = if isTargetBuild then "" else ''
- mkdir -m0755 x86
- cd x86
- ${linkProfile staticUsrProfileMulti}
- cd ..
- '';
-
- setupLibDirs = if isTargetBuild then setupLibDirs_target
- else setupLibDirs_multi;
-
- # setup library paths only for the targeted architecture
- setupLibDirs_target = ''
- mkdir -m0755 lib
-
- # copy content of targetPaths
- cp -rsf ${staticUsrProfileTarget}/lib/* lib/
- '';
-
- # setup /lib, /lib32 and /lib64
- setupLibDirs_multi = ''
- mkdir -m0755 lib
- mkdir -m0755 lib64
- ln -s lib lib32
-
- # copy glibc stuff
- cp -rsf ${staticUsrProfileTarget}/lib/32/* lib/
-
- # copy content of multiPaths (32bit libs)
- cp -rsf ${staticUsrProfileMulti}/lib/* lib/
-
- # copy content of targetPaths (64bit libs)
- cp -rsf ${staticUsrProfileTarget}/lib/* lib64/
-
- # most 64bit only libs put their stuff into /lib
- # some pkgs (like gcc_multi) put 32bit libs into and /lib 64bit libs into /lib64
- # by overwriting these we will hopefully catch all these cases
- # in the end /lib should only contain 32bit and /lib64 only 64bit libs
- cp -rsf ${staticUsrProfileTarget}/lib64/* lib64/
-
- # copy gcc libs (and may overwrite exitsting wrongly placed libs)
- cp -rsf ${choosenGcc.cc}/lib/* lib/
- cp -rsf ${choosenGcc.cc}/lib64/* lib64/
- '';
+ name = env.pname;
in stdenv.mkDerivation {
- name = "${name}-chrootenv";
+ name = "${name}-chrootenv";
+ preferLocalBuild = true;
buildCommand = ''
- mkdir -p "$out/sw"
- cd "$out/sw"
- ${setupTargetProfile}
- ${setupMultiProfile}
- cd ..
+ mkdir -p $out/bin
+ cd $out/bin
- mkdir -p bin
- cd bin
-
- sed -e "s|@chrootEnv@|$out|g" \
+ sed -e "s|@chrootEnv@|${env}|g" \
-e "s|@name@|${name}|g" \
-e "s|@shell@|${stdenv.shell}|g" \
${initSh} > init-${name}-chrootenv
@@ -202,18 +38,10 @@ in stdenv.mkDerivation {
${umountSh} > umount-${name}-chrootenv
chmod +x umount-${name}-chrootenv
- sed -e "s|@chrootEnv@|$out|g" \
+ sed -e "s|@chrootEnv@|${env}|g" \
-e "s|@shell@|${stdenv.shell}|g" \
-e "s|@name@|${name}|g" \
${destroySh} > destroy-${name}-chrootenv
chmod +x destroy-${name}-chrootenv
-
- cd ..
-
- cd "$out/sw"
- ${extraBuildCommands}
- cd "$out/sw"
- ${if isMultiBuild then extraBuildCommandsMulti else ""}
- cd ..
'';
}
diff --git a/pkgs/build-support/build-fhs-chrootenv/destroy.sh.in b/pkgs/build-support/build-fhs-chrootenv/destroy.sh.in
index 8ddf350913e..015f742d85a 100644
--- a/pkgs/build-support/build-fhs-chrootenv/destroy.sh.in
+++ b/pkgs/build-support/build-fhs-chrootenv/destroy.sh.in
@@ -6,9 +6,9 @@ chrootenvDest=/run/chrootenv/@name@
rmdir $chrootenvDest/{dev,nix/store,nix,proc,sys,host-etc,home,var,run,tmp}
# Remove symlinks to the software that should be part of the chroot system profile
-for i in @chrootEnv@/sw/*
+for i in @chrootEnv@/*
do
- if [ "$i" != "@chrootEnv@/sw/etc" ] && [ "$i" != "@chrootEnv@/sw/var" ]
+ if [ "$i" != "@chrootEnv@/etc" ] && [ "$i" != "@chrootEnv@/var" ]
then
rm $chrootenvDest/$(basename $i)
fi
diff --git a/pkgs/build-support/build-fhs-chrootenv/env.nix b/pkgs/build-support/build-fhs-chrootenv/env.nix
new file mode 100644
index 00000000000..b810adefab1
--- /dev/null
+++ b/pkgs/build-support/build-fhs-chrootenv/env.nix
@@ -0,0 +1,177 @@
+{ nixpkgs, nixpkgs_i686, system
+} :
+{ name, pkgs ? [], profile ? ""
+, targetPkgs ? null, multiPkgs ? null
+, extraBuildCommands ? "", extraBuildCommandsMulti ? ""
+}:
+
+assert pkgs != [] -> targetPkgs == null && multiPkgs == null;
+assert targetPkgs != null -> multiPkgs != null;
+assert multiPkgs != null -> targetPkgs != null;
+assert targetPkgs != null -> pkgs == [];
+
+
+# HOWTO:
+# If pkgs is defined buildFHSEnv will run in legacy mode. This means
+# it will build all pkgs contained in pkgs and basePkgs and then just merge
+# all of their contents together via buildEnv.
+#
+# The new way is to define both targetPkgs and multiPkgs. These two are
+# functions which get a pkgs environment supplied and should then return a list
+# of packages based this environment.
+# For example: targetPkgs = pkgs: [ pkgs.nmap ];
+#
+# All packages (most likely programs) placed in targetPkgs will only be
+# installed once--matching the hosts architecture (64bit on x86_64 and 32bit on
+# x86). These packages will populate the chroot directory tree.
+#
+# Packages (most likeley libraries) defined in multiPkgs will be installed once
+# on x86 systems and twice on x86_64 systems.
+# On x86 they will just be merge with the packages defined in targetPkgs.
+# On x86_64 they will be added to targetPkgs and in addition their 32bit
+# versions will also be installed. The final directory should look as follows:
+# /lib will include 32bit libraries from multiPkgs
+# /lib32 will link to /lib
+# /lib64 will include 64bit libraries from multiPkgs and targetPkgs
+# /x86 will contain a complete 32bit environment composed by multiPkgs
+
+let
+ is64Bit = system == "x86_64-linux";
+ # enable multi builds on x86_64 hosts if pakgs_target/multi are defined
+ isMultiBuild = is64Bit && targetPkgs != null;
+ isTargetBuild = !isMultiBuild;
+
+ # list of packages (usually programs) which will only be installed for the
+ # hosts architecture
+ targetPaths = if targetPkgs == null
+ then pkgs
+ else targetPkgs nixpkgs ++ multiPkgs nixpkgs;
+
+ # list of pckages which should be build for both x86 and x86_64 on x86_64
+ # systems
+ multiPaths = if isMultiBuild
+ then multiPkgs nixpkgs_i686
+ else [];
+
+ # base packages of the chroot
+ # these match the hosts architecture, gcc/glibc_multi will be choosen
+ # on multi builds
+ chosenGcc = if isMultiBuild then nixpkgs.gcc_multi else nixpkgs.gcc;
+ basePkgs = with nixpkgs;
+ [ (if isMultiBuild then glibc_multi else glibc)
+ chosenGcc
+ bashInteractive coreutils less shadow su
+ gawk diffutils findutils gnused gnugrep
+ gnutar gzip bzip2 xz
+ ];
+
+ # Compose a global profile for the chroot environment
+ profilePkg = nixpkgs.stdenv.mkDerivation {
+ name = "${name}-chrootenv-profile";
+ buildCommand = ''
+ mkdir -p $out/etc
+ cat >> $out/etc/profile << "EOF"
+ export PS1='${name}-chrootenv:\u@\h:\w\$ '
+ ${profile}
+ EOF
+ '';
+ };
+
+ # Composes a /usr like directory structure
+ staticUsrProfileTarget = nixpkgs.buildEnv {
+ name = "system-profile-target";
+ paths = basePkgs ++ [ profilePkg ] ++ targetPaths;
+ };
+
+ staticUsrProfileMulti = nixpkgs.buildEnv {
+ name = "system-profile-multi";
+ paths = multiPaths;
+ };
+
+ linkProfile = profile: ''
+ for i in ${profile}/{etc,bin,sbin,share,var}; do
+ if [ -x "$i" ]
+ then
+ ln -s "$i"
+ fi
+ done
+ '';
+
+ # the target profile is the actual profile that will be used for the chroot
+ setupTargetProfile = ''
+ ${linkProfile staticUsrProfileTarget}
+ ${setupLibDirs}
+
+ mkdir -m0755 usr
+ cd usr
+ ${linkProfile staticUsrProfileTarget}
+ ${setupLibDirs}
+ cd ..
+ '';
+
+ # this will happen on x86_64 host:
+ # /x86 -> links to the whole profile defined by multiPaths
+ # /lib, /lib32 -> links to 32bit binaries
+ # /lib64 -> links to 64bit binaries
+ # /usr/lib* -> same as above
+ setupMultiProfile = if isTargetBuild then "" else ''
+ mkdir -m0755 x86
+ cd x86
+ ${linkProfile staticUsrProfileMulti}
+ cd ..
+ '';
+
+ setupLibDirs = if isTargetBuild then setupLibDirs_target
+ else setupLibDirs_multi;
+
+ # setup library paths only for the targeted architecture
+ setupLibDirs_target = ''
+ mkdir -m0755 lib
+
+ # copy content of targetPaths
+ cp -rsf ${staticUsrProfileTarget}/lib/* lib/
+ '';
+
+ # setup /lib, /lib32 and /lib64
+ setupLibDirs_multi = ''
+ mkdir -m0755 lib
+ mkdir -m0755 lib64
+ ln -s lib lib32
+
+ # copy glibc stuff
+ cp -rsf ${staticUsrProfileTarget}/lib/32/* lib/
+
+ # copy content of multiPaths (32bit libs)
+ cp -rsf ${staticUsrProfileMulti}/lib/* lib/
+
+ # copy content of targetPaths (64bit libs)
+ cp -rsf ${staticUsrProfileTarget}/lib/* lib64/
+
+ # most 64bit only libs put their stuff into /lib
+ # some pkgs (like gcc_multi) put 32bit libs into and /lib 64bit libs into /lib64
+ # by overwriting these we will hopefully catch all these cases
+ # in the end /lib should only contain 32bit and /lib64 only 64bit libs
+ cp -rsf ${staticUsrProfileTarget}/lib64/* lib64/
+
+ # copy gcc libs (and may overwrite exitsting wrongly placed libs)
+ cp -rsf ${chosenGcc.cc}/lib/* lib/
+ cp -rsf ${chosenGcc.cc}/lib64/* lib64/
+ '';
+
+in nixpkgs.stdenv.mkDerivation {
+ name = "${name}-fhs";
+ buildCommand = ''
+ mkdir -p $out
+ cd $out
+ ${setupTargetProfile}
+ ${setupMultiProfile}
+ cd $out
+ ${extraBuildCommands}
+ cd $out
+ ${if isMultiBuild then extraBuildCommandsMulti else ""}
+ '';
+ preferLocalBuild = true;
+ passthru = {
+ pname = name;
+ };
+}
diff --git a/pkgs/build-support/build-fhs-chrootenv/init.sh.in b/pkgs/build-support/build-fhs-chrootenv/init.sh.in
index 079ec09d60f..f3bdad85fa7 100644
--- a/pkgs/build-support/build-fhs-chrootenv/init.sh.in
+++ b/pkgs/build-support/build-fhs-chrootenv/init.sh.in
@@ -6,9 +6,9 @@ chrootenvDest=/run/chrootenv/@name@
mkdir -p $chrootenvDest/{nix/store,dev,proc,sys,host-etc,home,var,run}
# Symlink the software that should be part of the chroot system profile
-for i in @chrootEnv@/sw/*
+for i in @chrootEnv@/*
do
- if [ "$i" != "@chrootEnv@/sw/etc" ] && [ "$i" != "@chrootEnv@/sw/var" ]
+ if [ "$i" != "@chrootEnv@/etc" ] && [ "$i" != "@chrootEnv@/var" ]
then
ln -s "$i" "$chrootenvDest"
fi
@@ -18,7 +18,7 @@ done
mkdir $chrootenvDest/etc
-for i in @chrootEnv@/sw/etc/*
+for i in @chrootEnv@/etc/*
do
ln -s "$i" $chrootenvDest/etc
done
diff --git a/pkgs/build-support/build-fhs-userenv/chroot-user.rb b/pkgs/build-support/build-fhs-userenv/chroot-user.rb
new file mode 100755
index 00000000000..857ccd58cd7
--- /dev/null
+++ b/pkgs/build-support/build-fhs-userenv/chroot-user.rb
@@ -0,0 +1,175 @@
+#!/usr/bin/env ruby
+
+# Bind mounts hierarchy: [from, to (relative)]
+# If 'to' is nil, path will be the same
+mounts = [ ['/nix/store', nil],
+ ['/dev', nil],
+ ['/proc', nil],
+ ['/sys', nil],
+ ['/etc', 'host-etc'],
+ ['/home', nil],
+ ['/var', nil],
+ ['/run', nil],
+ ['/root', nil],
+ ].map! { |x| [ x[0], x[1].nil? ? x[0].sub(/^\/*/, '') : x[1] ] }
+
+# Create directories
+mkdirs = ['tmp',
+ ]
+
+# Symlinks: [from, to (dir)]
+symlinks =
+ # /etc symlinks: [file name, prefix in host-etc]
+ [ ['passwd', ''],
+ ['group', ''],
+ ['shadow', ''],
+ ['hosts', ''],
+ ['resolv.conf', ''],
+ ['nsswitch.conf', ''],
+ ['pam.d', 'static'],
+ ['fonts/fonts.conf', 'static'],
+ ['fonts/conf.d/00-nixos.conf', 'static'],
+ ].map! { |x| [ "host-etc/#{x[1]}/#{x[0]}", "etc/#{File.dirname x[0]}" ] }
+
+require 'tmpdir'
+require 'fileutils'
+require 'pathname'
+require 'set'
+require 'fiddle'
+
+def write_file(path, str)
+ File.open(path, 'w') { |file| file.write str }
+end
+
+# Import C standard library and several needed calls
+$libc = Fiddle.dlopen nil
+
+def make_fcall(name, args, output)
+ c = Fiddle::Function.new $libc[name], args, output
+ lambda do |*args|
+ ret = c.call *args
+ raise SystemCallError.new Fiddle.last_error if ret < 0
+ return ret
+ end
+end
+
+$fork = make_fcall 'fork', [], Fiddle::TYPE_INT
+
+CLONE_NEWNS = 0x00020000
+CLONE_NEWUSER = 0x10000000
+$unshare = make_fcall 'unshare', [Fiddle::TYPE_INT], Fiddle::TYPE_INT
+
+MS_BIND = 0x1000
+MS_REC = 0x4000
+$mount = make_fcall 'mount', [Fiddle::TYPE_VOIDP,
+ Fiddle::TYPE_VOIDP,
+ Fiddle::TYPE_VOIDP,
+ Fiddle::TYPE_LONG,
+ Fiddle::TYPE_VOIDP],
+ Fiddle::TYPE_INT
+
+# Read command line args
+abort "Usage: chrootenv swdir program args..." unless ARGV.length >= 2
+swdir = Pathname.new ARGV[0]
+execp = ARGV.drop 1
+
+# Create temporary directory for root and chdir
+root = Dir.mktmpdir 'chrootenv'
+
+# Fork process; we need this to do a proper cleanup because
+# child process will chroot into temporary directory.
+# We use imported 'fork' instead of native to overcome
+# CRuby's meddling with threads; this should be safe because
+# we don't use threads at all.
+$cpid = $fork.call
+if $cpid == 0
+ # Save user UID and GID
+ uid = Process.uid
+ gid = Process.gid
+
+ # Create new mount and user namespaces
+ # CLONE_NEWUSER requires a program to be non-threaded, hence
+ # native fork above.
+ $unshare.call CLONE_NEWNS | CLONE_NEWUSER
+
+ # Map users and groups to the parent namespace
+ write_file '/proc/self/setgroups', 'deny'
+ write_file '/proc/self/uid_map', "#{uid} #{uid} 1"
+ write_file '/proc/self/gid_map', "#{gid} #{gid} 1"
+
+ # Do mkdirs
+ mkdirs.each { |x| FileUtils.mkdir_p x }
+
+ # Do rbind mounts.
+ mounts.each do |x|
+ to = "#{root}/#{x[1]}"
+ FileUtils.mkdir_p to
+ $mount.call x[0], to, nil, MS_BIND | MS_REC, nil
+ end
+
+ # Chroot!
+ Dir.chroot root
+ Dir.chdir '/'
+
+ # Do symlinks
+ symlinks.each do |x|
+ FileUtils.mkdir_p x[1]
+ FileUtils.ln_s x[0], x[1]
+ end
+
+ # Symlink swdir hierarchy
+ mount_dirs = Set.new mounts.map { |x| Pathname.new x[1] }
+ link_swdir = lambda do |swdir, prefix|
+ swdir.find do |path|
+ rel = prefix.join path.relative_path_from(swdir)
+ # Don't symlink anything in binded or symlinked directories
+ Find.prune if mount_dirs.include? rel or rel.symlink?
+ if not rel.directory?
+ # File does not exist; make a symlink and bail out
+ rel.make_symlink path
+ Find.prune
+ end
+ # Recursively follow symlinks
+ link_swdir.call path.readlink, rel if path.symlink?
+ end
+ end
+ link_swdir.call swdir, Pathname.new('')
+
+ # New environment
+ oldenv = ENV.to_h
+ ENV.replace({ 'PS1' => oldenv['PS1'],
+ 'TERM' => oldenv['TERM'],
+ 'DISPLAY' => oldenv['DISPLAY'],
+ 'HOME' => oldenv['HOME'],
+ 'PATH' => '/bin:/sbin',
+ 'XDG_RUNTIME_DIR' => oldenv['XDG_RUNTIME_DIR'],
+ })
+
+ # Finally, exec!
+ exec *execp
+end
+
+# Wait for a child. If we catch a signal, resend it to child and continue
+# waiting.
+def wait_child
+ begin
+ Process.wait
+
+ # Return child's exit code
+ if $?.exited?
+ exit $?.exitstatus
+ else
+ exit 1
+ end
+ rescue SignalException => e
+ Process.kill e.signo, $cpid
+ wait_child
+ end
+end
+
+begin
+ wait_child
+ensure
+ # Cleanup
+ FileUtils.rm_rf root, secure: true
+end
diff --git a/pkgs/build-support/build-fhs-userenv/default.nix b/pkgs/build-support/build-fhs-userenv/default.nix
new file mode 100644
index 00000000000..57864b4934b
--- /dev/null
+++ b/pkgs/build-support/build-fhs-userenv/default.nix
@@ -0,0 +1,37 @@
+{ writeTextFile, stdenv, ruby } : { env, runScript } :
+
+let
+ name = env.pname;
+
+ # Sandboxing script
+ chroot-user = writeTextFile {
+ name = "chroot-user";
+ executable = true;
+ destination = "/bin/chroot-user";
+ text = ''
+ #! ${ruby}/bin/ruby
+ ${builtins.readFile ./chroot-user.rb}
+ '';
+ };
+
+in stdenv.mkDerivation {
+ name = "${name}-userenv";
+ buildInputs = [ ruby ];
+ preferLocalBuild = true;
+ buildCommand = ''
+ mkdir -p $out/bin
+ cat > $out/bin/${name} < $out/libexec/run <&2 "'steamChrootEnv' is replaced with 'steam' now"
+ echo >&2 "You now need just to run 'steam' without root rights"
+ '';
+ });
stuntrally = callPackage ../games/stuntrally { };