Merge branch 'master' into core-updates

This commit is contained in:
Ludovic Courtès 2014-12-26 23:31:04 +01:00
commit 763a401ed1
99 changed files with 22291 additions and 343 deletions

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "nix-upstream"]
path = nix-upstream
url = https://github.com/NixOS/nix.git

View File

@ -1,15 +1,5 @@
#!/bin/sh
# Import missing source files and create the build system.
# Create the build system.
set -e -x
top_srcdir="$PWD"
export top_srcdir
git submodule init
git submodule update
./nix/sync-with-upstream
exec autoreconf -vfi

View File

@ -4177,10 +4177,91 @@ tool suite.)
the ``message of the day''.
@end deffn
@deffn {Monadic Procedure} nscd-service [#:glibc glibc]
Return a service that runs libc's name service cache daemon (nscd).
@cindex name service cache daemon
@cindex nscd
@deffn {Monadic Procedure} nscd-service [@var{config}] [#:glibc glibc]
Return a service that runs libc's name service cache daemon (nscd) with the
given @var{config}---an @code{<nscd-configuration>} object.
@end deffn
@defvr {Scheme Variable} %nscd-default-configuration
This is the default @code{<nscd-configuration>} value (see below) used
by @code{nscd-service}. This uses the caches defined by
@var{%nscd-default-caches}; see below.
@end defvr
@deftp {Data Type} nscd-configuration
This is the type representing the name service cache daemon (nscd)
configuration.
@table @asis
@item @code{log-file} (default: @code{"/var/log/nscd.log"})
Name of nscd's log file. This is where debugging output goes when
@code{debug-level} is strictly positive.
@item @code{debug-level} (default: @code{0})
Integer denoting the debugging levels. Higher numbers mean more
debugging output is logged.
@item @code{caches} (default: @var{%nscd-default-caches})
List of @code{<nscd-cache>} objects denoting things to be cached; see
below.
@end table
@end deftp
@deftp {Data Type} nscd-cache
Data type representing a cache database of nscd and its parameters.
@table @asis
@item @code{database}
This is a symbol representing the name of the database to be cached.
Valid values are @code{passwd}, @code{group}, @code{hosts}, and
@code{services}, which designate the corresponding NSS database
(@pxref{NSS Basics,,, libc, The GNU C Library Reference Manual}).
@item @code{positive-time-to-live}
@itemx @code{negative-time-to-live} (default: @code{20})
A number representing the number of seconds during which a positive or
negative lookup result remains in cache.
@item @code{check-files?} (default: @code{#t})
Whether to check for updates of the files corresponding to
@var{database}.
For instance, when @var{database} is @code{hosts}, setting this flag
instructs nscd to check for updates in @file{/etc/hosts} and to take
them into account.
@item @code{persistent?} (default: @code{#t})
Whether the cache should be stored persistently on disk.
@item @code{shared?} (default: @code{#t})
Whether the cache should be shared among users.
@item @code{max-database-size} (default: 32@tie{}MiB)
Maximum size in bytes of the database cache.
@c XXX: 'suggested-size' and 'auto-propagate?' seem to be expert
@c settings, so leave them out.
@end table
@end deftp
@defvr {Scheme Variable} %nscd-default-caches
List of @code{<nscd-cache>} objects used by default by
@code{nscd-configuration} (see above.)
It enables persistent and aggressive caching of service and host name
lookups. The latter provides better host name lookup performance,
resilience in the face of unreliable name servers, and also better
privacy---often the result of host name lookups is in local cache, so
external name servers do not even need to be queried.
@end defvr
@deffn {Monadic Procedure} syslog-service
Return a service that runs @code{syslogd} with reasonable default
settings.

View File

@ -96,6 +96,7 @@ GNU_SYSTEM_MODULES = \
gnu/packages/freeipmi.scm \
gnu/packages/ftp.scm \
gnu/packages/fribidi.scm \
gnu/packages/game-development.scm \
gnu/packages/games.scm \
gnu/packages/gawk.scm \
gnu/packages/gcal.scm \
@ -377,6 +378,15 @@ dist_patch_DATA = \
gnu/packages/patches/guix-test-networking.patch \
gnu/packages/patches/gtkglext-disable-disable-deprecated.patch \
gnu/packages/patches/hop-bigloo-4.0b.patch \
gnu/packages/patches/icecat-CVE-2014-1587-bug-1042567.patch \
gnu/packages/patches/icecat-CVE-2014-1587-bug-1072847.patch \
gnu/packages/patches/icecat-CVE-2014-1587-bug-1079729.patch \
gnu/packages/patches/icecat-CVE-2014-1587-bug-1080312.patch \
gnu/packages/patches/icecat-CVE-2014-1587-bug-1089207.patch \
gnu/packages/patches/icecat-CVE-2014-1590.patch \
gnu/packages/patches/icecat-CVE-2014-1592.patch \
gnu/packages/patches/icecat-CVE-2014-1593.patch \
gnu/packages/patches/icecat-CVE-2014-1594.patch \
gnu/packages/patches/inkscape-stray-comma.patch \
gnu/packages/patches/jbig2dec-ignore-testtest.patch \
gnu/packages/patches/kmod-module-directory.patch \
@ -450,6 +460,7 @@ dist_patch_DATA = \
gnu/packages/patches/wmctrl-64-fix.patch \
gnu/packages/patches/xf86-input-synaptics-glibc-2.20.patch \
gnu/packages/patches/xf86-video-openchrome-includes.patch \
gnu/packages/patches/xfce4-panel-plugins.patch \
gnu/packages/patches/xmodmap-asprintf.patch
bootstrapdir = $(guilemoduledir)/gnu/packages/bootstrap

View File

@ -105,8 +105,12 @@
(append environment `((,%distro-root-directory . "gnu/packages"))))))
(define* (scheme-files directory)
"Return the list of Scheme files found under DIRECTORY."
(file-system-fold (const #t) ; enter?
"Return the list of Scheme files found under DIRECTORY, recursively. The
returned list is sorted in alphabetical order."
;; Sort entries so that 'fold-packages' works in a deterministic fashion
;; regardless of details of the underlying file system.
(sort (file-system-fold (const #t) ; enter?
(lambda (path stat result) ; leaf
(if (string-suffix? ".scm" path)
(cons path result)
@ -122,7 +126,8 @@
result)
'()
directory
stat))
stat)
string<?))
(define file-name->module-name
(let ((not-slash (char-set-complement (char-set #\/))))

View File

@ -28,6 +28,113 @@
#:use-module (gnu packages pkg-config)
#:use-module (gnu packages python))
(define-public bedtools
(package
(name "bedtools")
(version "2.22.0")
(source (origin
(method url-fetch)
(uri (string-append "https://github.com/arq5x/bedtools2/archive/v"
version ".tar.gz"))
(sha256
(base32
"16aq0w3dmbd0853j32xk9jin4vb6v6fgakfyvrsmsjizzbn3fpfl"))))
(build-system gnu-build-system)
(native-inputs `(("python" ,python-2)))
(inputs `(("samtools" ,samtools)
("zlib" ,zlib)))
(arguments
'(#:test-target "test"
#:phases
(alist-cons-after
'unpack 'patch-makefile-SHELL-definition
(lambda _
;; patch-makefile-SHELL cannot be used here as it does not
;; yet patch definitions with `:='. Since changes to
;; patch-makefile-SHELL result in a full rebuild, features
;; of patch-makefile-SHELL are reimplemented here.
(substitute* "Makefile"
(("^SHELL := .*$") (string-append "SHELL := " (which "bash") " -e \n"))))
(alist-delete
'configure
(alist-replace
'install
(lambda* (#:key outputs #:allow-other-keys)
(let ((bin (string-append (assoc-ref outputs "out") "/bin/")))
(mkdir-p bin)
(for-each (lambda (file)
(copy-file file (string-append bin (basename file))))
(find-files "bin" ".*"))))
%standard-phases)))))
(home-page "https://github.com/arq5x/bedtools2")
(synopsis "Tools for genome analysis and arithmetic")
(description
"Collectively, the bedtools utilities are a swiss-army knife of tools for
a wide-range of genomics analysis tasks. The most widely-used tools enable
genome arithmetic: that is, set theory on the genome. For example, bedtools
allows one to intersect, merge, count, complement, and shuffle genomic
intervals from multiple files in widely-used genomic file formats such as BAM,
BED, GFF/GTF, VCF.")
(license license:gpl2)))
(define-public bowtie
(package
(name "bowtie")
(version "2.2.4")
(source (origin
(method url-fetch)
(uri (string-append "https://github.com/BenLangmead/bowtie2/archive/v"
version ".tar.gz"))
(sha256
(base32
"15dnbqippwvhyh9zqjhaxkabk7lm1xbh1nvar1x4b5kwm117zijn"))
(modules '((guix build utils)))
(snippet
'(substitute* "Makefile"
(("^CC = .*$") "CC = gcc")
(("^CPP = .*$") "CPP = g++")
;; replace BUILD_HOST and BUILD_TIME for deterministic build
(("-DBUILD_HOST=.*") "-DBUILD_HOST=\"\\\"guix\\\"\"")
(("-DBUILD_TIME=.*") "-DBUILD_TIME=\"\\\"0\\\"\"")))))
(build-system gnu-build-system)
(inputs `(("perl" ,perl)
("perl-clone" ,perl-clone)
("perl-test-deep" ,perl-test-deep)
("perl-test-simple" ,perl-test-simple)
("python" ,python-2)))
(arguments
'(#:make-flags '("allall")
#:phases
(alist-delete
'configure
(alist-replace
'install
(lambda* (#:key outputs #:allow-other-keys)
(let ((bin (string-append (assoc-ref outputs "out") "/bin/")))
(mkdir-p bin)
(for-each (lambda (file)
(copy-file file (string-append bin file)))
(find-files "." "bowtie2.*"))))
(alist-replace
'check
(lambda* (#:key outputs #:allow-other-keys)
(system* "perl"
"scripts/test/simple_tests.pl"
"--bowtie2=./bowtie2"
"--bowtie2-build=./bowtie2-build"))
%standard-phases)))))
(home-page "http://bowtie-bio.sourceforge.net/bowtie2/index.shtml")
(synopsis "Fast and sensitive nucleotide sequence read aligner")
(description
"Bowtie 2 is a fast and memory-efficient tool for aligning sequencing
reads to long reference sequences. It is particularly good at aligning reads
of about 50 up to 100s or 1,000s of characters, and particularly good at
aligning to relatively long (e.g. mammalian) genomes. Bowtie 2 indexes the
genome with an FM Index to keep its memory footprint small: for the human
genome, its memory footprint is typically around 3.2 GB. Bowtie 2 supports
gapped, local, and paired-end alignment modes.")
(license license:gpl3+)))
(define-public samtools
(package
(name "samtools")
@ -43,7 +150,14 @@
"1y5p2hs4gif891b4ik20275a8xf3qrr1zh9wpysp4g8m0g1jckf2"))))
(build-system gnu-build-system)
(arguments
'(#:make-flags (list (string-append "prefix=" (assoc-ref %outputs "out")))
`(;; There are 87 test failures when building on non-64-bit architectures
;; due to invalid test data. This has since been fixed upstream (see
;; <https://github.com/samtools/samtools/pull/307>), but as there has
;; not been a new release we disable the tests for all non-64-bit
;; systems.
#:tests? ,(string=? (or (%current-system) (%current-target-system))
"x86_64-linux")
#:make-flags (list (string-append "prefix=" (assoc-ref %outputs "out")))
#:phases
(alist-cons-after
'unpack

View File

@ -70,6 +70,26 @@ independent of the input data and can be reduced, if necessary, at some cost
in compression.")
(license license:zlib)))
(define-public fastjar
(package
(name "fastjar")
(version "0.98")
(source (origin
(method url-fetch)
(uri (string-append "mirror://savannah/fastjar/fastjar-"
version ".tar.gz"))
(sha256
(base32
"0iginbz2m15hcsa3x4y7v3mhk54gr1r7m3ghx0pg4n46vv2snmpi"))))
(build-system gnu-build-system)
(inputs `(("zlib" ,zlib)))
(home-page "http://savannah.nongnu.org/projects/fastjar")
(synopsis "Replacement for Sun's 'jar' utility")
(description
"FastJar is an attempt to create a much faster replacement for Sun's 'jar'
utility. Instead of being written in Java, FastJar is written in C.")
(license license:gpl2+)))
(define-public gzip
(package
(name "gzip")

View File

@ -0,0 +1,48 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2014 Tomáš Čech <sleep_walker@suse.cz>
;;;
;;; This file is part of GNU Guix.
;;;
;;; GNU Guix is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Guix is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
(define-module (gnu packages game-development)
#:use-module (guix licenses)
#:use-module (guix packages)
#:use-module (guix download)
#:use-module (guix build-system cmake)
#:use-module (gnu packages))
(define-public bullet
(package
(name "bullet")
(version "2.82-r2704")
(source (origin
(method url-fetch)
(uri (string-append "https://bullet.googlecode.com/files/bullet-"
version ".tgz"))
(sha256
(base32
"1lnfksxa9b1slyfcxys313ymsllvbsnxh9np06azkbgpfvmwkr37"))))
(build-system cmake-build-system)
(arguments '(#:tests? #f ; no 'test' target
#:configure-flags (list
(string-append
"-DCMAKE_CXX_FLAGS=-fPIC "
(or (getenv "CXXFLAGS") "")))))
(home-page "http://bulletphysics.org/")
(synopsis "3D physics engine library")
(description
"Bullet is a physics engine library usable for collision detection. It
is used in some video games and movies.")
(license zlib)))

View File

@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2013 Andreas Enge <andreas@enge.fr>
;;; Copyright © 2014 Mark H Weaver <mhw@netris.org>
;;;
;;; This file is part of GNU Guix.
;;;
@ -33,14 +34,14 @@
(define-public lcms
(package
(name "lcms")
(version "2.4")
(version "2.6")
(source (origin
(method url-fetch)
(uri (string-append
"http://downloads.sourceforge.net/project/lcms/lcms/"
version "/lcms2-" version ".tar.gz"))
(sha256 (base32
"1s1ppvqaydf2yqc72mw6zfviwxccb311a6hrbi802sgjxw84sl9a"))))
"1c8lgq8gfs3nyplvbx9k8wzfj6r2bqi3f611vb1m8z3476454wji"))))
(build-system gnu-build-system)
(inputs `(("libjpeg-8" ,libjpeg-8)
("libtiff" ,libtiff)
@ -118,13 +119,13 @@ printing, and psresize, for adjusting page sizes.")
(define-public ghostscript
(package
(name "ghostscript")
(version "9.06.0")
(version "9.14.0")
(source (origin
(method url-fetch)
(uri (string-append "mirror://gnu/ghostscript/gnu-ghostscript-"
version ".tar.xz"))
(sha256 (base32
"0bcg2203p7cm0f53f3s883xhj2c91xnaxakj2cy7kcdknfxplvs4"))))
"0q4jj41p0qbr4mgcc9q78f5zs8cm1g57wgryhsm2yq4lfslm3ib1"))))
(build-system gnu-build-system)
(inputs `(("freetype" ,freetype)
("lcms" ,lcms)
@ -160,7 +161,7 @@ printing, and psresize, for adjusting page sizes.")
file format. It also includes a C library that implements the graphics
capabilities of the PostScript language. It supports a wide variety of
output file formats and printers.")
(license license:gpl3+)
(license license:agpl3+)
(home-page "http://www.gnu.org/software/ghostscript/")))
(define-public gs-fonts

View File

@ -47,7 +47,8 @@
#:use-module (gnu packages gl)
#:use-module (gnu packages compression)
#:use-module (gnu packages xorg)
#:use-module (gnu packages xdisorg))
#:use-module (gnu packages xdisorg)
#:use-module (gnu packages ncurses))
(define-public brasero
(package
@ -1292,3 +1293,89 @@ engineering.")
(description
"The default GNOME 3 themes (Adwaita and some accessibility themes).")
(license license:lgpl2.1+)))
(define-public vala
(package
(name "vala")
(version "0.26.1")
(source (origin
(method url-fetch)
(uri (string-append "mirror://gnome/sources/" name "/"
(version-major+minor version) "/"
name "-" version ".tar.xz"))
(sha256
(base32
"0swyym2papln0f62ah05dpvq3vv6fssap26jq2zqp9dkkaqsn1w4"))))
(build-system gnu-build-system)
(arguments '(#:make-flags '("CC=gcc")))
(native-inputs
`(("pkg-config" ,pkg-config)
("flex" ,flex)
("bison" ,bison)
("xsltproc" ,libxslt)
("dbus" ,dbus) ; for dbus tests
("gobject-introspection" ,gobject-introspection))) ; for gir tests
(propagated-inputs
`(("glib" ,glib))) ; required by libvala-0.26.pc
(home-page "http://live.gnome.org/Vala/")
(synopsis "Compiler for the GObject type system")
(description
"Vala is a programming language that aims to bring modern programming
language features to GNOME developers without imposing any additional runtime
requirements and without using a different ABI compared to applications and
libraries written in C.")
(license license:lgpl2.1+)))
(define-public vte
(package
(name "vte")
(version "0.38.2")
(source (origin
(method url-fetch)
(uri (string-append "mirror://gnome/sources/" name "/"
(version-major+minor version) "/"
name "-" version ".tar.xz"))
(sha256
(base32
"1rbxrigff9yszbgdw0gw4c2saz4d1hbbpz21phzxx14w49wvmnmj"))))
(build-system gnu-build-system)
(native-inputs
`(("pkg-config" ,pkg-config)
("intltool" ,intltool)
("vala" ,vala)
("gobject-introspection" ,gobject-introspection)
("glib" ,glib "bin") ; for glib-genmarshal, etc.
("xmllint" ,libxml2)))
(propagated-inputs
`(("gtk+" ,gtk+))) ; required by libvte-2.91.pc
(home-page "http://www.gnome.org/")
(synopsis "Virtual Terminal Emulator")
(description
"VTE is a library (libvte) implementing a terminal emulator widget for
GTK+, and a minimal sample application (vte) using that. Vte is mainly used in
gnome-terminal, but can also be used to embed a console/terminal in games,
editors, IDEs, etc.")
(license license:lgpl2.1+)))
;; stable version for gtk2, required by xfce4-terminal.
(define-public vte/gtk+-2
(package (inherit vte)
(name "vte")
(version "0.28.2")
(source (origin
(method url-fetch)
(uri (string-append "mirror://gnome/sources/" name "/"
(version-major+minor version) "/"
name "-" version ".tar.xz"))
(sha256
(base32
"1bmhahkf8wdsra9whd3k5l5z4rv7r58ksr8mshzajgq2ma0hpkw6"))))
(arguments
'(#:configure-flags '("--disable-python")))
(native-inputs
`(("pkg-config" ,pkg-config)
("intltool" ,intltool)
("glib" ,glib "bin"))) ; for glib-genmarshal, etc.
(propagated-inputs
`(("gtk+" ,gtk+-2) ; required by libvte.pc
("ncurses" ,ncurses))))) ; required by libvte.la

View File

@ -1,6 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2013 Andreas Enge <andreas@enge.fr>
;;; Copyright © 2013 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2013, 2014 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2014 Mark H Weaver <mhw@netris.org>
;;;
;;; This file is part of GNU Guix.
@ -53,7 +53,17 @@
version "/" name "-" version ".tar.xz"))
(sha256
(base32
"02r9klfc0z26w270inq652249hq0wfzvwhzvwmk0n8v8nzkk5idh"))))
"02r9klfc0z26w270inq652249hq0wfzvwhzvwmk0n8v8nzkk5idh"))
(patches (map search-patch
'("icecat-CVE-2014-1587-bug-1042567.patch"
"icecat-CVE-2014-1587-bug-1072847.patch"
"icecat-CVE-2014-1587-bug-1079729.patch"
"icecat-CVE-2014-1587-bug-1080312.patch"
"icecat-CVE-2014-1587-bug-1089207.patch"
"icecat-CVE-2014-1590.patch"
"icecat-CVE-2014-1592.patch"
"icecat-CVE-2014-1593.patch"
"icecat-CVE-2014-1594.patch")))))
(build-system gnu-build-system)
(inputs
`(("alsa-lib" ,alsa-lib)
@ -90,6 +100,7 @@
"--disable-debug"
"--disable-debug-symbols"
"--enable-pulseaudio"
"--disable-webrtc" ; webrtc fails to build
"--with-system-zlib"

View File

@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2013 Andreas Enge <andreas@enge.fr>
;;; Copyright © 2014 Mark H Weaver <mhw@netris.org>
;;;
;;; This file is part of GNU Guix.
;;;
@ -31,13 +32,13 @@
(define-public groff
(package
(name "groff")
(version "1.22.2")
(version "1.22.3")
(source (origin
(method url-fetch)
(uri (string-append "mirror://gnu/groff/groff-" version
".tar.gz"))
(sha256 (base32
"0xi07nhj5vdgax37rj25mwxzdmsz1ifx50hjgc6hqbkpqkd6821q"))))
"1998v2kcs288d3y7kfxpvl369nqi06zbbvjzafyvyl3pr7bajj1s"))))
(build-system gnu-build-system)
(inputs `(("ghostscript" ,ghostscript)
("netpbm" ,netpbm)))
@ -45,6 +46,7 @@
("perl" ,perl)
("psutils" ,psutils)
("texinfo" ,texinfo)))
(arguments '(#:parallel-build? #f)) ; parallel build fails
(synopsis "Typesetting from plain text mixed with formatting commands")
(description
"Groff is a typesetting package that reads plain text and produces

View File

@ -192,7 +192,7 @@ for SYSTEM, or #f if there is no configuration for SYSTEM."
#f)))
(define-public linux-libre
(let* ((version "3.18")
(let* ((version "3.18.1")
(build-phase
'(lambda* (#:key system inputs #:allow-other-keys #:rest args)
;; Apply the neat patch.
@ -265,7 +265,7 @@ for SYSTEM, or #f if there is no configuration for SYSTEM."
(uri (linux-libre-urls version))
(sha256
(base32
"1kv03bhls9rya4sg3qixyjirc79pn2g5bcwldcj7hs4apa77sd0g"))))
"0yj6sz9cvsbhrc9jksr4wgg63crzmqh65903l7bq9k0gz1f3x1s8"))))
(build-system gnu-build-system)
(native-inputs `(("perl" ,perl)
("bc" ,bc)

View File

@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright 2014 John Darrington <jmd@gnu.org>
;;; Copyright © 2014 John Darrington <jmd@gnu.org>
;;; Copyright © 2014 Mark H Weaver <mhw@netris.org>
;;;
;;; This file is part of GNU Guix.
;;;
@ -20,8 +21,11 @@
#:use-module (gnu packages)
#:use-module (gnu packages which)
#:use-module (gnu packages linux)
#:use-module (guix licenses)
#:use-module (gnu packages pkg-config)
#:use-module (gnu packages openssl)
#:use-module ((guix licenses) #:prefix l:)
#:use-module (guix packages)
#:use-module (guix utils)
#:use-module (guix download)
#:use-module (guix build-system gnu)
#:use-module (srfi srfi-1))
@ -29,29 +33,31 @@
(define-public ntp
(package
(name "ntp")
(version "4.2.6p5")
(version "4.2.8")
(source (origin
(method url-fetch)
(uri (string-append
"http://www.eecis.udel.edu/~ntp/ntp_spool/ntp4/ntp-"
(string-join (take (string-split version #\.) 2) ".")
"http://archive.ntp.org/ntp4/ntp-"
(version-major+minor version)
"/ntp-" version ".tar.gz"))
(sha256
(base32
"077r69a41hasl8zf5c44km7cqgfhrkaj6a4jnr75j7nkz5qq7ayn"))))
(native-inputs `(("which" ,which)))
"1vnqa1542d01xmlkw8f3rq57y360b2j7yxkkg9b11955nvw0v4if"))))
(native-inputs `(("which" ,which)
("pkg-config" ,pkg-config)))
(inputs
`(("openssl" ,openssl)
;; Build with POSIX capabilities support on GNU/Linux. This allows 'ntpd'
;; to run as non-root (when invoked with '-u'.)
(if (string-suffix? "-linux"
,@(if (string-suffix? "-linux"
(or (%current-target-system) (%current-system)))
`(("libcap" ,libcap))
'()))
'())))
(build-system gnu-build-system)
(synopsis "Real time clock synchonization system")
(description "NTP is a system designed to synchronize the clocks of
computers over a network.")
(license (x11-style
(license (l:x11-style
"http://www.eecis.udel.edu/~mills/ntp/html/copyright.html"
"A non-copyleft free licence from the University of Delaware"))
(home-page "http://www.ntp.org")))

View File

@ -0,0 +1,30 @@
commit 60529fc02cf10482d8fecd699eea271ddc22bcb9
Author: Jason Orendorff <jorendorff@mozilla.com>
Date: Thu Aug 28 15:43:57 2014 -0500
Bug 1042567 - Reflect JSPropertyOp properties more consistently as data properties. r=efaust, a=lmandel
Modified js/src/jsobj.cpp
diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp
index 2745509..ad336f3 100644
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -235,11 +235,18 @@ js::GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, HandleId id,
if (pobj->isNative()) {
desc.setAttributes(GetShapeAttributes(pobj, shape));
if (desc.hasGetterOrSetterObject()) {
+ MOZ_ASSERT(desc.isShared());
doGet = false;
if (desc.hasGetterObject())
desc.setGetterObject(shape->getterObject());
if (desc.hasSetterObject())
desc.setSetterObject(shape->setterObject());
+ } else {
+ // This is either a straight-up data property or (rarely) a
+ // property with a JSPropertyOp getter/setter. The latter must be
+ // reported to the caller as a plain data property, so don't
+ // populate desc.getter/setter, and mask away the SHARED bit.
+ desc.attributesRef() &= ~JSPROP_SHARED;
}
} else {
if (!JSObject::getGenericAttributes(cx, pobj, id, &desc.attributesRef()))

View File

@ -0,0 +1,19 @@
commit 5d91f3b10f999e852e0392470198bd6aefc87e1e
Author: Jeff Muizelaar <jmuizelaar@mozilla.com>
Date: Tue Oct 28 10:08:25 2014 -0400
Bug 1072847 - Initialize mSurface. r=BenWa, a=bkerensa
Modified gfx/2d/DrawTargetCairo.cpp
diff --git a/gfx/2d/DrawTargetCairo.cpp b/gfx/2d/DrawTargetCairo.cpp
index 48c2c73..78d9e4f 100644
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -353,6 +353,7 @@ NeedIntermediateSurface(const Pattern& aPattern, const DrawOptions& aOptions)
DrawTargetCairo::DrawTargetCairo()
: mContext(nullptr)
+ , mSurface(nullptr)
, mLockedBits(nullptr)
{
}

View File

@ -0,0 +1,191 @@
commit 5de6730cc26744b9efcf4d4adb4a4c45023ef8a0
Author: Randell Jesup <rjesup@jesup.org>
Date: Tue Oct 28 11:06:00 2014 -0400
Bug 1079729: Fix handling of increasing number of SCTP channels used by DataChannels r=tuexen a=lsblakk
Modified media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h
diff --git a/media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h b/media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h
index ba8e1ff..8d964f1 100755
--- a/media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h
@@ -225,7 +225,7 @@ typedef struct fsmdef_media_t_ {
/*
* Data Channel properties
*/
-#define WEBRTC_DATACHANNEL_STREAMS_DEFAULT 16
+#define WEBRTC_DATACHANNEL_STREAMS_DEFAULT 256
uint32 datachannel_streams;
char datachannel_protocol[SDP_MAX_STRING_LEN + 1];
Modified netwerk/sctp/datachannel/DataChannel.cpp
diff --git a/netwerk/sctp/datachannel/DataChannel.cpp b/netwerk/sctp/datachannel/DataChannel.cpp
index 414e3db..a00d938 100644
--- a/netwerk/sctp/datachannel/DataChannel.cpp
+++ b/netwerk/sctp/datachannel/DataChannel.cpp
@@ -910,10 +910,12 @@ DataChannelConnection::RequestMoreStreams(int32_t aNeeded)
uint32_t outStreamsNeeded;
socklen_t len;
- if (aNeeded + mStreams.Length() > MAX_NUM_STREAMS)
+ if (aNeeded + mStreams.Length() > MAX_NUM_STREAMS) {
aNeeded = MAX_NUM_STREAMS - mStreams.Length();
- if (aNeeded <= 0)
+ }
+ if (aNeeded <= 0) {
return false;
+ }
len = (socklen_t)sizeof(struct sctp_status);
if (usrsctp_getsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_STATUS, &status, &len) < 0) {
@@ -922,19 +924,25 @@ DataChannelConnection::RequestMoreStreams(int32_t aNeeded)
}
outStreamsNeeded = aNeeded; // number to add
- memset(&sas, 0, sizeof(struct sctp_add_streams));
+ // Note: if multiple channel opens happen when we don't have enough space,
+ // we'll call RequestMoreStreams() multiple times
+ memset(&sas, 0, sizeof(sas));
sas.sas_instrms = 0;
sas.sas_outstrms = (uint16_t)outStreamsNeeded; /* XXX error handling */
// Doesn't block, we get an event when it succeeds or fails
if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_ADD_STREAMS, &sas,
(socklen_t) sizeof(struct sctp_add_streams)) < 0) {
- if (errno == EALREADY)
+ if (errno == EALREADY) {
+ LOG(("Already have %u output streams", outStreamsNeeded));
return true;
+ }
LOG(("***failed: setsockopt ADD errno=%d", errno));
return false;
}
LOG(("Requested %u more streams", outStreamsNeeded));
+ // We add to mStreams when we get a SCTP_STREAM_CHANGE_EVENT and the
+ // values are larger than mStreams.Length()
return true;
}
@@ -1050,6 +1058,13 @@ DataChannelConnection::SendDeferredMessages()
channel->mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED,
channel->mPrPolicy, channel->mPrValue)) {
channel->mFlags &= ~DATA_CHANNEL_FLAGS_SEND_REQ;
+
+ channel->mState = OPEN;
+ channel->mReady = true;
+ LOG(("%s: sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel.get()));
+ NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
+ DataChannelOnMessageAvailable::ON_CHANNEL_OPEN, this,
+ channel));
sent = true;
} else {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
@@ -1177,6 +1192,7 @@ DataChannelConnection::HandleOpenRequestMessage(const struct rtcweb_datachannel_
prPolicy = SCTP_PR_SCTP_TTL;
break;
default:
+ LOG(("Unknown channel type", req->channel_type));
/* XXX error handling */
return;
}
@@ -1203,6 +1219,10 @@ DataChannelConnection::HandleOpenRequestMessage(const struct rtcweb_datachannel_
}
return;
}
+ if (stream >= mStreams.Length()) {
+ LOG(("%s: stream %u out of bounds (%u)", __FUNCTION__, stream, mStreams.Length()));
+ return;
+ }
nsCString label(nsDependentCSubstring(&req->label[0], ntohs(req->label_length)));
nsCString protocol(nsDependentCSubstring(&req->label[ntohs(req->label_length)],
@@ -1220,8 +1240,8 @@ DataChannelConnection::HandleOpenRequestMessage(const struct rtcweb_datachannel_
channel->mState = DataChannel::WAITING_TO_OPEN;
- LOG(("%s: sending ON_CHANNEL_CREATED for %s/%s: %u", __FUNCTION__,
- channel->mLabel.get(), channel->mProtocol.get(), stream));
+ LOG(("%s: sending ON_CHANNEL_CREATED for %s/%s: %u (state %u)", __FUNCTION__,
+ channel->mLabel.get(), channel->mProtocol.get(), stream, channel->mState));
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CHANNEL_CREATED,
this, channel));
@@ -1739,13 +1759,14 @@ DataChannelConnection::HandleStreamResetEvent(const struct sctp_stream_reset_eve
// 2. We sent our own reset (CLOSING); either they crossed on the
// wire, or this is a response to our Reset.
// Go to CLOSED
- // 3. We've sent a open but haven't gotten a response yet (OPENING)
+ // 3. We've sent a open but haven't gotten a response yet (CONNECTING)
// I believe this is impossible, as we don't have an input stream yet.
LOG(("Incoming: Channel %u closed, state %d",
channel->mStream, channel->mState));
ASSERT_WEBRTC(channel->mState == DataChannel::OPEN ||
channel->mState == DataChannel::CLOSING ||
+ channel->mState == DataChannel::CONNECTING ||
channel->mState == DataChannel::WAITING_TO_OPEN);
if (channel->mState == DataChannel::OPEN ||
channel->mState == DataChannel::WAITING_TO_OPEN) {
@@ -1791,20 +1812,21 @@ DataChannelConnection::HandleStreamChangeEvent(const struct sctp_stream_change_e
return;
} else {
if (strchg->strchange_instrms > mStreams.Length()) {
- LOG(("Other side increased streamds from %u to %u",
+ LOG(("Other side increased streams from %u to %u",
mStreams.Length(), strchg->strchange_instrms));
}
- if (strchg->strchange_outstrms > mStreams.Length()) {
+ if (strchg->strchange_outstrms > mStreams.Length() ||
+ strchg->strchange_instrms > mStreams.Length()) {
uint16_t old_len = mStreams.Length();
+ uint16_t new_len = std::max(strchg->strchange_outstrms,
+ strchg->strchange_instrms);
LOG(("Increasing number of streams from %u to %u - adding %u (in: %u)",
- old_len,
- strchg->strchange_outstrms,
- strchg->strchange_outstrms - old_len,
+ old_len, new_len, new_len - old_len,
strchg->strchange_instrms));
// make sure both are the same length
- mStreams.AppendElements(strchg->strchange_outstrms - old_len);
+ mStreams.AppendElements(new_len - old_len);
LOG(("New length = %d (was %d)", mStreams.Length(), old_len));
- for (uint32_t i = old_len; i < mStreams.Length(); ++i) {
+ for (size_t i = old_len; i < mStreams.Length(); ++i) {
mStreams[i] = nullptr;
}
// Re-process any channels waiting for streams.
@@ -1815,13 +1837,17 @@ DataChannelConnection::HandleStreamChangeEvent(const struct sctp_stream_change_e
// Could make a more complex API for OpenXxxFinish() and avoid this loop
int32_t num_needed = mPending.GetSize();
LOG(("%d of %d new streams already needed", num_needed,
- strchg->strchange_outstrms - old_len));
- num_needed -= (strchg->strchange_outstrms - old_len); // number we added
+ new_len - old_len));
+ num_needed -= (new_len - old_len); // number we added
if (num_needed > 0) {
if (num_needed < 16)
num_needed = 16;
LOG(("Not enough new streams, asking for %d more", num_needed));
RequestMoreStreams(num_needed);
+ } else if (strchg->strchange_outstrms < strchg->strchange_instrms) {
+ LOG(("Requesting %d output streams to match partner",
+ strchg->strchange_instrms - strchg->strchange_outstrms));
+ RequestMoreStreams(strchg->strchange_instrms - strchg->strchange_outstrms);
}
ProcessQueuedOpens();
Modified netwerk/sctp/datachannel/DataChannelProtocol.h
diff --git a/netwerk/sctp/datachannel/DataChannelProtocol.h b/netwerk/sctp/datachannel/DataChannelProtocol.h
index 549f74b..74fbe58 100644
--- a/netwerk/sctp/datachannel/DataChannelProtocol.h
+++ b/netwerk/sctp/datachannel/DataChannelProtocol.h
@@ -17,7 +17,7 @@
#endif
// Duplicated in fsm.def
-#define WEBRTC_DATACHANNEL_STREAMS_DEFAULT 16
+#define WEBRTC_DATACHANNEL_STREAMS_DEFAULT 256
#define DATA_CHANNEL_PPID_CONTROL 50
#define DATA_CHANNEL_PPID_BINARY 52

View File

@ -0,0 +1,308 @@
commit d74bdb4589ad714e2a45e282974db075de2be673
Author: Randell Jesup <rjesup@jesup.org>
Date: Wed Nov 12 22:59:53 2014 -0500
Bug 1080312 - Update iteration code from upstream. r=jesup, a=abillings
Modified netwerk/sctp/src/moz.build
diff --git a/netwerk/sctp/src/moz.build b/netwerk/sctp/src/moz.build
index 1901a41..82103b9 100644
--- a/netwerk/sctp/src/moz.build
+++ b/netwerk/sctp/src/moz.build
@@ -31,7 +31,6 @@ SOURCES += [
'user_environment.c',
'user_mbuf.c',
'user_recv_thread.c',
- 'user_sctp_timer_iterate.c',
'user_socket.c',
]
Modified netwerk/sctp/src/netinet/sctp_callout.c
diff --git a/netwerk/sctp/src/netinet/sctp_callout.c b/netwerk/sctp/src/netinet/sctp_callout.c
index 67b7566..e8ac77f 100755
--- a/netwerk/sctp/src/netinet/sctp_callout.c
+++ b/netwerk/sctp/src/netinet/sctp_callout.c
@@ -30,9 +30,27 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/
+#if defined(__Userspace__)
+#include <sys/types.h>
+#if !defined (__Userspace_os_Windows)
+#include <sys/wait.h>
+#include <unistd.h>
+#include <pthread.h>
+#endif
+#if defined(__Userspace_os_NaCl)
+#include <sys/select.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <netinet/sctp_sysctl.h>
+#include <netinet/sctp_pcb.h>
+#else
#include <netinet/sctp_os.h>
#include <netinet/sctp_callout.h>
#include <netinet/sctp_pcb.h>
+#endif
/*
* Callout/Timer routines for OS that doesn't have them
@@ -117,24 +135,16 @@ sctp_os_timer_stop(sctp_os_timer_t *c)
return (1);
}
-#if defined(__APPLE__)
-/*
- * For __APPLE__, use a single main timer at a faster resolution than
- * fastim. The timer just calls this existing callout infrastructure.
- */
-#endif
-void
-sctp_timeout(void *arg SCTP_UNUSED)
+static void
+sctp_handle_tick(int delta)
{
sctp_os_timer_t *c;
void (*c_func)(void *);
void *c_arg;
SCTP_TIMERQ_LOCK();
-#if defined(__APPLE__)
/* update our tick count */
- ticks += SCTP_BASE_VAR(sctp_main_timer_ticks);
-#endif
+ ticks += delta;
c = TAILQ_FIRST(&SCTP_BASE_INFO(callqueue));
while (c) {
if (c->c_time <= ticks) {
@@ -155,9 +165,60 @@ sctp_timeout(void *arg SCTP_UNUSED)
}
sctp_os_timer_next = NULL;
SCTP_TIMERQ_UNLOCK();
+}
#if defined(__APPLE__)
- /* restart the main timer */
+void
+sctp_timeout(void *arg SCTP_UNUSED)
+{
+ sctp_handle_tick(SCTP_BASE_VAR(sctp_main_timer_ticks));
sctp_start_main_timer();
+}
#endif
+
+#if defined(__Userspace__)
+#define TIMEOUT_INTERVAL 10
+
+void *
+user_sctp_timer_iterate(void *arg)
+{
+ for (;;) {
+#if defined (__Userspace_os_Windows)
+ Sleep(TIMEOUT_INTERVAL);
+#else
+ struct timeval timeout;
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 1000 * TIMEOUT_INTERVAL;
+ select(0, NULL, NULL, NULL, &timeout);
+#endif
+ if (SCTP_BASE_VAR(timer_thread_should_exit)) {
+ break;
+ }
+ sctp_handle_tick(MSEC_TO_TICKS(TIMEOUT_INTERVAL));
+ }
+ return (NULL);
}
+
+void
+sctp_start_timer(void)
+{
+ /*
+ * No need to do SCTP_TIMERQ_LOCK_INIT();
+ * here, it is being done in sctp_pcb_init()
+ */
+#if defined (__Userspace_os_Windows)
+ if ((SCTP_BASE_VAR(timer_thread) = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)user_sctp_timer_iterate, NULL, 0, NULL)) == NULL) {
+ SCTP_PRINTF("ERROR; Creating ithread failed\n");
+ }
+#else
+ int rc;
+
+ rc = pthread_create(&SCTP_BASE_VAR(timer_thread), NULL, user_sctp_timer_iterate, NULL);
+ if (rc) {
+ SCTP_PRINTF("ERROR; return code from pthread_create() is %d\n", rc);
+ }
+#endif
+}
+
+#endif
Modified netwerk/sctp/src/netinet/sctp_callout.h
diff --git a/netwerk/sctp/src/netinet/sctp_callout.h b/netwerk/sctp/src/netinet/sctp_callout.h
index 2782945..c53c5a4 100755
--- a/netwerk/sctp/src/netinet/sctp_callout.h
+++ b/netwerk/sctp/src/netinet/sctp_callout.h
@@ -64,7 +64,6 @@ __FBSDID("$FreeBSD$");
#endif
extern int ticks;
-extern void sctp_start_timer();
#endif
TAILQ_HEAD(calloutlist, sctp_callout);
@@ -94,6 +93,11 @@ int sctp_os_timer_stop(sctp_os_timer_t *);
#define SCTP_OS_TIMER_ACTIVE(tmr) ((tmr)->c_flags & SCTP_CALLOUT_ACTIVE)
#define SCTP_OS_TIMER_DEACTIVATE(tmr) ((tmr)->c_flags &= ~SCTP_CALLOUT_ACTIVE)
+#if defined(__Userspace__)
+void sctp_start_timer(void);
+#endif
+#if defined(__APPLE__)
void sctp_timeout(void *);
+#endif
#endif
Modified netwerk/sctp/src/netinet/sctp_usrreq.c
diff --git a/netwerk/sctp/src/netinet/sctp_usrreq.c b/netwerk/sctp/src/netinet/sctp_usrreq.c
index d4115ad..c17ea04 100755
--- a/netwerk/sctp/src/netinet/sctp_usrreq.c
+++ b/netwerk/sctp/src/netinet/sctp_usrreq.c
@@ -56,6 +56,9 @@ __FBSDID("$FreeBSD: head/sys/netinet/sctp_usrreq.c 259943 2013-12-27 13:07:00Z t
#include <netinet/sctp_timer.h>
#include <netinet/sctp_auth.h>
#include <netinet/sctp_bsd_addr.h>
+#if defined(__Userspace__)
+#include <netinet/sctp_callout.h>
+#endif
#if !defined(__Userspace_os_Windows)
#include <netinet/udp.h>
#endif
Deleted netwerk/sctp/src/user_sctp_timer_iterate.c
diff --git a/netwerk/sctp/src/user_sctp_timer_iterate.c b/netwerk/sctp/src/user_sctp_timer_iterate.c
deleted file mode 100755
index 0a9dbce..0000000
--- a/netwerk/sctp/src/user_sctp_timer_iterate.c
+++ /dev/null
@@ -1,119 +0,0 @@
-/*-
- * Copyright (c) 2012 Michael Tuexen
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- */
-
-#include <sys/types.h>
-#if !defined (__Userspace_os_Windows)
-#include <sys/wait.h>
-#include <unistd.h>
-#include <pthread.h>
-#endif
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <netinet/sctp_pcb.h>
-#include <netinet/sctp_sysctl.h>
-#include "netinet/sctp_callout.h"
-
-/* This is the polling time of callqueue in milliseconds
- * 10ms seems to work well. 1ms was giving erratic behavior
- */
-#define TIMEOUT_INTERVAL 10
-
-extern int ticks;
-
-void *
-user_sctp_timer_iterate(void *arg)
-{
- sctp_os_timer_t *c;
- void (*c_func)(void *);
- void *c_arg;
- sctp_os_timer_t *sctp_os_timer_next;
- /*
- * The MSEC_TO_TICKS conversion depends on hz. The to_ticks in
- * sctp_os_timer_start also depends on hz. E.g. if hz=1000 then
- * for multiple INIT the to_ticks is 2000, 4000, 8000, 16000, 32000, 60000
- * and further to_ticks level off at 60000 i.e. 60 seconds.
- * If hz=100 then for multiple INIT the to_ticks are 200, 400, 800 and so-on.
- */
- for (;;) {
-#if defined (__Userspace_os_Windows)
- Sleep(TIMEOUT_INTERVAL);
-#else
- struct timeval timeout;
-
- timeout.tv_sec = 0;
- timeout.tv_usec = 1000 * TIMEOUT_INTERVAL;
- select(0, NULL, NULL, NULL, &timeout);
-#endif
- if (SCTP_BASE_VAR(timer_thread_should_exit)) {
- break;
- }
- SCTP_TIMERQ_LOCK();
- /* update our tick count */
- ticks += MSEC_TO_TICKS(TIMEOUT_INTERVAL);
- c = TAILQ_FIRST(&SCTP_BASE_INFO(callqueue));
- while (c) {
- if (c->c_time <= ticks) {
- sctp_os_timer_next = TAILQ_NEXT(c, tqe);
- TAILQ_REMOVE(&SCTP_BASE_INFO(callqueue), c, tqe);
- c_func = c->c_func;
- c_arg = c->c_arg;
- c->c_flags &= ~SCTP_CALLOUT_PENDING;
- SCTP_TIMERQ_UNLOCK();
- c_func(c_arg);
- SCTP_TIMERQ_LOCK();
- c = sctp_os_timer_next;
- } else {
- c = TAILQ_NEXT(c, tqe);
- }
- }
- SCTP_TIMERQ_UNLOCK();
- }
- return (NULL);
-}
-
-void
-sctp_start_timer(void)
-{
- /*
- * No need to do SCTP_TIMERQ_LOCK_INIT();
- * here, it is being done in sctp_pcb_init()
- */
-#if defined (__Userspace_os_Windows)
- if ((SCTP_BASE_VAR(timer_thread) = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)user_sctp_timer_iterate, NULL, 0, NULL)) == NULL) {
- SCTP_PRINTF("ERROR; Creating ithread failed\n");
- }
-#else
- int rc;
-
- rc = pthread_create(&SCTP_BASE_VAR(timer_thread), NULL, user_sctp_timer_iterate, NULL);
- if (rc) {
- SCTP_PRINTF("ERROR; return code from pthread_create() is %d\n", rc);
- }
-#endif
-}

View File

@ -0,0 +1,119 @@
commit 9df10fea93b483af6646ef2f7aab35598fbaab2f
Author: Nils Ohlmeier [:drno] <drno@ohlmeier.org>
Date: Thu Nov 6 12:21:57 2014 -0500
Bug 1089207: fix parsing of invalid fmtp att r=drno,jesup a=lmandel
Modified media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr.c
diff --git a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr.c b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr.c
index fa5ca2e..33d26c0 100644
--- a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr.c
+++ b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr.c
@@ -458,7 +458,6 @@ sdp_result_e sdp_parse_attr_fmtp (sdp_t *sdp_p, sdp_attr_t *attr_p,
char tmp[SDP_MAX_STRING_LEN];
char *src_ptr;
char *temp_ptr = NULL;
- tinybool flag=FALSE;
char *tok=NULL;
char *temp=NULL;
u16 custom_x=0;
@@ -495,29 +494,11 @@ sdp_result_e sdp_parse_attr_fmtp (sdp_t *sdp_p, sdp_attr_t *attr_p,
fmtp_p->packetization_mode = 0;
fmtp_p->level_asymmetry_allowed = SDP_DEFAULT_LEVEL_ASYMMETRY_ALLOWED_VALUE;
- /* BEGIN - a typical macro fn to replace '/' with ';' from fmtp line*/
- /* This ugly replacement of '/' with ';' is only done because
- * econf/MS client sends in this wierd /illegal format.
- * fmtp parameters MUST be separated by ';'
- */
temp_ptr = cpr_strdup(ptr);
if (temp_ptr == NULL) {
return (SDP_FAILURE);
}
fmtp_ptr = src_ptr = temp_ptr;
- while (flag == FALSE) {
- if (*src_ptr == '\n') {
- flag = TRUE;
- break;
- }
- if (*src_ptr == '/') {
- *src_ptr =';' ;
- }
- src_ptr++;
- }
- /* END */
- /* Once we move to RFC compliant video codec implementations, the above
- * patch should be removed */
src_ptr = temp_ptr;
while (!done) {
Modified media/webrtc/signaling/src/sipcc/core/sdp/sdp_main.c
diff --git a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_main.c b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_main.c
index 0be02aa..9760d4e 100644
--- a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_main.c
+++ b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_main.c
@@ -1002,7 +1002,12 @@ sdp_result_e sdp_parse (sdp_t *sdp_p, char **bufp, u16 len)
*/
ptr = next_ptr;
line_end = sdp_findchar(ptr, "\n");
- if (line_end >= (*bufp + len)) {
+ if ((line_end >= (*bufp + len)) ||
+ (*line_end == '\0')) {
+ /* As this does not update the result value the SDP up to this point
+ * is still accept as valid. So encountering this is not treated as
+ * an error.
+ */
sdp_parse_error(sdp_p->peerconnection,
"%s End of line beyond end of buffer.",
sdp_p->debug_str);
Modified media/webrtc/signaling/test/sdp_unittests.cpp
diff --git a/media/webrtc/signaling/test/sdp_unittests.cpp b/media/webrtc/signaling/test/sdp_unittests.cpp
index 51df09b..9f98eed 100644
--- a/media/webrtc/signaling/test/sdp_unittests.cpp
+++ b/media/webrtc/signaling/test/sdp_unittests.cpp
@@ -755,13 +755,13 @@ TEST_F(SdpTest, parseFmtpMaxFs) {
u32 val = 0;
ParseSdp(kVideoSdp + "a=fmtp:120 max-fs=300;max-fr=30\r\n");
ASSERT_EQ(sdp_attr_get_fmtp_max_fs(sdp_ptr_, 1, 0, 1, &val), SDP_SUCCESS);
- ASSERT_EQ(val, 300);
+ ASSERT_EQ(val, 300U);
}
TEST_F(SdpTest, parseFmtpMaxFr) {
u32 val = 0;
ParseSdp(kVideoSdp + "a=fmtp:120 max-fs=300;max-fr=30\r\n");
ASSERT_EQ(sdp_attr_get_fmtp_max_fr(sdp_ptr_, 1, 0, 1, &val), SDP_SUCCESS);
- ASSERT_EQ(val, 30);
+ ASSERT_EQ(val, 30U);
}
TEST_F(SdpTest, addFmtpMaxFs) {
@@ -789,6 +789,29 @@ TEST_F(SdpTest, addFmtpMaxFsFr) {
std::string::npos);
}
+static const std::string kBrokenFmtp =
+ "v=0\r\n"
+ "o=- 137331303 2 IN IP4 127.0.0.1\r\n"
+ "s=SIP Call\r\n"
+ "t=0 0\r\n"
+ "m=video 56436 RTP/SAVPF 120\r\n"
+ "c=IN IP4 198.51.100.7\r\n"
+ "a=rtpmap:120 VP8/90000\r\n"
+ /* Note: the \0 in this string triggered bz://1089207
+ */
+ "a=fmtp:120 max-fs=300;max\0fr=30";
+
+TEST_F(SdpTest, parseBrokenFmtp) {
+ u32 val = 0;
+ char *buf = const_cast<char *>(kBrokenFmtp.data());
+ ResetSdp();
+ /* We need to manually invoke the parser here to be able to specify the length
+ * of the string beyond the \0 in last line of the string.
+ */
+ ASSERT_EQ(sdp_parse(sdp_ptr_, &buf, 165), SDP_SUCCESS);
+ ASSERT_EQ(sdp_attr_get_fmtp_max_fs(sdp_ptr_, 1, 0, 1, &val), SDP_INVALID_PARAMETER);
+}
+
} // End namespace test.
int main(int argc, char **argv) {

View File

@ -0,0 +1,33 @@
commit 50c5ca4bacf7cda77c3a7ab1b8d82ded18fb3355
Author: Olli Pettay <Olli.Pettay@helsinki.fi>
Date: Sun Nov 2 22:01:55 2014 +0200
Bug 1087633 - Filter out XPConnect wrapped input streams. r=bz, a=lmandel
Modified content/base/src/nsXMLHttpRequest.h
diff --git a/content/base/src/nsXMLHttpRequest.h b/content/base/src/nsXMLHttpRequest.h
index b1fc4e3..4ab4f29 100644
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -28,7 +28,8 @@
#include "nsIPrincipal.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsISizeOfEventTarget.h"
-
+#include "nsIXPConnect.h"
+#include "nsIInputStream.h"
#include "mozilla/Assertions.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/MemoryReporting.h"
@@ -446,6 +447,11 @@ public:
void Send(nsIInputStream* aStream, ErrorResult& aRv)
{
NS_ASSERTION(aStream, "Null should go to string version");
+ nsCOMPtr<nsIXPConnectWrappedJS> wjs = do_QueryInterface(aStream);
+ if (wjs) {
+ aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
+ return;
+ }
aRv = Send(RequestBody(aStream));
}
void SendAsBinary(const nsAString& aBody, ErrorResult& aRv);

View File

@ -0,0 +1,400 @@
commit 7efadbb03cdffa11ebfc2da3113377d2f33b893b
Author: Henri Sivonen <hsivonen@hsivonen.fi>
Date: Mon Nov 3 15:23:26 2014 +0200
Bug 1088635. r=smaug, a=bkerensa
Modified content/base/src/nsDocument.cpp
diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp
index cbed38d..3493bce 100644
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -3916,7 +3916,7 @@ nsDocument::InsertChildAt(nsIContent* aKid, uint32_t aIndex,
bool aNotify)
{
if (aKid->IsElement() && GetRootElement()) {
- NS_ERROR("Inserting element child when we already have one");
+ NS_WARNING("Inserting root element when we already have one");
return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR;
}
Modified parser/html/nsHtml5Parser.cpp
diff --git a/parser/html/nsHtml5Parser.cpp b/parser/html/nsHtml5Parser.cpp
index a485be4..f28adb4 100644
--- a/parser/html/nsHtml5Parser.cpp
+++ b/parser/html/nsHtml5Parser.cpp
@@ -237,7 +237,8 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
* WillBuildModel to be called before the document has had its
* script global object set.
*/
- mExecutor->WillBuildModel(eDTDMode_unknown);
+ rv = mExecutor->WillBuildModel(eDTDMode_unknown);
+ NS_ENSURE_SUCCESS(rv, rv);
}
// Return early if the parser has processed EOF
@@ -255,7 +256,7 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
}
mDocumentClosed = true;
if (!mBlocked && !mInDocumentWrite) {
- ParseUntilBlocked();
+ return ParseUntilBlocked();
}
return NS_OK;
}
@@ -378,7 +379,8 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
if (mTreeBuilder->HasScript()) {
mTreeBuilder->Flush(); // Move ops to the executor
- mExecutor->FlushDocumentWrite(); // run the ops
+ rv = mExecutor->FlushDocumentWrite(); // run the ops
+ NS_ENSURE_SUCCESS(rv, rv);
// Flushing tree ops can cause all sorts of things.
// Return early if the parser got terminated.
if (mExecutor->IsComplete()) {
@@ -437,7 +439,8 @@ nsHtml5Parser::Parse(const nsAString& aSourceBuffer,
"Buffer wasn't tokenized to completion?");
// Scripting semantics require a forced tree builder flush here
mTreeBuilder->Flush(); // Move ops to the executor
- mExecutor->FlushDocumentWrite(); // run the ops
+ rv = mExecutor->FlushDocumentWrite(); // run the ops
+ NS_ENSURE_SUCCESS(rv, rv);
} else if (stackBuffer.hasMore()) {
// The buffer wasn't tokenized to completion. Tokenize the untokenized
// content in order to preload stuff. This content will be retokenized
@@ -594,11 +597,13 @@ nsHtml5Parser::IsScriptCreated()
/* End nsIParser */
// not from interface
-void
+nsresult
nsHtml5Parser::ParseUntilBlocked()
{
- if (mBlocked || mExecutor->IsComplete() || NS_FAILED(mExecutor->IsBroken())) {
- return;
+ nsresult rv = mExecutor->IsBroken();
+ NS_ENSURE_SUCCESS(rv, rv);
+ if (mBlocked || mExecutor->IsComplete()) {
+ return NS_OK;
}
NS_ASSERTION(mExecutor->HasStarted(), "Bad life cycle.");
NS_ASSERTION(!mInDocumentWrite,
@@ -611,7 +616,7 @@ nsHtml5Parser::ParseUntilBlocked()
if (mFirstBuffer == mLastBuffer) {
if (mExecutor->IsComplete()) {
// something like cache manisfests stopped the parse in mid-flight
- return;
+ return NS_OK;
}
if (mDocumentClosed) {
NS_ASSERTION(!GetStreamParser(),
@@ -620,8 +625,10 @@ nsHtml5Parser::ParseUntilBlocked()
mTreeBuilder->StreamEnded();
mTreeBuilder->Flush();
mExecutor->FlushDocumentWrite();
+ // The below call does memory cleanup, so call it even if the
+ // parser has been marked as broken.
mTokenizer->end();
- return;
+ return NS_OK;
}
// never release the last buffer.
NS_ASSERTION(!mLastBuffer->getStart() && !mLastBuffer->getEnd(),
@@ -643,14 +650,14 @@ nsHtml5Parser::ParseUntilBlocked()
NS_ASSERTION(mExecutor->IsInFlushLoop(),
"How did we come here without being in the flush loop?");
}
- return; // no more data for now but expecting more
+ return NS_OK; // no more data for now but expecting more
}
mFirstBuffer = mFirstBuffer->next;
continue;
}
if (mBlocked || mExecutor->IsComplete()) {
- return;
+ return NS_OK;
}
// now we have a non-empty buffer
@@ -667,10 +674,11 @@ nsHtml5Parser::ParseUntilBlocked()
}
if (mTreeBuilder->HasScript()) {
mTreeBuilder->Flush();
- mExecutor->FlushDocumentWrite();
+ nsresult rv = mExecutor->FlushDocumentWrite();
+ NS_ENSURE_SUCCESS(rv, rv);
}
if (mBlocked) {
- return;
+ return NS_OK;
}
}
continue;
Modified parser/html/nsHtml5Parser.h
diff --git a/parser/html/nsHtml5Parser.h b/parser/html/nsHtml5Parser.h
index aff79c7..e2ef2f8 100644
--- a/parser/html/nsHtml5Parser.h
+++ b/parser/html/nsHtml5Parser.h
@@ -262,7 +262,7 @@ class nsHtml5Parser : public nsIParser,
/**
* Parse until pending data is exhausted or a script blocks the parser
*/
- void ParseUntilBlocked();
+ nsresult ParseUntilBlocked();
private:
Modified parser/html/nsHtml5StreamParser.cpp
diff --git a/parser/html/nsHtml5StreamParser.cpp b/parser/html/nsHtml5StreamParser.cpp
index 4790568..7e3917b 100644
--- a/parser/html/nsHtml5StreamParser.cpp
+++ b/parser/html/nsHtml5StreamParser.cpp
@@ -796,7 +796,7 @@ nsHtml5StreamParser::WriteStreamBytes(const uint8_t* aFromSegment,
// NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE.
if (!mLastBuffer) {
NS_WARNING("mLastBuffer should not be null!");
- MarkAsBroken();
+ MarkAsBroken(NS_ERROR_NULL_POINTER);
return NS_ERROR_NULL_POINTER;
}
if (mLastBuffer->getEnd() == NS_HTML5_STREAM_PARSER_READ_BUFFER_SIZE) {
@@ -902,7 +902,8 @@ nsHtml5StreamParser::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
* WillBuildModel to be called before the document has had its
* script global object set.
*/
- mExecutor->WillBuildModel(eDTDMode_unknown);
+ rv = mExecutor->WillBuildModel(eDTDMode_unknown);
+ NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<nsHtml5OwningUTF16Buffer> newBuf =
nsHtml5OwningUTF16Buffer::FalliblyCreate(
@@ -1003,8 +1004,9 @@ nsHtml5StreamParser::DoStopRequest()
if (!mUnicodeDecoder) {
uint32_t writeCount;
- if (NS_FAILED(FinalizeSniffing(nullptr, 0, &writeCount, 0))) {
- MarkAsBroken();
+ nsresult rv;
+ if (NS_FAILED(rv = FinalizeSniffing(nullptr, 0, &writeCount, 0))) {
+ MarkAsBroken(rv);
return;
}
} else if (mFeedChardet) {
@@ -1076,7 +1078,7 @@ nsHtml5StreamParser::DoDataAvailable(const uint8_t* aBuffer, uint32_t aLength)
rv = SniffStreamBytes(aBuffer, aLength, &writeCount);
}
if (NS_FAILED(rv)) {
- MarkAsBroken();
+ MarkAsBroken(rv);
return;
}
NS_ASSERTION(writeCount == aLength, "Wrong number of stream bytes written/sniffed.");
@@ -1662,13 +1664,13 @@ nsHtml5StreamParser::TimerFlush()
}
void
-nsHtml5StreamParser::MarkAsBroken()
+nsHtml5StreamParser::MarkAsBroken(nsresult aRv)
{
NS_ASSERTION(IsParserThread(), "Wrong thread!");
mTokenizerMutex.AssertCurrentThreadOwns();
Terminate();
- mTreeBuilder->MarkAsBroken();
+ mTreeBuilder->MarkAsBroken(aRv);
mozilla::DebugOnly<bool> hadOps = mTreeBuilder->Flush(false);
NS_ASSERTION(hadOps, "Should have had the markAsBroken op!");
if (NS_FAILED(NS_DispatchToMainThread(mExecutorFlusher))) {
Modified parser/html/nsHtml5StreamParser.h
diff --git a/parser/html/nsHtml5StreamParser.h b/parser/html/nsHtml5StreamParser.h
index c7dcbbe..476ef16 100644
--- a/parser/html/nsHtml5StreamParser.h
+++ b/parser/html/nsHtml5StreamParser.h
@@ -218,7 +218,7 @@ class nsHtml5StreamParser : public nsICharsetDetectionObserver {
}
#endif
- void MarkAsBroken();
+ void MarkAsBroken(nsresult aRv);
/**
* Marks the stream parser as interrupted. If you ever add calls to this
Modified parser/html/nsHtml5TreeBuilderCppSupplement.h
diff --git a/parser/html/nsHtml5TreeBuilderCppSupplement.h b/parser/html/nsHtml5TreeBuilderCppSupplement.h
index 4cd5c7c..1e65394 100644
--- a/parser/html/nsHtml5TreeBuilderCppSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderCppSupplement.h
@@ -949,14 +949,14 @@ nsHtml5TreeBuilder::DropHandles()
}
void
-nsHtml5TreeBuilder::MarkAsBroken()
+nsHtml5TreeBuilder::MarkAsBroken(nsresult aRv)
{
if (MOZ_UNLIKELY(mBuilder)) {
MOZ_ASSUME_UNREACHABLE("Must not call this with builder.");
return;
}
mOpQueue.Clear(); // Previous ops don't matter anymore
- mOpQueue.AppendElement()->Init(eTreeOpMarkAsBroken);
+ mOpQueue.AppendElement()->Init(aRv);
}
void
Modified parser/html/nsHtml5TreeBuilderHSupplement.h
diff --git a/parser/html/nsHtml5TreeBuilderHSupplement.h b/parser/html/nsHtml5TreeBuilderHSupplement.h
index a321e80..8d380eb 100644
--- a/parser/html/nsHtml5TreeBuilderHSupplement.h
+++ b/parser/html/nsHtml5TreeBuilderHSupplement.h
@@ -223,4 +223,4 @@
void errEndWithUnclosedElements(nsIAtom* aName);
- void MarkAsBroken();
+ void MarkAsBroken(nsresult aRv);
Modified parser/html/nsHtml5TreeOpExecutor.cpp
diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp
index ebcafca..6c52e5f 100644
--- a/parser/html/nsHtml5TreeOpExecutor.cpp
+++ b/parser/html/nsHtml5TreeOpExecutor.cpp
@@ -411,7 +411,11 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
GetParser()->GetStreamParser();
// Now parse content left in the document.write() buffer queue if any.
// This may generate tree ops on its own or dequeue a speculation.
- GetParser()->ParseUntilBlocked();
+ nsresult rv = GetParser()->ParseUntilBlocked();
+ if (NS_FAILED(rv)) {
+ MarkAsBroken(rv);
+ return;
+ }
}
if (mOpQueue.IsEmpty()) {
@@ -496,21 +500,24 @@ nsHtml5TreeOpExecutor::RunFlushLoop()
}
}
-void
+nsresult
nsHtml5TreeOpExecutor::FlushDocumentWrite()
{
+ nsresult rv = IsBroken();
+ NS_ENSURE_SUCCESS(rv, rv);
+
FlushSpeculativeLoads(); // Make sure speculative loads never start after the
// corresponding normal loads for the same URLs.
if (MOZ_UNLIKELY(!mParser)) {
// The parse has ended.
mOpQueue.Clear(); // clear in order to be able to assert in destructor
- return;
+ return rv;
}
if (mFlushState != eNotFlushing) {
// XXX Can this happen? In case it can, let's avoid crashing.
- return;
+ return rv;
}
mFlushState = eInFlush;
@@ -545,7 +552,7 @@ nsHtml5TreeOpExecutor::FlushDocumentWrite()
}
NS_ASSERTION(mFlushState == eInDocUpdate,
"Tried to perform tree op outside update batch.");
- nsresult rv = iter->Perform(this, &scriptElement);
+ rv = iter->Perform(this, &scriptElement);
if (NS_FAILED(rv)) {
MarkAsBroken(rv);
break;
@@ -560,13 +567,14 @@ nsHtml5TreeOpExecutor::FlushDocumentWrite()
if (MOZ_UNLIKELY(!mParser)) {
// Ending the doc update caused a call to nsIParser::Terminate().
- return;
+ return rv;
}
if (scriptElement) {
// must be tail call when mFlushState is eNotFlushing
RunScript(scriptElement);
}
+ return rv;
}
// copied from HTML content sink
Modified parser/html/nsHtml5TreeOpExecutor.h
diff --git a/parser/html/nsHtml5TreeOpExecutor.h b/parser/html/nsHtml5TreeOpExecutor.h
index 9617dcb..1f81448 100644
--- a/parser/html/nsHtml5TreeOpExecutor.h
+++ b/parser/html/nsHtml5TreeOpExecutor.h
@@ -173,7 +173,7 @@ class nsHtml5TreeOpExecutor : public nsHtml5DocumentBuilder,
void RunFlushLoop();
- void FlushDocumentWrite();
+ nsresult FlushDocumentWrite();
void MaybeSuspend();
Modified parser/html/nsHtml5TreeOperation.cpp
diff --git a/parser/html/nsHtml5TreeOperation.cpp b/parser/html/nsHtml5TreeOperation.cpp
index 48b71dc..7ad65247 100644
--- a/parser/html/nsHtml5TreeOperation.cpp
+++ b/parser/html/nsHtml5TreeOperation.cpp
@@ -214,6 +214,9 @@ nsHtml5TreeOperation::AppendToDocument(nsIContent* aNode,
nsIDocument* doc = aBuilder->GetDocument();
uint32_t childCount = doc->GetChildCount();
rv = doc->AppendChildTo(aNode, false);
+ if (rv == NS_ERROR_DOM_HIERARCHY_REQUEST_ERR) {
+ return NS_OK;
+ }
NS_ENSURE_SUCCESS(rv, rv);
nsNodeUtils::ContentInserted(doc, aNode, childCount);
@@ -739,8 +742,7 @@ nsHtml5TreeOperation::Perform(nsHtml5TreeOpExecutor* aBuilder,
return NS_OK;
}
case eTreeOpMarkAsBroken: {
- aBuilder->MarkAsBroken(NS_ERROR_OUT_OF_MEMORY);
- return NS_OK;
+ return mOne.result;
}
case eTreeOpRunScript: {
nsIContent* node = *(mOne.node);
Modified parser/html/nsHtml5TreeOperation.h
diff --git a/parser/html/nsHtml5TreeOperation.h b/parser/html/nsHtml5TreeOperation.h
index 2727733..06d0274 100644
--- a/parser/html/nsHtml5TreeOperation.h
+++ b/parser/html/nsHtml5TreeOperation.h
@@ -435,6 +435,15 @@ class nsHtml5TreeOperation {
mFour.integer = aInt;
}
+ inline void Init(nsresult aRv)
+ {
+ NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
+ "Op code must be uninitialized when initializing.");
+ NS_PRECONDITION(NS_FAILED(aRv), "Initialized tree op with non-failure.");
+ mOpCode = eTreeOpMarkAsBroken;
+ mOne.result = aRv;
+ }
+
inline void InitAddClass(nsIContentHandle* aNode, const char16_t* aClass)
{
NS_PRECONDITION(mOpCode == eTreeOpUninitialized,
@@ -487,11 +496,12 @@ class nsHtml5TreeOperation {
nsIAtom* atom;
nsHtml5HtmlAttributes* attributes;
nsHtml5DocumentMode mode;
- char16_t* unicharPtr;
+ char16_t* unicharPtr;
char* charPtr;
nsHtml5TreeOperationStringPair* stringPair;
nsAHtml5TreeBuilderState* state;
int32_t integer;
+ nsresult result;
} mOne, mTwo, mThree, mFour;
};

View File

@ -0,0 +1,154 @@
commit a58cea744ac5b93b99a66554e1029b2c7aa3255d
Author: Matthew Gregan <kinetik@flim.org>
Date: Tue Nov 11 08:58:52 2014 +1300
Bug 1085175. r=roc, a=dveditz
Modified content/media/MediaCache.cpp
diff --git a/content/media/MediaCache.cpp b/content/media/MediaCache.cpp
index 598d905..c99f724 100644
--- a/content/media/MediaCache.cpp
+++ b/content/media/MediaCache.cpp
@@ -1174,6 +1174,7 @@ MediaCache::Update()
// Figure out where we should be reading from. It's the first
// uncached byte after the current mStreamOffset.
int64_t dataOffset = stream->GetCachedDataEndInternal(stream->mStreamOffset);
+ MOZ_ASSERT(dataOffset >= 0);
// Compute where we'd actually seek to to read at readOffset
int64_t desiredOffset = dataOffset;
@@ -1702,6 +1703,7 @@ MediaCacheStream::NotifyDataStarted(int64_t aOffset)
ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor());
NS_WARN_IF_FALSE(aOffset == mChannelOffset,
"Server is giving us unexpected offset");
+ MOZ_ASSERT(aOffset >= 0);
mChannelOffset = aOffset;
if (mStreamLength >= 0) {
// If we started reading at a certain offset, then for sure
@@ -2118,23 +2120,28 @@ MediaCacheStream::Seek(int32_t aWhence, int64_t aOffset)
return NS_ERROR_FAILURE;
int64_t oldOffset = mStreamOffset;
+ int64_t newOffset = mStreamOffset;
switch (aWhence) {
case PR_SEEK_END:
if (mStreamLength < 0)
return NS_ERROR_FAILURE;
- mStreamOffset = mStreamLength + aOffset;
+ newOffset = mStreamLength + aOffset;
break;
case PR_SEEK_CUR:
- mStreamOffset += aOffset;
+ newOffset += aOffset;
break;
case PR_SEEK_SET:
- mStreamOffset = aOffset;
+ newOffset = aOffset;
break;
default:
NS_ERROR("Unknown whence");
return NS_ERROR_FAILURE;
}
+ if (newOffset < 0)
+ return NS_ERROR_FAILURE;
+ mStreamOffset = newOffset;
+
CACHE_LOG(PR_LOG_DEBUG, ("Stream %p Seek to %lld", this, (long long)mStreamOffset));
gMediaCache->NoteSeek(this, oldOffset);
@@ -2176,11 +2183,10 @@ MediaCacheStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
break;
}
size = std::min(size, bytesRemaining);
- // Clamp size until 64-bit file size issues (bug 500784) are fixed.
+ // Clamp size until 64-bit file size issues are fixed.
size = std::min(size, int64_t(INT32_MAX));
}
- int32_t bytes;
int32_t cacheBlock = streamBlock < mBlocks.Length() ? mBlocks[streamBlock] : -1;
if (cacheBlock < 0) {
// We don't have a complete cached block here.
@@ -2208,7 +2214,10 @@ MediaCacheStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
// We can just use the data in mPartialBlockBuffer. In fact we should
// use it rather than waiting for the block to fill and land in
// the cache.
- bytes = std::min<int64_t>(size, streamWithPartialBlock->mChannelOffset - mStreamOffset);
+ int64_t bytes = std::min<int64_t>(size, streamWithPartialBlock->mChannelOffset - mStreamOffset);
+ // Clamp bytes until 64-bit file size issues are fixed.
+ bytes = std::min(bytes, int64_t(INT32_MAX));
+ NS_ABORT_IF_FALSE(bytes >= 0 && bytes <= aCount, "Bytes out of range.");
memcpy(aBuffer,
reinterpret_cast<char*>(streamWithPartialBlock->mPartialBlockBuffer.get()) + offsetInStreamBlock, bytes);
if (mCurrentMode == MODE_METADATA) {
@@ -2232,6 +2241,7 @@ MediaCacheStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aBytes)
gMediaCache->NoteBlockUsage(this, cacheBlock, mCurrentMode, TimeStamp::Now());
int64_t offset = cacheBlock*BLOCK_SIZE + offsetInStreamBlock;
+ int32_t bytes;
NS_ABORT_IF_FALSE(size >= 0 && size <= INT32_MAX, "Size out of range.");
nsresult rv = gMediaCache->ReadCacheFile(offset, aBuffer + count, int32_t(size), &bytes);
if (NS_FAILED(rv)) {
@@ -2268,9 +2278,7 @@ MediaCacheStream::ReadAt(int64_t aOffset, char* aBuffer,
}
nsresult
-MediaCacheStream::ReadFromCache(char* aBuffer,
- int64_t aOffset,
- int64_t aCount)
+MediaCacheStream::ReadFromCache(char* aBuffer, int64_t aOffset, int64_t aCount)
{
ReentrantMonitorAutoEnter mon(gMediaCache->GetReentrantMonitor());
if (mClosed)
@@ -2292,7 +2300,7 @@ MediaCacheStream::ReadFromCache(char* aBuffer,
return NS_ERROR_FAILURE;
}
size = std::min(size, bytesRemaining);
- // Clamp size until 64-bit file size issues (bug 500784) are fixed.
+ // Clamp size until 64-bit file size issues are fixed.
size = std::min(size, int64_t(INT32_MAX));
}
@@ -2303,7 +2311,10 @@ MediaCacheStream::ReadFromCache(char* aBuffer,
// We can just use the data in mPartialBlockBuffer. In fact we should
// use it rather than waiting for the block to fill and land in
// the cache.
- bytes = std::min<int64_t>(size, mChannelOffset - streamOffset);
+ // Clamp bytes until 64-bit file size issues are fixed.
+ int64_t toCopy = std::min<int64_t>(size, mChannelOffset - streamOffset);
+ bytes = std::min(toCopy, int64_t(INT32_MAX));
+ NS_ABORT_IF_FALSE(bytes >= 0 && bytes <= toCopy, "Bytes out of range.");
memcpy(aBuffer + count,
reinterpret_cast<char*>(mPartialBlockBuffer.get()) + offsetInStreamBlock, bytes);
} else {
Modified media/libnestegg/include/nestegg-stdint.h
diff --git a/media/libnestegg/include/nestegg-stdint.h b/media/libnestegg/include/nestegg-stdint.h
index 599a7a5..c315991 100644
--- a/media/libnestegg/include/nestegg-stdint.h
+++ b/media/libnestegg/include/nestegg-stdint.h
@@ -1,6 +1,9 @@
#ifdef _WIN32
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
+#if !defined(INT64_MAX)
+#define INT64_MAX 9223372036854775807LL
+#endif
#else
#include <stdint.h>
#endif
Modified media/libnestegg/src/nestegg.c
diff --git a/media/libnestegg/src/nestegg.c b/media/libnestegg/src/nestegg.c
index 8813cf2..56884d7 100644
--- a/media/libnestegg/src/nestegg.c
+++ b/media/libnestegg/src/nestegg.c
@@ -1950,6 +1950,9 @@ nestegg_offset_seek(nestegg * ctx, uint64_t offset)
{
int r;
+ if (offset > INT64_MAX)
+ return -1;
+
/* Seek and set up parser state for segment-level element (Cluster). */
r = ne_io_seek(ctx->io, offset, NESTEGG_SEEK_SET);
if (r != 0)

View File

@ -0,0 +1,34 @@
commit 7a8497c0df722b1ed145b99a82c71ed1f7b1d6ce
Author: Markus Stange <mstange@themasta.com>
Date: Thu Oct 9 21:26:27 2014 -0400
Bug 1074280 - Use AsContainerLayer() in order to avoid a bad cast. r=roc, a=bkerensa
Modified gfx/layers/basic/BasicLayerManager.cpp
diff --git a/gfx/layers/basic/BasicLayerManager.cpp b/gfx/layers/basic/BasicLayerManager.cpp
index 5a3a1f6..ff42bc0 100644
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -901,18 +901,17 @@ BasicLayerManager::PaintLayer(gfxContext* aTarget,
RenderTraceScope trace("BasicLayerManager::PaintLayer", "707070");
const nsIntRect* clipRect = aLayer->GetEffectiveClipRect();
- // aLayer might not be a container layer, but if so we take care not to use
- // the container variable
- BasicContainerLayer* container = static_cast<BasicContainerLayer*>(aLayer);
- bool needsGroup = aLayer->GetFirstChild() &&
+ BasicContainerLayer* container =
+ static_cast<BasicContainerLayer*>(aLayer->AsContainerLayer());
+ bool needsGroup = container &&
container->UseIntermediateSurface();
BasicImplData* data = ToData(aLayer);
bool needsClipToVisibleRegion =
data->GetClipToVisibleRegion() && !aLayer->AsThebesLayer();
- NS_ASSERTION(needsGroup || !aLayer->GetFirstChild() ||
+ NS_ASSERTION(needsGroup || !container ||
container->GetOperator() == CompositionOp::OP_OVER,
"non-OVER operator should have forced UseIntermediateSurface");
- NS_ASSERTION(!aLayer->GetFirstChild() || !aLayer->GetMaskLayer() ||
+ NS_ASSERTION(!container || !aLayer->GetMaskLayer() ||
container->UseIntermediateSurface(),
"ContainerLayer with mask layer should force UseIntermediateSurface");

View File

@ -0,0 +1,115 @@
Search for xfce4 panel plugins in the directories specified
in XDG_DATA_DIRS and X_XFCE4_LIB_DIRS. For discussion of the
relevant issues, see:
https://bugzilla.xfce.org/show_bug.cgi?id=5455
Patch by Mark H Weaver <mhw@netris.org>
--- xfce4-panel-4.10.0/panel/panel-module.c.orig 2012-04-28 16:31:35.000000000 -0400
+++ xfce4-panel-4.10.0/panel/panel-module.c 2014-12-14 01:31:55.728107386 -0500
@@ -35,8 +35,14 @@
#include <panel/panel-plugin-external-wrapper.h>
#include <panel/panel-plugin-external-46.h>
-#define PANEL_PLUGINS_LIB_DIR (LIBDIR G_DIR_SEPARATOR_S "panel" G_DIR_SEPARATOR_S "plugins")
-#define PANEL_PLUGINS_LIB_DIR_OLD (LIBDIR G_DIR_SEPARATOR_S "panel-plugins")
+#define PANEL_PLUGINS_LIB_DIR_TAIL (G_DIR_SEPARATOR_S "panel" G_DIR_SEPARATOR_S "plugins")
+#define PANEL_PLUGINS_LIB_DIR_TAIL_OLD (G_DIR_SEPARATOR_S "panel-plugins")
+
+static const gchar *plugins_lib_dir_tails[] =
+{
+ PANEL_PLUGINS_LIB_DIR_TAIL,
+ PANEL_PLUGINS_LIB_DIR_TAIL_OLD
+};
typedef enum _PanelModuleRunMode PanelModuleRunMode;
@@ -335,21 +341,39 @@
/* show a messsage if the old module path key still exists */
g_message ("Plugin %s: The \"X-XFCE-Module-Path\" key is "
"ignored in \"%s\", the panel will look for the "
- "module in %s. See bug #5455 why this decision was made",
- name, filename, PANEL_PLUGINS_LIB_DIR);
+ "module in DIR%s for each DIR in $X_XFCE4_LIB_DIRS "
+ "(%s by default). See bug #5455 for discussion.",
+ name, filename, PANEL_PLUGINS_LIB_DIR_TAIL, LIBDIR);
}
#endif
- path = g_module_build_path (PANEL_PLUGINS_LIB_DIR, module_name);
- found = g_file_test (path, G_FILE_TEST_EXISTS);
+ /* search for module */
+ {
+ gchar *dirs_string;
+ gchar **dirs;
+ int i, j;
+
+ dirs_string = (gchar *) g_getenv ("X_XFCE4_LIB_DIRS");
+ if (!dirs_string)
+ dirs_string = LIBDIR;
+ dirs = g_strsplit (dirs_string, G_SEARCHPATH_SEPARATOR_S, 0);
+
+ found = FALSE;
+ path = NULL;
+
+ for (i = 0; !found && dirs[i] != NULL; i++)
+ for (j = 0; !found && j < G_N_ELEMENTS (plugins_lib_dir_tails); j++)
+ {
+ gchar *dir = g_strconcat (dirs[i], plugins_lib_dir_tails[j], NULL);
+
+ g_free (path);
+ path = g_module_build_path (dir, module_name);
+ found = g_file_test (path, G_FILE_TEST_EXISTS);
+ g_free (dir);
+ }
- if (!found)
- {
- /* deprecated location for module plugin directories */
- g_free (path);
- path = g_module_build_path (PANEL_PLUGINS_LIB_DIR_OLD, module_name);
- found = g_file_test (path, G_FILE_TEST_EXISTS);
- }
+ g_strfreev (dirs);
+ }
if (G_LIKELY (found))
{
--- xfce4-panel-4.10.0/panel/panel-module-factory.c.orig 2012-04-28 16:31:35.000000000 -0400
+++ xfce4-panel-4.10.0/panel/panel-module-factory.c 2014-12-13 23:55:27.439404812 -0500
@@ -42,6 +42,11 @@
#define PANEL_PLUGINS_DATA_DIR (DATADIR G_DIR_SEPARATOR_S "panel" G_DIR_SEPARATOR_S "plugins")
#define PANEL_PLUGINS_DATA_DIR_OLD (DATADIR G_DIR_SEPARATOR_S "panel-plugins")
+static const gchar *plugins_data_dir_tails[] =
+{
+ (G_DIR_SEPARATOR_S "xfce4" G_DIR_SEPARATOR_S "panel" G_DIR_SEPARATOR_S "plugins"),
+ (G_DIR_SEPARATOR_S "xfce4" G_DIR_SEPARATOR_S "panel-plugins")
+};
static void panel_module_factory_finalize (GObject *object);
@@ -223,8 +228,22 @@
panel_module_factory_load_modules (PanelModuleFactory *factory,
gboolean warn_if_known)
{
+ const gchar * const * system_data_dirs;
+ int i, j;
+
panel_return_if_fail (PANEL_IS_MODULE_FACTORY (factory));
+ system_data_dirs = g_get_system_data_dirs ();
+ for (i = 0; system_data_dirs[i] != NULL; i++)
+ for (j = 0; j < G_N_ELEMENTS (plugins_data_dir_tails); j++)
+ {
+ gchar *dir;
+
+ dir = g_strconcat (system_data_dirs[i], plugins_data_dir_tails[j], NULL);
+ panel_module_factory_load_modules_dir (factory, dir, warn_if_known);
+ g_free (dir);
+ }
+
/* load from the new and old location */
panel_module_factory_load_modules_dir (factory, PANEL_PLUGINS_DATA_DIR, warn_if_known);
panel_module_factory_load_modules_dir (factory, PANEL_PLUGINS_DATA_DIR_OLD, warn_if_known);

View File

@ -74,6 +74,27 @@
(home-page "http://www.perl.org/")
(license gpl1+))) ; or "Artistic"
(define-public perl-clone
(package
(name "perl-clone")
(version "0.37")
(source (origin
(method url-fetch)
(uri (string-append "mirror://cpan/authors/id/G/GA/GARU/"
"Clone-" version ".tar.gz"))
(sha256
(base32
"17fdhxpzrq2nwim3zkcrz4m9gjixp0i886yz54ysrshxy3k53wnr"))))
(build-system perl-build-system)
(synopsis "Recursively copy Perl datatypes")
(description
"This module provides a clone() method which makes recursive copies of
nested hash, array, scalar and reference types, including tied variables and
objects.")
(home-page (string-append "http://search.cpan.org/~garu/"
"Clone-" version))
(license (package-license perl))))
(define-public perl-file-list
(package
(name "perl-file-list")
@ -253,6 +274,54 @@ Perlish API and none of the bloat and rarely used features of IPC::Run.")
;; licenses, any version."
(license (list bsd-3 gpl3+))))
(define-public perl-test-deep
(package
(name "perl-test-deep")
(version "0.114")
(source (origin
(method url-fetch)
(uri (string-append "mirror://cpan/authors/id/R/RJ/RJBS/"
"Test-Deep-" version ".tar.gz"))
(sha256
(base32
"09yr47vw7vj27sdik312x08938higcij8ybyq8k67mlccx8cpqf0"))))
(build-system perl-build-system)
(inputs `(("perl-test-tester" ,perl-test-tester)
("perl-test-nowarnings" ,perl-test-nowarnings)))
(synopsis "Flexible deep comparison for the Test::Builder framework")
(description
"Test::Deep compares two structures by going through each level, ensuring
that the values match, that arrays and hashes have the same elements and that
references are blessed into the correct class. It also handles circular data
structures without getting caught in an infinite loop.")
(home-page (string-append "http://search.cpan.org/~rjbs/"
"Test-Deep-" version))
(license gpl1+))) ; or "Artistic License"
(define-public perl-test-nowarnings
(package
(name "perl-test-nowarnings")
(version "1.04")
(source (origin
(method url-fetch)
(uri (string-append "mirror://cpan/authors/id/A/AD/ADAMK/"
"Test-NoWarnings-" version ".tar.gz"))
(sha256
(base32
"0v385ch0hzz9naqwdw2az3zdqi15gka76pmiwlgsy6diiijmg2k3"))))
(build-system perl-build-system)
(inputs `(("perl-test-tester" ,perl-test-tester)))
(synopsis "Ensure no warnings are produced while testing")
(description
"This modules causes any warnings during testing to be captured and
stored. It automatically adds an extra test that will run when your script
ends to check that there were no warnings. If there were any warings, the
test will fail and output diagnostics of where, when and what the warning was,
including a stack trace of what was going on when it occurred.")
(home-page (string-append "http://search.cpan.org/~adamk/"
"Test-NoWarnings-" version))
(license lgpl2.1)))
(define-public perl-test-script
(package
(name "perl-test-script")
@ -277,6 +346,46 @@ bin as is also commonly used) paths of your Perl distribution.")
"Test-Script-" version))
(license (package-license perl))))
(define-public perl-test-simple
(package
(name "perl-test-simple")
(version "1.001009")
(source (origin
(method url-fetch)
(uri (string-append "mirror://cpan/authors/id/E/EX/EXODIST/"
"Test-Simple-" version ".tar.gz"))
(sha256
(base32
"1klxpy658aj1pmrw63j1hc16gilwh5rzhp9rb2d1iydi3hcm8xb5"))))
(build-system perl-build-system)
(synopsis "Basic utilities for writing tests")
(description
"Test::Simple contains basic utilities for writing tests.")
(home-page (string-append "http://search.cpan.org/~exodist/"
"Test-Simple-" version))
(license (package-license perl))))
(define-public perl-test-tester
(package
(name "perl-test-tester")
(version "0.109")
(source (origin
(method url-fetch)
(uri (string-append "mirror://cpan/authors/id/F/FD/FDALY/"
"Test-Tester-" version ".tar.gz"))
(sha256
(base32
"0m9n28z09kq455r5nydj1bnr85lvmbfpcbjdkjfbpmfb5xgciiyk"))))
(build-system perl-build-system)
(synopsis "Simplify running Test::Builder tests")
(description
"Test::Tester allows testing of test modules based on Test::Builder with
a minimum of effort.")
(home-page (string-append "http://search.cpan.org/~fdaly/"
"Test-Tester-" version))
;; "Under the same license as Perl itself"
(license (package-license perl))))
(define-public perl-file-which
(package
(name "perl-file-which")

View File

@ -37,6 +37,7 @@
#:use-module (gnu packages openssl)
#:use-module (gnu packages elf)
#:use-module (gnu packages maths)
#:use-module (gnu packages ncurses)
#:use-module (gnu packages gcc)
#:use-module (gnu packages pkg-config)
#:use-module (gnu packages databases)
@ -50,6 +51,9 @@
#:use-module (gnu packages fontutils)
#:use-module (gnu packages which)
#:use-module (gnu packages perl)
#:use-module (gnu packages xorg)
#:use-module (gnu packages glib)
#:use-module (gnu packages gtk)
#:use-module (guix packages)
#:use-module (guix download)
#:use-module (guix git-download)
@ -614,6 +618,43 @@ get the local timezone information, unless you know the zoneinfo name, and
under several distributions that's hard or impossible to figure out.")
(license cc0)))
(define-public python-pysam
(package
(name "python-pysam")
(version "0.8.1")
(source
(origin
(method url-fetch)
(uri (string-append "https://pypi.python.org/packages/source/p/pysam/pysam-"
version ".tar.gz"))
(sha256
(base32
"1fb6i6hbpzxaxb62kyyp5alaidwhj40f7c6gwbhr6njzlqd5l459"))))
(build-system python-build-system)
(arguments
`(#:tests? #f ; tests are excluded in the manifest
#:phases
(alist-cons-before
'build 'set-flags
(lambda _
(setenv "LDFLAGS" "-lncurses")
(setenv "CFLAGS" "-D_CURSES_LIB=1"))
%standard-phases)))
(inputs
`(("python-cython" ,python-cython)
("python-setuptools" ,python-setuptools)
("ncurses" ,ncurses)
("zlib" ,zlib)))
(home-page "https://github.com/pysam-developers/pysam")
(synopsis "Python bindings to the SAMtools C API")
(description
"Pysam is a Python module for reading and manipulating files in the
SAM/BAM format. Pysam is a lightweight wrapper of the SAMtools C API. It
also includes an interface for tabix.")
(license expat)))
(define-public python2-pysam
(package-with-python2 python-pysam))
(define-public python2-pysqlite
(package
@ -2117,10 +2158,35 @@ that client code uses to construct the grammar directly in Python code.")
"0m6v9nwdldlwk22gcd339zg6mny5m301fxgks7z8sb8m9wawg8qp"))))
(build-system python-build-system)
(outputs '("out" "doc"))
(propagated-inputs ; the following packages are all needed at run time
`(("python-pyparsing" ,python-pyparsing)
("python-pygobject" ,python-pygobject)
("gobject-introspection" ,gobject-introspection)
;; The 'gtk+' package (and 'gdk-pixbuf', 'atk' and 'pango' propagated
;; from 'gtk+') provides the required 'typelib' files used by
;; 'gobject-introspection'. The location of these files is set with the
;; help of the environment variable GI_TYPELIB_PATH. At build time this
;; is done automatically by a 'native-search-path' procedure. However,
;; at run-time the user must set this variable as follows:
;;
;; export GI_TYPELIB_PATH=~/.guix-profile/lib/girepository-1.0
;;
;; 'typelib' files include references to dynamic libraries. Currently
;; the references do not include the full path to the libraries. For
;; this reason the user must set the LD_LIBRARY_PATH to the location of
;; 'libgtk-3.so.0', 'libgdk-3.so.0' and 'libatk-1.0.so.0':
;;
;; export LD_LIBRARY_PATH=~/.guix-profile/lib
("gtk+" ,gtk+)
;; From version 1.4.0 'matplotlib' makes use of 'cairocffi' instead of
;; 'pycairo'. However, 'pygobject' makes use of a 'pycairo' 'context'
;; object. For this reason we need to import both libraries.
;; https://pythonhosted.org/cairocffi/cffi_api.html#converting-pycairo
("python-pycairo" ,python-pycairo)
("python-cairocffi" ,python-cairocffi)))
(inputs
`(("python-setuptools" ,python-setuptools)
("python-dateutil" ,python-dateutil-2)
("python-pyparsing" ,python-pyparsing)
("python-six" ,python-six)
("python-pytz" ,python-pytz)
("python-numpy" ,python-numpy-bootstrap)
@ -2131,10 +2197,10 @@ that client code uses to construct the grammar directly in Python code.")
("libpng" ,libpng)
("imagemagick" ,imagemagick)
("freetype" ,freetype)
("cairo" ,cairo)
("glib" ,glib)
("python-pillow" ,python-pillow)
;; FIXME: Add backends when available.
;("python-pygtk" ,python-pygtk)
;("python-pycairo" ,python-pycairo)
;("python-pygobject" ,python-pygobject)
;("python-wxpython" ,python-wxpython)
;("python-pyqt" ,python-pyqt)
))
@ -2144,6 +2210,24 @@ that client code uses to construct the grammar directly in Python code.")
("texinfo" ,texinfo)))
(arguments
`(#:phases
(alist-cons-before
'build 'configure-environment
(lambda* (#:key outputs inputs #:allow-other-keys)
(let ((cairo (assoc-ref inputs "cairo"))
(gtk+ (assoc-ref inputs "gtk+")))
;; Setting these directories in the 'basedirlist' of 'setup.cfg'
;; has not effect.
;;
;; FIXME: setting LD_LIBRARY_PATH should be removed once we patch
;; gobject-introspection to include the full path of shared
;; libraries in 'typelib' files.
(setenv "LD_LIBRARY_PATH"
(string-append cairo "/lib:" gtk+ "/lib"))
(setenv "HOME" (getcwd))
(call-with-output-file "setup.cfg"
(lambda (port)
(format port "[rc_options]~%
backend = GTK3Agg~%")))))
(alist-cons-after
'install 'install-doc
(lambda* (#:key outputs #:allow-other-keys)
@ -2169,15 +2253,8 @@ that client code uses to construct the grammar directly in Python code.")
(string-append info "/matplotlib.info"))
(copy-file "build/latex/Matplotlib.pdf"
(string-append doc "/Matplotlib.pdf"))
(with-directory-excursion "build/html"
(map (lambda (file)
(let* ((dir (dirname file))
(tgt-dir (string-append html "/" dir)))
(unless (equal? "." dir)
(mkdir-p tgt-dir))
(copy-file file (string-append html "/" file))))
(find-files "." ".*"))))))
%standard-phases)))
(copy-recursively "build/html" html))))
%standard-phases))))
(home-page "http://matplotlib.org")
(synopsis "2D plotting library for Python")
(description
@ -2193,7 +2270,15 @@ toolkits.")
(package (inherit matplotlib)
;; Make sure we use exactly PYTHON2-NUMPYDOC, which is
;; customized for Python 2.
(inputs `(("python2-numpydoc" ,python2-numpydoc)
(propagated-inputs
`(("python2-py2cairo" ,python2-py2cairo)
("python2-pygobject-2" ,python2-pygobject-2)
,@(alist-delete "python-pycairo"
(alist-delete "python-pygobject"
(package-propagated-inputs
matplotlib)))))
(inputs
`(("python2-numpydoc" ,python2-numpydoc)
,@(alist-delete "python-numpydoc"
(package-inputs matplotlib)))))))
@ -2542,3 +2627,102 @@ a front-end for C compilers or analysis tools.")
(define-public python2-cffi
(package-with-python2 python-cffi))
(define-public python-xcffib
(package
(name "python-xcffib")
(version "0.1.9")
(source
(origin
(method url-fetch)
(uri (string-append "https://pypi.python.org/packages/source/x/"
"xcffib/xcffib-" version ".tar.gz"))
(sha256
(base32
"0655hzxv57h1a9ja9kwp0ichbkhf3djw32k33d66xp0q37dq2y81"))))
(build-system python-build-system)
(inputs
`(("libxcb" ,libxcb)
("python-six" ,python-six)))
(native-inputs
`(("python-setuptools" ,python-setuptools)))
(propagated-inputs
`(("python-cffi" ,python-cffi))) ; used at run time
(arguments
`(#:phases
(alist-cons-after
'install 'install-doc
(lambda* (#:key outputs #:allow-other-keys)
(let ((doc (string-append (assoc-ref outputs "out") "/share"
"/doc/" ,name "-" ,version)))
(mkdir-p doc)
(copy-file "README.md"
(string-append doc "/README.md"))))
%standard-phases)))
(home-page "https://github.com/tych0/xcffib")
(synopsis "XCB Python bindings")
(description
"Xcffib is a replacement for xpyb, an XCB Python bindings. It adds
support for Python 3 and PyPy. It is based on cffi.")
(license expat)))
(define-public python2-xcffib
(package-with-python2 python-xcffib))
(define-public python-cairocffi
(package
(name "python-cairocffi")
(version "0.6")
(source
(origin
(method url-fetch)
;; The archive on pypi is missing the 'utils' directory!
(uri (string-append "https://github.com/SimonSapin/cairocffi/archive/v"
version ".tar.gz"))
(sha256
(base32
"03w5p62sp3nqiccx864sbq0jvh7946277jqx3rcc3dch5xwfvv51"))))
(build-system python-build-system)
(outputs '("out" "doc"))
(inputs
`(("gdk-pixbuf" ,gdk-pixbuf)
("cairo" ,cairo)))
(native-inputs
`(("pkg-config" ,pkg-config)
("python-sphinx" ,python-sphinx)
("python-docutils" ,python-docutils)
("python-setuptools" ,python-setuptools)))
(propagated-inputs
`(("python-xcffib" ,python-xcffib))) ; used at run time
(arguments
`(#:phases
(alist-cons-after
'install 'install-doc
(lambda* (#:key inputs outputs #:allow-other-keys)
(let* ((data (string-append (assoc-ref outputs "doc") "/share"))
(doc (string-append data "/doc/" ,name "-" ,version))
(html (string-append doc "/html")))
(setenv "LD_LIBRARY_PATH"
(string-append (assoc-ref inputs "cairo") "/lib" ":"
(assoc-ref inputs "gdk-pixbuf") "/lib"))
(setenv "LANG" "en_US.UTF-8")
(mkdir-p html)
(for-each (lambda (file)
(copy-file (string-append "." file)
(string-append doc file)))
'("/README.rst" "/CHANGES" "/LICENSE"))
(system* "python" "setup.py" "build_sphinx")
(copy-recursively "docs/_build/html" html)))
%standard-phases)))
(home-page "https://github.com/SimonSapin/cairocffi")
(synopsis "Python bindings and object-oriented API for Cairo")
(description
"Cairocffi is a CFFI-based drop-in replacement for Pycairo, a set of
Python bindings and object-oriented API for cairo. Cairo is a 2D vector
graphics library with support for multiple backends including image buffers,
PNG, PostScript, PDF, and SVG file output.")
(license bsd-3)))
(define-public python2-cairocffi
(package-with-python2 python-cairocffi))

View File

@ -42,14 +42,14 @@
;; This is QEMU without GUI support.
(package
(name "qemu-headless")
(version "2.0.0")
(version "2.2.0")
(source (origin
(method url-fetch)
(uri (string-append "http://wiki.qemu-project.org/download/qemu-"
version ".tar.bz2"))
(sha256
(base32
"0frsahiw56jr4cqr9m6s383lyj4ar9hfs2wp3y4yr76krah1mk30"))))
"1703c3scl5n07gmpilg7g2xzyxnr7jczxgx6nn4m8kv9gin9p35n"))))
(build-system gnu-build-system)
(arguments
'(#:phases (alist-replace

View File

@ -28,7 +28,7 @@
(define-public tmux
(package
(name "tmux")
(version "1.7")
(version "1.9a")
(source (origin
(method url-fetch)
(uri (string-append
@ -36,7 +36,7 @@
version "/tmux-" version ".tar.gz"))
(sha256
(base32
"0ywy1x2g905hmhkdz418ik42lcvnhnwr8fv63rcqczfg27d6nd38"))))
"1x9k4wfd4l5jg6fh7xkr3yyilizha6ka8m5b1nr0kw8wj0mv5qy5"))))
(build-system gnu-build-system)
(inputs
`(("libevent" ,libevent)

View File

@ -395,7 +395,7 @@ SVCD, DVD, 3ivx, DivX 3/4/5, WMV and H.264 movies.")
(define-public youtube-dl
(package
(name "youtube-dl")
(version "2014.11.21.1")
(version "2014.12.15")
(source (origin
(method url-fetch)
(uri (string-append "http://youtube-dl.org/downloads/"
@ -403,7 +403,7 @@ SVCD, DVD, 3ivx, DivX 3/4/5, WMV and H.264 movies.")
version ".tar.gz"))
(sha256
(base32
"0rxpx8j4qhhsws6czlfji1x9igsinkbbwvld10qdylll7g9q1v7j"))))
"09z7v6jxs4a36kyy681mcypcqsxipplnbdy9s3rva1rpp5f74h2z"))))
(build-system python-build-system)
(inputs `(("setuptools" ,python-setuptools)))
(home-page "http://youtube-dl.org")

View File

@ -29,6 +29,7 @@
#:use-module (gnu packages image)
#:use-module (gnu packages pkg-config)
#:use-module (gnu packages glib)
#:use-module (gnu packages perl)
#:use-module (gnu packages xorg))
;; packages outside the x.org system proper
@ -57,6 +58,47 @@ can also be used for copying files, as an alternative to sftp/scp, thus
avoiding password prompts when X11 forwarding has already been setup.")
(license license:gpl2+)))
(define-public xdotool
(package
(name "xdotool")
(version "2.20110530.1")
(source
(origin
(method url-fetch)
(uri (string-append
"http://semicomplete.googlecode.com/files/" name "-"
version ".tar.gz"))
(sha256
(base32
"0rxggg1cy7nnkwidx8x2w3c5f3pk6dh2b6q0q7hp069r3n5jrd77"))))
(build-system gnu-build-system)
(arguments
'(#:tests? #f ; Test suite requires a lot of black magic
#:phases
(alist-replace 'configure
(lambda* (#:key outputs #:allow-other-keys #:rest args)
(setenv "PREFIX" (assoc-ref outputs "out"))
(setenv "LDFLAGS" (string-append "-Wl,-rpath="
(assoc-ref
%outputs "out") "/lib"))
(setenv "CC" "gcc"))
%standard-phases)))
(native-inputs `(("perl" ,perl))) ; for pod2man
(inputs `(("libx11" ,libx11)
("libxext" ,libxext)
("libxi" ,libxi)
("libxinerama" ,libxinerama)
("libxtst" ,libxtst)))
(home-page "http://www.semicomplete.com/projects/xdotool")
(synopsis "Fake keyboard/mouse input, window management, and more")
(description "Xdotool lets you simulate keyboard input and mouse activity,
move and resize windows, etc. It does this using X11's XTEST extension and
other Xlib functions. Additionally, you can search for windows and move,
resize, hide, and modify window properties like the title. If your window
manager supports it, you can use xdotool to switch desktops, move windows
between desktops, and change the number of desktops.")
(license license:bsd-3)))
(define-public xeyes
(package
(name "xeyes")

View File

@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2014 Sou Bunnbu <iyzsong@gmail.com>
;;; Copyright © 2014 Mark H Weaver <mhw@netris.org>
;;;
;;; This file is part of GNU Guix.
;;;
@ -22,6 +23,7 @@
#:use-module (guix download)
#:use-module (guix utils)
#:use-module (guix build-system gnu)
#:use-module (gnu packages)
#:use-module (gnu packages pkg-config)
#:use-module (gnu packages glib)
#:use-module (gnu packages gtk)
@ -249,7 +251,8 @@ management D-Bus specification.")
"/src/" name "-" version ".tar.bz2"))
(sha256
(base32
"1f8903nx6ivzircl8d8s9zna4vjgfy0qhjk5d2x19g9bmycgj89k"))))
"1f8903nx6ivzircl8d8s9zna4vjgfy0qhjk5d2x19g9bmycgj89k"))
(patches (list (search-patch "xfce4-panel-plugins.patch")))))
(build-system gnu-build-system)
(native-inputs
`(("pkg-config" ,pkg-config)
@ -261,6 +264,10 @@ management D-Bus specification.")
("garcon", garcon)
("libwnck" ,libwnck-1)
("libxfce4ui" ,libxfce4ui)))
(native-search-paths
(list (search-path-specification
(variable "X_XFCE4_LIB_DIRS")
(directories '("lib/xfce4")))))
(home-page "http://www.xfce.org/")
(synopsis "Xfce desktop panel")
(description
@ -269,6 +276,35 @@ applications menu, workspace switcher and more.")
;; Libraries are under LGPLv2.1+, and programs under GPLv2+.
(license (list gpl2+ lgpl2.1+))))
(define-public xfce4-battery-plugin
(package
(name "xfce4-battery-plugin")
(version "1.0.5")
(source (origin
(method url-fetch)
(uri (string-append "http://archive.xfce.org/src/panel-plugins/"
name "/" (version-major+minor version) "/"
name "-" version ".tar.bz2"))
(sha256
(base32
"04gbplcj8z4vg5xbks8cc2jjf62mmf9sdymg90scjwmb82pv2ngn"))))
(build-system gnu-build-system)
(native-inputs `(("pkg-config" ,pkg-config)
("intltool" ,intltool)))
(inputs `(("glib" ,glib)
("gtk+" ,gtk+-2)
("libxfce4util" ,libxfce4util)
("libxfce4ui" ,libxfce4ui)
("xfce4-panel" ,xfce4-panel)))
(home-page
"http://goodies.xfce.org/projects/panel-plugins/xfce4-battery-plugin")
(synopsis "Battery monitor panel plugin for Xfce4")
(description
"A battery monitor panel plugin for Xfce4, compatible with APM and ACPI.")
;; The main plugin code is covered by gpl2+, but the files containing code
;; to read the battery state via ACPI or APM are covered by lgpl2.0+.
(license (list gpl2+ lgpl2.0+))))
(define-public xfce4-appfinder
(package
(name "xfce4-appfinder")
@ -476,3 +512,33 @@ on the screen.")
optional application menu or icons for minimized applications or launchers,
devices and folders.")
(license gpl2+)))
(define-public xfce4-terminal
(package
(name "xfce4-terminal")
(version "0.6.3")
(source (origin
(method url-fetch)
(uri (string-append "http://archive.xfce.org/src/apps/" name "/"
(version-major+minor version) "/"
name "-" version ".tar.bz2"))
(sha256
(base32
"023y0lkfijifh05yz8grimxadqpi98mrivr00sl18nirq8b4fbwi"))))
(build-system gnu-build-system)
(native-inputs
`(("pkg-config" ,pkg-config)
("intltool" ,intltool)))
(inputs
`(("libxfce4ui" ,libxfce4ui)
("vte" ,vte/gtk+-2)))
(home-page "http://www.xfce.org/")
(synopsis "Xfce terminal emulator")
(description
"A lightweight and easy to use terminal emulator for Xfce. Features
include a simple configuration interface, the ability to use multiple tabs
with terminals within a single window, the possibility to have a
pseudo-transparent terminal background, and a compact mode (where both the
menubar and the window decorations are hidden) that helps you to save space
on your desktop.")
(license gpl2+)))

View File

@ -33,8 +33,10 @@
#:select (mount-flags->bit-mask))
#:use-module (guix gexp)
#:use-module (guix monads)
#:use-module (guix records)
#:use-module (srfi srfi-1)
#:use-module (srfi srfi-26)
#:use-module (ice-9 match)
#:use-module (ice-9 format)
#:export (root-file-system-service
file-system-service
@ -46,6 +48,16 @@
console-font-service
udev-service
mingetty-service
%nscd-default-caches
%nscd-default-configuration
nscd-configuration
nscd-configuration?
nscd-cache
nscd-cache?
nscd-service
syslog-service
guix-service
@ -374,9 +386,110 @@ the ``message of the day''."
#:allow-empty-passwords? allow-empty-passwords?
#:motd motd)))))))
(define* (nscd-service #:key (glibc (canonical-package glibc)))
"Return a service that runs libc's name service cache daemon (nscd)."
(with-monad %store-monad
(define-record-type* <nscd-configuration> nscd-configuration
make-nscd-configuration
nscd-configuration?
(log-file nscd-configuration-log-file ;string
(default "/var/log/nscd.log"))
(debug-level nscd-debug-level ;integer
(default 0))
;; TODO: See nscd.conf in glibc for other options to add.
(caches nscd-configuration-caches ;list of <nscd-cache>
(default %nscd-default-caches)))
(define-record-type* <nscd-cache> nscd-cache make-nscd-cache
nscd-cache?
(database nscd-cache-database) ;symbol
(positive-time-to-live nscd-cache-positive-time-to-live) ;integer
(negative-time-to-live nscd-cache-negative-time-to-live
(default 20)) ;integer
(suggested-size nscd-cache-suggested-size ;integer ("default module
;of hash table")
(default 211))
(check-files? nscd-cache-check-files? ;Boolean
(default #t))
(persistent? nscd-cache-persistent? ;Boolean
(default #t))
(shared? nscd-cache-shared? ;Boolean
(default #t))
(max-database-size nscd-cache-max-database-size ;integer
(default (* 32 (expt 2 20))))
(auto-propagate? nscd-cache-auto-propagate? ;Boolean
(default #t)))
(define %nscd-default-caches
;; Caches that we want to enable by default. Note that when providing an
;; empty nscd.conf, all caches are disabled.
(list (nscd-cache (database 'hosts)
;; Aggressively cache the host name cache to improve
;; privacy and resilience.
(positive-time-to-live (* 3600 12))
(negative-time-to-live 20)
(persistent? #t))
(nscd-cache (database 'services)
;; Services are unlikely to change, so we can be even more
;; aggressive.
(positive-time-to-live (* 3600 24))
(negative-time-to-live 3600)
(check-files? #t) ;check /etc/services changes
(persistent? #t))))
(define %nscd-default-configuration
;; Default nscd configuration.
(nscd-configuration))
(define (nscd.conf-file config)
"Return the @file{nscd.conf} configuration file for @var{config}, an
@code{<nscd-configuration>} object."
(define cache->config
(match-lambda
(($ <nscd-cache> (= symbol->string database)
positive-ttl negative-ttl size check-files?
persistent? shared? max-size propagate?)
(string-append "\nenable-cache\t" database "\tyes\n"
"positive-time-to-live\t" database "\t"
(number->string positive-ttl) "\n"
"negative-time-to-live\t" database "\t"
(number->string negative-ttl) "\n"
"suggested-size\t" database "\t"
(number->string size) "\n"
"check-files\t" database "\t"
(if check-files? "yes\n" "no\n")
"persistent\t" database "\t"
(if persistent? "yes\n" "no\n")
"shared\t" database "\t"
(if shared? "yes\n" "no\n")
"max-db-size\t" database "\t"
(number->string max-size) "\n"
"auto-propagate\t" database "\t"
(if propagate? "yes\n" "no\n")))))
(match config
(($ <nscd-configuration> log-file debug-level caches)
(text-file "nscd.conf"
(string-append "\
# Configuration of libc's name service cache daemon (nscd).\n\n"
(if log-file
(string-append "logfile\t" log-file)
"")
"\n"
(if debug-level
(string-append "debug-level\t"
(number->string debug-level))
"")
"\n"
(string-concatenate
(map cache->config caches)))))))
(define* (nscd-service #:optional (config %nscd-default-configuration)
#:key (glibc (canonical-package glibc)))
"Return a service that runs libc's name service cache daemon (nscd) with the
given @var{config}---an @code{<nscd-configuration>} object."
(mlet %store-monad ((nscd.conf (nscd.conf-file config)))
(return (service
(documentation "Run libc's name service cache daemon (nscd).")
(provision '(nscd))
@ -388,7 +501,7 @@ the ``message of the day''."
(start #~(make-forkexec-constructor
(list (string-append #$glibc "/sbin/nscd")
"-f" "/dev/null" "--foreground")))
"-f" #$nscd.conf "--foreground")))
(stop #~(make-kill-destructor))
(respawn? #f)))))

View File

@ -80,34 +80,35 @@ fe80::1%lo0 apps.facebook.com\n")
gateway
(provision '(networking))
(name-servers '())
(inetutils inetutils)
(net-tools net-tools))
"Return a service that starts @var{interface} with address @var{ip}. If
@var{gateway} is true, it must be a string specifying the default network
gateway."
(define loopback?
(memq 'loopback provision))
;; TODO: Eventually we should do this using Guile's networking procedures,
;; like 'configure-qemu-networking' does, but the patch that does this is
;; not yet in stock Guile.
;; TODO: Eventually replace 'route' with bindings for the appropriate
;; ioctls.
(with-monad %store-monad
(return
(service
;; Unless we're providing the loopback interface, wait for udev to be up
;; and running so that INTERFACE is actually usable.
(requirement (if (memq 'loopback provision)
'()
'(udev)))
(requirement (if loopback? '() '(udev)))
(documentation
"Bring up the networking interface using a static IP address.")
(provision provision)
(start #~(lambda _
;; Return #t if successfully started.
(and (zero? (system* (string-append #$inetutils
"/bin/ifconfig")
"-i" #$interface "-A" #$ip
"-i" #$interface "--up"))
(let* ((addr (inet-pton AF_INET #$ip))
(sockaddr (make-socket-address AF_INET addr 0)))
(configure-network-interface #$interface sockaddr
(logior IFF_UP
#$(if loopback?
#~IFF_LOOPBACK
0))))
#$(if gateway
#~(zero? (system* (string-append #$net-tools
"/sbin/route")
@ -124,16 +125,17 @@ gateway."
(format port "nameserver ~a~%"
server))
'#$name-servers)))
#t))))
#t)))
(stop #~(lambda _
;; Return #f is successfully stopped.
(not (and (system* (string-append #$inetutils "/bin/ifconfig")
#$interface "down")
#$(if gateway
(let ((sock (socket AF_INET SOCK_STREAM 0)))
(set-network-interface-flags sock #$interface 0)
(close-port sock))
(not #$(if gateway
#~(system* (string-append #$net-tools
"/sbin/route")
"del" "-net" "default")
#t)))))
#t))))
(respawn? #f)))))
(define* (dhcp-client-service #:key (dhcp isc-dhcp))

View File

@ -36,7 +36,7 @@
#:use-module (srfi srfi-26)
#:use-module (ice-9 match)
#:export (xorg-start-command
%default-xsessions
%default-slim-theme
%default-slim-theme-name
slim-service))
@ -136,9 +136,10 @@ EndSection
(define* (xinitrc #:key
(guile (canonical-package guile-2.0))
(ratpoison ratpoison)
(windowmaker windowmaker))
"Return a system-wide xinitrc script that starts the specified X session."
fallback-session)
"Return a system-wide xinitrc script that starts the specified X session,
which should be passed to this script as the first argument. If not, the
@var{fallback-session} will be used."
(define builder
#~(begin
(use-modules (ice-9 match))
@ -155,20 +156,14 @@ EndSection
(execl shell shell "--login" "-c"
(string-join (cons command args))))))
(let ((home (getenv "HOME"))
(session (match (command-line)
((_ x) x)
(_ #$fallback-session))))
;; First, try to run ~/.xsession.
(let* ((home (getenv "HOME"))
(xsession (string-append home "/.xsession")))
(exec-from-login-shell xsession))
;; Then try a pre-configured session type.
(let ((ratpoison (string-append #$ratpoison "/bin/ratpoison"))
(wmaker (string-append #$windowmaker "/bin/wmaker")))
(match (command-line)
((_ "ratpoison")
(exec-from-login-shell ratpoison))
(_
(exec-from-login-shell wmaker))))))
(exec-from-login-shell (string-append home "/.xsession"))
;; Then try to start the specified session.
(exec-from-login-shell session))))
(gexp->script "xinitrc" builder))
@ -176,6 +171,35 @@ EndSection
;;; SLiM log-in manager.
;;;
(define %default-xsessions
;; Default xsessions available for log-in manager, representing as a list of
;; monadic desktop entries.
(list (text-file* "wmaker.desktop" "
[Desktop Entry]
Name=Window Maker
Exec=" windowmaker "/bin/wmaker
Type=Application
")
(text-file* "ratpoison.desktop" "
[Desktop Entry]
Name=Ratpoison
Exec=" ratpoison "/bin/ratpoison
Type=Application
")))
(define (xsessions-directory sessions)
"Return a directory containing SESSIONS, which should be a list of monadic
desktop entries."
(mlet %store-monad ((sessions (sequence %store-monad sessions)))
(define builder
#~(begin
(mkdir #$output)
(for-each (lambda (session)
(symlink session (string-append #$output "/"
(basename session))))
'#$sessions)))
(gexp->derivation "xsessions-dir" builder)))
(define %default-slim-theme
;; Theme based on work by Felipe López.
#~(string-append #$%artwork-repository "/slim"))
@ -191,6 +215,9 @@ EndSection
(theme %default-slim-theme)
(theme-name %default-slim-theme-name)
(xauth xauth) (dmd dmd) (bash bash)
(sessions %default-xsessions)
(auto-login-session #~(string-append #$windowmaker
"/bin/wmaker"))
startx)
"Return a service that spawns the SLiM graphical login manager, which in
turn starts the X display server with @var{startx}, a command as returned by
@ -198,7 +225,7 @@ turn starts the X display server with @var{startx}, a command as returned by
When @var{allow-empty-passwords?} is true, allow logins with an empty
password. When @var{auto-login?} is true, log in automatically as
@var{default-user}.
@var{default-user} with @var{auto-login-session}.
If @var{theme} is @code{#f}, the use the default log-in theme; otherwise
@var{theme} must be a gexp denoting the name of a directory containing the
@ -207,7 +234,9 @@ theme."
(define (slim.cfg)
(mlet %store-monad ((startx (or startx (xorg-start-command)))
(xinitrc (xinitrc)))
(xinitrc (xinitrc #:fallback-session
auto-login-session))
(sessiondir (xsessions-directory sessions)))
(text-file* "slim.cfg" "
default_path /run/current-system/profile/bin
default_xserver " startx "
@ -218,7 +247,7 @@ authfile /var/run/slim.auth
# The login command. '%session' is replaced by the chosen session name, one
# of the names specified in the 'sessions' setting: 'wmaker', 'xfce', etc.
login_cmd exec " xinitrc " %session
sessions wmaker,ratpoison
sessiondir " sessiondir "
halt_cmd " dmd "/sbin/halt
reboot_cmd " dmd "/sbin/reboot

View File

@ -145,6 +145,14 @@ configuration template file in the installation system."
#~(unless (file-exists? #$local-template)
(copy-file #$template #$local-template)))))))
(define %nscd-minimal-caches
;; Minimal in-memory caching policy for nscd.
(list (nscd-cache (database 'hosts)
(positive-time-to-live (* 3600 12))
(negative-time-to-live 20)
(persistent? #f)
(max-database-size (* 5 (expt 2 20)))))) ;5 MiB
(define (installation-services)
"Return the list services for the installation image."
(let ((motd (text-file "motd" "
@ -206,7 +214,10 @@ You have been warned. Thanks for being so brave.
(console-font-service "tty5")
(console-font-service "tty6")
(nscd-service))))
;; Since this is running on a USB stick with a unionfs as the root
;; file system, use an appropriate cache configuration.
(nscd-service (nscd-configuration
(caches %nscd-minimal-caches))))))
(define %issue
;; Greeting.

View File

@ -55,8 +55,7 @@ PYTHON-BUILD-SYSTEM, such that it is compiled with PYTHON instead. The
inputs are changed recursively accordingly. If the name of P starts with
OLD-PREFIX, this is replaced by NEW-PREFIX; otherwise, NEW-PREFIX is
prepended to the name."
(let* ((build-system (package-build-system p))
(rewrite-if-package
(let* ((rewrite-if-package
(lambda (content)
;; CONTENT may be a file name, in which case it is returned, or a
;; package, which is rewritten with the new PYTHON and NEW-PREFIX.
@ -68,28 +67,23 @@ prepended to the name."
(match-lambda
((name content . rest)
(append (list name (rewrite-if-package content)) rest)))))
(if (eq? (package-build-system p) python-build-system)
(package (inherit p)
(name
(let ((name (package-name p)))
(if (eq? build-system python-build-system)
(name (let ((name (package-name p)))
(string-append new-prefix
(if (string-prefix? old-prefix name)
(substring name (string-length old-prefix))
name))
name)))
name))))
(arguments
(let ((arguments (package-arguments p)))
(if (eq? build-system python-build-system)
(if (member #:python arguments)
(substitute-keyword-arguments arguments ((#:python p) python))
(append arguments `(#:python ,python)))
arguments)))
(inputs
(map rewrite (package-inputs p)))
(propagated-inputs
(map rewrite (package-propagated-inputs p)))
(native-inputs
(map rewrite (package-native-inputs p))))))
(append arguments `(#:python ,python)))))
(inputs (map rewrite (package-inputs p)))
(propagated-inputs (map rewrite (package-propagated-inputs p)))
(native-inputs (map rewrite (package-native-inputs p))))
p)))
(define package-with-python2
(cut package-with-explicit-python <> (default-python2) "python-" "python2-"))

View File

@ -105,9 +105,26 @@
files)))
bindirs)))
(define* (rename-pth-file #:key name inputs outputs #:allow-other-keys)
"Rename easy-install.pth to NAME.pth to avoid conflicts between packages
installed with setuptools."
(let* ((out (assoc-ref outputs "out"))
(python (assoc-ref inputs "python"))
(site-packages (string-append out "/lib/python"
(get-python-version python)
"/site-packages"))
(easy-install-pth (string-append site-packages "/easy-install.pth"))
(new-pth (string-append site-packages "/" name ".pth")))
(when (file-exists? easy-install-pth)
(rename-file easy-install-pth new-pth))
#t))
(define %standard-phases
;; 'configure' and 'build' phases are not needed. Everything is done during
;; 'install'.
(alist-cons-before
'strip 'rename-pth-file
rename-pth-file
(alist-cons-after
'install 'wrap
wrap
@ -117,7 +134,7 @@
'check check
(alist-replace 'install install
(alist-delete 'configure
gnu:%standard-phases))))))
gnu:%standard-phases)))))))
(define* (python-build #:key inputs (phases %standard-phases)
#:allow-other-keys #:rest args)

View File

@ -42,7 +42,11 @@
all-network-interfaces
network-interfaces
network-interface-flags
loopback-network-interface?))
loopback-network-interface?
network-interface-address
set-network-interface-flags
set-network-interface-address
configure-network-interface))
;;; Commentary:
;;;
@ -228,6 +232,77 @@ user-land process."
(scandir "/proc"))
<))
;;;
;;; Packed structures.
;;;
(define-syntax sizeof*
;; XXX: This duplicates 'compile-time-value'.
(syntax-rules (int128)
((_ int128)
16)
((_ type)
(let-syntax ((v (lambda (s)
(let ((val (sizeof type)))
(syntax-case s ()
(_ val))))))
v))))
(define-syntax type-size
(syntax-rules (~)
((_ (type ~ order))
(sizeof* type))
((_ type)
(sizeof* type))))
(define-syntax write-type
(syntax-rules (~)
((_ bv offset (type ~ order) value)
(bytevector-uint-set! bv offset value
(endianness order) (sizeof* type)))
((_ bv offset type value)
(bytevector-uint-set! bv offset value
(native-endianness) (sizeof* type)))))
(define-syntax write-types
(syntax-rules ()
((_ bv offset () ())
#t)
((_ bv offset (type0 types ...) (field0 fields ...))
(begin
(write-type bv offset type0 field0)
(write-types bv (+ offset (type-size type0))
(types ...) (fields ...))))))
(define-syntax read-type
(syntax-rules (~)
((_ bv offset (type ~ order))
(bytevector-uint-ref bv offset
(endianness order) (sizeof* type)))
((_ bv offset type)
(bytevector-uint-ref bv offset
(native-endianness) (sizeof* type)))))
(define-syntax read-types
(syntax-rules ()
((_ bv offset ())
'())
((_ bv offset (type0 types ...))
(cons (read-type bv offset type0)
(read-types bv (+ offset (type-size type0)) (types ...))))))
(define-syntax define-c-struct
(syntax-rules ()
"Define READ as an optimized serializer and WRITE! as a deserializer for
the C structure with the given TYPES."
((_ name read write! (fields types) ...)
(begin
(define (write! bv offset fields ...)
(write-types bv offset (types ...) (fields ...)))
(define (read bv offset)
(read-types bv offset (types ...)))))))
;;;
;;; Network interfaces.
@ -241,6 +316,18 @@ user-land process."
(if (string-contains %host-type "linux")
#x8913 ;GNU/Linux
#xc4804191)) ;GNU/Hurd
(define SIOCSIFFLAGS
(if (string-contains %host-type "linux")
#x8914 ;GNU/Linux
-1)) ;FIXME: GNU/Hurd?
(define SIOCGIFADDR
(if (string-contains %host-type "linux")
#x8915 ;GNU/Linux
-1)) ;FIXME: GNU/Hurd?
(define SIOCSIFADDR
(if (string-contains %host-type "linux")
#x8916 ;GNU/Linux
-1)) ;FIXME: GNU/Hurd?
;; Flags and constants from <net/if.h>.
@ -263,6 +350,56 @@ user-land process."
40
32))
(define-c-struct sockaddr-in ;<linux/in.h>
read-sockaddr-in
write-sockaddr-in!
(family unsigned-short)
(port (int16 ~ big))
(address (int32 ~ big)))
(define-c-struct sockaddr-in6 ;<linux/in6.h>
read-sockaddr-in6
write-sockaddr-in6!
(family unsigned-short)
(port (int16 ~ big))
(flowinfo (int32 ~ big))
(address (int128 ~ big))
(scopeid int32))
(define (write-socket-address! sockaddr bv index)
"Write SOCKADDR, a socket address as returned by 'make-socket-address', to
bytevector BV at INDEX."
(let ((family (sockaddr:fam sockaddr)))
(cond ((= family AF_INET)
(write-sockaddr-in! bv index
family
(sockaddr:port sockaddr)
(sockaddr:addr sockaddr)))
((= family AF_INET6)
(write-sockaddr-in6! bv index
family
(sockaddr:port sockaddr)
(sockaddr:flowinfo sockaddr)
(sockaddr:addr sockaddr)
(sockaddr:scopeid sockaddr)))
(else
(error "unsupported socket address" sockaddr)))))
(define (read-socket-address bv index)
"Read a socket address from bytevector BV at INDEX."
(let ((family (bytevector-u16-native-ref bv index)))
(cond ((= family AF_INET)
(match (read-sockaddr-in bv index)
((family port address)
(make-socket-address family address port))))
((= family AF_INET6)
(match (read-sockaddr-in6 bv index)
((family port flowinfo address scopeid)
(make-socket-address family address port
flowinfo scopeid))))
(else
"unsupported socket address family" family))))
(define %ioctl
;; The most terrible interface, live from Scheme.
(pointer->procedure int
@ -354,4 +491,65 @@ interface NAME."
(close-port sock)
(not (zero? (logand flags IFF_LOOPBACK)))))
(define (set-network-interface-flags socket name flags)
"Set the flag of network interface NAME to FLAGS."
(let ((req (make-bytevector ifreq-struct-size)))
(bytevector-copy! (string->utf8 name) 0 req 0
(min (string-length name) (- IF_NAMESIZE 1)))
;; Set the 'ifr_flags' field.
(bytevector-uint-set! req IF_NAMESIZE flags (native-endianness)
(sizeof short))
(let* ((ret (%ioctl (fileno socket) SIOCSIFFLAGS
(bytevector->pointer req)))
(err (errno)))
(unless (zero? ret)
(throw 'system-error "set-network-interface-flags"
"set-network-interface-flags on ~A: ~A"
(list name (strerror err))
(list err))))))
(define (set-network-interface-address socket name sockaddr)
"Set the address of network interface NAME to SOCKADDR."
(let ((req (make-bytevector ifreq-struct-size)))
(bytevector-copy! (string->utf8 name) 0 req 0
(min (string-length name) (- IF_NAMESIZE 1)))
;; Set the 'ifr_addr' field.
(write-socket-address! sockaddr req IF_NAMESIZE)
(let* ((ret (%ioctl (fileno socket) SIOCSIFADDR
(bytevector->pointer req)))
(err (errno)))
(unless (zero? ret)
(throw 'system-error "set-network-interface-address"
"set-network-interface-address on ~A: ~A"
(list name (strerror err))
(list err))))))
(define (network-interface-address socket name)
"Return the address of network interface NAME. The result is an object of
the same type as that returned by 'make-socket-address'."
(let ((req (make-bytevector ifreq-struct-size)))
(bytevector-copy! (string->utf8 name) 0 req 0
(min (string-length name) (- IF_NAMESIZE 1)))
(let* ((ret (%ioctl (fileno socket) SIOCGIFADDR
(bytevector->pointer req)))
(err (errno)))
(if (zero? ret)
(read-socket-address req IF_NAMESIZE)
(throw 'system-error "network-interface-address"
"network-interface-address on ~A: ~A"
(list name (strerror err))
(list err))))))
(define (configure-network-interface name sockaddr flags)
"Configure network interface NAME to use SOCKADDR, an address as returned by
'make-socket-address', and FLAGS, a bitwise-or of IFF_* constants."
(let ((sock (socket (sockaddr:fam sockaddr) SOCK_STREAM 0)))
(dynamic-wind
(const #t)
(lambda ()
(set-network-interface-address sock name sockaddr)
(set-network-interface-flags sock name flags))
(lambda ()
(close-port sock)))))
;;; syscalls.scm ends here

View File

@ -1,5 +1,6 @@
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2012, 2013, 2014 Ludovic Courtès <ludo@gnu.org>
;;; Copyright © 2014 Mark H Weaver <mhw@netris.org>
;;;
;;; This file is part of GNU Guix.
;;;
@ -546,40 +547,38 @@ for the host system (\"native inputs\"), and not target inputs."
recursively."
(transitive-inputs (package-propagated-inputs package)))
(define-syntax-rule (first-value exp)
"Truncate all but the first value returned by EXP."
(call-with-values (lambda () exp)
(lambda (result . _)
result)))
(define-syntax define-memoized/v
(lambda (form)
"Define a memoized single-valued unary procedure with docstring.
The procedure argument is compared to cached keys using `eqv?'."
(syntax-case form ()
((_ (proc arg) docstring body body* ...)
(string? (syntax->datum #'docstring))
#'(define proc
(let ((cache (make-hash-table)))
(define (proc arg)
docstring
(match (hashv-get-handle cache arg)
((_ . value)
value)
(_
(let ((result (let () body body* ...)))
(hashv-set! cache arg result)
result))))
proc))))))
(define (package-transitive-supported-systems package)
(define-memoized/v (package-transitive-supported-systems package)
"Return the intersection of the systems supported by PACKAGE and those
supported by its dependencies."
(first-value
(let loop ((package package)
(systems (package-supported-systems package))
(visited vlist-null))
(match (vhash-assq package visited)
((_ . result)
(values (lset-intersection string=? systems result)
visited))
(#f
(call-with-values
(lambda ()
(fold2 (lambda (input systems visited)
(fold (lambda (input systems)
(match input
((label (? package? package) . _)
(loop package systems visited))
((label (? package? p) . _)
(lset-intersection
string=? systems (package-transitive-supported-systems p)))
(_
(values systems visited))))
(lset-intersection string=?
systems
(package-supported-systems package))
visited
systems)))
(package-supported-systems package)
(package-direct-inputs package)))
(lambda (systems visited)
(values systems
(vhash-consq package systems visited)))))))))
(define (bag-transitive-inputs bag)
"Same as 'package-transitive-inputs', but applied to a bag."

View File

@ -414,7 +414,13 @@ INFO-DIR? is #f."
(return #f))))
(define inputs
(if info-dir
(cons info-dir (manifest-inputs manifest))
;; XXX: Here we use the tuple (INFO-DIR "out") just so that the list
;; is unambiguous for the gexp code when MANIFEST has a single input
;; denoted as a string (the pattern (DRV STRING) is normally
;; interpreted in a gexp as "the STRING output of DRV".). See
;; <http://lists.gnu.org/archive/html/guix-devel/2014-12/msg00292.html>.
(cons (list info-dir "out")
(manifest-inputs manifest))
(manifest-inputs manifest)))
(define builder

@ -1 +0,0 @@
Subproject commit e7720aa10a1da63bb15a4587837d649268944943

2
nix/AUTHORS Normal file
View File

@ -0,0 +1,2 @@
Most of the code is this directory was written by several people for
the Nix project (http://nixos.org/nix). Thanks!

504
nix/COPYING Normal file
View File

@ -0,0 +1,504 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

38
nix/boost/assert.hpp Normal file
View File

@ -0,0 +1,38 @@
//
// boost/assert.hpp - BOOST_ASSERT(expr)
//
// Copyright (c) 2001, 2002 Peter Dimov and Multi Media Ltd.
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
// Note: There are no include guards. This is intentional.
//
// See http://www.boost.org/libs/utility/assert.html for documentation.
//
#undef BOOST_ASSERT
#if defined(BOOST_DISABLE_ASSERTS)
# define BOOST_ASSERT(expr) ((void)0)
#elif defined(BOOST_ENABLE_ASSERT_HANDLER)
#include <boost/current_function.hpp>
namespace boost
{
void assertion_failed(char const * expr, char const * function, char const * file, long line); // user defined
} // namespace boost
#define BOOST_ASSERT(expr) ((expr)? ((void)0): ::boost::assertion_failed(#expr, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__))
#else
# include <assert.h>
# define BOOST_ASSERT(expr) assert(expr)
#endif

64
nix/boost/format.hpp Normal file
View File

@ -0,0 +1,64 @@
// -*- C++ -*-
// Boost general library 'format' ---------------------------
// See http://www.boost.org for updates, documentation, and revision history.
// (C) Samuel Krempp 2001
// krempp@crans.ens-cachan.fr
// Permission to copy, use, modify, sell and
// distribute this software is granted provided this copyright notice appears
// in all copies. This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
// ideas taken from Rüdiger Loos's format class
// and Karl Nelson's ofstream
// ----------------------------------------------------------------------------
// format.hpp : primary header
// ----------------------------------------------------------------------------
#ifndef BOOST_FORMAT_HPP
#define BOOST_FORMAT_HPP
#include <vector>
#include <string>
#include <sstream>
#include <cassert>
#if HAVE_LOCALE
#include <locale>
#else
#define BOOST_NO_STD_LOCALE
#define BOOST_NO_LOCALE_ISIDIGIT
#include <cctype>
#endif
#include <boost/format/macros_default.hpp>
// **** Forward declarations ----------------------------------
#include <boost/format/format_fwd.hpp> // basic_format<Ch,Tr>, and other frontends
#include <boost/format/internals_fwd.hpp> // misc forward declarations for internal use
// **** Auxiliary structs (stream_format_state<Ch,Tr> , and format_item<Ch,Tr> )
#include <boost/format/internals.hpp>
// **** Format class interface --------------------------------
#include <boost/format/format_class.hpp>
// **** Exceptions -----------------------------------------------
#include <boost/format/exceptions.hpp>
// **** Implementation -------------------------------------------
//#include <boost/format/format_implementation.hpp> // member functions
#include <boost/format/group.hpp> // class for grouping arguments
#include <boost/format/feed_args.hpp> // argument-feeding functions
//#include <boost/format/parsing.hpp> // format-string parsing (member-)functions
// **** Implementation of the free functions ----------------------
//#include <boost/format/free_funcs.hpp>
#endif // BOOST_FORMAT_HPP

View File

@ -0,0 +1,96 @@
// -*- C++ -*-
// Boost general library 'format' ---------------------------
// See http://www.boost.org for updates, documentation, and revision history.
// (C) Samuel Krempp 2001
// krempp@crans.ens-cachan.fr
// Permission to copy, use, modify, sell and
// distribute this software is granted provided this copyright notice appears
// in all copies. This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
// ideas taken from Rüdiger Loos's format class
// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)
// ------------------------------------------------------------------------------
// exceptions.hpp
// ------------------------------------------------------------------------------
#ifndef BOOST_FORMAT_EXCEPTIONS_HPP
#define BOOST_FORMAT_EXCEPTIONS_HPP
#include <stdexcept>
namespace boost {
namespace io {
// **** exceptions -----------------------------------------------
class format_error : public std::exception
{
public:
format_error() {}
virtual const char *what() const throw()
{
return "boost::format_error: "
"format generic failure";
}
};
class bad_format_string : public format_error
{
public:
bad_format_string() {}
virtual const char *what() const throw()
{
return "boost::bad_format_string: "
"format-string is ill-formed";
}
};
class too_few_args : public format_error
{
public:
too_few_args() {}
virtual const char *what() const throw()
{
return "boost::too_few_args: "
"format-string refered to more arguments than were passed";
}
};
class too_many_args : public format_error
{
public:
too_many_args() {}
virtual const char *what() const throw()
{
return "boost::too_many_args: "
"format-string refered to less arguments than were passed";
}
};
class out_of_range : public format_error
{
public:
out_of_range() {}
virtual const char *what() const throw()
{
return "boost::out_of_range: "
"tried to refer to an argument (or item) number which is out of range, "
"according to the format string.";
}
};
} // namespace io
} // namespace boost
#endif // BOOST_FORMAT_EXCEPTIONS_HPP

View File

@ -0,0 +1,247 @@
// -*- C++ -*-
// Boost general library 'format' ---------------------------
// See http://www.boost.org for updates, documentation, and revision history.
// (C) Samuel Krempp 2001
// krempp@crans.ens-cachan.fr
// Permission to copy, use, modify, sell and
// distribute this software is granted provided this copyright notice appears
// in all copies. This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
// ideas taken from Rüdiger Loos's format class
// and Karl Nelson's ofstream
// ----------------------------------------------------------------------------
// feed_args.hpp : functions for processing each argument
// (feed, feed_manip, and distribute)
// ----------------------------------------------------------------------------
#ifndef BOOST_FORMAT_FEED_ARGS_HPP
#define BOOST_FORMAT_FEED_ARGS_HPP
#include "boost/format/format_class.hpp"
#include "boost/format/group.hpp"
#include "boost/throw_exception.hpp"
namespace boost {
namespace io {
namespace detail {
namespace {
inline
void empty_buf(BOOST_IO_STD ostringstream & os) {
static const std::string emptyStr;
os.str(emptyStr);
}
void do_pad( std::string & s,
std::streamsize w,
const char c,
std::ios::fmtflags f,
bool center)
// applies centered / left / right padding to the string s.
// Effects : string s is padded.
{
std::streamsize n=w-s.size();
if(n<=0) {
return;
}
if(center)
{
s.reserve(w); // allocate once for the 2 inserts
const std::streamsize n1 = n /2, n0 = n - n1;
s.insert(s.begin(), n0, c);
s.append(n1, c);
}
else
{
if(f & std::ios::left) {
s.append(n, c);
}
else {
s.insert(s.begin(), n, c);
}
}
} // -do_pad(..)
template<class T> inline
void put_head(BOOST_IO_STD ostream& , const T& ) {
}
template<class T> inline
void put_head( BOOST_IO_STD ostream& os, const group1<T>& x ) {
os << group_head(x.a1_); // send the first N-1 items, not the last
}
template<class T> inline
void put_last( BOOST_IO_STD ostream& os, const T& x ) {
os << x ;
}
template<class T> inline
void put_last( BOOST_IO_STD ostream& os, const group1<T>& x ) {
os << group_last(x.a1_); // this selects the last element
}
#ifndef BOOST_NO_OVERLOAD_FOR_NON_CONST
template<class T> inline
void put_head( BOOST_IO_STD ostream& , T& ) {
}
template<class T> inline
void put_last( BOOST_IO_STD ostream& os, T& x ) {
os << x ;
}
#endif
template<class T>
void put( T x,
const format_item& specs,
std::string & res,
BOOST_IO_STD ostringstream& oss_ )
{
// does the actual conversion of x, with given params, into a string
// using the *supplied* strinstream. (the stream state is important)
typedef std::string string_t;
typedef format_item format_item_t;
stream_format_state prev_state(oss_);
specs.state_.apply_on(oss_);
// in case x is a group, apply the manip part of it,
// in order to find width
put_head( oss_, x );
empty_buf( oss_);
const std::streamsize w=oss_.width();
const std::ios::fmtflags fl=oss_.flags();
const bool internal = (fl & std::ios::internal) != 0;
const bool two_stepped_padding = internal
&& ! ( specs.pad_scheme_ & format_item_t::spacepad )
&& specs.truncate_ < 0 ;
if(! two_stepped_padding)
{
if(w>0) // handle simple padding via do_pad, not natively in stream
oss_.width(0);
put_last( oss_, x);
res = oss_.str();
if (specs.truncate_ >= 0)
res.erase(specs.truncate_);
// complex pads :
if(specs.pad_scheme_ & format_item_t::spacepad)
{
if( res.size()==0 || ( res[0]!='+' && res[0]!='-' ))
{
res.insert(res.begin(), 1, ' '); // insert 1 space at pos 0
}
}
if(w > 0) // need do_pad
{
do_pad(res,w,oss_.fill(), fl, (specs.pad_scheme_ & format_item_t::centered) !=0 );
}
}
else // 2-stepped padding
{
put_last( oss_, x); // oss_.width() may result in padding.
res = oss_.str();
if (specs.truncate_ >= 0)
res.erase(specs.truncate_);
if( res.size() - w > 0)
{ // length w exceeded
// either it was multi-output with first output padding up all width..
// either it was one big arg and we are fine.
empty_buf( oss_);
oss_.width(0);
put_last(oss_, x );
string_t tmp = oss_.str(); // minimal-length output
std::streamsize d;
if( (d=w - tmp.size()) <=0 )
{
// minimal length is already >= w, so no padding (cool!)
res.swap(tmp);
}
else
{ // hum.. we need to pad (it was necessarily multi-output)
typedef typename string_t::size_type size_type;
size_type i = 0;
while( i<tmp.size() && tmp[i] == res[i] ) // find where we should pad.
++i;
tmp.insert(i, static_cast<size_type>( d ), oss_.fill());
res.swap( tmp );
}
}
else
{ // okay, only one thing was printed and padded, so res is fine.
}
}
prev_state.apply_on(oss_);
empty_buf( oss_);
oss_.clear();
} // end- put(..)
} // local namespace
template<class T>
void distribute(basic_format& self, T x)
// call put(x, ..) on every occurence of the current argument :
{
if(self.cur_arg_ >= self.num_args_)
{
if( self.exceptions() & too_many_args_bit )
boost::throw_exception(too_many_args()); // too many variables have been supplied !
else return;
}
for(unsigned long i=0; i < self.items_.size(); ++i)
{
if(self.items_[i].argN_ == self.cur_arg_)
{
put<T> (x, self.items_[i], self.items_[i].res_, self.oss_ );
}
}
}
template<class T>
basic_format& feed(basic_format& self, T x)
{
if(self.dumped_) self.clear();
distribute<T> (self, x);
++self.cur_arg_;
if(self.bound_.size() != 0)
{
while( self.cur_arg_ < self.num_args_ && self.bound_[self.cur_arg_] )
++self.cur_arg_;
}
// this arg is finished, reset the stream's format state
self.state0_.apply_on(self.oss_);
return self;
}
} // namespace detail
} // namespace io
} // namespace boost
#endif // BOOST_FORMAT_FEED_ARGS_HPP

View File

@ -0,0 +1,135 @@
// -*- C++ -*-
// Boost general library 'format' ---------------------------
// See http://www.boost.org for updates, documentation, and revision history.
// (C) Samuel Krempp 2001
// krempp@crans.ens-cachan.fr
// Permission to copy, use, modify, sell and
// distribute this software is granted provided this copyright notice appears
// in all copies. This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
// ideas taken from Rüdiger Loos's format class
// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)
// ------------------------------------------------------------------------------
// format_class.hpp : class interface
// ------------------------------------------------------------------------------
#ifndef BOOST_FORMAT_CLASS_HPP
#define BOOST_FORMAT_CLASS_HPP
#include <vector>
#include <string>
#include <boost/format/format_fwd.hpp>
#include <boost/format/internals_fwd.hpp>
#include <boost/format/internals.hpp>
namespace boost {
class basic_format
{
public:
typedef std::string string_t;
typedef BOOST_IO_STD ostringstream internal_stream_t;
private:
typedef BOOST_IO_STD ostream stream_t;
typedef io::detail::stream_format_state stream_format_state;
typedef io::detail::format_item format_item_t;
public:
basic_format(const char* str);
basic_format(const string_t& s);
#ifndef BOOST_NO_STD_LOCALE
basic_format(const char* str, const std::locale & loc);
basic_format(const string_t& s, const std::locale & loc);
#endif // no locale
basic_format(const basic_format& x);
basic_format& operator= (const basic_format& x);
basic_format& clear(); // empty the string buffers (except bound arguments, see clear_binds() )
// pass arguments through those operators :
template<class T> basic_format& operator%(const T& x)
{
return io::detail::feed<const T&>(*this,x);
}
#ifndef BOOST_NO_OVERLOAD_FOR_NON_CONST
template<class T> basic_format& operator%(T& x)
{
return io::detail::feed<T&>(*this,x);
}
#endif
// system for binding arguments :
template<class T>
basic_format& bind_arg(int argN, const T& val)
{
return io::detail::bind_arg_body(*this, argN, val);
}
basic_format& clear_bind(int argN);
basic_format& clear_binds();
// modify the params of a directive, by applying a manipulator :
template<class T>
basic_format& modify_item(int itemN, const T& manipulator)
{
return io::detail::modify_item_body(*this, itemN, manipulator) ;
}
// Choosing which errors will throw exceptions :
unsigned char exceptions() const;
unsigned char exceptions(unsigned char newexcept);
// final output
string_t str() const;
friend BOOST_IO_STD ostream&
operator<< ( BOOST_IO_STD ostream& , const basic_format& );
template<class T> friend basic_format&
io::detail::feed(basic_format&, T);
template<class T> friend
void io::detail::distribute(basic_format&, T);
template<class T> friend
basic_format& io::detail::modify_item_body(basic_format&, int, const T&);
template<class T> friend
basic_format& io::detail::bind_arg_body(basic_format&, int, const T&);
// make the members private only if the friend templates are supported
private:
// flag bits, used for style_
enum style_values { ordered = 1, // set only if all directives are positional directives
special_needs = 4 };
// parse the format string :
void parse(const string_t&);
int style_; // style of format-string : positional or not, etc
int cur_arg_; // keep track of wich argument will come
int num_args_; // number of expected arguments
mutable bool dumped_; // true only after call to str() or <<
std::vector<format_item_t> items_; // vector of directives (aka items)
string_t prefix_; // piece of string to insert before first item
std::vector<bool> bound_; // stores which arguments were bound
// size = num_args OR zero
internal_stream_t oss_; // the internal stream.
stream_format_state state0_; // reference state for oss_
unsigned char exceptions_;
}; // class basic_format
} // namespace boost
#endif // BOOST_FORMAT_CLASS_HPP

View File

@ -0,0 +1,49 @@
// -*- C++ -*-
// Boost general library 'format' ---------------------------
// See http://www.boost.org for updates, documentation, and revision history.
// (C) Samuel Krempp 2001
// krempp@crans.ens-cachan.fr
// Permission to copy, use, modify, sell and
// distribute this software is granted provided this copyright notice appears
// in all copies. This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
// ideas taken from Rüdiger Loos's format class
// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)
// ------------------------------------------------------------------------------
// format_fwd.hpp : forward declarations, for primary header format.hpp
// ------------------------------------------------------------------------------
#ifndef BOOST_FORMAT_FWD_HPP
#define BOOST_FORMAT_FWD_HPP
#include <string>
#include <iosfwd>
namespace boost {
class basic_format;
typedef basic_format format;
namespace io {
enum format_error_bits { bad_format_string_bit = 1,
too_few_args_bit = 2, too_many_args_bit = 4,
out_of_range_bit = 8,
all_error_bits = 255, no_error_bits=0 };
// Convertion: format to string
std::string str(const basic_format& ) ;
} // namespace io
BOOST_IO_STD ostream&
operator<<( BOOST_IO_STD ostream&, const basic_format&);
} // namespace boost
#endif // BOOST_FORMAT_FWD_HPP

View File

@ -0,0 +1,256 @@
// -*- C++ -*-
// Boost general library format ---------------------------
// See http://www.boost.org for updates, documentation, and revision history.
// (C) Samuel Krempp 2001
// krempp@crans.ens-cachan.fr
// Permission to copy, use, modify, sell and
// distribute this software is granted provided this copyright notice appears
// in all copies. This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
// ideas taken from Rüdiger Loos's format class
// and Karl Nelson's ofstream
// ----------------------------------------------------------------------------
// format_implementation.hpp Implementation of the basic_format class
// ----------------------------------------------------------------------------
#ifndef BOOST_FORMAT_IMPLEMENTATION_HPP
#define BOOST_FORMAT_IMPLEMENTATION_HPP
#include <boost/throw_exception.hpp>
#include <boost/assert.hpp>
#include <boost/format.hpp>
namespace boost {
// -------- format:: -------------------------------------------
basic_format::basic_format(const char* str)
: style_(0), cur_arg_(0), num_args_(0), dumped_(false),
items_(), oss_(), exceptions_(io::all_error_bits)
{
state0_.set_by_stream(oss_);
string_t emptyStr;
if( !str) str = emptyStr.c_str();
parse( str );
}
#ifndef BOOST_NO_STD_LOCALE
basic_format::basic_format(const char* str, const std::locale & loc)
: style_(0), cur_arg_(0), num_args_(0), dumped_(false),
items_(), oss_(), exceptions_(io::all_error_bits)
{
oss_.imbue( loc );
state0_.set_by_stream(oss_);
string_t emptyStr;
if( !str) str = emptyStr.c_str();
parse( str );
}
basic_format::basic_format(const string_t& s, const std::locale & loc)
: style_(0), cur_arg_(0), num_args_(0), dumped_(false),
items_(), oss_(), exceptions_(io::all_error_bits)
{
oss_.imbue( loc );
state0_.set_by_stream(oss_);
parse(s);
}
#endif //BOOST_NO_STD_LOCALE
basic_format::basic_format(const string_t& s)
: style_(0), cur_arg_(0), num_args_(0), dumped_(false),
items_(), oss_(), exceptions_(io::all_error_bits)
{
state0_.set_by_stream(oss_);
parse(s);
}
basic_format:: basic_format(const basic_format& x)
: style_(x.style_), cur_arg_(x.cur_arg_), num_args_(x.num_args_), dumped_(false),
items_(x.items_), prefix_(x.prefix_), bound_(x.bound_),
oss_(), // <- we obviously can't copy x.oss_
state0_(x.state0_), exceptions_(x.exceptions_)
{
state0_.apply_on(oss_);
}
basic_format& basic_format::operator= (const basic_format& x)
{
if(this == &x)
return *this;
state0_ = x.state0_;
state0_.apply_on(oss_);
// plus all the other (trivial) assignments :
exceptions_ = x.exceptions_;
items_ = x.items_;
prefix_ = x.prefix_;
bound_=x.bound_;
style_=x.style_;
cur_arg_=x.cur_arg_;
num_args_=x.num_args_;
dumped_=x.dumped_;
return *this;
}
unsigned char basic_format::exceptions() const
{
return exceptions_;
}
unsigned char basic_format::exceptions(unsigned char newexcept)
{
unsigned char swp = exceptions_;
exceptions_ = newexcept;
return swp;
}
basic_format& basic_format ::clear()
// empty the string buffers (except bound arguments, see clear_binds() )
// and make the format object ready for formatting a new set of arguments
{
BOOST_ASSERT( bound_.size()==0 || num_args_ == static_cast<int>(bound_.size()) );
for(unsigned long i=0; i<items_.size(); ++i){
items_[i].state_ = items_[i].ref_state_;
// clear converted strings only if the corresponding argument is not bound :
if( bound_.size()==0 || !bound_[ items_[i].argN_ ] ) items_[i].res_.resize(0);
}
cur_arg_=0; dumped_=false;
// maybe first arg is bound:
if(bound_.size() != 0)
{
while(cur_arg_ < num_args_ && bound_[cur_arg_] ) ++cur_arg_;
}
return *this;
}
basic_format& basic_format ::clear_binds()
// cancel all bindings, and clear()
{
bound_.resize(0);
clear();
return *this;
}
basic_format& basic_format::clear_bind(int argN)
// cancel the binding of ONE argument, and clear()
{
if(argN<1 || argN > num_args_ || bound_.size()==0 || !bound_[argN-1] )
{
if( exceptions() & io::out_of_range_bit )
boost::throw_exception(io::out_of_range()); // arg not in range.
else return *this;
}
bound_[argN-1]=false;
clear();
return *this;
}
std::string basic_format::str() const
{
dumped_=true;
if(items_.size()==0)
return prefix_;
if( cur_arg_ < num_args_)
if( exceptions() & io::too_few_args_bit )
boost::throw_exception(io::too_few_args()); // not enough variables have been supplied !
unsigned long sz = prefix_.size();
unsigned long i;
for(i=0; i < items_.size(); ++i)
sz += items_[i].res_.size() + items_[i].appendix_.size();
string_t res;
res.reserve(sz);
res += prefix_;
for(i=0; i < items_.size(); ++i)
{
const format_item_t& item = items_[i];
res += item.res_;
if( item.argN_ == format_item_t::argN_tabulation)
{
BOOST_ASSERT( item.pad_scheme_ & format_item_t::tabulation);
std::streamsize n = item.state_.width_ - res.size();
if( n > 0 )
res.append( n, item.state_.fill_ );
}
res += item.appendix_;
}
return res;
}
namespace io {
namespace detail {
template<class T>
basic_format& bind_arg_body( basic_format& self,
int argN,
const T& val)
// bind one argument to a fixed value
// this is persistent over clear() calls, thus also over str() and <<
{
if(self.dumped_) self.clear(); // needed, because we will modify cur_arg_..
if(argN<1 || argN > self.num_args_)
{
if( self.exceptions() & io::out_of_range_bit )
boost::throw_exception(io::out_of_range()); // arg not in range.
else return self;
}
if(self.bound_.size()==0)
self.bound_.assign(self.num_args_,false);
else
BOOST_ASSERT( self.num_args_ == static_cast<signed int>(self.bound_.size()) );
int o_cur_arg = self.cur_arg_;
self.cur_arg_ = argN-1; // arrays begin at 0
self.bound_[self.cur_arg_]=false; // if already set, we unset and re-sets..
self.operator%(val); // put val at the right place, because cur_arg is set
// Now re-position cur_arg before leaving :
self.cur_arg_ = o_cur_arg;
self.bound_[argN-1]=true;
if(self.cur_arg_ == argN-1 )
// hum, now this arg is bound, so move to next free arg
{
while(self.cur_arg_ < self.num_args_ && self.bound_[self.cur_arg_]) ++self.cur_arg_;
}
// In any case, we either have all args, or are on a non-binded arg :
BOOST_ASSERT( self.cur_arg_ >= self.num_args_ || ! self.bound_[self.cur_arg_]);
return self;
}
template<class T>
basic_format& modify_item_body( basic_format& self,
int itemN,
const T& manipulator)
// applies a manipulator to the format_item describing a given directive.
// this is a permanent change, clear or clear_binds won't cancel that.
{
if(itemN<1 || itemN >= static_cast<signed int>(self.items_.size() ))
{
if( self.exceptions() & io::out_of_range_bit )
boost::throw_exception(io::out_of_range()); // item not in range.
else return self;
}
self.items_[itemN-1].ref_state_.apply_manip( manipulator );
self.items_[itemN-1].state_ = self.items_[itemN-1].ref_state_;
return self;
}
} // namespace detail
} // namespace io
} // namespace boost
#endif // BOOST_FORMAT_IMPLEMENTATION_HPP

View File

@ -0,0 +1,71 @@
// -*- C++ -*-
// Boost general library 'format' ---------------------------
// See http://www.boost.org for updates, documentation, and revision history.
// (C) Samuel Krempp 2001
// krempp@crans.ens-cachan.fr
// Permission to copy, use, modify, sell and
// distribute this software is granted provided this copyright notice appears
// in all copies. This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
// ideas taken from Rüdiger Loos's format class
// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)
// ------------------------------------------------------------------------------
// free_funcs.hpp : implementation of the free functions declared in namespace format
// ------------------------------------------------------------------------------
#ifndef BOOST_FORMAT_FUNCS_HPP
#define BOOST_FORMAT_FUNCS_HPP
#include "boost/format.hpp"
#include "boost/throw_exception.hpp"
namespace boost {
namespace io {
inline
std::string str(const basic_format& f)
// adds up all pieces of strings and converted items, and return the formatted string
{
return f.str();
}
} // - namespace io
BOOST_IO_STD ostream&
operator<<( BOOST_IO_STD ostream& os,
const boost::basic_format& f)
// effect: "return os << str(f);" but we can try to do it faster
{
typedef boost::basic_format format_t;
if(f.items_.size()==0)
os << f.prefix_;
else {
if(f.cur_arg_ < f.num_args_)
if( f.exceptions() & io::too_few_args_bit )
boost::throw_exception(io::too_few_args()); // not enough variables have been supplied !
if(f.style_ & format_t::special_needs)
os << f.str();
else {
// else we dont have to count chars output, so we dump directly to os :
os << f.prefix_;
for(unsigned long i=0; i<f.items_.size(); ++i)
{
const format_t::format_item_t& item = f.items_[i];
os << item.res_;
os << item.appendix_;
}
}
}
f.dumped_=true;
return os;
}
} // namespace boost
#endif // BOOST_FORMAT_FUNCS_HPP

680
nix/boost/format/group.hpp Normal file
View File

@ -0,0 +1,680 @@
// -*- C++ -*-
// Boost general library 'format' ---------------------------
// See http://www.boost.org for updates, documentation, and revision history.
// (C) Samuel Krempp 2001
// krempp@crans.ens-cachan.fr
// Permission to copy, use, modify, sell and
// distribute this software is granted provided this copyright notice appears
// in all copies. This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
// ideas taken from Rüdiger Loos's format class
// and Karl Nelson's ofstream
// ----------------------------------------------------------------------------
// group.hpp : encapsulates a group of manipulators along with an argument
//
// group_head : cut the last element of a group out.
// (is overloaded below on each type of group)
// group_last : returns the last element of a group
// (is overloaded below on each type of group)
// ----------------------------------------------------------------------------
#ifndef BOOST_FORMAT_GROUP_HPP
#define BOOST_FORMAT_GROUP_HPP
namespace boost {
namespace io {
namespace detail {
// empty group, but useful even though.
struct group0
{
group0() {}
};
template <class Ch, class Tr>
inline
BOOST_IO_STD ostream&
operator << ( BOOST_IO_STD ostream& os,
const group0& )
{
return os;
}
template <class T1>
struct group1
{
T1 a1_;
group1(T1 a1)
: a1_(a1)
{}
};
template <class Ch, class Tr, class T1>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
const group1<T1>& x)
{
os << x.a1_;
return os;
}
template <class T1,class T2>
struct group2
{
T1 a1_;
T2 a2_;
group2(T1 a1,T2 a2)
: a1_(a1),a2_(a2)
{}
};
template <class Ch, class Tr, class T1,class T2>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
const group2<T1,T2>& x)
{
os << x.a1_<< x.a2_;
return os;
}
template <class T1,class T2,class T3>
struct group3
{
T1 a1_;
T2 a2_;
T3 a3_;
group3(T1 a1,T2 a2,T3 a3)
: a1_(a1),a2_(a2),a3_(a3)
{}
};
template <class Ch, class Tr, class T1,class T2,class T3>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
const group3<T1,T2,T3>& x)
{
os << x.a1_<< x.a2_<< x.a3_;
return os;
}
template <class T1,class T2,class T3,class T4>
struct group4
{
T1 a1_;
T2 a2_;
T3 a3_;
T4 a4_;
group4(T1 a1,T2 a2,T3 a3,T4 a4)
: a1_(a1),a2_(a2),a3_(a3),a4_(a4)
{}
};
template <class Ch, class Tr, class T1,class T2,class T3,class T4>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
const group4<T1,T2,T3,T4>& x)
{
os << x.a1_<< x.a2_<< x.a3_<< x.a4_;
return os;
}
template <class T1,class T2,class T3,class T4,class T5>
struct group5
{
T1 a1_;
T2 a2_;
T3 a3_;
T4 a4_;
T5 a5_;
group5(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5)
: a1_(a1),a2_(a2),a3_(a3),a4_(a4),a5_(a5)
{}
};
template <class Ch, class Tr, class T1,class T2,class T3,class T4,class T5>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
const group5<T1,T2,T3,T4,T5>& x)
{
os << x.a1_<< x.a2_<< x.a3_<< x.a4_<< x.a5_;
return os;
}
template <class T1,class T2,class T3,class T4,class T5,class T6>
struct group6
{
T1 a1_;
T2 a2_;
T3 a3_;
T4 a4_;
T5 a5_;
T6 a6_;
group6(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6)
: a1_(a1),a2_(a2),a3_(a3),a4_(a4),a5_(a5),a6_(a6)
{}
};
template <class Ch, class Tr, class T1,class T2,class T3,class T4,class T5,class T6>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
const group6<T1,T2,T3,T4,T5,T6>& x)
{
os << x.a1_<< x.a2_<< x.a3_<< x.a4_<< x.a5_<< x.a6_;
return os;
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7>
struct group7
{
T1 a1_;
T2 a2_;
T3 a3_;
T4 a4_;
T5 a5_;
T6 a6_;
T7 a7_;
group7(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7)
: a1_(a1),a2_(a2),a3_(a3),a4_(a4),a5_(a5),a6_(a6),a7_(a7)
{}
};
template <class Ch, class Tr, class T1,class T2,class T3,class T4,class T5,class T6,class T7>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
const group7<T1,T2,T3,T4,T5,T6,T7>& x)
{
os << x.a1_<< x.a2_<< x.a3_<< x.a4_<< x.a5_<< x.a6_<< x.a7_;
return os;
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8>
struct group8
{
T1 a1_;
T2 a2_;
T3 a3_;
T4 a4_;
T5 a5_;
T6 a6_;
T7 a7_;
T8 a8_;
group8(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8)
: a1_(a1),a2_(a2),a3_(a3),a4_(a4),a5_(a5),a6_(a6),a7_(a7),a8_(a8)
{}
};
template <class Ch, class Tr, class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
const group8<T1,T2,T3,T4,T5,T6,T7,T8>& x)
{
os << x.a1_<< x.a2_<< x.a3_<< x.a4_<< x.a5_<< x.a6_<< x.a7_<< x.a8_;
return os;
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9>
struct group9
{
T1 a1_;
T2 a2_;
T3 a3_;
T4 a4_;
T5 a5_;
T6 a6_;
T7 a7_;
T8 a8_;
T9 a9_;
group9(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8,T9 a9)
: a1_(a1),a2_(a2),a3_(a3),a4_(a4),a5_(a5),a6_(a6),a7_(a7),a8_(a8),a9_(a9)
{}
};
template <class Ch, class Tr, class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
const group9<T1,T2,T3,T4,T5,T6,T7,T8,T9>& x)
{
os << x.a1_<< x.a2_<< x.a3_<< x.a4_<< x.a5_<< x.a6_<< x.a7_<< x.a8_<< x.a9_;
return os;
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9,class T10>
struct group10
{
T1 a1_;
T2 a2_;
T3 a3_;
T4 a4_;
T5 a5_;
T6 a6_;
T7 a7_;
T8 a8_;
T9 a9_;
T10 a10_;
group10(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8,T9 a9,T10 a10)
: a1_(a1),a2_(a2),a3_(a3),a4_(a4),a5_(a5),a6_(a6),a7_(a7),a8_(a8),a9_(a9),a10_(a10)
{}
};
template <class Ch, class Tr, class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9,class T10>
inline
BOOST_IO_STD ostream&
operator << (BOOST_IO_STD ostream& os,
const group10<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10>& x)
{
os << x.a1_<< x.a2_<< x.a3_<< x.a4_<< x.a5_<< x.a6_<< x.a7_<< x.a8_<< x.a9_<< x.a10_;
return os;
}
template <class T1,class T2>
inline
group1<T1>
group_head( group2<T1,T2> const& x)
{
return group1<T1> (x.a1_);
}
template <class T1,class T2>
inline
group1<T2>
group_last( group2<T1,T2> const& x)
{
return group1<T2> (x.a2_);
}
template <class T1,class T2,class T3>
inline
group2<T1,T2>
group_head( group3<T1,T2,T3> const& x)
{
return group2<T1,T2> (x.a1_,x.a2_);
}
template <class T1,class T2,class T3>
inline
group1<T3>
group_last( group3<T1,T2,T3> const& x)
{
return group1<T3> (x.a3_);
}
template <class T1,class T2,class T3,class T4>
inline
group3<T1,T2,T3>
group_head( group4<T1,T2,T3,T4> const& x)
{
return group3<T1,T2,T3> (x.a1_,x.a2_,x.a3_);
}
template <class T1,class T2,class T3,class T4>
inline
group1<T4>
group_last( group4<T1,T2,T3,T4> const& x)
{
return group1<T4> (x.a4_);
}
template <class T1,class T2,class T3,class T4,class T5>
inline
group4<T1,T2,T3,T4>
group_head( group5<T1,T2,T3,T4,T5> const& x)
{
return group4<T1,T2,T3,T4> (x.a1_,x.a2_,x.a3_,x.a4_);
}
template <class T1,class T2,class T3,class T4,class T5>
inline
group1<T5>
group_last( group5<T1,T2,T3,T4,T5> const& x)
{
return group1<T5> (x.a5_);
}
template <class T1,class T2,class T3,class T4,class T5,class T6>
inline
group5<T1,T2,T3,T4,T5>
group_head( group6<T1,T2,T3,T4,T5,T6> const& x)
{
return group5<T1,T2,T3,T4,T5> (x.a1_,x.a2_,x.a3_,x.a4_,x.a5_);
}
template <class T1,class T2,class T3,class T4,class T5,class T6>
inline
group1<T6>
group_last( group6<T1,T2,T3,T4,T5,T6> const& x)
{
return group1<T6> (x.a6_);
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7>
inline
group6<T1,T2,T3,T4,T5,T6>
group_head( group7<T1,T2,T3,T4,T5,T6,T7> const& x)
{
return group6<T1,T2,T3,T4,T5,T6> (x.a1_,x.a2_,x.a3_,x.a4_,x.a5_,x.a6_);
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7>
inline
group1<T7>
group_last( group7<T1,T2,T3,T4,T5,T6,T7> const& x)
{
return group1<T7> (x.a7_);
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8>
inline
group7<T1,T2,T3,T4,T5,T6,T7>
group_head( group8<T1,T2,T3,T4,T5,T6,T7,T8> const& x)
{
return group7<T1,T2,T3,T4,T5,T6,T7> (x.a1_,x.a2_,x.a3_,x.a4_,x.a5_,x.a6_,x.a7_);
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8>
inline
group1<T8>
group_last( group8<T1,T2,T3,T4,T5,T6,T7,T8> const& x)
{
return group1<T8> (x.a8_);
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9>
inline
group8<T1,T2,T3,T4,T5,T6,T7,T8>
group_head( group9<T1,T2,T3,T4,T5,T6,T7,T8,T9> const& x)
{
return group8<T1,T2,T3,T4,T5,T6,T7,T8> (x.a1_,x.a2_,x.a3_,x.a4_,x.a5_,x.a6_,x.a7_,x.a8_);
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9>
inline
group1<T9>
group_last( group9<T1,T2,T3,T4,T5,T6,T7,T8,T9> const& x)
{
return group1<T9> (x.a9_);
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9,class T10>
inline
group9<T1,T2,T3,T4,T5,T6,T7,T8,T9>
group_head( group10<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10> const& x)
{
return group9<T1,T2,T3,T4,T5,T6,T7,T8,T9> (x.a1_,x.a2_,x.a3_,x.a4_,x.a5_,x.a6_,x.a7_,x.a8_,x.a9_);
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9,class T10>
inline
group1<T10>
group_last( group10<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10> const& x)
{
return group1<T10> (x.a10_);
}
} // namespace detail
// helper functions
inline detail::group1< detail::group0 >
group() { return detail::group1< detail::group0 > ( detail::group0() ); }
template <class T1, class Var>
inline
detail::group1< detail::group2<T1, Var const&> >
group(T1 a1, Var const& var)
{
return detail::group1< detail::group2<T1, Var const&> >
( detail::group2<T1, Var const&>
(a1, var)
);
}
template <class T1,class T2, class Var>
inline
detail::group1< detail::group3<T1,T2, Var const&> >
group(T1 a1,T2 a2, Var const& var)
{
return detail::group1< detail::group3<T1,T2, Var const&> >
( detail::group3<T1,T2, Var const&>
(a1,a2, var)
);
}
template <class T1,class T2,class T3, class Var>
inline
detail::group1< detail::group4<T1,T2,T3, Var const&> >
group(T1 a1,T2 a2,T3 a3, Var const& var)
{
return detail::group1< detail::group4<T1,T2,T3, Var const&> >
( detail::group4<T1,T2,T3, Var const&>
(a1,a2,a3, var)
);
}
template <class T1,class T2,class T3,class T4, class Var>
inline
detail::group1< detail::group5<T1,T2,T3,T4, Var const&> >
group(T1 a1,T2 a2,T3 a3,T4 a4, Var const& var)
{
return detail::group1< detail::group5<T1,T2,T3,T4, Var const&> >
( detail::group5<T1,T2,T3,T4, Var const&>
(a1,a2,a3,a4, var)
);
}
template <class T1,class T2,class T3,class T4,class T5, class Var>
inline
detail::group1< detail::group6<T1,T2,T3,T4,T5, Var const&> >
group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5, Var const& var)
{
return detail::group1< detail::group6<T1,T2,T3,T4,T5, Var const&> >
( detail::group6<T1,T2,T3,T4,T5, Var const&>
(a1,a2,a3,a4,a5, var)
);
}
template <class T1,class T2,class T3,class T4,class T5,class T6, class Var>
inline
detail::group1< detail::group7<T1,T2,T3,T4,T5,T6, Var const&> >
group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6, Var const& var)
{
return detail::group1< detail::group7<T1,T2,T3,T4,T5,T6, Var const&> >
( detail::group7<T1,T2,T3,T4,T5,T6, Var const&>
(a1,a2,a3,a4,a5,a6, var)
);
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7, class Var>
inline
detail::group1< detail::group8<T1,T2,T3,T4,T5,T6,T7, Var const&> >
group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7, Var const& var)
{
return detail::group1< detail::group8<T1,T2,T3,T4,T5,T6,T7, Var const&> >
( detail::group8<T1,T2,T3,T4,T5,T6,T7, Var const&>
(a1,a2,a3,a4,a5,a6,a7, var)
);
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8, class Var>
inline
detail::group1< detail::group9<T1,T2,T3,T4,T5,T6,T7,T8, Var const&> >
group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8, Var const& var)
{
return detail::group1< detail::group9<T1,T2,T3,T4,T5,T6,T7,T8, Var const&> >
( detail::group9<T1,T2,T3,T4,T5,T6,T7,T8, Var const&>
(a1,a2,a3,a4,a5,a6,a7,a8, var)
);
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9, class Var>
inline
detail::group1< detail::group10<T1,T2,T3,T4,T5,T6,T7,T8,T9, Var const&> >
group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8,T9 a9, Var const& var)
{
return detail::group1< detail::group10<T1,T2,T3,T4,T5,T6,T7,T8,T9, Var const&> >
( detail::group10<T1,T2,T3,T4,T5,T6,T7,T8,T9, Var const&>
(a1,a2,a3,a4,a5,a6,a7,a8,a9, var)
);
}
#ifndef BOOST_NO_OVERLOAD_FOR_NON_CONST
template <class T1, class Var>
inline
detail::group1< detail::group2<T1, Var&> >
group(T1 a1, Var& var)
{
return detail::group1< detail::group2<T1, Var&> >
( detail::group2<T1, Var&>
(a1, var)
);
}
template <class T1,class T2, class Var>
inline
detail::group1< detail::group3<T1,T2, Var&> >
group(T1 a1,T2 a2, Var& var)
{
return detail::group1< detail::group3<T1,T2, Var&> >
( detail::group3<T1,T2, Var&>
(a1,a2, var)
);
}
template <class T1,class T2,class T3, class Var>
inline
detail::group1< detail::group4<T1,T2,T3, Var&> >
group(T1 a1,T2 a2,T3 a3, Var& var)
{
return detail::group1< detail::group4<T1,T2,T3, Var&> >
( detail::group4<T1,T2,T3, Var&>
(a1,a2,a3, var)
);
}
template <class T1,class T2,class T3,class T4, class Var>
inline
detail::group1< detail::group5<T1,T2,T3,T4, Var&> >
group(T1 a1,T2 a2,T3 a3,T4 a4, Var& var)
{
return detail::group1< detail::group5<T1,T2,T3,T4, Var&> >
( detail::group5<T1,T2,T3,T4, Var&>
(a1,a2,a3,a4, var)
);
}
template <class T1,class T2,class T3,class T4,class T5, class Var>
inline
detail::group1< detail::group6<T1,T2,T3,T4,T5, Var&> >
group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5, Var& var)
{
return detail::group1< detail::group6<T1,T2,T3,T4,T5, Var&> >
( detail::group6<T1,T2,T3,T4,T5, Var&>
(a1,a2,a3,a4,a5, var)
);
}
template <class T1,class T2,class T3,class T4,class T5,class T6, class Var>
inline
detail::group1< detail::group7<T1,T2,T3,T4,T5,T6, Var&> >
group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6, Var& var)
{
return detail::group1< detail::group7<T1,T2,T3,T4,T5,T6, Var&> >
( detail::group7<T1,T2,T3,T4,T5,T6, Var&>
(a1,a2,a3,a4,a5,a6, var)
);
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7, class Var>
inline
detail::group1< detail::group8<T1,T2,T3,T4,T5,T6,T7, Var&> >
group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7, Var& var)
{
return detail::group1< detail::group8<T1,T2,T3,T4,T5,T6,T7, Var&> >
( detail::group8<T1,T2,T3,T4,T5,T6,T7, Var&>
(a1,a2,a3,a4,a5,a6,a7, var)
);
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8, class Var>
inline
detail::group1< detail::group9<T1,T2,T3,T4,T5,T6,T7,T8, Var&> >
group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8, Var& var)
{
return detail::group1< detail::group9<T1,T2,T3,T4,T5,T6,T7,T8, Var&> >
( detail::group9<T1,T2,T3,T4,T5,T6,T7,T8, Var&>
(a1,a2,a3,a4,a5,a6,a7,a8, var)
);
}
template <class T1,class T2,class T3,class T4,class T5,class T6,class T7,class T8,class T9, class Var>
inline
detail::group1< detail::group10<T1,T2,T3,T4,T5,T6,T7,T8,T9, Var&> >
group(T1 a1,T2 a2,T3 a3,T4 a4,T5 a5,T6 a6,T7 a7,T8 a8,T9 a9, Var& var)
{
return detail::group1< detail::group10<T1,T2,T3,T4,T5,T6,T7,T8,T9, Var&> >
( detail::group10<T1,T2,T3,T4,T5,T6,T7,T8,T9, Var&>
(a1,a2,a3,a4,a5,a6,a7,a8,a9, var)
);
}
#endif //end- #ifndef BOOST_NO_OVERLOAD_FOR_NON_CONST
} // namespace io
} // namespace boost
#endif // BOOST_FORMAT_GROUP_HPP

View File

@ -0,0 +1,167 @@
// -*- C++ -*-
// Boost general library 'format' ---------------------------
// See http://www.boost.org for updates, documentation, and revision history.
// (C) Samuel Krempp 2001
// krempp@crans.ens-cachan.fr
// Permission to copy, use, modify, sell and
// distribute this software is granted provided this copyright notice appears
// in all copies. This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
// ideas taken from Rüdiger Loos's format class
// and Karl Nelson's ofstream
// ----------------------------------------------------------------------------
// internals.hpp : internal structs. included by format.hpp
// stream_format_state, and format_item
// ----------------------------------------------------------------------------
#ifndef BOOST_FORMAT_INTERNALS_HPP
#define BOOST_FORMAT_INTERNALS_HPP
#include <string>
#include <sstream>
namespace boost {
namespace io {
namespace detail {
// --------------
// set of params that define the format state of a stream
struct stream_format_state
{
typedef std::ios basic_ios;
std::streamsize width_;
std::streamsize precision_;
char fill_;
std::ios::fmtflags flags_;
stream_format_state() : width_(-1), precision_(-1), fill_(0), flags_(std::ios::dec) {}
stream_format_state(basic_ios& os) {set_by_stream(os); }
void apply_on(basic_ios & os) const; //- applies format_state to the stream
template<class T> void apply_manip(T manipulator) //- modifies state by applying manipulator.
{ apply_manip_body<T>( *this, manipulator) ; }
void reset(); //- sets to default state.
void set_by_stream(const basic_ios& os); //- sets to os's state.
};
// --------------
// format_item : stores all parameters that can be defined by directives in the format-string
struct format_item
{
enum pad_values { zeropad = 1, spacepad =2, centered=4, tabulation = 8 };
enum arg_values { argN_no_posit = -1, // non-positional directive. argN will be set later.
argN_tabulation = -2, // tabulation directive. (no argument read)
argN_ignored = -3 // ignored directive. (no argument read)
};
typedef BOOST_IO_STD ios basic_ios;
typedef detail::stream_format_state stream_format_state;
typedef std::string string_t;
typedef BOOST_IO_STD ostringstream internal_stream_t;
int argN_; //- argument number (starts at 0, eg : %1 => argN=0)
// negative values are used for items that don't process
// an argument
string_t res_; //- result of the formatting of this item
string_t appendix_; //- piece of string between this item and the next
stream_format_state ref_state_;// set by parsing the format_string, is only affected by modify_item
stream_format_state state_; // always same as ref_state, _unless_ modified by manipulators 'group(..)'
// non-stream format-state parameters
signed int truncate_; //- is >=0 for directives like %.5s (take 5 chars from the string)
unsigned int pad_scheme_; //- several possible padding schemes can mix. see pad_values
format_item() : argN_(argN_no_posit), truncate_(-1), pad_scheme_(0) {}
void compute_states(); // sets states according to truncate and pad_scheme.
};
// -----------------------------------------------------------
// Definitions
// -----------------------------------------------------------
// --- stream_format_state:: -------------------------------------------
inline
void stream_format_state::apply_on(basic_ios & os) const
// set the state of this stream according to our params
{
if(width_ != -1)
os.width(width_);
if(precision_ != -1)
os.precision(precision_);
if(fill_ != 0)
os.fill(fill_);
os.flags(flags_);
}
inline
void stream_format_state::set_by_stream(const basic_ios& os)
// set our params according to the state of this stream
{
flags_ = os.flags();
width_ = os.width();
precision_ = os.precision();
fill_ = os.fill();
}
template<class T> inline
void apply_manip_body( stream_format_state& self,
T manipulator)
// modify our params according to the manipulator
{
BOOST_IO_STD stringstream ss;
self.apply_on( ss );
ss << manipulator;
self.set_by_stream( ss );
}
inline
void stream_format_state::reset()
// set our params to standard's default state
{
width_=-1; precision_=-1; fill_=0;
flags_ = std::ios::dec;
}
// --- format_items:: -------------------------------------------
inline
void format_item::compute_states()
// reflect pad_scheme_ on state_ and ref_state_
// because some pad_schemes has complex consequences on several state params.
{
if(pad_scheme_ & zeropad)
{
if(ref_state_.flags_ & std::ios::left)
{
pad_scheme_ = pad_scheme_ & (~zeropad); // ignore zeropad in left alignment
}
else
{
ref_state_.fill_='0';
ref_state_.flags_ |= std::ios::internal;
}
}
state_ = ref_state_;
}
} } } // namespaces boost :: io :: detail
#endif // BOOST_FORMAT_INTERNALS_HPP

View File

@ -0,0 +1,65 @@
// -*- C++ -*-
// Boost general library 'format' ---------------------------
// See http://www.boost.org for updates, documentation, and revision history.
// (C) Samuel Krempp 2001
// krempp@crans.ens-cachan.fr
// Permission to copy, use, modify, sell and
// distribute this software is granted provided this copyright notice appears
// in all copies. This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
// ideas taken from Rüdiger Loos's format class
// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)
// ------------------------------------------------------------------------------
// internals_fwd.hpp : forward declarations, for internal headers
// ------------------------------------------------------------------------------
#ifndef BOOST_FORMAT_INTERNAL_FWD_HPP
#define BOOST_FORMAT_INTERNAL_FWD_HPP
#include "boost/format/format_fwd.hpp"
namespace boost {
namespace io {
namespace detail {
struct stream_format_state;
struct format_item;
}
namespace detail {
// these functions were intended as methods,
// but MSVC have problems with template member functions :
// defined in format_implementation.hpp :
template<class T>
basic_format& modify_item_body( basic_format& self,
int itemN, const T& manipulator);
template<class T>
basic_format& bind_arg_body( basic_format& self,
int argN, const T& val);
template<class T>
void apply_manip_body( stream_format_state& self,
T manipulator);
// argument feeding (defined in feed_args.hpp ) :
template<class T>
void distribute(basic_format& self, T x);
template<class T>
basic_format& feed(basic_format& self, T x);
} // namespace detail
} // namespace io
} // namespace boost
#endif // BOOST_FORMAT_INTERNAL_FWD_HPP

View File

@ -0,0 +1,48 @@
// -*- C++ -*-
// Boost general library 'format' ---------------------------
// See http://www.boost.org for updates, documentation, and revision history.
// (C) Samuel Krempp 2001
// krempp@crans.ens-cachan.fr
// Permission to copy, use, modify, sell and
// distribute this software is granted provided this copyright notice appears
// in all copies. This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
// ideas taken from Rüdiger Loos's format class
// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)
// ------------------------------------------------------------------------------
// macros_default.hpp : configuration for the format library
// provides default values for the stl workaround macros
// ------------------------------------------------------------------------------
#ifndef BOOST_FORMAT_MACROS_DEFAULT_HPP
#define BOOST_FORMAT_MACROS_DEFAULT_HPP
// *** This should go to "boost/config/suffix.hpp".
#ifndef BOOST_IO_STD
# define BOOST_IO_STD std::
#endif
// **** Workaround for io streams, stlport and msvc.
#ifdef BOOST_IO_NEEDS_USING_DECLARATION
namespace boost {
using std::char_traits;
using std::basic_ostream;
using std::basic_ostringstream;
namespace io {
using std::basic_ostream;
namespace detail {
using std::basic_ios;
using std::basic_ostream;
using std::basic_ostringstream;
}
}
}
#endif
// ------------------------------------------------------------------------------
#endif // BOOST_FORMAT_MACROS_DEFAULT_HPP

454
nix/boost/format/parsing.cc Normal file
View File

@ -0,0 +1,454 @@
// -*- C++ -*-
// Boost general library 'format' ---------------------------
// See http://www.boost.org for updates, documentation, and revision history.
// (C) Samuel Krempp 2001
// krempp@crans.ens-cachan.fr
// Permission to copy, use, modify, sell and
// distribute this software is granted provided this copyright notice appears
// in all copies. This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
// ideas taken from Rudiger Loos's format class
// and Karl Nelson's ofstream (also took its parsing code as basis for printf parsing)
// ------------------------------------------------------------------------------
// parsing.hpp : implementation of the parsing member functions
// ( parse, parse_printf_directive)
// ------------------------------------------------------------------------------
#ifndef BOOST_FORMAT_PARSING_HPP
#define BOOST_FORMAT_PARSING_HPP
#include <boost/format.hpp>
#include <boost/throw_exception.hpp>
#include <boost/assert.hpp>
namespace boost {
namespace io {
namespace detail {
template<class Stream> inline
bool wrap_isdigit(char c, Stream &os)
{
#ifndef BOOST_NO_LOCALE_ISIDIGIT
return std::isdigit(c, os.rdbuf()->getloc() );
# else
using namespace std;
return isdigit(c);
#endif
} //end- wrap_isdigit(..)
template<class Res> inline
Res str2int(const std::string& s,
std::string::size_type start,
BOOST_IO_STD ios &os,
const Res = Res(0) )
// Input : char string, with starting index
// a basic_ios& merely to call its widen/narrow member function in the desired locale.
// Effects : reads s[start:] and converts digits into an integral n, of type Res
// Returns : n
{
Res n = 0;
while(start<s.size() && wrap_isdigit(s[start], os) ) {
char cur_ch = s[start];
BOOST_ASSERT(cur_ch != 0 ); // since we called isdigit, this should not happen.
n *= 10;
n += cur_ch - '0'; // 22.2.1.1.2 of the C++ standard
++start;
}
return n;
}
void skip_asterisk(const std::string & buf,
std::string::size_type * pos_p,
BOOST_IO_STD ios &os)
// skip printf's "asterisk-fields" directives in the format-string buf
// Input : char string, with starting index *pos_p
// a basic_ios& merely to call its widen/narrow member function in the desired locale.
// Effects : advance *pos_p by skipping printf's asterisk fields.
// Returns : nothing
{
using namespace std;
BOOST_ASSERT( pos_p != 0);
if(*pos_p >= buf.size() ) return;
if(buf[ *pos_p]=='*') {
++ (*pos_p);
while (*pos_p < buf.size() && wrap_isdigit(buf[*pos_p],os)) ++(*pos_p);
if(buf[*pos_p]=='$') ++(*pos_p);
}
}
inline void maybe_throw_exception( unsigned char exceptions)
// auxiliary func called by parse_printf_directive
// for centralising error handling
// it either throws if user sets the corresponding flag, or does nothing.
{
if(exceptions & io::bad_format_string_bit)
boost::throw_exception(io::bad_format_string());
}
bool parse_printf_directive(const std::string & buf,
std::string::size_type * pos_p,
detail::format_item * fpar,
BOOST_IO_STD ios &os,
unsigned char exceptions)
// Input : a 'printf-directive' in the format-string, starting at buf[ *pos_p ]
// a basic_ios& merely to call its widen/narrow member function in the desired locale.
// a bitset'excpetions' telling whether to throw exceptions on errors.
// Returns : true if parse somehow succeeded (possibly ignoring errors if exceptions disabled)
// false if it failed so bad that the directive should be printed verbatim
// Effects : - *pos_p is incremented so that buf[*pos_p] is the first char after the directive
// - *fpar is set with the parameters read in the directive
{
typedef format_item format_item_t;
BOOST_ASSERT( pos_p != 0);
std::string::size_type &i1 = *pos_p,
i0;
fpar->argN_ = format_item_t::argN_no_posit; // if no positional-directive
bool in_brackets=false;
if(buf[i1]=='|')
{
in_brackets=true;
if( ++i1 >= buf.size() ) {
maybe_throw_exception(exceptions);
return false;
}
}
// the flag '0' would be picked as a digit for argument order, but here it's a flag :
if(buf[i1]=='0')
goto parse_flags;
// handle argument order (%2$d) or possibly width specification: %2d
i0 = i1; // save position before digits
while (i1 < buf.size() && wrap_isdigit(buf[i1], os))
++i1;
if (i1!=i0)
{
if( i1 >= buf.size() ) {
maybe_throw_exception(exceptions);
return false;
}
int n=str2int(buf,i0, os, int(0) );
// %N% case : this is already the end of the directive
if( buf[i1] == '%' )
{
fpar->argN_ = n-1;
++i1;
if( in_brackets)
maybe_throw_exception(exceptions);
// but don't return. maybe "%" was used in lieu of '$', so we go on.
else return true;
}
if ( buf[i1]=='$' )
{
fpar->argN_ = n-1;
++i1;
}
else
{
// non-positionnal directive
fpar->ref_state_.width_ = n;
fpar->argN_ = format_item_t::argN_no_posit;
goto parse_precision;
}
}
parse_flags:
// handle flags
while ( i1 <buf.size()) // as long as char is one of + - = # 0 l h or ' '
{
// misc switches
switch (buf[i1])
{
case '\'' : break; // no effect yet. (painful to implement)
case 'l':
case 'h': // short/long modifier : for printf-comaptibility (no action needed)
break;
case '-':
fpar->ref_state_.flags_ |= std::ios::left;
break;
case '=':
fpar->pad_scheme_ |= format_item_t::centered;
break;
case ' ':
fpar->pad_scheme_ |= format_item_t::spacepad;
break;
case '+':
fpar->ref_state_.flags_ |= std::ios::showpos;
break;
case '0':
fpar->pad_scheme_ |= format_item_t::zeropad;
// need to know alignment before really setting flags,
// so just add 'zeropad' flag for now, it will be processed later.
break;
case '#':
fpar->ref_state_.flags_ |= std::ios::showpoint | std::ios::showbase;
break;
default:
goto parse_width;
}
++i1;
} // loop on flag.
if( i1>=buf.size()) {
maybe_throw_exception(exceptions);
return true;
}
parse_width:
// handle width spec
skip_asterisk(buf, &i1, os); // skips 'asterisk fields' : *, or *N$
i0 = i1; // save position before digits
while (i1<buf.size() && wrap_isdigit(buf[i1], os))
i1++;
if (i1!=i0)
{ fpar->ref_state_.width_ = str2int( buf,i0, os, std::streamsize(0) ); }
parse_precision:
if( i1>=buf.size()) {
maybe_throw_exception(exceptions);
return true;
}
// handle precision spec
if (buf[i1]=='.')
{
++i1;
skip_asterisk(buf, &i1, os);
i0 = i1; // save position before digits
while (i1<buf.size() && wrap_isdigit(buf[i1], os))
++i1;
if(i1==i0)
fpar->ref_state_.precision_ = 0;
else
fpar->ref_state_.precision_ = str2int(buf,i0, os, std::streamsize(0) );
}
// handle formatting-type flags :
while( i1<buf.size() &&
( buf[i1]=='l' || buf[i1]=='L' || buf[i1]=='h') )
++i1;
if( i1>=buf.size()) {
maybe_throw_exception(exceptions);
return true;
}
if( in_brackets && buf[i1]=='|' )
{
++i1;
return true;
}
switch (buf[i1])
{
case 'X':
fpar->ref_state_.flags_ |= std::ios::uppercase;
case 'p': // pointer => set hex.
case 'x':
fpar->ref_state_.flags_ &= ~std::ios::basefield;
fpar->ref_state_.flags_ |= std::ios::hex;
break;
case 'o':
fpar->ref_state_.flags_ &= ~std::ios::basefield;
fpar->ref_state_.flags_ |= std::ios::oct;
break;
case 'E':
fpar->ref_state_.flags_ |= std::ios::uppercase;
case 'e':
fpar->ref_state_.flags_ &= ~std::ios::floatfield;
fpar->ref_state_.flags_ |= std::ios::scientific;
fpar->ref_state_.flags_ &= ~std::ios::basefield;
fpar->ref_state_.flags_ |= std::ios::dec;
break;
case 'f':
fpar->ref_state_.flags_ &= ~std::ios::floatfield;
fpar->ref_state_.flags_ |= std::ios::fixed;
case 'u':
case 'd':
case 'i':
fpar->ref_state_.flags_ &= ~std::ios::basefield;
fpar->ref_state_.flags_ |= std::ios::dec;
break;
case 'T':
++i1;
if( i1 >= buf.size())
maybe_throw_exception(exceptions);
else
fpar->ref_state_.fill_ = buf[i1];
fpar->pad_scheme_ |= format_item_t::tabulation;
fpar->argN_ = format_item_t::argN_tabulation;
break;
case 't':
fpar->ref_state_.fill_ = ' ';
fpar->pad_scheme_ |= format_item_t::tabulation;
fpar->argN_ = format_item_t::argN_tabulation;
break;
case 'G':
fpar->ref_state_.flags_ |= std::ios::uppercase;
break;
case 'g': // 'g' conversion is default for floats.
fpar->ref_state_.flags_ &= ~std::ios::basefield;
fpar->ref_state_.flags_ |= std::ios::dec;
// CLEAR all floatield flags, so stream will CHOOSE
fpar->ref_state_.flags_ &= ~std::ios::floatfield;
break;
case 'C':
case 'c':
fpar->truncate_ = 1;
break;
case 'S':
case 's':
fpar->truncate_ = fpar->ref_state_.precision_;
fpar->ref_state_.precision_ = -1;
break;
case 'n' :
fpar->argN_ = format_item_t::argN_ignored;
break;
default:
maybe_throw_exception(exceptions);
}
++i1;
if( in_brackets )
{
if( i1<buf.size() && buf[i1]=='|' )
{
++i1;
return true;
}
else maybe_throw_exception(exceptions);
}
return true;
}
} // detail namespace
} // io namespace
// -----------------------------------------------
// format :: parse(..)
void basic_format::parse(const string_t & buf)
// parse the format-string
{
using namespace std;
const char arg_mark = '%';
bool ordered_args=true;
int max_argN=-1;
string_t::size_type i1=0;
int num_items=0;
// A: find upper_bound on num_items and allocates arrays
i1=0;
while( (i1=buf.find(arg_mark,i1)) != string::npos )
{
if( i1+1 >= buf.size() ) {
if(exceptions() & io::bad_format_string_bit)
boost::throw_exception(io::bad_format_string()); // must not end in "bla bla %"
else break; // stop there, ignore last '%'
}
if(buf[i1+1] == buf[i1] ) { i1+=2; continue; } // escaped "%%" / "##"
++i1;
// in case of %N% directives, dont count it double (wastes allocations..) :
while(i1 < buf.size() && io::detail::wrap_isdigit(buf[i1],oss_)) ++i1;
if( i1 < buf.size() && buf[i1] == arg_mark ) ++ i1;
++num_items;
}
items_.assign( num_items, format_item_t() );
// B: Now the real parsing of the format string :
num_items=0;
i1 = 0;
string_t::size_type i0 = i1;
bool special_things=false;
int cur_it=0;
while( (i1=buf.find(arg_mark,i1)) != string::npos )
{
string_t & piece = (cur_it==0) ? prefix_ : items_[cur_it-1].appendix_;
if( buf[i1+1] == buf[i1] ) // escaped mark, '%%'
{
piece += buf.substr(i0, i1-i0) + buf[i1];
i1+=2; i0=i1;
continue;
}
BOOST_ASSERT( static_cast<unsigned int>(cur_it) < items_.size() || cur_it==0);
if(i1!=i0) piece += buf.substr(i0, i1-i0);
++i1;
bool parse_ok;
parse_ok = io::detail::parse_printf_directive(buf, &i1, &items_[cur_it], oss_, exceptions());
if( ! parse_ok ) continue; // the directive will be printed verbatim
i0=i1;
items_[cur_it].compute_states(); // process complex options, like zeropad, into stream params.
int argN=items_[cur_it].argN_;
if(argN == format_item_t::argN_ignored)
continue;
if(argN ==format_item_t::argN_no_posit)
ordered_args=false;
else if(argN == format_item_t::argN_tabulation) special_things=true;
else if(argN > max_argN) max_argN = argN;
++num_items;
++cur_it;
} // loop on %'s
BOOST_ASSERT(cur_it == num_items);
// store the final piece of string
string_t & piece = (cur_it==0) ? prefix_ : items_[cur_it-1].appendix_;
piece += buf.substr(i0);
if( !ordered_args)
{
if(max_argN >= 0 ) // dont mix positional with non-positionnal directives
{
if(exceptions() & io::bad_format_string_bit)
boost::throw_exception(io::bad_format_string());
// else do nothing. => positionnal arguments are processed as non-positionnal
}
// set things like it would have been with positional directives :
int non_ordered_items = 0;
for(int i=0; i< num_items; ++i)
if(items_[i].argN_ == format_item_t::argN_no_posit)
{
items_[i].argN_ = non_ordered_items;
++non_ordered_items;
}
max_argN = non_ordered_items-1;
}
// C: set some member data :
items_.resize(num_items);
if(special_things) style_ |= special_needs;
num_args_ = max_argN + 1;
if(ordered_args) style_ |= ordered;
else style_ &= ~ordered;
}
} // namespace boost
#endif // BOOST_FORMAT_PARSING_HPP

View File

@ -0,0 +1,47 @@
#ifndef BOOST_THROW_EXCEPTION_HPP_INCLUDED
#define BOOST_THROW_EXCEPTION_HPP_INCLUDED
// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
//
// boost/throw_exception.hpp
//
// Copyright (c) 2002 Peter Dimov and Multi Media Ltd.
//
// Permission to copy, use, modify, sell and distribute this software
// is granted provided this copyright notice appears in all copies.
// This software is provided "as is" without express or implied
// warranty, and with no claim as to its suitability for any purpose.
//
// http://www.boost.org/libs/utility/throw_exception.html
//
//#include <boost/config.hpp>
#ifdef BOOST_NO_EXCEPTIONS
# include <exception>
#endif
namespace boost
{
#ifdef BOOST_NO_EXCEPTIONS
void throw_exception(std::exception const & e); // user defined
#else
template<class E> void throw_exception(E const & e)
{
throw e;
}
#endif
} // namespace boost
#endif // #ifndef BOOST_THROW_EXCEPTION_HPP_INCLUDED

3351
nix/libstore/build.cc Normal file

File diff suppressed because it is too large Load Diff

278
nix/libstore/derivations.cc Normal file
View File

@ -0,0 +1,278 @@
#include "derivations.hh"
#include "store-api.hh"
#include "globals.hh"
#include "util.hh"
#include "misc.hh"
namespace nix {
void DerivationOutput::parseHashInfo(bool & recursive, HashType & hashType, Hash & hash) const
{
recursive = false;
string algo = hashAlgo;
if (string(algo, 0, 2) == "r:") {
recursive = true;
algo = string(algo, 2);
}
hashType = parseHashType(algo);
if (hashType == htUnknown)
throw Error(format("unknown hash algorithm `%1%'") % algo);
hash = parseHash(hashType, this->hash);
}
Path writeDerivation(StoreAPI & store,
const Derivation & drv, const string & name, bool repair)
{
PathSet references;
references.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs)
references.insert(i->first);
/* Note that the outputs of a derivation are *not* references
(that can be missing (of course) and should not necessarily be
held during a garbage collection). */
string suffix = name + drvExtension;
string contents = unparseDerivation(drv);
return settings.readOnlyMode
? computeStorePathForText(suffix, contents, references)
: store.addTextToStore(suffix, contents, references, repair);
}
static Path parsePath(std::istream & str)
{
string s = parseString(str);
if (s.size() == 0 || s[0] != '/')
throw Error(format("bad path `%1%' in derivation") % s);
return s;
}
static StringSet parseStrings(std::istream & str, bool arePaths)
{
StringSet res;
while (!endOfList(str))
res.insert(arePaths ? parsePath(str) : parseString(str));
return res;
}
Derivation parseDerivation(const string & s)
{
Derivation drv;
std::istringstream str(s);
expect(str, "Derive([");
/* Parse the list of outputs. */
while (!endOfList(str)) {
DerivationOutput out;
expect(str, "("); string id = parseString(str);
expect(str, ","); out.path = parsePath(str);
expect(str, ","); out.hashAlgo = parseString(str);
expect(str, ","); out.hash = parseString(str);
expect(str, ")");
drv.outputs[id] = out;
}
/* Parse the list of input derivations. */
expect(str, ",[");
while (!endOfList(str)) {
expect(str, "(");
Path drvPath = parsePath(str);
expect(str, ",[");
drv.inputDrvs[drvPath] = parseStrings(str, false);
expect(str, ")");
}
expect(str, ",["); drv.inputSrcs = parseStrings(str, true);
expect(str, ","); drv.platform = parseString(str);
expect(str, ","); drv.builder = parseString(str);
/* Parse the builder arguments. */
expect(str, ",[");
while (!endOfList(str))
drv.args.push_back(parseString(str));
/* Parse the environment variables. */
expect(str, ",[");
while (!endOfList(str)) {
expect(str, "("); string name = parseString(str);
expect(str, ","); string value = parseString(str);
expect(str, ")");
drv.env[name] = value;
}
expect(str, ")");
return drv;
}
static void printString(string & res, const string & s)
{
res += '"';
for (const char * i = s.c_str(); *i; i++)
if (*i == '\"' || *i == '\\') { res += "\\"; res += *i; }
else if (*i == '\n') res += "\\n";
else if (*i == '\r') res += "\\r";
else if (*i == '\t') res += "\\t";
else res += *i;
res += '"';
}
template<class ForwardIterator>
static void printStrings(string & res, ForwardIterator i, ForwardIterator j)
{
res += '[';
bool first = true;
for ( ; i != j; ++i) {
if (first) first = false; else res += ',';
printString(res, *i);
}
res += ']';
}
string unparseDerivation(const Derivation & drv)
{
string s;
s.reserve(65536);
s += "Derive([";
bool first = true;
foreach (DerivationOutputs::const_iterator, i, drv.outputs) {
if (first) first = false; else s += ',';
s += '('; printString(s, i->first);
s += ','; printString(s, i->second.path);
s += ','; printString(s, i->second.hashAlgo);
s += ','; printString(s, i->second.hash);
s += ')';
}
s += "],[";
first = true;
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) {
if (first) first = false; else s += ',';
s += '('; printString(s, i->first);
s += ','; printStrings(s, i->second.begin(), i->second.end());
s += ')';
}
s += "],";
printStrings(s, drv.inputSrcs.begin(), drv.inputSrcs.end());
s += ','; printString(s, drv.platform);
s += ','; printString(s, drv.builder);
s += ','; printStrings(s, drv.args.begin(), drv.args.end());
s += ",[";
first = true;
foreach (StringPairs::const_iterator, i, drv.env) {
if (first) first = false; else s += ',';
s += '('; printString(s, i->first);
s += ','; printString(s, i->second);
s += ')';
}
s += "])";
return s;
}
bool isDerivation(const string & fileName)
{
return hasSuffix(fileName, drvExtension);
}
bool isFixedOutputDrv(const Derivation & drv)
{
return drv.outputs.size() == 1 &&
drv.outputs.begin()->first == "out" &&
drv.outputs.begin()->second.hash != "";
}
DrvHashes drvHashes;
/* Returns the hash of a derivation modulo fixed-output
subderivations. A fixed-output derivation is a derivation with one
output (`out') for which an expected hash and hash algorithm are
specified (using the `outputHash' and `outputHashAlgo'
attributes). We don't want changes to such derivations to
propagate upwards through the dependency graph, changing output
paths everywhere.
For instance, if we change the url in a call to the `fetchurl'
function, we do not want to rebuild everything depending on it
(after all, (the hash of) the file being downloaded is unchanged).
So the *output paths* should not change. On the other hand, the
*derivation paths* should change to reflect the new dependency
graph.
That's what this function does: it returns a hash which is just the
hash of the derivation ATerm, except that any input derivation
paths have been replaced by the result of a recursive call to this
function, and that for fixed-output derivations we return a hash of
its output path. */
Hash hashDerivationModulo(StoreAPI & store, Derivation drv)
{
/* Return a fixed hash for fixed-output derivations. */
if (isFixedOutputDrv(drv)) {
DerivationOutputs::const_iterator i = drv.outputs.begin();
return hashString(htSHA256, "fixed:out:"
+ i->second.hashAlgo + ":"
+ i->second.hash + ":"
+ i->second.path);
}
/* For other derivations, replace the inputs paths with recursive
calls to this function.*/
DerivationInputs inputs2;
foreach (DerivationInputs::const_iterator, i, drv.inputDrvs) {
Hash h = drvHashes[i->first];
if (h.type == htUnknown) {
assert(store.isValidPath(i->first));
Derivation drv2 = parseDerivation(readFile(i->first));
h = hashDerivationModulo(store, drv2);
drvHashes[i->first] = h;
}
inputs2[printHash(h)] = i->second;
}
drv.inputDrvs = inputs2;
return hashString(htSHA256, unparseDerivation(drv));
}
DrvPathWithOutputs parseDrvPathWithOutputs(const string & s)
{
size_t n = s.find("!");
return n == s.npos
? DrvPathWithOutputs(s, std::set<string>())
: DrvPathWithOutputs(string(s, 0, n), tokenizeString<std::set<string> >(string(s, n + 1), ","));
}
Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outputs)
{
return outputs.empty()
? drvPath
: drvPath + "!" + concatStringsSep(",", outputs);
}
bool wantOutput(const string & output, const std::set<string> & wanted)
{
return wanted.empty() || wanted.find(output) != wanted.end();
}
}

View File

@ -0,0 +1,93 @@
#pragma once
#include "types.hh"
#include "hash.hh"
#include <map>
namespace nix {
/* Extension of derivations in the Nix store. */
const string drvExtension = ".drv";
/* Abstract syntax of derivations. */
struct DerivationOutput
{
Path path;
string hashAlgo; /* hash used for expected hash computation */
string hash; /* expected hash, may be null */
DerivationOutput()
{
}
DerivationOutput(Path path, string hashAlgo, string hash)
{
this->path = path;
this->hashAlgo = hashAlgo;
this->hash = hash;
}
void parseHashInfo(bool & recursive, HashType & hashType, Hash & hash) const;
};
typedef std::map<string, DerivationOutput> DerivationOutputs;
/* For inputs that are sub-derivations, we specify exactly which
output IDs we are interested in. */
typedef std::map<Path, StringSet> DerivationInputs;
typedef std::map<string, string> StringPairs;
struct Derivation
{
DerivationOutputs outputs; /* keyed on symbolic IDs */
DerivationInputs inputDrvs; /* inputs that are sub-derivations */
PathSet inputSrcs; /* inputs that are sources */
string platform;
Path builder;
Strings args;
StringPairs env;
};
class StoreAPI;
/* Write a derivation to the Nix store, and return its path. */
Path writeDerivation(StoreAPI & store,
const Derivation & drv, const string & name, bool repair = false);
/* Parse a derivation. */
Derivation parseDerivation(const string & s);
/* Print a derivation. */
string unparseDerivation(const Derivation & drv);
/* Check whether a file name ends with the extensions for
derivations. */
bool isDerivation(const string & fileName);
/* Return true iff this is a fixed-output derivation. */
bool isFixedOutputDrv(const Derivation & drv);
Hash hashDerivationModulo(StoreAPI & store, Derivation drv);
/* Memoisation of hashDerivationModulo(). */
typedef std::map<Path, Hash> DrvHashes;
extern DrvHashes drvHashes;
/* Split a string specifying a derivation and a set of outputs
(/nix/store/hash-foo!out1,out2,...) into the derivation path and
the outputs. */
typedef std::pair<string, std::set<string> > DrvPathWithOutputs;
DrvPathWithOutputs parseDrvPathWithOutputs(const string & s);
Path makeDrvPathWithOutputs(const Path & drvPath, const std::set<string> & outputs);
bool wantOutput(const string & output, const std::set<string> & wanted);
}

748
nix/libstore/gc.cc Normal file
View File

@ -0,0 +1,748 @@
#include "globals.hh"
#include "misc.hh"
#include "local-store.hh"
#include <functional>
#include <queue>
#include <algorithm>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
namespace nix {
static string gcLockName = "gc.lock";
static string tempRootsDir = "temproots";
static string gcRootsDir = "gcroots";
/* Acquire the global GC lock. This is used to prevent new Nix
processes from starting after the temporary root files have been
read. To be precise: when they try to create a new temporary root
file, they will block until the garbage collector has finished /
yielded the GC lock. */
int LocalStore::openGCLock(LockType lockType)
{
Path fnGCLock = (format("%1%/%2%")
% settings.nixStateDir % gcLockName).str();
debug(format("acquiring global GC lock `%1%'") % fnGCLock);
AutoCloseFD fdGCLock = open(fnGCLock.c_str(), O_RDWR | O_CREAT, 0600);
if (fdGCLock == -1)
throw SysError(format("opening global GC lock `%1%'") % fnGCLock);
closeOnExec(fdGCLock);
if (!lockFile(fdGCLock, lockType, false)) {
printMsg(lvlError, format("waiting for the big garbage collector lock..."));
lockFile(fdGCLock, lockType, true);
}
/* !!! Restrict read permission on the GC root. Otherwise any
process that can open the file for reading can DoS the
collector. */
return fdGCLock.borrow();
}
static void makeSymlink(const Path & link, const Path & target)
{
/* Create directories up to `gcRoot'. */
createDirs(dirOf(link));
/* Create the new symlink. */
Path tempLink = (format("%1%.tmp-%2%-%3%")
% link % getpid() % rand()).str();
createSymlink(target, tempLink);
/* Atomically replace the old one. */
if (rename(tempLink.c_str(), link.c_str()) == -1)
throw SysError(format("cannot rename `%1%' to `%2%'")
% tempLink % link);
}
void LocalStore::syncWithGC()
{
AutoCloseFD fdGCLock = openGCLock(ltRead);
}
void LocalStore::addIndirectRoot(const Path & path)
{
string hash = printHash32(hashString(htSHA1, path));
Path realRoot = canonPath((format("%1%/%2%/auto/%3%")
% settings.nixStateDir % gcRootsDir % hash).str());
makeSymlink(realRoot, path);
}
Path addPermRoot(StoreAPI & store, const Path & _storePath,
const Path & _gcRoot, bool indirect, bool allowOutsideRootsDir)
{
Path storePath(canonPath(_storePath));
Path gcRoot(canonPath(_gcRoot));
assertStorePath(storePath);
if (isInStore(gcRoot))
throw Error(format(
"creating a garbage collector root (%1%) in the Nix store is forbidden "
"(are you running nix-build inside the store?)") % gcRoot);
if (indirect) {
/* Don't clobber the the link if it already exists and doesn't
point to the Nix store. */
if (pathExists(gcRoot) && (!isLink(gcRoot) || !isInStore(readLink(gcRoot))))
throw Error(format("cannot create symlink `%1%'; already exists") % gcRoot);
makeSymlink(gcRoot, storePath);
store.addIndirectRoot(gcRoot);
}
else {
if (!allowOutsideRootsDir) {
Path rootsDir = canonPath((format("%1%/%2%") % settings.nixStateDir % gcRootsDir).str());
if (string(gcRoot, 0, rootsDir.size() + 1) != rootsDir + "/")
throw Error(format(
"path `%1%' is not a valid garbage collector root; "
"it's not in the directory `%2%'")
% gcRoot % rootsDir);
}
makeSymlink(gcRoot, storePath);
}
/* Check that the root can be found by the garbage collector.
!!! This can be very slow on machines that have many roots.
Instead of reading all the roots, it would be more efficient to
check if the root is in a directory in or linked from the
gcroots directory. */
if (settings.checkRootReachability) {
Roots roots = store.findRoots();
if (roots.find(gcRoot) == roots.end())
printMsg(lvlError,
format(
"warning: `%1%' is not in a directory where the garbage collector looks for roots; "
"therefore, `%2%' might be removed by the garbage collector")
% gcRoot % storePath);
}
/* Grab the global GC root, causing us to block while a GC is in
progress. This prevents the set of permanent roots from
increasing while a GC is in progress. */
store.syncWithGC();
return gcRoot;
}
/* The file to which we write our temporary roots. */
static Path fnTempRoots;
static AutoCloseFD fdTempRoots;
void LocalStore::addTempRoot(const Path & path)
{
/* Create the temporary roots file for this process. */
if (fdTempRoots == -1) {
while (1) {
Path dir = (format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str();
createDirs(dir);
fnTempRoots = (format("%1%/%2%")
% dir % getpid()).str();
AutoCloseFD fdGCLock = openGCLock(ltRead);
if (pathExists(fnTempRoots))
/* It *must* be stale, since there can be no two
processes with the same pid. */
unlink(fnTempRoots.c_str());
fdTempRoots = openLockFile(fnTempRoots, true);
fdGCLock.close();
debug(format("acquiring read lock on `%1%'") % fnTempRoots);
lockFile(fdTempRoots, ltRead, true);
/* Check whether the garbage collector didn't get in our
way. */
struct stat st;
if (fstat(fdTempRoots, &st) == -1)
throw SysError(format("statting `%1%'") % fnTempRoots);
if (st.st_size == 0) break;
/* The garbage collector deleted this file before we could
get a lock. (It won't delete the file after we get a
lock.) Try again. */
}
}
/* Upgrade the lock to a write lock. This will cause us to block
if the garbage collector is holding our lock. */
debug(format("acquiring write lock on `%1%'") % fnTempRoots);
lockFile(fdTempRoots, ltWrite, true);
string s = path + '\0';
writeFull(fdTempRoots, (const unsigned char *) s.data(), s.size());
/* Downgrade to a read lock. */
debug(format("downgrading to read lock on `%1%'") % fnTempRoots);
lockFile(fdTempRoots, ltRead, true);
}
void removeTempRoots()
{
if (fdTempRoots != -1) {
fdTempRoots.close();
unlink(fnTempRoots.c_str());
}
}
/* Automatically clean up the temporary roots file when we exit. */
struct RemoveTempRoots
{
~RemoveTempRoots()
{
removeTempRoots();
}
};
static RemoveTempRoots autoRemoveTempRoots __attribute__((unused));
typedef std::shared_ptr<AutoCloseFD> FDPtr;
typedef list<FDPtr> FDs;
static void readTempRoots(PathSet & tempRoots, FDs & fds)
{
/* Read the `temproots' directory for per-process temporary root
files. */
Strings tempRootFiles = readDirectory(
(format("%1%/%2%") % settings.nixStateDir % tempRootsDir).str());
foreach (Strings::iterator, i, tempRootFiles) {
Path path = (format("%1%/%2%/%3%") % settings.nixStateDir % tempRootsDir % *i).str();
debug(format("reading temporary root file `%1%'") % path);
FDPtr fd(new AutoCloseFD(open(path.c_str(), O_RDWR, 0666)));
if (*fd == -1) {
/* It's okay if the file has disappeared. */
if (errno == ENOENT) continue;
throw SysError(format("opening temporary roots file `%1%'") % path);
}
/* This should work, but doesn't, for some reason. */
//FDPtr fd(new AutoCloseFD(openLockFile(path, false)));
//if (*fd == -1) continue;
/* Try to acquire a write lock without blocking. This can
only succeed if the owning process has died. In that case
we don't care about its temporary roots. */
if (lockFile(*fd, ltWrite, false)) {
printMsg(lvlError, format("removing stale temporary roots file `%1%'") % path);
unlink(path.c_str());
writeFull(*fd, (const unsigned char *) "d", 1);
continue;
}
/* Acquire a read lock. This will prevent the owning process
from upgrading to a write lock, therefore it will block in
addTempRoot(). */
debug(format("waiting for read lock on `%1%'") % path);
lockFile(*fd, ltRead, true);
/* Read the entire file. */
string contents = readFile(*fd);
/* Extract the roots. */
string::size_type pos = 0, end;
while ((end = contents.find((char) 0, pos)) != string::npos) {
Path root(contents, pos, end - pos);
debug(format("got temporary root `%1%'") % root);
assertStorePath(root);
tempRoots.insert(root);
pos = end + 1;
}
fds.push_back(fd); /* keep open */
}
}
static void foundRoot(StoreAPI & store,
const Path & path, const Path & target, Roots & roots)
{
Path storePath = toStorePath(target);
if (store.isValidPath(storePath))
roots[path] = storePath;
else
printMsg(lvlInfo, format("skipping invalid root from `%1%' to `%2%'") % path % storePath);
}
static void findRoots(StoreAPI & store, const Path & path, Roots & roots)
{
try {
struct stat st = lstat(path);
if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path);
foreach (Strings::iterator, i, names)
findRoots(store, path + "/" + *i, roots);
}
else if (S_ISLNK(st.st_mode)) {
Path target = readLink(path);
if (isInStore(target))
foundRoot(store, path, target, roots);
/* Handle indirect roots. */
else {
target = absPath(target, dirOf(path));
if (!pathExists(target)) {
if (isInDir(path, settings.nixStateDir + "/" + gcRootsDir + "/auto")) {
printMsg(lvlInfo, format("removing stale link from `%1%' to `%2%'") % path % target);
unlink(path.c_str());
}
} else {
struct stat st2 = lstat(target);
if (!S_ISLNK(st2.st_mode)) return;
Path target2 = readLink(target);
if (isInStore(target2)) foundRoot(store, target, target2, roots);
}
}
}
}
catch (SysError & e) {
/* We only ignore permanent failures. */
if (e.errNo == EACCES || e.errNo == ENOENT || e.errNo == ENOTDIR)
printMsg(lvlInfo, format("cannot read potential root `%1%'") % path);
else
throw;
}
}
Roots LocalStore::findRoots()
{
Roots roots;
/* Process direct roots in {gcroots,manifests,profiles}. */
nix::findRoots(*this, settings.nixStateDir + "/" + gcRootsDir, roots);
nix::findRoots(*this, settings.nixStateDir + "/manifests", roots);
nix::findRoots(*this, settings.nixStateDir + "/profiles", roots);
return roots;
}
static void addAdditionalRoots(StoreAPI & store, PathSet & roots)
{
Path rootFinder = getEnv("NIX_ROOT_FINDER",
settings.nixLibexecDir + "/guix/list-runtime-roots");
if (rootFinder.empty()) return;
debug(format("executing `%1%' to find additional roots") % rootFinder);
string result = runProgram(rootFinder);
StringSet paths = tokenizeString<StringSet>(result, "\n");
foreach (StringSet::iterator, i, paths) {
if (isInStore(*i)) {
Path path = toStorePath(*i);
if (roots.find(path) == roots.end() && store.isValidPath(path)) {
debug(format("got additional root `%1%'") % path);
roots.insert(path);
}
}
}
}
struct GCLimitReached { };
struct LocalStore::GCState
{
GCOptions options;
GCResults & results;
PathSet roots;
PathSet tempRoots;
PathSet dead;
PathSet alive;
bool gcKeepOutputs;
bool gcKeepDerivations;
unsigned long long bytesInvalidated;
Path trashDir;
bool shouldDelete;
GCState(GCResults & results_) : results(results_), bytesInvalidated(0) { }
};
bool LocalStore::isActiveTempFile(const GCState & state,
const Path & path, const string & suffix)
{
return hasSuffix(path, suffix)
&& state.tempRoots.find(string(path, 0, path.size() - suffix.size())) != state.tempRoots.end();
}
void LocalStore::deleteGarbage(GCState & state, const Path & path)
{
unsigned long long bytesFreed;
deletePath(path, bytesFreed);
state.results.bytesFreed += bytesFreed;
}
void LocalStore::deletePathRecursive(GCState & state, const Path & path)
{
checkInterrupt();
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;
throw SysError(format("getting status of %1%") % path);
}
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 (!isValidPath(path)) {
/* A lock file belonging to a path that we're building right
now isn't garbage. */
if (isActiveTempFile(state, path, ".lock")) return;
/* Don't delete .chroot directories for derivations that are
currently being built. */
if (isActiveTempFile(state, path, ".chroot")) return;
}
PathSet visited;
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);
}
}
/* Unlink all files in /nix/store/.links that have a link count of 1,
which indicates that there are no other links and so they can be
safely deleted. FIXME: race condition with optimisePath(): we
might see a link count of 1 just before optimisePath() increases
the link count. */
void LocalStore::removeUnusedLinks(const GCState & state)
{
AutoCloseDir dir = opendir(linksDir.c_str());
if (!dir) throw SysError(format("opening directory `%1%'") % linksDir);
long long actualSize = 0, unsharedSize = 0;
struct dirent * dirent;
while (errno = 0, dirent = readdir(dir)) {
checkInterrupt();
string name = dirent->d_name;
if (name == "." || name == "..") continue;
Path path = linksDir + "/" + name;
struct stat st;
if (lstat(path.c_str(), &st) == -1)
throw SysError(format("statting `%1%'") % path);
if (st.st_nlink != 1) {
unsigned long long size = st.st_blocks * 512ULL;
actualSize += size;
unsharedSize += (st.st_nlink - 1) * size;
continue;
}
printMsg(lvlTalkative, format("deleting unused link `%1%'") % path);
if (unlink(path.c_str()) == -1)
throw SysError(format("deleting `%1%'") % path);
state.results.bytesFreed += st.st_blocks * 512;
}
struct stat st;
if (stat(linksDir.c_str(), &st) == -1)
throw SysError(format("statting `%1%'") % linksDir);
long long overhead = st.st_blocks * 512ULL;
printMsg(lvlInfo, format("note: currently hard linking saves %.2f MiB")
% ((unsharedSize - actualSize - overhead) / (1024.0 * 1024.0)));
}
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;
/* Using `--ignore-liveness' with `--delete' can have unintended
consequences if `gc-keep-outputs' or `gc-keep-derivations' are
true (the garbage collector will recurse into deleting the
outputs or derivers, respectively). So disable them. */
if (options.action == GCOptions::gcDeleteSpecific && options.ignoreLiveness) {
state.gcKeepOutputs = false;
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. */
AutoCloseFD fdGCLock = openGCLock(ltWrite);
/* Find the roots. Since we've grabbed the GC lock, the set of
permanent roots cannot increase now. */
printMsg(lvlError, format("finding garbage collector roots..."));
Roots rootMap = options.ignoreLiveness ? Roots() : findRoots();
foreach (Roots::iterator, i, rootMap) state.roots.insert(i->second);
/* Add additional roots returned by the program specified by the
NIX_ROOT_FINDER environment variable. This is typically used
to add running programs to the set of roots (to prevent them
from being garbage collected). */
if (!options.ignoreLiveness)
addAdditionalRoots(*this, state.roots);
/* Read the temporary roots. This acquires read locks on all
per-process temporary root files. So after this point no paths
can be added to the set of temporary roots. */
FDs fds;
readTempRoots(state.tempRoots, fds);
state.roots.insert(state.tempRoots.begin(), state.tempRoots.end());
/* After this point the set of roots or temporary roots cannot
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). */
if (options.action == GCOptions::gcDeleteSpecific) {
foreach (PathSet::iterator, i, options.pathsToDelete) {
assertStorePath(*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 (state.shouldDelete)
printMsg(lvlError, format("deleting garbage..."));
else
printMsg(lvlError, format("determining live/dead paths..."));
try {
AutoCloseDir dir = opendir(settings.nixStore.c_str());
if (!dir) throw SysError(format("opening directory `%1%'") % settings.nixStore);
/* Read the store and immediately delete all paths that
aren't valid. When using --max-freed etc., deleting
invalid paths is preferred over deleting unreachable
paths, since unreachable paths could become reachable
again. We don't use readDirectory() here so that GCing
can start faster. */
Paths entries;
struct dirent * dirent;
while (errno = 0, dirent = readdir(dir)) {
checkInterrupt();
string name = dirent->d_name;
if (name == "." || name == "..") continue;
Path path = settings.nixStore + "/" + name;
if (isValidPath(path))
entries.push_back(path);
else
tryToDelete(state, path);
}
dir.close();
/* Now delete the unreachable valid paths. Randomise the
order in which we delete entries to make the collector
less biased towards deleting paths that come
alphabetically first (e.g. /nix/store/000...). This
matters when using --max-freed etc. */
vector<Path> entries_(entries.begin(), entries.end());
random_shuffle(entries_.begin(), entries_.end());
foreach (vector<Path>::iterator, i, entries_)
tryToDelete(state, *i);
} catch (GCLimitReached & e) {
}
}
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();
fds.clear();
/* 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) {
printMsg(lvlError, format("deleting unused links..."));
removeUnusedLinks(state);
}
/* While we're at it, vacuum the database. */
if (options.action == GCOptions::gcDeleteDead) vacuumDB();
}
}

240
nix/libstore/globals.cc Normal file
View File

@ -0,0 +1,240 @@
#include "config.h"
#include "globals.hh"
#include "util.hh"
#include <map>
#include <algorithm>
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;
Settings::Settings()
{
keepFailed = false;
keepGoing = false;
tryFallback = false;
buildVerbosity = lvlError;
maxBuildJobs = 1;
buildCores = 1;
readOnlyMode = false;
thisSystem = SYSTEM;
maxSilentTime = 0;
buildTimeout = 0;
useBuildHook = true;
printBuildTrace = false;
reservedSize = 1024 * 1024;
fsyncMetadata = true;
useSQLiteWAL = true;
syncBeforeRegistering = false;
useSubstitutes = true;
useChroot = false;
useSshSubstituter = false;
impersonateLinux26 = false;
keepLog = true;
compressLog = true;
maxLogSize = 0;
cacheFailure = false;
pollInterval = 5;
checkRootReachability = false;
gcKeepOutputs = false;
gcKeepDerivations = true;
autoOptimiseStore = false;
envKeepDerivations = false;
lockCPU = getEnv("NIX_AFFINITY_HACK", "1") == "1";
showTrace = false;
}
void Settings::processEnvironment()
{
nixStore = canonPath(getEnv("NIX_STORE_DIR", getEnv("NIX_STORE", NIX_STORE_DIR)));
nixDataDir = canonPath(getEnv("NIX_DATA_DIR", NIX_DATA_DIR));
nixLogDir = canonPath(getEnv("NIX_LOG_DIR", NIX_LOG_DIR));
nixStateDir = canonPath(getEnv("NIX_STATE_DIR", NIX_STATE_DIR));
nixDBPath = getEnv("NIX_DB_DIR", nixStateDir + "/db");
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);
}
void Settings::loadConfFile()
{
Path settingsFile = (format("%1%/%2%") % nixConfDir % "nix.conf").str();
if (!pathExists(settingsFile)) return;
string contents = readFile(settingsFile);
unsigned int pos = 0;
while (pos < contents.size()) {
string line;
while (pos < contents.size() && contents[pos] != '\n')
line += contents[pos++];
pos++;
string::size_type hash = line.find('#');
if (hash != string::npos)
line = string(line, 0, hash);
vector<string> tokens = tokenizeString<vector<string> >(line);
if (tokens.empty()) continue;
if (tokens.size() < 2 || tokens[1] != "=")
throw Error(format("illegal configuration line `%1%' in `%2%'") % line % settingsFile);
string name = tokens[0];
vector<string>::iterator i = tokens.begin();
advance(i, 2);
settings[name] = concatStringsSep(" ", Strings(i, tokens.end())); // FIXME: slow
};
}
void Settings::set(const string & name, const string & value)
{
settings[name] = value;
overrides[name] = value;
}
void Settings::update()
{
get(tryFallback, "build-fallback");
get(maxBuildJobs, "build-max-jobs");
get(buildCores, "build-cores");
get(thisSystem, "system");
get(maxSilentTime, "build-max-silent-time");
get(buildTimeout, "build-timeout");
get(reservedSize, "gc-reserved-space");
get(fsyncMetadata, "fsync-metadata");
get(useSQLiteWAL, "use-sqlite-wal");
get(syncBeforeRegistering, "sync-before-registering");
get(useSubstitutes, "build-use-substitutes");
get(buildUsersGroup, "build-users-group");
get(useChroot, "build-use-chroot");
get(dirsInChroot, "build-chroot-dirs");
get(impersonateLinux26, "build-impersonate-linux-26");
get(keepLog, "build-keep-log");
get(compressLog, "build-compress-log");
get(maxLogSize, "build-max-log-size");
get(cacheFailure, "build-cache-failure");
get(pollInterval, "build-poll-interval");
get(checkRootReachability, "gc-check-reachability");
get(gcKeepOutputs, "gc-keep-outputs");
get(gcKeepDerivations, "gc-keep-derivations");
get(autoOptimiseStore, "auto-optimise-store");
get(envKeepDerivations, "env-keep-derivations");
get(sshSubstituterHosts, "ssh-substituter-hosts");
get(useSshSubstituter, "use-ssh-substituter");
string subs = getEnv("NIX_SUBSTITUTERS", "default");
if (subs == "default") {
substituters.clear();
#if 0
if (getEnv("NIX_OTHER_STORES") != "")
substituters.push_back(nixLibexecDir + "/nix/substituters/copy-from-other-stores.pl");
#endif
substituters.push_back(nixLibexecDir + "/nix/substituters/download-using-manifests.pl");
substituters.push_back(nixLibexecDir + "/nix/substituters/download-from-binary-cache.pl");
if (useSshSubstituter)
substituters.push_back(nixLibexecDir + "/nix/substituters/download-via-ssh");
} else
substituters = tokenizeString<Strings>(subs, ":");
}
void Settings::get(string & res, const string & name)
{
SettingsMap::iterator i = settings.find(name);
if (i == settings.end()) return;
res = i->second;
}
void Settings::get(bool & res, const string & name)
{
SettingsMap::iterator i = settings.find(name);
if (i == settings.end()) return;
if (i->second == "true") res = true;
else if (i->second == "false") res = false;
else throw Error(format("configuration option `%1%' should be either `true' or `false', not `%2%'")
% name % i->second);
}
void Settings::get(StringSet & res, const string & name)
{
SettingsMap::iterator i = settings.find(name);
if (i == settings.end()) return;
res.clear();
Strings ss = tokenizeString<Strings>(i->second);
res.insert(ss.begin(), ss.end());
}
void Settings::get(Strings & res, const string & name)
{
SettingsMap::iterator i = settings.find(name);
if (i == settings.end()) return;
res = tokenizeString<Strings>(i->second);
}
template<class N> void Settings::get(N & res, const string & name)
{
SettingsMap::iterator i = settings.find(name);
if (i == settings.end()) return;
if (!string2Int(i->second, res))
throw Error(format("configuration setting `%1%' should have an integer value") % name);
}
string Settings::pack()
{
string s;
foreach (SettingsMap::iterator, i, settings) {
if (i->first.find('\n') != string::npos ||
i->first.find('=') != string::npos ||
i->second.find('\n') != string::npos)
throw Error("illegal option name/value");
s += i->first; s += '='; s += i->second; s += '\n';
}
return s;
}
void Settings::unpack(const string & pack) {
Strings lines = tokenizeString<Strings>(pack, "\n");
foreach (Strings::iterator, i, lines) {
string::size_type eq = i->find('=');
if (eq == string::npos)
throw Error("illegal option name/value");
set(i->substr(0, eq), i->substr(eq + 1));
}
}
Settings::SettingsMap Settings::getOverrides()
{
return overrides;
}
const string nixVersion = PACKAGE_VERSION;
}

218
nix/libstore/globals.hh Normal file
View File

@ -0,0 +1,218 @@
#pragma once
#include "types.hh"
#include <map>
#include <sys/types.h>
namespace nix {
struct Settings {
typedef std::map<string, string> SettingsMap;
Settings();
void processEnvironment();
void loadConfFile();
void set(const string & name, const string & value);
void update();
string pack();
void unpack(const string & pack);
SettingsMap getOverrides();
/* The directory where we store sources and derived files. */
Path nixStore;
Path nixDataDir; /* !!! fix */
/* The directory where we log various operations. */
Path nixLogDir;
/* The directory where state is stored. */
Path nixStateDir;
/* The directory where we keep the SQLite database. */
Path nixDBPath;
/* The directory where configuration files are stored. */
Path nixConfDir;
/* The directory where internal helper programs are stored. */
Path nixLibexecDir;
/* 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;
/* Whether to keep building subgoals when a sibling (another
subgoal of the same goal) fails. */
bool keepGoing;
/* Whether, if we cannot realise the known closure corresponding
to a derivation, we should try to normalise the derivation
instead. */
bool tryFallback;
/* Verbosity level for build output. */
Verbosity buildVerbosity;
/* Maximum number of parallel build jobs. 0 means unlimited. */
unsigned int maxBuildJobs;
/* Number of CPU cores to utilize in parallel within a build,
i.e. by passing this number to Make via '-j'. 0 means that the
number of actual CPU cores on the local host ought to be
auto-detected. */
unsigned int buildCores;
/* Read-only mode. Don't copy stuff to the store, don't change
the database. */
bool readOnlyMode;
/* The canonical system name, as returned by config.guess. */
string thisSystem;
/* The maximum time in seconds that a builer can go without
producing any output on stdout/stderr before it is killed. 0
means infinity. */
time_t maxSilentTime;
/* The maximum duration in seconds that a builder can run. 0
means infinity. */
time_t buildTimeout;
/* The substituters. There are programs that can somehow realise
a store path without building, e.g., by downloading it or
copying it from a CD. */
Paths substituters;
/* Whether to use build hooks (for distributed builds). Sometimes
users want to disable this from the command-line. */
bool useBuildHook;
/* Whether buildDerivations() should print out lines on stderr in
a fixed format to allow its progress to be monitored. Each
line starts with a "@". The following are defined:
@ build-started <drvpath> <outpath> <system> <logfile>
@ build-failed <drvpath> <outpath> <exitcode> <error text>
@ build-succeeded <drvpath> <outpath>
@ substituter-started <outpath> <substituter>
@ substituter-failed <outpath> <exitcode> <error text>
@ substituter-succeeded <outpath>
Best combined with --no-build-output, otherwise stderr might
conceivably contain lines in this format printed by the
builders. */
bool printBuildTrace;
/* Amount of reserved space for the garbage collector
(/nix/var/nix/db/reserved). */
off_t reservedSize;
/* Whether SQLite should use fsync. */
bool fsyncMetadata;
/* Whether SQLite should use WAL mode. */
bool useSQLiteWAL;
/* Whether to call sync() before registering a path as valid. */
bool syncBeforeRegistering;
/* Whether to use substitutes. */
bool useSubstitutes;
/* The Unix group that contains the build users. */
string buildUsersGroup;
/* Whether to build in chroot. */
bool useChroot;
/* The directories from the host filesystem to be included in the
chroot. */
StringSet dirsInChroot;
/* Set of ssh connection strings for the ssh substituter */
Strings sshSubstituterHosts;
/* Whether to use the ssh substituter at all */
bool useSshSubstituter;
/* Whether to impersonate a Linux 2.6 machine on newer kernels. */
bool impersonateLinux26;
/* Whether to store build logs. */
bool keepLog;
/* Whether to compress logs. */
bool compressLog;
/* Maximum number of bytes a builder can write to stdout/stderr
before being killed (0 means no limit). */
unsigned long maxLogSize;
/* Whether to cache build failures. */
bool cacheFailure;
/* How often (in seconds) to poll for locks. */
unsigned int pollInterval;
/* Whether to check if new GC roots can in fact be found by the
garbage collector. */
bool checkRootReachability;
/* Whether the garbage collector should keep outputs of live
derivations. */
bool gcKeepOutputs;
/* Whether the garbage collector should keep derivers of live
paths. */
bool gcKeepDerivations;
/* Whether to automatically replace files with identical contents
with hard links. */
bool autoOptimiseStore;
/* Whether to add derivations as a dependency of user environments
(to prevent them from being GCed). */
bool envKeepDerivations;
/* Whether to lock the Nix client and worker to the same CPU. */
bool lockCPU;
/* Whether to show a stack trace if Nix evaluation fails. */
bool showTrace;
private:
SettingsMap settings, overrides;
void get(string & res, const string & name);
void get(bool & res, const string & name);
void get(StringSet & res, const string & name);
void get(Strings & res, const string & name);
template<class N> void get(N & res, const string & name);
};
// FIXME: don't use a global variable.
extern Settings settings;
extern const string nixVersion;
}

2010
nix/libstore/local-store.cc Normal file

File diff suppressed because it is too large Load Diff

333
nix/libstore/local-store.hh Normal file
View File

@ -0,0 +1,333 @@
#pragma once
#include <string>
#include "store-api.hh"
#include "util.hh"
#include "pathlocks.hh"
class sqlite3;
class sqlite3_stmt;
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. Version 7 is Nix 1.3. */
const int nixSchemaVersion = 7;
extern string drvsLogDir;
struct Derivation;
struct OptimiseStats
{
unsigned long totalFiles;
unsigned long sameContents;
unsigned long filesLinked;
unsigned long long bytesFreed;
unsigned long long blocksFreed;
OptimiseStats()
{
totalFiles = sameContents = filesLinked = 0;
bytesFreed = blocksFreed = 0;
}
};
struct RunningSubstituter
{
Path program;
Pid pid;
AutoCloseFD to, from, error;
FdSource fromBuf;
bool disabled;
RunningSubstituter() : disabled(false) { };
};
/* Wrapper object to close the SQLite database automatically. */
struct SQLite
{
sqlite3 * db;
SQLite() { db = 0; }
~SQLite();
operator sqlite3 * () { return db; }
};
/* Wrapper object to create and destroy SQLite prepared statements. */
struct SQLiteStmt
{
sqlite3 * db;
sqlite3_stmt * stmt;
unsigned int curArg;
SQLiteStmt() { stmt = 0; }
void create(sqlite3 * db, const string & s);
void reset();
~SQLiteStmt();
operator sqlite3_stmt * () { return stmt; }
void bind(const string & value);
void bind(int value);
void bind64(long long value);
void bind();
};
class LocalStore : public StoreAPI
{
private:
typedef std::map<Path, RunningSubstituter> RunningSubstituters;
RunningSubstituters runningSubstituters;
Path linksDir;
public:
/* Initialise the local store, upgrading the schema if
necessary. */
LocalStore(bool reserveSpace = true);
~LocalStore();
/* Implementations of abstract store API methods. */
bool isValidPath(const Path & path);
PathSet queryValidPaths(const PathSet & paths);
PathSet queryAllValidPaths();
ValidPathInfo queryPathInfo(const Path & path);
Hash queryPathHash(const Path & path);
void queryReferences(const Path & path, PathSet & references);
void queryReferrers(const Path & path, PathSet & referrers);
Path queryDeriver(const Path & path);
PathSet queryValidDerivers(const Path & path);
PathSet queryDerivationOutputs(const Path & path);
StringSet queryDerivationOutputNames(const Path & path);
Path queryPathFromHashPart(const string & hashPart);
PathSet querySubstitutablePaths(const PathSet & paths);
void querySubstitutablePathInfos(const Path & substituter,
PathSet & paths, SubstitutablePathInfos & infos);
void querySubstitutablePathInfos(const PathSet & paths,
SubstitutablePathInfos & infos);
Path addToStore(const Path & srcPath,
bool recursive = true, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter, bool repair = false);
/* Like addToStore(), but the contents of the path are contained
in `dump', which is either a NAR serialisation (if recursive ==
true) or simply the contents of a regular file (if recursive ==
false). */
Path addToStoreFromDump(const string & dump, const string & name,
bool recursive = true, HashType hashAlgo = htSHA256, bool repair = false);
Path addTextToStore(const string & name, const string & s,
const PathSet & references, bool repair = false);
void exportPath(const Path & path, bool sign,
Sink & sink);
Paths importPaths(bool requireSignature, Source & source);
void buildPaths(const PathSet & paths, BuildMode buildMode);
void ensurePath(const Path & path);
void addTempRoot(const Path & path);
void addIndirectRoot(const Path & path);
void syncWithGC();
Roots findRoots();
void collectGarbage(const GCOptions & options, GCResults & results);
/* Optimise the disk space usage of the Nix store by hard-linking
files with the same contents. */
void optimiseStore(OptimiseStats & stats);
/* Optimise a single store path. */
void optimisePath(const Path & path);
/* Check the integrity of the Nix store. Returns true if errors
remain. */
bool verifyStore(bool checkContents, bool repair);
/* Register the validity of a path, i.e., that `path' exists, that
the paths referenced by it exists, and in the case of an output
path of a derivation, that it has been produced by a successful
execution of the derivation (or something equivalent). Also
register the hash of the file system contents of the path. The
hash must be a SHA-256 hash. */
void registerValidPath(const ValidPathInfo & info);
void registerValidPaths(const ValidPathInfos & infos);
/* Register that the build of a derivation with output `path' has
failed. */
void registerFailedPath(const Path & path);
/* Query whether `path' previously failed to build. */
bool hasPathFailed(const Path & path);
PathSet queryFailedPaths();
void clearFailedPaths(const PathSet & paths);
void vacuumDB();
/* Repair the contents of the given path by redownloading it using
a substituter (if available). */
void repairPath(const Path & path);
/* Check whether the given valid path exists and has the right
contents. */
bool pathContentsGood(const Path & path);
void markContentsGood(const Path & path);
void setSubstituterEnv();
private:
Path schemaPath;
/* Lock file used for upgrading. */
AutoCloseFD globalLock;
/* The SQLite database object. */
SQLite db;
/* Some precompiled SQLite statements. */
SQLiteStmt stmtRegisterValidPath;
SQLiteStmt stmtUpdatePathInfo;
SQLiteStmt stmtAddReference;
SQLiteStmt stmtQueryPathInfo;
SQLiteStmt stmtQueryReferences;
SQLiteStmt stmtQueryReferrers;
SQLiteStmt stmtInvalidatePath;
SQLiteStmt stmtRegisterFailedPath;
SQLiteStmt stmtHasPathFailed;
SQLiteStmt stmtQueryFailedPaths;
SQLiteStmt stmtClearFailedPath;
SQLiteStmt stmtAddDerivationOutput;
SQLiteStmt stmtQueryValidDerivers;
SQLiteStmt stmtQueryDerivationOutputs;
SQLiteStmt stmtQueryPathFromHashPart;
/* Cache for pathContentsGood(). */
std::map<Path, bool> pathContentsGoodCache;
bool didSetSubstituterEnv;
int getSchema();
void openDB(bool create);
void makeStoreWritable();
unsigned long long queryValidPathId(const Path & path);
unsigned long long addValidPath(const ValidPathInfo & info, bool checkOutputs = true);
void addReference(unsigned long long referrer, unsigned long long reference);
void appendReferrer(const Path & from, const Path & to, bool lock);
void rewriteReferrers(const Path & path, bool purge, PathSet referrers);
void invalidatePath(const Path & path);
/* Delete a path from the Nix store. */
void invalidatePathChecked(const Path & path);
void verifyPath(const Path & path, const PathSet & store,
PathSet & done, PathSet & validPaths, bool repair, bool & errors);
void updatePathInfo(const ValidPathInfo & info);
void upgradeStore6();
void upgradeStore7();
PathSet queryValidPathsOld();
ValidPathInfo queryPathInfoOld(const Path & path);
struct GCState;
void deleteGarbage(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);
int openGCLock(LockType lockType);
void removeUnusedLinks(const GCState & state);
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);
void checkDerivationOutputs(const Path & drvPath, const Derivation & drv);
void optimisePath_(OptimiseStats & stats, const Path & path);
// Internal versions that are not wrapped in retry_sqlite.
bool isValidPath_(const Path & path);
void queryReferrers_(const Path & path, PathSet & referrers);
};
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.,
00:00:01 1/1/1970 UTC)
- the permissions are set of 444 or 555 (i.e., read-only with or
without execute permission; setuid bits etc. are cleared)
- the owner and group are set to the Nix user and group, if we're
running as root. */
void canonicalisePathMetaData(const Path & path, uid_t fromUid, InodesSeen & inodesSeen);
void canonicalisePathMetaData(const Path & path, uid_t fromUid);
void canonicaliseTimestampAndPermissions(const Path & path);
MakeError(PathInUse, Error);
}

220
nix/libstore/misc.cc Normal file
View File

@ -0,0 +1,220 @@
#include "misc.hh"
#include "store-api.hh"
#include "local-store.hh"
#include "globals.hh"
namespace nix {
Derivation derivationFromPath(StoreAPI & store, const Path & drvPath)
{
assertStorePath(drvPath);
store.ensurePath(drvPath);
return parseDerivation(readFile(drvPath));
}
void computeFSClosure(StoreAPI & store, const Path & path,
PathSet & paths, bool flipDirection, bool includeOutputs, bool includeDerivers)
{
if (paths.find(path) != paths.end()) return;
paths.insert(path);
PathSet edges;
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, edges)
computeFSClosure(store, *i, paths, flipDirection, includeOutputs, includeDerivers);
}
Path findOutput(const Derivation & drv, string id)
{
foreach (DerivationOutputs::const_iterator, i, drv.outputs)
if (i->first == id) return i->second.path;
throw Error(format("derivation has no output `%1%'") % id);
}
void queryMissing(StoreAPI & store, const PathSet & targets,
PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,
unsigned long long & downloadSize, unsigned long long & narSize)
{
downloadSize = narSize = 0;
PathSet todo(targets.begin(), targets.end()), done;
/* Getting substitute info has high latency when using the binary
cache substituter. Thus it's essential to do substitute
queries in parallel as much as possible. To accomplish this
we do the following:
- For all paths still to be processed (todo), we add all
paths for which we need info to the set query. For an
unbuilt derivation this is the output paths; otherwise, it's
the path itself.
- We get info about all paths in query in parallel.
- We process the results and add new items to todo if
necessary. E.g. if a path is substitutable, then we need to
get info on its references.
- Repeat until todo is empty.
*/
while (!todo.empty()) {
PathSet query, todoDrv, todoNonDrv;
foreach (PathSet::iterator, i, todo) {
if (done.find(*i) != done.end()) continue;
done.insert(*i);
DrvPathWithOutputs i2 = parseDrvPathWithOutputs(*i);
if (isDerivation(i2.first)) {
if (!store.isValidPath(i2.first)) {
// FIXME: we could try to substitute p.
unknown.insert(*i);
continue;
}
Derivation drv = derivationFromPath(store, i2.first);
PathSet invalid;
foreach (DerivationOutputs::iterator, j, drv.outputs)
if (wantOutput(j->first, i2.second)
&& !store.isValidPath(j->second.path))
invalid.insert(j->second.path);
if (invalid.empty()) continue;
todoDrv.insert(*i);
if (settings.useSubstitutes && !willBuildLocally(drv))
query.insert(invalid.begin(), invalid.end());
}
else {
if (store.isValidPath(*i)) continue;
query.insert(*i);
todoNonDrv.insert(*i);
}
}
todo.clear();
SubstitutablePathInfos infos;
store.querySubstitutablePathInfos(query, infos);
foreach (PathSet::iterator, i, todoDrv) {
DrvPathWithOutputs i2 = parseDrvPathWithOutputs(*i);
// FIXME: cache this
Derivation drv = derivationFromPath(store, i2.first);
PathSet outputs;
bool mustBuild = false;
if (settings.useSubstitutes && !willBuildLocally(drv)) {
foreach (DerivationOutputs::iterator, j, drv.outputs) {
if (!wantOutput(j->first, i2.second)) continue;
if (!store.isValidPath(j->second.path)) {
if (infos.find(j->second.path) == infos.end())
mustBuild = true;
else
outputs.insert(j->second.path);
}
}
} else
mustBuild = true;
if (mustBuild) {
willBuild.insert(i2.first);
todo.insert(drv.inputSrcs.begin(), drv.inputSrcs.end());
foreach (DerivationInputs::iterator, j, drv.inputDrvs)
todo.insert(makeDrvPathWithOutputs(j->first, j->second));
} else
todoNonDrv.insert(outputs.begin(), outputs.end());
}
foreach (PathSet::iterator, i, todoNonDrv) {
done.insert(*i);
SubstitutablePathInfos::iterator info = infos.find(*i);
if (info != infos.end()) {
willSubstitute.insert(*i);
downloadSize += info->second.downloadSize;
narSize += info->second.narSize;
todo.insert(info->second.references.begin(), info->second.references.end());
} else
unknown.insert(*i);
}
}
}
static void dfsVisit(StoreAPI & store, const PathSet & paths,
const Path & path, PathSet & visited, Paths & sorted,
PathSet & parents)
{
if (parents.find(path) != parents.end())
throw BuildError(format("cycle detected in the references of `%1%'") % path);
if (visited.find(path) != visited.end()) return;
visited.insert(path);
parents.insert(path);
PathSet references;
if (store.isValidPath(path))
store.queryReferences(path, references);
foreach (PathSet::iterator, i, references)
/* Don't traverse into paths that don't exist. That can
happen due to substitutes for non-existent paths. */
if (*i != path && paths.find(*i) != paths.end())
dfsVisit(store, paths, *i, visited, sorted, parents);
sorted.push_front(path);
parents.erase(path);
}
Paths topoSortPaths(StoreAPI & store, const PathSet & paths)
{
Paths sorted;
PathSet visited, parents;
foreach (PathSet::const_iterator, i, paths)
dfsVisit(store, paths, *i, visited, sorted, parents);
return sorted;
}
}

38
nix/libstore/misc.hh Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include "derivations.hh"
namespace nix {
/* Read a derivation, after ensuring its existence through
ensurePath(). */
Derivation derivationFromPath(StoreAPI & store, const Path & drvPath);
/* Place in `paths' the set of all store paths in the file system
closure of `storePath'; that is, all paths than can be directly or
indirectly reached from it. `paths' is not cleared. If
`flipDirection' is true, the set of paths that can reach
`storePath' is returned; that is, the closures under the
`referrers' relation instead of the `references' relation is
returned. */
void computeFSClosure(StoreAPI & store, const Path & path,
PathSet & paths, bool flipDirection = false,
bool includeOutputs = false, bool includeDerivers = false);
/* Return the path corresponding to the output identifier `id' in the
given derivation. */
Path findOutput(const Derivation & drv, string id);
/* Given a set of paths that are to be built, return the set of
derivations that will be built, and the set of output paths that
will be substituted. */
void queryMissing(StoreAPI & store, const PathSet & targets,
PathSet & willBuild, PathSet & willSubstitute, PathSet & unknown,
unsigned long long & downloadSize, unsigned long long & narSize);
bool willBuildLocally(const Derivation & drv);
}

View File

@ -0,0 +1,180 @@
#include "config.h"
#include "util.hh"
#include "local-store.hh"
#include "globals.hh"
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
namespace nix {
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 (chmod(path.c_str(), st.st_mode | S_IWUSR) == -1)
throw SysError(format("changing writability of `%1%'") % path);
}
struct MakeReadOnly
{
Path path;
MakeReadOnly(const Path & path) : path(path) { }
~MakeReadOnly()
{
try {
/* This will make the path read-only. */
if (path != "") canonicaliseTimestampAndPermissions(path);
} catch (...) {
ignoreException();
}
}
};
void LocalStore::optimisePath_(OptimiseStats & stats, const Path & path)
{
checkInterrupt();
struct stat st;
if (lstat(path.c_str(), &st))
throw SysError(format("getting attributes of path `%1%'") % path);
if (S_ISDIR(st.st_mode)) {
Strings names = readDirectory(path);
foreach (Strings::iterator, i, names)
optimisePath_(stats, path + "/" + *i);
return;
}
/* We can hard link regular files and maybe symlinks. */
if (!S_ISREG(st.st_mode)
#if CAN_LINK_SYMLINK
&& !S_ISLNK(st.st_mode)
#endif
) return;
/* Sometimes SNAFUs can cause files in the Nix store to be
modified, in particular when running programs as root under
NixOS (example: $fontconfig/var/cache being modified). Skip
those files. FIXME: check the modification time. */
if (S_ISREG(st.st_mode) && (st.st_mode & S_IWUSR)) {
printMsg(lvlError, format("skipping suspicious writable file `%1%'") % path);
return;
}
/* Hash the file. Note that hashPath() returns the hash over the
NAR serialisation, which includes the execute bit on the file.
Thus, executable and non-executable files with the same
contents *won't* be linked (which is good because otherwise the
permissions would be screwed up).
Also note that if `path' is a symlink, then we're hashing the
contents of the symlink (i.e. the result of readlink()), not
the contents of the target (which may not even exist). */
Hash hash = hashPath(htSHA256, path).first;
stats.totalFiles++;
printMsg(lvlDebug, format("`%1%' has hash `%2%'") % path % printHash(hash));
/* Check if this is a known hash. */
Path linkPath = linksDir + "/" + printHash32(hash);
if (!pathExists(linkPath)) {
/* Nope, create a hard link in the links directory. */
if (link(path.c_str(), linkPath.c_str()) == 0) return;
if (errno != EEXIST)
throw SysError(format("cannot link `%1%' to `%2%'") % linkPath % path);
/* Fall through if another process created linkPath before
we did. */
}
/* Yes! We've seen a file with the same contents. Replace the
current file with a hard link to that file. */
struct stat stLink;
if (lstat(linkPath.c_str(), &stLink))
throw SysError(format("getting attributes of path `%1%'") % linkPath);
stats.sameContents++;
if (st.st_ino == stLink.st_ino) {
printMsg(lvlDebug, format("`%1%' is already linked to `%2%'") % path % linkPath);
return;
}
printMsg(lvlTalkative, format("linking `%1%' to `%2%'") % path % linkPath);
/* Make the containing directory writable, but only if it's not
the store itself (we don't want or need to mess with its
permissions). */
bool mustToggle = !isStorePath(path);
if (mustToggle) makeWritable(dirOf(path));
/* When we're done, make the directory read-only again and reset
its timestamp back to 0. */
MakeReadOnly makeReadOnly(mustToggle ? dirOf(path) : "");
Path tempLink = (format("%1%/.tmp-link-%2%-%3%")
% settings.nixStore % getpid() % rand()).str();
if (link(linkPath.c_str(), tempLink.c_str()) == -1) {
if (errno == EMLINK) {
/* Too many links to the same file (>= 32000 on most file
systems). This is likely to happen with empty files.
Just shrug and ignore. */
if (st.st_size)
printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath);
return;
}
throw SysError(format("cannot link `%1%' to `%2%'") % tempLink % linkPath);
}
/* Atomically replace the old file with the new hard link. */
if (rename(tempLink.c_str(), path.c_str()) == -1) {
if (unlink(tempLink.c_str()) == -1)
printMsg(lvlError, format("unable to unlink `%1%'") % tempLink);
if (errno == EMLINK) {
/* Some filesystems generate too many links on the rename,
rather than on the original link. (Probably it
temporarily increases the st_nlink field before
decreasing it again.) */
if (st.st_size)
printMsg(lvlInfo, format("`%1%' has maximum number of links") % linkPath);
return;
}
throw SysError(format("cannot rename `%1%' to `%2%'") % tempLink % path);
}
stats.filesLinked++;
stats.bytesFreed += st.st_size;
stats.blocksFreed += st.st_blocks;
}
void LocalStore::optimiseStore(OptimiseStats & stats)
{
PathSet paths = queryAllValidPaths();
foreach (PathSet::iterator, i, paths) {
addTempRoot(*i);
if (!isValidPath(*i)) continue; /* path was GC'ed, probably */
startNest(nest, lvlChatty, format("hashing files in `%1%'") % *i);
optimisePath_(stats, *i);
}
}
void LocalStore::optimisePath(const Path & path)
{
OptimiseStats stats;
if (settings.autoOptimiseStore) optimisePath_(stats, path);
}
}

199
nix/libstore/pathlocks.cc Normal file
View File

@ -0,0 +1,199 @@
#include "pathlocks.hh"
#include "util.hh"
#include <cerrno>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
namespace nix {
int openLockFile(const Path & path, bool create)
{
AutoCloseFD fd;
fd = open(path.c_str(), O_RDWR | (create ? O_CREAT : 0), 0600);
if (fd == -1 && (create || errno != ENOENT))
throw SysError(format("opening lock file `%1%'") % path);
closeOnExec(fd);
return fd.borrow();
}
void deleteLockFile(const Path & path, int fd)
{
/* Get rid of the lock file. Have to be careful not to introduce
races. Write a (meaningless) token to the file to indicate to
other processes waiting on this lock that the lock is stale
(deleted). */
unlink(path.c_str());
writeFull(fd, (const unsigned char *) "d", 1);
/* Note that the result of unlink() is ignored; removing the lock
file is an optimisation, not a necessity. */
}
bool lockFile(int fd, LockType lockType, bool wait)
{
struct flock lock;
if (lockType == ltRead) lock.l_type = F_RDLCK;
else if (lockType == ltWrite) lock.l_type = F_WRLCK;
else if (lockType == ltNone) lock.l_type = F_UNLCK;
else abort();
lock.l_whence = SEEK_SET;
lock.l_start = 0;
lock.l_len = 0; /* entire file */
if (wait) {
while (fcntl(fd, F_SETLKW, &lock) != 0) {
checkInterrupt();
if (errno != EINTR)
throw SysError(format("acquiring/releasing lock"));
}
} else {
while (fcntl(fd, F_SETLK, &lock) != 0) {
checkInterrupt();
if (errno == EACCES || errno == EAGAIN) return false;
if (errno != EINTR)
throw SysError(format("acquiring/releasing lock"));
}
}
return true;
}
/* This enables us to check whether are not already holding a lock on
a file ourselves. POSIX locks (fcntl) suck in this respect: if we
close a descriptor, the previous lock will be closed as well. And
there is no way to query whether we already have a lock (F_GETLK
only works on locks held by other processes). */
static StringSet lockedPaths; /* !!! not thread-safe */
PathLocks::PathLocks()
: deletePaths(false)
{
}
PathLocks::PathLocks(const PathSet & paths, const string & waitMsg)
: deletePaths(false)
{
lockPaths(paths, waitMsg);
}
bool PathLocks::lockPaths(const PathSet & _paths,
const string & waitMsg, bool wait)
{
assert(fds.empty());
/* Note that `fds' is built incrementally so that the destructor
will only release those locks that we have already acquired. */
/* Sort the paths. This assures that locks are always acquired in
the same order, thus preventing deadlocks. */
Paths paths(_paths.begin(), _paths.end());
paths.sort();
/* Acquire the lock for each path. */
foreach (Paths::iterator, i, paths) {
checkInterrupt();
Path path = *i;
Path lockPath = path + ".lock";
debug(format("locking path `%1%'") % path);
if (lockedPaths.find(lockPath) != lockedPaths.end())
throw Error("deadlock: trying to re-acquire self-held lock");
AutoCloseFD fd;
while (1) {
/* Open/create the lock file. */
fd = openLockFile(lockPath, true);
/* Acquire an exclusive lock. */
if (!lockFile(fd, ltWrite, false)) {
if (wait) {
if (waitMsg != "") printMsg(lvlError, waitMsg);
lockFile(fd, ltWrite, true);
} else {
/* Failed to lock this path; release all other
locks. */
unlock();
return false;
}
}
debug(format("lock acquired on `%1%'") % lockPath);
/* Check that the lock file hasn't become stale (i.e.,
hasn't been unlinked). */
struct stat st;
if (fstat(fd, &st) == -1)
throw SysError(format("statting lock file `%1%'") % lockPath);
if (st.st_size != 0)
/* This lock file has been unlinked, so we're holding
a lock on a deleted file. This means that other
processes may create and acquire a lock on
`lockPath', and proceed. So we must retry. */
debug(format("open lock file `%1%' has become stale") % lockPath);
else
break;
}
/* Use borrow so that the descriptor isn't closed. */
fds.push_back(FDPair(fd.borrow(), lockPath));
lockedPaths.insert(lockPath);
}
return true;
}
PathLocks::~PathLocks()
{
unlock();
}
void PathLocks::unlock()
{
foreach (list<FDPair>::iterator, i, fds) {
if (deletePaths) deleteLockFile(i->second, i->first);
lockedPaths.erase(i->second);
if (close(i->first) == -1)
printMsg(lvlError,
format("error (ignored): cannot close lock file on `%1%'") % i->second);
debug(format("lock released on `%1%'") % i->second);
}
fds.clear();
}
void PathLocks::setDeletion(bool deletePaths)
{
this->deletePaths = deletePaths;
}
bool pathIsLockedByMe(const Path & path)
{
Path lockPath = path + ".lock";
return lockedPaths.find(lockPath) != lockedPaths.end();
}
}

45
nix/libstore/pathlocks.hh Normal file
View File

@ -0,0 +1,45 @@
#pragma once
#include "types.hh"
namespace nix {
/* Open (possibly create) a lock file and return the file descriptor.
-1 is returned if create is false and the lock could not be opened
because it doesn't exist. Any other error throws an exception. */
int openLockFile(const Path & path, bool create);
/* Delete an open lock file. */
void deleteLockFile(const Path & path, int fd);
enum LockType { ltRead, ltWrite, ltNone };
bool lockFile(int fd, LockType lockType, bool wait);
class PathLocks
{
private:
typedef std::pair<int, Path> FDPair;
list<FDPair> fds;
bool deletePaths;
public:
PathLocks();
PathLocks(const PathSet & paths,
const string & waitMsg = "");
bool lockPaths(const PathSet & _paths,
const string & waitMsg = "",
bool wait = true);
~PathLocks();
void unlock();
void setDeletion(bool deletePaths);
};
bool pathIsLockedByMe(const Path & path);
}

122
nix/libstore/references.cc Normal file
View File

@ -0,0 +1,122 @@
#include "references.hh"
#include "hash.hh"
#include "util.hh"
#include "archive.hh"
#include <map>
#include <cstdlib>
namespace nix {
static unsigned int refLength = 32; /* characters */
static void search(const unsigned char * s, unsigned int len,
StringSet & hashes, StringSet & seen)
{
static bool initialised = false;
static bool isBase32[256];
if (!initialised) {
for (unsigned int i = 0; i < 256; ++i) isBase32[i] = false;
for (unsigned int i = 0; i < base32Chars.size(); ++i)
isBase32[(unsigned char) base32Chars[i]] = true;
initialised = true;
}
for (unsigned int i = 0; i + refLength <= len; ) {
int j;
bool match = true;
for (j = refLength - 1; j >= 0; --j)
if (!isBase32[(unsigned char) s[i + j]]) {
i += j + 1;
match = false;
break;
}
if (!match) continue;
string ref((const char *) s + i, refLength);
if (hashes.find(ref) != hashes.end()) {
debug(format("found reference to `%1%' at offset `%2%'")
% ref % i);
seen.insert(ref);
hashes.erase(ref);
}
++i;
}
}
struct RefScanSink : Sink
{
HashSink hashSink;
StringSet hashes;
StringSet seen;
string tail;
RefScanSink() : hashSink(htSHA256) { }
void operator () (const unsigned char * data, size_t len);
};
void RefScanSink::operator () (const unsigned char * data, size_t len)
{
hashSink(data, len);
/* It's possible that a reference spans the previous and current
fragment, so search in the concatenation of the tail of the
previous fragment and the start of the current fragment. */
string s = tail + string((const char *) data, len > refLength ? refLength : len);
search((const unsigned char *) s.data(), s.size(), hashes, seen);
search(data, len, hashes, seen);
unsigned int tailLen = len <= refLength ? len : refLength;
tail =
string(tail, tail.size() < refLength - tailLen ? 0 : tail.size() - (refLength - tailLen)) +
string((const char *) data + len - tailLen, tailLen);
}
PathSet scanForReferences(const string & path,
const PathSet & refs, HashResult & hash)
{
RefScanSink sink;
std::map<string, Path> backMap;
/* For efficiency (and a higher hit rate), just search for the
hash part of the file name. (This assumes that all references
have the form `HASH-bla'). */
foreach (PathSet::const_iterator, i, refs) {
string baseName = baseNameOf(*i);
string::size_type pos = baseName.find('-');
if (pos == string::npos)
throw Error(format("bad reference `%1%'") % *i);
string s = string(baseName, 0, pos);
assert(s.size() == refLength);
assert(backMap.find(s) == backMap.end());
// parseHash(htSHA256, s);
sink.hashes.insert(s);
backMap[s] = *i;
}
/* Look for the hashes in the NAR dump of the path. */
dumpPath(path, sink);
/* Map the hashes found back to their store paths. */
PathSet found;
foreach (StringSet::iterator, i, sink.seen) {
std::map<string, Path>::iterator j;
if ((j = backMap.find(*i)) == backMap.end()) abort();
found.insert(j->second);
}
hash = sink.hashSink.finish();
return found;
}
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "types.hh"
#include "hash.hh"
namespace nix {
PathSet scanForReferences(const Path & path, const PathSet & refs,
HashResult & hash);
}

View File

@ -0,0 +1,602 @@
#include "serialise.hh"
#include "util.hh"
#include "remote-store.hh"
#include "worker-protocol.hh"
#include "archive.hh"
#include "affinity.hh"
#include "globals.hh"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <iostream>
#include <unistd.h>
#include <cstring>
namespace nix {
Path readStorePath(Source & from)
{
Path path = readString(from);
assertStorePath(path);
return path;
}
template<class T> T readStorePaths(Source & from)
{
T paths = readStrings<T>(from);
foreach (typename T::iterator, i, paths) assertStorePath(*i);
return paths;
}
template PathSet readStorePaths(Source & from);
RemoteStore::RemoteStore()
{
initialised = false;
}
void RemoteStore::openConnection(bool reserveSpace)
{
if (initialised) return;
initialised = true;
string remoteMode = getEnv("NIX_REMOTE");
if (remoteMode == "daemon")
/* Connect to a daemon that does the privileged work for
us. */
connectToDaemon();
else
throw Error(format("invalid setting for NIX_REMOTE, `%1%'") % remoteMode);
from.fd = fdSocket;
to.fd = fdSocket;
/* Send the magic greeting, check for the reply. */
try {
writeInt(WORKER_MAGIC_1, to);
to.flush();
unsigned int magic = readInt(from);
if (magic != WORKER_MAGIC_2) throw Error("protocol mismatch");
daemonVersion = readInt(from);
if (GET_PROTOCOL_MAJOR(daemonVersion) != GET_PROTOCOL_MAJOR(PROTOCOL_VERSION))
throw Error("Nix daemon protocol version not supported");
writeInt(PROTOCOL_VERSION, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 14) {
int cpu = settings.lockCPU ? lockToCurrentCPU() : -1;
if (cpu != -1) {
writeInt(1, to);
writeInt(cpu, to);
} else
writeInt(0, to);
}
if (GET_PROTOCOL_MINOR(daemonVersion) >= 11)
writeInt(reserveSpace, to);
processStderr();
}
catch (Error & e) {
throw Error(format("cannot start worker (%1%)")
% e.msg());
}
setOptions();
}
void RemoteStore::connectToDaemon()
{
fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
if (fdSocket == -1)
throw SysError("cannot create Unix domain socket");
closeOnExec(fdSocket);
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
path name. !!! this is probably a bad idea in multi-threaded
applications... */
AutoCloseFD fdPrevDir = open(".", O_RDONLY);
if (fdPrevDir == -1) throw SysError("couldn't open current directory");
chdir(dirOf(socketPath).c_str());
Path socketPathRel = "./" + baseNameOf(socketPath);
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
if (socketPathRel.size() >= sizeof(addr.sun_path))
throw Error(format("socket path `%1%' is too long") % socketPathRel);
using namespace std;
strcpy(addr.sun_path, socketPathRel.c_str());
if (connect(fdSocket, (struct sockaddr *) &addr, sizeof(addr)) == -1)
throw SysError(format("cannot connect to daemon at `%1%'") % socketPath);
if (fchdir(fdPrevDir) == -1)
throw SysError("couldn't change back to previous directory");
}
RemoteStore::~RemoteStore()
{
try {
to.flush();
fdSocket.close();
if (child != -1)
child.wait(true);
} catch (...) {
ignoreException();
}
}
void RemoteStore::setOptions()
{
writeInt(wopSetOptions, to);
writeInt(settings.keepFailed, to);
writeInt(settings.keepGoing, to);
writeInt(settings.tryFallback, to);
writeInt(verbosity, to);
writeInt(settings.maxBuildJobs, to);
writeInt(settings.maxSilentTime, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 2)
writeInt(settings.useBuildHook, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 4) {
writeInt(settings.buildVerbosity, to);
writeInt(logType, to);
writeInt(settings.printBuildTrace, to);
}
if (GET_PROTOCOL_MINOR(daemonVersion) >= 6)
writeInt(settings.buildCores, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 10)
writeInt(settings.useSubstitutes, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 12) {
Settings::SettingsMap overrides = settings.getOverrides();
writeInt(overrides.size(), to);
foreach (Settings::SettingsMap::iterator, i, overrides) {
writeString(i->first, to);
writeString(i->second, to);
}
}
processStderr();
}
bool RemoteStore::isValidPath(const Path & path)
{
openConnection();
writeInt(wopIsValidPath, to);
writeString(path, to);
processStderr();
unsigned int reply = readInt(from);
return reply != 0;
}
PathSet RemoteStore::queryValidPaths(const PathSet & paths)
{
openConnection();
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
PathSet res;
foreach (PathSet::const_iterator, i, paths)
if (isValidPath(*i)) res.insert(*i);
return res;
} else {
writeInt(wopQueryValidPaths, to);
writeStrings(paths, to);
processStderr();
return readStorePaths<PathSet>(from);
}
}
PathSet RemoteStore::queryAllValidPaths()
{
openConnection();
writeInt(wopQueryAllValidPaths, to);
processStderr();
return readStorePaths<PathSet>(from);
}
PathSet RemoteStore::querySubstitutablePaths(const PathSet & paths)
{
openConnection();
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
PathSet res;
foreach (PathSet::const_iterator, i, paths) {
writeInt(wopHasSubstitutes, to);
writeString(*i, to);
processStderr();
if (readInt(from)) res.insert(*i);
}
return res;
} else {
writeInt(wopQuerySubstitutablePaths, to);
writeStrings(paths, to);
processStderr();
return readStorePaths<PathSet>(from);
}
}
void RemoteStore::querySubstitutablePathInfos(const PathSet & paths,
SubstitutablePathInfos & infos)
{
if (paths.empty()) return;
openConnection();
if (GET_PROTOCOL_MINOR(daemonVersion) < 3) return;
if (GET_PROTOCOL_MINOR(daemonVersion) < 12) {
foreach (PathSet::const_iterator, i, paths) {
SubstitutablePathInfo info;
writeInt(wopQuerySubstitutablePathInfo, to);
writeString(*i, to);
processStderr();
unsigned int reply = readInt(from);
if (reply == 0) continue;
info.deriver = readString(from);
if (info.deriver != "") assertStorePath(info.deriver);
info.references = readStorePaths<PathSet>(from);
info.downloadSize = readLongLong(from);
info.narSize = GET_PROTOCOL_MINOR(daemonVersion) >= 7 ? readLongLong(from) : 0;
infos[*i] = info;
}
} else {
writeInt(wopQuerySubstitutablePathInfos, to);
writeStrings(paths, to);
processStderr();
unsigned int count = readInt(from);
for (unsigned int n = 0; n < count; n++) {
Path path = readStorePath(from);
SubstitutablePathInfo & info(infos[path]);
info.deriver = readString(from);
if (info.deriver != "") assertStorePath(info.deriver);
info.references = readStorePaths<PathSet>(from);
info.downloadSize = readLongLong(from);
info.narSize = readLongLong(from);
}
}
}
ValidPathInfo RemoteStore::queryPathInfo(const Path & path)
{
openConnection();
writeInt(wopQueryPathInfo, to);
writeString(path, to);
processStderr();
ValidPathInfo info;
info.path = path;
info.deriver = readString(from);
if (info.deriver != "") assertStorePath(info.deriver);
info.hash = parseHash(htSHA256, readString(from));
info.references = readStorePaths<PathSet>(from);
info.registrationTime = readInt(from);
info.narSize = readLongLong(from);
return info;
}
Hash RemoteStore::queryPathHash(const Path & path)
{
openConnection();
writeInt(wopQueryPathHash, to);
writeString(path, to);
processStderr();
string hash = readString(from);
return parseHash(htSHA256, hash);
}
void RemoteStore::queryReferences(const Path & path,
PathSet & references)
{
openConnection();
writeInt(wopQueryReferences, to);
writeString(path, to);
processStderr();
PathSet references2 = readStorePaths<PathSet>(from);
references.insert(references2.begin(), references2.end());
}
void RemoteStore::queryReferrers(const Path & path,
PathSet & referrers)
{
openConnection();
writeInt(wopQueryReferrers, to);
writeString(path, to);
processStderr();
PathSet referrers2 = readStorePaths<PathSet>(from);
referrers.insert(referrers2.begin(), referrers2.end());
}
Path RemoteStore::queryDeriver(const Path & path)
{
openConnection();
writeInt(wopQueryDeriver, to);
writeString(path, to);
processStderr();
Path drvPath = readString(from);
if (drvPath != "") assertStorePath(drvPath);
return drvPath;
}
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();
writeInt(wopQueryDerivationOutputs, to);
writeString(path, to);
processStderr();
return readStorePaths<PathSet>(from);
}
PathSet RemoteStore::queryDerivationOutputNames(const Path & path)
{
openConnection();
writeInt(wopQueryDerivationOutputNames, to);
writeString(path, to);
processStderr();
return readStrings<PathSet>(from);
}
Path RemoteStore::queryPathFromHashPart(const string & hashPart)
{
openConnection();
writeInt(wopQueryPathFromHashPart, to);
writeString(hashPart, to);
processStderr();
Path path = readString(from);
if (!path.empty()) assertStorePath(path);
return path;
}
Path RemoteStore::addToStore(const Path & _srcPath,
bool recursive, HashType hashAlgo, PathFilter & filter, bool repair)
{
if (repair) throw Error("repairing is not supported when building through the Nix daemon");
openConnection();
Path srcPath(absPath(_srcPath));
writeInt(wopAddToStore, to);
writeString(baseNameOf(srcPath), to);
/* backwards compatibility hack */
writeInt((hashAlgo == htSHA256 && recursive) ? 0 : 1, to);
writeInt(recursive ? 1 : 0, to);
writeString(printHashType(hashAlgo), to);
dumpPath(srcPath, to, filter);
processStderr();
return readStorePath(from);
}
Path RemoteStore::addTextToStore(const string & name, const string & s,
const PathSet & references, bool repair)
{
if (repair) throw Error("repairing is not supported when building through the Nix daemon");
openConnection();
writeInt(wopAddTextToStore, to);
writeString(name, to);
writeString(s, to);
writeStrings(references, to);
processStderr();
return readStorePath(from);
}
void RemoteStore::exportPath(const Path & path, bool sign,
Sink & sink)
{
openConnection();
writeInt(wopExportPath, to);
writeString(path, to);
writeInt(sign ? 1 : 0, to);
processStderr(&sink); /* sink receives the actual data */
readInt(from);
}
Paths RemoteStore::importPaths(bool requireSignature, Source & source)
{
openConnection();
writeInt(wopImportPaths, to);
/* We ignore requireSignature, since the worker forces it to true
anyway. */
processStderr(0, &source);
return readStorePaths<Paths>(from);
}
void RemoteStore::buildPaths(const PathSet & drvPaths, BuildMode buildMode)
{
if (buildMode != bmNormal) throw Error("repairing or checking is not supported when building through the Nix daemon");
openConnection();
writeInt(wopBuildPaths, 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);
}
void RemoteStore::ensurePath(const Path & path)
{
openConnection();
writeInt(wopEnsurePath, to);
writeString(path, to);
processStderr();
readInt(from);
}
void RemoteStore::addTempRoot(const Path & path)
{
openConnection();
writeInt(wopAddTempRoot, to);
writeString(path, to);
processStderr();
readInt(from);
}
void RemoteStore::addIndirectRoot(const Path & path)
{
openConnection();
writeInt(wopAddIndirectRoot, to);
writeString(path, to);
processStderr();
readInt(from);
}
void RemoteStore::syncWithGC()
{
openConnection();
writeInt(wopSyncWithGC, to);
processStderr();
readInt(from);
}
Roots RemoteStore::findRoots()
{
openConnection();
writeInt(wopFindRoots, to);
processStderr();
unsigned int count = readInt(from);
Roots result;
while (count--) {
Path link = readString(from);
Path target = readStorePath(from);
result[link] = target;
}
return result;
}
void RemoteStore::collectGarbage(const GCOptions & options, GCResults & results)
{
openConnection(false);
writeInt(wopCollectGarbage, to);
writeInt(options.action, to);
writeStrings(options.pathsToDelete, to);
writeInt(options.ignoreLiveness, to);
writeLongLong(options.maxFreed, to);
writeInt(0, to);
if (GET_PROTOCOL_MINOR(daemonVersion) >= 5) {
/* removed options */
writeInt(0, to);
writeInt(0, to);
}
processStderr();
results.paths = readStrings<PathSet>(from);
results.bytesFreed = readLongLong(from);
readLongLong(from); // obsolete
}
PathSet RemoteStore::queryFailedPaths()
{
openConnection();
writeInt(wopQueryFailedPaths, to);
processStderr();
return readStorePaths<PathSet>(from);
}
void RemoteStore::clearFailedPaths(const PathSet & paths)
{
openConnection();
writeInt(wopClearFailedPaths, to);
writeStrings(paths, to);
processStderr();
readInt(from);
}
void RemoteStore::processStderr(Sink * sink, Source * source)
{
to.flush();
unsigned int msg;
while ((msg = readInt(from)) == STDERR_NEXT
|| msg == STDERR_READ || msg == STDERR_WRITE) {
if (msg == STDERR_WRITE) {
string s = readString(from);
if (!sink) throw Error("no sink");
(*sink)((const unsigned char *) s.data(), s.size());
}
else if (msg == STDERR_READ) {
if (!source) throw Error("no source");
size_t len = readInt(from);
unsigned char * buf = new unsigned char[len];
AutoDeleteArray<unsigned char> d(buf);
writeString(buf, source->read(buf, len), to);
to.flush();
}
else {
string s = readString(from);
writeToStderr(s);
}
}
if (msg == STDERR_ERROR) {
string error = readString(from);
unsigned int status = GET_PROTOCOL_MINOR(daemonVersion) >= 8 ? readInt(from) : 1;
throw Error(format("%1%") % error, status);
}
else if (msg != STDERR_LAST)
throw Error("protocol error processing standard error");
}
}

View File

@ -0,0 +1,104 @@
#pragma once
#include <string>
#include "store-api.hh"
namespace nix {
class Pipe;
class Pid;
struct FdSink;
struct FdSource;
class RemoteStore : public StoreAPI
{
public:
RemoteStore();
~RemoteStore();
/* Implementations of abstract store API methods. */
bool isValidPath(const Path & path);
PathSet queryValidPaths(const PathSet & paths);
PathSet queryAllValidPaths();
ValidPathInfo queryPathInfo(const Path & path);
Hash queryPathHash(const Path & path);
void queryReferences(const Path & path, PathSet & references);
void queryReferrers(const Path & path, PathSet & referrers);
Path queryDeriver(const Path & path);
PathSet queryValidDerivers(const Path & path);
PathSet queryDerivationOutputs(const Path & path);
StringSet queryDerivationOutputNames(const Path & path);
Path queryPathFromHashPart(const string & hashPart);
PathSet querySubstitutablePaths(const PathSet & paths);
void querySubstitutablePathInfos(const PathSet & paths,
SubstitutablePathInfos & infos);
Path addToStore(const Path & srcPath,
bool recursive = true, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter, bool repair = false);
Path addTextToStore(const string & name, const string & s,
const PathSet & references, bool repair = false);
void exportPath(const Path & path, bool sign,
Sink & sink);
Paths importPaths(bool requireSignature, Source & source);
void buildPaths(const PathSet & paths, BuildMode buildMode);
void ensurePath(const Path & path);
void addTempRoot(const Path & path);
void addIndirectRoot(const Path & path);
void syncWithGC();
Roots findRoots();
void collectGarbage(const GCOptions & options, GCResults & results);
PathSet queryFailedPaths();
void clearFailedPaths(const PathSet & paths);
private:
AutoCloseFD fdSocket;
FdSink to;
FdSource from;
Pid child;
unsigned int daemonVersion;
bool initialised;
void openConnection(bool reserveSpace = true);
void processStderr(Sink * sink = 0, Source * source = 0);
void connectToDaemon();
void setOptions();
};
}

44
nix/libstore/schema.sql Normal file
View File

@ -0,0 +1,44 @@
create table if not exists ValidPaths (
id integer primary key autoincrement not null,
path text unique not null,
hash text not null,
registrationTime integer not null,
deriver text,
narSize integer
);
create table if not exists Refs (
referrer integer not null,
reference integer not null,
primary key (referrer, reference),
foreign key (referrer) references ValidPaths(id) on delete cascade,
foreign key (reference) references ValidPaths(id) on delete restrict
);
create index if not exists IndexReferrer on Refs(referrer);
create index if not exists IndexReference on Refs(reference);
-- Paths can refer to themselves, causing a tuple (N, N) in the Refs
-- table. This causes a deletion of the corresponding row in
-- ValidPaths to cause a foreign key constraint violation (due to `on
-- delete restrict' on the `reference' column). Therefore, explicitly
-- get rid of self-references.
create trigger if not exists DeleteSelfRefs before delete on ValidPaths
begin
delete from Refs where referrer = old.id and reference = old.id;
end;
create table if not exists DerivationOutputs (
drv integer not null,
id text not null, -- symbolic output id, usually "out"
path text not null,
primary key (drv, id),
foreign key (drv) references ValidPaths(id) on delete cascade
);
create index if not exists IndexDerivationOutputs on DerivationOutputs(path);
create table if not exists FailedPaths (
path text primary key not null,
time integer not null
);

331
nix/libstore/store-api.cc Normal file
View File

@ -0,0 +1,331 @@
#include "store-api.hh"
#include "globals.hh"
#include "util.hh"
#include <climits>
namespace nix {
GCOptions::GCOptions()
{
action = gcDeleteDead;
ignoreLiveness = false;
maxFreed = ULLONG_MAX;
}
bool isInStore(const Path & path)
{
return isInDir(path, settings.nixStore);
}
bool isStorePath(const Path & path)
{
return isInStore(path)
&& path.find('/', settings.nixStore.size() + 1) == Path::npos;
}
void assertStorePath(const Path & path)
{
if (!isStorePath(path))
throw Error(format("path `%1%' is not in the Nix store") % path);
}
Path toStorePath(const Path & path)
{
if (!isInStore(path))
throw Error(format("path `%1%' is not in the Nix store") % path);
Path::size_type slash = path.find('/', settings.nixStore.size() + 1);
if (slash == Path::npos)
return path;
else
return Path(path, 0, slash);
}
Path followLinksToStore(const Path & _path)
{
Path path = absPath(_path);
while (!isInStore(path)) {
if (!isLink(path)) break;
string target = readLink(path);
path = absPath(target, dirOf(path));
}
if (!isInStore(path))
throw Error(format("path `%1%' is not in the Nix store") % path);
return path;
}
Path followLinksToStorePath(const Path & path)
{
return toStorePath(followLinksToStore(path));
}
string storePathToName(const Path & path)
{
assertStorePath(path);
return string(path, settings.nixStore.size() + 34);
}
void checkStoreName(const string & name)
{
string validChars = "+-._?=";
/* Disallow names starting with a dot for possible security
reasons (e.g., "." and ".."). */
if (string(name, 0, 1) == ".")
throw Error(format("illegal name: `%1%'") % name);
foreach (string::const_iterator, i, name)
if (!((*i >= 'A' && *i <= 'Z') ||
(*i >= 'a' && *i <= 'z') ||
(*i >= '0' && *i <= '9') ||
validChars.find(*i) != string::npos))
{
throw Error(format("invalid character `%1%' in name `%2%'")
% *i % name);
}
}
/* Store paths have the following form:
<store>/<h>-<name>
where
<store> = the location of the Nix store, usually /nix/store
<name> = a human readable name for the path, typically obtained
from the name attribute of the derivation, or the name of the
source file from which the store path is created. For derivation
outputs other than the default "out" output, the string "-<id>"
is suffixed to <name>.
<h> = base-32 representation of the first 160 bits of a SHA-256
hash of <s>; the hash part of the store name
<s> = the string "<type>:sha256:<h2>:<store>:<name>";
note that it includes the location of the store as well as the
name to make sure that changes to either of those are reflected
in the hash (e.g. you won't get /nix/store/<h>-name1 and
/nix/store/<h>-name2 with equal hash parts).
<type> = one of:
"text:<r1>:<r2>:...<rN>"
for plain text files written to the store using
addTextToStore(); <r1> ... <rN> are the references of the
path.
"source"
for paths copied to the store using addToStore() when recursive
= true and hashAlgo = "sha256"
"output:<id>"
for either the outputs created by derivations, OR paths copied
to the store using addToStore() with recursive != true or
hashAlgo != "sha256" (in that case "source" is used; it's
silly, but it's done that way for compatibility). <id> is the
name of the output (usually, "out").
<h2> = base-16 representation of a SHA-256 hash of:
if <type> = "text:...":
the string written to the resulting store path
if <type> = "source":
the serialisation of the path from which this store path is
copied, as returned by hashPath()
if <type> = "output:out":
for non-fixed derivation outputs:
the derivation (see hashDerivationModulo() in
primops.cc)
for paths copied by addToStore() or produced by fixed-output
derivations:
the string "fixed:out:<rec><algo>:<hash>:", where
<rec> = "r:" for recursive (path) hashes, or "" or flat
(file) hashes
<algo> = "md5", "sha1" or "sha256"
<hash> = base-16 representation of the path or flat hash of
the contents of the path (or expected contents of the
path for fixed-output derivations)
It would have been nicer to handle fixed-output derivations under
"source", e.g. have something like "source:<rec><algo>", but we're
stuck with this for now...
The main reason for this way of computing names is to prevent name
collisions (for security). For instance, it shouldn't be feasible
to come up with a derivation whose output path collides with the
path for a copied source. The former would have a <s> starting with
"output:out:", while the latter would have a <2> starting with
"source:".
*/
Path makeStorePath(const string & type,
const Hash & hash, const string & name)
{
/* e.g., "source:sha256:1abc...:/nix/store:foo.tar.gz" */
string s = type + ":sha256:" + printHash(hash) + ":"
+ settings.nixStore + ":" + name;
checkStoreName(name);
return settings.nixStore + "/"
+ printHash32(compressHash(hashString(htSHA256, s), 20))
+ "-" + name;
}
Path makeOutputPath(const string & id,
const Hash & hash, const string & name)
{
return makeStorePath("output:" + id, hash,
name + (id == "out" ? "" : "-" + id));
}
Path makeFixedOutputPath(bool recursive,
HashType hashAlgo, Hash hash, string name)
{
return hashAlgo == htSHA256 && recursive
? makeStorePath("source", hash, name)
: makeStorePath("output:out", hashString(htSHA256,
"fixed:out:" + (recursive ? (string) "r:" : "") +
printHashType(hashAlgo) + ":" + printHash(hash) + ":"),
name);
}
std::pair<Path, Hash> computeStorePathForPath(const Path & srcPath,
bool recursive, HashType hashAlgo, PathFilter & filter)
{
HashType ht(hashAlgo);
Hash h = recursive ? hashPath(ht, srcPath, filter).first : hashFile(ht, srcPath);
string name = baseNameOf(srcPath);
Path dstPath = makeFixedOutputPath(recursive, hashAlgo, h, name);
return std::pair<Path, Hash>(dstPath, h);
}
Path computeStorePathForText(const string & name, const string & s,
const PathSet & references)
{
Hash hash = hashString(htSHA256, s);
/* Stuff the references (if any) into the type. This is a bit
hacky, but we can't put them in `s' since that would be
ambiguous. */
string type = "text";
foreach (PathSet::const_iterator, i, references) {
type += ":";
type += *i;
}
return makeStorePath(type, hash, name);
}
/* Return a string accepted by decodeValidPathInfo() that
registers the specified paths as valid. Note: it's the
responsibility of the caller to provide a closure. */
string StoreAPI::makeValidityRegistration(const PathSet & paths,
bool showDerivers, bool showHash)
{
string s = "";
foreach (PathSet::iterator, i, paths) {
s += *i + "\n";
ValidPathInfo info = queryPathInfo(*i);
if (showHash) {
s += printHash(info.hash) + "\n";
s += (format("%1%\n") % info.narSize).str();
}
Path deriver = showDerivers ? info.deriver : "";
s += deriver + "\n";
s += (format("%1%\n") % info.references.size()).str();
foreach (PathSet::iterator, j, info.references)
s += *j + "\n";
}
return s;
}
ValidPathInfo decodeValidPathInfo(std::istream & str, bool hashGiven)
{
ValidPathInfo info;
getline(str, info.path);
if (str.eof()) { info.path = ""; return info; }
if (hashGiven) {
string s;
getline(str, s);
info.hash = parseHash(htSHA256, s);
getline(str, s);
if (!string2Int(s, info.narSize)) throw Error("number expected");
}
getline(str, info.deriver);
string s; int n;
getline(str, s);
if (!string2Int(s, n)) throw Error("number expected");
while (n--) {
getline(str, s);
info.references.insert(s);
}
if (!str || str.eof()) throw Error("missing input");
return info;
}
string showPaths(const PathSet & paths)
{
string s;
foreach (PathSet::const_iterator, i, paths) {
if (s.size() != 0) s += ", ";
s += "`" + *i + "'";
}
return s;
}
void exportPaths(StoreAPI & store, const Paths & paths,
bool sign, Sink & sink)
{
foreach (Paths::const_iterator, i, paths) {
writeInt(1, sink);
store.exportPath(*i, sign, sink);
}
writeInt(0, sink);
}
}
#include "local-store.hh"
#include "serialise.hh"
#include "remote-store.hh"
namespace nix {
std::shared_ptr<StoreAPI> store;
std::shared_ptr<StoreAPI> openStore(bool reserveSpace)
{
if (getEnv("NIX_REMOTE") == "")
return std::shared_ptr<StoreAPI>(new LocalStore(reserveSpace));
else
return std::shared_ptr<StoreAPI>(new RemoteStore());
}
}

366
nix/libstore/store-api.hh Normal file
View File

@ -0,0 +1,366 @@
#pragma once
#include "hash.hh"
#include "serialise.hh"
#include <string>
#include <map>
#include <memory>
namespace nix {
typedef std::map<Path, Path> Roots;
struct GCOptions
{
/* Garbage collector operation:
- `gcReturnLive': return the set of paths reachable from
(i.e. in the closure of) the roots.
- `gcReturnDead': return the set of paths not reachable from
the roots.
- `gcDeleteDead': actually delete the latter set.
- `gcDeleteSpecific': delete the paths listed in
`pathsToDelete', insofar as they are not reachable.
*/
typedef enum {
gcReturnLive,
gcReturnDead,
gcDeleteDead,
gcDeleteSpecific,
} GCAction;
GCAction action;
/* If `ignoreLiveness' is set, then reachability from the roots is
ignored (dangerous!). However, the paths must still be
unreferenced *within* the store (i.e., there can be no other
store paths that depend on them). */
bool ignoreLiveness;
/* For `gcDeleteSpecific', the paths to delete. */
PathSet pathsToDelete;
/* Stop after at least `maxFreed' bytes have been freed. */
unsigned long long maxFreed;
GCOptions();
};
struct GCResults
{
/* Depending on the action, the GC roots, or the paths that would
be or have been deleted. */
PathSet paths;
/* For `gcReturnDead', `gcDeleteDead' and `gcDeleteSpecific', the
number of bytes that would be or was freed. */
unsigned long long bytesFreed;
GCResults()
{
bytesFreed = 0;
}
};
struct SubstitutablePathInfo
{
Path deriver;
PathSet references;
unsigned long long downloadSize; /* 0 = unknown or inapplicable */
unsigned long long narSize; /* 0 = unknown */
};
typedef std::map<Path, SubstitutablePathInfo> SubstitutablePathInfos;
struct ValidPathInfo
{
Path path;
Path deriver;
Hash hash;
PathSet references;
time_t registrationTime;
unsigned long long narSize; // 0 = unknown
unsigned long long id; // internal use only
ValidPathInfo() : registrationTime(0), narSize(0) { }
};
typedef list<ValidPathInfo> ValidPathInfos;
enum BuildMode { bmNormal, bmRepair, bmCheck };
class StoreAPI
{
public:
virtual ~StoreAPI() { }
/* Check whether a path is valid. */
virtual bool isValidPath(const Path & path) = 0;
/* Query which of the given paths is valid. */
virtual PathSet queryValidPaths(const PathSet & paths) = 0;
/* Query the set of all valid paths. */
virtual PathSet queryAllValidPaths() = 0;
/* Query information about a valid path. */
virtual ValidPathInfo queryPathInfo(const Path & path) = 0;
/* Query the hash of a valid path. */
virtual Hash queryPathHash(const Path & path) = 0;
/* Query the set of outgoing FS references for a store path. The
result is not cleared. */
virtual void queryReferences(const Path & path,
PathSet & references) = 0;
/* Queries the set of incoming FS references for a store path.
The result is not cleared. */
virtual void queryReferrers(const Path & path,
PathSet & referrers) = 0;
/* Query the deriver of a store path. Return the empty string if
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;
/* Query the output names of the derivation denoted by `path'. */
virtual StringSet queryDerivationOutputNames(const Path & path) = 0;
/* Query the full store path given the hash part of a valid store
path, or "" if the path doesn't exist. */
virtual Path queryPathFromHashPart(const string & hashPart) = 0;
/* Query which of the given paths have substitutes. */
virtual PathSet querySubstitutablePaths(const PathSet & paths) = 0;
/* Query substitute info (i.e. references, derivers and download
sizes) of a set of paths. If a path does not have substitute
info, it's omitted from the resulting infos map. */
virtual void querySubstitutablePathInfos(const PathSet & paths,
SubstitutablePathInfos & infos) = 0;
/* Copy the contents of a path to the store and register the
validity the resulting path. The resulting path is returned.
The function object `filter' can be used to exclude files (see
libutil/archive.hh). */
virtual Path addToStore(const Path & srcPath,
bool recursive = true, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter, bool repair = false) = 0;
/* Like addToStore, but the contents written to the output path is
a regular file containing the given string. */
virtual Path addTextToStore(const string & name, const string & s,
const PathSet & references, bool repair = false) = 0;
/* Export a store path, that is, create a NAR dump of the store
path and append its references and its deriver. Optionally, a
cryptographic signature (created by OpenSSL) of the preceding
data is attached. */
virtual void exportPath(const Path & path, bool sign,
Sink & sink) = 0;
/* Import a sequence of NAR dumps created by exportPaths() into
the Nix store. */
virtual Paths importPaths(bool requireSignature, Source & source) = 0;
/* For each path, if it's a derivation, build it. Building a
derivation means ensuring that the output paths are valid. If
they are already valid, this is a no-op. Otherwise, validity
can be reached in two ways. First, if the output paths is
substitutable, then build the path that way. Second, the
output paths can be created by running the builder, after
recursively building any sub-derivations. For inputs that are
not derivations, substitute them. */
virtual void buildPaths(const PathSet & paths, BuildMode buildMode = bmNormal) = 0;
/* Ensure that a path is valid. If it is not currently valid, it
may be made valid by running a substitute (if defined for the
path). */
virtual void ensurePath(const Path & path) = 0;
/* Add a store path as a temporary root of the garbage collector.
The root disappears as soon as we exit. */
virtual void addTempRoot(const Path & path) = 0;
/* Add an indirect root, which is merely a symlink to `path' from
/nix/var/nix/gcroots/auto/<hash of `path'>. `path' is supposed
to be a symlink to a store path. The garbage collector will
automatically remove the indirect root when it finds that
`path' has disappeared. */
virtual void addIndirectRoot(const Path & path) = 0;
/* Acquire the global GC lock, then immediately release it. This
function must be called after registering a new permanent root,
but before exiting. Otherwise, it is possible that a running
garbage collector doesn't see the new root and deletes the
stuff we've just built. By acquiring the lock briefly, we
ensure that either:
- The collector is already running, and so we block until the
collector is finished. The collector will know about our
*temporary* locks, which should include whatever it is we
want to register as a permanent lock.
- The collector isn't running, or it's just started but hasn't
acquired the GC lock yet. In that case we get and release
the lock right away, then exit. The collector scans the
permanent root and sees our's.
In either case the permanent root is seen by the collector. */
virtual void syncWithGC() = 0;
/* Find the roots of the garbage collector. Each root is a pair
(link, storepath) where `link' is the path of the symlink
outside of the Nix store that point to `storePath'. */
virtual Roots findRoots() = 0;
/* Perform a garbage collection. */
virtual void collectGarbage(const GCOptions & options, GCResults & results) = 0;
/* Return the set of paths that have failed to build.*/
virtual PathSet queryFailedPaths() = 0;
/* Clear the "failed" status of the given paths. The special
value `*' causes all failed paths to be cleared. */
virtual void clearFailedPaths(const PathSet & paths) = 0;
/* Return a string representing information about the path that
can be loaded into the database using `nix-store --load-db' or
`nix-store --register-validity'. */
string makeValidityRegistration(const PathSet & paths,
bool showDerivers, bool showHash);
};
/* !!! These should be part of the store API, I guess. */
/* Throw an exception if `path' is not directly in the Nix store. */
void assertStorePath(const Path & path);
bool isInStore(const Path & path);
bool isStorePath(const Path & path);
/* Extract the name part of the given store path. */
string storePathToName(const Path & path);
void checkStoreName(const string & name);
/* Chop off the parts after the top-level store name, e.g.,
/nix/store/abcd-foo/bar => /nix/store/abcd-foo. */
Path toStorePath(const Path & path);
/* Follow symlinks until we end up with a path in the Nix store. */
Path followLinksToStore(const Path & path);
/* Same as followLinksToStore(), but apply toStorePath() to the
result. */
Path followLinksToStorePath(const Path & path);
/* Constructs a unique store path name. */
Path makeStorePath(const string & type,
const Hash & hash, const string & name);
Path makeOutputPath(const string & id,
const Hash & hash, const string & name);
Path makeFixedOutputPath(bool recursive,
HashType hashAlgo, Hash hash, string name);
/* This is the preparatory part of addToStore() and addToStoreFixed();
it computes the store path to which srcPath is to be copied.
Returns the store path and the cryptographic hash of the
contents of srcPath. */
std::pair<Path, Hash> computeStorePathForPath(const Path & srcPath,
bool recursive = true, HashType hashAlgo = htSHA256,
PathFilter & filter = defaultPathFilter);
/* Preparatory part of addTextToStore().
!!! Computation of the path should take the references given to
addTextToStore() into account, otherwise we have a (relatively
minor) security hole: a caller can register a source file with
bogus references. If there are too many references, the path may
not be garbage collected when it has to be (not really a problem,
the caller could create a root anyway), or it may be garbage
collected when it shouldn't be (more serious).
Hashing the references would solve this (bogus references would
simply yield a different store path, so other users wouldn't be
affected), but it has some backwards compatibility issues (the
hashing scheme changes), so I'm not doing that for now. */
Path computeStorePathForText(const string & name, const string & s,
const PathSet & references);
/* Remove the temporary roots file for this process. Any temporary
root becomes garbage after this point unless it has been registered
as a (permanent) root. */
void removeTempRoots();
/* Register a permanent GC root. */
Path addPermRoot(StoreAPI & store, const Path & storePath,
const Path & gcRoot, bool indirect, bool allowOutsideRootsDir = false);
/* Sort a set of paths topologically under the references relation.
If p refers to q, then p preceeds q in this list. */
Paths topoSortPaths(StoreAPI & store, const PathSet & paths);
/* For now, there is a single global store API object, but we'll
purify that in the future. */
extern std::shared_ptr<StoreAPI> store;
/* Factory method: open the Nix database, either through the local or
remote implementation. */
std::shared_ptr<StoreAPI> openStore(bool reserveSpace = true);
/* Display a set of paths in human-readable form (i.e., between quotes
and separated by commas). */
string showPaths(const PathSet & paths);
ValidPathInfo decodeValidPathInfo(std::istream & str,
bool hashGiven = false);
/* Export multiple paths in the format expected by nix-store
--import. */
void exportPaths(StoreAPI & store, const Paths & paths,
bool sign, Sink & sink);
MakeError(SubstError, Error)
MakeError(BuildError, Error) /* denotes a permanent build failure */
}

View File

@ -0,0 +1,60 @@
#pragma once
namespace nix {
#define WORKER_MAGIC_1 0x6e697863
#define WORKER_MAGIC_2 0x6478696f
#define PROTOCOL_VERSION 0x10e
#define GET_PROTOCOL_MAJOR(x) ((x) & 0xff00)
#define GET_PROTOCOL_MINOR(x) ((x) & 0x00ff)
typedef enum {
wopQuit = 0,
wopIsValidPath = 1,
wopHasSubstitutes = 3,
wopQueryPathHash = 4,
wopQueryReferences = 5,
wopQueryReferrers = 6,
wopAddToStore = 7,
wopAddTextToStore = 8,
wopBuildPaths = 9,
wopEnsurePath = 10,
wopAddTempRoot = 11,
wopAddIndirectRoot = 12,
wopSyncWithGC = 13,
wopFindRoots = 14,
wopExportPath = 16,
wopQueryDeriver = 18,
wopSetOptions = 19,
wopCollectGarbage = 20,
wopQuerySubstitutablePathInfo = 21,
wopQueryDerivationOutputs = 22,
wopQueryAllValidPaths = 23,
wopQueryFailedPaths = 24,
wopClearFailedPaths = 25,
wopQueryPathInfo = 26,
wopImportPaths = 27,
wopQueryDerivationOutputNames = 28,
wopQueryPathFromHashPart = 29,
wopQuerySubstitutablePathInfos = 30,
wopQueryValidPaths = 31,
wopQuerySubstitutablePaths = 32,
wopQueryValidDerivers = 33,
} WorkerOp;
#define STDERR_NEXT 0x6f6c6d67
#define STDERR_READ 0x64617461 // data needed from source
#define STDERR_WRITE 0x64617416 // data for sink
#define STDERR_LAST 0x616c7473
#define STDERR_ERROR 0x63787470
Path readStorePath(Source & from);
template<class T> T readStorePaths(Source & from);
}

55
nix/libutil/affinity.cc Normal file
View File

@ -0,0 +1,55 @@
#include "types.hh"
#include "util.hh"
#include "affinity.hh"
#if HAVE_SCHED_H
#include <sched.h>
#endif
namespace nix {
#if HAVE_SCHED_SETAFFINITY
static bool didSaveAffinity = false;
static cpu_set_t savedAffinity;
#endif
void setAffinityTo(int cpu)
{
#if HAVE_SCHED_SETAFFINITY
if (sched_getaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1) return;
didSaveAffinity = true;
printMsg(lvlDebug, format("locking this thread to CPU %1%") % cpu);
cpu_set_t newAffinity;
CPU_ZERO(&newAffinity);
CPU_SET(cpu, &newAffinity);
if (sched_setaffinity(0, sizeof(cpu_set_t), &newAffinity) == -1)
printMsg(lvlError, format("failed to lock thread to CPU %1%") % cpu);
#endif
}
int lockToCurrentCPU()
{
#if HAVE_SCHED_SETAFFINITY
int cpu = sched_getcpu();
if (cpu != -1) setAffinityTo(cpu);
return cpu;
#else
return -1;
#endif
}
void restoreAffinity()
{
#if HAVE_SCHED_SETAFFINITY
if (!didSaveAffinity) return;
if (sched_setaffinity(0, sizeof(cpu_set_t), &savedAffinity) == -1)
printMsg(lvlError, "failed to restore affinity %1%");
#endif
}
}

9
nix/libutil/affinity.hh Normal file
View File

@ -0,0 +1,9 @@
#pragma once
namespace nix {
void setAffinityTo(int cpu);
int lockToCurrentCPU();
void restoreAffinity();
}

335
nix/libutil/archive.cc Normal file
View File

@ -0,0 +1,335 @@
#include "config.h"
#include <cerrno>
#include <algorithm>
#include <vector>
#define _XOPEN_SOURCE 600
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include "archive.hh"
#include "util.hh"
namespace nix {
static string archiveVersion1 = "nix-archive-1";
PathFilter defaultPathFilter;
static void dump(const string & path, Sink & sink, PathFilter & filter);
static void dumpEntries(const Path & path, Sink & sink, PathFilter & filter)
{
Strings names = readDirectory(path);
vector<string> names2(names.begin(), names.end());
sort(names2.begin(), names2.end());
for (vector<string>::iterator i = names2.begin();
i != names2.end(); ++i)
{
Path entry = path + "/" + *i;
if (filter(entry)) {
writeString("entry", sink);
writeString("(", sink);
writeString("name", sink);
writeString(*i, sink);
writeString("node", sink);
dump(entry, sink, filter);
writeString(")", sink);
}
}
}
static void dumpContents(const Path & path, size_t size,
Sink & sink)
{
writeString("contents", sink);
writeLongLong(size, sink);
AutoCloseFD fd = open(path.c_str(), O_RDONLY);
if (fd == -1) throw SysError(format("opening file `%1%'") % path);
unsigned char buf[65536];
size_t left = size;
while (left > 0) {
size_t n = left > sizeof(buf) ? sizeof(buf) : left;
readFull(fd, buf, n);
left -= n;
sink(buf, n);
}
writePadding(size, sink);
}
static void dump(const Path & path, Sink & sink, PathFilter & filter)
{
struct stat st;
if (lstat(path.c_str(), &st))
throw SysError(format("getting attributes of path `%1%'") % path);
writeString("(", sink);
if (S_ISREG(st.st_mode)) {
writeString("type", sink);
writeString("regular", sink);
if (st.st_mode & S_IXUSR) {
writeString("executable", sink);
writeString("", sink);
}
dumpContents(path, (size_t) st.st_size, sink);
}
else if (S_ISDIR(st.st_mode)) {
writeString("type", sink);
writeString("directory", sink);
dumpEntries(path, sink, filter);
}
else if (S_ISLNK(st.st_mode)) {
writeString("type", sink);
writeString("symlink", sink);
writeString("target", sink);
writeString(readLink(path), sink);
}
else throw Error(format("file `%1%' has an unknown type") % path);
writeString(")", sink);
}
void dumpPath(const Path & path, Sink & sink, PathFilter & filter)
{
writeString(archiveVersion1, sink);
dump(path, sink, filter);
}
static SerialisationError badArchive(string s)
{
return SerialisationError("bad archive: " + s);
}
static void skipGeneric(Source & source)
{
if (readString(source) == "(") {
while (readString(source) != ")")
skipGeneric(source);
}
}
static void parse(ParseSink & sink, Source & source, const Path & path);
static void parseEntry(ParseSink & sink, Source & source, const Path & path)
{
string s, name;
s = readString(source);
if (s != "(") throw badArchive("expected open tag");
while (1) {
checkInterrupt();
s = readString(source);
if (s == ")") {
break;
} else if (s == "name") {
name = readString(source);
} else if (s == "node") {
if (s == "") throw badArchive("entry name missing");
parse(sink, source, path + "/" + name);
} else {
throw badArchive("unknown field " + s);
skipGeneric(source);
}
}
}
static void parseContents(ParseSink & sink, Source & source, const Path & path)
{
unsigned long long size = readLongLong(source);
sink.preallocateContents(size);
unsigned long long left = size;
unsigned char buf[65536];
while (left) {
checkInterrupt();
unsigned int n = sizeof(buf);
if ((unsigned long long) n > left) n = left;
source(buf, n);
sink.receiveContents(buf, n);
left -= n;
}
readPadding(size, source);
}
static void parse(ParseSink & sink, Source & source, const Path & path)
{
string s;
s = readString(source);
if (s != "(") throw badArchive("expected open tag");
enum { tpUnknown, tpRegular, tpDirectory, tpSymlink } type = tpUnknown;
while (1) {
checkInterrupt();
s = readString(source);
if (s == ")") {
break;
}
else if (s == "type") {
if (type != tpUnknown)
throw badArchive("multiple type fields");
string t = readString(source);
if (t == "regular") {
type = tpRegular;
sink.createRegularFile(path);
}
else if (t == "directory") {
sink.createDirectory(path);
type = tpDirectory;
}
else if (t == "symlink") {
type = tpSymlink;
}
else throw badArchive("unknown file type " + t);
}
else if (s == "contents" && type == tpRegular) {
parseContents(sink, source, path);
}
else if (s == "executable" && type == tpRegular) {
readString(source);
sink.isExecutable();
}
else if (s == "entry" && type == tpDirectory) {
parseEntry(sink, source, path);
}
else if (s == "target" && type == tpSymlink) {
string target = readString(source);
sink.createSymlink(path, target);
}
else {
throw badArchive("unknown field " + s);
skipGeneric(source);
}
}
}
void parseDump(ParseSink & sink, Source & source)
{
string version;
try {
version = readString(source);
} catch (SerialisationError & e) {
/* This generally means the integer at the start couldn't be
decoded. Ignore and throw the exception below. */
}
if (version != archiveVersion1)
throw badArchive("input doesn't look like a Nix archive");
parse(sink, source, "");
}
struct RestoreSink : ParseSink
{
Path dstPath;
AutoCloseFD fd;
void createDirectory(const Path & path)
{
Path p = dstPath + path;
if (mkdir(p.c_str(), 0777) == -1)
throw SysError(format("creating directory `%1%'") % p);
};
void createRegularFile(const Path & path)
{
Path p = dstPath + path;
fd.close();
fd = open(p.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0666);
if (fd == -1) throw SysError(format("creating file `%1%'") % p);
}
void isExecutable()
{
struct stat st;
if (fstat(fd, &st) == -1)
throw SysError("fstat");
if (fchmod(fd, st.st_mode | (S_IXUSR | S_IXGRP | S_IXOTH)) == -1)
throw SysError("fchmod");
}
void preallocateContents(unsigned long long len)
{
#if HAVE_POSIX_FALLOCATE
if (len) {
errno = posix_fallocate(fd, 0, len);
/* Note that EINVAL may indicate that the underlying
filesystem doesn't support preallocation (e.g. on
OpenSolaris). Since preallocation is just an
optimisation, ignore it. */
if (errno && errno != EINVAL)
throw SysError(format("preallocating file of %1% bytes") % len);
}
#endif
}
void receiveContents(unsigned char * data, unsigned int len)
{
writeFull(fd, data, len);
}
void createSymlink(const Path & path, const string & target)
{
Path p = dstPath + path;
nix::createSymlink(target, p);
}
};
void restorePath(const Path & path, Source & source)
{
RestoreSink sink;
sink.dstPath = path;
parseDump(sink, source);
}
}

75
nix/libutil/archive.hh Normal file
View File

@ -0,0 +1,75 @@
#pragma once
#include "types.hh"
#include "serialise.hh"
namespace nix {
/* dumpPath creates a Nix archive of the specified path. The format
is as follows:
IF path points to a REGULAR FILE:
dump(path) = attrs(
[ ("type", "regular")
, ("contents", contents(path))
])
IF path points to a DIRECTORY:
dump(path) = attrs(
[ ("type", "directory")
, ("entries", concat(map(f, sort(entries(path)))))
])
where f(fn) = attrs(
[ ("name", fn)
, ("file", dump(path + "/" + fn))
])
where:
attrs(as) = concat(map(attr, as)) + encN(0)
attrs((a, b)) = encS(a) + encS(b)
encS(s) = encN(len(s)) + s + (padding until next 64-bit boundary)
encN(n) = 64-bit little-endian encoding of n.
contents(path) = the contents of a regular file.
sort(strings) = lexicographic sort by 8-bit value (strcmp).
entries(path) = the entries of a directory, without `.' and
`..'.
`+' denotes string concatenation. */
struct PathFilter
{
virtual ~PathFilter() { }
virtual bool operator () (const Path & path) { return true; }
};
extern PathFilter defaultPathFilter;
void dumpPath(const Path & path, Sink & sink,
PathFilter & filter = defaultPathFilter);
struct ParseSink
{
virtual void createDirectory(const Path & path) { };
virtual void createRegularFile(const Path & path) { };
virtual void isExecutable() { };
virtual void preallocateContents(unsigned long long size) { };
virtual void receiveContents(unsigned char * data, unsigned int len) { };
virtual void createSymlink(const Path & path, const string & target) { };
};
void parseDump(ParseSink & sink, Source & source);
void restorePath(const Path & path, Source & source);
}

382
nix/libutil/hash.cc Normal file
View File

@ -0,0 +1,382 @@
#include "config.h"
#include <iostream>
#include <cstring>
#ifdef HAVE_OPENSSL
#include <openssl/md5.h>
#include <openssl/sha.h>
#else
extern "C" {
#include "md5.h"
#include "sha1.h"
#include "sha256.h"
}
#endif
#include "hash.hh"
#include "archive.hh"
#include "util.hh"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
namespace nix {
Hash::Hash()
{
type = htUnknown;
hashSize = 0;
memset(hash, 0, maxHashSize);
}
Hash::Hash(HashType type)
{
this->type = type;
if (type == htMD5) hashSize = md5HashSize;
else if (type == htSHA1) hashSize = sha1HashSize;
else if (type == htSHA256) hashSize = sha256HashSize;
else throw Error("unknown hash type");
assert(hashSize <= maxHashSize);
memset(hash, 0, maxHashSize);
}
bool Hash::operator == (const Hash & h2) const
{
if (hashSize != h2.hashSize) return false;
for (unsigned int i = 0; i < hashSize; i++)
if (hash[i] != h2.hash[i]) return false;
return true;
}
bool Hash::operator != (const Hash & h2) const
{
return !(*this == h2);
}
bool Hash::operator < (const Hash & h) const
{
for (unsigned int i = 0; i < hashSize; i++) {
if (hash[i] < h.hash[i]) return true;
if (hash[i] > h.hash[i]) return false;
}
return false;
}
const string base16Chars = "0123456789abcdef";
string printHash(const Hash & hash)
{
char buf[hash.hashSize * 2];
for (unsigned int i = 0; i < hash.hashSize; i++) {
buf[i * 2] = base16Chars[hash.hash[i] >> 4];
buf[i * 2 + 1] = base16Chars[hash.hash[i] & 0x0f];
}
return string(buf, hash.hashSize * 2);
}
Hash parseHash(HashType ht, const string & s)
{
Hash hash(ht);
if (s.length() != hash.hashSize * 2)
throw Error(format("invalid hash `%1%'") % s);
for (unsigned int i = 0; i < hash.hashSize; i++) {
string s2(s, i * 2, 2);
if (!isxdigit(s2[0]) || !isxdigit(s2[1]))
throw Error(format("invalid hash `%1%'") % s);
std::istringstream str(s2);
int n;
str >> std::hex >> n;
hash.hash[i] = n;
}
return hash;
}
static unsigned char divMod(unsigned char * bytes, unsigned char y)
{
unsigned int borrow = 0;
int pos = Hash::maxHashSize - 1;
while (pos >= 0 && !bytes[pos]) --pos;
for ( ; pos >= 0; --pos) {
unsigned int s = bytes[pos] + (borrow << 8);
unsigned int d = s / y;
borrow = s % y;
bytes[pos] = d;
}
return borrow;
}
unsigned int hashLength32(const Hash & hash)
{
return (hash.hashSize * 8 - 1) / 5 + 1;
}
// omitted: E O U T
const string base32Chars = "0123456789abcdfghijklmnpqrsvwxyz";
string printHash32(const Hash & hash)
{
Hash hash2(hash);
unsigned int len = hashLength32(hash);
const char * chars = base32Chars.data();
string s(len, '0');
int pos = len - 1;
while (pos >= 0) {
unsigned char digit = divMod(hash2.hash, 32);
s[pos--] = chars[digit];
}
for (unsigned int i = 0; i < hash2.maxHashSize; ++i)
assert(hash2.hash[i] == 0);
return s;
}
string printHash16or32(const Hash & hash)
{
return hash.type == htMD5 ? printHash(hash) : printHash32(hash);
}
static bool mul(unsigned char * bytes, unsigned char y, int maxSize)
{
unsigned char carry = 0;
for (int pos = 0; pos < maxSize; ++pos) {
unsigned int m = bytes[pos] * y + carry;
bytes[pos] = m & 0xff;
carry = m >> 8;
}
return carry;
}
static bool add(unsigned char * bytes, unsigned char y, int maxSize)
{
unsigned char carry = y;
for (int pos = 0; pos < maxSize; ++pos) {
unsigned int m = bytes[pos] + carry;
bytes[pos] = m & 0xff;
carry = m >> 8;
if (carry == 0) break;
}
return carry;
}
Hash parseHash32(HashType ht, const string & s)
{
Hash hash(ht);
const char * chars = base32Chars.data();
for (unsigned int i = 0; i < s.length(); ++i) {
char c = s[i];
unsigned char digit;
for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */
if (chars[digit] == c) break;
if (digit >= 32)
throw Error(format("invalid base-32 hash `%1%'") % s);
if (mul(hash.hash, 32, hash.hashSize) ||
add(hash.hash, digit, hash.hashSize))
throw Error(format("base-32 hash `%1%' is too large") % s);
}
return hash;
}
Hash parseHash16or32(HashType ht, const string & s)
{
Hash hash(ht);
if (s.size() == hash.hashSize * 2)
/* hexadecimal representation */
hash = parseHash(ht, s);
else if (s.size() == hashLength32(hash))
/* base-32 representation */
hash = parseHash32(ht, s);
else
throw Error(format("hash `%1%' has wrong length for hash type `%2%'")
% s % printHashType(ht));
return hash;
}
bool isHash(const string & s)
{
if (s.length() != 32) return false;
for (int i = 0; i < 32; i++) {
char c = s[i];
if (!((c >= '0' && c <= '9') ||
(c >= 'a' && c <= 'f')))
return false;
}
return true;
}
struct Ctx
{
MD5_CTX md5;
SHA_CTX sha1;
SHA256_CTX sha256;
};
static void start(HashType ht, Ctx & ctx)
{
if (ht == htMD5) MD5_Init(&ctx.md5);
else if (ht == htSHA1) SHA1_Init(&ctx.sha1);
else if (ht == htSHA256) SHA256_Init(&ctx.sha256);
}
static void update(HashType ht, Ctx & ctx,
const unsigned char * bytes, unsigned int len)
{
if (ht == htMD5) MD5_Update(&ctx.md5, bytes, len);
else if (ht == htSHA1) SHA1_Update(&ctx.sha1, bytes, len);
else if (ht == htSHA256) SHA256_Update(&ctx.sha256, bytes, len);
}
static void finish(HashType ht, Ctx & ctx, unsigned char * hash)
{
if (ht == htMD5) MD5_Final(hash, &ctx.md5);
else if (ht == htSHA1) SHA1_Final(hash, &ctx.sha1);
else if (ht == htSHA256) SHA256_Final(hash, &ctx.sha256);
}
Hash hashString(HashType ht, const string & s)
{
Ctx ctx;
Hash hash(ht);
start(ht, ctx);
update(ht, ctx, (const unsigned char *) s.data(), s.length());
finish(ht, ctx, hash.hash);
return hash;
}
Hash hashFile(HashType ht, const Path & path)
{
Ctx ctx;
Hash hash(ht);
start(ht, ctx);
AutoCloseFD fd = open(path.c_str(), O_RDONLY);
if (fd == -1) throw SysError(format("opening file `%1%'") % path);
unsigned char buf[8192];
ssize_t n;
while ((n = read(fd, buf, sizeof(buf)))) {
checkInterrupt();
if (n == -1) throw SysError(format("reading file `%1%'") % path);
update(ht, ctx, buf, n);
}
finish(ht, ctx, hash.hash);
return hash;
}
HashSink::HashSink(HashType ht) : ht(ht)
{
ctx = new Ctx;
bytes = 0;
start(ht, *ctx);
}
HashSink::~HashSink()
{
bufPos = 0;
delete ctx;
}
void HashSink::write(const unsigned char * data, size_t len)
{
bytes += len;
update(ht, *ctx, data, len);
}
HashResult HashSink::finish()
{
flush();
Hash hash(ht);
nix::finish(ht, *ctx, hash.hash);
return HashResult(hash, bytes);
}
HashResult HashSink::currentHash()
{
flush();
Ctx ctx2 = *ctx;
Hash hash(ht);
nix::finish(ht, ctx2, hash.hash);
return HashResult(hash, bytes);
}
HashResult hashPath(
HashType ht, const Path & path, PathFilter & filter)
{
HashSink sink(ht);
dumpPath(path, sink, filter);
return sink.finish();
}
Hash compressHash(const Hash & hash, unsigned int newSize)
{
Hash h;
h.hashSize = newSize;
for (unsigned int i = 0; i < hash.hashSize; ++i)
h.hash[i % newSize] ^= hash.hash[i];
return h;
}
HashType parseHashType(const string & s)
{
if (s == "md5") return htMD5;
else if (s == "sha1") return htSHA1;
else if (s == "sha256") return htSHA256;
else return htUnknown;
}
string printHashType(HashType ht)
{
if (ht == htMD5) return "md5";
else if (ht == htSHA1) return "sha1";
else if (ht == htSHA256) return "sha256";
else throw Error("cannot print unknown hash type");
}
}

113
nix/libutil/hash.hh Normal file
View File

@ -0,0 +1,113 @@
#pragma once
#include "types.hh"
#include "serialise.hh"
namespace nix {
typedef enum { htUnknown, htMD5, htSHA1, htSHA256 } HashType;
const int md5HashSize = 16;
const int sha1HashSize = 20;
const int sha256HashSize = 32;
extern const string base32Chars;
struct Hash
{
static const unsigned int maxHashSize = 32;
unsigned int hashSize;
unsigned char hash[maxHashSize];
HashType type;
/* Create an unusable hash object. */
Hash();
/* Create a zero-filled hash object. */
Hash(HashType type);
/* Check whether two hash are equal. */
bool operator == (const Hash & h2) const;
/* Check whether two hash are not equal. */
bool operator != (const Hash & h2) const;
/* For sorting. */
bool operator < (const Hash & h) const;
};
/* Convert a hash to a hexadecimal representation. */
string printHash(const Hash & hash);
/* Parse a hexadecimal representation of a hash code. */
Hash parseHash(HashType ht, const string & s);
/* Returns the length of a base-32 hash representation. */
unsigned int hashLength32(const Hash & hash);
/* Convert a hash to a base-32 representation. */
string printHash32(const Hash & hash);
/* Print a hash in base-16 if it's MD5, or base-32 otherwise. */
string printHash16or32(const Hash & hash);
/* Parse a base-32 representation of a hash code. */
Hash parseHash32(HashType ht, const string & s);
/* Parse a base-16 or base-32 representation of a hash code. */
Hash parseHash16or32(HashType ht, const string & s);
/* Verify that the given string is a valid hash code. */
bool isHash(const string & s);
/* Compute the hash of the given string. */
Hash hashString(HashType ht, const string & s);
/* Compute the hash of the given file. */
Hash hashFile(HashType ht, const Path & path);
/* Compute the hash of the given path. The hash is defined as
(essentially) hashString(ht, dumpPath(path)). */
struct PathFilter;
extern PathFilter defaultPathFilter;
typedef std::pair<Hash, unsigned long long> HashResult;
HashResult hashPath(HashType ht, const Path & path,
PathFilter & filter = defaultPathFilter);
/* Compress a hash to the specified number of bytes by cyclically
XORing bytes together. */
Hash compressHash(const Hash & hash, unsigned int newSize);
/* Parse a string representing a hash type. */
HashType parseHashType(const string & s);
/* And the reverse. */
string printHashType(HashType ht);
struct Ctx;
class HashSink : public BufferedSink
{
private:
HashType ht;
Ctx * ctx;
unsigned long long bytes;
public:
HashSink(HashType ht);
HashSink(const HashSink & h);
~HashSink();
void write(const unsigned char * data, size_t len);
HashResult finish();
HashResult currentHash();
};
}

259
nix/libutil/serialise.cc Normal file
View File

@ -0,0 +1,259 @@
#include "serialise.hh"
#include "util.hh"
#include <cstring>
#include <cerrno>
namespace nix {
BufferedSink::~BufferedSink()
{
/* We can't call flush() here, because C++ for some insane reason
doesn't allow you to call virtual methods from a destructor. */
assert(!bufPos);
delete[] buffer;
}
void BufferedSink::operator () (const unsigned char * data, size_t len)
{
if (!buffer) buffer = new unsigned char[bufSize];
while (len) {
/* Optimisation: bypass the buffer if the data exceeds the
buffer size. */
if (bufPos + len >= bufSize) {
flush();
write(data, len);
break;
}
/* Otherwise, copy the bytes to the buffer. Flush the buffer
when it's full. */
size_t n = bufPos + len > bufSize ? bufSize - bufPos : len;
memcpy(buffer + bufPos, data, n);
data += n; bufPos += n; len -= n;
if (bufPos == bufSize) flush();
}
}
void BufferedSink::flush()
{
if (bufPos == 0) return;
size_t n = bufPos;
bufPos = 0; // don't trigger the assert() in ~BufferedSink()
write(buffer, n);
}
FdSink::~FdSink()
{
try { flush(); } catch (...) { ignoreException(); }
}
void FdSink::write(const unsigned char * data, size_t len)
{
writeFull(fd, data, len);
}
void Source::operator () (unsigned char * data, size_t len)
{
while (len) {
size_t n = read(data, len);
data += n; len -= n;
}
}
BufferedSource::~BufferedSource()
{
delete[] buffer;
}
size_t BufferedSource::read(unsigned char * data, size_t len)
{
if (!buffer) buffer = new unsigned char[bufSize];
if (!bufPosIn) bufPosIn = readUnbuffered(buffer, bufSize);
/* Copy out the data in the buffer. */
size_t n = len > bufPosIn - bufPosOut ? bufPosIn - bufPosOut : len;
memcpy(data, buffer + bufPosOut, n);
bufPosOut += n;
if (bufPosIn == bufPosOut) bufPosIn = bufPosOut = 0;
return n;
}
bool BufferedSource::hasData()
{
return bufPosOut < bufPosIn;
}
size_t FdSource::readUnbuffered(unsigned char * data, size_t len)
{
ssize_t n;
do {
checkInterrupt();
n = ::read(fd, (char *) data, bufSize);
} while (n == -1 && errno == EINTR);
if (n == -1) throw SysError("reading from file");
if (n == 0) throw EndOfFile("unexpected end-of-file");
return n;
}
size_t StringSource::read(unsigned char * data, size_t len)
{
if (pos == s.size()) throw EndOfFile("end of string reached");
size_t n = s.copy((char *) data, len, pos);
pos += n;
return n;
}
void writePadding(size_t len, Sink & sink)
{
if (len % 8) {
unsigned char zero[8];
memset(zero, 0, sizeof(zero));
sink(zero, 8 - (len % 8));
}
}
void writeInt(unsigned int n, Sink & sink)
{
unsigned char buf[8];
memset(buf, 0, sizeof(buf));
buf[0] = n & 0xff;
buf[1] = (n >> 8) & 0xff;
buf[2] = (n >> 16) & 0xff;
buf[3] = (n >> 24) & 0xff;
sink(buf, sizeof(buf));
}
void writeLongLong(unsigned long long n, Sink & sink)
{
unsigned char buf[8];
buf[0] = n & 0xff;
buf[1] = (n >> 8) & 0xff;
buf[2] = (n >> 16) & 0xff;
buf[3] = (n >> 24) & 0xff;
buf[4] = (n >> 32) & 0xff;
buf[5] = (n >> 40) & 0xff;
buf[6] = (n >> 48) & 0xff;
buf[7] = (n >> 56) & 0xff;
sink(buf, sizeof(buf));
}
void writeString(const unsigned char * buf, size_t len, Sink & sink)
{
writeInt(len, sink);
sink(buf, len);
writePadding(len, sink);
}
void writeString(const string & s, Sink & sink)
{
writeString((const unsigned char *) s.data(), s.size(), sink);
}
template<class T> void writeStrings(const T & ss, Sink & sink)
{
writeInt(ss.size(), sink);
foreach (typename T::const_iterator, i, ss)
writeString(*i, sink);
}
template void writeStrings(const Paths & ss, Sink & sink);
template void writeStrings(const PathSet & ss, Sink & sink);
void readPadding(size_t len, Source & source)
{
if (len % 8) {
unsigned char zero[8];
size_t n = 8 - (len % 8);
source(zero, n);
for (unsigned int i = 0; i < n; i++)
if (zero[i]) throw SerialisationError("non-zero padding");
}
}
unsigned int readInt(Source & source)
{
unsigned char buf[8];
source(buf, sizeof(buf));
if (buf[4] || buf[5] || buf[6] || buf[7])
throw SerialisationError("implementation cannot deal with > 32-bit integers");
return
buf[0] |
(buf[1] << 8) |
(buf[2] << 16) |
(buf[3] << 24);
}
unsigned long long readLongLong(Source & source)
{
unsigned char buf[8];
source(buf, sizeof(buf));
return
((unsigned long long) buf[0]) |
((unsigned long long) buf[1] << 8) |
((unsigned long long) buf[2] << 16) |
((unsigned long long) buf[3] << 24) |
((unsigned long long) buf[4] << 32) |
((unsigned long long) buf[5] << 40) |
((unsigned long long) buf[6] << 48) |
((unsigned long long) buf[7] << 56);
}
size_t readString(unsigned char * buf, size_t max, Source & source)
{
size_t len = readInt(source);
if (len > max) throw Error("string is too long");
source(buf, len);
readPadding(len, source);
return len;
}
string readString(Source & source)
{
size_t len = readInt(source);
unsigned char * buf = new unsigned char[len];
AutoDeleteArray<unsigned char> d(buf);
source(buf, len);
readPadding(len, source);
return string((char *) buf, len);
}
template<class T> T readStrings(Source & source)
{
unsigned int count = readInt(source);
T ss;
while (count--)
ss.insert(ss.end(), readString(source));
return ss;
}
template Paths readStrings(Source & source);
template PathSet readStrings(Source & source);
}

133
nix/libutil/serialise.hh Normal file
View File

@ -0,0 +1,133 @@
#pragma once
#include "types.hh"
namespace nix {
/* Abstract destination of binary data. */
struct Sink
{
virtual ~Sink() { }
virtual void operator () (const unsigned char * data, size_t len) = 0;
};
/* A buffered abstract sink. */
struct BufferedSink : Sink
{
size_t bufSize, bufPos;
unsigned char * buffer;
BufferedSink(size_t bufSize = 32 * 1024)
: bufSize(bufSize), bufPos(0), buffer(0) { }
~BufferedSink();
void operator () (const unsigned char * data, size_t len);
void flush();
virtual void write(const unsigned char * data, size_t len) = 0;
};
/* Abstract source of binary data. */
struct Source
{
virtual ~Source() { }
/* Store exactly len bytes in the buffer pointed to by data.
It blocks until all the requested data is available, or throws
an error if it is not going to be available. */
void operator () (unsigned char * data, size_t len);
/* Store up to len in the buffer pointed to by data, and
return the number of bytes stored. If blocks until at least
one byte is available. */
virtual size_t read(unsigned char * data, size_t len) = 0;
};
/* A buffered abstract source. */
struct BufferedSource : Source
{
size_t bufSize, bufPosIn, bufPosOut;
unsigned char * buffer;
BufferedSource(size_t bufSize = 32 * 1024)
: bufSize(bufSize), bufPosIn(0), bufPosOut(0), buffer(0) { }
~BufferedSource();
size_t read(unsigned char * data, size_t len);
/* Underlying read call, to be overridden. */
virtual size_t readUnbuffered(unsigned char * data, size_t len) = 0;
bool hasData();
};
/* A sink that writes data to a file descriptor. */
struct FdSink : BufferedSink
{
int fd;
FdSink() : fd(-1) { }
FdSink(int fd) : fd(fd) { }
~FdSink();
void write(const unsigned char * data, size_t len);
};
/* A source that reads data from a file descriptor. */
struct FdSource : BufferedSource
{
int fd;
FdSource() : fd(-1) { }
FdSource(int fd) : fd(fd) { }
size_t readUnbuffered(unsigned char * data, size_t len);
};
/* A sink that writes data to a string. */
struct StringSink : Sink
{
string s;
void operator () (const unsigned char * data, size_t len)
{
s.append((const char *) data, len);
}
};
/* A source that reads data from a string. */
struct StringSource : Source
{
const string & s;
size_t pos;
StringSource(const string & _s) : s(_s), pos(0) { }
size_t read(unsigned char * data, size_t len);
};
void writePadding(size_t len, Sink & sink);
void writeInt(unsigned int n, Sink & sink);
void writeLongLong(unsigned long long n, Sink & sink);
void writeString(const unsigned char * buf, size_t len, Sink & sink);
void writeString(const string & s, Sink & sink);
template<class T> void writeStrings(const T & ss, Sink & sink);
void readPadding(size_t len, Source & source);
unsigned int readInt(Source & source);
unsigned long long readLongLong(Source & source);
size_t readString(unsigned char * buf, size_t max, Source & source);
string readString(Source & source);
template<class T> T readStrings(Source & source);
MakeError(SerialisationError, Error)
}

86
nix/libutil/types.hh Normal file
View File

@ -0,0 +1,86 @@
#pragma once
#include "config.h"
#include <string>
#include <list>
#include <set>
#include <boost/format.hpp>
namespace nix {
/* Inherit some names from other namespaces for convenience. */
using std::string;
using std::list;
using std::set;
using std::vector;
using boost::format;
struct FormatOrString
{
string s;
FormatOrString(const string & s) : s(s) { };
FormatOrString(const format & f) : s(f.str()) { };
FormatOrString(const char * s) : s(s) { };
};
/* BaseError should generally not be caught, as it has Interrupted as
a subclass. Catch Error instead. */
class BaseError : public std::exception
{
protected:
string prefix_; // used for location traces etc.
string err;
public:
unsigned int status; // exit status
BaseError(const FormatOrString & fs, unsigned int status = 1);
~BaseError() throw () { };
const char * what() const throw () { return err.c_str(); }
const string & msg() const throw () { return err; }
const string & prefix() const throw () { return prefix_; }
BaseError & addPrefix(const FormatOrString & fs);
};
#define MakeError(newClass, superClass) \
class newClass : public superClass \
{ \
public: \
newClass(const FormatOrString & fs, unsigned int status = 1) : superClass(fs, status) { }; \
};
MakeError(Error, BaseError)
class SysError : public Error
{
public:
int errNo;
SysError(const FormatOrString & fs);
};
typedef list<string> Strings;
typedef set<string> StringSet;
/* Paths are just strings. */
typedef string Path;
typedef list<Path> Paths;
typedef set<Path> PathSet;
typedef enum {
lvlError = 0,
lvlInfo,
lvlTalkative,
lvlChatty,
lvlDebug,
lvlVomit
} Verbosity;
}

1105
nix/libutil/util.cc Normal file

File diff suppressed because it is too large Load Diff

349
nix/libutil/util.hh Normal file
View File

@ -0,0 +1,349 @@
#pragma once
#include "types.hh"
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <signal.h>
#include <cstdio>
namespace nix {
#define foreach(it_type, it, collection) \
for (it_type it = (collection).begin(); it != (collection).end(); ++it)
#define foreach_reverse(it_type, it, collection) \
for (it_type it = (collection).rbegin(); it != (collection).rend(); ++it)
/* Return an environment variable. */
string getEnv(const string & key, const string & def = "");
/* Return an absolutized path, resolving paths relative to the
specified directory, or the current directory otherwise. The path
is also canonicalised. */
Path absPath(Path path, Path dir = "");
/* Canonicalise a path by removing all `.' or `..' components and
double or trailing slashes. Optionally resolves all symlink
components such that each component of the resulting path is *not*
a symbolic link. */
Path canonPath(const Path & path, bool resolveSymlinks = false);
/* Return the directory part of the given canonical path, i.e.,
everything before the final `/'. If the path is the root or an
immediate child thereof (e.g., `/foo'), this means an empty string
is returned. */
Path dirOf(const Path & path);
/* Return the base name of the given canonical path, i.e., everything
following the final `/'. */
string baseNameOf(const Path & path);
/* Check whether a given path is a descendant of the given
directory. */
bool isInDir(const Path & path, const Path & dir);
/* Get status of `path'. */
struct stat lstat(const Path & path);
/* Return true iff the given path exists. */
bool pathExists(const Path & path);
/* Read the contents (target) of a symbolic link. The result is not
in any way canonicalised. */
Path readLink(const Path & path);
bool isLink(const Path & path);
/* Read the contents of a directory. The entries `.' and `..' are
removed. */
Strings readDirectory(const Path & path);
/* Read the contents of a file into a string. */
string readFile(int fd);
string readFile(const Path & path, bool drain = false);
/* Write a string to a file. */
void writeFile(const Path & path, const string & s);
/* Read a line from a file descriptor. */
string readLine(int fd);
/* Write a line to a file descriptor. */
void writeLine(int fd, string s);
/* Delete a path; i.e., in the case of a directory, it is deleted
recursively. Don't use this at home, kids. The second variant
returns the number of bytes and blocks freed. */
void deletePath(const Path & path);
void deletePath(const Path & path, unsigned long long & bytesFreed);
/* Create a temporary directory. */
Path createTempDir(const Path & tmpRoot = "", const Path & prefix = "nix",
bool includePid = true, bool useGlobalCounter = true, mode_t mode = 0755);
/* Create a directory and all its parents, if necessary. Returns the
list of created directories, in order of creation. */
Paths createDirs(const Path & path);
/* Create a symlink. */
void createSymlink(const Path & target, const Path & link);
template<class T, class A>
T singleton(const A & a)
{
T t;
t.insert(a);
return t;
}
/* Messages. */
typedef enum {
ltPretty, /* nice, nested output */
ltEscapes, /* nesting indicated using escape codes (for log2xml) */
ltFlat /* no nesting */
} LogType;
extern LogType logType;
extern Verbosity verbosity; /* suppress msgs > this */
class Nest
{
private:
bool nest;
public:
Nest();
~Nest();
void open(Verbosity level, const FormatOrString & fs);
void close();
};
void printMsg_(Verbosity level, const FormatOrString & fs);
#define startNest(varName, level, f) \
Nest varName; \
if (level <= verbosity) { \
varName.open(level, (f)); \
}
#define printMsg(level, f) \
do { \
if (level <= verbosity) { \
printMsg_(level, (f)); \
} \
} while (0)
#define debug(f) printMsg(lvlDebug, f)
void warnOnce(bool & haveWarned, const FormatOrString & fs);
void writeToStderr(const string & s);
extern void (*_writeToStderr) (const unsigned char * buf, size_t count);
/* Wrappers arount read()/write() that read/write exactly the
requested number of bytes. */
void readFull(int fd, unsigned char * buf, size_t count);
void writeFull(int fd, const unsigned char * buf, size_t count);
MakeError(EndOfFile, Error)
/* Read a file descriptor until EOF occurs. */
string drainFD(int fd);
/* Automatic cleanup of resources. */
template <class T>
struct AutoDeleteArray
{
T * p;
AutoDeleteArray(T * p) : p(p) { }
~AutoDeleteArray()
{
delete [] p;
}
};
class AutoDelete
{
Path path;
bool del;
bool recursive;
public:
AutoDelete(const Path & p, bool recursive = true);
~AutoDelete();
void cancel();
};
class AutoCloseFD
{
int fd;
public:
AutoCloseFD();
AutoCloseFD(int fd);
AutoCloseFD(const AutoCloseFD & fd);
~AutoCloseFD();
void operator =(int fd);
operator int() const;
void close();
bool isOpen();
int borrow();
};
class Pipe
{
public:
AutoCloseFD readSide, writeSide;
void create();
};
class AutoCloseDir
{
DIR * dir;
public:
AutoCloseDir();
AutoCloseDir(DIR * dir);
~AutoCloseDir();
void operator =(DIR * dir);
operator DIR *();
void close();
};
class Pid
{
pid_t pid;
bool separatePG;
int killSignal;
public:
Pid();
~Pid();
void operator =(pid_t pid);
operator pid_t();
void kill();
int wait(bool block);
void setSeparatePG(bool separatePG);
void setKillSignal(int signal);
};
/* Kill all processes running under the specified uid by sending them
a SIGKILL. */
void killUser(uid_t uid);
/* Run a program and return its stdout in a string (i.e., like the
shell backtick operator). */
string runProgram(Path program, bool searchPath = false,
const Strings & args = Strings());
/* Close all file descriptors except stdin, stdout, stderr, and those
listed in the given set. Good practice in child processes. */
void closeMostFDs(const set<int> & exceptions);
/* Set the close-on-exec flag for the given file descriptor. */
void closeOnExec(int fd);
/* Call vfork() if available, otherwise fork(). */
extern pid_t (*maybeVfork)();
/* User interruption. */
extern volatile sig_atomic_t _isInterrupted;
void _interrupted();
void inline checkInterrupt()
{
if (_isInterrupted) _interrupted();
}
MakeError(Interrupted, BaseError)
/* String tokenizer. */
template<class C> C tokenizeString(const string & s, const string & separators = " \t\n\r");
/* Concatenate the given strings with a separator between the
elements. */
string concatStringsSep(const string & sep, const Strings & ss);
string concatStringsSep(const string & sep, const StringSet & ss);
/* Remove trailing whitespace from a string. */
string chomp(const string & s);
/* Convert the exit status of a child as returned by wait() into an
error string. */
string statusToString(int status);
bool statusOk(int status);
/* Parse a string into an integer. */
template<class N> bool string2Int(const string & s, N & n)
{
std::istringstream str(s);
str >> n;
return str && str.get() == EOF;
}
template<class N> string int2String(N n)
{
std::ostringstream str;
str << n;
return str.str();
}
/* Return true iff `s' ends in `suffix'. */
bool hasSuffix(const string & s, const string & suffix);
/* Read string `s' from stream `str'. */
void expect(std::istream & str, const string & s);
/* Read a C-style string from stream `str'. */
string parseString(std::istream & str);
/* Utility function used to parse legacy ATerms. */
bool endOfList(std::istream & str);
/* Escape a string that contains octal-encoded escape codes such as
used in /etc/fstab and /proc/mounts (e.g. "foo\040bar" decodes to
"foo bar"). */
string decodeOctalEscaped(const string & s);
/* Exception handling in destructors: print an error message, then
ignore the exception. */
void ignoreException();
}

94
nix/libutil/xml-writer.cc Normal file
View File

@ -0,0 +1,94 @@
#include <assert.h>
#include "xml-writer.hh"
namespace nix {
XMLWriter::XMLWriter(bool indent, std::ostream & output)
: output(output), indent(indent)
{
output << "<?xml version='1.0' encoding='utf-8'?>" << std::endl;
closed = false;
}
XMLWriter::~XMLWriter()
{
close();
}
void XMLWriter::close()
{
if (closed) return;
while (!pendingElems.empty()) closeElement();
closed = true;
}
void XMLWriter::indent_(unsigned int depth)
{
if (!indent) return;
output << string(depth * 2, ' ');
}
void XMLWriter::openElement(const string & name,
const XMLAttrs & attrs)
{
assert(!closed);
indent_(pendingElems.size());
output << "<" << name;
writeAttrs(attrs);
output << ">";
if (indent) output << std::endl;
pendingElems.push_back(name);
}
void XMLWriter::closeElement()
{
assert(!pendingElems.empty());
indent_(pendingElems.size() - 1);
output << "</" << pendingElems.back() << ">";
if (indent) output << std::endl;
pendingElems.pop_back();
if (pendingElems.empty()) closed = true;
}
void XMLWriter::writeEmptyElement(const string & name,
const XMLAttrs & attrs)
{
assert(!closed);
indent_(pendingElems.size());
output << "<" << name;
writeAttrs(attrs);
output << " />";
if (indent) output << std::endl;
}
void XMLWriter::writeAttrs(const XMLAttrs & attrs)
{
for (XMLAttrs::const_iterator i = attrs.begin(); i != attrs.end(); ++i) {
output << " " << i->first << "=\"";
for (unsigned int j = 0; j < i->second.size(); ++j) {
char c = i->second[j];
if (c == '"') output << "&quot;";
else if (c == '<') output << "&lt;";
else if (c == '>') output << "&gt;";
else if (c == '&') output << "&amp;";
/* Escape newlines to prevent attribute normalisation (see
XML spec, section 3.3.3. */
else if (c == '\n') output << "&#xA;";
else output << c;
}
output << "\"";
}
}
}

69
nix/libutil/xml-writer.hh Normal file
View File

@ -0,0 +1,69 @@
#pragma once
#include <iostream>
#include <string>
#include <list>
#include <map>
namespace nix {
using std::string;
using std::map;
using std::list;
typedef map<string, string> XMLAttrs;
class XMLWriter
{
private:
std::ostream & output;
bool indent;
bool closed;
list<string> pendingElems;
public:
XMLWriter(bool indent, std::ostream & output);
~XMLWriter();
void close();
void openElement(const string & name,
const XMLAttrs & attrs = XMLAttrs());
void closeElement();
void writeEmptyElement(const string & name,
const XMLAttrs & attrs = XMLAttrs());
private:
void writeAttrs(const XMLAttrs & attrs);
void indent_(unsigned int depth);
};
class XMLOpenElement
{
private:
XMLWriter & writer;
public:
XMLOpenElement(XMLWriter & writer, const string & name,
const XMLAttrs & attrs = XMLAttrs())
: writer(writer)
{
writer.openElement(name, attrs);
}
~XMLOpenElement()
{
writer.closeElement();
}
};
}

View File

@ -0,0 +1,939 @@
#include "shared.hh"
#include "local-store.hh"
#include "util.hh"
#include "serialise.hh"
#include "worker-protocol.hh"
#include "archive.hh"
#include "affinity.hh"
#include "globals.hh"
#include <cstring>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <fcntl.h>
#include <errno.h>
using namespace nix;
/* On platforms that have O_ASYNC, we can detect when a client
disconnects and immediately kill any ongoing builds. On platforms
that lack it, we only notice the disconnection the next time we try
to write to the client. So if you have a builder that never
generates output on stdout/stderr, the daemon will never notice
that the client has disconnected until the builder terminates.
GNU/Hurd does have O_ASYNC, but its Unix-domain socket translator
(pflocal) does not implement F_SETOWN. See
<http://lists.gnu.org/archive/html/bug-guix/2013-07/msg00021.html> for
details.*/
#if defined O_ASYNC && !defined __GNU__
#define HAVE_HUP_NOTIFICATION
#ifndef SIGPOLL
#define SIGPOLL SIGIO
#endif
#endif
static FdSource from(STDIN_FILENO);
static FdSink to(STDOUT_FILENO);
bool canSendStderr;
pid_t myPid;
/* This function is called anytime we want to write something to
stderr. If we're in a state where the protocol allows it (i.e.,
when canSendStderr), send the message to the client over the
socket. */
static void tunnelStderr(const unsigned char * buf, size_t count)
{
/* Don't send the message to the client if we're a child of the
process handling the connection. Otherwise we could screw up
the protocol. It's up to the parent to redirect stderr and
send it to the client somehow (e.g., as in build.cc). */
if (canSendStderr && myPid == getpid()) {
try {
writeInt(STDERR_NEXT, to);
writeString(buf, count, to);
to.flush();
} catch (...) {
/* Write failed; that means that the other side is
gone. */
canSendStderr = false;
throw;
}
} else
writeFull(STDERR_FILENO, buf, count);
}
/* Return true if the remote side has closed its end of the
connection, false otherwise. Should not be called on any socket on
which we expect input! */
static bool isFarSideClosed(int socket)
{
struct timeval timeout;
timeout.tv_sec = timeout.tv_usec = 0;
fd_set fds;
FD_ZERO(&fds);
FD_SET(socket, &fds);
while (select(socket + 1, &fds, 0, 0, &timeout) == -1)
if (errno != EINTR) throw SysError("select()");
if (!FD_ISSET(socket, &fds)) return false;
/* Destructive read to determine whether the select() marked the
socket as readable because there is actual input or because
we've reached EOF (i.e., a read of size 0 is available). */
char c;
int rd;
if ((rd = read(socket, &c, 1)) > 0)
throw Error("EOF expected (protocol error?)");
else if (rd == -1 && errno != ECONNRESET)
throw SysError("expected connection reset or EOF");
return true;
}
/* A SIGPOLL signal is received when data is available on the client
communication socket, or when the client has closed its side of the
socket. This handler is enabled at precisely those moments in the
protocol when we're doing work and the client is supposed to be
quiet. Thus, if we get a SIGPOLL signal, it means that the client
has quit. So we should quit as well.
Too bad most operating systems don't support the POLL_HUP value for
si_code in siginfo_t. That would make most of the SIGPOLL
complexity unnecessary, i.e., we could just enable SIGPOLL all the
time and wouldn't have to worry about races. */
static void sigPollHandler(int sigNo)
{
using namespace std;
try {
/* Check that the far side actually closed. We're still
getting spurious signals every once in a while. I.e.,
there is no input available, but we get a signal with
POLL_IN set. Maybe it's delayed or something. */
if (isFarSideClosed(from.fd)) {
if (!blockInt) {
_isInterrupted = 1;
blockInt = 1;
canSendStderr = false;
const char * s = "SIGPOLL\n";
write(STDERR_FILENO, s, strlen(s));
}
} else {
const char * s = "spurious SIGPOLL\n";
write(STDERR_FILENO, s, strlen(s));
}
}
catch (Error & e) {
/* Shouldn't happen. */
string s = "impossible: " + e.msg() + '\n';
write(STDERR_FILENO, s.data(), s.size());
throw;
}
}
static void setSigPollAction(bool enable)
{
#ifdef HAVE_HUP_NOTIFICATION
struct sigaction act, oact;
act.sa_handler = enable ? sigPollHandler : SIG_IGN;
sigfillset(&act.sa_mask);
act.sa_flags = 0;
if (sigaction(SIGPOLL, &act, &oact))
throw SysError("setting handler for SIGPOLL");
#endif
}
/* startWork() means that we're starting an operation for which we
want to send out stderr to the client. */
static void startWork()
{
canSendStderr = true;
/* Handle client death asynchronously. */
setSigPollAction(true);
/* Of course, there is a race condition here: the socket could
have closed between when we last read from / wrote to it, and
between the time we set the handler for SIGPOLL. In that case
we won't get the signal. So do a non-blocking select() to find
out if any input is available on the socket. If there is, it
has to be the 0-byte read that indicates that the socket has
closed. */
if (isFarSideClosed(from.fd)) {
_isInterrupted = 1;
checkInterrupt();
}
}
/* stopWork() means that we're done; stop sending stderr to the
client. */
static void stopWork(bool success = true, const string & msg = "", unsigned int status = 0)
{
/* Stop handling async client death; we're going to a state where
we're either sending or receiving from the client, so we'll be
notified of client death anyway. */
setSigPollAction(false);
canSendStderr = false;
if (success)
writeInt(STDERR_LAST, to);
else {
writeInt(STDERR_ERROR, to);
writeString(msg, to);
if (status != 0) writeInt(status, to);
}
}
struct TunnelSink : Sink
{
Sink & to;
TunnelSink(Sink & to) : to(to) { }
virtual void operator () (const unsigned char * data, size_t len)
{
writeInt(STDERR_WRITE, to);
writeString(data, len, to);
}
};
struct TunnelSource : BufferedSource
{
Source & from;
TunnelSource(Source & from) : from(from) { }
size_t readUnbuffered(unsigned char * data, size_t len)
{
/* Careful: we're going to receive data from the client now,
so we have to disable the SIGPOLL handler. */
setSigPollAction(false);
canSendStderr = false;
writeInt(STDERR_READ, to);
writeInt(len, to);
to.flush();
size_t n = readString(data, len, from);
startWork();
if (n == 0) throw EndOfFile("unexpected end-of-file");
return n;
}
};
/* If the NAR archive contains a single file at top-level, then save
the contents of the file to `s'. Otherwise barf. */
struct RetrieveRegularNARSink : ParseSink
{
bool regular;
string s;
RetrieveRegularNARSink() : regular(true) { }
void createDirectory(const Path & path)
{
regular = false;
}
void receiveContents(unsigned char * data, unsigned int len)
{
s.append((const char *) data, len);
}
void createSymlink(const Path & path, const string & target)
{
regular = false;
}
};
/* Adapter class of a Source that saves all data read to `s'. */
struct SavingSourceAdapter : Source
{
Source & orig;
string s;
SavingSourceAdapter(Source & orig) : orig(orig) { }
size_t read(unsigned char * data, size_t len)
{
size_t n = orig.read(data, len);
s.append((const char *) data, n);
return n;
}
};
static void performOp(bool trusted, unsigned int clientVersion,
Source & from, Sink & to, unsigned int op)
{
switch (op) {
#if 0
case wopQuit: {
/* Close the database. */
store.reset((StoreAPI *) 0);
writeInt(1, to);
break;
}
#endif
case wopIsValidPath: {
/* 'readStorePath' could raise an error leading to the connection
being closed. To be able to recover from an invalid path error,
call 'startWork' early, and do 'assertStorePath' afterwards so
that the 'Error' exception handler doesn't close the
connection. */
Path path = readString(from);
startWork();
assertStorePath(path);
bool result = store->isValidPath(path);
stopWork();
writeInt(result, to);
break;
}
case wopQueryValidPaths: {
PathSet paths = readStorePaths<PathSet>(from);
startWork();
PathSet res = store->queryValidPaths(paths);
stopWork();
writeStrings(res, to);
break;
}
case wopHasSubstitutes: {
Path path = readStorePath(from);
startWork();
PathSet res = store->querySubstitutablePaths(singleton<PathSet>(path));
stopWork();
writeInt(res.find(path) != res.end(), to);
break;
}
case wopQuerySubstitutablePaths: {
PathSet paths = readStorePaths<PathSet>(from);
startWork();
PathSet res = store->querySubstitutablePaths(paths);
stopWork();
writeStrings(res, to);
break;
}
case wopQueryPathHash: {
Path path = readStorePath(from);
startWork();
Hash hash = store->queryPathHash(path);
stopWork();
writeString(printHash(hash), to);
break;
}
case wopQueryReferences:
case wopQueryReferrers:
case wopQueryValidDerivers:
case wopQueryDerivationOutputs: {
Path path = readStorePath(from);
startWork();
PathSet paths;
if (op == wopQueryReferences)
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);
break;
}
case wopQueryDerivationOutputNames: {
Path path = readStorePath(from);
startWork();
StringSet names;
names = store->queryDerivationOutputNames(path);
stopWork();
writeStrings(names, to);
break;
}
case wopQueryDeriver: {
Path path = readStorePath(from);
startWork();
Path deriver = store->queryDeriver(path);
stopWork();
writeString(deriver, to);
break;
}
case wopQueryPathFromHashPart: {
string hashPart = readString(from);
startWork();
Path path = store->queryPathFromHashPart(hashPart);
stopWork();
writeString(path, to);
break;
}
case wopAddToStore: {
string baseName = readString(from);
bool fixed = readInt(from) == 1; /* obsolete */
bool recursive = readInt(from) == 1;
string s = readString(from);
/* Compatibility hack. */
if (!fixed) {
s = "sha256";
recursive = true;
}
HashType hashAlgo = parseHashType(s);
SavingSourceAdapter savedNAR(from);
RetrieveRegularNARSink savedRegular;
if (recursive) {
/* Get the entire NAR dump from the client and save it to
a string so that we can pass it to
addToStoreFromDump(). */
ParseSink sink; /* null sink; just parse the NAR */
parseDump(sink, savedNAR);
} else
parseDump(savedRegular, from);
startWork();
if (!savedRegular.regular) throw Error("regular file expected");
Path path = dynamic_cast<LocalStore *>(store.get())
->addToStoreFromDump(recursive ? savedNAR.s : savedRegular.s, baseName, recursive, hashAlgo);
stopWork();
writeString(path, to);
break;
}
case wopAddTextToStore: {
string suffix = readString(from);
string s = readString(from);
PathSet refs = readStorePaths<PathSet>(from);
startWork();
Path path = store->addTextToStore(suffix, s, refs);
stopWork();
writeString(path, to);
break;
}
case wopExportPath: {
Path path = readStorePath(from);
bool sign = readInt(from) == 1;
startWork();
TunnelSink sink(to);
store->exportPath(path, sign, sink);
stopWork();
writeInt(1, to);
break;
}
case wopImportPaths: {
startWork();
TunnelSource source(from);
Paths paths = store->importPaths(true, source);
stopWork();
writeStrings(paths, to);
break;
}
case wopBuildPaths: {
PathSet drvs = readStorePaths<PathSet>(from);
startWork();
store->buildPaths(drvs);
stopWork();
writeInt(1, to);
break;
}
case wopEnsurePath: {
Path path = readStorePath(from);
startWork();
store->ensurePath(path);
stopWork();
writeInt(1, to);
break;
}
case wopAddTempRoot: {
Path path = readStorePath(from);
startWork();
store->addTempRoot(path);
stopWork();
writeInt(1, to);
break;
}
case wopAddIndirectRoot: {
Path path = absPath(readString(from));
startWork();
store->addIndirectRoot(path);
stopWork();
writeInt(1, to);
break;
}
case wopSyncWithGC: {
startWork();
store->syncWithGC();
stopWork();
writeInt(1, to);
break;
}
case wopFindRoots: {
startWork();
Roots roots = store->findRoots();
stopWork();
writeInt(roots.size(), to);
for (Roots::iterator i = roots.begin(); i != roots.end(); ++i) {
writeString(i->first, to);
writeString(i->second, to);
}
break;
}
case wopCollectGarbage: {
GCOptions options;
options.action = (GCOptions::GCAction) readInt(from);
options.pathsToDelete = readStorePaths<PathSet>(from);
options.ignoreLiveness = readInt(from);
options.maxFreed = readLongLong(from);
readInt(from); // obsolete field
if (GET_PROTOCOL_MINOR(clientVersion) >= 5) {
/* removed options */
readInt(from);
readInt(from);
}
GCResults results;
startWork();
if (options.ignoreLiveness)
throw Error("you are not allowed to ignore liveness");
store->collectGarbage(options, results);
stopWork();
writeStrings(results.paths, to);
writeLongLong(results.bytesFreed, to);
writeLongLong(0, to); // obsolete
break;
}
case wopSetOptions: {
settings.keepFailed = readInt(from) != 0;
settings.keepGoing = readInt(from) != 0;
settings.set("build-fallback", readInt(from) ? "true" : "false");
verbosity = (Verbosity) readInt(from);
settings.set("build-max-jobs", int2String(readInt(from)));
settings.set("build-max-silent-time", int2String(readInt(from)));
if (GET_PROTOCOL_MINOR(clientVersion) >= 2)
settings.useBuildHook = readInt(from) != 0;
if (GET_PROTOCOL_MINOR(clientVersion) >= 4) {
settings.buildVerbosity = (Verbosity) readInt(from);
logType = (LogType) readInt(from);
settings.printBuildTrace = readInt(from) != 0;
}
if (GET_PROTOCOL_MINOR(clientVersion) >= 6)
settings.set("build-cores", int2String(readInt(from)));
if (GET_PROTOCOL_MINOR(clientVersion) >= 10)
settings.set("build-use-substitutes", readInt(from) ? "true" : "false");
if (GET_PROTOCOL_MINOR(clientVersion) >= 12) {
unsigned int n = readInt(from);
for (unsigned int i = 0; i < n; i++) {
string name = readString(from);
string value = readString(from);
if (name == "build-timeout" || name == "use-ssh-substituter")
settings.set(name, value);
else
settings.set(trusted ? name : "untrusted-" + name, value);
}
}
settings.update();
startWork();
stopWork();
break;
}
case wopQuerySubstitutablePathInfo: {
Path path = absPath(readString(from));
startWork();
SubstitutablePathInfos infos;
store->querySubstitutablePathInfos(singleton<PathSet>(path), infos);
stopWork();
SubstitutablePathInfos::iterator i = infos.find(path);
if (i == infos.end())
writeInt(0, to);
else {
writeInt(1, to);
writeString(i->second.deriver, to);
writeStrings(i->second.references, to);
writeLongLong(i->second.downloadSize, to);
if (GET_PROTOCOL_MINOR(clientVersion) >= 7)
writeLongLong(i->second.narSize, to);
}
break;
}
case wopQuerySubstitutablePathInfos: {
PathSet paths = readStorePaths<PathSet>(from);
startWork();
SubstitutablePathInfos infos;
store->querySubstitutablePathInfos(paths, infos);
stopWork();
writeInt(infos.size(), to);
foreach (SubstitutablePathInfos::iterator, i, infos) {
writeString(i->first, to);
writeString(i->second.deriver, to);
writeStrings(i->second.references, to);
writeLongLong(i->second.downloadSize, to);
writeLongLong(i->second.narSize, to);
}
break;
}
case wopQueryAllValidPaths: {
startWork();
PathSet paths = store->queryAllValidPaths();
stopWork();
writeStrings(paths, to);
break;
}
case wopQueryFailedPaths: {
startWork();
PathSet paths = store->queryFailedPaths();
stopWork();
writeStrings(paths, to);
break;
}
case wopClearFailedPaths: {
PathSet paths = readStrings<PathSet>(from);
startWork();
store->clearFailedPaths(paths);
stopWork();
writeInt(1, to);
break;
}
case wopQueryPathInfo: {
Path path = readStorePath(from);
startWork();
ValidPathInfo info = store->queryPathInfo(path);
stopWork();
writeString(info.deriver, to);
writeString(printHash(info.hash), to);
writeStrings(info.references, to);
writeInt(info.registrationTime, to);
writeLongLong(info.narSize, to);
break;
}
default:
throw Error(format("invalid operation %1%") % op);
}
}
static void processConnection(bool trusted)
{
canSendStderr = false;
myPid = getpid();
_writeToStderr = tunnelStderr;
#ifdef HAVE_HUP_NOTIFICATION
/* Allow us to receive SIGPOLL for events on the client socket. */
setSigPollAction(false);
if (fcntl(from.fd, F_SETOWN, getpid()) == -1)
throw SysError("F_SETOWN");
if (fcntl(from.fd, F_SETFL, fcntl(from.fd, F_GETFL, 0) | O_ASYNC) == -1)
throw SysError("F_SETFL");
#endif
/* Exchange the greeting. */
unsigned int magic = readInt(from);
if (magic != WORKER_MAGIC_1) throw Error("protocol mismatch");
writeInt(WORKER_MAGIC_2, to);
writeInt(PROTOCOL_VERSION, to);
to.flush();
unsigned int clientVersion = readInt(from);
if (GET_PROTOCOL_MINOR(clientVersion) >= 14 && readInt(from))
setAffinityTo(readInt(from));
bool reserveSpace = true;
if (GET_PROTOCOL_MINOR(clientVersion) >= 11)
reserveSpace = readInt(from) != 0;
/* Send startup error messages to the client. */
startWork();
try {
/* If we can't accept clientVersion, then throw an error
*here* (not above). */
#if 0
/* Prevent users from doing something very dangerous. */
if (geteuid() == 0 &&
querySetting("build-users-group", "") == "")
throw Error("if you run `nix-daemon' as root, then you MUST set `build-users-group'!");
#endif
/* Open the store. */
store = std::shared_ptr<StoreAPI>(new LocalStore(reserveSpace));
stopWork();
to.flush();
} catch (Error & e) {
stopWork(false, e.msg());
to.flush();
return;
}
/* Process client requests. */
unsigned int opCount = 0;
while (true) {
WorkerOp op;
try {
op = (WorkerOp) readInt(from);
} catch (EndOfFile & e) {
break;
}
opCount++;
try {
performOp(trusted, clientVersion, from, to, op);
} catch (Error & e) {
/* If we're not in a state where we can send replies, then
something went wrong processing the input of the
client. This can happen especially if I/O errors occur
during addTextToStore() / importPath(). If that
happens, just send the error message and exit. */
bool errorAllowed = canSendStderr;
if (!errorAllowed) printMsg(lvlError, format("error processing client input: %1%") % e.msg());
stopWork(false, e.msg(), GET_PROTOCOL_MINOR(clientVersion) >= 8 ? e.status : 0);
if (!errorAllowed) break;
} catch (std::bad_alloc & e) {
if (canSendStderr)
stopWork(false, "Nix daemon out of memory", GET_PROTOCOL_MINOR(clientVersion) >= 8 ? 1 : 0);
throw;
}
to.flush();
assert(!canSendStderr);
};
printMsg(lvlError, format("%1% operations") % opCount);
}
static void sigChldHandler(int sigNo)
{
/* Reap all dead children. */
while (waitpid(-1, 0, WNOHANG) > 0) ;
}
static void setSigChldAction(bool autoReap)
{
struct sigaction act, oact;
act.sa_handler = autoReap ? sigChldHandler : SIG_DFL;
sigfillset(&act.sa_mask);
act.sa_flags = 0;
if (sigaction(SIGCHLD, &act, &oact))
throw SysError("setting SIGCHLD handler");
}
#define SD_LISTEN_FDS_START 3
static void daemonLoop()
{
/* Get rid of children automatically; don't let them become
zombies. */
setSigChldAction(true);
AutoCloseFD fdSocket;
/* Handle socket-based activation by systemd. */
if (getEnv("LISTEN_FDS") != "") {
if (getEnv("LISTEN_PID") != int2String(getpid()) || getEnv("LISTEN_FDS") != "1")
throw Error("unexpected systemd environment variables");
fdSocket = SD_LISTEN_FDS_START;
}
/* Otherwise, create and bind to a Unix domain socket. */
else {
/* Create and bind to a Unix domain socket. */
fdSocket = socket(PF_UNIX, SOCK_STREAM, 0);
if (fdSocket == -1)
throw SysError("cannot create Unix domain socket");
string socketPath = settings.nixDaemonSocketFile;
createDirs(dirOf(socketPath));
/* Urgh, sockaddr_un allows path names of only 108 characters.
So chdir to the socket directory so that we can pass a
relative path name. */
chdir(dirOf(socketPath).c_str());
Path socketPathRel = "./" + baseNameOf(socketPath);
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
if (socketPathRel.size() >= sizeof(addr.sun_path))
throw Error(format("socket path `%1%' is too long") % socketPathRel);
strcpy(addr.sun_path, socketPathRel.c_str());
unlink(socketPath.c_str());
/* Make sure that the socket is created with 0666 permission
(everybody can connect --- provided they have access to the
directory containing the socket). */
mode_t oldMode = umask(0111);
int res = bind(fdSocket, (struct sockaddr *) &addr, sizeof(addr));
umask(oldMode);
if (res == -1)
throw SysError(format("cannot bind to socket `%1%'") % socketPath);
chdir("/"); /* back to the root */
if (listen(fdSocket, 5) == -1)
throw SysError(format("cannot listen on socket `%1%'") % socketPath);
}
closeOnExec(fdSocket);
/* Loop accepting connections. */
while (1) {
try {
/* Important: the server process *cannot* open the SQLite
database, because it doesn't like forks very much. */
assert(!store);
/* Accept a connection. */
struct sockaddr_un remoteAddr;
socklen_t remoteAddrLen = sizeof(remoteAddr);
AutoCloseFD remote = accept(fdSocket,
(struct sockaddr *) &remoteAddr, &remoteAddrLen);
checkInterrupt();
if (remote == -1) {
if (errno == EINTR)
continue;
else
throw SysError("accepting connection");
}
closeOnExec(remote);
/* 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;
socklen_t credLen = sizeof(cred);
if (getsockopt(remote, SOL_SOCKET, SO_PEERCRED, &cred, &credLen) != -1) {
clientPid = cred.pid;
clientUid = cred.uid;
if (clientUid == 0) trusted = true;
}
#endif
printMsg(lvlInfo, format("accepted connection from pid %1%, uid %2%") % clientPid % clientUid);
/* Fork a child to handle the connection. */
pid_t child;
child = fork();
switch (child) {
case -1:
throw SysError("unable to fork");
case 0:
try { /* child */
/* Background the daemon. */
if (setsid() == -1)
throw SysError(format("creating a new session"));
/* Restore normal handling of SIGCHLD. */
setSigChldAction(false);
/* For debugging, stuff the pid into argv[1]. */
if (clientPid != -1 && argvSaved[1]) {
string processName = int2String(clientPid);
strncpy(argvSaved[1], processName.c_str(), strlen(argvSaved[1]));
}
/* Handle the connection. */
from.fd = remote;
to.fd = remote;
processConnection(trusted);
} catch (std::exception & e) {
writeToStderr("unexpected Nix daemon error: " + string(e.what()) + "\n");
}
exit(0);
}
} catch (Interrupted & e) {
throw;
} catch (Error & e) {
printMsg(lvlError, format("error processing connection: %1%") % e.msg());
}
}
}
void run(Strings args)
{
for (Strings::iterator i = args.begin(); i != args.end(); ) {
string arg = *i++;
if (arg == "--daemon") /* ignored for backwards compatibility */;
}
chdir("/");
daemonLoop();
}
void printHelp()
{
showManPage("nix-daemon");
}
string programId = "nix-daemon";

View File

@ -1,89 +0,0 @@
#!/bin/sh
# GNU Guix --- Functional package management for GNU
# Copyright © 2012, 2013, 2014 Ludovic Courtès <ludo@gnu.org>
#
# This file is part of GNU Guix.
#
# GNU Guix is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or (at
# your option) any later version.
#
# GNU Guix is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
#
# Update the local copy of Nix source code needed to build the daemon.
# Assume GNU Coreutils and Git are available.
#
top_srcdir="${top_srcdir:-..}"
log()
{
echo "sync-with-upstream: $@" >&2
}
# checked_in_p FILE
checked_in_p()
{
( cd "$top_srcdir" ;
git ls-tree HEAD -- "nix/$1" | grep "$1" > /dev/null )
}
if [ ! -d "$top_srcdir/build-aux" ]
then
log "\`$top_srcdir' is not the valid top-level source directory"
exit 1
fi
set -e
for upstream_file in `cd "$top_srcdir/nix-upstream/src" ;
find . -name \*.c -or -name \*.h -or -name \*.cc -or -name \*.hh \
-or -name \*.cpp -or -name \*.hpp -or -name \*.sql`
do
if grep "$upstream_file" "$top_srcdir/daemon.am" > /dev/null
then
if checked_in_p "$upstream_file"
then
log "skipping \`$upstream_file', which has a checked-in copy"
else
( cd "$top_srcdir/nix-upstream/src" && \
cp -v --parents "$upstream_file" ../../nix )
fi
else
log "skipping \`$upstream_file', which is not used"
fi
done
# This file should be generated by our build system so remove it.
rm -fv "$top_srcdir/nix/libstore/schema.sql.hh"
cp -v "$top_srcdir/nix-upstream/COPYING" "$top_srcdir/nix"
# Generate an 'AUTHORS' file since upstream Nix no longer has one.
cat > "$top_srcdir/nix/AUTHORS" <<EOF
Most of the code is this directory was written by the following people for
the Nix project (http://nixos.org/nix). Thank you!
EOF
( cd "$top_srcdir/nix-upstream" ; git shortlog --summary ) \
| sed -'es/^ *[0-9]\+\(.*\)/ \1/g' \
>> "$top_srcdir/nix/AUTHORS"
# Substitutions.
sed -i "$top_srcdir/nix/libstore/gc.cc" \
-e 's|/nix/find-runtime-roots\.pl|/guix/list-runtime-roots|g'
# Our 'guix_hash_context' structure has a copy constructor, specifically to
# handle the use case in 'HashSink::currentHash()' where the copy of the
# context is expected to truly copy the underlying hash context. The copy
# constructor cannot be used in 'Ctx' if that's a union, so turn it into a
# structure (we can afford to two wasted words.)
sed -i "$top_srcdir/nix/libutil/hash.cc" "$top_srcdir/nix/libutil/hash.hh" \
-e 's|union Ctx|struct Ctx|g'

View File

@ -5,6 +5,7 @@ de
en@boldquot
en@quot
eo
fr
hu
pt_BR
sr

1368
po/guix/fr.po Normal file

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@ de
en@boldquot
en@quot
eo
fr
hu
pt_BR
sr

1567
po/packages/fr.po Normal file

File diff suppressed because it is too large Load Diff

View File

@ -35,6 +35,11 @@
(define %store
(open-connection-for-tests))
(define-syntax-rule (test-assertm name exp)
(test-assert name
(run-with-store %store exp
#:guile-for-build (%guile-for-build))))
;; Example manifest entries.
(define guile-1.8.8
@ -156,8 +161,7 @@
(equal? (list glibc) install)
(equal? (list (cons guile-1.8.8 guile-2.0.9)) upgrade)))))
(test-assert "profile-derivation"
(run-with-store %store
(test-assertm "profile-derivation"
(mlet* %store-monad
((entry -> (package->manifest-entry %bootstrap-guile))
(guile (package->derivation %bootstrap-guile))
@ -168,7 +172,7 @@
(_ (built-derivations (list drv))))
(return (and (file-exists? (string-append bindir "/guile"))
(string=? (dirname (readlink bindir))
(derivation->output-path guile)))))))
(derivation->output-path guile))))))
(test-end "profiles")

View File

@ -74,7 +74,7 @@
(lset<= string=? names (all-network-interfaces)))))
(test-assert "network-interface-flags"
(let* ((sock (socket SOCK_STREAM AF_INET 0))
(let* ((sock (socket AF_INET SOCK_STREAM 0))
(flags (network-interface-flags sock "lo")))
(close-port sock)
(and (not (zero? (logand flags IFF_LOOPBACK)))
@ -90,6 +90,38 @@
(lambda args
(system-error-errno args)))))
(test-skip (if (zero? (getuid)) 1 0))
(test-equal "set-network-interface-flags"
EPERM
(let ((sock (socket AF_INET SOCK_STREAM 0)))
(catch 'system-error
(lambda ()
(set-network-interface-flags sock "lo" IFF_UP))
(lambda args
(close-port sock)
(system-error-errno args)))))
(test-equal "network-interface-address lo"
(make-socket-address AF_INET (inet-pton AF_INET "127.0.0.1") 0)
(let* ((sock (socket AF_INET SOCK_STREAM 0))
(addr (network-interface-address sock "lo")))
(close-port sock)
addr))
(test-equal "set-network-interface-address"
EPERM
(let ((sock (socket AF_INET SOCK_STREAM 0)))
(catch 'system-error
(lambda ()
(set-network-interface-address sock "nonexistent"
(make-socket-address
AF_INET
(inet-pton AF_INET "127.12.14.15")
0)))
(lambda args
(close-port sock)
(system-error-errno args)))))
(test-end)