Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6016bcd30e | ||
|
|
1b6ee8f4c7 | ||
|
|
cd49ee0897 | ||
|
|
1e2c7c04b1 | ||
|
|
6cc2a8f8ed | ||
|
|
bfee9a2581 | ||
|
|
f9ff67e948 | ||
|
|
6b05f688ee | ||
|
|
5c06e5297d | ||
|
|
24a356bf71 | ||
|
|
24e063efdc | ||
|
|
ca70fba0bf | ||
|
|
5959c591a0 | ||
|
|
c5f9d0d080 | ||
|
|
75e12b8e66 | ||
|
|
f0576d6775 | ||
|
|
ff08306746 | ||
|
|
bc2e43f3c8 | ||
|
|
039d5a023f | ||
|
|
b09b87321c | ||
|
|
107505e13a | ||
|
|
72d8209548 | ||
|
|
18a48d80a0 | ||
|
|
1b3a03f161 | ||
|
|
229567293c | ||
|
|
a4cb62ac25 | ||
|
|
31a551a60f | ||
|
|
6e85d1b5ba | ||
|
|
3a0cc43ac8 | ||
|
|
be0b9dda31 | ||
|
|
78206f06ec | ||
|
|
2ee9da9e22 | ||
|
|
6eba05613a | ||
|
|
7a03cbf09d | ||
|
|
69b8f9980f | ||
|
|
e93acab852 | ||
|
|
806970349b | ||
|
|
ea019e9a26 | ||
|
|
cc837e2458 | ||
|
|
28034bfa49 | ||
|
|
93f4fa8a15 | ||
|
|
c51b6a893c | ||
|
|
e6c44d166a | ||
|
|
7391533ea5 | ||
|
|
5cc2fc46ec | ||
|
|
3628b61ce0 | ||
|
|
470553bd05 | ||
|
|
4ddd077bfa | ||
|
|
0374d94437 | ||
|
|
00f698eb8b | ||
|
|
938092a213 | ||
|
|
772b70952f | ||
|
|
f9974f856e | ||
|
|
6955d41f2b | ||
|
|
934cf2d1f4 | ||
|
|
08d96ffad0 | ||
|
|
a9b4e26b5c | ||
|
|
c642441beb | ||
|
|
05420e7883 | ||
|
|
258897c265 | ||
|
|
239841787b | ||
|
|
cc63db1dd5 | ||
|
|
2c9cf50746 | ||
|
|
c3fc60d936 | ||
|
|
f72ed36025 | ||
|
|
78d777ca15 | ||
|
|
a68ebf8e37 | ||
|
|
804709706c | ||
|
|
c56bc3d81c | ||
|
|
4b07476848 | ||
|
|
bdd4646338 | ||
|
|
e73d9e9488 | ||
|
|
28bba8c44f | ||
|
|
8057a192e3 | ||
|
|
9fa1bee575 | ||
|
|
f45c731cd7 | ||
|
|
88936411bc | ||
|
|
0111ba98ea | ||
|
|
b008674e46 | ||
|
|
826dc0d07d | ||
|
|
97c6009c47 | ||
|
|
ca9c02dff1 | ||
|
|
5526a282b5 | ||
|
|
dadf7a5b46 | ||
|
|
3e067ac11c | ||
|
|
5ea138dc4b | ||
|
|
79a3ba7fa3 | ||
|
|
5f18cd2e84 | ||
|
|
52172607cf | ||
|
|
01a5ea9914 | ||
|
|
8add116acd | ||
|
|
d6143801fd | ||
|
|
9842077cb2 | ||
|
|
5e9c3da412 | ||
|
|
99ed558a93 | ||
|
|
1943b60ad8 | ||
|
|
96fbbbde55 | ||
|
|
d6fd6d8aff | ||
|
|
536c85ea49 | ||
|
|
66fa9e6a4d | ||
|
|
e42df686f3 | ||
|
|
92926be2fe | ||
|
|
b424d29d1b | ||
|
|
3007f57377 | ||
|
|
def5160b61 | ||
|
|
0a4e90395c | ||
|
|
f12492c66d | ||
|
|
aebea2e489 | ||
|
|
649bb60617 | ||
|
|
42d6f640c1 | ||
|
|
299141ecbd | ||
|
|
1b3a78a459 | ||
|
|
82248abd8f | ||
|
|
12f9129f60 | ||
|
|
b7629778ef | ||
|
|
68dcbb187e | ||
|
|
5ee7d8fbab | ||
|
|
2754a07ead | ||
|
|
06f62defe6 | ||
|
|
9c29a2ed35 | ||
|
|
e775d4d84f | ||
|
|
fbf0b2fa45 | ||
|
|
228ea7c2f9 | ||
|
|
cc511fd65b | ||
|
|
a9045c727f | ||
|
|
9fa12fc201 | ||
|
|
3ad53e43c8 | ||
|
|
772778c0ec | ||
|
|
e087bfef5f |
+1
-1
@@ -106,6 +106,7 @@ AC_LANG_POP(C++)
|
||||
# Check for chroot support (requires chroot() and bind mounts).
|
||||
AC_CHECK_FUNCS([chroot])
|
||||
AC_CHECK_FUNCS([unshare])
|
||||
AC_CHECK_FUNCS([statvfs])
|
||||
AC_CHECK_HEADERS([sched.h])
|
||||
AC_CHECK_HEADERS([sys/param.h])
|
||||
AC_CHECK_HEADERS([sys/mount.h], [], [],
|
||||
@@ -354,7 +355,6 @@ fi
|
||||
AC_SUBST(tarFlags)
|
||||
|
||||
|
||||
|
||||
AM_CONFIG_HEADER([config.h])
|
||||
AC_CONFIG_FILES([Makefile
|
||||
src/Makefile
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
with import <nix/config.nix>;
|
||||
|
||||
{system ? builtins.currentSystem, url, outputHash ? "", outputHashAlgo ? "", md5 ? "", sha1 ? "", sha256 ? ""}:
|
||||
{system ? builtins.currentSystem, url, outputHash ? "", outputHashAlgo ? "", md5 ? "", sha1 ? "", sha256 ? "", executable ? false}:
|
||||
|
||||
assert (outputHash != "" && outputHashAlgo != "")
|
||||
|| md5 != "" || sha1 != "" || sha256 != "";
|
||||
@@ -8,10 +8,10 @@ assert (outputHash != "" && outputHashAlgo != "")
|
||||
let
|
||||
|
||||
builder = builtins.toFile "fetchurl.sh"
|
||||
''
|
||||
(''
|
||||
echo "downloading $url into $out"
|
||||
${curl} --fail --location --max-redirs 20 --insecure "$url" > "$out"
|
||||
'';
|
||||
'' + (if executable then "${coreutils}/chmod +x $out" else ""));
|
||||
|
||||
in
|
||||
|
||||
@@ -25,6 +25,7 @@ derivation {
|
||||
if sha256 != "" then "sha256" else if sha1 != "" then "sha1" else "md5";
|
||||
outputHash = if outputHash != "" then outputHash else
|
||||
if sha256 != "" then sha256 else if sha1 != "" then sha1 else md5;
|
||||
outputHashMode = if executable then "recursive" else "flat";
|
||||
|
||||
inherit system url;
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
To produce a `stable' release from the trunk:
|
||||
|
||||
-1. Update the release notes; make sure that the release date is
|
||||
correct.
|
||||
|
||||
0. Make sure that the trunk builds in the release supervisor.
|
||||
|
||||
1. Branch the trunk, e.g., `svn cp .../trunk
|
||||
.../branches/0.5-release'.
|
||||
|
||||
2. Switch to the branch, e.g., `svn switch .../branches/0.5-release'.
|
||||
|
||||
3. In `configure.ac', change `STABLE=0' into `STABLE=1' and commit.
|
||||
|
||||
4. In the release supervisor, add a one-time job to build
|
||||
`.../branches/0.5-release'.
|
||||
|
||||
5. Make sure that the release succeeds.
|
||||
|
||||
6. Move the branch to a tag, e.g., `svn mv .../branches/0.5-release
|
||||
.../tags/0.5'.
|
||||
|
||||
Note that the branch should not be used for maintenance; it should
|
||||
be deleted after the release has been created. A maintenance
|
||||
branch (e.g., `.../branches/0.5') should be created from the
|
||||
original revision of the trunk (since maintenance releases should
|
||||
also be tested first; hence, we cannot have `STABLE=1'). The same
|
||||
procedure can then be followed to produce maintenance releases;
|
||||
just substitute `.../branches/VERSION' for the trunk.
|
||||
|
||||
7. Switch back to the trunk.
|
||||
|
||||
8. Bump the version number in `configure.ac' (in AC_INIT).
|
||||
@@ -302,6 +302,18 @@ stdenv.mkDerivation {
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.hashString</function>
|
||||
<replaceable>type</replaceable> <replaceable>s</replaceable></term>
|
||||
|
||||
<listitem><para>Return a base-16 representation of the
|
||||
cryptographic hash of string <replaceable>s</replaceable>. The
|
||||
hash algorithm specified by <replaceable>type</replaceable> must
|
||||
be one of <literal>"md5"</literal>, <literal>"sha1"</literal> or
|
||||
<literal>"sha256"</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><function>builtins.head</function>
|
||||
<replaceable>list</replaceable></term>
|
||||
|
||||
|
||||
+51
-44
@@ -33,18 +33,18 @@ env-keep-derivations = false
|
||||
<para>You can override settings using the <option>--option</option>
|
||||
flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
|
||||
<para>The following settings are currently available:
|
||||
<para>The following settings are currently available:
|
||||
|
||||
<variablelist>
|
||||
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-gc-keep-outputs"><term><literal>gc-keep-outputs</literal></term>
|
||||
|
||||
<listitem><para>If <literal>true</literal>, the garbage collector
|
||||
will keep the outputs of non-garbage derivations. If
|
||||
<literal>false</literal> (default), outputs will be deleted unless
|
||||
they are GC roots themselves (or reachable from other roots).</para>
|
||||
|
||||
|
||||
<para>In general, outputs must be registered as roots separately.
|
||||
However, even if the output of a derivation is registered as a
|
||||
root, the collector will still delete store paths that are used
|
||||
@@ -53,7 +53,7 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
this option to <literal>true</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-gc-keep-derivations"><term><literal>gc-keep-derivations</literal></term>
|
||||
|
||||
@@ -71,7 +71,7 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><literal>env-keep-derivations</literal></term>
|
||||
|
||||
<listitem><para>If <literal>false</literal> (default), derivations
|
||||
@@ -95,7 +95,7 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-max-jobs"><term><literal>build-max-jobs</literal></term>
|
||||
|
||||
<listitem><para>This option defines the maximum number of jobs
|
||||
@@ -234,7 +234,27 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-chroot-dirs"><term><literal>build-chroot-dirs</literal></term>
|
||||
|
||||
<listitem><para>When builds are performed in a chroot environment,
|
||||
Nix will mount some directories from the normal file system
|
||||
hierarchy inside the chroot. These are the Nix store, the
|
||||
temporary build directory (usually
|
||||
<filename>/tmp/nix-build-<replaceable>drvname</replaceable>-<replaceable>number</replaceable></filename>),
|
||||
the <literal>/proc</literal> filesystem, and the directories
|
||||
listed here. The default is <literal>/dev /dev/pts</literal>,
|
||||
since these contain files needed by many builds (such as
|
||||
<filename>/dev/null</filename>). You can use the syntax
|
||||
<literal><replaceable>target</replaceable>=<replaceable>source</replaceable></literal>
|
||||
to mount a path in a different location in the chroot; for
|
||||
instance, <literal>/bin=/nix-bin</literal> will mount the
|
||||
directory <literal>/nix-bin</literal> as <literal>/bin</literal>
|
||||
inside the chroot.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>build-use-substitutes</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal> (default), Nix
|
||||
@@ -243,7 +263,7 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><literal>build-fallback</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal>, Nix will fall
|
||||
@@ -253,34 +273,7 @@ flag, e.g. <literal>--option gc-keep-outputs false</literal>.</para>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry xml:id="conf-build-chroot-dirs"><term><literal>build-chroot-dirs</literal></term>
|
||||
|
||||
<listitem><para>When builds are performed in a chroot environment,
|
||||
Nix will mount (using <command>mount --bind</command> on Linux)
|
||||
some directories from the normal file system hierarchy inside the
|
||||
chroot. These are the Nix store, the temporary build directory
|
||||
(usually
|
||||
<filename>/tmp/nix-<replaceable>pid</replaceable>-<replaceable>number</replaceable></filename>)
|
||||
and the directories listed here. The default is <literal>dev
|
||||
/proc</literal>. Files in <filename>/dev</filename> (such as
|
||||
<filename>/dev/null</filename>) are needed by many builds, and
|
||||
some files in <filename>/proc</filename> may also be needed
|
||||
occasionally.</para>
|
||||
|
||||
<para>The value used on NixOS is
|
||||
|
||||
<programlisting>
|
||||
build-use-chroot = /dev /proc /bin</programlisting>
|
||||
|
||||
to make the <filename>/bin/sh</filename> symlink available (which
|
||||
is still needed by many builders).</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>build-cache-failures</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal>, Nix will
|
||||
@@ -331,8 +324,8 @@ build-use-chroot = /dev /proc /bin</programlisting>
|
||||
<varlistentry><term><literal>binary-caches</literal></term>
|
||||
|
||||
<listitem><para>A list of URLs of binary caches, separated by
|
||||
whitespace. The default is empty.<!-- The default is
|
||||
<literal>http://nixos.org/binary-cache</literal>. --></para></listitem>
|
||||
whitespace. The default is
|
||||
<literal>http://cache.nixos.org</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -341,10 +334,12 @@ build-use-chroot = /dev /proc /bin</programlisting>
|
||||
|
||||
<listitem><para>A list of names of files that will be read to
|
||||
obtain additional binary cache URLs. The default is
|
||||
<literal>/nix/var/nix/profiles/per-user/root/channels/binary-caches/*</literal>,
|
||||
which ensures that Nix will use the binary caches corresponding to
|
||||
the channels installed by root. Do not set this option to read
|
||||
files created by untrusted users!</para></listitem>
|
||||
<literal>/nix/var/nix/profiles/per-user/<replaceable>username</replaceable>/channels/binary-caches/*</literal>.
|
||||
Note that when you’re using the Nix daemon,
|
||||
<replaceable>username</replaceable> is always equal to
|
||||
<literal>root</literal>, so Nix will only use the binary caches
|
||||
provided by the channels installed by root. Do not set this
|
||||
option to read files created by untrusted users!</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -355,13 +350,25 @@ build-use-chroot = /dev /proc /bin</programlisting>
|
||||
whitespace. These are not used by default, but can be enabled by
|
||||
users of the Nix daemon by specifying <literal>--option
|
||||
binary-caches <replaceable>urls</replaceable></literal> on the
|
||||
command line. Daemon users are only allowed to pass a subset of
|
||||
the URLs listed in <literal>binary-caches</literal> and
|
||||
command line. Unprivileged users are only allowed to pass a
|
||||
subset of the URLs listed in <literal>binary-caches</literal> and
|
||||
<literal>trusted-binary-caches</literal>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>extra-binary-caches</literal></term>
|
||||
|
||||
<listitem><para>Additional binary caches appended to those
|
||||
specified in <option>binary-caches</option> and
|
||||
<option>binary-caches-files</option>. When used by unprivileged
|
||||
users, untrusted binary caches (i.e. those not listed in
|
||||
<option>trusted-binary-caches</option>) are silently
|
||||
ignored.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
<varlistentry><term><literal>binary-caches-parallel-connections</literal></term>
|
||||
|
||||
<listitem><para>The maximum number of parallel HTTP connections
|
||||
@@ -415,7 +422,7 @@ build-use-chroot = /dev /proc /bin</programlisting>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
|
||||
<varlistentry><term><literal>auto-optimise-store</literal></term>
|
||||
|
||||
<listitem><para>If set to <literal>true</literal> (the default),
|
||||
|
||||
@@ -283,7 +283,7 @@ $ mount -o bind /mnt/otherdisk/nix /nix</screen>
|
||||
|
||||
<listitem><para>This variable should be set to
|
||||
<literal>daemon</literal> if you want to use the Nix daemon to
|
||||
executed Nix operations, which is necessary in <link
|
||||
execute Nix operations. This is necessary in <link
|
||||
linkend="ssec-multi-user">multi-user Nix installations</link>.
|
||||
Otherwise, it should be left unset.</para></listitem>
|
||||
|
||||
|
||||
@@ -166,11 +166,10 @@ a source distribution.</para>
|
||||
|
||||
<listitem><para>Recent versions of Bison and Flex to build the
|
||||
parser. (This is because Nix needs GLR support in Bison and
|
||||
reentrancy support in Flex.) For Bison, you need version 2.3 or
|
||||
higher (1.875 does <emphasis>not</emphasis> work), which can be
|
||||
obtained from the <link
|
||||
reentrancy support in Flex.) For Bison, you need version 2.6, which
|
||||
can be obtained from the <link
|
||||
xlink:href="ftp://alpha.gnu.org/pub/gnu/bison">GNU FTP
|
||||
server</link>. For Flex, you need version 2.5.33, which is
|
||||
server</link>. For Flex, you need version 2.5.35, which is
|
||||
available on <link
|
||||
xlink:href="http://lex.sourceforge.net/">SourceForge</link>.
|
||||
Slightly older versions may also work, but ancient versions like the
|
||||
@@ -539,7 +538,7 @@ a symbolic link to the current <emphasis>user environment</emphasis>
|
||||
installed packages). The simplest way to set the required environment
|
||||
variables is to include the file
|
||||
<filename><replaceable>prefix</replaceable>/etc/profile.d/nix.sh</filename>
|
||||
in your <filename>~/.bashrc</filename> (or similar), like this:</para>
|
||||
in your <filename>~/.profile</filename> (or similar), like this:</para>
|
||||
|
||||
<screen>
|
||||
source <replaceable>prefix</replaceable>/etc/profile.d/nix.sh</screen>
|
||||
|
||||
+13
-4
@@ -93,11 +93,10 @@ also <xref linkend="sec-common-options" />.</phrase></para>
|
||||
|
||||
<listitem><para>Specifies the profile to be used by those
|
||||
operations that operate on a profile (designated below as the
|
||||
<emphasis>active profile</emphasis>). A profile is sequence of
|
||||
<emphasis>active profile</emphasis>). A profile is a sequence of
|
||||
user environments called <emphasis>generations</emphasis>, one of
|
||||
which is the <emphasis>current generation</emphasis>. The default
|
||||
profile is the target of the symbolic link
|
||||
<filename>~/.nix-profile</filename> (see below).</para></listitem>
|
||||
which is the <emphasis>current
|
||||
generation</emphasis>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
@@ -1256,6 +1255,16 @@ error: no generation older than the current (91) exists</screen>
|
||||
<refsection condition="manpage"><title>Environment variables</title>
|
||||
|
||||
<variablelist>
|
||||
|
||||
<varlistentry><term><envar>NIX_PROFILE</envar></term>
|
||||
|
||||
<listitem><para>Location of the Nix profile. Defaults to the
|
||||
target of the symlink <filename>~/.nix-profile</filename>, if it
|
||||
exists, or <filename>/nix/var/nix/profiles/default</filename>
|
||||
otherwise.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
<xi:include href="env-common.xml#xmlns(db=http://docbook.org/ns/docbook)xpointer(//db:variablelist[@xml:id='env-common']/*)" />
|
||||
</variablelist>
|
||||
|
||||
|
||||
@@ -252,7 +252,7 @@ The properties that are currently supported are:
|
||||
<listitem><para>Each binary cache has a priority (defaulting to
|
||||
50). Binary caches are checked for binaries in order of ascending
|
||||
priority; thus a higher number denotes a lower priority. The
|
||||
binary cache <uri>http://nixos.org/binary-cache</uri> has priority
|
||||
binary cache <uri>http://cache.nixos.org</uri> has priority
|
||||
40.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
@@ -270,14 +270,14 @@ URL <replaceable>url</replaceable> has a binary for
|
||||
<replaceable>p</replaceable>, Nix fetches
|
||||
<replaceable>url/h</replaceable>, where <replaceable>h</replaceable>
|
||||
is the hash part of <replaceable>p</replaceable>. Thus, if we have a
|
||||
cache <uri>http://nixos.org/binary-cache</uri> and we want to obtain
|
||||
cache <uri>http://cache.nixos.org</uri> and we want to obtain
|
||||
the store path
|
||||
<screen>
|
||||
/nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7
|
||||
</screen>
|
||||
then Nix will attempt to fetch
|
||||
<screen>
|
||||
http://nixos.org/binary-cache/a8922c0h87iilxzzvwn2hmv8x210aqb9.narinfo
|
||||
http://cache.nixos.org/a8922c0h87iilxzzvwn2hmv8x210aqb9.narinfo
|
||||
</screen>
|
||||
(Commands such as <command>nix-env -qas</command> will issue an HTTP
|
||||
HEAD request, since it only needs to know if the
|
||||
@@ -381,7 +381,7 @@ The fields are as follows:
|
||||
references exist (e.g.,
|
||||
<filename>/nix/store/2ma2k0ys8knh4an48n28vigcmc2z8773-linux-headers-2.6.23.16</filename>),
|
||||
Nix will fetch <screen>
|
||||
http://nixos.org/binary-cache/nar/0zzjpdz46mdn74v09m053yczlz4am038g8r74iy8w43gx8801h70.nar.bz2
|
||||
http://cache.nixos.org/nar/0zzjpdz46mdn74v09m053yczlz4am038g8r74iy8w43gx8801h70.nar.bz2
|
||||
</screen> and decompress and unpack it to
|
||||
<filename>/nix/store/a8922c0h87iilxzzvwn2hmv8x210aqb9-glibc-2.7</filename>.</para>
|
||||
|
||||
|
||||
@@ -852,7 +852,7 @@ in Nix itself.</para>
|
||||
|
||||
<refsection><title>Description</title>
|
||||
|
||||
<para>The operation <option>--verify-paths</option> compares the
|
||||
<para>The operation <option>--verify-path</option> compares the
|
||||
contents of the given store paths to their cryptographic hashes stored
|
||||
in Nix’s database. For every changed path, it prints a warning
|
||||
message. The exit status is 0 if no path has changed, and 1
|
||||
|
||||
@@ -343,10 +343,11 @@
|
||||
|
||||
<varlistentry><term><option>-I</option> <replaceable>path</replaceable></term>
|
||||
|
||||
<listitem><para>Add a path to the Nix expression search path. See
|
||||
the <envar>NIX_PATH</envar> environment variable for details. Paths
|
||||
added through <option>-I</option> take precedence over
|
||||
<envar>NIX_PATH</envar>.</para></listitem>
|
||||
<listitem><para>Add a path to the Nix expression search path. This
|
||||
option may be given multiple times. See the <envar>NIX_PATH</envar>
|
||||
environment variable for information on the semantics of the Nix
|
||||
search path. Paths added through <option>-I</option> take
|
||||
precedence over <envar>NIX_PATH</envar>.</para></listitem>
|
||||
|
||||
</varlistentry>
|
||||
|
||||
|
||||
@@ -538,7 +538,7 @@ dependencies) to a file, and then unpack that file into another Nix
|
||||
store. For example,
|
||||
|
||||
<screen>
|
||||
$ nix-store --export $(type -p firefox) > firefox.closure</screen>
|
||||
$ nix-store --export $(nix-store -qR $(type -p firefox)) > firefox.closure</screen>
|
||||
|
||||
writes the closure of Firefox to a file. You can then copy this file
|
||||
to another machine and install the closure:
|
||||
@@ -552,7 +552,7 @@ another command, e.g. to copy and install a closure directly to/on
|
||||
another machine:
|
||||
|
||||
<screen>
|
||||
$ nix-store --export $(type -p firefox) | bzip2 | \
|
||||
$ nix-store --export $(nix-store -qR $(type -p firefox)) | bzip2 | \
|
||||
ssh alice@itchy.example.org "bunzip2 | nix-store --import"</screen>
|
||||
|
||||
But note that <command>nix-copy-closure</command> is generally more
|
||||
@@ -583,7 +583,7 @@ $ nix-env -i firefox</screen>
|
||||
and Nix has to build a path that it sees is already present in
|
||||
<filename>/mnt/nix</filename>, then it will just copy from there
|
||||
instead of building it from source.</para>
|
||||
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
@@ -14,9 +14,10 @@ to the following chapters.</para>
|
||||
<listitem><para>Download a source tarball or RPM or Debian/Ubuntu
|
||||
package from <link xlink:href='http://nixos.org/'/>. Build source
|
||||
distributions using the regular sequence:
|
||||
|
||||
|
||||
<screen>
|
||||
$ tar xvfj nix-<replaceable>version</replaceable>.tar.bz2
|
||||
$ cd nix-<replaceable>version</replaceable>
|
||||
$ ./configure
|
||||
$ make
|
||||
$ make install <lineannotation>(as root)</lineannotation></screen>
|
||||
@@ -35,7 +36,7 @@ changed using
|
||||
|
||||
<listitem><para>You should add
|
||||
<filename><replaceable>prefix</replaceable>/etc/profile.d/nix.sh</filename>
|
||||
to your <filename>~/.bashrc</filename> (or some other login
|
||||
to your <filename>~/.profile</filename> (or some other login
|
||||
file).</para></listitem>
|
||||
|
||||
<listitem><para>Subscribe to the Nix Packages channel.
|
||||
@@ -69,7 +70,7 @@ libxslt-1.1.0
|
||||
</para></listitem>
|
||||
|
||||
<listitem><para>Install some packages from the channel:
|
||||
|
||||
|
||||
<screen>
|
||||
$ nix-env -i hello firefox <replaceable>...</replaceable> </screen>
|
||||
|
||||
@@ -87,7 +88,7 @@ $ firefox
|
||||
<lineannotation>(read Slashdot or something)</lineannotation></screen>
|
||||
|
||||
</para></listitem>
|
||||
|
||||
|
||||
<listitem><para>Uninstall a package:
|
||||
|
||||
<screen>
|
||||
|
||||
@@ -6,6 +6,138 @@
|
||||
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.5.3"><title>Release 1.5.3 (June 17, 2013)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. The following changes are
|
||||
noteworthy:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem>
|
||||
<para>Yet another security bug involving hard links to files
|
||||
outside the store was fixed. This bug only affected multi-user
|
||||
installations that do not have hard link restrictions
|
||||
enabled. (NixOS is thus not vulnerable.)</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The default binary cache URL has changed from
|
||||
<uri>http://nixos.org/binary-cache</uri> to
|
||||
<uri>http://cache.nixos.org</uri>. The latter is hosted on Amazon
|
||||
CloudFront (courtesy of <link
|
||||
xlink:href="http://www.logicblox.com/">LogicBlox</link>) and
|
||||
should provide better performance for users in both Europe and
|
||||
North America.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>The binary cache substituter now prints a warning message if
|
||||
fetching information from the cache takes more than five seconds.
|
||||
Thus network or server problems no longer make Nix appear to just
|
||||
hang.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Stack traces now show function names, e.g.
|
||||
<screen>
|
||||
while evaluating `concatMapStrings' at `<replaceable>...</replaceable>/nixpkgs/pkgs/lib/strings.nix:18:25':
|
||||
</screen>
|
||||
Also, if a function is called with an unexpected argument, Nix
|
||||
now shows the name of the argument.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.5.2"><title>Release 1.5.2 (May 13, 2013)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. It has contributions from
|
||||
Eelco Dolstra, Lluís Batlle i Rossell and Shea Levy.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.5.1"><title>Release 1.5.1 (February 28, 2013)</title>
|
||||
|
||||
<para>The bug fix to the bug fix had a bug itself, of course. But
|
||||
this time it will work for sure!</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.5"><title>Release 1.5 (February 27, 2013)</title>
|
||||
|
||||
<para>This is a brown paper bag release to fix a regression introduced
|
||||
by the hard link security fix in 1.4.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.4"><title>Release 1.4 (February 26, 2013)</title>
|
||||
|
||||
<para>This release fixes a security bug in multi-user operation. It
|
||||
was possible for derivations to cause the mode of files outside of the
|
||||
Nix store to be changed to 444 (read-only but world-readable) by
|
||||
creating hard links to those files (<link
|
||||
xlink:href="https://github.com/NixOS/nix/commit/5526a282b5b44e9296e61e07d7d2626a79141ac4">details</link>).</para>
|
||||
|
||||
<para>There are also the following improvements:</para>
|
||||
|
||||
<itemizedlist>
|
||||
|
||||
<listitem><para>New built-in function:
|
||||
<function>builtins.hashString</function>.</para></listitem>
|
||||
|
||||
<listitem><para>Build logs are now stored in
|
||||
<filename>/nix/var/log/nix/drvs/<replaceable>XX</replaceable>/</filename>,
|
||||
where <replaceable>XX</replaceable> is the first two characters of
|
||||
the derivation. This is useful on machines that keep a lot of build
|
||||
logs (such as Hydra servers).</para></listitem>
|
||||
|
||||
<listitem><para>The function <function>corepkgs/fetchurl</function>
|
||||
can now make the downloaded file executable. This will allow
|
||||
getting rid of all bootstrap binaries in the Nixpkgs source
|
||||
tree.</para></listitem>
|
||||
|
||||
<listitem><para>Language change: The expression <literal>"${./path}
|
||||
..."</literal> now evaluates to a string instead of a
|
||||
path.</para></listitem>
|
||||
|
||||
</itemizedlist>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.3"><title>Release 1.3 (January 4, 2013)</title>
|
||||
|
||||
<para>This is primarily a bug fix release. When this version is first
|
||||
run on Linux, it removes any immutable bits from the Nix store and
|
||||
increases the schema version of the Nix store. (The previous release
|
||||
removed support for setting the immutable bit; this release clears any
|
||||
remaining immutable bits to make certain operations more
|
||||
efficient.)</para>
|
||||
|
||||
<para>This release has contributions from Eelco Dolstra and Stuart
|
||||
Pernsteiner.</para>
|
||||
|
||||
</section>
|
||||
|
||||
|
||||
<!--==================================================================-->
|
||||
|
||||
<section xml:id="ssec-relnotes-1.2"><title>Release 1.2 (December 6, 2012)</title>
|
||||
@@ -23,10 +155,10 @@
|
||||
configuration setting <option>binary-caches</option> contains a
|
||||
list of URLs of binary caches. For instance, doing
|
||||
<screen>
|
||||
$ nix-env -i thunderbird --option binary-caches http://nixos.org/binary-cache
|
||||
$ nix-env -i thunderbird --option binary-caches http://cache.nixos.org
|
||||
</screen>
|
||||
will install Thunderbird and its dependencies, using the available
|
||||
pre-built binaries in <uri>http://nixos.org/binary-cache</uri>.
|
||||
pre-built binaries in <uri>http://cache.nixos.org</uri>.
|
||||
The main advantage over the old “manifest”-based method of getting
|
||||
pre-built binaries is that you don’t have to worry about your
|
||||
manifest being in sync with the Nix expressions you’re installing
|
||||
@@ -39,7 +171,7 @@ $ nix-env -i thunderbird --option binary-caches http://nixos.org/binary-cache
|
||||
used automatically if you subscribe to that channel. If you use
|
||||
the Nixpkgs or NixOS channels
|
||||
(<uri>http://nixos.org/channels</uri>) you automatically get the
|
||||
cache <uri>http://nixos.org/binary-cache</uri>.</para>
|
||||
cache <uri>http://cache.nixos.org</uri>.</para>
|
||||
|
||||
<para>Binary caches are created using <command>nix-push</command>.
|
||||
For details on the operation and format of binary caches, see the
|
||||
|
||||
@@ -3,16 +3,13 @@ package Nix::CopyClosure;
|
||||
use strict;
|
||||
use Nix::Config;
|
||||
use Nix::Store;
|
||||
use List::Util qw(sum);
|
||||
|
||||
|
||||
sub copyTo {
|
||||
my ($sshHost, $sshOpts, $storePaths, $compressor, $decompressor,
|
||||
$includeOutputs, $dryRun, $sign, $progressViewer, $useSubstitutes) = @_;
|
||||
|
||||
$compressor = "$compressor |" if $compressor ne "";
|
||||
$decompressor = "$decompressor |" if $decompressor ne "";
|
||||
$progressViewer = "$progressViewer |" if $progressViewer ne "";
|
||||
|
||||
# Get the closure of this path.
|
||||
my @closure = reverse(topoSortPaths(computeFSClosure(0, $includeOutputs,
|
||||
map { followLinksToStorePath $_ } @{$storePaths})));
|
||||
@@ -28,21 +25,28 @@ sub copyTo {
|
||||
# we'll want to use ‘--from-stdin’, but we can't rely on the
|
||||
# target having this option yet.
|
||||
my @missing = ();
|
||||
my $missingSize = 0;
|
||||
while (scalar(@closure) > 0) {
|
||||
my @ps = splice(@closure, 0, 1500);
|
||||
open(READ, "set -f; ssh $sshHost @{$sshOpts} nix-store --check-validity --print-invalid @ps|");
|
||||
while (<READ>) {
|
||||
chomp;
|
||||
push @missing, $_;
|
||||
my ($deriver, $narHash, $time, $narSize, $refs) = queryPathInfo($_, 1);
|
||||
$missingSize += $narSize;
|
||||
}
|
||||
close READ or die;
|
||||
}
|
||||
|
||||
$compressor = "$compressor |" if $compressor ne "";
|
||||
$decompressor = "$decompressor |" if $decompressor ne "";
|
||||
$progressViewer = "$progressViewer -s $missingSize |" if $progressViewer ne "";
|
||||
|
||||
# Export the store paths and import them on the remote machine.
|
||||
if (scalar @missing > 0) {
|
||||
print STDERR "copying ", scalar @missing, " missing paths to ‘$sshHost’...\n";
|
||||
unless ($dryRun) {
|
||||
open SSH, "| $compressor $progressViewer ssh $sshHost @{$sshOpts} '$decompressor nix-store --import' > /dev/null" or die;
|
||||
open SSH, "| $progressViewer $compressor ssh $sshHost @{$sshOpts} '$decompressor nix-store --import' > /dev/null" or die;
|
||||
exportPaths(fileno(SSH), $sign, @missing);
|
||||
close SSH or die "copying store paths to remote machine `$sshHost' failed: $?";
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ sub closeSSHConnection {
|
||||
if ($sshStarted) {
|
||||
system("ssh $sshHost @sshOpts -O exit 2> /dev/null") == 0
|
||||
or warn "unable to stop SSH master: $?";
|
||||
$sshStarted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-17
@@ -42,50 +42,50 @@ if ($Nix::Config::useBindings) {
|
||||
use File::Temp;
|
||||
use Fcntl qw/F_SETFD/;
|
||||
|
||||
sub hashFile {
|
||||
*hashFile = sub {
|
||||
my ($algo, $base32, $path) = @_;
|
||||
my $res = backtick("$Nix::Config::binDir/nix-hash", "--flat", $path, "--type", $algo, $base32 ? "--base32" : ());
|
||||
chomp $res;
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub hashPath {
|
||||
};
|
||||
|
||||
*hashPath = sub {
|
||||
my ($algo, $base32, $path) = @_;
|
||||
my $res = backtick("$Nix::Config::binDir/nix-hash", $path, "--type", $algo, $base32 ? "--base32" : ());
|
||||
chomp $res;
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub hashString {
|
||||
};
|
||||
|
||||
*hashString = sub {
|
||||
my ($algo, $base32, $s) = @_;
|
||||
my $fh = File::Temp->new();
|
||||
print $fh $s;
|
||||
my $res = backtick("$Nix::Config::binDir/nix-hash", $fh->filename, "--type", $algo, $base32 ? "--base32" : ());
|
||||
chomp $res;
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub addToStore {
|
||||
};
|
||||
|
||||
*addToStore = sub {
|
||||
my ($srcPath, $recursive, $algo) = @_;
|
||||
die "not implemented" if $recursive || $algo ne "sha256";
|
||||
my $res = backtick("$Nix::Config::binDir/nix-store", "--add", $srcPath);
|
||||
chomp $res;
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub isValidPath {
|
||||
};
|
||||
|
||||
*isValidPath = sub {
|
||||
my ($path) = @_;
|
||||
my $res = backtick("$Nix::Config::binDir/nix-store", "--check-validity", "--print-invalid", $path);
|
||||
chomp $res;
|
||||
return $res ne $path;
|
||||
}
|
||||
|
||||
sub queryPathHash {
|
||||
};
|
||||
|
||||
*queryPathHash = sub {
|
||||
my ($path) = @_;
|
||||
my $res = backtick("$Nix::Config::binDir/nix-store", "--query", "--hash", $path);
|
||||
chomp $res;
|
||||
return $res;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
+12
-9
@@ -15,7 +15,7 @@
|
||||
using namespace nix;
|
||||
|
||||
|
||||
void doInit()
|
||||
void doInit()
|
||||
{
|
||||
if (!store) {
|
||||
try {
|
||||
@@ -237,32 +237,35 @@ SV * derivationFromPath(char * drvPath)
|
||||
doInit();
|
||||
Derivation drv = derivationFromPath(*store, drvPath);
|
||||
hash = newHV();
|
||||
|
||||
/* TODO: handle drv.outputs */
|
||||
|
||||
|
||||
HV * outputs = newHV();
|
||||
for (DerivationOutputs::iterator i = drv.outputs.begin(); i != drv.outputs.end(); ++i)
|
||||
hv_store(outputs, i->first.c_str(), i->first.size(), newSVpv(i->second.path.c_str(), 0), 0);
|
||||
hv_stores(hash, "outputs", newRV((SV *) outputs));
|
||||
|
||||
AV * inputDrvs = newAV();
|
||||
for (DerivationInputs::iterator i = drv.inputDrvs.begin(); i != drv.inputDrvs.end(); ++i)
|
||||
av_push(inputDrvs, newSVpv(i->first.c_str(), 0)); // !!! ignores i->second
|
||||
hv_stores(hash, "inputDrvs", newRV((SV *) inputDrvs));
|
||||
|
||||
|
||||
AV * inputSrcs = newAV();
|
||||
for (PathSet::iterator i = drv.inputSrcs.begin(); i != drv.inputSrcs.end(); ++i)
|
||||
av_push(inputSrcs, newSVpv(i->c_str(), 0));
|
||||
hv_stores(hash, "inputSrcs", newRV((SV *) inputSrcs));
|
||||
|
||||
|
||||
hv_stores(hash, "platform", newSVpv(drv.platform.c_str(), 0));
|
||||
hv_stores(hash, "builder", newSVpv(drv.builder.c_str(), 0));
|
||||
|
||||
|
||||
AV * args = newAV();
|
||||
for (Strings::iterator i = drv.args.begin(); i != drv.args.end(); ++i)
|
||||
av_push(args, newSVpv(i->c_str(), 0));
|
||||
hv_stores(hash, "args", newRV((SV *) args));
|
||||
|
||||
|
||||
HV * env = newHV();
|
||||
for (StringPairs::iterator i = drv.env.begin(); i != drv.env.end(); ++i)
|
||||
hv_store(env, i->first.c_str(), i->first.size(), newSVpv(i->second.c_str(), 0), 0);
|
||||
hv_stores(hash, "env", newRV((SV *) env));
|
||||
|
||||
|
||||
RETVAL = newRV_noinc((SV *)hash);
|
||||
} catch (Error & e) {
|
||||
croak(e.what());
|
||||
|
||||
@@ -23,7 +23,7 @@ sub uniq {
|
||||
|
||||
sub writeFile {
|
||||
my ($fn, $s) = @_;
|
||||
open TMP, ">$fn" or die;
|
||||
open TMP, ">$fn" or die "cannot create file `$fn': $!";
|
||||
print TMP "$s" or die;
|
||||
close TMP or die;
|
||||
}
|
||||
@@ -31,7 +31,7 @@ sub writeFile {
|
||||
sub readFile {
|
||||
local $/ = undef;
|
||||
my ($fn) = @_;
|
||||
open TMP, "<$fn" or die;
|
||||
open TMP, "<$fn" or die "cannot open file `$fn': $!";
|
||||
my $s = <TMP>;
|
||||
close TMP or die;
|
||||
return $s;
|
||||
|
||||
+33
-21
@@ -1,15 +1,19 @@
|
||||
{ nixpkgs ? <nixpkgs>
|
||||
, nix ? { outPath = ./.; revCount = 1234; shortRev = "abcdef"; }
|
||||
{ nix ? { outPath = ./.; revCount = 1234; shortRev = "abcdef"; }
|
||||
, officialRelease ? false
|
||||
}:
|
||||
|
||||
let
|
||||
|
||||
pkgs = import <nixpkgs> {};
|
||||
|
||||
systems = [ "x86_64-linux" "i686-linux" "x86_64-darwin" "x86_64-freebsd" "i686-freebsd" "i686-cygwin" "i686-solaris" ];
|
||||
|
||||
|
||||
jobs = rec {
|
||||
|
||||
|
||||
tarball =
|
||||
with import nixpkgs {};
|
||||
with pkgs;
|
||||
|
||||
releaseTools.sourceTarball {
|
||||
name = "nix-tarball";
|
||||
@@ -19,7 +23,7 @@ let
|
||||
inherit officialRelease;
|
||||
|
||||
buildInputs =
|
||||
[ curl bison25 flex2535 perl libxml2 libxslt w3m bzip2
|
||||
[ curl bison flex2535 perl libxml2 libxslt w3m bzip2
|
||||
tetex dblatex nukeReferences pkgconfig sqlite git
|
||||
];
|
||||
|
||||
@@ -72,10 +76,9 @@ let
|
||||
};
|
||||
|
||||
|
||||
build =
|
||||
{ system ? "x86_64-linux" }:
|
||||
build = pkgs.lib.genAttrs systems (system:
|
||||
|
||||
with import nixpkgs { inherit system; };
|
||||
with import <nixpkgs> { inherit system; };
|
||||
|
||||
releaseTools.nixBuild {
|
||||
name = "nix";
|
||||
@@ -89,20 +92,25 @@ let
|
||||
--with-dbd-sqlite=${perlPackages.DBDSQLite}/${perl.libPrefix}
|
||||
--with-www-curl=${perlPackages.WWWCurl}/${perl.libPrefix}
|
||||
--enable-gc
|
||||
--sysconfdir=/etc
|
||||
'';
|
||||
|
||||
enableParallelBuilding = true;
|
||||
|
||||
makeFlags = "profiledir=$(out)/etc/profile.d";
|
||||
|
||||
installFlags = "sysconfdir=$(out)/etc";
|
||||
|
||||
doInstallCheck = true;
|
||||
};
|
||||
});
|
||||
|
||||
binaryTarball =
|
||||
{ system ? "x86_64-linux" }:
|
||||
|
||||
with import nixpkgs { inherit system; };
|
||||
binaryTarball = pkgs.lib.genAttrs systems (system:
|
||||
|
||||
with import <nixpkgs> { inherit system; };
|
||||
|
||||
let
|
||||
toplevel = build { inherit system; };
|
||||
toplevel = builtins.getAttr system jobs.build;
|
||||
version = toplevel.src.version;
|
||||
in
|
||||
|
||||
@@ -125,11 +133,11 @@ let
|
||||
--transform "s,$TMPDIR/install,/usr/bin/nix-finish-install," \
|
||||
--transform "s,$TMPDIR/reginfo,/nix/store/reginfo," \
|
||||
$TMPDIR/install $TMPDIR/reginfo $storePaths
|
||||
'';
|
||||
'');
|
||||
|
||||
|
||||
coverage =
|
||||
with import nixpkgs { system = "x86_64-linux"; };
|
||||
with import <nixpkgs> { system = "x86_64-linux"; };
|
||||
|
||||
releaseTools.coverageAnalysis {
|
||||
name = "nix-build";
|
||||
@@ -161,14 +169,16 @@ let
|
||||
};
|
||||
|
||||
|
||||
rpm_fedora13i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora13i386) 50;
|
||||
rpm_fedora13x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora13x86_64) 50;
|
||||
rpm_fedora16i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora16i386) 50;
|
||||
rpm_fedora16x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora16x86_64) 50;
|
||||
rpm_fedora18i386 = makeRPM_i686 (diskImageFuns: diskImageFuns.fedora18i386) 60;
|
||||
rpm_fedora18x86_64 = makeRPM_x86_64 (diskImageFunsFun: diskImageFunsFun.fedora18x86_64) 60;
|
||||
|
||||
|
||||
deb_debian60i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian60i386) 50;
|
||||
deb_debian60x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian60x86_64) 50;
|
||||
deb_debian70i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.debian70i386) 60;
|
||||
deb_debian70x86_64 = makeDeb_x86_64 (diskImageFunsFun: diskImageFunsFun.debian70x86_64) 60;
|
||||
|
||||
deb_ubuntu1004i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1004i386) 50;
|
||||
deb_ubuntu1004x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1004x86_64) 50;
|
||||
@@ -180,15 +190,17 @@ let
|
||||
deb_ubuntu1204x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1204x86_64) 60;
|
||||
deb_ubuntu1210i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1210i386) 70;
|
||||
deb_ubuntu1210x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1210x86_64) 70;
|
||||
deb_ubuntu1304i386 = makeDeb_i686 (diskImageFuns: diskImageFuns.ubuntu1304i386) 80;
|
||||
deb_ubuntu1304x86_64 = makeDeb_x86_64 (diskImageFuns: diskImageFuns.ubuntu1304x86_64) 80;
|
||||
|
||||
|
||||
# System tests.
|
||||
tests.remote_builds = (import ./tests/remote-builds.nix rec {
|
||||
nix = build { inherit system; }; system = "x86_64-linux";
|
||||
nix = build.x86_64-linux; system = "x86_64-linux";
|
||||
}).test;
|
||||
|
||||
tests.nix_copy_closure = (import ./tests/nix-copy-closure.nix rec {
|
||||
nix = build { inherit system; }; system = "x86_64-linux";
|
||||
nix = build.x86_64-linux; system = "x86_64-linux";
|
||||
}).test;
|
||||
|
||||
};
|
||||
@@ -200,7 +212,7 @@ let
|
||||
makeRPM =
|
||||
system: diskImageFun: prio:
|
||||
|
||||
with import nixpkgs { inherit system; };
|
||||
with import <nixpkgs> { inherit system; };
|
||||
|
||||
releaseTools.rpmBuild rec {
|
||||
name = "nix-rpm";
|
||||
@@ -219,7 +231,7 @@ let
|
||||
makeDeb =
|
||||
system: diskImageFun: prio:
|
||||
|
||||
with import nixpkgs { inherit system; };
|
||||
with import <nixpkgs> { inherit system; };
|
||||
|
||||
releaseTools.debBuild {
|
||||
name = "nix-deb";
|
||||
@@ -229,7 +241,7 @@ let
|
||||
memSize = 1024;
|
||||
meta.schedulingPriority = prio;
|
||||
configureFlags = "--sysconfdir=/etc";
|
||||
debRequires = [ "curl" "libdbd-sqlite3-perl" "libsqlite3-0" "libbz2-1.0" "bzip2" "xz-utils" ];
|
||||
debRequires = [ "curl" "libdbd-sqlite3-perl" "libsqlite3-0" "libbz2-1.0" "bzip2" "xz-utils" "libwww-curl-perl" ];
|
||||
doInstallCheck = true;
|
||||
};
|
||||
|
||||
|
||||
+4
-2
@@ -7,9 +7,11 @@ noinst_SCRIPTS = nix-profile.sh \
|
||||
find-runtime-roots.pl build-remote.pl nix-reduce-build \
|
||||
copy-from-other-stores.pl nix-http-export.cgi
|
||||
|
||||
profiledir = $(sysconfdir)/profile.d
|
||||
|
||||
install-exec-local: download-using-manifests.pl copy-from-other-stores.pl download-from-binary-cache.pl find-runtime-roots.pl
|
||||
$(INSTALL) -d $(DESTDIR)$(sysconfdir)/profile.d
|
||||
$(INSTALL_DATA) nix-profile.sh $(DESTDIR)$(sysconfdir)/profile.d/nix.sh
|
||||
$(INSTALL) -d $(DESTDIR)$(profiledir)
|
||||
$(INSTALL_DATA) nix-profile.sh $(DESTDIR)$(profiledir)/nix.sh
|
||||
$(INSTALL) -d $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_PROGRAM) find-runtime-roots.pl $(DESTDIR)$(libexecdir)/nix
|
||||
$(INSTALL_PROGRAM) build-remote.pl $(DESTDIR)$(libexecdir)/nix
|
||||
|
||||
+16
-14
@@ -46,7 +46,7 @@ sub all { $_ || return 0 for @_; 1 }
|
||||
# Initialisation.
|
||||
my $loadIncreased = 0;
|
||||
|
||||
my ($localSystem, $maxSilentTime, $printBuildTrace, $buildTimeout) = @ARGV;
|
||||
my ($localSystem, $printBuildTrace) = @ARGV;
|
||||
|
||||
my $currentLoad = $ENV{"NIX_CURRENT_LOAD"};
|
||||
my $conf = $ENV{"NIX_REMOTE_SYSTEMS"};
|
||||
@@ -195,9 +195,13 @@ REQ: while (1) {
|
||||
# Connect to the selected machine.
|
||||
@sshOpts = ("-i", $machine->{sshKeys}, "-x");
|
||||
$hostName = $machine->{hostName};
|
||||
last REQ if openSSHConnection $hostName;
|
||||
|
||||
warn "unable to open SSH connection to $hostName, trying other available machines...\n";
|
||||
if (openSSHConnection($hostName)) {
|
||||
last REQ if system("ssh $hostName @sshOpts nix-builds-inhibited < /dev/null > /dev/null 2>&1") != 0;
|
||||
warn "machine `$hostName' is refusing builds, trying other available machines...\n";
|
||||
closeSSHConnection;
|
||||
} else {
|
||||
warn "unable to open SSH connection to `$hostName', trying other available machines...\n";
|
||||
}
|
||||
$machine->{enabled} = 0;
|
||||
}
|
||||
}
|
||||
@@ -258,8 +262,8 @@ close UPLOADLOCK;
|
||||
|
||||
# Perform the build.
|
||||
my $buildFlags =
|
||||
"--max-silent-time $maxSilentTime --option build-timeout $buildTimeout "
|
||||
. "--fallback --add-root $rootsDir/\$PPID.out --option verbosity 0";
|
||||
" --fallback --add-root $rootsDir/\$PPID.out --quiet"
|
||||
. " --option build-keep-log false";
|
||||
|
||||
# We let the remote side kill its process group when the connection is
|
||||
# closed unexpectedly. This is necessary to ensure that no processes
|
||||
@@ -269,7 +273,7 @@ my $buildFlags =
|
||||
# in which case every child receives SIGHUP; however, `-tt' doesn't
|
||||
# work on some platforms when connection sharing is used.)
|
||||
pipe STDIN, DUMMY; # make sure we have a readable STDIN
|
||||
if (system("exec ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & nix-store -r $drvPath $buildFlags > /dev/null' 2>&4") != 0) {
|
||||
if (system("exec ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & exec nix-store -r $drvPath $buildFlags > /dev/null' 2>&4") != 0) {
|
||||
# Note that if we get exit code 100 from `nix-store -r', it
|
||||
# denotes a permanent build failure (as opposed to an SSH problem
|
||||
# or a temporary Nix problem). We propagate this to the caller to
|
||||
@@ -285,13 +289,11 @@ if (system("exec ssh $hostName @sshOpts '(read; kill -INT -\$\$) <&0 & nix-store
|
||||
|
||||
|
||||
# Copy the output from the build machine.
|
||||
foreach my $output (@outputs) {
|
||||
my $maybeSignRemote = "";
|
||||
$maybeSignRemote = "--sign" if $UID != 0;
|
||||
next if isValidPath($output);
|
||||
system("exec ssh $hostName @sshOpts 'nix-store --export $maybeSignRemote $output'" .
|
||||
"| NIX_HELD_LOCKS=$output @bindir@/nix-store --import > /dev/null") == 0
|
||||
or die "cannot copy $output from $hostName: $?";
|
||||
my @outputs2 = grep { !isValidPath($_) } @outputs;
|
||||
if (scalar @outputs2 > 0) {
|
||||
system("exec ssh $hostName @sshOpts 'nix-store --export @outputs2'" .
|
||||
"| NIX_HELD_LOCKS='@outputs2' @bindir@/nix-store --import > /dev/null") == 0
|
||||
or die("cannot copy paths " . join(", ", @outputs) . " from `$hostName': $?");
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -24,8 +24,11 @@ my $ttlNegative = 24 * 3600; # when to purge negative lookups from the database
|
||||
my $ttlNegativeUse = 3600; # how long negative lookups are valid for non-"have" lookups
|
||||
my $didExpiration = 0;
|
||||
|
||||
my $debug = ($ENV{"NIX_DEBUG_SUBST"} // "") eq 1;
|
||||
open(STDERR, ">>/dev/tty") if $debug;
|
||||
my $showAfter = 5; # show that we're waiting for a request after this many seconds
|
||||
|
||||
my $debug = ($Nix::Config::config{"debug-subst"} // "") eq 1 || ($Nix::Config::config{"untrusted-debug-subst"} // "") eq 1;
|
||||
|
||||
my $cacheFileURLs = ($ENV{"_NIX_CACHE_FILE_URLS"} // "") eq 1; # for testing
|
||||
|
||||
my ($dbh, $queryCache, $insertNAR, $queryNAR, $insertNARExistence, $queryNARExistence, $expireNARExistence);
|
||||
|
||||
@@ -36,13 +39,16 @@ my %requests;
|
||||
my %scheduled;
|
||||
my $caBundle = $ENV{"CURL_CA_BUNDLE"} // $ENV{"OPENSSL_X509_CERT_FILE"};
|
||||
|
||||
my $userName = getpwuid($<) or die "cannot figure out user name";
|
||||
|
||||
|
||||
sub addRequest {
|
||||
my ($storePath, $url, $head) = @_;
|
||||
|
||||
my $curl = WWW::Curl::Easy->new;
|
||||
my $curlId = $curlIdCount++;
|
||||
$requests{$curlId} = { storePath => $storePath, url => $url, handle => $curl, content => "", type => $head ? "HEAD" : "GET" };
|
||||
$requests{$curlId} = { storePath => $storePath, url => $url, handle => $curl, content => "", type => $head ? "HEAD" : "GET"
|
||||
, shown => 0, started => time() };
|
||||
|
||||
$curl->setopt(CURLOPT_PRIVATE, $curlId);
|
||||
$curl->setopt(CURLOPT_URL, $url);
|
||||
@@ -53,6 +59,7 @@ sub addRequest {
|
||||
$curl->setopt(CURLOPT_USERAGENT, "Nix/$Nix::Config::version");
|
||||
$curl->setopt(CURLOPT_NOBODY, 1) if $head;
|
||||
$curl->setopt(CURLOPT_FAILONERROR, 1);
|
||||
$curl->setopt(CURLOPT_TIMEOUT, int($ENV{"NIX_CONNECT_TIMEOUT"} // 0));
|
||||
|
||||
if ($activeRequests >= $maxParallelRequests) {
|
||||
$scheduled{$curlId} = 1;
|
||||
@@ -72,7 +79,7 @@ sub processRequests {
|
||||
|
||||
# Sleep until we can read or write some data.
|
||||
if (scalar @{$rfds} + scalar @{$wfds} + scalar @{$efds} > 0) {
|
||||
IO::Select->select(IO::Select->new(@{$rfds}), IO::Select->new(@{$wfds}), IO::Select->new(@{$efds}), 0.1);
|
||||
IO::Select->select(IO::Select->new(@{$rfds}), IO::Select->new(@{$wfds}), IO::Select->new(@{$efds}), 1.0);
|
||||
}
|
||||
|
||||
if ($curlm->perform() != $activeRequests) {
|
||||
@@ -97,6 +104,16 @@ sub processRequests {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $time = time();
|
||||
while (my ($key, $request) = each %requests) {
|
||||
next unless defined $request->{handle};
|
||||
next if $request->{shown};
|
||||
if ($time > $request->{started} + $showAfter) {
|
||||
print STDERR "still waiting for ‘$request->{url}’ after $showAfter seconds...\n";
|
||||
$request->{shown} = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,11 +208,11 @@ sub getAvailableCaches {
|
||||
return map { s/\/+$//; $_ } split(/ /, $s);
|
||||
}
|
||||
|
||||
my @urls = strToList ($Nix::Config::config{"binary-caches"} // "");
|
||||
# // ($Nix::Config::storeDir eq "/nix/store" ? "http://nixos.org/binary-cache" : ""));
|
||||
my @urls = strToList($Nix::Config::config{"binary-caches"} //
|
||||
($Nix::Config::storeDir eq "/nix/store" ? "http://cache.nixos.org" : ""));
|
||||
|
||||
my $urlsFiles = $Nix::Config::config{"binary-cache-files"}
|
||||
// "/nix/var/nix/profiles/per-user/root/channels/binary-caches/*";
|
||||
// "$Nix::Config::stateDir/profiles/per-user/$userName/channels/binary-caches/*";
|
||||
foreach my $urlFile (glob $urlsFiles) {
|
||||
next unless -f $urlFile;
|
||||
open FILE, "<$urlFile" or die "cannot open ‘$urlFile’\n";
|
||||
@@ -204,12 +221,15 @@ sub getAvailableCaches {
|
||||
push @urls, strToList($url);
|
||||
}
|
||||
|
||||
push @urls, strToList($Nix::Config::config{"extra-binary-caches"} // "");
|
||||
|
||||
# Allow Nix daemon users to override the binary caches to a subset
|
||||
# of those listed in the config file. Note that ‘untrusted-*’
|
||||
# denotes options passed by the client.
|
||||
my @trustedUrls = uniq(@urls, strToList($Nix::Config::config{"trusted-binary-caches"} // ""));
|
||||
|
||||
if (defined $Nix::Config::config{"untrusted-binary-caches"}) {
|
||||
my @untrustedUrls = strToList $Nix::Config::config{"untrusted-binary-caches"};
|
||||
my @trustedUrls = uniq(@urls, strToList($Nix::Config::config{"trusted-binary-caches"} // ""));
|
||||
@urls = ();
|
||||
foreach my $url (@untrustedUrls) {
|
||||
die "binary cache ‘$url’ is not trusted (please add it to ‘trusted-binary-caches’ [@trustedUrls] in $Nix::Config::confDir/nix.conf)\n"
|
||||
@@ -218,6 +238,12 @@ sub getAvailableCaches {
|
||||
}
|
||||
}
|
||||
|
||||
my @untrustedUrls = strToList $Nix::Config::config{"untrusted-extra-binary-caches"} // "";
|
||||
foreach my $url (@untrustedUrls) {
|
||||
next unless scalar(grep { $url eq $_ } @trustedUrls) > 0;
|
||||
push @urls, $url;
|
||||
}
|
||||
|
||||
foreach my $url (uniq @urls) {
|
||||
|
||||
# FIXME: not atomic.
|
||||
@@ -252,11 +278,12 @@ sub getAvailableCaches {
|
||||
elsif ($1 eq "Priority") { $priority = int($2); }
|
||||
}
|
||||
|
||||
$dbh->do("insert into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)",
|
||||
$dbh->do("insert or replace into BinaryCaches(url, timestamp, storeDir, wantMassQuery, priority) values (?, ?, ?, ?, ?)",
|
||||
{}, $url, time(), $storeDir, $wantMassQuery, $priority);
|
||||
my $id = $dbh->last_insert_id("", "", "", "");
|
||||
$queryCache->execute($url);
|
||||
$res = $queryCache->fetchrow_hashref() or die;
|
||||
next if $storeDir ne $Nix::Config::storeDir;
|
||||
push @caches, { id => $id, url => $url, wantMassQuery => $wantMassQuery, priority => $priority };
|
||||
push @caches, { id => $res->{id}, url => $url, wantMassQuery => $wantMassQuery, priority => $priority };
|
||||
}
|
||||
|
||||
@caches = sort { $a->{priority} <=> $b->{priority} } @caches;
|
||||
@@ -265,16 +292,22 @@ sub getAvailableCaches {
|
||||
}
|
||||
|
||||
|
||||
sub shouldCache {
|
||||
my ($url) = @_;
|
||||
return $cacheFileURLs || $url !~ /^file:/;
|
||||
}
|
||||
|
||||
|
||||
sub processNARInfo {
|
||||
my ($storePath, $cache, $request) = @_;
|
||||
|
||||
if ($request->{result} != 0) {
|
||||
if ($request->{result} != 37 && $request->{httpStatus} != 404) {
|
||||
if ($request->{result} != 37 && $request->{httpStatus} != 404 && $request->{httpStatus} != 403) {
|
||||
print STDERR "could not download ‘$request->{url}’ (" .
|
||||
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
||||
} else {
|
||||
$insertNARExistence->execute($cache->{id}, basename($storePath), 0, time())
|
||||
unless $request->{url} =~ /^file:/;
|
||||
if shouldCache $request->{url};
|
||||
}
|
||||
return undef;
|
||||
}
|
||||
@@ -287,7 +320,7 @@ sub processNARInfo {
|
||||
$cache->{id}, basename($storePath), $narInfo->{url}, $narInfo->{compression},
|
||||
$narInfo->{fileHash}, $narInfo->{fileSize}, $narInfo->{narHash}, $narInfo->{narSize},
|
||||
join(" ", @{$narInfo->{refs}}), $narInfo->{deriver}, $narInfo->{system}, time())
|
||||
unless $request->{url} =~ /^file:/;
|
||||
if shouldCache $request->{url};
|
||||
|
||||
return $narInfo;
|
||||
}
|
||||
@@ -448,17 +481,17 @@ sub printSubstitutablePaths {
|
||||
|
||||
foreach my $request (values %requests) {
|
||||
if ($request->{result} != 0) {
|
||||
if ($request->{result} != 37 && $request->{httpStatus} != 404) {
|
||||
if ($request->{result} != 37 && $request->{httpStatus} != 404 && $request->{httpStatus} != 403) {
|
||||
print STDERR "could not check ‘$request->{url}’ (" .
|
||||
($request->{result} != 0 ? "Curl error $request->{result}" : "HTTP status $request->{httpStatus}") . ")\n";
|
||||
} else {
|
||||
$insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 0, time())
|
||||
unless $request->{url} =~ /^file:/;
|
||||
if shouldCache $request->{url};
|
||||
}
|
||||
push @left2, $request->{storePath};
|
||||
} else {
|
||||
$insertNARExistence->execute($cache->{id}, basename($request->{storePath}), 1, time())
|
||||
unless $request->{url} =~ /^file:/;
|
||||
if shouldCache $request->{url};
|
||||
print "$request->{storePath}\n";
|
||||
}
|
||||
}
|
||||
@@ -506,6 +539,7 @@ sub downloadBinary {
|
||||
}
|
||||
|
||||
print STDERR "could not download ‘$storePath’ from any binary cache\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
#! @perl@ -w
|
||||
#! @perl@ -w @perlFlags@
|
||||
|
||||
use strict;
|
||||
|
||||
my $procDir = "/proc";
|
||||
use Nix::Utils;
|
||||
use Nix::Config;
|
||||
|
||||
|
||||
sub readProc {
|
||||
return unless -d $procDir;
|
||||
return unless -d "/proc";
|
||||
|
||||
opendir DIR, "/proc" or return;
|
||||
|
||||
opendir DIR, $procDir or return;
|
||||
|
||||
foreach my $name (readdir DIR) {
|
||||
next unless $name =~ /^\d+$/;
|
||||
|
||||
my $process = "$procDir/$name";
|
||||
my $process = "/proc/$name";
|
||||
|
||||
#print STDERR "=== $process\n";
|
||||
|
||||
|
||||
my $target;
|
||||
print "$target\n" if $target = readlink "$process/exe";
|
||||
print "$target\n" if $target = readlink "$process/cwd";
|
||||
@@ -36,6 +36,13 @@ sub readProc {
|
||||
}
|
||||
close MAP;
|
||||
}
|
||||
|
||||
# Get all store paths that appear in the environment of this process.
|
||||
eval {
|
||||
my $env = Nix::Utils::readFile "$process/environ";
|
||||
my @matches = $env =~ /\Q$Nix::Config::storeDir\E\/[0-9a-z]+[0-9a-zA-Z\+\-\._\?=]*/g;
|
||||
print "$_\n" foreach @matches;
|
||||
}
|
||||
}
|
||||
|
||||
closedir DIR;
|
||||
@@ -58,18 +65,15 @@ readProc;
|
||||
lsof;
|
||||
|
||||
|
||||
sub readFile {
|
||||
my $path = shift;
|
||||
if (-e $path) {
|
||||
if (open FILE, "$path") {
|
||||
while (<FILE>) {
|
||||
print;
|
||||
}
|
||||
close FILE;
|
||||
}
|
||||
sub printFile {
|
||||
my ($fn) = @_;
|
||||
if (-e $fn) {
|
||||
print Nix::Utils::readFile($fn), "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# This is rather NixOS-specific, so it probably shouldn't be here.
|
||||
readFile "/proc/sys/kernel/modprobe";
|
||||
readFile "/proc/sys/kernel/fbsplash";
|
||||
printFile "/proc/sys/kernel/modprobe";
|
||||
printFile "/proc/sys/kernel/fbsplash";
|
||||
printFile "/proc/sys/kernel/poweroff_cmd";
|
||||
|
||||
@@ -87,7 +87,7 @@ for (my $n = 0; $n < scalar @ARGV; $n++) {
|
||||
$n += 2;
|
||||
}
|
||||
|
||||
elsif ($arg eq "--max-jobs" or $arg eq "-j" or $arg eq "--max-silent-time" or $arg eq "--log-type" or $arg eq "--cores") {
|
||||
elsif ($arg eq "--max-jobs" or $arg eq "-j" or $arg eq "--max-silent-time" or $arg eq "--log-type" or $arg eq "--cores" or $arg eq "--timeout") {
|
||||
$n++;
|
||||
die "$0: `$arg' requires an argument\n" unless $n < scalar @ARGV;
|
||||
push @buildArgs, ($arg, $ARGV[$n]);
|
||||
|
||||
@@ -4,6 +4,7 @@ use Nix::SSH;
|
||||
use Nix::Config;
|
||||
use Nix::Store;
|
||||
use Nix::CopyClosure;
|
||||
use List::Util qw(sum);
|
||||
|
||||
|
||||
if (scalar @ARGV < 1) {
|
||||
@@ -77,6 +78,8 @@ while (@ARGV) {
|
||||
}
|
||||
}
|
||||
|
||||
die "$0: you did not specify a host name\n" unless defined $sshHost;
|
||||
|
||||
|
||||
openSSHConnection $sshHost or die "$0: unable to start SSH\n";
|
||||
|
||||
@@ -104,6 +107,11 @@ else { # Copy FROM the remote machine.
|
||||
|
||||
close READ or die "nix-store on remote machine `$sshHost' failed: $?";
|
||||
|
||||
my $missingSize = 0;
|
||||
if ($progressViewer ne "") {
|
||||
$missingSize = sum (split ' ', `set -f; ssh @sshOpts $sshHost nix-store -q --size @missing`) or die;
|
||||
}
|
||||
|
||||
# Export the store paths on the remote machine and import them locally.
|
||||
if (scalar @missing > 0) {
|
||||
print STDERR "copying ", scalar @missing, " missing paths from ‘$sshHost’...\n";
|
||||
@@ -113,9 +121,9 @@ else { # Copy FROM the remote machine.
|
||||
}
|
||||
$compressor = "| $compressor" if $compressor ne "";
|
||||
$decompressor = "$decompressor |" if $decompressor ne "";
|
||||
$progressViewer = "$progressViewer |" if $progressViewer ne "";
|
||||
$progressViewer = "$progressViewer -s $missingSize |" if $progressViewer ne "";
|
||||
my $extraOpts = $sign ? "--sign" : "";
|
||||
system("set -f; ssh $sshHost @sshOpts 'nix-store --export $extraOpts @missing $compressor' | $progressViewer $decompressor $Nix::Config::binDir/nix-store --import > /dev/null") == 0
|
||||
system("set -f; ssh $sshHost @sshOpts 'nix-store --export $extraOpts @missing $compressor' | $decompressor $progressViewer $Nix::Config::binDir/nix-store --import > /dev/null") == 0
|
||||
or die "copying store paths from remote machine `$sshHost' failed: $?";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,16 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s2))
|
||||
void EvalState::forceValue(Value & v)
|
||||
{
|
||||
if (v.type == tThunk) {
|
||||
ValueType saved = v.type;
|
||||
Env * env = v.thunk.env;
|
||||
Expr * expr = v.thunk.expr;
|
||||
try {
|
||||
v.type = tBlackhole;
|
||||
//checkInterrupt();
|
||||
v.thunk.expr->eval(*this, *v.thunk.env, v);
|
||||
expr->eval(*this, *env, v);
|
||||
} catch (Error & e) {
|
||||
v.type = saved;
|
||||
v.type = tThunk;
|
||||
v.thunk.env = env;
|
||||
v.thunk.expr = expr;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
+19
-10
@@ -141,10 +141,10 @@ EvalState::EvalState()
|
||||
, sOutputs(symbols.create("outputs"))
|
||||
, sOutputName(symbols.create("outputName"))
|
||||
, sIgnoreNulls(symbols.create("__ignoreNulls"))
|
||||
, repair(false)
|
||||
, baseEnv(allocEnv(128))
|
||||
, baseEnvDispl(0)
|
||||
, staticBaseEnv(false, 0)
|
||||
, repair(false)
|
||||
{
|
||||
nrEnvs = nrValuesInEnvs = nrValues = nrListElems = 0;
|
||||
nrAttrsets = nrOpUpdates = nrOpUpdateValuesCopied = 0;
|
||||
@@ -247,6 +247,11 @@ LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos, const
|
||||
throw TypeError(format(s) % pos % s2);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const string & s1, const string & s2))
|
||||
{
|
||||
throw TypeError(format(s) % s1 % s2);
|
||||
}
|
||||
|
||||
LocalNoInlineNoReturn(void throwTypeError(const char * s, const Pos & pos))
|
||||
{
|
||||
throw TypeError(format(s) % pos);
|
||||
@@ -755,8 +760,8 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
foreach (Formals::Formals_::iterator, i, fun.lambda.fun->formals->formals) {
|
||||
Bindings::iterator j = arg.attrs->find(i->name);
|
||||
if (j == arg.attrs->end()) {
|
||||
if (!i->def) throwTypeError("function at %1% called without required argument `%2%'",
|
||||
fun.lambda.fun->pos, i->name);
|
||||
if (!i->def) throwTypeError("%1% called without required argument `%2%'",
|
||||
fun.lambda.fun->showNamePos(), i->name);
|
||||
env2.values[displ++] = i->def->maybeThunk(*this, env2);
|
||||
} else {
|
||||
attrsUsed++;
|
||||
@@ -765,11 +770,15 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
}
|
||||
|
||||
/* Check that each actual argument is listed as a formal
|
||||
argument (unless the attribute match specifies a `...').
|
||||
TODO: show the names of the expected/unexpected
|
||||
arguments. */
|
||||
if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size())
|
||||
throwTypeError("function at %1% called with unexpected argument", fun.lambda.fun->pos);
|
||||
argument (unless the attribute match specifies a `...'). */
|
||||
if (!fun.lambda.fun->formals->ellipsis && attrsUsed != arg.attrs->size()) {
|
||||
/* Nope, so show the first unexpected argument to the
|
||||
user. */
|
||||
foreach (Bindings::iterator, i, *arg.attrs)
|
||||
if (fun.lambda.fun->formals->argNames.find(i->name) == fun.lambda.fun->formals->argNames.end())
|
||||
throwTypeError("%1% called with unexpected argument `%2%'", fun.lambda.fun->showNamePos(), i->name);
|
||||
abort(); // can't happen
|
||||
}
|
||||
}
|
||||
|
||||
nrFunctionCalls++;
|
||||
@@ -778,7 +787,7 @@ void EvalState::callFunction(Value & fun, Value & arg, Value & v)
|
||||
try {
|
||||
fun.lambda.fun->body->eval(*this, env2, v);
|
||||
} catch (Error & e) {
|
||||
addErrorPrefix(e, "while evaluating the function at %1%:\n", fun.lambda.fun->pos);
|
||||
addErrorPrefix(e, "while evaluating %1%:\n", fun.lambda.fun->showNamePos());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
@@ -966,7 +975,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||
since paths are copied when they are used in a derivation),
|
||||
and none of the strings are allowed to have contexts. */
|
||||
if (first) {
|
||||
isPath = vStr.type == tPath;
|
||||
isPath = !forceString && vStr.type == tPath;
|
||||
first = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
|
||||
%{
|
||||
#include "nixexpr.hh"
|
||||
#define BISON_HEADER_HACK
|
||||
#include "parser-tab.hh"
|
||||
|
||||
using namespace nix;
|
||||
|
||||
@@ -324,4 +324,24 @@ void ExprConcatStrings::bindVars(const StaticEnv & env)
|
||||
}
|
||||
|
||||
|
||||
/* Storing function names. */
|
||||
|
||||
void Expr::setName(Symbol & name)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void ExprLambda::setName(Symbol & name)
|
||||
{
|
||||
this->name = name;
|
||||
body->setName(name);
|
||||
}
|
||||
|
||||
|
||||
string ExprLambda::showNamePos()
|
||||
{
|
||||
return (format("%1% at %2%") % (name.set() ? "`" + (string) name + "'" : "an anonymous function") % pos).str();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ struct Expr
|
||||
virtual void bindVars(const StaticEnv & env);
|
||||
virtual void eval(EvalState & state, Env & env, Value & v);
|
||||
virtual Value * maybeThunk(EvalState & state, Env & env);
|
||||
virtual void setName(Symbol & name);
|
||||
};
|
||||
|
||||
std::ostream & operator << (std::ostream & str, Expr & e);
|
||||
@@ -197,6 +198,7 @@ struct Formals
|
||||
struct ExprLambda : Expr
|
||||
{
|
||||
Pos pos;
|
||||
Symbol name;
|
||||
Symbol arg;
|
||||
bool matchAttrs;
|
||||
Formals * formals;
|
||||
@@ -208,6 +210,8 @@ struct ExprLambda : Expr
|
||||
throw ParseError(format("duplicate formal function argument `%1%' at %2%")
|
||||
% arg % pos);
|
||||
};
|
||||
void setName(Symbol & name);
|
||||
string showNamePos();
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
@@ -277,8 +281,10 @@ MakeBinOp(OpConcatLists, "++")
|
||||
|
||||
struct ExprConcatStrings : Expr
|
||||
{
|
||||
bool forceString;
|
||||
vector<Expr *> * es;
|
||||
ExprConcatStrings(vector<Expr *> * es) : es(es) { };
|
||||
ExprConcatStrings(bool forceString, vector<Expr *> * es)
|
||||
: forceString(forceString), es(es) { };
|
||||
COMMON_METHODS
|
||||
};
|
||||
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
%error-verbose
|
||||
%defines
|
||||
/* %no-lines */
|
||||
%parse-param { yyscan_t scanner }
|
||||
%parse-param { ParseData * data }
|
||||
%lex-param { yyscan_t scanner }
|
||||
%lex-param { ParseData * data }
|
||||
%parse-param { void * scanner }
|
||||
%parse-param { nix::ParseData * data }
|
||||
%lex-param { void * scanner }
|
||||
%lex-param { nix::ParseData * data }
|
||||
%expect 1
|
||||
%expect-rr 1
|
||||
|
||||
@@ -52,7 +52,6 @@ namespace nix {
|
||||
|
||||
#include "parser-tab.hh"
|
||||
#include "lexer-tab.hh"
|
||||
#define YYSTYPE YYSTYPE // workaround a bug in Bison 2.4
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -105,6 +104,7 @@ static void addAttr(ExprAttrs * attrs, AttrPath & attrPath,
|
||||
}
|
||||
}
|
||||
}
|
||||
e->setName(attrPath.back());
|
||||
}
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ static Expr * stripIndentation(SymbolTable & symbols, vector<Expr *> & es)
|
||||
es2->push_back(new ExprString(symbols.create(s2)));
|
||||
}
|
||||
|
||||
return es2->size() == 1 ? (*es2)[0] : new ExprConcatStrings(es2);
|
||||
return es2->size() == 1 ? (*es2)[0] : new ExprConcatStrings(true, es2);
|
||||
}
|
||||
|
||||
|
||||
@@ -318,7 +318,7 @@ expr_op
|
||||
{ vector<Expr *> * l = new vector<Expr *>;
|
||||
l->push_back($1);
|
||||
l->push_back($3);
|
||||
$$ = new ExprConcatStrings(l);
|
||||
$$ = new ExprConcatStrings(false, l);
|
||||
}
|
||||
| expr_op CONCAT expr_op { $$ = new ExprOpConcatLists($1, $3); }
|
||||
| expr_app
|
||||
@@ -349,7 +349,7 @@ expr_simple
|
||||
/* For efficiency, and to simplify parse trees a bit. */
|
||||
if ($2->empty()) $$ = new ExprString(data->symbols.create(""));
|
||||
else if ($2->size() == 1) $$ = $2->front();
|
||||
else $$ = new ExprConcatStrings($2);
|
||||
else $$ = new ExprConcatStrings(true, $2);
|
||||
}
|
||||
| IND_STRING_OPEN ind_string_parts IND_STRING_CLOSE {
|
||||
$$ = stripIndentation(data->symbols, *$2);
|
||||
|
||||
@@ -1107,6 +1107,21 @@ static void prim_unsafeDiscardOutputDependency(EvalState & state, Value * * args
|
||||
}
|
||||
|
||||
|
||||
/* Return the cryptographic hash of a string in base-16. */
|
||||
static void prim_hashString(EvalState & state, Value * * args, Value & v)
|
||||
{
|
||||
string type = state.forceStringNoCtx(*args[0]);
|
||||
HashType ht = parseHashType(type);
|
||||
if (ht == htUnknown)
|
||||
throw Error(format("unknown hash type `%1%'") % type);
|
||||
|
||||
PathSet context; // discarded
|
||||
string s = state.forceString(*args[1], context);
|
||||
|
||||
mkString(v, printHash(hashString(ht, s)), context);
|
||||
};
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* Versions
|
||||
*************************************************************/
|
||||
@@ -1234,6 +1249,7 @@ void EvalState::createBaseEnv()
|
||||
addPrimOp("__stringLength", 1, prim_stringLength);
|
||||
addPrimOp("__unsafeDiscardStringContext", 1, prim_unsafeDiscardStringContext);
|
||||
addPrimOp("__unsafeDiscardOutputDependency", 1, prim_unsafeDiscardOutputDependency);
|
||||
addPrimOp("__hashString", 2, prim_hashString);
|
||||
|
||||
// Versions
|
||||
addPrimOp("__parseDrvName", 1, prim_parseDrvName);
|
||||
|
||||
@@ -28,17 +28,17 @@ private:
|
||||
|
||||
public:
|
||||
Symbol() : s(0) { };
|
||||
|
||||
|
||||
bool operator == (const Symbol & s2) const
|
||||
{
|
||||
return s == s2.s;
|
||||
}
|
||||
|
||||
|
||||
bool operator != (const Symbol & s2) const
|
||||
{
|
||||
return s != s2.s;
|
||||
}
|
||||
|
||||
|
||||
bool operator < (const Symbol & s2) const
|
||||
{
|
||||
return s < s2.s;
|
||||
@@ -49,6 +49,11 @@ public:
|
||||
return *s;
|
||||
}
|
||||
|
||||
bool set() const
|
||||
{
|
||||
return s;
|
||||
}
|
||||
|
||||
bool empty() const
|
||||
{
|
||||
return s->empty();
|
||||
@@ -66,7 +71,7 @@ inline std::ostream & operator << (std::ostream & str, const Symbol & sym)
|
||||
class SymbolTable
|
||||
{
|
||||
private:
|
||||
#if HAVE_TR1_UNORDERED_SET
|
||||
#if HAVE_TR1_UNORDERED_SET
|
||||
typedef std::tr1::unordered_set<string> Symbols;
|
||||
#else
|
||||
typedef std::set<string> Symbols;
|
||||
|
||||
@@ -131,7 +131,7 @@ static inline void mkStringNoCopy(Value & v, const char * s)
|
||||
|
||||
static inline void mkString(Value & v, const Symbol & s)
|
||||
{
|
||||
mkStringNoCopy(v, ((string) s).c_str());
|
||||
mkStringNoCopy(v, ((const string &) s).c_str());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <cctype>
|
||||
#include <exception>
|
||||
|
||||
#include <sys/time.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -135,6 +136,11 @@ static void initAndRun(int argc, char * * argv)
|
||||
everybody. */
|
||||
umask(0022);
|
||||
|
||||
/* Initialise the PRNG. */
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, 0);
|
||||
srandom(tv.tv_usec);
|
||||
|
||||
/* Process the NIX_LOG_TYPE environment variable. */
|
||||
string lt = getEnv("NIX_LOG_TYPE");
|
||||
if (lt != "") setLogType(lt);
|
||||
@@ -162,11 +168,10 @@ static void initAndRun(int argc, char * * argv)
|
||||
remaining.clear();
|
||||
|
||||
/* Process default options. */
|
||||
int verbosityDelta = lvlInfo;
|
||||
for (Strings::iterator i = args.begin(); i != args.end(); ++i) {
|
||||
string arg = *i;
|
||||
if (arg == "--verbose" || arg == "-v") verbosityDelta++;
|
||||
else if (arg == "--quiet") verbosityDelta--;
|
||||
if (arg == "--verbose" || arg == "-v") verbosity = (Verbosity) (verbosity + 1);
|
||||
else if (arg == "--quiet") verbosity = verbosity > lvlError ? (Verbosity) (verbosity - 1) : lvlError;
|
||||
else if (arg == "--log-type") {
|
||||
string s = getArg(arg, i, args.end());
|
||||
setLogType(s);
|
||||
@@ -213,8 +218,6 @@ static void initAndRun(int argc, char * * argv)
|
||||
else remaining.push_back(arg);
|
||||
}
|
||||
|
||||
verbosity = (Verbosity) (verbosityDelta < 0 ? 0 : verbosityDelta);
|
||||
|
||||
settings.update();
|
||||
|
||||
run(remaining);
|
||||
|
||||
+151
-115
@@ -7,7 +7,6 @@
|
||||
#include "local-store.hh"
|
||||
#include "util.hh"
|
||||
#include "archive.hh"
|
||||
#include "immutable.hh"
|
||||
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
@@ -44,6 +43,12 @@
|
||||
#include <sched.h>
|
||||
#endif
|
||||
|
||||
/* In GNU libc 2.11, <sys/mount.h> does not define `MS_PRIVATE', but
|
||||
<linux/fs.h> does. */
|
||||
#if !defined MS_PRIVATE && defined HAVE_LINUX_FS_H
|
||||
#include <linux/fs.h>
|
||||
#endif
|
||||
|
||||
#define CHROOT_ENABLED HAVE_CHROOT && HAVE_UNSHARE && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE) && defined(CLONE_NEWNS)
|
||||
|
||||
#if CHROOT_ENABLED
|
||||
@@ -93,7 +98,7 @@ typedef map<Path, WeakGoalPtr> WeakGoalMap;
|
||||
class Goal : public boost::enable_shared_from_this<Goal>
|
||||
{
|
||||
public:
|
||||
typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters} ExitCode;
|
||||
typedef enum {ecBusy, ecSuccess, ecFailed, ecNoSubstituters, ecIncompleteClosure} ExitCode;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -114,6 +119,10 @@ protected:
|
||||
failed because there are no substituters. */
|
||||
unsigned int nrNoSubstituters;
|
||||
|
||||
/* Number of substitution goals we are/were waiting for that
|
||||
failed because othey had unsubstitutable references. */
|
||||
unsigned int nrIncompleteClosure;
|
||||
|
||||
/* Name of this goal for debugging purposes. */
|
||||
string name;
|
||||
|
||||
@@ -122,7 +131,7 @@ protected:
|
||||
|
||||
Goal(Worker & worker) : worker(worker)
|
||||
{
|
||||
nrFailed = nrNoSubstituters = 0;
|
||||
nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
|
||||
exitCode = ecBusy;
|
||||
}
|
||||
|
||||
@@ -163,7 +172,7 @@ public:
|
||||
/* Cancel the goal. It should wake up its waiters, get rid of any
|
||||
running child processes that are being monitored by the worker
|
||||
(important!), etc. */
|
||||
virtual void cancel() = 0;
|
||||
virtual void cancel(bool timeout) = 0;
|
||||
|
||||
protected:
|
||||
void amDone(ExitCode result);
|
||||
@@ -177,9 +186,10 @@ struct Child
|
||||
{
|
||||
WeakGoalPtr goal;
|
||||
set<int> fds;
|
||||
bool monitorForSilence;
|
||||
bool respectTimeouts;
|
||||
bool inBuildSlot;
|
||||
time_t lastOutput; /* time we last got output on stdout/stderr */
|
||||
time_t timeStarted;
|
||||
};
|
||||
|
||||
typedef map<pid_t, Child> Children;
|
||||
@@ -223,9 +233,6 @@ private:
|
||||
/* Last time the goals in `waitingForAWhile' where woken up. */
|
||||
time_t lastWokenUp;
|
||||
|
||||
/* Last time `waitForInput' was last called. */
|
||||
time_t lastWait;
|
||||
|
||||
public:
|
||||
|
||||
/* Set if at least one derivation had a BuildError (i.e. permanent
|
||||
@@ -257,7 +264,7 @@ public:
|
||||
/* Registers a running child process. `inBuildSlot' means that
|
||||
the process counts towards the jobs limit. */
|
||||
void childStarted(GoalPtr goal, pid_t pid,
|
||||
const set<int> & fds, bool inBuildSlot, bool monitorForSilence);
|
||||
const set<int> & fds, bool inBuildSlot, bool respectTimeouts);
|
||||
|
||||
/* Unregisters a running child process. `wakeSleepers' should be
|
||||
false if there is no sense in waking up goals that are sleeping
|
||||
@@ -307,10 +314,12 @@ void Goal::waiteeDone(GoalPtr waitee, ExitCode result)
|
||||
trace(format("waitee `%1%' done; %2% left") %
|
||||
waitee->name % waitees.size());
|
||||
|
||||
if (result == ecFailed || result == ecNoSubstituters) ++nrFailed;
|
||||
if (result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure) ++nrFailed;
|
||||
|
||||
if (result == ecNoSubstituters) ++nrNoSubstituters;
|
||||
|
||||
if (result == ecIncompleteClosure) ++nrIncompleteClosure;
|
||||
|
||||
if (waitees.empty() || (result == ecFailed && !settings.keepGoing)) {
|
||||
|
||||
/* If we failed and keepGoing is not set, we remove all
|
||||
@@ -333,7 +342,7 @@ void Goal::amDone(ExitCode result)
|
||||
{
|
||||
trace("done");
|
||||
assert(exitCode == ecBusy);
|
||||
assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters);
|
||||
assert(result == ecSuccess || result == ecFailed || result == ecNoSubstituters || result == ecIncompleteClosure);
|
||||
exitCode = result;
|
||||
foreach (WeakGoals::iterator, i, waiters) {
|
||||
GoalPtr goal = i->lock();
|
||||
@@ -686,9 +695,7 @@ HookInstance::HookInstance()
|
||||
throw SysError("dupping builder's stdout/stderr");
|
||||
|
||||
execl(buildHook.c_str(), buildHook.c_str(), settings.thisSystem.c_str(),
|
||||
(format("%1%") % settings.maxSilentTime).str().c_str(),
|
||||
(format("%1%") % settings.printBuildTrace).str().c_str(),
|
||||
(format("%1%") % settings.buildTimeout).str().c_str(),
|
||||
NULL);
|
||||
|
||||
throw SysError(format("executing `%1%'") % buildHook);
|
||||
@@ -710,13 +717,7 @@ HookInstance::HookInstance()
|
||||
HookInstance::~HookInstance()
|
||||
{
|
||||
try {
|
||||
/* Cleanly shut down the hook by closing its stdin if it's not
|
||||
already building. Otherwise pid's destructor will kill
|
||||
it. */
|
||||
if (pid != -1 && toHook.writeSide != -1) {
|
||||
toHook.writeSide.close();
|
||||
pid.wait(true);
|
||||
}
|
||||
pid.kill();
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
@@ -763,6 +764,10 @@ private:
|
||||
/* Whether additional wanted outputs have been added. */
|
||||
bool needRestart;
|
||||
|
||||
/* Whether to retry substituting the outputs after building the
|
||||
inputs. */
|
||||
bool retrySubstitution;
|
||||
|
||||
/* The derivation stored at drvPath. */
|
||||
Derivation drv;
|
||||
|
||||
@@ -781,6 +786,12 @@ private:
|
||||
/* Outputs that are already valid. */
|
||||
PathSet validPaths;
|
||||
|
||||
/* Outputs that are corrupt or not valid. */
|
||||
PathSet missingPaths;
|
||||
|
||||
/* Paths that have been subject to hash rewriting. */
|
||||
PathSet rewrittenPaths;
|
||||
|
||||
/* User selected for running the builder. */
|
||||
UserLock buildUser;
|
||||
|
||||
@@ -819,7 +830,8 @@ private:
|
||||
GoalState state;
|
||||
|
||||
/* Stuff we need to pass to initChild(). */
|
||||
PathSet dirsInChroot;
|
||||
typedef map<Path, Path> DirsInChroot; // maps target path to source path
|
||||
DirsInChroot dirsInChroot;
|
||||
typedef map<string, string> Environment;
|
||||
Environment env;
|
||||
|
||||
@@ -832,6 +844,11 @@ private:
|
||||
bool repair;
|
||||
map<Path, Path> redirectedBadOutputs;
|
||||
|
||||
/* Set of inodes seen during calls to canonicalisePathMetaData()
|
||||
for this build's outputs. This needs to be shared between
|
||||
outputs to allow hard links between outputs. */
|
||||
InodesSeen inodesSeen;
|
||||
|
||||
/* Magic exit code denoting that setting up the child environment
|
||||
failed. (It's possible that the child actually returns the
|
||||
exit code, but ah well.) */
|
||||
@@ -841,7 +858,7 @@ public:
|
||||
DerivationGoal(const Path & drvPath, const StringSet & wantedOutputs, Worker & worker, bool repair = false);
|
||||
~DerivationGoal();
|
||||
|
||||
void cancel();
|
||||
void cancel(bool timeout);
|
||||
|
||||
void work();
|
||||
|
||||
@@ -911,6 +928,7 @@ DerivationGoal::DerivationGoal(const Path & drvPath, const StringSet & wantedOut
|
||||
: Goal(worker)
|
||||
, wantedOutputs(wantedOutputs)
|
||||
, needRestart(false)
|
||||
, retrySubstitution(false)
|
||||
, fLogFile(0)
|
||||
, bzLogFile(0)
|
||||
, useChroot(false)
|
||||
@@ -966,8 +984,10 @@ void DerivationGoal::killChild()
|
||||
}
|
||||
|
||||
|
||||
void DerivationGoal::cancel()
|
||||
void DerivationGoal::cancel(bool timeout)
|
||||
{
|
||||
if (timeout)
|
||||
printMsg(lvlError, format("@ build-failed %1% - timeout") % drvPath);
|
||||
killChild();
|
||||
amDone(ecFailed);
|
||||
}
|
||||
@@ -1067,10 +1087,15 @@ void DerivationGoal::outputsSubstituted()
|
||||
{
|
||||
trace("all outputs substituted (maybe)");
|
||||
|
||||
if (nrFailed > 0 && nrFailed > nrNoSubstituters && !settings.tryFallback)
|
||||
if (nrFailed > 0 && nrFailed > nrNoSubstituters + nrIncompleteClosure && !settings.tryFallback)
|
||||
throw Error(format("some substitutes for the outputs of derivation `%1%' failed; try `--fallback'") % drvPath);
|
||||
|
||||
nrFailed = nrNoSubstituters = 0;
|
||||
/* If the substitutes form an incomplete closure, then we should
|
||||
build the dependencies of this derivation, but after that, we
|
||||
can still use the substitutes for this derivation itself. */
|
||||
if (nrIncompleteClosure > 0 && !retrySubstitution) retrySubstitution = true;
|
||||
|
||||
nrFailed = nrNoSubstituters = nrIncompleteClosure = 0;
|
||||
|
||||
if (needRestart) {
|
||||
needRestart = false;
|
||||
@@ -1176,6 +1201,11 @@ void DerivationGoal::inputsRealised()
|
||||
return;
|
||||
}
|
||||
|
||||
if (retrySubstitution) {
|
||||
haveDerivation();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Gather information necessary for computing the closure and/or
|
||||
running the build hook. */
|
||||
|
||||
@@ -1286,6 +1316,9 @@ void DerivationGoal::tryToBuild()
|
||||
return;
|
||||
}
|
||||
|
||||
missingPaths = outputPaths(drv.outputs);
|
||||
foreach (PathSet::iterator, i, validPaths) missingPaths.erase(*i);
|
||||
|
||||
/* If any of the outputs already exist but are not valid, delete
|
||||
them. */
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||
@@ -1347,8 +1380,8 @@ void DerivationGoal::tryToBuild()
|
||||
outputLocks.unlock();
|
||||
buildUser.release();
|
||||
if (settings.printBuildTrace)
|
||||
printMsg(lvlError, format("@ build-failed %1% %2% %3% %4%")
|
||||
% drvPath % drv.outputs["out"].path % 0 % e.msg());
|
||||
printMsg(lvlError, format("@ build-failed %1% - %2% %3%")
|
||||
% drvPath % 0 % e.msg());
|
||||
worker.permanentFailure = true;
|
||||
amDone(ecFailed);
|
||||
return;
|
||||
@@ -1367,10 +1400,8 @@ void replaceValidPath(const Path & storePath, const Path tmpPath)
|
||||
way first. We'd better not be interrupted here, because if
|
||||
we're repairing (say) Glibc, we end up with a broken system. */
|
||||
Path oldPath = (format("%1%.old-%2%-%3%") % storePath % getpid() % rand()).str();
|
||||
if (pathExists(storePath)) {
|
||||
makeMutable(storePath);
|
||||
if (pathExists(storePath))
|
||||
rename(storePath.c_str(), oldPath.c_str());
|
||||
}
|
||||
if (rename(tmpPath.c_str(), storePath.c_str()) == -1)
|
||||
throw SysError(format("moving `%1%' to `%2%'") % tmpPath % storePath);
|
||||
if (pathExists(oldPath))
|
||||
@@ -1425,8 +1456,8 @@ void DerivationGoal::buildDone()
|
||||
/* Some cleanup per path. We do this here and not in
|
||||
computeClosure() for convenience when the build has
|
||||
failed. */
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||
Path path = i->second.path;
|
||||
foreach (PathSet::iterator, i, missingPaths) {
|
||||
Path path = *i;
|
||||
|
||||
/* If the output was already valid, just skip (discard) it. */
|
||||
if (validPaths.find(path) != validPaths.end()) continue;
|
||||
@@ -1463,6 +1494,12 @@ void DerivationGoal::buildDone()
|
||||
/* Apply hash rewriting if necessary. */
|
||||
if (!rewritesFromTmp.empty()) {
|
||||
printMsg(lvlError, format("warning: rewriting hashes in `%1%'; cross fingers") % path);
|
||||
|
||||
/* Canonicalise first. This ensures that the path
|
||||
we're rewriting doesn't contain a hard link to
|
||||
/etc/shadow or something like that. */
|
||||
canonicalisePathMetaData(path, buildUser.enabled() ? buildUser.getUID() : -1, inodesSeen);
|
||||
|
||||
/* FIXME: this is in-memory. */
|
||||
StringSink sink;
|
||||
dumpPath(path, sink);
|
||||
@@ -1470,6 +1507,8 @@ void DerivationGoal::buildDone()
|
||||
sink.s = rewriteHashes(sink.s, rewritesFromTmp);
|
||||
StringSource source(sink.s);
|
||||
restorePath(path, source);
|
||||
|
||||
rewrittenPaths.insert(path);
|
||||
}
|
||||
|
||||
/* Gain ownership of the build result using the setuid
|
||||
@@ -1522,11 +1561,11 @@ void DerivationGoal::buildDone()
|
||||
|
||||
if (settings.printBuildTrace) {
|
||||
if (hook && hookError)
|
||||
printMsg(lvlError, format("@ hook-failed %1% %2% %3% %4%")
|
||||
% drvPath % drv.outputs["out"].path % status % e.msg());
|
||||
printMsg(lvlError, format("@ hook-failed %1% - %2% %3%")
|
||||
% drvPath % status % e.msg());
|
||||
else
|
||||
printMsg(lvlError, format("@ build-failed %1% %2% %3% %4%")
|
||||
% drvPath % drv.outputs["out"].path % 1 % e.msg());
|
||||
printMsg(lvlError, format("@ build-failed %1% - %2% %3%")
|
||||
% drvPath % 1 % e.msg());
|
||||
}
|
||||
|
||||
/* Register the outputs of this build as "failed" so we won't
|
||||
@@ -1549,8 +1588,7 @@ void DerivationGoal::buildDone()
|
||||
buildUser.release();
|
||||
|
||||
if (settings.printBuildTrace) {
|
||||
printMsg(lvlError, format("@ build-succeeded %1% %2%")
|
||||
% drvPath % drv.outputs["out"].path);
|
||||
printMsg(lvlError, format("@ build-succeeded %1% -") % drvPath);
|
||||
}
|
||||
|
||||
amDone(ecSuccess);
|
||||
@@ -1629,11 +1667,11 @@ HookReply DerivationGoal::tryBuildHook()
|
||||
set<int> fds;
|
||||
fds.insert(hook->fromHook.readSide);
|
||||
fds.insert(hook->builderOut.readSide);
|
||||
worker.childStarted(shared_from_this(), hook->pid, fds, false, false);
|
||||
worker.childStarted(shared_from_this(), hook->pid, fds, false, true);
|
||||
|
||||
if (settings.printBuildTrace)
|
||||
printMsg(lvlError, format("@ build-started %1% %2% %3% %4%")
|
||||
% drvPath % drv.outputs["out"].path % drv.platform % logFile);
|
||||
printMsg(lvlError, format("@ build-started %1% - %2% %3%")
|
||||
% drvPath % drv.platform % logFile);
|
||||
|
||||
return rpAccept;
|
||||
}
|
||||
@@ -1655,9 +1693,7 @@ int childEntry(void * arg)
|
||||
|
||||
void DerivationGoal::startBuilder()
|
||||
{
|
||||
PathSet missing = outputPaths(drv.outputs);
|
||||
foreach (PathSet::iterator, i, validPaths) missing.erase(*i);
|
||||
startNest(nest, lvlInfo, format("building path(s) %1%") % showPaths(missing));
|
||||
startNest(nest, lvlInfo, format(repair ? "repairing path(s) %1%" : "building path(s) %1%") % showPaths(missingPaths));
|
||||
|
||||
/* Right platform? */
|
||||
if (!canBuildLocally(drv.platform))
|
||||
@@ -1678,7 +1714,8 @@ void DerivationGoal::startBuilder()
|
||||
if HOME is not set, but they will just assume that the settings file
|
||||
they are looking for does not exist if HOME is set but points to some
|
||||
non-existing path. */
|
||||
env["HOME"] = "/homeless-shelter";
|
||||
Path homeDir = "/homeless-shelter";
|
||||
env["HOME"] = homeDir;
|
||||
|
||||
/* Tell the builder where the Nix store is. Usually they
|
||||
shouldn't care, but this is useful for purity checking (e.g.,
|
||||
@@ -1869,8 +1906,14 @@ void DerivationGoal::startBuilder()
|
||||
|
||||
/* Bind-mount a user-configurable set of directories from the
|
||||
host file system. */
|
||||
dirsInChroot = settings.dirsInChroot;
|
||||
dirsInChroot.insert(tmpDir);
|
||||
foreach (StringSet::iterator, i, settings.dirsInChroot) {
|
||||
size_t p = i->find('=');
|
||||
if (p == string::npos)
|
||||
dirsInChroot[*i] = *i;
|
||||
else
|
||||
dirsInChroot[string(*i, 0, p)] = string(*i, p + 1);
|
||||
}
|
||||
dirsInChroot[tmpDir] = tmpDir;
|
||||
|
||||
/* Make the closure of the inputs available in the chroot,
|
||||
rather than the whole Nix store. This prevents any access
|
||||
@@ -1887,12 +1930,8 @@ void DerivationGoal::startBuilder()
|
||||
if (lstat(i->c_str(), &st))
|
||||
throw SysError(format("getting attributes of path `%1%'") % *i);
|
||||
if (S_ISDIR(st.st_mode))
|
||||
dirsInChroot.insert(*i);
|
||||
dirsInChroot[*i] = *i;
|
||||
else {
|
||||
/* Creating a hard link to *i is impossible if its
|
||||
immutable bit is set. So clear it first. */
|
||||
makeMutable(*i);
|
||||
|
||||
Path p = chrootRootDir + *i;
|
||||
if (link(i->c_str(), p.c_str()) == -1) {
|
||||
/* Hard-linking fails if we exceed the maximum
|
||||
@@ -1925,6 +1964,9 @@ void DerivationGoal::startBuilder()
|
||||
|
||||
else {
|
||||
|
||||
if (pathExists(homeDir))
|
||||
throw Error(format("directory `%1%' exists; please remove it") % homeDir);
|
||||
|
||||
/* We're not doing a chroot build, but we have some valid
|
||||
output paths. Since we can't just overwrite or delete
|
||||
them, we have to do hash rewriting: i.e. in the
|
||||
@@ -1942,7 +1984,7 @@ void DerivationGoal::startBuilder()
|
||||
/* If we're repairing, then we don't want to delete the
|
||||
corrupt outputs in advance. So rewrite them as well. */
|
||||
if (repair)
|
||||
foreach (PathSet::iterator, i, missing)
|
||||
foreach (PathSet::iterator, i, missingPaths)
|
||||
if (worker.store.isValidPath(*i) && pathExists(*i))
|
||||
redirectedBadOutputs[*i] = addHashRewrite(*i);
|
||||
}
|
||||
@@ -2008,8 +2050,8 @@ void DerivationGoal::startBuilder()
|
||||
singleton<set<int> >(builderOut.readSide), true, true);
|
||||
|
||||
if (settings.printBuildTrace) {
|
||||
printMsg(lvlError, format("@ build-started %1% %2% %3% %4%")
|
||||
% drvPath % drv.outputs["out"].path % drv.platform % logFile);
|
||||
printMsg(lvlError, format("@ build-started %1% - %2% %3%")
|
||||
% drvPath % drv.platform % logFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2062,9 +2104,9 @@ void DerivationGoal::initChild()
|
||||
/* Bind-mount all the directories from the "host"
|
||||
filesystem that we want in the chroot
|
||||
environment. */
|
||||
foreach (PathSet::iterator, i, dirsInChroot) {
|
||||
Path source = *i;
|
||||
Path target = chrootRootDir + source;
|
||||
foreach (DirsInChroot::iterator, i, dirsInChroot) {
|
||||
Path source = i->second;
|
||||
Path target = chrootRootDir + i->first;
|
||||
if (source == "/proc") continue; // backwards compatibility
|
||||
debug(format("bind mounting `%1%' to `%2%'") % source % target);
|
||||
createDirs(target);
|
||||
@@ -2227,6 +2269,8 @@ void DerivationGoal::computeClosure()
|
||||
output paths read-only. */
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||
Path path = i->second.path;
|
||||
if (missingPaths.find(path) == missingPaths.end()) continue;
|
||||
|
||||
if (!pathExists(path)) {
|
||||
throw BuildError(
|
||||
format("builder for `%1%' failed to produce output path `%2%'")
|
||||
@@ -2265,8 +2309,10 @@ void DerivationGoal::computeClosure()
|
||||
% path % i->second.hashAlgo % printHash16or32(h) % printHash16or32(h2));
|
||||
}
|
||||
|
||||
/* Get rid of all weird permissions. */
|
||||
canonicalisePathMetaData(path);
|
||||
/* Get rid of all weird permissions. This also checks that
|
||||
all files are owned by the build user, if applicable. */
|
||||
canonicalisePathMetaData(path,
|
||||
buildUser.enabled() && rewrittenPaths.find(path) == rewrittenPaths.end() ? buildUser.getUID() : -1, inodesSeen);
|
||||
|
||||
/* For this output path, find the references to other paths
|
||||
contained in it. Compute the SHA-256 NAR hash at the same
|
||||
@@ -2308,12 +2354,12 @@ void DerivationGoal::computeClosure()
|
||||
paths referenced by each of them. If there are cycles in the
|
||||
outputs, this will fail. */
|
||||
ValidPathInfos infos;
|
||||
foreach (DerivationOutputs::iterator, i, drv.outputs) {
|
||||
foreach (PathSet::iterator, i, missingPaths) {
|
||||
ValidPathInfo info;
|
||||
info.path = i->second.path;
|
||||
info.hash = contentHashes[i->second.path].first;
|
||||
info.narSize = contentHashes[i->second.path].second;
|
||||
info.references = allReferences[i->second.path];
|
||||
info.path = *i;
|
||||
info.hash = contentHashes[*i].first;
|
||||
info.narSize = contentHashes[*i].second;
|
||||
info.references = allReferences[*i];
|
||||
info.deriver = drvPath;
|
||||
infos.push_back(info);
|
||||
}
|
||||
@@ -2328,13 +2374,15 @@ Path DerivationGoal::openLogFile()
|
||||
{
|
||||
if (!settings.keepLog) return "";
|
||||
|
||||
string baseName = baseNameOf(drvPath);
|
||||
|
||||
/* Create a log file. */
|
||||
Path dir = (format("%1%/%2%") % settings.nixLogDir % drvsLogDir).str();
|
||||
Path dir = (format("%1%/%2%/%3%/") % settings.nixLogDir % drvsLogDir % string(baseName, 0, 2)).str();
|
||||
createDirs(dir);
|
||||
|
||||
if (settings.compressLog) {
|
||||
|
||||
Path logFileName = (format("%1%/%2%.bz2") % dir % baseNameOf(drvPath)).str();
|
||||
Path logFileName = (format("%1%/%2%.bz2") % dir % string(baseName, 2)).str();
|
||||
AutoCloseFD fd = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
|
||||
if (fd == -1) throw SysError(format("creating log file `%1%'") % logFileName);
|
||||
closeOnExec(fd);
|
||||
@@ -2349,7 +2397,7 @@ Path DerivationGoal::openLogFile()
|
||||
return logFileName;
|
||||
|
||||
} else {
|
||||
Path logFileName = (format("%1%/%2%") % dir % baseNameOf(drvPath)).str();
|
||||
Path logFileName = (format("%1%/%2%") % dir % string(baseName, 2)).str();
|
||||
fdLogFile = open(logFileName.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0666);
|
||||
if (fdLogFile == -1) throw SysError(format("creating log file `%1%'") % logFileName);
|
||||
closeOnExec(fdLogFile);
|
||||
@@ -2443,7 +2491,7 @@ bool DerivationGoal::pathFailed(const Path & path)
|
||||
printMsg(lvlError, format("builder for `%1%' failed previously (cached)") % path);
|
||||
|
||||
if (settings.printBuildTrace)
|
||||
printMsg(lvlError, format("@ build-failed %1% %2% cached") % drvPath % path);
|
||||
printMsg(lvlError, format("@ build-failed %1% - cached") % drvPath);
|
||||
|
||||
worker.permanentFailure = true;
|
||||
amDone(ecFailed);
|
||||
@@ -2514,7 +2562,7 @@ public:
|
||||
SubstitutionGoal(const Path & storePath, Worker & worker, bool repair = false);
|
||||
~SubstitutionGoal();
|
||||
|
||||
void cancel();
|
||||
void cancel(bool timeout);
|
||||
|
||||
void work();
|
||||
|
||||
@@ -2555,8 +2603,10 @@ SubstitutionGoal::~SubstitutionGoal()
|
||||
}
|
||||
|
||||
|
||||
void SubstitutionGoal::cancel()
|
||||
void SubstitutionGoal::cancel(bool timeout)
|
||||
{
|
||||
if (timeout)
|
||||
printMsg(lvlError, format("@ substituter-failed %1% timeout") % storePath);
|
||||
if (pid != -1) {
|
||||
pid_t savedPid = pid;
|
||||
pid.kill();
|
||||
@@ -2638,7 +2688,7 @@ void SubstitutionGoal::referencesValid()
|
||||
|
||||
if (nrFailed > 0) {
|
||||
debug(format("some references of path `%1%' could not be realised") % storePath);
|
||||
amDone(ecFailed);
|
||||
amDone(nrNoSubstituters > 0 || nrIncompleteClosure > 0 ? ecIncompleteClosure : ecFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2747,10 +2797,8 @@ void SubstitutionGoal::tryToRun()
|
||||
|
||||
state = &SubstitutionGoal::finished;
|
||||
|
||||
if (settings.printBuildTrace) {
|
||||
printMsg(lvlError, format("@ substituter-started %1% %2%")
|
||||
% storePath % sub);
|
||||
}
|
||||
if (settings.printBuildTrace)
|
||||
printMsg(lvlError, format("@ substituter-started %1% %2%") % storePath % sub);
|
||||
}
|
||||
|
||||
|
||||
@@ -2816,7 +2864,7 @@ void SubstitutionGoal::finished()
|
||||
return;
|
||||
}
|
||||
|
||||
canonicalisePathMetaData(destPath);
|
||||
canonicalisePathMetaData(destPath, -1);
|
||||
|
||||
worker.store.optimisePath(destPath); // FIXME: combine with hashPath()
|
||||
|
||||
@@ -2837,9 +2885,8 @@ void SubstitutionGoal::finished()
|
||||
printMsg(lvlChatty,
|
||||
format("substitution of path `%1%' succeeded") % storePath);
|
||||
|
||||
if (settings.printBuildTrace) {
|
||||
if (settings.printBuildTrace)
|
||||
printMsg(lvlError, format("@ substituter-succeeded %1%") % storePath);
|
||||
}
|
||||
|
||||
amDone(ecSuccess);
|
||||
}
|
||||
@@ -2967,14 +3014,14 @@ unsigned Worker::getNrLocalBuilds()
|
||||
|
||||
void Worker::childStarted(GoalPtr goal,
|
||||
pid_t pid, const set<int> & fds, bool inBuildSlot,
|
||||
bool monitorForSilence)
|
||||
bool respectTimeouts)
|
||||
{
|
||||
Child child;
|
||||
child.goal = goal;
|
||||
child.fds = fds;
|
||||
child.lastOutput = time(0);
|
||||
child.timeStarted = child.lastOutput = time(0);
|
||||
child.inBuildSlot = inBuildSlot;
|
||||
child.monitorForSilence = monitorForSilence;
|
||||
child.respectTimeouts = respectTimeouts;
|
||||
children[pid] = child;
|
||||
if (inBuildSlot) nrLocalBuilds++;
|
||||
}
|
||||
@@ -3090,31 +3137,22 @@ void Worker::waitForInput()
|
||||
timeout.tv_usec = 0;
|
||||
time_t before = time(0);
|
||||
|
||||
/* If a global timeout has been set, sleep until it's done. */
|
||||
if (settings.buildTimeout != 0) {
|
||||
useTimeout = true;
|
||||
if (lastWait == 0 || lastWait > before) lastWait = before;
|
||||
timeout.tv_sec = std::max((time_t) 0, lastWait + settings.buildTimeout - before);
|
||||
/* If we're monitoring for silence on stdout/stderr, or if there
|
||||
is a build timeout, then wait for input until the first
|
||||
deadline for any child. */
|
||||
assert(sizeof(time_t) >= sizeof(long));
|
||||
time_t nearest = LONG_MAX; // nearest deadline
|
||||
foreach (Children::iterator, i, children) {
|
||||
if (!i->second.respectTimeouts) continue;
|
||||
if (settings.maxSilentTime != 0)
|
||||
nearest = std::min(nearest, i->second.lastOutput + settings.maxSilentTime);
|
||||
if (settings.buildTimeout != 0)
|
||||
nearest = std::min(nearest, i->second.timeStarted + settings.buildTimeout);
|
||||
}
|
||||
|
||||
/* If we're monitoring for silence on stdout/stderr, sleep until
|
||||
the first deadline for any child. */
|
||||
if (settings.maxSilentTime != 0) {
|
||||
time_t oldest = 0;
|
||||
foreach (Children::iterator, i, children) {
|
||||
if (i->second.monitorForSilence) {
|
||||
oldest = oldest == 0 || i->second.lastOutput < oldest
|
||||
? i->second.lastOutput : oldest;
|
||||
}
|
||||
}
|
||||
if (oldest) {
|
||||
time_t silenceTimeout = std::max((time_t) 0, oldest + settings.maxSilentTime - before);
|
||||
timeout.tv_sec = useTimeout
|
||||
? std::min(silenceTimeout, timeout.tv_sec)
|
||||
: silenceTimeout;
|
||||
useTimeout = true;
|
||||
printMsg(lvlVomit, format("sleeping %1% seconds") % timeout.tv_sec);
|
||||
}
|
||||
if (nearest != LONG_MAX) {
|
||||
timeout.tv_sec = std::max((time_t) 1, nearest - before);
|
||||
useTimeout = true;
|
||||
printMsg(lvlVomit, format("sleeping %1% seconds") % timeout.tv_sec);
|
||||
}
|
||||
|
||||
/* If we are polling goals that are waiting for a lock, then wake
|
||||
@@ -3124,7 +3162,7 @@ void Worker::waitForInput()
|
||||
if (lastWokenUp == 0)
|
||||
printMsg(lvlError, "waiting for locks or build slots...");
|
||||
if (lastWokenUp == 0 || lastWokenUp > before) lastWokenUp = before;
|
||||
timeout.tv_sec = std::max((time_t) 0, (time_t) (lastWokenUp + settings.pollInterval - before));
|
||||
timeout.tv_sec = std::max((time_t) 1, (time_t) (lastWokenUp + settings.pollInterval - before));
|
||||
} else lastWokenUp = 0;
|
||||
|
||||
using namespace std;
|
||||
@@ -3148,9 +3186,6 @@ void Worker::waitForInput()
|
||||
|
||||
time_t after = time(0);
|
||||
|
||||
/* Keep track of when we were last called. */
|
||||
lastWait = after;
|
||||
|
||||
/* Process all available file descriptors. */
|
||||
|
||||
/* Since goals may be canceled from inside the loop below (causing
|
||||
@@ -3191,22 +3226,23 @@ void Worker::waitForInput()
|
||||
}
|
||||
|
||||
if (settings.maxSilentTime != 0 &&
|
||||
j->second.monitorForSilence &&
|
||||
j->second.respectTimeouts &&
|
||||
after - j->second.lastOutput >= (time_t) settings.maxSilentTime)
|
||||
{
|
||||
printMsg(lvlError,
|
||||
format("%1% timed out after %2% seconds of silence")
|
||||
% goal->getName() % settings.maxSilentTime);
|
||||
goal->cancel();
|
||||
goal->cancel(true);
|
||||
}
|
||||
|
||||
if (settings.buildTimeout != 0 &&
|
||||
after - before >= (time_t) settings.buildTimeout)
|
||||
j->second.respectTimeouts &&
|
||||
after - j->second.timeStarted >= (time_t) settings.buildTimeout)
|
||||
{
|
||||
printMsg(lvlError,
|
||||
format("%1% timed out after %2% seconds of activity")
|
||||
format("%1% timed out after %2% seconds")
|
||||
% goal->getName() % settings.buildTimeout);
|
||||
goal->cancel();
|
||||
goal->cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+146
-140
@@ -1,7 +1,6 @@
|
||||
#include "globals.hh"
|
||||
#include "misc.hh"
|
||||
#include "local-store.hh"
|
||||
#include "immutable.hh"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
@@ -373,9 +372,9 @@ static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
|
||||
|
||||
string result = runProgram(rootFinder);
|
||||
|
||||
Strings paths = tokenizeString<Strings>(result, "\n");
|
||||
StringSet paths = tokenizeString<StringSet>(result, "\n");
|
||||
|
||||
foreach (Strings::iterator, i, paths) {
|
||||
foreach (StringSet::iterator, i, paths) {
|
||||
if (isInStore(*i)) {
|
||||
Path path = toStorePath(*i);
|
||||
if (roots.find(path) == roots.end() && store.isValidPath(path)) {
|
||||
@@ -396,24 +395,17 @@ struct LocalStore::GCState
|
||||
GCResults & results;
|
||||
PathSet roots;
|
||||
PathSet tempRoots;
|
||||
PathSet deleted;
|
||||
PathSet live;
|
||||
PathSet busy;
|
||||
PathSet invalidated;
|
||||
PathSet dead;
|
||||
PathSet alive;
|
||||
bool gcKeepOutputs;
|
||||
bool gcKeepDerivations;
|
||||
unsigned long long bytesInvalidated;
|
||||
Path trashDir;
|
||||
bool shouldDelete;
|
||||
GCState(GCResults & results_) : results(results_), bytesInvalidated(0) { }
|
||||
};
|
||||
|
||||
|
||||
static bool shouldDelete(GCOptions::GCAction action)
|
||||
{
|
||||
return action == GCOptions::gcDeleteDead
|
||||
|| action == GCOptions::gcDeleteSpecific;
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::isActiveTempFile(const GCState & state,
|
||||
const Path & path, const string & suffix)
|
||||
{
|
||||
@@ -424,152 +416,149 @@ bool LocalStore::isActiveTempFile(const GCState & state,
|
||||
|
||||
void LocalStore::deleteGarbage(GCState & state, const Path & path)
|
||||
{
|
||||
printMsg(lvlInfo, format("deleting `%1%'") % path);
|
||||
unsigned long long bytesFreed;
|
||||
deletePathWrapped(path, bytesFreed);
|
||||
state.results.bytesFreed += bytesFreed;
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||
void LocalStore::deletePathRecursive(GCState & state, const Path & path)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
if (path == linksDir) return true;
|
||||
unsigned long long size = 0;
|
||||
|
||||
if (isValidPath(path)) {
|
||||
PathSet referrers;
|
||||
queryReferrers(path, referrers);
|
||||
foreach (PathSet::iterator, i, referrers)
|
||||
if (*i != path) deletePathRecursive(state, *i);
|
||||
size = queryPathInfo(path).narSize;
|
||||
invalidatePathChecked(path);
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st)) {
|
||||
if (errno == ENOENT) return true;
|
||||
if (errno == ENOENT) return;
|
||||
throw SysError(format("getting status of %1%") % path);
|
||||
}
|
||||
|
||||
if (state.deleted.find(path) != state.deleted.end()) return true;
|
||||
if (state.live.find(path) != state.live.end()) return false;
|
||||
printMsg(lvlInfo, format("deleting `%1%'") % path);
|
||||
|
||||
state.results.paths.insert(path);
|
||||
|
||||
/* If the path is not a regular file or symlink, move it to the
|
||||
trash directory. The move is to ensure that later (when we're
|
||||
not holding the global GC lock) we can delete the path without
|
||||
being afraid that the path has become alive again. Otherwise
|
||||
delete it right away. */
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
// Estimate the amount freed using the narSize field. FIXME:
|
||||
// if the path was not valid, need to determine the actual
|
||||
// size.
|
||||
state.bytesInvalidated += size;
|
||||
// Mac OS X cannot rename directories if they are read-only.
|
||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError(format("making `%1%' writable") % path);
|
||||
Path tmp = state.trashDir + "/" + baseNameOf(path);
|
||||
if (rename(path.c_str(), tmp.c_str()))
|
||||
throw SysError(format("unable to rename `%1%' to `%2%'") % path % tmp);
|
||||
} else
|
||||
deleteGarbage(state, path);
|
||||
|
||||
if (state.results.bytesFreed + state.bytesInvalidated > state.options.maxFreed) {
|
||||
printMsg(lvlInfo, format("deleted or invalidated more than %1% bytes; stopping") % state.options.maxFreed);
|
||||
throw GCLimitReached();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool LocalStore::canReachRoot(GCState & state, PathSet & visited, const Path & path)
|
||||
{
|
||||
if (visited.find(path) != visited.end()) return false;
|
||||
|
||||
if (state.alive.find(path) != state.alive.end()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (state.dead.find(path) != state.dead.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (state.roots.find(path) != state.roots.end()) {
|
||||
printMsg(lvlDebug, format("cannot delete `%1%' because it's a root") % path);
|
||||
state.alive.insert(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
visited.insert(path);
|
||||
|
||||
if (!isValidPath(path)) return false;
|
||||
|
||||
PathSet incoming;
|
||||
|
||||
/* Don't delete this path if any of its referrers are alive. */
|
||||
queryReferrers(path, incoming);
|
||||
|
||||
/* If gc-keep-derivations is set and this is a derivation, then
|
||||
don't delete the derivation if any of the outputs are alive. */
|
||||
if (state.gcKeepDerivations && isDerivation(path)) {
|
||||
PathSet outputs = queryDerivationOutputs(path);
|
||||
foreach (PathSet::iterator, i, outputs)
|
||||
if (isValidPath(*i) && queryDeriver(*i) == path)
|
||||
incoming.insert(*i);
|
||||
}
|
||||
|
||||
/* If gc-keep-outputs is set, then don't delete this path if there
|
||||
are derivers of this path that are not garbage. */
|
||||
if (state.gcKeepOutputs) {
|
||||
PathSet derivers = queryValidDerivers(path);
|
||||
foreach (PathSet::iterator, i, derivers)
|
||||
incoming.insert(*i);
|
||||
}
|
||||
|
||||
foreach (PathSet::iterator, i, incoming)
|
||||
if (*i != path)
|
||||
if (canReachRoot(state, visited, *i)) {
|
||||
state.alive.insert(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::tryToDelete(GCState & state, const Path & path)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
if (path == linksDir || path == state.trashDir) return;
|
||||
|
||||
startNest(nest, lvlDebug, format("considering whether to delete `%1%'") % path);
|
||||
|
||||
/* If gc-keep-outputs and gc-keep-derivations are both set, we can
|
||||
have cycles in the liveness graph, so we need to treat such
|
||||
strongly connected components as a single unit (‘paths’). That
|
||||
is, we can delete the elements of ‘paths’ only if all referrers
|
||||
of ‘paths’ are garbage. */
|
||||
PathSet paths, referrers;
|
||||
Paths pathsSorted;
|
||||
|
||||
if (isValidPath(path)) {
|
||||
|
||||
/* Add derivers and outputs of ‘path’ to ‘paths’. */
|
||||
PathSet todo;
|
||||
todo.insert(path);
|
||||
while (!todo.empty()) {
|
||||
Path p = *todo.begin();
|
||||
assertStorePath(p);
|
||||
todo.erase(p);
|
||||
if (paths.find(p) != paths.end()) continue;
|
||||
paths.insert(p);
|
||||
/* If gc-keep-derivations is set and this is a derivation,
|
||||
then don't delete the derivation if any of the outputs
|
||||
are live. */
|
||||
if (state.gcKeepDerivations && isDerivation(p)) {
|
||||
PathSet outputs = queryDerivationOutputs(p);
|
||||
foreach (PathSet::iterator, i, outputs)
|
||||
if (isValidPath(*i)) todo.insert(*i);
|
||||
}
|
||||
/* If gc-keep-outputs is set, then don't delete this path
|
||||
if there are derivers of this path that are not
|
||||
garbage. */
|
||||
if (state.gcKeepOutputs) {
|
||||
PathSet derivers = queryValidDerivers(p);
|
||||
foreach (PathSet::iterator, i, derivers) todo.insert(*i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
if (!isValidPath(path)) {
|
||||
/* A lock file belonging to a path that we're building right
|
||||
now isn't garbage. */
|
||||
if (isActiveTempFile(state, path, ".lock")) return false;
|
||||
if (isActiveTempFile(state, path, ".lock")) return;
|
||||
|
||||
/* Don't delete .chroot directories for derivations that are
|
||||
currently being built. */
|
||||
if (isActiveTempFile(state, path, ".chroot")) return false;
|
||||
|
||||
paths.insert(path);
|
||||
if (isActiveTempFile(state, path, ".chroot")) return;
|
||||
}
|
||||
|
||||
/* Check if any path in ‘paths’ is a root. */
|
||||
foreach (PathSet::iterator, i, paths)
|
||||
if (state.roots.find(*i) != state.roots.end()) {
|
||||
printMsg(lvlDebug, format("cannot delete `%1%' because it's a root") % *i);
|
||||
goto isLive;
|
||||
}
|
||||
PathSet visited;
|
||||
|
||||
/* Recursively try to delete the referrers of this strongly
|
||||
connected component. If any referrer can't be deleted, then
|
||||
these paths can't be deleted either. */
|
||||
foreach (PathSet::iterator, i, paths)
|
||||
if (isValidPath(*i)) queryReferrers(*i, referrers);
|
||||
|
||||
foreach (PathSet::iterator, i, referrers)
|
||||
if (paths.find(*i) == paths.end() && !tryToDelete(state, *i)) {
|
||||
printMsg(lvlDebug, format("cannot delete `%1%' because it has live referrers") % *i);
|
||||
goto isLive;
|
||||
}
|
||||
|
||||
/* The paths are garbage, so delete them. */
|
||||
pathsSorted = topoSortPaths(*this, paths);
|
||||
foreach (Paths::iterator, i, pathsSorted) {
|
||||
if (shouldDelete(state.options.action)) {
|
||||
|
||||
/* If it's a valid path that's not a regular file or
|
||||
symlink, invalidate it, rename it, and schedule it for
|
||||
deletion. The renaming is to ensure that later (when
|
||||
we're not holding the global GC lock) we can delete the
|
||||
path without being afraid that the path has become
|
||||
alive again. Otherwise delete it right away. */
|
||||
if (isValidPath(*i)) {
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
printMsg(lvlInfo, format("invalidating `%1%'") % *i);
|
||||
// Estimate the amount freed using the narSize field.
|
||||
state.bytesInvalidated += queryPathInfo(*i).narSize;
|
||||
invalidatePathChecked(*i);
|
||||
makeMutable(i->c_str());
|
||||
// Mac OS X cannot rename directories if they are read-only.
|
||||
if (chmod(i->c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError(format("making `%1%' writable") % *i);
|
||||
Path tmp = (format("%1%-gc-%2%") % *i % getpid()).str();
|
||||
if (rename(i->c_str(), tmp.c_str()))
|
||||
throw SysError(format("unable to rename `%1%' to `%2%'") % *i % tmp);
|
||||
state.invalidated.insert(tmp);
|
||||
} else {
|
||||
invalidatePathChecked(*i);
|
||||
deleteGarbage(state, *i);
|
||||
}
|
||||
} else
|
||||
deleteGarbage(state, *i);
|
||||
|
||||
if (state.results.bytesFreed + state.bytesInvalidated > state.options.maxFreed) {
|
||||
printMsg(lvlInfo, format("deleted or invalidated more than %1% bytes; stopping") % state.options.maxFreed);
|
||||
throw GCLimitReached();
|
||||
}
|
||||
|
||||
} else
|
||||
printMsg(lvlTalkative, format("would delete `%1%'") % *i);
|
||||
|
||||
state.deleted.insert(*i);
|
||||
if (state.options.action != GCOptions::gcReturnLive)
|
||||
state.results.paths.insert(*i);
|
||||
if (canReachRoot(state, visited, path)) {
|
||||
printMsg(lvlDebug, format("cannot delete `%1%' because it's still reachable") % path);
|
||||
} else {
|
||||
/* No path we visited was a root, so everything is garbage.
|
||||
But we only delete ‘path’ and its referrers here so that
|
||||
‘nix-store --delete’ doesn't have the unexpected effect of
|
||||
recursing into derivations and outputs. */
|
||||
state.dead.insert(visited.begin(), visited.end());
|
||||
if (state.shouldDelete)
|
||||
deletePathRecursive(state, path);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
isLive:
|
||||
foreach (PathSet::iterator, i, paths) {
|
||||
state.live.insert(*i);
|
||||
if (state.options.action == GCOptions::gcReturnLive)
|
||||
state.results.paths.insert(*i);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -625,7 +614,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
{
|
||||
GCState state(results);
|
||||
state.options = options;
|
||||
|
||||
state.trashDir = settings.nixStore + "/trash";
|
||||
state.gcKeepOutputs = settings.gcKeepOutputs;
|
||||
state.gcKeepDerivations = settings.gcKeepDerivations;
|
||||
|
||||
@@ -638,6 +627,8 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
state.gcKeepDerivations = false;
|
||||
}
|
||||
|
||||
state.shouldDelete = options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific;
|
||||
|
||||
/* Acquire the global GC root. This prevents
|
||||
a) New roots from being added.
|
||||
b) Processes from creating new temporary root files. */
|
||||
@@ -668,6 +659,11 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
increase, since we hold locks on everything. So everything
|
||||
that is not reachable from `roots'. */
|
||||
|
||||
if (state.shouldDelete) {
|
||||
if (pathExists(state.trashDir)) deleteGarbage(state, state.trashDir);
|
||||
createDirs(state.trashDir);
|
||||
}
|
||||
|
||||
/* Now either delete all garbage paths, or just the specified
|
||||
paths (for gcDeleteSpecific). */
|
||||
|
||||
@@ -675,13 +671,14 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
|
||||
foreach (PathSet::iterator, i, options.pathsToDelete) {
|
||||
assertStorePath(*i);
|
||||
if (!tryToDelete(state, *i))
|
||||
tryToDelete(state, *i);
|
||||
if (state.dead.find(*i) == state.dead.end())
|
||||
throw Error(format("cannot delete path `%1%' since it is still alive") % *i);
|
||||
}
|
||||
|
||||
} else if (options.maxFreed > 0) {
|
||||
|
||||
if (shouldDelete(state.options.action))
|
||||
if (state.shouldDelete)
|
||||
printMsg(lvlError, format("deleting garbage..."));
|
||||
else
|
||||
printMsg(lvlError, format("determining live/dead paths..."));
|
||||
@@ -727,13 +724,22 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results)
|
||||
}
|
||||
}
|
||||
|
||||
if (state.options.action == GCOptions::gcReturnLive) {
|
||||
state.results.paths = state.alive;
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.options.action == GCOptions::gcReturnDead) {
|
||||
state.results.paths = state.dead;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Allow other processes to add to the store from here on. */
|
||||
fdGCLock.close();
|
||||
|
||||
/* Delete the invalidated paths now that the lock has been
|
||||
released. */
|
||||
foreach (PathSet::iterator, i, state.invalidated)
|
||||
deleteGarbage(state, *i);
|
||||
/* Delete the trash directory. */
|
||||
printMsg(lvlInfo, format("deleting `%1%'") % state.trashDir);
|
||||
deleteGarbage(state, state.trashDir);
|
||||
|
||||
/* Clean up the links directory. */
|
||||
if (options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific) {
|
||||
|
||||
+10
-1
@@ -10,6 +10,14 @@
|
||||
namespace nix {
|
||||
|
||||
|
||||
/* The default location of the daemon socket, relative to nixStateDir.
|
||||
The socket is in a directory to allow you to control access to the
|
||||
Nix daemon by setting the mode/ownership of the directory
|
||||
appropriately. (This wouldn't work on the socket itself since it
|
||||
must be deleted and recreated on startup.) */
|
||||
#define DEFAULT_SOCKET_PATH "/daemon-socket/socket"
|
||||
|
||||
|
||||
Settings settings;
|
||||
|
||||
|
||||
@@ -58,6 +66,7 @@ void Settings::processEnvironment()
|
||||
nixConfDir = canonPath(getEnv("NIX_CONF_DIR", NIX_CONF_DIR));
|
||||
nixLibexecDir = canonPath(getEnv("NIX_LIBEXEC_DIR", NIX_LIBEXEC_DIR));
|
||||
nixBinDir = canonPath(getEnv("NIX_BIN_DIR", NIX_BIN_DIR));
|
||||
nixDaemonSocketFile = canonPath(nixStateDir + DEFAULT_SOCKET_PATH);
|
||||
|
||||
string subs = getEnv("NIX_SUBSTITUTERS", "default");
|
||||
if (subs == "default") {
|
||||
@@ -158,7 +167,7 @@ void Settings::get(bool & res, const string & name)
|
||||
}
|
||||
|
||||
|
||||
void Settings::get(PathSet & res, const string & name)
|
||||
void Settings::get(StringSet & res, const string & name)
|
||||
{
|
||||
SettingsMap::iterator i = settings.find(name);
|
||||
if (i == settings.end()) return;
|
||||
|
||||
@@ -50,6 +50,9 @@ struct Settings {
|
||||
/* The directory where the main programs are stored. */
|
||||
Path nixBinDir;
|
||||
|
||||
/* File name of the socket the daemon listens to. */
|
||||
Path nixDaemonSocketFile;
|
||||
|
||||
/* Whether to keep temporary directories of failed builds. */
|
||||
bool keepFailed;
|
||||
|
||||
@@ -139,7 +142,7 @@ struct Settings {
|
||||
|
||||
/* The directories from the host filesystem to be included in the
|
||||
chroot. */
|
||||
PathSet dirsInChroot;
|
||||
StringSet dirsInChroot;
|
||||
|
||||
/* Whether to impersonate a Linux 2.6 machine on newer kernels. */
|
||||
bool impersonateLinux26;
|
||||
@@ -181,7 +184,7 @@ private:
|
||||
|
||||
void get(string & res, const string & name);
|
||||
void get(bool & res, const string & name);
|
||||
void get(PathSet & res, const string & name);
|
||||
void get(StringSet & res, const string & name);
|
||||
template<class N> void get(N & res, const string & name);
|
||||
};
|
||||
|
||||
|
||||
+235
-92
@@ -5,7 +5,6 @@
|
||||
#include "pathlocks.hh"
|
||||
#include "worker-protocol.hh"
|
||||
#include "derivations.hh"
|
||||
#include "immutable.hh"
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
@@ -20,11 +19,18 @@
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
#if HAVE_UNSHARE
|
||||
#if HAVE_UNSHARE && HAVE_STATVFS && HAVE_SYS_MOUNT_H
|
||||
#include <sched.h>
|
||||
#include <sys/statvfs.h>
|
||||
#include <sys/mount.h>
|
||||
#endif
|
||||
|
||||
#if HAVE_LINUX_FS_H
|
||||
#include <linux/fs.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
|
||||
@@ -42,13 +48,17 @@ static void throwSQLiteError(sqlite3 * db, const format & f)
|
||||
{
|
||||
int err = sqlite3_errcode(db);
|
||||
if (err == SQLITE_BUSY) {
|
||||
printMsg(lvlError, "warning: SQLite database is busy");
|
||||
static bool warned = false;
|
||||
if (!warned) {
|
||||
printMsg(lvlError, "warning: SQLite database is busy");
|
||||
warned = true;
|
||||
}
|
||||
/* Sleep for a while since retrying the transaction right away
|
||||
is likely to fail again. */
|
||||
#if HAVE_NANOSLEEP
|
||||
struct timespec t;
|
||||
t.tv_sec = 0;
|
||||
t.tv_nsec = 100 * 1000 * 1000; /* 0.1s */
|
||||
t.tv_nsec = (random() % 100) * 1000 * 1000; /* <= 0.1s */
|
||||
nanosleep(&t, 0);
|
||||
#else
|
||||
sleep(1);
|
||||
@@ -292,6 +302,7 @@ LocalStore::LocalStore(bool reserveSpace)
|
||||
curSchema = getSchema();
|
||||
|
||||
if (curSchema < 6) upgradeStore6();
|
||||
else if (curSchema < 7) { upgradeStore7(); openDB(true); }
|
||||
|
||||
writeFile(schemaPath, (format("%1%") % nixSchemaVersion).str());
|
||||
|
||||
@@ -424,30 +435,20 @@ void LocalStore::openDB(bool create)
|
||||
bind mount. So make the Nix store writable for this process. */
|
||||
void LocalStore::makeStoreWritable()
|
||||
{
|
||||
#if HAVE_UNSHARE
|
||||
#if HAVE_UNSHARE && HAVE_STATVFS && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_REMOUNT)
|
||||
if (getuid() != 0) return;
|
||||
/* Check if /nix/store is on a read-only mount. */
|
||||
struct statvfs stat;
|
||||
if (statvfs(settings.nixStore.c_str(), &stat) !=0)
|
||||
throw SysError("Getting info of nix store mountpoint");
|
||||
|
||||
if (!pathExists("/proc/self/mountinfo")) return;
|
||||
if (stat.f_flag & ST_RDONLY) {
|
||||
if (unshare(CLONE_NEWNS) == -1)
|
||||
throw SysError("setting up a private mount namespace");
|
||||
|
||||
/* Check if /nix/store is a read-only bind mount. */
|
||||
bool found = false;
|
||||
Strings mounts = tokenizeString<Strings>(readFile("/proc/self/mountinfo", true), "\n");
|
||||
foreach (Strings::iterator, i, mounts) {
|
||||
vector<string> fields = tokenizeString<vector<string> >(*i, " ");
|
||||
if (fields.at(3) == "/" || fields.at(4) != settings.nixStore) continue;
|
||||
Strings options = tokenizeString<Strings>(fields.at(5), ",");
|
||||
if (std::find(options.begin(), options.end(), "ro") == options.end()) continue;
|
||||
found = true;
|
||||
break;
|
||||
if (mount(0, settings.nixStore.c_str(), 0, MS_REMOUNT | MS_BIND, 0) == -1)
|
||||
throw SysError(format("remounting %1% writable") % settings.nixStore);
|
||||
}
|
||||
|
||||
if (!found) return;
|
||||
|
||||
if (unshare(CLONE_NEWNS) == -1)
|
||||
throw SysError("setting up a private mount namespace");
|
||||
|
||||
if (mount(0, settings.nixStore.c_str(), 0, MS_REMOUNT | MS_BIND, 0) == -1)
|
||||
throw SysError(format("remounting %1% writable") % settings.nixStore);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -455,36 +456,8 @@ void LocalStore::makeStoreWritable()
|
||||
const time_t mtimeStore = 1; /* 1 second into the epoch */
|
||||
|
||||
|
||||
void canonicalisePathMetaData(const Path & path, bool recurse)
|
||||
static void canonicaliseTimestampAndPermissions(const Path & path, const struct stat & st)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||
|
||||
/* Really make sure that the path is of a supported type. This
|
||||
has already been checked in dumpPath(). */
|
||||
assert(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode));
|
||||
|
||||
/* Change ownership to the current uid. If it's a symlink, use
|
||||
lchown if available, otherwise don't bother. Wrong ownership
|
||||
of a symlink doesn't matter, since the owning user can't change
|
||||
the symlink and can't delete it because the directory is not
|
||||
writable. The only exception is top-level paths in the Nix
|
||||
store (since that directory is group-writable for the Nix build
|
||||
users group); we check for this case below. */
|
||||
if (st.st_uid != geteuid()) {
|
||||
#if HAVE_LCHOWN
|
||||
if (lchown(path.c_str(), geteuid(), (gid_t) -1) == -1)
|
||||
#else
|
||||
if (!S_ISLNK(st.st_mode) &&
|
||||
chown(path.c_str(), geteuid(), (gid_t) -1) == -1)
|
||||
#endif
|
||||
throw SysError(format("changing owner of `%1%' to %2%")
|
||||
% path % geteuid());
|
||||
}
|
||||
|
||||
if (!S_ISLNK(st.st_mode)) {
|
||||
|
||||
/* Mask out all type related bits. */
|
||||
@@ -515,18 +488,78 @@ void canonicalisePathMetaData(const Path & path, bool recurse)
|
||||
#endif
|
||||
throw SysError(format("changing modification time of `%1%'") % path);
|
||||
}
|
||||
}
|
||||
|
||||
if (recurse && S_ISDIR(st.st_mode)) {
|
||||
|
||||
void canonicaliseTimestampAndPermissions(const Path & path)
|
||||
{
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||
canonicaliseTimestampAndPermissions(path, st);
|
||||
}
|
||||
|
||||
|
||||
static void canonicalisePathMetaData_(const Path & path, uid_t fromUid, InodesSeen & inodesSeen)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||
|
||||
/* Really make sure that the path is of a supported type. This
|
||||
has already been checked in dumpPath(). */
|
||||
assert(S_ISREG(st.st_mode) || S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode));
|
||||
|
||||
/* Fail if the file is not owned by the build user. This prevents
|
||||
us from messing up the ownership/permissions of files
|
||||
hard-linked into the output (e.g. "ln /etc/shadow $out/foo").
|
||||
However, ignore files that we chown'ed ourselves previously to
|
||||
ensure that we don't fail on hard links within the same build
|
||||
(i.e. "touch $out/foo; ln $out/foo $out/bar"). */
|
||||
if (fromUid != (uid_t) -1 && st.st_uid != fromUid) {
|
||||
assert(!S_ISDIR(st.st_mode));
|
||||
if (inodesSeen.find(Inode(st.st_dev, st.st_ino)) == inodesSeen.end())
|
||||
throw BuildError(format("invalid ownership on file `%1%'") % path);
|
||||
mode_t mode = st.st_mode & ~S_IFMT;
|
||||
assert(S_ISLNK(st.st_mode) || (st.st_uid == geteuid() && (mode == 0444 || mode == 0555) && st.st_mtime == mtimeStore));
|
||||
return;
|
||||
}
|
||||
|
||||
inodesSeen.insert(Inode(st.st_dev, st.st_ino));
|
||||
|
||||
canonicaliseTimestampAndPermissions(path, st);
|
||||
|
||||
/* Change ownership to the current uid. If it's a symlink, use
|
||||
lchown if available, otherwise don't bother. Wrong ownership
|
||||
of a symlink doesn't matter, since the owning user can't change
|
||||
the symlink and can't delete it because the directory is not
|
||||
writable. The only exception is top-level paths in the Nix
|
||||
store (since that directory is group-writable for the Nix build
|
||||
users group); we check for this case below. */
|
||||
if (st.st_uid != geteuid()) {
|
||||
#if HAVE_LCHOWN
|
||||
if (lchown(path.c_str(), geteuid(), (gid_t) -1) == -1)
|
||||
#else
|
||||
if (!S_ISLNK(st.st_mode) &&
|
||||
chown(path.c_str(), geteuid(), (gid_t) -1) == -1)
|
||||
#endif
|
||||
throw SysError(format("changing owner of `%1%' to %2%")
|
||||
% path % geteuid());
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
foreach (Strings::iterator, i, names)
|
||||
canonicalisePathMetaData(path + "/" + *i, true);
|
||||
canonicalisePathMetaData_(path + "/" + *i, fromUid, inodesSeen);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void canonicalisePathMetaData(const Path & path)
|
||||
void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & inodesSeen)
|
||||
{
|
||||
canonicalisePathMetaData(path, true);
|
||||
canonicalisePathMetaData_(path, fromUid, inodesSeen);
|
||||
|
||||
/* On platforms that don't have lchown(), the top-level path can't
|
||||
be a symlink, since we can't change its ownership. */
|
||||
@@ -541,6 +574,13 @@ void canonicalisePathMetaData(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
void canonicalisePathMetaData(const Path & path, uid_t fromUid)
|
||||
{
|
||||
InodesSeen inodesSeen;
|
||||
canonicalisePathMetaData(path, fromUid, inodesSeen);
|
||||
}
|
||||
|
||||
|
||||
void LocalStore::checkDerivationOutputs(const Path & drvPath, const Derivation & drv)
|
||||
{
|
||||
string drvName = storePathToName(drvPath);
|
||||
@@ -995,15 +1035,72 @@ void LocalStore::startSubstituter(const Path & substituter, RunningSubstituter &
|
||||
|
||||
/* Parent. */
|
||||
|
||||
run.program = baseNameOf(substituter);
|
||||
run.to = toPipe.writeSide.borrow();
|
||||
run.from = fromPipe.readSide.borrow();
|
||||
run.from = run.fromBuf.fd = fromPipe.readSide.borrow();
|
||||
run.error = errorPipe.readSide.borrow();
|
||||
}
|
||||
|
||||
|
||||
template<class T> T getIntLine(int fd)
|
||||
/* Read a line from the substituter's stdout, while also processing
|
||||
its stderr. */
|
||||
string LocalStore::getLineFromSubstituter(RunningSubstituter & run)
|
||||
{
|
||||
string s = readLine(fd);
|
||||
string res, err;
|
||||
|
||||
/* We might have stdout data left over from the last time. */
|
||||
if (run.fromBuf.hasData()) goto haveData;
|
||||
|
||||
while (1) {
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(run.from, &fds);
|
||||
FD_SET(run.error, &fds);
|
||||
|
||||
/* Wait for data to appear on the substituter's stdout or
|
||||
stderr. */
|
||||
if (select(run.from > run.error ? run.from + 1 : run.error + 1, &fds, 0, 0, 0) == -1) {
|
||||
if (errno == EINTR) continue;
|
||||
throw SysError("waiting for input from the substituter");
|
||||
}
|
||||
|
||||
/* Completely drain stderr before dealing with stdout. */
|
||||
if (FD_ISSET(run.error, &fds)) {
|
||||
char buf[4096];
|
||||
ssize_t n = read(run.error, (unsigned char *) buf, sizeof(buf));
|
||||
if (n == -1) {
|
||||
if (errno == EINTR) continue;
|
||||
throw SysError("reading from substituter's stderr");
|
||||
}
|
||||
if (n == 0) throw Error(format("substituter `%1%' died unexpectedly") % run.program);
|
||||
err.append(buf, n);
|
||||
string::size_type p;
|
||||
while ((p = err.find('\n')) != string::npos) {
|
||||
printMsg(lvlError, run.program + ": " + string(err, 0, p));
|
||||
err = string(err, p + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Read from stdout until we get a newline or the buffer is empty. */
|
||||
else if (run.fromBuf.hasData() || FD_ISSET(run.from, &fds)) {
|
||||
haveData:
|
||||
do {
|
||||
unsigned char c;
|
||||
run.fromBuf(&c, 1);
|
||||
if (c == '\n') {
|
||||
if (!err.empty()) printMsg(lvlError, run.program + ": " + err);
|
||||
return res;
|
||||
}
|
||||
res += c;
|
||||
} while (run.fromBuf.hasData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<class T> T LocalStore::getIntLineFromSubstituter(RunningSubstituter & run)
|
||||
{
|
||||
string s = getLineFromSubstituter(run);
|
||||
T res;
|
||||
if (!string2Int(s, res)) throw Error("integer expected from stream");
|
||||
return res;
|
||||
@@ -1026,13 +1123,9 @@ PathSet LocalStore::querySubstitutablePaths(const PathSet & paths)
|
||||
substituters should only write (short) messages to
|
||||
stderr when they fail. I.e. they shouldn't write debug
|
||||
output. */
|
||||
try {
|
||||
Path path = readLine(run.from);
|
||||
if (path == "") break;
|
||||
res.insert(path);
|
||||
} catch (EndOfFile e) {
|
||||
throw Error(format("substituter `%1%' failed: %2%") % *i % chomp(drainFD(run.error)));
|
||||
}
|
||||
Path path = getLineFromSubstituter(run);
|
||||
if (path == "") break;
|
||||
res.insert(path);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
@@ -1051,26 +1144,22 @@ void LocalStore::querySubstitutablePathInfos(const Path & substituter,
|
||||
writeLine(run.to, s);
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
Path path = readLine(run.from);
|
||||
if (path == "") break;
|
||||
if (paths.find(path) == paths.end())
|
||||
throw Error(format("got unexpected path `%1%' from substituter") % path);
|
||||
paths.erase(path);
|
||||
SubstitutablePathInfo & info(infos[path]);
|
||||
info.deriver = readLine(run.from);
|
||||
if (info.deriver != "") assertStorePath(info.deriver);
|
||||
int nrRefs = getIntLine<int>(run.from);
|
||||
while (nrRefs--) {
|
||||
Path p = readLine(run.from);
|
||||
assertStorePath(p);
|
||||
info.references.insert(p);
|
||||
}
|
||||
info.downloadSize = getIntLine<long long>(run.from);
|
||||
info.narSize = getIntLine<long long>(run.from);
|
||||
} catch (EndOfFile e) {
|
||||
throw Error(format("substituter `%1%' failed: %2%") % substituter % chomp(drainFD(run.error)));
|
||||
Path path = getLineFromSubstituter(run);
|
||||
if (path == "") break;
|
||||
if (paths.find(path) == paths.end())
|
||||
throw Error(format("got unexpected path `%1%' from substituter") % path);
|
||||
paths.erase(path);
|
||||
SubstitutablePathInfo & info(infos[path]);
|
||||
info.deriver = getLineFromSubstituter(run);
|
||||
if (info.deriver != "") assertStorePath(info.deriver);
|
||||
int nrRefs = getIntLineFromSubstituter<int>(run);
|
||||
while (nrRefs--) {
|
||||
Path p = getLineFromSubstituter(run);
|
||||
assertStorePath(p);
|
||||
info.references.insert(p);
|
||||
}
|
||||
info.downloadSize = getIntLineFromSubstituter<long long>(run);
|
||||
info.narSize = getIntLineFromSubstituter<long long>(run);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1114,9 +1203,10 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos)
|
||||
|
||||
foreach (ValidPathInfos::const_iterator, i, infos) {
|
||||
assert(i->hash.type == htSHA256);
|
||||
/* !!! Maybe the registration info should be updated if the
|
||||
path is already valid. */
|
||||
if (!isValidPath(i->path)) addValidPath(*i);
|
||||
if (isValidPath(i->path))
|
||||
updatePathInfo(*i);
|
||||
else
|
||||
addValidPath(*i);
|
||||
paths.insert(i->path);
|
||||
}
|
||||
|
||||
@@ -1188,7 +1278,7 @@ Path LocalStore::addToStoreFromDump(const string & dump, const string & name,
|
||||
} else
|
||||
writeFile(dstPath, dump);
|
||||
|
||||
canonicalisePathMetaData(dstPath);
|
||||
canonicalisePathMetaData(dstPath, -1);
|
||||
|
||||
/* Register the SHA-256 hash of the NAR serialisation of
|
||||
the path in the database. We may just have computed it
|
||||
@@ -1253,7 +1343,7 @@ Path LocalStore::addTextToStore(const string & name, const string & s,
|
||||
|
||||
writeFile(dstPath, s);
|
||||
|
||||
canonicalisePathMetaData(dstPath);
|
||||
canonicalisePathMetaData(dstPath, -1);
|
||||
|
||||
HashResult hash = hashPath(htSHA256, dstPath);
|
||||
|
||||
@@ -1488,7 +1578,7 @@ Path LocalStore::importPath(bool requireSignature, Source & source)
|
||||
throw SysError(format("cannot move `%1%' to `%2%'")
|
||||
% unpacked % dstPath);
|
||||
|
||||
canonicalisePathMetaData(dstPath);
|
||||
canonicalisePathMetaData(dstPath, -1);
|
||||
|
||||
/* !!! if we were clever, we could prevent the hashPath()
|
||||
here. */
|
||||
@@ -1787,6 +1877,59 @@ void LocalStore::upgradeStore6()
|
||||
}
|
||||
|
||||
|
||||
#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL)
|
||||
|
||||
static void makeMutable(const Path & path)
|
||||
{
|
||||
checkInterrupt();
|
||||
|
||||
struct stat st = lstat(path);
|
||||
|
||||
if (!S_ISDIR(st.st_mode) && !S_ISREG(st.st_mode)) return;
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
foreach (Strings::iterator, i, names)
|
||||
makeMutable(path + "/" + *i);
|
||||
}
|
||||
|
||||
/* The O_NOFOLLOW is important to prevent us from changing the
|
||||
mutable bit on the target of a symlink (which would be a
|
||||
security hole). */
|
||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW);
|
||||
if (fd == -1) {
|
||||
if (errno == ELOOP) return; // it's a symlink
|
||||
throw SysError(format("opening file `%1%'") % path);
|
||||
}
|
||||
|
||||
unsigned int flags = 0, old;
|
||||
|
||||
/* Silently ignore errors getting/setting the immutable flag so
|
||||
that we work correctly on filesystems that don't support it. */
|
||||
if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) return;
|
||||
old = flags;
|
||||
flags &= ~FS_IMMUTABLE_FL;
|
||||
if (old == flags) return;
|
||||
if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) return;
|
||||
}
|
||||
|
||||
/* Upgrade from schema 6 (Nix 0.15) to schema 7 (Nix >= 1.3). */
|
||||
void LocalStore::upgradeStore7()
|
||||
{
|
||||
if (getuid() != 0) return;
|
||||
printMsg(lvlError, "removing immutable bits from the Nix store (this may take a while)...");
|
||||
makeMutable(settings.nixStore);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
void LocalStore::upgradeStore7()
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void LocalStore::vacuumDB()
|
||||
{
|
||||
if (sqlite3_exec(db, "vacuum;", 0, 0, 0) != SQLITE_OK)
|
||||
|
||||
@@ -17,8 +17,8 @@ namespace nix {
|
||||
/* Nix store and database schema version. Version 1 (or 0) was Nix <=
|
||||
0.7. Version 2 was Nix 0.8 and 0.9. Version 3 is Nix 0.10.
|
||||
Version 4 is Nix 0.11. Version 5 is Nix 0.12-0.16. Version 6 is
|
||||
Nix 1.0. */
|
||||
const int nixSchemaVersion = 6;
|
||||
Nix 1.0. Version 7 is Nix 1.3. */
|
||||
const int nixSchemaVersion = 7;
|
||||
|
||||
|
||||
extern string drvsLogDir;
|
||||
@@ -44,8 +44,10 @@ struct OptimiseStats
|
||||
|
||||
struct RunningSubstituter
|
||||
{
|
||||
Path program;
|
||||
Pid pid;
|
||||
AutoCloseFD to, from, error;
|
||||
FdSource fromBuf;
|
||||
};
|
||||
|
||||
|
||||
@@ -111,10 +113,6 @@ public:
|
||||
|
||||
Path queryDeriver(const Path & path);
|
||||
|
||||
/* Return all currently valid derivations that have `path' as an
|
||||
output. (Note that the result of `queryDeriver()' is the
|
||||
derivation that was actually used to produce `path', which may
|
||||
not exist anymore.) */
|
||||
PathSet queryValidDerivers(const Path & path);
|
||||
|
||||
PathSet queryDerivationOutputs(const Path & path);
|
||||
@@ -269,6 +267,7 @@ private:
|
||||
void updatePathInfo(const ValidPathInfo & info);
|
||||
|
||||
void upgradeStore6();
|
||||
void upgradeStore7();
|
||||
PathSet queryValidPathsOld();
|
||||
ValidPathInfo queryPathInfoOld(const Path & path);
|
||||
|
||||
@@ -276,7 +275,11 @@ private:
|
||||
|
||||
void deleteGarbage(GCState & state, const Path & path);
|
||||
|
||||
bool tryToDelete(GCState & state, const Path & path);
|
||||
void tryToDelete(GCState & state, const Path & path);
|
||||
|
||||
bool canReachRoot(GCState & state, PathSet & visited, const Path & path);
|
||||
|
||||
void deletePathRecursive(GCState & state, const Path & path);
|
||||
|
||||
bool isActiveTempFile(const GCState & state,
|
||||
const Path & path, const string & suffix);
|
||||
@@ -288,6 +291,10 @@ private:
|
||||
void startSubstituter(const Path & substituter,
|
||||
RunningSubstituter & runningSubstituter);
|
||||
|
||||
string getLineFromSubstituter(RunningSubstituter & run);
|
||||
|
||||
template<class T> T getIntLineFromSubstituter(RunningSubstituter & run);
|
||||
|
||||
Path createTempDirInStore();
|
||||
|
||||
Path importPath(bool requireSignature, Source & source);
|
||||
@@ -298,6 +305,10 @@ private:
|
||||
};
|
||||
|
||||
|
||||
typedef std::pair<dev_t, ino_t> Inode;
|
||||
typedef set<Inode> InodesSeen;
|
||||
|
||||
|
||||
/* "Fix", or canonicalise, the meta-data of the files in a store path
|
||||
after it has been built. In particular:
|
||||
- the last modification date on each file is set to 1 (i.e.,
|
||||
@@ -306,9 +317,10 @@ private:
|
||||
without execute permission; setuid bits etc. are cleared)
|
||||
- the owner and group are set to the Nix user and group, if we're
|
||||
in a setuid Nix installation. */
|
||||
void canonicalisePathMetaData(const Path & path);
|
||||
void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & inodesSeen);
|
||||
void canonicalisePathMetaData(const Path & path, uid_t fromUid);
|
||||
|
||||
void canonicalisePathMetaData(const Path & path, bool recurse);
|
||||
void canonicaliseTimestampAndPermissions(const Path & path);
|
||||
|
||||
MakeError(PathInUse, Error);
|
||||
|
||||
|
||||
+36
-16
@@ -15,27 +15,47 @@ Derivation derivationFromPath(StoreAPI & store, const Path & drvPath)
|
||||
}
|
||||
|
||||
|
||||
void computeFSClosure(StoreAPI & store, const Path & storePath,
|
||||
PathSet & paths, bool flipDirection, bool includeOutputs)
|
||||
void computeFSClosure(StoreAPI & store, const Path & path,
|
||||
PathSet & paths, bool flipDirection, bool includeOutputs, bool includeDerivers)
|
||||
{
|
||||
if (paths.find(storePath) != paths.end()) return;
|
||||
paths.insert(storePath);
|
||||
if (paths.find(path) != paths.end()) return;
|
||||
paths.insert(path);
|
||||
|
||||
PathSet references;
|
||||
if (flipDirection)
|
||||
store.queryReferrers(storePath, references);
|
||||
else
|
||||
store.queryReferences(storePath, references);
|
||||
PathSet edges;
|
||||
|
||||
if (includeOutputs && isDerivation(storePath)) {
|
||||
PathSet outputs = store.queryDerivationOutputs(storePath);
|
||||
foreach (PathSet::iterator, i, outputs)
|
||||
if (store.isValidPath(*i))
|
||||
computeFSClosure(store, *i, paths, flipDirection, true);
|
||||
if (flipDirection) {
|
||||
store.queryReferrers(path, edges);
|
||||
|
||||
if (includeOutputs) {
|
||||
PathSet derivers = store.queryValidDerivers(path);
|
||||
foreach (PathSet::iterator, i, derivers)
|
||||
edges.insert(*i);
|
||||
}
|
||||
|
||||
if (includeDerivers && isDerivation(path)) {
|
||||
PathSet outputs = store.queryDerivationOutputs(path);
|
||||
foreach (PathSet::iterator, i, outputs)
|
||||
if (store.isValidPath(*i) && store.queryDeriver(*i) == path)
|
||||
edges.insert(*i);
|
||||
}
|
||||
|
||||
} else {
|
||||
store.queryReferences(path, edges);
|
||||
|
||||
if (includeOutputs && isDerivation(path)) {
|
||||
PathSet outputs = store.queryDerivationOutputs(path);
|
||||
foreach (PathSet::iterator, i, outputs)
|
||||
if (store.isValidPath(*i)) edges.insert(*i);
|
||||
}
|
||||
|
||||
if (includeDerivers) {
|
||||
Path deriver = store.queryDeriver(path);
|
||||
if (store.isValidPath(deriver)) edges.insert(deriver);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (PathSet::iterator, i, references)
|
||||
computeFSClosure(store, *i, paths, flipDirection, includeOutputs);
|
||||
foreach (PathSet::iterator, i, edges)
|
||||
computeFSClosure(store, *i, paths, flipDirection, includeOutputs, includeDerivers);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@ Derivation derivationFromPath(StoreAPI & store, const Path & drvPath);
|
||||
`storePath' is returned; that is, the closures under the
|
||||
`referrers' relation instead of the `references' relation is
|
||||
returned. */
|
||||
void computeFSClosure(StoreAPI & store, const Path & storePath,
|
||||
void computeFSClosure(StoreAPI & store, const Path & path,
|
||||
PathSet & paths, bool flipDirection = false,
|
||||
bool includeOutputs = false);
|
||||
bool includeOutputs = false, bool includeDerivers = false);
|
||||
|
||||
/* Return the path corresponding to the output identifier `id' in the
|
||||
given derivation. */
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
#include "util.hh"
|
||||
#include "local-store.hh"
|
||||
#include "immutable.hh"
|
||||
#include "globals.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
@@ -20,7 +19,6 @@ static void makeWritable(const Path & path)
|
||||
struct stat st;
|
||||
if (lstat(path.c_str(), &st))
|
||||
throw SysError(format("getting attributes of path `%1%'") % path);
|
||||
if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) makeMutable(path);
|
||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError(format("changing writability of `%1%'") % path);
|
||||
}
|
||||
@@ -34,7 +32,7 @@ struct MakeReadOnly
|
||||
{
|
||||
try {
|
||||
/* This will make the path read-only. */
|
||||
if (path != "") canonicalisePathMetaData(path, false);
|
||||
if (path != "") canonicaliseTimestampAndPermissions(path);
|
||||
} catch (...) {
|
||||
ignoreException();
|
||||
}
|
||||
@@ -91,7 +89,6 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
|
||||
|
||||
if (!pathExists(linkPath)) {
|
||||
/* Nope, create a hard link in the links directory. */
|
||||
makeMutable(path);
|
||||
if (link(path.c_str(), linkPath.c_str()) == 0) return;
|
||||
if (errno != EEXIST)
|
||||
throw SysError(format("cannot link `%1%' to `%2%'") % linkPath % path);
|
||||
@@ -123,12 +120,6 @@ void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
|
||||
its timestamp back to 0. */
|
||||
MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
|
||||
|
||||
/* If ‘linkPath’ is immutable, we can't create hard links to it,
|
||||
so make it mutable first. We also have to make ‘path’ mutable,
|
||||
otherwise rename() will fail to delete it. */
|
||||
makeMutable(path);
|
||||
makeMutable(linkPath);
|
||||
|
||||
Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
|
||||
% settings.nixStore % getpid() % rand()).str();
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ void RemoteStore::connectToDaemon()
|
||||
throw SysError("cannot create Unix domain socket");
|
||||
closeOnExec(fdSocket);
|
||||
|
||||
string socketPath = settings.nixStateDir + DEFAULT_SOCKET_PATH;
|
||||
string socketPath = settings.nixDaemonSocketFile;
|
||||
|
||||
/* Urgh, sockaddr_un allows path names of only 108 characters. So
|
||||
chdir to the socket directory so that we can pass a relative
|
||||
@@ -334,6 +334,16 @@ Path RemoteStore::queryDeriver(const Path & path)
|
||||
}
|
||||
|
||||
|
||||
PathSet RemoteStore::queryValidDerivers(const Path & path)
|
||||
{
|
||||
openConnection();
|
||||
writeInt(wopQueryValidDerivers, to);
|
||||
writeString(path, to);
|
||||
processStderr();
|
||||
return readStorePaths<PathSet>(from);
|
||||
}
|
||||
|
||||
|
||||
PathSet RemoteStore::queryDerivationOutputs(const Path & path)
|
||||
{
|
||||
openConnection();
|
||||
@@ -431,7 +441,16 @@ void RemoteStore::buildPaths(const PathSet & drvPaths, bool repair)
|
||||
if (repair) throw Error("repairing is not supported when building through the Nix daemon");
|
||||
openConnection();
|
||||
writeInt(wopBuildPaths, to);
|
||||
writeStrings(drvPaths, to);
|
||||
if (GET_PROTOCOL_MINOR(daemonVersion) >= 13)
|
||||
writeStrings(drvPaths, to);
|
||||
else {
|
||||
/* For backwards compatibility with old daemons, strip output
|
||||
identifiers. */
|
||||
PathSet drvPaths2;
|
||||
foreach (PathSet::const_iterator, i, drvPaths)
|
||||
drvPaths2.insert(string(*i, 0, i->find('!')));
|
||||
writeStrings(drvPaths2, to);
|
||||
}
|
||||
processStderr();
|
||||
readInt(from);
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ public:
|
||||
|
||||
Path queryDeriver(const Path & path);
|
||||
|
||||
PathSet queryValidDerivers(const Path & path);
|
||||
|
||||
PathSet queryDerivationOutputs(const Path & path);
|
||||
|
||||
StringSet queryDerivationOutputNames(const Path & path);
|
||||
|
||||
@@ -133,6 +133,12 @@ public:
|
||||
no deriver has been set. */
|
||||
virtual Path queryDeriver(const Path & path) = 0;
|
||||
|
||||
/* Return all currently valid derivations that have `path' as an
|
||||
output. (Note that the result of `queryDeriver()' is the
|
||||
derivation that was actually used to produce `path', which may
|
||||
not exist anymore.) */
|
||||
virtual PathSet queryValidDerivers(const Path & path) = 0;
|
||||
|
||||
/* Query the outputs of the derivation denoted by `path'. */
|
||||
virtual PathSet queryDerivationOutputs(const Path & path) = 0;
|
||||
|
||||
@@ -322,7 +328,7 @@ Path addPermRoot(StoreAPI & store, const Path & storePath,
|
||||
|
||||
|
||||
/* Sort a set of paths topologically under the references relation.
|
||||
If p refers to q, then p follows q in this list. */
|
||||
If p refers to q, then p preceeds q in this list. */
|
||||
Paths topoSortPaths(StoreAPI & store, const PathSet & paths);
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace nix {
|
||||
#define WORKER_MAGIC_1 0x6e697863
|
||||
#define WORKER_MAGIC_2 0x6478696f
|
||||
|
||||
#define PROTOCOL_VERSION 0x10c
|
||||
#define PROTOCOL_VERSION 0x10d
|
||||
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
|
||||
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
|
||||
|
||||
@@ -42,6 +42,7 @@ typedef enum {
|
||||
wopQuerySubstitutablePathInfos = 30,
|
||||
wopQueryValidPaths = 31,
|
||||
wopQuerySubstitutablePaths = 32,
|
||||
wopQueryValidDerivers = 33,
|
||||
} WorkerOp;
|
||||
|
||||
|
||||
@@ -52,14 +53,6 @@ typedef enum {
|
||||
#define STDERR_ERROR 0x63787470
|
||||
|
||||
|
||||
/* The default location of the daemon socket, relative to nixStateDir.
|
||||
The socket is in a directory to allow you to control access to the
|
||||
Nix daemon by setting the mode/ownership of the directory
|
||||
appropriately. (This wouldn't work on the socket itself since it
|
||||
must be deleted and recreated on startup.) */
|
||||
#define DEFAULT_SOCKET_PATH "/daemon-socket/socket"
|
||||
|
||||
|
||||
Path readStorePath(Source & from);
|
||||
template<class T> T readStorePaths(Source & from);
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
pkglib_LTLIBRARIES = libutil.la
|
||||
|
||||
libutil_la_SOURCES = util.cc hash.cc serialise.cc \
|
||||
archive.cc xml-writer.cc immutable.cc
|
||||
archive.cc xml-writer.cc
|
||||
|
||||
libutil_la_LIBADD = ../boost/format/libformat.la
|
||||
|
||||
pkginclude_HEADERS = util.hh hash.hh serialise.hh \
|
||||
archive.hh xml-writer.hh types.hh immutable.hh
|
||||
archive.hh xml-writer.hh types.hh
|
||||
|
||||
if !HAVE_OPENSSL
|
||||
libutil_la_SOURCES += \
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
#include "config.h"
|
||||
|
||||
#include "immutable.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#if HAVE_LINUX_FS_H
|
||||
#include <linux/fs.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
||||
void makeMutable(const Path & path)
|
||||
{
|
||||
#if defined(FS_IOC_SETFLAGS) && defined(FS_IOC_GETFLAGS) && defined(FS_IMMUTABLE_FL)
|
||||
|
||||
/* Don't even try if we're not root. One day we should support
|
||||
the CAP_LINUX_IMMUTABLE capability. */
|
||||
if (getuid() != 0) return;
|
||||
|
||||
/* The O_NOFOLLOW is important to prevent us from changing the
|
||||
mutable bit on the target of a symlink (which would be a
|
||||
security hole). */
|
||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW);
|
||||
if (fd == -1) {
|
||||
if (errno == ELOOP) return; // it's a symlink
|
||||
throw SysError(format("opening file `%1%'") % path);
|
||||
}
|
||||
|
||||
unsigned int flags = 0, old;
|
||||
|
||||
/* Silently ignore errors getting/setting the immutable flag so
|
||||
that we work correctly on filesystems that don't support it. */
|
||||
if (ioctl(fd, FS_IOC_GETFLAGS, &flags)) return;
|
||||
old = flags;
|
||||
flags &= ~FS_IMMUTABLE_FL;
|
||||
if (old == flags) return;
|
||||
if (ioctl(fd, FS_IOC_SETFLAGS, &flags)) return;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <types.hh>
|
||||
|
||||
namespace nix {
|
||||
|
||||
/* Make the given path mutable. */
|
||||
void makeMutable(const Path & path);
|
||||
|
||||
}
|
||||
@@ -90,6 +90,12 @@ size_t BufferedSource::read(unsigned char * data, size_t len)
|
||||
}
|
||||
|
||||
|
||||
bool BufferedSource::hasData()
|
||||
{
|
||||
return bufPosOut < bufPosIn;
|
||||
}
|
||||
|
||||
|
||||
size_t FdSource::readUnbuffered(unsigned char * data, size_t len)
|
||||
{
|
||||
ssize_t n;
|
||||
|
||||
@@ -63,6 +63,8 @@ struct BufferedSource : Source
|
||||
|
||||
/* Underlying read call, to be overriden. */
|
||||
virtual size_t readUnbuffered(unsigned char * data, size_t len) = 0;
|
||||
|
||||
bool hasData();
|
||||
};
|
||||
|
||||
|
||||
|
||||
+44
-47
@@ -13,7 +13,6 @@
|
||||
#include <limits.h>
|
||||
|
||||
#include "util.hh"
|
||||
#include "immutable.hh"
|
||||
|
||||
|
||||
extern char * * environ;
|
||||
@@ -100,7 +99,7 @@ Path canonPath(const Path & path, bool resolveSymlinks)
|
||||
i++;
|
||||
|
||||
/* If `..', delete the last component. */
|
||||
else if (*i == '.' && i + 1 < end && i[1] == '.' &&
|
||||
else if (*i == '.' && i + 1 < end && i[1] == '.' &&
|
||||
(i + 2 == end || i[2] == '/'))
|
||||
{
|
||||
if (!s.empty()) s.erase(s.rfind('/'));
|
||||
@@ -215,7 +214,7 @@ string readFile(int fd)
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == -1)
|
||||
throw SysError("statting file");
|
||||
|
||||
|
||||
unsigned char * buf = new unsigned char[st.st_size];
|
||||
AutoDeleteArray<unsigned char> d(buf);
|
||||
readFull(fd, buf, st.st_size);
|
||||
@@ -280,9 +279,9 @@ static void _computePathSize(const Path & path,
|
||||
blocks += st.st_blocks;
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
Strings names = readDirectory(path);
|
||||
|
||||
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
|
||||
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
|
||||
_computePathSize(path + "/" + *i, bytes, blocks);
|
||||
}
|
||||
}
|
||||
@@ -305,21 +304,19 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed)
|
||||
|
||||
struct stat st = lstat(path);
|
||||
|
||||
if (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode)) makeMutable(path);
|
||||
|
||||
if (!S_ISDIR(st.st_mode) && st.st_nlink == 1)
|
||||
bytesFreed += st.st_blocks * 512;
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
Strings names = readDirectory(path);
|
||||
|
||||
/* Make the directory writable. */
|
||||
if (!(st.st_mode & S_IWUSR)) {
|
||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError(format("making `%1%' writable") % path);
|
||||
}
|
||||
/* Make the directory writable. */
|
||||
if (!(st.st_mode & S_IWUSR)) {
|
||||
if (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
|
||||
throw SysError(format("making `%1%' writable") % path);
|
||||
}
|
||||
|
||||
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
|
||||
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
|
||||
_deletePath(path + "/" + *i, bytesFreed);
|
||||
}
|
||||
|
||||
@@ -351,14 +348,14 @@ void makePathReadOnly(const Path & path)
|
||||
struct stat st = lstat(path);
|
||||
|
||||
if (!S_ISLNK(st.st_mode) && (st.st_mode & S_IWUSR)) {
|
||||
if (chmod(path.c_str(), st.st_mode & ~S_IWUSR) == -1)
|
||||
throw SysError(format("making `%1%' read-only") % path);
|
||||
if (chmod(path.c_str(), st.st_mode & ~S_IWUSR) == -1)
|
||||
throw SysError(format("making `%1%' read-only") % path);
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
Strings names = readDirectory(path);
|
||||
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
|
||||
makePathReadOnly(path + "/" + *i);
|
||||
for (Strings::iterator i = names.begin(); i != names.end(); ++i)
|
||||
makePathReadOnly(path + "/" + *i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,25 +377,25 @@ Path createTempDir(const Path & tmpRoot, const Path & prefix,
|
||||
static int globalCounter = 0;
|
||||
int localCounter = 0;
|
||||
int & counter(useGlobalCounter ? globalCounter : localCounter);
|
||||
|
||||
|
||||
while (1) {
|
||||
checkInterrupt();
|
||||
Path tmpDir = tempName(tmpRoot, prefix, includePid, counter);
|
||||
if (mkdir(tmpDir.c_str(), mode) == 0) {
|
||||
/* Explicitly set the group of the directory. This is to
|
||||
work around around problems caused by BSD's group
|
||||
ownership semantics (directories inherit the group of
|
||||
the parent). For instance, the group of /tmp on
|
||||
FreeBSD is "wheel", so all directories created in /tmp
|
||||
will be owned by "wheel"; but if the user is not in
|
||||
"wheel", then "tar" will fail to unpack archives that
|
||||
have the setgid bit set on directories. */
|
||||
if (chown(tmpDir.c_str(), (uid_t) -1, getegid()) != 0)
|
||||
throw SysError(format("setting group of directory `%1%'") % tmpDir);
|
||||
return tmpDir;
|
||||
}
|
||||
if (errno != EEXIST)
|
||||
throw SysError(format("creating directory `%1%'") % tmpDir);
|
||||
Path tmpDir = tempName(tmpRoot, prefix, includePid, counter);
|
||||
if (mkdir(tmpDir.c_str(), mode) == 0) {
|
||||
/* Explicitly set the group of the directory. This is to
|
||||
work around around problems caused by BSD's group
|
||||
ownership semantics (directories inherit the group of
|
||||
the parent). For instance, the group of /tmp on
|
||||
FreeBSD is "wheel", so all directories created in /tmp
|
||||
will be owned by "wheel"; but if the user is not in
|
||||
"wheel", then "tar" will fail to unpack archives that
|
||||
have the setgid bit set on directories. */
|
||||
if (chown(tmpDir.c_str(), (uid_t) -1, getegid()) != 0)
|
||||
throw SysError(format("setting group of directory `%1%'") % tmpDir);
|
||||
return tmpDir;
|
||||
}
|
||||
if (errno != EEXIST)
|
||||
throw SysError(format("creating directory `%1%'") % tmpDir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,7 +415,7 @@ Paths createDirs(const Path & path)
|
||||
}
|
||||
|
||||
if (!S_ISDIR(st.st_mode)) throw Error(format("`%1%' is not a directory") % path);
|
||||
|
||||
|
||||
return created;
|
||||
}
|
||||
|
||||
@@ -727,8 +724,8 @@ AutoCloseDir::operator DIR *()
|
||||
void AutoCloseDir::close()
|
||||
{
|
||||
if (dir) {
|
||||
closedir(dir);
|
||||
dir = 0;
|
||||
closedir(dir);
|
||||
dir = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -843,12 +840,12 @@ void killUser(uid_t uid)
|
||||
if (setuid(uid) == -1)
|
||||
throw SysError("setting uid");
|
||||
|
||||
while (true) {
|
||||
if (kill(-1, SIGKILL) == 0) break;
|
||||
if (errno == ESRCH) break; /* no more processes */
|
||||
if (errno != EINTR)
|
||||
throw SysError(format("cannot kill processes for uid `%1%'") % uid);
|
||||
}
|
||||
while (true) {
|
||||
if (kill(-1, SIGKILL) == 0) break;
|
||||
if (errno == ESRCH) break; /* no more processes */
|
||||
if (errno != EINTR)
|
||||
throw SysError(format("cannot kill processes for uid `%1%'") % uid);
|
||||
}
|
||||
|
||||
} catch (std::exception & e) {
|
||||
writeToStderr((format("killing processes belonging to uid `%1%': %2%\n") % uid % e.what()).str());
|
||||
@@ -856,7 +853,7 @@ void killUser(uid_t uid)
|
||||
}
|
||||
_exit(0);
|
||||
}
|
||||
|
||||
|
||||
/* parent */
|
||||
int status = pid.wait(true);
|
||||
if (status != 0)
|
||||
@@ -1043,14 +1040,14 @@ string statusToString(int status)
|
||||
if (WIFEXITED(status))
|
||||
return (format("failed with exit code %1%") % WEXITSTATUS(status)).str();
|
||||
else if (WIFSIGNALED(status)) {
|
||||
int sig = WTERMSIG(status);
|
||||
int sig = WTERMSIG(status);
|
||||
#if HAVE_STRSIGNAL
|
||||
const char * description = strsignal(sig);
|
||||
return (format("failed due to signal %1% (%2%)") % sig % description).str();
|
||||
#else
|
||||
return (format("failed due to signal %1%") % sig).str();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
return "died abnormally";
|
||||
} else return "succeeded";
|
||||
|
||||
+2
-2
@@ -174,7 +174,7 @@ struct AutoDeleteArray
|
||||
{
|
||||
T * p;
|
||||
AutoDeleteArray(T * p) : p(p) { }
|
||||
~AutoDeleteArray()
|
||||
~AutoDeleteArray()
|
||||
{
|
||||
delete [] p;
|
||||
}
|
||||
@@ -185,7 +185,7 @@ class AutoDelete
|
||||
{
|
||||
Path path;
|
||||
bool del;
|
||||
bool recursive;
|
||||
bool recursive;
|
||||
public:
|
||||
AutoDelete(const Path & p, bool recursive = true);
|
||||
~AutoDelete();
|
||||
|
||||
@@ -273,7 +273,7 @@ struct SavingSourceAdapter : Source
|
||||
};
|
||||
|
||||
|
||||
static void performOp(unsigned int clientVersion,
|
||||
static void performOp(bool trusted, unsigned int clientVersion,
|
||||
Source & from, Sink & to, unsigned int op)
|
||||
{
|
||||
switch (op) {
|
||||
@@ -334,6 +334,7 @@ static void performOp(unsigned int clientVersion,
|
||||
|
||||
case wopQueryReferences:
|
||||
case wopQueryReferrers:
|
||||
case wopQueryValidDerivers:
|
||||
case wopQueryDerivationOutputs: {
|
||||
Path path = readStorePath(from);
|
||||
startWork();
|
||||
@@ -342,6 +343,8 @@ static void performOp(unsigned int clientVersion,
|
||||
store->queryReferences(path, paths);
|
||||
else if (op == wopQueryReferrers)
|
||||
store->queryReferrers(path, paths);
|
||||
else if (op == wopQueryValidDerivers)
|
||||
paths = store->queryValidDerivers(path);
|
||||
else paths = store->queryDerivationOutputs(path);
|
||||
stopWork();
|
||||
writeStrings(paths, to);
|
||||
@@ -548,7 +551,10 @@ static void performOp(unsigned int clientVersion,
|
||||
for (unsigned int i = 0; i < n; i++) {
|
||||
string name = readString(from);
|
||||
string value = readString(from);
|
||||
settings.set("untrusted-" + name, value);
|
||||
if (name == "build-timeout")
|
||||
string2Int(value, settings.buildTimeout);
|
||||
else
|
||||
settings.set(trusted ? name : "untrusted-" + name, value);
|
||||
}
|
||||
}
|
||||
startWork();
|
||||
@@ -637,7 +643,7 @@ static void performOp(unsigned int clientVersion,
|
||||
}
|
||||
|
||||
|
||||
static void processConnection()
|
||||
static void processConnection(bool trusted)
|
||||
{
|
||||
canSendStderr = false;
|
||||
myPid = getpid();
|
||||
@@ -705,7 +711,7 @@ static void processConnection()
|
||||
opCount++;
|
||||
|
||||
try {
|
||||
performOp(clientVersion, from, to, op);
|
||||
performOp(trusted, clientVersion, from, to, op);
|
||||
} catch (Error & e) {
|
||||
/* If we're not in a state were we can send replies, then
|
||||
something went wrong processing the input of the
|
||||
@@ -771,7 +777,7 @@ static void daemonLoop()
|
||||
if (fdSocket == -1)
|
||||
throw SysError("cannot create Unix domain socket");
|
||||
|
||||
string socketPath = settings.nixStateDir + DEFAULT_SOCKET_PATH;
|
||||
string socketPath = settings.nixDaemonSocketFile;
|
||||
|
||||
createDirs(dirOf(socketPath));
|
||||
|
||||
@@ -833,6 +839,7 @@ static void daemonLoop()
|
||||
/* Get the identity of the caller, if possible. */
|
||||
uid_t clientUid = -1;
|
||||
pid_t clientPid = -1;
|
||||
bool trusted = false;
|
||||
|
||||
#if defined(SO_PEERCRED)
|
||||
ucred cred;
|
||||
@@ -840,6 +847,7 @@ static void daemonLoop()
|
||||
if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) != -1) {
|
||||
clientPid = cred.pid;
|
||||
clientUid = cred.uid;
|
||||
if (clientUid == 0) trusted = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -873,7 +881,7 @@ static void daemonLoop()
|
||||
/* Handle the connection. */
|
||||
from.fd = remote;
|
||||
to.fd = remote;
|
||||
processConnection();
|
||||
processConnection(trusted);
|
||||
|
||||
} catch (std::exception & e) {
|
||||
writeToStderr("child error: " + string(e.what()) + "\n");
|
||||
@@ -892,8 +900,6 @@ static void daemonLoop()
|
||||
|
||||
void run(Strings args)
|
||||
{
|
||||
bool daemon = false;
|
||||
|
||||
for (Strings::iterator i = args.begin(); i != args.end(); ) {
|
||||
string arg = *i++;
|
||||
if (arg == "--daemon") /* ignored for backwards compatibility */;
|
||||
|
||||
@@ -1350,6 +1350,9 @@ void run(Strings args)
|
||||
|
||||
if (!op) throw UsageError("no operation specified");
|
||||
|
||||
if (globals.profile == "")
|
||||
globals.profile = getEnv("NIX_PROFILE", "");
|
||||
|
||||
if (globals.profile == "") {
|
||||
Path profileLink = getHomeDir() + "/.nix-profile";
|
||||
globals.profile = pathExists(profileLink)
|
||||
|
||||
+44
-40
@@ -238,12 +238,6 @@ static void printTree(const Path & path,
|
||||
PathSet references;
|
||||
store->queryReferences(path, references);
|
||||
|
||||
#if 0
|
||||
for (PathSet::iterator i = drv.inputSrcs.begin();
|
||||
i != drv.inputSrcs.end(); ++i)
|
||||
cout << format("%1%%2%\n") % (tailPad + treeConn) % *i;
|
||||
#endif
|
||||
|
||||
/* Topologically sort under the relation A < B iff A \in
|
||||
closure(B). That is, if derivation A is an (possibly indirect)
|
||||
input of B, then A is printed first. This has the effect of
|
||||
@@ -251,7 +245,7 @@ static void printTree(const Path & path,
|
||||
Paths sorted = topoSortPaths(*store, references);
|
||||
reverse(sorted.begin(), sorted.end());
|
||||
|
||||
for (Paths::iterator i = sorted.begin(); i != sorted.end(); ++i) {
|
||||
foreach (Paths::iterator, i, sorted) {
|
||||
Paths::iterator j = i; ++j;
|
||||
printTree(*i, tailPad + treeConn,
|
||||
j == sorted.end() ? tailPad + treeNull : tailPad + treeLine,
|
||||
@@ -293,7 +287,7 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||
else if (*i == "--resolve") query = qResolve;
|
||||
else if (*i == "--roots") query = qRoots;
|
||||
else if (*i == "--use-output" || *i == "-u") useOutput = true;
|
||||
else if (*i == "--force-realise" || *i == "-f") forceRealise = true;
|
||||
else if (*i == "--force-realise" || *i == "--force-realize" || *i == "-f") forceRealise = true;
|
||||
else if (*i == "--include-outputs") includeOutputs = true;
|
||||
else throw UsageError(format("unknown flag `%1%'") % *i);
|
||||
|
||||
@@ -404,7 +398,8 @@ static void opQuery(Strings opFlags, Strings opArgs)
|
||||
foreach (Strings::iterator, i, opArgs) {
|
||||
PathSet paths = maybeUseOutputs(followLinksToStorePath(*i), useOutput, forceRealise);
|
||||
foreach (PathSet::iterator, j, paths)
|
||||
computeFSClosure(*store, *j, referrers, true);
|
||||
computeFSClosure(*store, *j, referrers, true,
|
||||
settings.gcKeepOutputs, settings.gcKeepDerivations);
|
||||
}
|
||||
Roots roots = store->findRoots();
|
||||
foreach (Roots::iterator, i, roots)
|
||||
@@ -459,35 +454,42 @@ static void opReadLog(Strings opFlags, Strings opArgs)
|
||||
foreach (Strings::iterator, i, opArgs) {
|
||||
Path path = useDeriver(followLinksToStorePath(*i));
|
||||
|
||||
Path logPath = (format("%1%/%2%/%3%") %
|
||||
settings.nixLogDir % drvsLogDir % baseNameOf(path)).str();
|
||||
Path logBz2Path = logPath + ".bz2";
|
||||
for (int j = 0; j <= 2; j++) {
|
||||
if (j == 2) throw Error(format("build log of derivation `%1%' is not available") % path);
|
||||
|
||||
if (pathExists(logPath)) {
|
||||
/* !!! Make this run in O(1) memory. */
|
||||
string log = readFile(logPath);
|
||||
writeFull(STDOUT_FILENO, (const unsigned char *) log.data(), log.size());
|
||||
string baseName = baseNameOf(path);
|
||||
Path logPath =
|
||||
j == 0
|
||||
? (format("%1%/%2%/%3%/%4%") % settings.nixLogDir % drvsLogDir % string(baseName, 0, 2) % string(baseName, 2)).str()
|
||||
: (format("%1%/%2%/%3%") % settings.nixLogDir % drvsLogDir % baseName).str();
|
||||
Path logBz2Path = logPath + ".bz2";
|
||||
|
||||
if (pathExists(logPath)) {
|
||||
/* !!! Make this run in O(1) memory. */
|
||||
string log = readFile(logPath);
|
||||
writeFull(STDOUT_FILENO, (const unsigned char *) log.data(), log.size());
|
||||
break;
|
||||
}
|
||||
|
||||
else if (pathExists(logBz2Path)) {
|
||||
AutoCloseFD fd = open(logBz2Path.c_str(), O_RDONLY);
|
||||
FILE * f = 0;
|
||||
if (fd == -1 || (f = fdopen(fd.borrow(), "r")) == 0)
|
||||
throw SysError(format("opening file `%1%'") % logBz2Path);
|
||||
int err;
|
||||
BZFILE * bz = BZ2_bzReadOpen(&err, f, 0, 0, 0, 0);
|
||||
if (!bz) throw Error(format("cannot open bzip2 file `%1%'") % logBz2Path);
|
||||
unsigned char buf[128 * 1024];
|
||||
do {
|
||||
int n = BZ2_bzRead(&err, bz, buf, sizeof(buf));
|
||||
if (err != BZ_OK && err != BZ_STREAM_END)
|
||||
throw Error(format("error reading bzip2 file `%1%'") % logBz2Path);
|
||||
writeFull(STDOUT_FILENO, buf, n);
|
||||
} while (err != BZ_STREAM_END);
|
||||
BZ2_bzReadClose(&err, bz);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
else if (pathExists(logBz2Path)) {
|
||||
AutoCloseFD fd = open(logBz2Path.c_str(), O_RDONLY);
|
||||
FILE * f = 0;
|
||||
if (fd == -1 || (f = fdopen(fd.borrow(), "r")) == 0)
|
||||
throw SysError(format("opening file `%1%'") % logBz2Path);
|
||||
int err;
|
||||
BZFILE * bz = BZ2_bzReadOpen(&err, f, 0, 0, 0, 0);
|
||||
if (!bz) throw Error(format("cannot open bzip2 file `%1%'") % logBz2Path);
|
||||
unsigned char buf[128 * 1024];
|
||||
do {
|
||||
int n = BZ2_bzRead(&err, bz, buf, sizeof(buf));
|
||||
if (err != BZ_OK && err != BZ_STREAM_END)
|
||||
throw Error(format("error reading bzip2 file `%1%'") % logBz2Path);
|
||||
writeFull(STDOUT_FILENO, buf, n);
|
||||
} while (err != BZ_STREAM_END);
|
||||
BZ2_bzReadClose(&err, bz);
|
||||
}
|
||||
|
||||
else throw Error(format("build log of derivation `%1%' is not available") % path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -513,7 +515,7 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise)
|
||||
if (!store->isValidPath(info.path) || reregister) {
|
||||
/* !!! races */
|
||||
if (canonicalise)
|
||||
canonicalisePathMetaData(info.path);
|
||||
canonicalisePathMetaData(info.path, -1);
|
||||
if (!hashGiven) {
|
||||
HashResult hash = hashPath(htSHA256, info.path);
|
||||
info.hash = hash.first;
|
||||
@@ -692,7 +694,9 @@ static void opExport(Strings opFlags, Strings opArgs)
|
||||
else throw UsageError(format("unknown flag `%1%'") % *i);
|
||||
|
||||
FdSink sink(STDOUT_FILENO);
|
||||
exportPaths(*store, opArgs, sign, sink);
|
||||
Paths sorted = topoSortPaths(*store, PathSet(opArgs.begin(), opArgs.end()));
|
||||
reverse(sorted.begin(), sorted.end());
|
||||
exportPaths(*store, sorted, sign, sink);
|
||||
}
|
||||
|
||||
|
||||
@@ -841,7 +845,7 @@ void run(Strings args)
|
||||
|
||||
Operation oldOp = op;
|
||||
|
||||
if (arg == "--realise" || arg == "-r")
|
||||
if (arg == "--realise" || arg == "--realize" || arg == "-r")
|
||||
op = opRealise;
|
||||
else if (arg == "--add" || arg == "-A")
|
||||
op = opAdd;
|
||||
@@ -883,7 +887,7 @@ void run(Strings args)
|
||||
op = opVerifyPath;
|
||||
else if (arg == "--repair-path")
|
||||
op = opRepairPath;
|
||||
else if (arg == "--optimise")
|
||||
else if (arg == "--optimise" || arg == "--optimize")
|
||||
op = opOptimise;
|
||||
else if (arg == "--query-failed-paths")
|
||||
op = opQueryFailedPaths;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
-e "s^@bindir\@^$(bindir)^g" \
|
||||
-e "s^@datadir\@^$(datadir)^g" \
|
||||
-e "s^@sysconfdir\@^$(sysconfdir)^g" \
|
||||
-e "s^@profiledir\@^$(profiledir)^g" \
|
||||
-e "s^@localstatedir\@^$(localstatedir)^g" \
|
||||
-e "s^@datadir\@^$(datadir)^g" \
|
||||
-e "s^@libdir\@^$(libdir)^g" \
|
||||
@@ -36,3 +37,6 @@
|
||||
-e "s^@testPath\@^$(coreutils):$$(dirname $$(type -p expr))^g" \
|
||||
< $< > $@ || rm $@
|
||||
if test -x $<; then chmod +x $@; fi
|
||||
|
||||
$(CONFIG_HEADER):
|
||||
true
|
||||
|
||||
@@ -14,6 +14,8 @@ TESTS = init.sh hash.sh lang.sh add.sh simple.sh dependencies.sh \
|
||||
|
||||
XFAIL_TESTS =
|
||||
|
||||
profiledir = $(sysconfdir)/profile.d
|
||||
|
||||
include ../substitute.mk
|
||||
|
||||
$(TESTS): common.sh config.nix
|
||||
|
||||
+26
-1
@@ -5,7 +5,7 @@ clearManifests
|
||||
|
||||
# Create the binary cache.
|
||||
cacheDir=$TEST_ROOT/binary-cache
|
||||
rm -rf $cacheDir
|
||||
rm -rf "$cacheDir"
|
||||
|
||||
outPath=$(nix-build dependencies.nix --no-out-link)
|
||||
|
||||
@@ -17,6 +17,8 @@ nix-push --dest $cacheDir $outPath
|
||||
clearStore
|
||||
rm -f $NIX_STATE_DIR/binary-cache*
|
||||
|
||||
export _NIX_CACHE_FILE_URLS=1
|
||||
|
||||
nix-env --option binary-caches "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "---"
|
||||
|
||||
nix-store --option binary-caches "file://$cacheDir" -r $outPath
|
||||
@@ -37,3 +39,26 @@ nix-store --option binary-caches "file://$cacheDir" -r $outPath
|
||||
nix-store --check-validity $outPath
|
||||
nix-store -qR $outPath | grep input-2
|
||||
|
||||
|
||||
# Test whether fallback works if we have cached info but the
|
||||
# corresponding NAR has disappeared.
|
||||
clearStore
|
||||
|
||||
nix-build --option binary-caches "file://$cacheDir" dependencies.nix --dry-run # get info
|
||||
|
||||
mkdir $cacheDir/tmp
|
||||
mv $cacheDir/*.nar.xz $cacheDir/tmp/
|
||||
|
||||
NIX_DEBUG_SUBST=1 nix-build --option binary-caches "file://$cacheDir" dependencies.nix -o $TEST_ROOT/result --fallback
|
||||
|
||||
mv $cacheDir/tmp/* $cacheDir/
|
||||
|
||||
|
||||
# Test whether building works if the binary cache contains an
|
||||
# incomplete closure.
|
||||
clearStore
|
||||
|
||||
rm $(grep -l "StorePath:.*dependencies-input-2" $cacheDir/*.narinfo)
|
||||
|
||||
nix-build --option binary-caches "file://$cacheDir" dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log
|
||||
grep -q "Downloading" $TEST_ROOT/log
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
set -e
|
||||
|
||||
datadir="@datadir@"
|
||||
sysconfdir="@sysconfdir@"
|
||||
profiledir="@profiledir@"
|
||||
|
||||
export TEST_ROOT=$(pwd)/test-tmp
|
||||
export NIX_STORE_DIR
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
[ "d41d8cd98f00b204e9800998ecf8427e" "6c69ee7f211c640419d5366cc076ae46" "bb3438fbabd460ea6dbd27d153e2233b" "da39a3ee5e6b4b0d3255bfef95601890afd80709" "cd54e8568c1b37cf1e5badb0779bcbf382212189" "6d12e10b1d331dad210e47fd25d4f260802b7e77" "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" "900a4469df00ccbfd0c145c6d1e4b7953dd0afafadd7534e3a4019e8d38fc663" "ad0387b3bd8652f730ca46d25f9c170af0fd589f42e7f23f5a9e6412d97d7e56" ]
|
||||
@@ -0,0 +1,7 @@
|
||||
let
|
||||
md5 = builtins.hashString "md5";
|
||||
sha1 = builtins.hashString "sha1";
|
||||
sha256 = builtins.hashString "sha256";
|
||||
strings = [ "" "text 1" "text 2" ];
|
||||
in
|
||||
(builtins.map md5 strings) ++ (builtins.map sha1 strings) ++ (builtins.map sha256 strings)
|
||||
@@ -13,14 +13,14 @@ makeTest ({ pkgs, ... }: let pkgA = pkgs.aterm; pkgB = pkgs.wget; in {
|
||||
virtualisation.pathsInNixDB = [ pkgA ];
|
||||
environment.nix = nix;
|
||||
};
|
||||
|
||||
|
||||
server =
|
||||
{ config, pkgs, ... }:
|
||||
{ services.openssh.enable = true;
|
||||
virtualisation.writableStore = true;
|
||||
virtualisation.pathsInNixDB = [ pkgB ];
|
||||
environment.nix = nix;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = { nodes }:
|
||||
@@ -36,8 +36,8 @@ makeTest ({ pkgs, ... }: let pkgA = pkgs.aterm; pkgB = pkgs.wget; in {
|
||||
# Install the SSH key on the server.
|
||||
$server->succeed("mkdir -m 700 /root/.ssh");
|
||||
$server->copyFileFromHost("key.pub", "/root/.ssh/authorized_keys");
|
||||
$server->waitForJob("sshd");
|
||||
$client->waitForJob("network-interfaces");
|
||||
$server->waitForUnit("sshd");
|
||||
$client->waitForUnit("network.target");
|
||||
$client->succeed("ssh -o StrictHostKeyChecking=no " . $server->name() . " 'echo hello world'");
|
||||
|
||||
# Copy the closure of package A from the client to the server.
|
||||
|
||||
@@ -3,8 +3,8 @@ source common.sh
|
||||
home=$TEST_ROOT/home
|
||||
rm -rf $home
|
||||
mkdir -p $home
|
||||
HOME=$home $SHELL -e -c ". $sysconfdir/profile.d/nix.sh"
|
||||
HOME=$home $SHELL -e -c ". $sysconfdir/profile.d/nix.sh" # test idempotency
|
||||
HOME=$home $SHELL -e -c ". $profiledir/nix.sh"
|
||||
HOME=$home $SHELL -e -c ". $profiledir/nix.sh" # test idempotency
|
||||
|
||||
[ -L $home/.nix-profile ]
|
||||
[ -e $home/.nix-channels ]
|
||||
|
||||
@@ -72,11 +72,11 @@ in
|
||||
$client->succeed("chmod 600 /root/.ssh/id_dsa");
|
||||
|
||||
# Install the SSH key on the slaves.
|
||||
$client->waitForJob("network-interfaces");
|
||||
$client->waitForUnit("network.target");
|
||||
foreach my $slave ($slave1, $slave2) {
|
||||
$slave->succeed("mkdir -m 700 /root/.ssh");
|
||||
$slave->copyFileFromHost("key.pub", "/root/.ssh/authorized_keys");
|
||||
$slave->waitForJob("sshd");
|
||||
$slave->waitForUnit("sshd");
|
||||
$client->succeed("ssh -o StrictHostKeyChecking=no " . $slave->name() . " 'echo hello world'");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
echo "\`timeout' builder entering an infinite loop"
|
||||
while true ; do : ; done
|
||||
while true ; do echo foo; sleep 1; done
|
||||
|
||||
@@ -3,6 +3,4 @@ with import ./config.nix;
|
||||
mkDerivation {
|
||||
name = "timeout";
|
||||
builder = ./timeout.builder.sh;
|
||||
PATH = "";
|
||||
goodPath = path;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user