Compare commits

...
129 Commits
Author SHA1 Message Date
Eelco Dolstra 6016bcd30e Update release notes for Nix 1.5.3 2013-06-17 11:47:38 +02:00
Eelco Dolstra 1b6ee8f4c7 Allow hard links between the outputs of a derivation 2013-06-13 17:29:56 +02:00
Eelco Dolstra cd49ee0897 Fix a security bug in hash rewriting
Before calling dumpPath(), we have to make sure the files are owned by
the build user.  Otherwise, the build could contain a hard link to
(say) /etc/shadow, which would then be read by the daemon and
rewritten as a world-readable file.

This only affects systems that don't have hard link restrictions
enabled.
2013-06-13 17:12:24 +02:00
Eelco Dolstra 1e2c7c04b1 Fix assertion failure in canonicalisePathMetaData() after hash rewriting
The assertion in canonicalisePathMetaData() failed because the
ownership of the path already changed due to the hash rewriting.  The
solution is not to check the ownership of rewritten paths.

Issue #122.
2013-06-13 17:12:06 +02:00
Eelco Dolstra 6cc2a8f8ed computeFSClosure: Only process the missing/corrupt paths
Issue #122.
2013-06-13 16:43:20 +02:00
Eelco Dolstra bfee9a2581 Typo 2013-06-13 14:51:11 +02:00
Eelco Dolstra f9ff67e948 In repair mode, update the hash of rebuilt paths
Otherwise subsequent invocations of "--repair" will keep rebuilding
the path.  This only happens if the path content differs between
builds (e.g. due to timestamps).
2013-06-13 14:46:07 +02:00
Eelco Dolstra 6b05f688ee nix-daemon: Trust options like binary-caches when the client is root
Fixes #127.
2013-06-12 12:10:26 +02:00
Eelco Dolstra 5c06e5297d download-from-binary-cache.pl: Respect $NIX_CONNECT_TIMEOUT 2013-06-07 16:25:12 +02:00
Eelco Dolstra 24a356bf71 Replace $NIX_DEBUG_SUBST with an option ‘debug-subst’
Thus passing ‘--option debug-subst 1’ allows daemon users to turn on
debug info and see what the substituter is doing.
2013-06-07 15:36:36 +02:00
Eelco Dolstra 24e063efdc download-from-binary-cache.pl: Show if we're waiting for a URL
Previously, if a binary cache is hanging/unreachable/slow,
download-from-binary-cache.pl would also hang without any indication
to the user.  Now, if fetching a URL takes more than 5 seconds, it
will print a message to that effect.
2013-06-07 15:33:44 +02:00
Eelco Dolstra ca70fba0bf Remove obsolete EOF checks 2013-06-07 15:10:23 +02:00
Eelco Dolstra 5959c591a0 Process stderr from substituters while doing have/info queries 2013-06-07 15:02:14 +02:00
Eelco Dolstra c5f9d0d080 Buffer reads from the substituter
This greatly reduces the number of system calls.
2013-06-07 14:00:23 +02:00
Eelco Dolstra 75e12b8e66 download-from-binary-cache.pl: Fix race condition
Fixes the error "DBD::SQLite::db do failed: column url is not unique".
2013-06-05 16:25:07 +02:00
Eelco Dolstra f0576d6775 Update the default binary cache URL to cache.nixos.org 2013-06-05 13:36:43 +02:00
Eelco Dolstra ff08306746 download-from-binary-cache.pl: Treat a 403 error as a 404
Amazon S3 returns HTTP status code 403 if a file doesn't exist and the
user has no permission to list the contents of the bucket.  So treat
it as 404 (meaning it's cached in the NARExistence table).
2013-06-04 15:20:37 +02:00
Eelco Dolstra bc2e43f3c8 build-remote.pl: Remove meaningless signing when importing the output paths
The "$UID != 0" makes no sense: if the local side has write access to
the Nix store (which is always the case) then it doesn't matter if
we're root - we can import unsigned paths either way.
2013-05-29 18:18:59 +02:00
Eelco Dolstra 039d5a023f .bashrc -> .profile 2013-05-29 12:36:56 +02:00
Eelco Dolstra b09b87321c nix-store --export: Export paths in topologically sorted order
Fixes #118.
2013-05-23 14:55:36 -04:00
Eelco Dolstra 107505e13a build-remote.pl: Copy all outputs in one operation 2013-05-23 14:39:58 -04:00
Eelco Dolstra 72d8209548 build-remote.pl: Indicate if remote machine is refusing builds
Fixes #120.
2013-05-23 14:04:46 -04:00
Eelco Dolstra 18a48d80a0 Show function names in error messages
Functions in Nix are anonymous, but if they're assigned to a
variable/attribute, we can use the variable/attribute name in error
messages, e.g.

while evaluating `concatMapStrings' at `/nix/var/nix/profiles/per-user/root/channels/nixos/nixpkgs/pkgs/lib/strings.nix:18:25':
...
2013-05-16 19:08:02 +02:00
Eelco Dolstra 1b3a03f161 Show which function argument was unexpected
Fixes #116.
2013-05-16 17:56:14 +02:00
Eelco Dolstra 229567293c Shut up a compiler warning 2013-05-16 17:48:19 +02:00
Eelco Dolstra a4cb62ac25 download-from-binary-cache.pl: Get rid of an uninitialized value warning
Reported by Pablo Costa.
2013-05-15 15:47:05 +02:00
Eelco Dolstra 31a551a60f Bump version 2013-05-13 23:50:31 +02:00
Eelco Dolstra 6e85d1b5ba Bump release date 2013-05-13 16:52:08 +02:00
Eelco Dolstra 3a0cc43ac8 build-remote.pl: Properly close the SSH connection between attempts 2013-05-10 02:38:50 +02:00
Eelco Dolstra be0b9dda31 build-remote.pl: Pass /dev/null as SSH's stdin
Otherwise it will set the parent's stdin to non-blocking mode, causing
the subsequent read of the set of inputs/outputs to fail randomly.
That's insane.
2013-05-10 02:32:13 +02:00
Eelco Dolstra 78206f06ec build-remote.pl: Allow a machine to refuse a build
Before selecting a machine, build-remote.pl will try to run the
command "nix-builds-inhibited" on the machine.  If this command exists
and returns a 0 exit code, then the machine won't be used.  It's up to
the user to provide this command, but it would typically be a script
that checks whether there is enough disk space and whether the load is
not too high.
2013-05-10 01:09:46 +02:00
Eelco Dolstra 2ee9da9e22 In trace messages, don't print the output path
This doesn't work if there is no output named "out".  Hydra didn't use
it anyway.
2013-05-10 00:24:33 +02:00
Eelco Dolstra 6eba05613a Communicate build timeouts to Hydra 2013-05-09 18:39:04 +02:00
Eelco Dolstra 7a03cbf09d build-remote.pl: Create one process fewer on the remote side 2013-05-09 17:30:07 +02:00
Eelco Dolstra 69b8f9980f build-remote.pl: Enforce timeouts locally
Don't pass --timeout / --max-silent-time to the remote builder.
Instead, let the local Nix process terminate the build if it exceeds a
timeout.  The remote builder will be killed as a side-effect.  This
gives better error reporting (since the timeout message from the
remote side wasn't properly propagated) and handles non-Nix problems
like SSH hangs.
2013-05-09 17:17:17 +02:00
Eelco Dolstra e93acab852 Build Fedora 18 RPMs 2013-05-08 14:41:35 +02:00
Eelco Dolstra 806970349b Update release date 2013-05-07 15:46:25 +02:00
Eelco Dolstra ea019e9a26 Add option ‘extra-binary-caches’
This allows providing additional binary caches, useful in scripts like
Hydra's build reproduction scripts, in particular because untrusted
caches are ignored.
2013-05-07 15:37:28 +02:00
Eelco Dolstra cc837e2458 Build Debian 7.0 debs 2013-05-07 11:21:30 +02:00
Eelco Dolstra 28034bfa49 Build Ubuntu 13.04 debs 2013-05-03 14:14:46 +02:00
Eelco Dolstra 93f4fa8a15 Update release notes 2013-05-03 11:28:32 +02:00
Eelco Dolstra c51b6a893c nix-copy-closure: Show a proper error message if no host name is given 2013-05-03 11:12:11 +02:00
Lluís Batlle i RossellandEelco Dolstra e6c44d166a Fixing the pv position regarding compression
Problem noticed by niksnut.
2013-05-03 11:08:51 +02:00
Lluís Batlle i RossellandEelco Dolstra 7391533ea5 Fixing the pv reference; I didn't mean to change it 2013-05-03 11:08:51 +02:00
Lluís Batlle i RossellandEelco Dolstra 5cc2fc46ec Adding ETA support to the --show-progress in nix-copy-closure
Based on https://github.com/NixOS/nix/pull/6 from shlevy
2013-05-03 11:08:51 +02:00
Eelco Dolstra 3628b61ce0 Nix 1.5.2 release notes 2013-05-01 13:31:33 +02:00
Eelco Dolstra 470553bd05 Don't let stderr writes in substituters cause a deadlock 2013-05-01 13:21:39 +02:00
Eelco Dolstra 4ddd077bfa find-runtime-roots.pl: Don't hardcode /nix/store 2013-04-26 12:15:54 +02:00
Eelco Dolstra 0374d94437 addAdditionalRoots(): Check each path only once 2013-04-26 12:07:25 +02:00
Eelco Dolstra 00f698eb8b find-runtime-roots.pl: Search process environments for roots
For instance, this prevents paths from being deleted that are in use
by a "nix-build --run-env" session.
2013-04-26 12:06:39 +02:00
Eelco Dolstra 938092a213 find-runtime-roots.pl: Use Nix::Utils::readFile 2013-04-26 11:44:19 +02:00
Eelco Dolstra 772b70952f Fix --timeout
I'm not sure if it has ever worked correctly.  The line "lastWait =
after;" seems to mean that the timer was reset every time a build
produced log output.

Note that the timeout is now per build, as documented ("the maximum
number of seconds that a builder can run").
2013-04-23 18:04:59 +02:00
Eelco Dolstra f9974f856e Show that --timeout doesn't work if the build produces log output 2013-04-23 17:16:29 +02:00
Eelco Dolstra 6955d41f2b nix-build: Respect --timeout 2013-04-23 17:16:01 +02:00
Eelco Dolstra 934cf2d1f4 Nix daemon: respect build timeout from the client 2013-04-23 16:59:06 +02:00
Eelco Dolstra 08d96ffad0 Fix --fallback with the binary cache substituter
Reported by Peter Simons.
2013-04-23 12:45:01 +02:00
Eelco Dolstra a9b4e26b5c Test whether --fallback works if NARS have disappeared from the binary cache 2013-04-23 12:44:01 +02:00
Eelco Dolstra c642441beb Test NAR info caching 2013-04-23 12:43:28 +02:00
Eelco Dolstra 05420e7883 Manual: Add a missing step to the build instructions
Reported by Johan Grande.
2013-04-09 17:57:48 +02:00
Eelco Dolstra 258897c265 Complain if /homeless-shelter exists 2013-04-04 11:16:26 +02:00
Eelco Dolstra 239841787b Fix evaluation of the VM tests 2013-03-25 21:59:11 +01:00
Shea LevyandEelco Dolstra cc63db1dd5 makeStoreWritable: Ask forgiveness, not permission
It is surprisingly impossible to check if a mountpoint is a bind mount
on Linux, and in my previous commit I forgot to check if /nix/store was
even a mountpoint at all. statvfs.f_flag is not populated with MS_BIND
(and even if it were, my check was wrong in the previous commit).

Luckily, the semantics of mount with MS_REMOUNT | MS_BIND make both
checks unnecessary: if /nix/store is not a mountpoint, then mount will
fail with EINVAL, and if /nix/store is not a bind-mount, then it will
not be made writable. Thus, if /nix/store is not a mountpoint, we fail
immediately (since we don't know how to make it writable), and if
/nix/store IS a mountpoint but not a bind-mount, we fail at first write
(see below for why we can't check and fail immediately).

Note that, due to what is IMO buggy behavior in Linux, calling mount
with MS_REMOUNT | MS_BIND on a non-bind readonly mount makes the
mountpoint appear writable in two places: In the sixth (but not the
10th!) column of mountinfo, and in the f_flags member of struct statfs.
All other syscalls behave as if the mount point were still readonly (at
least for Linux 3.9-rc1, but I don't think this has changed recently or
is expected to soon). My preferred semantics would be for MS_REMOUNT |
MS_BIND to fail on a non-bind mount, as it doesn't make sense to remount
a non bind-mount as a bind mount.
2013-03-25 19:00:16 +01:00
Shea LevyandEelco Dolstra 2c9cf50746 makeStoreWritable: Use statvfs instead of /proc/self/mountinfo to find out if /nix/store is a read-only bind mount
/nix/store could be a read-only bind mount even if it is / in its own filesystem, so checking the 4th field in mountinfo is insufficient.

Signed-off-by: Shea Levy <shea@shealevy.com>
2013-03-25 19:00:16 +01:00
Eelco Dolstra c3fc60d936 Fix evaluation 2013-03-18 21:49:42 +01:00
Eelco Dolstra f72ed36025 Bump version number 2013-03-15 14:21:05 +01:00
Eelco Dolstra 78d777ca15 Remove the "system" jobset input 2013-03-15 13:18:49 +01:00
Eelco Dolstra a68ebf8e37 Require Bison 2.6 2013-03-14 18:33:15 +01:00
Eelco Dolstra 804709706c Fix building against Bison 2.6 2013-03-14 18:31:08 +01:00
Eelco Dolstra c56bc3d81c Make sure that thunks are restored properly if an exception occurs
Fixes Hydra bug #67.
2013-03-14 17:21:13 +01:00
Eelco Dolstra 4b07476848 Prevent config.h from being clobbered 2013-03-08 01:27:04 +01:00
Eelco Dolstra bdd4646338 Revert "Prevent config.h from being clobbered"
This reverts commit 28bba8c44f.
2013-03-08 01:24:59 +01:00
Eelco Dolstra e73d9e9488 Fix annoying Perl 5.16 warnings
I.e.

Subroutine Nix::Store::isValidPath redefined at /nix/store/clfzsf6gi7qh5i9c0vks1ifjam47rijn-perl-5.16.2/lib/perl5/5.16.2/XSLoader.pm line 92.

and so on.
2013-03-08 00:27:32 +01:00
Eelco Dolstra 28bba8c44f Prevent config.h from being clobbered 2013-03-07 23:55:55 +01:00
Eelco Dolstra 8057a192e3 Handle systems without lutimes() or lchown() 2013-02-28 19:55:09 +01:00
Eelco Dolstra 9fa1bee575 Update release notes
Also use a point release version number as suggested by several
people.
2013-02-28 19:36:02 +01:00
Eelco Dolstra f45c731cd7 Handle symlinks properly
Now it's really brown paper bag time...
2013-02-28 14:51:08 +01:00
Eelco Dolstra 88936411bc Bump version number 2013-02-28 13:03:53 +01:00
Eelco Dolstra 0111ba98ea Handle hard links to other files in the output 2013-02-27 17:18:41 +01:00
Eelco Dolstra b008674e46 Refactoring: Split off the non-recursive canonicalisePathMetaData()
Also, change the file mode before changing the owner.  This prevents a
slight time window in which a setuid binary would be setuid root.
2013-02-27 16:42:19 +01:00
Eelco Dolstra 826dc0d07d Remove outdated file 2013-02-26 14:32:48 +01:00
Eelco Dolstra 97c6009c47 Bump version number 2013-02-26 14:32:14 +01:00
Eelco Dolstra ca9c02dff1 Update release notes 2013-02-26 13:23:09 +01:00
Eelco Dolstra 5526a282b5 Security: Don't allow builders to change permissions on files they don't own
It turns out that in multi-user Nix, a builder may be able to do

  ln /etc/shadow $out/foo

Afterwards, canonicalisePathMetaData() will be applied to $out/foo,
causing /etc/shadow's mode to be set to 444 (readable by everybody but
writable by nobody).  That's obviously Very Bad.

Fortunately, this fails in NixOS's default configuration because
/nix/store is a bind mount, so "ln" will fail with "Invalid
cross-device link".  It also fails if hard-link restrictions are
enabled, so a workaround is:

  echo 1 > /proc/sys/fs/protected_hardlinks

The solution is to check that all files in $out are owned by the build
user.  This means that innocuous operations like "ln
${pkgs.foo}/some-file $out/" are now rejected, but that already failed
in chroot builds anyway.
2013-02-26 02:30:19 +01:00
Eelco Dolstra dadf7a5b46 build-remote: Use the --quiet flag
‘--option verbosity 0’ doesn't actually do anything.
2013-02-19 16:56:31 +01:00
Ludovic CourtèsandEelco Dolstra 3e067ac11c Add `Settings::nixDaemonSocketFile'. 2013-02-19 10:19:18 +01:00
Ludovic CourtèsandEelco Dolstra 5ea138dc4b Enable chroot support on old glibc versions. 2013-02-19 10:19:11 +01:00
Eelco Dolstra 79a3ba7fa3 Document ‘hashString’ 2013-02-08 20:04:14 +01:00
Eelco Dolstra 5f18cd2e84 Make "${./path} ..." evaluate to a string, not a path
Wacky string coercion semantics caused expressions like

  exec = "${./my-script} params...";

to evaluate to a path (‘/path/my-script params’), because
anti-quotations are desuged to string concatenation:

  exec = ./my-script + " params...";

By constrast, adding a space at the start would yield a string as
expected:

  exec = " ${./my-script} params...";

Now the first example also evaluates to a string.
2013-02-08 20:04:04 +01:00
Eelco Dolstra 52172607cf Rename "hash" to "hashString" and handle SHA-1 2013-02-08 19:36:23 +01:00
Marc WeberandEelco Dolstra 01a5ea9914 experimental/hash
adding primop function calculating hash of a string

Signed-off-by: Marc Weber <marco-oweber@gmx.de>
2013-02-08 19:26:34 +01:00
Eelco Dolstra 8add116acd Nix::Store::derivationFromPath: Return derivation outputs 2013-02-05 16:02:57 +01:00
Eelco Dolstra d6143801fd Support the colonies 2013-01-30 18:50:58 +01:00
Eelco Dolstra 9842077cb2 Improve -I description
Issue #88.
2013-01-24 13:00:44 +01:00
Eelco Dolstra 5e9c3da412 Only warn about SQLite being busy once
No need to get annoying.
2013-01-23 16:45:10 +01:00
Eelco Dolstra 99ed558a93 Correctly handle missing logs 2013-01-22 22:07:25 +01:00
Eelco Dolstra 1943b60ad8 Fix the VM tests 2013-01-21 22:36:23 +01:00
Eelco Dolstra 96fbbbde55 build-remote.pl: Don't keep build logs on the build slave 2013-01-21 22:26:25 +01:00
Shea LevyandEelco Dolstra d6fd6d8aff corepkgs/fetchurl: Enable making the downloaded file executable 2013-01-21 10:20:52 +01:00
Eelco Dolstra 536c85ea49 Store build logs in /nix/var/log/nix/drvs/<XX>
...where <XX> is the first two characters of the derivation.
Otherwise /nix/var/log/nix/drvs may become so large that we run into
all sorts of weird filesystem limits/inefficiences.  For instance,
ext3/ext4 filesystems will barf with "ext4_dx_add_entry:1551:
Directory index full!" once you hit a few million files.
2013-01-17 15:47:26 +01:00
Eelco Dolstra 66fa9e6a4d Bump version number to 1.4 2013-01-07 14:48:44 +01:00
Eelco Dolstra e42df686f3 Delete a left-over trash directory before doing a GC 2013-01-05 00:13:29 +01:00
Eelco Dolstra 92926be2fe Fix "0 store paths deleted" message 2013-01-04 15:17:19 +01:00
Eelco Dolstra b424d29d1b Open the database after removing immutable bits 2013-01-03 13:29:17 +01:00
Eelco Dolstra 3007f57377 Remove tabs 2013-01-03 13:00:46 +01:00
Eelco Dolstra def5160b61 Clear any immutable bits in the Nix store
Doing this once makes subsequent operations like garbage collecting
more efficient since we don't have to call makeMutable() first.
2013-01-03 12:59:23 +01:00
Eelco Dolstra 0a4e90395c Urgggh
http://hydra.nixos.org/build/3661100
2013-01-02 23:52:15 +01:00
Eelco Dolstra f12492c66d Manual: Fix "nix-store --export" example 2013-01-02 22:36:08 +01:00
Eelco Dolstra aebea2e489 Reinstate the http://nixos.org/binary-cache default for the binary-caches setting 2013-01-02 22:16:37 +01:00
Eelco Dolstra 649bb60617 Use sysconfdir=/etc 2013-01-02 22:12:19 +01:00
Eelco Dolstra 42d6f640c1 Update release notes 2013-01-02 13:00:55 +01:00
Eelco Dolstra 299141ecbd If a substitute closure is incomplete, build dependencies, then retry the substituter
Issue #77.
2013-01-02 12:38:28 +01:00
Eelco Dolstra 1b3a78a459 Automatically fall back if the references of a substitute are not substitutable
Fixes #77.
2013-01-02 12:00:26 +01:00
Eelco Dolstra 82248abd8f Add a test for incomplete closures in the binary cache
Issue #77.
2013-01-02 11:45:23 +01:00
Eelco Dolstra 12f9129f60 nix-build: Support talking to old daemons
Fixes #76.
2012-12-29 23:21:46 +01:00
Eelco Dolstra b7629778ef Allow mounting a path in a different location in the chroot
Fixes #24.
2012-12-29 23:04:02 +01:00
Eelco Dolstra 68dcbb187e download-from-binary-cache: Use the channels of the calling user rather than root
This should make live easier for single-user (non-daemon)
installations.  Note that when the daemon is used, the "calling user"
is root so we're not using any untrusted caches.
2012-12-21 15:00:07 +01:00
Eelco Dolstra 5ee7d8fbab Typo fix 2012-12-21 00:18:59 +01:00
Eelco Dolstra 2754a07ead nix-store -q --roots: Respect the gc-keep-outputs/gc-keep-derivations settings
So if a path is not garbage solely because it's reachable from a root
due to the gc-keep-outputs or gc-keep-derivations settings, ‘nix-store
-q --roots’ now shows that root.
2012-12-20 18:41:44 +01:00
Eelco Dolstra 06f62defe6 Yet another rewrite of the garbage collector
But this time it's *obviously* correct!  No more segfaults due to
infinite recursions for sure, etc.

Also, move directories to /nix/store/trash instead of renaming them to
/nix/store/bla-gc-<pid>.  Then we can just delete /nix/store/trash at
the end.
2012-12-20 17:32:15 +01:00
Eelco Dolstra 9c29a2ed35 Give a better error message if writeFile fails due to permission issues 2012-12-20 12:22:13 +01:00
Eelco Dolstra e775d4d84f If gc-keep-derivations is set, only keep the actual deriver
This prevents zillions of derivations from being kept, and fixes an
infinite recursion in the garbage collector (due to an obscure cycle
that can occur with fixed-output derivations).
2012-12-19 15:33:09 +01:00
Eelco Dolstra fbf0b2fa45 Kill the build hook rather than shutting it down cleanly
Waiting for the hook to shut down cleanly sometimes seems to lead to
hangs.
2012-12-19 11:47:42 +01:00
Eelco Dolstra 228ea7c2f9 Revert brain fart
This reverts commit cc511fd65b.
2012-12-18 20:43:47 +01:00
Eelco Dolstra cc511fd65b Check for potential infinite select() loops when building 2012-12-18 18:50:05 +01:00
Stuart PernsteinerandEelco Dolstra a9045c727f fix use-after-free bug in mkString(Value&, Symbol&) 2012-12-13 11:13:20 +01:00
Eelco Dolstra 9fa12fc201 Allow setting the profile location using $NIX_PROFILE
Fixes #69.
2012-12-12 16:01:46 +01:00
Eelco Dolstra 3ad53e43c8 Debian package: Add dependency on libwww-curl-perl
Fixes issue #70.
2012-12-11 16:50:21 +01:00
Eelco Dolstra 772778c0ec On SQLITE_BUSY, wait a random amount of time
If all contending processes wait a fixed amount of time (100 ms),
there is a good probability that they'll just collide again.
2012-12-11 11:49:42 +01:00
Eelco Dolstra e087bfef5f Bump version number 2012-12-07 13:14:04 +01:00
71 changed files with 1319 additions and 823 deletions
+1 -1
View File
@@ -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
+4 -3
View File
@@ -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;
-33
View File
@@ -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).
+12
View File
@@ -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
View File
@@ -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 youre 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),
+1 -1
View File
@@ -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>
+4 -5
View File
@@ -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
View File
@@ -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>
+4 -4
View File
@@ -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>
+1 -1
View File
@@ -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 Nixs database. For every changed path, it prints a warning
message. The exit status is 0 if no path has changed, and 1
+5 -4
View File
@@ -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>
+3 -3
View File
@@ -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>
+5 -4
View File
@@ -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>
+135 -3
View File
@@ -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 dont have to worry about your
manifest being in sync with the Nix expressions youre 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
+9 -5
View File
@@ -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: $?";
}
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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());
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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': $?");
}
+51 -17
View File
@@ -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;
}
+23 -19
View File
@@ -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";
+1 -1
View File
@@ -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]);
+10 -2
View File
@@ -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: $?";
}
}
+6 -3
View File
@@ -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
View File
@@ -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;
}
-1
View File
@@ -9,7 +9,6 @@
%{
#include "nixexpr.hh"
#define BISON_HEADER_HACK
#include "parser-tab.hh"
using namespace nix;
+20
View File
@@ -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();
}
}
+7 -1
View File
@@ -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
};
+8 -8
View File
@@ -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);
+16
View File
@@ -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);
+9 -4
View File
@@ -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;
+1 -1
View File
@@ -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());
}
+8 -5
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+5 -2
View File
@@ -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
View File
@@ -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)
+21 -9
View File
@@ -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
View File
@@ -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);
}
+2 -2
View File
@@ -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. */
+1 -10
View File
@@ -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();
+21 -2
View File
@@ -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);
}
+2
View File
@@ -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);
+7 -1
View File
@@ -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);
+2 -9
View File
@@ -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);
+2 -2
View File
@@ -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 += \
-49
View File
@@ -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
}
}
-10
View File
@@ -1,10 +0,0 @@
#pragma once
#include <types.hh>
namespace nix {
/* Make the given path mutable. */
void makeMutable(const Path & path);
}
+6
View File
@@ -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;
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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();
+14 -8
View File
@@ -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 */;
+3
View File
@@ -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
View File
@@ -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;
+4
View File
@@ -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
+2
View File
@@ -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
View File
@@ -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
View File
@@ -1,7 +1,7 @@
set -e
datadir="@datadir@"
sysconfdir="@sysconfdir@"
profiledir="@profiledir@"
export TEST_ROOT=$(pwd)/test-tmp
export NIX_STORE_DIR
+1
View File
@@ -0,0 +1 @@
[ "d41d8cd98f00b204e9800998ecf8427e" "6c69ee7f211c640419d5366cc076ae46" "bb3438fbabd460ea6dbd27d153e2233b" "da39a3ee5e6b4b0d3255bfef95601890afd80709" "cd54e8568c1b37cf1e5badb0779bcbf382212189" "6d12e10b1d331dad210e47fd25d4f260802b7e77" "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" "900a4469df00ccbfd0c145c6d1e4b7953dd0afafadd7534e3a4019e8d38fc663" "ad0387b3bd8652f730ca46d25f9c170af0fd589f42e7f23f5a9e6412d97d7e56" ]
+7
View File
@@ -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)
+4 -4
View File
@@ -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.
+2 -2
View File
@@ -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 ]
+2 -2
View File
@@ -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 -1
View File
@@ -1,2 +1,2 @@
echo "\`timeout' builder entering an infinite loop"
while true ; do : ; done
while true ; do echo foo; sleep 1; done
-2
View File
@@ -3,6 +3,4 @@ with import ./config.nix;
mkDerivation {
name = "timeout";
builder = ./timeout.builder.sh;
PATH = "";
goodPath = path;
}
+1 -1
View File
@@ -1 +1 @@
1.2
1.5.3