Merge branch 'master' into multiple-outputs

This commit is contained in:
Mathijs Kwik 2013-11-01 08:29:09 +01:00
commit db83d9a35b
1631 changed files with 72442 additions and 10896 deletions

1
.gitignore vendored
View File

@ -7,3 +7,4 @@ doc/NEWS.html
doc/NEWS.txt
doc/manual.html
doc/manual.pdf
.version-suffix

1
.version Normal file
View File

@ -0,0 +1 @@
13.10

View File

@ -1 +0,0 @@
1.0

View File

@ -5,7 +5,7 @@
<title>Nixpkgs Manual</title>
<subtitle>Draft (Version <xi:include href="../VERSION"
<subtitle>Draft (Version <xi:include href="../.version"
parse="text" />)</subtitle>
<author>

View File

@ -105,16 +105,6 @@ $ make menuconfig ARCH=<replaceable>arch</replaceable></screen>
</para>
</listitem>
<listitem>
<para>Make sure that
<literal>CONFIG_FB_TILEBLITTING</literal> is <emphasis>not
set</emphasis> (otherwise <command>fbsplash</command> won't
work). This option has a tendency to be enabled as a
side-effect of other options. If it is, investigate why
(there's probably another option that forces it to be on)
and fix it.</para>
</listitem>
<listitem>
<para>Copy <filename>.config</filename> over the new config
file (e.g. <filename>config-2.6.22-i686-smp</filename>).</para>
@ -137,22 +127,9 @@ $ make menuconfig ARCH=<replaceable>arch</replaceable></screen>
kernel modules and kernel-dependent packages listed in the
<varname>kernelPackagesFor</varname> function in
<filename>all-packages.nix</filename> (such as the NVIDIA drivers,
AUFS, splashutils, etc.). If the updated packages arent
backwards compatible with older kernels, you need to keep the
older versions and use some conditionals. For example, new
kernels require splashutils 1.5 while old kernel require 1.3, so
<varname>kernelPackagesFor</varname> says:
<programlisting>
splashutils =
if kernel.features ? fbSplash then splashutils_13 else
if kernel.features ? fbConDecor then splashutils_15 else
null;
splashutils_13 = ...;
splashutils_15 = ...;</programlisting>
</para>
AUFS, etc.). If the updated packages arent backwards compatible
with older kernels, you may need to keep the older versions
around.</para>
</listitem>
</orderedlist>

View File

@ -71,7 +71,7 @@ $ git add pkgs/development/libraries/libfoo/default.nix</screen>
<listitem>
<para>GNU Multiple Precision arithmetic library (GMP): <link
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/libraries/gmp/default.nix"><filename>pkgs/development/libraries/gmp/default.nix</filename></link>.
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/libraries/gmp/5.1.1.nix"><filename>pkgs/development/libraries/gmp/5.1.1.nix</filename></link>.
Also done by the generic builder, but has a dependency on
<varname>m4</varname>.</para>
</listitem>
@ -86,7 +86,7 @@ $ git add pkgs/development/libraries/libfoo/default.nix</screen>
<listitem>
<para>Apache HTTPD: <link
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/http/apache-httpd/default.nix"><filename>pkgs/servers/http/apache-httpd/default.nix</filename></link>.
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/http/apache-httpd/2.4.nix"><filename>pkgs/servers/http/apache-httpd/2.4.nix</filename></link>.
A bunch of optional features, variable substitutions in the
configure flags, a post-install hook, and miscellaneous
hackery.</para>
@ -105,7 +105,7 @@ $ git add pkgs/development/libraries/libfoo/default.nix</screen>
<listitem>
<para>Thunderbird: <link
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/networking/mailreaders/thunderbird/3.x.nix"><filename>pkgs/applications/networking/mailreaders/thunderbird/3.x.nix</filename></link>.
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/networking/mailreaders/thunderbird/default.nix"><filename>pkgs/applications/networking/mailreaders/thunderbird/default.nix</filename></link>.
Lots of dependencies.</para>
</listitem>

View File

@ -29,9 +29,8 @@ rec {
["x" "y"] applied with some value v returns `x.y = v;' */
setAttrByPath = attrPath: value:
if attrPath == [] then value
else listToAttrs [(
nameValuePair (head attrPath) (setAttrByPath (tail attrPath) value)
)];
else listToAttrs
[ { name = head attrPath; value = setAttrByPath (tail attrPath) value; } ];
getAttrFromPath = attrPath: set:
@ -133,7 +132,7 @@ rec {
=> { x = "x-foo"; y = "y-bar"; }
*/
mapAttrs = f: set:
listToAttrs (map (attr: nameValuePair attr (f attr (getAttr attr set))) (attrNames set));
listToAttrs (map (attr: { name = attr; value = f attr (getAttr attr set); }) (attrNames set));
/* Like `mapAttrs', but allows the name of each attribute to be
@ -240,7 +239,7 @@ rec {
# names, hopefully this does not affect the system because the maximal
# laziness avoid computing twice the same expression and listToAttrs does
# not care about duplicated attribute names.
zipAttrsWith = f: sets: zipWithNames (concatMap attrNames sets) f sets;
zipAttrsWith = f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets;
zipAttrs = zipAttrsWith (name: values: values);

View File

@ -8,7 +8,6 @@ let
sources = import ./sources.nix;
modules = import ./modules.nix;
options = import ./options.nix;
properties = import ./properties.nix;
types = import ./types.nix;
meta = import ./meta.nix;
debug = import ./debug.nix;
@ -21,11 +20,13 @@ let
in
{ inherit trivial lists strings stringsWithDeps attrsets sources options
properties modules types meta debug maintainers licenses platforms systems;
modules types meta debug maintainers licenses platforms systems;
# Pull in some builtins not included elsewhere.
inherit (builtins) pathExists readFile;
}
# !!! don't include everything at top-level; perhaps only the most
# commonly used functions.
// trivial // lists // strings // stringsWithDeps // attrsets // sources
// properties // options // types // meta // debug // misc // modules
// options // types // meta // debug // misc // modules
// systems
// customisation

View File

@ -34,11 +34,9 @@
url = "http://sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/AMD-ADL?revision=1.1";
};
apsl20 = {
shortName = "APSL 2.0";
fullName = "Apple Public Source License 2.0";
url = http://opensource.org/licenses/APSL-2.0;
};
# Apple Public Source License 2.0;
# http://opensource.org/licenses/APSL-2.0
apsl20 = "APSL 2.0";
asl20 = {
shortName = "ASL2.0";
@ -96,11 +94,9 @@
url = http://www.mysql.com/about/legal/licensing/foss-exception;
};
gpl2Plus = {
shortName = "GPLv2+";
fullName = "GNU General Public License version 2 or later";
url = http://www.gnu.org/licenses/old-licenses/gpl-2.0.html;
};
# GNU General Public License version 2 or later;
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
gpl2Plus = "GPLv2+";
gpl3 = {
shortName = "GPLv3";
@ -190,6 +186,12 @@
url = http://www.mozilla.org/MPL/MPL-1.1.html;
};
mpl20 = {
shortName = "MPL2.0";
fullName = "Mozilla Public License version 2.0";
url = https://www.mozilla.org/MPL/2.0;
};
openssl = {
shortName = "openssl";
fullName = "OpenSSL license";

View File

@ -1,14 +1,14 @@
# General list operations.
let
inherit (import ./trivial.nix) deepSeq;
inc = builtins.add 1;
dec = n: builtins.sub n 1;
inherit (builtins) elemAt;
in rec {
inherit (builtins) head tail length isList add sub lessThan;
inherit (builtins) head tail length isList add sub lessThan elemAt;
# Create a list consisting of a single element. `singleton x' is
@ -118,6 +118,11 @@ in rec {
all = pred: fold (x: y: if pred x then y else false) true;
# Count how many times function `pred' returns true for the elements
# of `list'.
count = pred: fold (x: c: if pred x then inc c else c) 0;
# Return a singleton list or an empty list, depending on a boolean
# value. Useful when building lists with optional elements
# (e.g. `++ optional (system == "i686-linux") flashplayer').
@ -165,10 +170,11 @@ in rec {
zipLists = zipListsWith (fst: snd: { inherit fst snd; });
# Reverse the order of the elements of a list.
# Reverse the order of the elements of a list. FIXME: O(n^2)!
reverseList = fold (e: acc: acc ++ [ e ]) [];
# Sort a list based on a comparator function which compares two
# elements and returns true if the first argument is strictly below
# the second argument. The returned list is sorted in an increasing

View File

@ -5,6 +5,7 @@
alphabetically sorted. */
aforemny = "Alexander Foremny <alexanderforemny@googlemail.com>";
algorith = "Dries Van Daele <dries_van_daele@telenet.be>";
all = "Nix Committers <nix-commits@lists.science.uu.nl>";
amiddelk = "Arie Middelkoop <amiddelk@gmail.com>";
amorsillo = "Andrew Morsillo <andrew.morsillo@gmail.com>";
@ -19,6 +20,7 @@
bodil = "Bodil Stokke <nix@bodil.org>";
chaoflow = "Florian Friesdorf <flo@chaoflow.net>";
coconnor = "Corey O'Connor <coreyoconnor@gmail.com>";
coroa = "Jonas Hörsch <jonas@chaoflow.net>";
edwtjo = "Edward Tjörnhammar <ed@cflags.cc>";
eelco = "Eelco Dolstra <eelco.dolstra@logicblox.com>";
ertes = "Ertugrul Söylemez <es@ertes.de>";
@ -41,6 +43,7 @@
phreedom = "Evgeny Egorochkin <phreedom@yandex.ru>";
pierron = "Nicolas B. Pierron <nixos@nbp.name>";
piotr = "Piotr Pietraszkiewicz <ppietrasa@gmail.com>";
pSub = "Pascal Wittmann <mail@pascal-wittmann.de>";
qknight = "Joachim Schiele <js@lastlog.de>";
raskin = "Michael Raskin <7c6f434c@mail.ru>";
rickynils = "Rickard Nilsson <rickynils@gmail.com>";
@ -56,6 +59,8 @@
vcunat = "Vladimír Čunát <vcunat@gmail.com>";
viric = "Lluís Batlle i Rossell <viric@viric.name>";
vizanto = "Danny Wilson <danny@prime.vc>";
vlstill = "Vladimír Štill <xstill@fi.muni.cz>";
winden = "Antonio Vargas Gonzalez <windenntw@gmail.com>";
z77z = "Marco Maggesi <maggesi@math.unifi.it>";
zef = "Zef Hemel <zef@zef.me>";
}

View File

@ -206,9 +206,7 @@ rec {
in
work startSet [] [];
genericClosure =
if builtins ? genericClosure then builtins.genericClosure
else lazyGenericClosure;
genericClosure = builtins.genericClosure or lazyGenericClosure;
innerModifySumArgs = f: x: a: b: if b == null then (f a b) // x else
innerModifySumArgs f x (a // b);

312
lib/modules.nix Normal file
View File

@ -0,0 +1,312 @@
with import ./lists.nix;
with import ./trivial.nix;
with import ./attrsets.nix;
with import ./options.nix;
with import ./debug.nix;
with import ./types.nix;
rec {
/* Evaluate a set of modules. The result is a set of two
attributes: options: the nested set of all option declarations,
and config: the nested set of all option values. */
evalModules = { modules, prefix ? [], args ? {}, check ? true }:
let
args' = args // result;
closed = closeModules modules args';
# Note: the list of modules is reversed to maintain backward
# compatibility with the old module system. Not sure if this is
# the most sensible policy.
options = mergeModules prefix (reverseList closed);
# Traverse options and extract the option values into the final
# config set. At the same time, check whether all option
# definitions have matching declarations.
config = yieldConfig prefix options;
yieldConfig = prefix: set:
let res = removeAttrs (mapAttrs (n: v:
if isOption v then v.value
else yieldConfig (prefix ++ [n]) v) set) ["_definedNames"];
in
if check && set ? _definedNames then
fold (m: res:
fold (name: res:
if hasAttr name set then res else throw "The option `${showOption (prefix ++ [name])}' defined in `${m.file}' does not exist.")
res m.names)
res set._definedNames
else
res;
result = { inherit options config; };
in result;
/* Close a set of modules under the imports relation. */
closeModules = modules: args:
let
toClosureList = file: parentKey: imap (n: x:
if isAttrs x || builtins.isFunction x then
unifyModuleSyntax file "${parentKey}:anon-${toString n}" (applyIfFunction x args)
else
unifyModuleSyntax (toString x) (toString x) (applyIfFunction (import x) args));
in
builtins.genericClosure {
startSet = toClosureList unknownModule "" modules;
operator = m: toClosureList m.file m.key m.imports;
};
/* Massage a module into canonical form, that is, a set consisting
of options, config and imports attributes. */
unifyModuleSyntax = file: key: m:
if m ? config || m ? options then
let badAttrs = removeAttrs m ["imports" "options" "config" "key" "_file"]; in
if badAttrs != {} then
throw "Module `${key}' has an unsupported attribute `${head (attrNames badAttrs)}'."
else
{ file = m._file or file;
key = toString m.key or key;
imports = m.imports or [];
options = m.options or {};
config = m.config or {};
}
else
{ file = m._file or file;
key = toString m.key or key;
imports = m.require or [] ++ m.imports or [];
options = {};
config = removeAttrs m ["key" "_file" "require" "imports"];
};
applyIfFunction = f: arg: if builtins.isFunction f then f arg else f;
/* Merge a list of modules. This will recurse over the option
declarations in all modules, combining them into a single set.
At the same time, for each option declaration, it will merge the
corresponding option definitions in all machines, returning them
in the value attribute of each option. */
mergeModules = prefix: modules:
mergeModules' prefix modules
(concatMap (m: map (config: { inherit (m) file; inherit config; }) (pushDownProperties m.config)) modules);
mergeModules' = prefix: options: configs:
listToAttrs (map (name: {
# We're descending into attribute name.
inherit name;
value =
let
loc = prefix ++ [name];
# Get all submodules that declare name.
decls = concatLists (map (m:
if hasAttr name m.options
then [ { inherit (m) file; options = getAttr name m.options; } ]
else []
) options);
# Get all submodules that define name.
defns = concatLists (map (m:
if hasAttr name m.config
then map (config: { inherit (m) file; inherit config; })
(pushDownProperties (getAttr name m.config))
else []
) configs);
nrOptions = count (m: isOption m.options) decls;
# Process mkMerge and mkIf properties.
defns' = concatMap (m:
if hasAttr name m.config
then map (m': { inherit (m) file; value = m'; }) (dischargeProperties (getAttr name m.config))
else []
) configs;
in
if nrOptions == length decls then
let opt = fixupOptionType loc (mergeOptionDecls loc decls);
in evalOptionValue loc opt defns'
else if nrOptions != 0 then
let
firstOption = findFirst (m: isOption m.options) "" decls;
firstNonOption = findFirst (m: !isOption m.options) "" decls;
in
throw "The option `${showOption loc}' in `${firstOption.file}' is a prefix of options in `${firstNonOption.file}'."
else
mergeModules' loc decls defns;
}) (concatMap (m: attrNames m.options) options))
// { _definedNames = map (m: { inherit (m) file; names = attrNames m.config; }) configs; };
/* Merge multiple option declarations into a single declaration. In
general, there should be only one declaration of each option.
The exception is the options attribute, which specifies
sub-options. These can be specified multiple times to allow one
module to add sub-options to an option declared somewhere else
(e.g. multiple modules define sub-options for fileSystems). */
mergeOptionDecls = loc: opts:
fold (opt: res:
if opt.options ? default && res ? default ||
opt.options ? example && res ? example ||
opt.options ? description && res ? description ||
opt.options ? apply && res ? apply ||
opt.options ? type && res ? type
then
throw "The option `${showOption loc}' in `${opt.file}' is already declared in ${showFiles res.declarations}."
else
opt.options // res //
{ declarations = [opt.file] ++ res.declarations;
options = if opt.options ? options then [(toList opt.options.options ++ res.options)] else [];
}
) { inherit loc; declarations = []; options = []; } opts;
/* Merge all the definitions of an option to produce the final
config value. */
evalOptionValue = loc: opt: defs:
let
# Process mkOverride properties, adding in the default
# value specified in the option declaration (if any).
defsFinal = filterOverrides
((if opt ? default then [{ file = head opt.declarations; value = mkOptionDefault opt.default; }] else []) ++ defs);
files = map (def: def.file) defsFinal;
# Type-check the remaining definitions, and merge them if
# possible.
merged =
if defsFinal == [] then
throw "The option `${showOption loc}' is used but not defined."
else
fold (def: res:
if opt.type.check def.value then res
else throw "The option value `${showOption loc}' in `${def.file}' is not a ${opt.type.name}.")
(opt.type.merge loc defsFinal) defsFinal;
# Finally, apply the apply function to the merged
# value. This allows options to yield a value computed
# from the definitions.
value = (opt.apply or id) merged;
in opt //
{ value = addErrorContext "while evaluating the option `${showOption loc}':" value;
definitions = map (def: def.value) defsFinal;
isDefined = defsFinal != [];
inherit files;
};
/* Given a config set, expand mkMerge properties, and push down the
mkIf properties into the children. The result is a list of
config sets that do not have properties at top-level. For
example,
mkMerge [ { boot = set1; } (mkIf cond { boot = set2; services = set3; }) ]
is transformed into
[ { boot = set1; } { boot = mkIf cond set2; services mkIf cond set3; } ].
This transform is the critical step that allows mkIf conditions
to refer to the full configuration without creating an infinite
recursion.
*/
pushDownProperties = cfg:
if cfg._type or "" == "merge" then
concatMap pushDownProperties cfg.contents
else if cfg._type or "" == "if" then
map (mapAttrs (n: v: mkIf cfg.condition v)) (pushDownProperties cfg.content)
else if cfg._type or "" == "override" then
map (mapAttrs (n: v: mkOverride cfg.priority v)) (pushDownProperties cfg.content)
else
[ cfg ];
/* Given a config value, expand mkMerge properties, and discharge
any mkIf conditions. That is, this is the place where mkIf
conditions are actually evaluated. The result is a list of
config values. For example, mkIf false x yields [],
mkIf true x yields [x], and
mkMerge [ 1 (mkIf true 2) (mkIf true (mkIf false 3)) ]
yields [ 1 2 ].
*/
dischargeProperties = def:
if def._type or "" == "merge" then
concatMap dischargeProperties def.contents
else if def._type or "" == "if" then
if def.condition then
dischargeProperties def.content
else
[ ]
else
[ def ];
/* Given a list of config values, process the mkOverride properties,
that is, return the values that have the highest (that is,
numerically lowest) priority, and strip the mkOverride
properties. For example,
[ { file = "/1"; value = mkOverride 10 "a"; }
{ file = "/2"; value = mkOverride 20 "b"; }
{ file = "/3"; value = "z"; }
{ file = "/4"; value = mkOverride 10 "d"; }
]
yields
[ { file = "/1"; value = "a"; }
{ file = "/4"; value = "d"; }
]
Note that "z" has the default priority 100.
*/
filterOverrides = defs:
let
defaultPrio = 100;
getPrio = def: if def.value._type or "" == "override" then def.value.priority else defaultPrio;
min = x: y: if builtins.lessThan x y then x else y;
highestPrio = fold (def: prio: min (getPrio def) prio) 9999 defs;
strip = def: if def.value._type or "" == "override" then def // { value = def.value.content; } else def;
in concatMap (def: if getPrio def == highestPrio then [(strip def)] else []) defs;
/* Hack for backward compatibility: convert options of type
optionSet to configOf. FIXME: remove eventually. */
fixupOptionType = loc: opt:
let
options' = opt.options or
(throw "Option `${showOption loc'}' has type optionSet but has no option attribute.");
coerce = x:
if builtins.isFunction x then x
else { config, ... }: { options = x; };
options = map coerce (flatten options');
f = tp:
if tp.name == "option set" then types.submodule options
else if tp.name == "attribute set of option sets" then types.attrsOf (types.submodule options)
else if tp.name == "list or attribute set of option sets" then types.loaOf (types.submodule options)
else if tp.name == "list of option sets" then types.listOf (types.submodule options)
else if tp.name == "null or option set" then types.nullOr (types.submodule options)
else tp;
in opt // { type = f (opt.type or types.unspecified); };
/* Properties. */
mkIf = condition: content:
{ _type = "if";
inherit condition content;
};
mkAssert = assertion: message: content:
mkIf
(if assertion then true else throw "\nFailed assertion: ${message}")
content;
mkMerge = contents:
{ _type = "merge";
inherit contents;
};
mkOverride = priority: content:
{ _type = "override";
inherit priority content;
};
mkOptionDefault = mkOverride 1001; # priority of option defaults
mkDefault = mkOverride 1000; # used in config sections of non-user modules to set a default
mkForce = mkOverride 50;
mkVMOverride = mkOverride 10; # used by nixos-rebuild build-vm
mkFixStrictness = id; # obsolete, no-op
# FIXME: Add mkOrder back in. It's not currently used anywhere in
# NixOS, but it should be useful.
/* Compatibility. */
fixMergeModules = modules: args: evalModules { inherit modules args; check = false; };
}

120
lib/options.nix Normal file
View File

@ -0,0 +1,120 @@
# Nixpkgs/NixOS option handling.
let lib = import ./default.nix; in
with import ./trivial.nix;
with import ./lists.nix;
with import ./misc.nix;
with import ./attrsets.nix;
with import ./strings.nix;
rec {
isOption = lib.isType "option";
mkOption =
{ default ? null # Default value used when no definition is given in the configuration.
, defaultText ? null # Textual representation of the default, for in the manual.
, example ? null # Example value used in the manual.
, description ? null # String describing the option.
, type ? null # Option type, providing type-checking and value merging.
, apply ? null # Function that converts the option value to something else.
, internal ? null # Whether the option is for NixOS developers only.
, visible ? null # Whether the option shows up in the manual.
, options ? null # Obsolete, used by types.optionSet.
} @ attrs:
attrs // { _type = "option"; };
mkEnableOption = name: mkOption {
default = false;
example = true;
description = "Whether to enable ${name}.";
type = lib.types.bool;
};
mergeDefaultOption = loc: defs:
let list = getValues defs; in
if length list == 1 then head list
else if all builtins.isFunction list then x: mergeDefaultOption loc (map (f: f x) list)
else if all isList list then concatLists list
else if all isAttrs list then fold lib.mergeAttrs {} list
else if all builtins.isBool list then fold lib.or false list
else if all builtins.isString list then lib.concatStrings list
else if all builtins.isInt list && all (x: x == head list) list then head list
else throw "Cannot merge definitions of `${showOption loc}' given in ${showFiles (getFiles defs)}.";
/* Obsolete, will remove soon. Specify an option type or apply
function instead. */
mergeTypedOption = typeName: predicate: merge: loc: list:
let list' = map (x: x.value) list; in
if all predicate list then merge list'
else throw "Expected a ${typeName}.";
mergeEnableOption = mergeTypedOption "boolean"
(x: true == x || false == x) (fold lib.or false);
mergeListOption = mergeTypedOption "list" isList concatLists;
mergeStringOption = mergeTypedOption "string" builtins.isString lib.concatStrings;
mergeOneOption = loc: defs:
if defs == [] then abort "This case should never happen."
else if length defs != 1 then
throw "The unique option `${showOption loc}' is defined multiple times, in ${showFiles (getFiles defs)}."
else (head defs).value;
getValues = map (x: x.value);
getFiles = map (x: x.file);
# Generate documentation template from the list of option declaration like
# the set generated with filterOptionSets.
optionAttrSetToDocList = optionAttrSetToDocList' [];
optionAttrSetToDocList' = prefix: options:
fold (opt: rest:
let
docOption = rec {
name = showOption opt.loc;
description = opt.description or (throw "Option `${name}' has no description.");
declarations = filter (x: x != unknownModule) opt.declarations;
internal = opt.internal or false;
visible = opt.visible or true;
}
// optionalAttrs (opt ? example) { example = scrubOptionValue opt.example; }
// optionalAttrs (opt ? default) { default = scrubOptionValue opt.default; }
// optionalAttrs (opt ? defaultText) { default = opt.defaultText; };
subOptions =
let ss = opt.type.getSubOptions opt.loc;
in if ss != {} then optionAttrSetToDocList' opt.loc ss else [];
in
# FIXME: expensive, O(n^2)
[ docOption ] ++ subOptions ++ rest) [] (collect isOption options);
/* This function recursively removes all derivation attributes from
`x' except for the `name' attribute. This is to make the
generation of `options.xml' much more efficient: the XML
representation of derivations is very large (on the order of
megabytes) and is not actually used by the manual generator. */
scrubOptionValue = x:
if isDerivation x then
{ type = "derivation"; drvPath = x.name; outPath = x.name; name = x.name; }
else if isList x then map scrubOptionValue x
else if isAttrs x then mapAttrs (n: v: scrubOptionValue v) (removeAttrs x ["_args"])
else x;
/* For use in the example option attribute. It causes the given
text to be included verbatim in documentation. This is necessary
for example values that are not simple values, e.g.,
functions. */
literalExample = text: { _type = "literalExample"; inherit text; };
/* Helper functions. */
showOption = concatStringsSep ".";
showFiles = files: concatStringsSep " and " (map (f: "`${f}'") files);
unknownModule = "<unknown-file>";
}

View File

@ -22,7 +22,7 @@ rec {
};
isCpuType = x: typeOf x == "cpu-type"
isCpuType = x: isType "cpu-type" x
&& elem x.bits [8 16 32 64 128]
&& (builtins.lessThan 8 x.bits -> isSignificantByte x.significantByte);
@ -69,7 +69,7 @@ rec {
};
isSystem = x: typeOf x == "system"
isSystem = x: isType "system" x
&& isCpuType x.cpu
&& isArchitecture x.arch
&& isKernel x.kernel;

210
lib/types.nix Normal file
View File

@ -0,0 +1,210 @@
# Definitions related to run-time type checking. Used in particular
# to type-check NixOS configurations.
with import ./lists.nix;
with import ./attrsets.nix;
with import ./options.nix;
with import ./trivial.nix;
with import ./strings.nix;
rec {
isType = type: x: (x._type or "") == type;
typeOf = x: x._type or "";
setType = typeName: value: value // {
_type = typeName;
};
isOptionType = isType "option-type";
mkOptionType =
{ # Human-readable representation of the type.
name
, # Function applied to each definition that should return true if
# its type-correct, false otherwise.
check ? (x: true)
, # Merge a list of definitions together into a single value.
# This function is called with two arguments: the location of
# the option in the configuration as a list of strings
# (e.g. ["boot" "loader "grub" "enable"]), and a list of
# definition values and locations (e.g. [ { file = "/foo.nix";
# value = 1; } { file = "/bar.nix"; value = 2 } ]).
merge ? mergeDefaultOption
, # Return a flat list of sub-options. Used to generate
# documentation.
getSubOptions ? prefix: {}
}:
{ _type = "option-type";
inherit name check merge getSubOptions;
};
types = rec {
unspecified = mkOptionType {
name = "unspecified";
};
bool = mkOptionType {
name = "boolean";
check = builtins.isBool;
merge = loc: fold (x: y: x.value || y) false;
};
int = mkOptionType {
name = "integer";
check = builtins.isInt;
merge = mergeOneOption;
};
str = mkOptionType {
name = "string";
check = builtins.isString;
merge = mergeOneOption;
};
# Merge multiple definitions by concatenating them (with the given
# separator between the values).
separatedString = sep: mkOptionType {
name = "string";
check = builtins.isString;
merge = loc: defs: concatStringsSep sep (getValues defs);
};
lines = separatedString "\n";
commas = separatedString ",";
envVar = separatedString ":";
# Deprecated; should not be used because it quietly concatenates
# strings, which is usually not what you want.
string = separatedString "";
attrs = mkOptionType {
name = "attribute set";
check = isAttrs;
merge = loc: fold (def: mergeAttrs def.value) {};
};
# derivation is a reserved keyword.
package = mkOptionType {
name = "derivation";
check = isDerivation;
merge = mergeOneOption;
};
path = mkOptionType {
name = "path";
# Hacky: there is no isPath primop.
check = x: builtins.unsafeDiscardStringContext (builtins.substring 0 1 (toString x)) == "/";
merge = mergeOneOption;
};
# drop this in the future:
list = builtins.trace "`types.list' is deprecated; use `types.listOf' instead" types.listOf;
listOf = elemType: mkOptionType {
name = "list of ${elemType.name}s";
check = value: isList value && all elemType.check value;
merge = loc: defs:
concatLists (imap (n: def: imap (m: def':
elemType.merge (loc ++ ["[${toString n}-${toString m}]"])
[{ inherit (def) file; value = def'; }]) def.value) defs);
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["*"]);
};
attrsOf = elemType: mkOptionType {
name = "attribute set of ${elemType.name}s";
check = x: isAttrs x && all elemType.check (attrValues x);
merge = loc: defs:
zipAttrsWith (name: elemType.merge (loc ++ [name]))
# Push down position info.
(map (def: listToAttrs (mapAttrsToList (n: def':
{ name = n; value = { inherit (def) file; value = def'; }; }) def.value)) defs);
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name>"]);
};
# List or attribute set of ...
loaOf = elemType:
let
convertIfList = defIdx: def:
if isList def.value then
{ inherit (def) file;
value = listToAttrs (
imap (elemIdx: elem:
{ name = "unnamed-${toString defIdx}.${toString elemIdx}";
value = elem;
}) def.value);
}
else
def;
listOnly = listOf elemType;
attrOnly = attrsOf elemType;
in mkOptionType {
name = "list or attribute set of ${elemType.name}s";
check = x:
if isList x then listOnly.check x
else if isAttrs x then attrOnly.check x
else false;
merge = loc: defs: attrOnly.merge loc (imap convertIfList defs);
getSubOptions = prefix: elemType.getSubOptions (prefix ++ ["<name?>"]);
};
uniq = elemType: mkOptionType {
inherit (elemType) name check;
merge = mergeOneOption;
getSubOptions = elemType.getSubOptions;
};
nullOr = elemType: mkOptionType {
name = "null or ${elemType.name}";
check = x: builtins.isNull x || elemType.check x;
merge = loc: defs:
let nrNulls = count (def: isNull def.value) defs; in
if nrNulls == length defs then null
else if nrNulls != 0 then
throw "The option `${showOption loc}' is defined both null and not null, in ${showFiles (getFiles defs)}."
else elemType.merge loc defs;
getSubOptions = elemType.getSubOptions;
};
functionTo = elemType: mkOptionType {
name = "function that evaluates to a(n) ${elemType.name}";
check = builtins.isFunction;
merge = loc: defs:
fnArgs: elemType.merge loc (map (fn: { inherit (fn) file; value = fn.value fnArgs; }) defs);
getSubOptions = elemType.getSubOptions;
};
submodule = opts:
let
opts' = toList opts;
inherit (import ./modules.nix) evalModules;
in
mkOptionType rec {
name = "submodule";
check = x: isAttrs x || builtins.isFunction x;
merge = loc: defs:
let
coerce = def: if builtins.isFunction def then def else { config = def; };
modules = opts' ++ map (def: { _file = def.file; imports = [(coerce def.value)]; }) defs;
in (evalModules { inherit modules; args.name = last loc; prefix = loc; }).config;
getSubOptions = prefix: (evalModules
{ modules = opts'; inherit prefix;
# FIXME: hack to get shit to evaluate.
args = { name = ""; }; }).options;
};
# Obsolete alternative to configOf. It takes its option
# declarations from the options attribute of containing option
# declaration.
optionSet = mkOptionType {
name = /* builtins.trace "types.optionSet is deprecated; use types.submodule instead" */ "option set";
};
# Augment the given type with an additional type check function.
addCheck = elemType: check: elemType // { check = x: elemType.check x && check x; };
};
}

View File

@ -1,27 +0,0 @@
Adding uClibc support for a new platform
Sometimes you want to cross-compile to another architecture, for example an
embedded devices. For embedded devices the uClibc C library is popular.
In Nixpkgs there is support for uClibc for several architectures but not
everything is supported.
Adding support is not very difficult,
* Add your architecture to the buildfiles in
$nixpkgs/development/tools/misc/binutils-cross
* Add your architecture to the buildfiles in
$nixpkgs/development/compilers/gcc-4.0-cross
* Add your architecture to the buildfiles in
$nixpkgs/os-specific/linux/kernel-headers-cross
* Add your architecture to the buildfiles in
$nixpkgs/development/uclibc
In the latter directory you will also need a configuration file for uClibc.
You can make these by unpacking the uClibc sources and run a "make menuconfig".
In the configuration a few things need to be adapted:
- kernel sources -> need to point at our own kernel headers
- install -> needs to point at $out

View File

@ -1,7 +1,7 @@
# Evaluate `release.nix' like Hydra would. Too bad nix-instantiate
# can't to do this.
with import ../../pkgs/lib;
with import ../../lib;
let
trace = if builtins.getEnv "VERBOSE" == "1" then builtins.trace else (x: y: y);
@ -10,14 +10,15 @@ let
# Add the recurseForDerivations attribute to ensure that
# nix-instantiate recurses into nested attribute sets.
recurse = attrs:
recurse = path: attrs:
if (builtins.tryEval attrs).success then
if isDerivation attrs
if isDerivation attrs
then
if (builtins.tryEval attrs.outPath).success
then attrs
else { }
else { recurseForDerivations = true; } // mapAttrs (n: v: recurse v) attrs
if (builtins.tryEval attrs.drvPath).success
then { inherit (attrs) name drvPath; }
else { failed = true; }
else { recurseForDerivations = true; } //
mapAttrs (n: v: let path' = path ++ [n]; in trace path' (recurse path' v)) attrs
else { };
in recurse rel
in recurse [] rel

1
nixos/.topmsg Normal file
View File

@ -0,0 +1 @@
improvements to vsftpd module

18
nixos/COPYING Normal file
View File

@ -0,0 +1,18 @@
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

5
nixos/README Normal file
View File

@ -0,0 +1,5 @@
*** NixOS ***
NixOS is a Linux distribution based on the purely functional package
management system Nix. More information can be found at
http://nixos.org/nixos and in the manual in doc/manual.

43
nixos/default.nix Normal file
View File

@ -0,0 +1,43 @@
{ configuration ? import ./lib/from-env.nix "NIXOS_CONFIG" <nixos-config>
, system ? builtins.currentSystem
}:
let
eval = import ./lib/eval-config.nix {
inherit system;
modules = [ configuration ];
};
inherit (eval) pkgs;
# This is for `nixos-rebuild build-vm'.
vmConfig = (import ./lib/eval-config.nix {
inherit system;
modules = [ configuration ./modules/virtualisation/qemu-vm.nix ];
}).config;
# This is for `nixos-rebuild build-vm-with-bootloader'.
vmWithBootLoaderConfig = (import ./lib/eval-config.nix {
inherit system;
modules =
[ configuration
./modules/virtualisation/qemu-vm.nix
{ virtualisation.useBootLoader = true; }
];
}).config;
in
{
inherit (eval) config options;
system = eval.config.system.build.toplevel;
vm = vmConfig.system.build.vm;
vmWithBootLoader = vmWithBootLoaderConfig.system.build.vm;
# The following are used by nixos-rebuild.
nixFallback = pkgs.nixUnstable;
}

View File

@ -0,0 +1,21 @@
{
boot = {
loader.grub.device = "/dev/sda";
};
fileSystems = [
{ mountPoint = "/";
device = "/dev/sda1";
}
];
swapDevices = [
{ device = "/dev/sdb1"; }
];
services = {
openssh = {
enable = true;
};
};
}

View File

@ -0,0 +1,32 @@
{
boot = {
loader.grub.device = "/dev/sda";
copyKernels = true;
bootMount = "(hd0,0)";
};
fileSystems = [
{ mountPoint = "/";
device = "/dev/sda3";
}
{ mountPoint = "/boot";
device = "/dev/sda1";
neededForBoot = true;
}
];
swapDevices = [
{ device = "/dev/sda2"; }
];
services = {
sshd = {
enable = true;
};
};
fonts = {
enableFontConfig = false;
};
}

View File

@ -0,0 +1,27 @@
# This configuration has / on a LVM volume. Since Grub
# doesn't know about LVM, a separate /boot is therefore
# needed.
#
# In this example, labels are used for file systems and
# swap devices: "boot" might be /dev/sda1, "root" might be
# /dev/my-volume-group/root, and "swap" might be /dev/sda2.
# In particular there is no specific reference to the fact
# that / is on LVM; that's figured out automatically.
{
boot.loader.grub.device = "/dev/sda";
boot.initrd.kernelModules = ["ata_piix"];
fileSystems = [
{ mountPoint = "/";
label = "root";
}
{ mountPoint = "/boot";
label = "boot";
}
];
swapDevices = [
{ label = "swap"; }
];
}

View File

@ -0,0 +1,36 @@
{
boot = {
loader.grub.device = "/dev/sda";
};
fileSystems = [
{ mountPoint = "/";
device = "/dev/sda1";
}
];
services = {
sshd = {
enable = true;
};
httpd = {
enable = true;
adminAddr = "admin@example.org";
subservices = {
subversion = {
enable = true;
dataDir = "/data/subversion";
notificationSender = "svn@example.org";
};
};
};
};
}

View File

@ -0,0 +1,20 @@
# Configuration file used to install NixOS-x86_64 on a USB stick.
{
boot = {
loader.grub.device = "/dev/sda";
initrd = {
kernelModules = ["usb_storage" "ehci_hcd" "ohci_hcd"];
};
};
fileSystems = [
{ mountPoint = "/";
label = "nixos-usb";
}
];
fonts = {
enableFontConfig = false;
};
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,118 @@
{ pkgs, options
, revision ? "master"
}:
with pkgs.lib;
let
# Remove invisible and internal options.
options' = filter (opt: opt.visible && !opt.internal) (optionAttrSetToDocList options);
# Clean up declaration sites to not refer to the NixOS source tree.
options'' = flip map options' (opt: opt // {
declarations = map (fn: stripPrefix fn) opt.declarations;
});
prefix = toString pkgs.path;
stripPrefix = fn:
if substring 0 (stringLength prefix) fn == prefix then
substring (add (stringLength prefix) 1) 1000 fn
else
fn;
optionsXML = builtins.toFile "options.xml" (builtins.unsafeDiscardStringContext (builtins.toXML options''));
optionsDocBook = pkgs.runCommand "options-db.xml" {} ''
if grep /nixpkgs/nixos/modules ${optionsXML}; then
echo "The manual appears to depend on the location of Nixpkgs, which is bad"
echo "since this prevents sharing via the NixOS channel. This is typically"
echo "caused by an option default that refers to a relative path (see above"
echo "for hints about the offending path)."
exit 1
fi
${pkgs.libxslt}/bin/xsltproc \
--stringparam revision '${revision}' \
-o $out ${./options-to-docbook.xsl} ${optionsXML}
'';
in rec {
# Generate the NixOS manual.
manual = pkgs.stdenv.mkDerivation {
name = "nixos-manual";
sources = sourceFilesBySuffices ./. [".xml"];
buildInputs = [ pkgs.libxml2 pkgs.libxslt ];
xsltFlags = ''
--param section.autolabel 1
--param section.label.includes.component.label 1
--param html.stylesheet 'style.css'
--param xref.with.number.and.title 1
--param toc.section.depth 3
--param admon.style '''
--param callout.graphics.extension '.gif'
'';
buildCommand = ''
ln -s $sources/*.xml . # */
ln -s ${optionsDocBook} options-db.xml
# Check the validity of the manual sources.
xmllint --noout --nonet --xinclude --noxincludenode \
--relaxng ${pkgs.docbook5}/xml/rng/docbook/docbook.rng \
manual.xml
# Generate the HTML manual.
dst=$out/share/doc/nixos
ensureDir $dst
xsltproc $xsltFlags --nonet --xinclude \
--output $dst/manual.html \
${pkgs.docbook5_xsl}/xml/xsl/docbook/xhtml/docbook.xsl \
./manual.xml
mkdir -p $dst/images/callouts
cp ${pkgs.docbook5_xsl}/xml/xsl/docbook/images/callouts/*.gif $dst/images/callouts/
cp ${./style.css} $dst/style.css
mkdir -p $out/nix-support
echo "nix-build out $out" >> $out/nix-support/hydra-build-products
echo "doc manual $dst manual.html" >> $out/nix-support/hydra-build-products
''; # */
meta.description = "The NixOS manual in HTML format";
};
# Generate the NixOS manpages.
manpages = pkgs.stdenv.mkDerivation {
name = "nixos-manpages";
sources = sourceFilesBySuffices ./. [".xml"];
buildInputs = [ pkgs.libxml2 pkgs.libxslt ];
buildCommand = ''
ln -s $sources/*.xml . # */
ln -s ${optionsDocBook} options-db.xml
# Check the validity of the manual sources.
xmllint --noout --nonet --xinclude --noxincludenode \
--relaxng ${pkgs.docbook5}/xml/rng/docbook/docbook.rng \
./man-pages.xml
# Generate manpages.
mkdir -p $out/share/man
xsltproc --nonet --xinclude \
--param man.output.in.separate.dir 1 \
--param man.output.base.dir "'$out/share/man/'" \
--param man.endnotes.are.numbered 0 \
${pkgs.docbook5_xsl}/xml/xsl/docbook/manpages/docbook.xsl \
./man-pages.xml
'';
};
}

View File

@ -0,0 +1,853 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Development</title>
<para>This chapter describes how you can modify and extend
NixOS.</para>
<!--===============================================================-->
<section>
<title>Getting the sources</title>
<para>By default, NixOSs <command>nixos-rebuild</command> command
uses the NixOS and Nixpkgs sources provided by the
<literal>nixos-unstable</literal> channel (kept in
<filename>/nix/var/nix/profiles/per-user/root/channels/nixos</filename>).
To modify NixOS, however, you should check out the latest sources from
Git. This is done using the following command:
<screen>
$ nixos-checkout <replaceable>/my/sources</replaceable>
</screen>
or
<screen>
$ mkdir -p <replaceable>/my/sources</replaceable>
$ cd <replaceable>/my/sources</replaceable>
$ nix-env -i git
$ git clone git://github.com/NixOS/nixpkgs.git
</screen>
This will check out the latest NixOS sources to
<filename><replaceable>/my/sources</replaceable>/nixpkgs/nixos</filename>
and the Nixpkgs sources to
<filename><replaceable>/my/sources</replaceable>/nixpkgs</filename>.
(The NixOS source tree lives in a subdirectory of the Nixpkgs
repository.) If you want to rebuild your system using your (modified)
sources, you need to tell <command>nixos-rebuild</command> about them
using the <option>-I</option> flag:
<screen>
$ nixos-rebuild switch -I nixpkgs=<replaceable>/my/sources</replaceable>/nixpkgs
</screen>
</para>
<para>If you want <command>nix-env</command> to use the expressions in
<replaceable>/my/sources</replaceable>, use <command>nix-env -f
<replaceable>/my/sources</replaceable>/nixpkgs</command>, or change
the default by adding a symlink in
<filename>~/.nix-defexpr</filename>:
<screen>
$ ln -s <replaceable>/my/sources</replaceable>/nixpkgs ~/.nix-defexpr/nixpkgs
</screen>
You may want to delete the symlink
<filename>~/.nix-defexpr/channels_root</filename> to prevent roots
NixOS channel from clashing with your own tree.</para>
<!-- FIXME: not sure what this means.
<para>You should not pass the base directory
<filename><replaceable>/my/sources</replaceable></filename>
to <command>nix-env</command>, as it will break after interpreting expressions
in <filename>nixos/</filename> as packages.</para>
-->
</section>
<!--===============================================================-->
<section>
<title>Writing NixOS modules</title>
<para>NixOS has a modular system for declarative configuration. This
system combines multiple <emphasis>modules</emphasis> to produce the
full system configuration. One of the modules that constitute the
configuration is <filename>/etc/nixos/configuration.nix</filename>.
Most of the others live in the <link
xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/modules"><filename>nixos/modules</filename></link>
subdirectory of the Nixpkgs tree.</para>
<para>Each NixOS module is a file that handles one logical aspect of
the configuration, such as a specific kind of hardware, a service, or
network settings. A module configuration does not have to handle
everything from scratch; it can use the functionality provided by
other modules for its implementation. Thus a module can
<emphasis>declare</emphasis> options that can be used by other
modules, and conversely can <emphasis>define</emphasis> options
provided by other modules in its own implementation. For example, the
module <link
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/security/pam.nix"><filename>pam.nix</filename></link>
declares the option <option>security.pam.services</option> that allows
other modules (e.g. <link
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/ssh/sshd.nix"><filename>sshd.nix</filename></link>)
to define PAM services; and it defines the option
<option>environment.etc</option> (declared by <link
xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/system/etc/etc.nix"><filename>etc.nix</filename></link>)
to cause files to be created in
<filename>/etc/pam.d</filename>.</para>
<para xml:id="para-module-syn">In <xref
linkend="sec-configuration-syntax"/>, we saw the following structure
of NixOS modules:
<programlisting>
{ config, pkgs, ... }:
{ <replaceable>option definitions</replaceable>
}
</programlisting>
This is actually an <emphasis>abbreviated</emphasis> form of module
that only defines options, but does not declare any. The structure of
full NixOS modules is shown in <xref linkend='ex-module-syntax' />.</para>
<example xml:id='ex-module-syntax'><title>Structure of NixOS modules</title>
<programlisting>
{ config, pkgs, ... }: <co xml:id='module-syntax-1' />
{
imports =
[ <replaceable>paths of other modules</replaceable> <co xml:id='module-syntax-2' />
];
options = {
<replaceable>option declarations</replaceable> <co xml:id='module-syntax-3' />
};
config = {
<replaceable>option definitions</replaceable> <co xml:id='module-syntax-4' />
};
}</programlisting>
</example>
<para>The meaning of each part is as follows.
<calloutlist>
<callout arearefs='module-syntax-1'>
<para>This line makes the current Nix expression a function. The
variable <varname>pkgs</varname> contains Nixpkgs, while
<varname>config</varname> contains the full system configuration.
This line can be omitted if there is no reference to
<varname>pkgs</varname> and <varname>config</varname> inside the
module.</para>
</callout>
<callout arearefs='module-syntax-2'>
<para>This list enumerates the paths to other NixOS modules that
should be included in the evaluation of the system configuration.
A default set of modules is defined in the file
<filename>modules/module-list.nix</filename>. These don't need to
be added in the import list.</para>
</callout>
<callout arearefs='module-syntax-3'>
<para>The attribute <varname>options</varname> is a nested set of
<emphasis>option declarations</emphasis> (described below).</para>
</callout>
<callout arearefs='module-syntax-4'>
<para>The attribute <varname>config</varname> is a nested set of
<emphasis>option definitions</emphasis> (also described
below).</para>
</callout>
</calloutlist>
</para>
<para><xref linkend='locate-example' /> shows a module that handles
the regular update of the “locate” database, an index of all files in
the file system. This module declares two options that can be defined
by other modules (typically the users
<filename>configuration.nix</filename>):
<option>services.locate.enable</option> (whether the database should
be updated) and <option>services.locate.period</option> (when the
update should be done). It implements its functionality by defining
two options declared by other modules:
<option>systemd.services</option> (the set of all systemd services)
and <option>services.cron.systemCronJobs</option> (the list of
commands to be executed periodically by <command>cron</command>).</para>
<example xml:id='locate-example'><title>NixOS module for the “locate” service</title>
<programlisting>
{ config, pkgs, ... }:
with pkgs.lib;
let locatedb = "/var/cache/locatedb"; in
{
options = {
services.locate = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
If enabled, NixOS will periodically update the database of
files used by the <command>locate</command> command.
'';
};
period = mkOption {
type = types.str;
default = "15 02 * * *";
description = ''
This option defines (in the format used by cron) when the
locate database is updated. The default is to update at
02:15 at night every day.
'';
};
};
};
config = {
systemd.services.update-locatedb =
{ description = "Update Locate Database";
path = [ pkgs.su ];
script =
''
mkdir -m 0755 -p $(dirname ${locatedb})
exec updatedb --localuser=nobody --output=${locatedb} --prunepaths='/tmp /var/tmp /media /run'
'';
};
services.cron.systemCronJobs = optional config.services.locate.enable
"${config.services.locate.period} root ${config.systemd.package}/bin/systemctl start update-locatedb.service";
};
}</programlisting>
</example>
<section><title>Option declarations</title>
<para>An option declaration specifies the name, type and description
of a NixOS configuration option. It is illegal to define an option
that hasnt been declared in any module. A option declaration
generally looks like this:
<programlisting>
options = {
<replaceable>name</replaceable> = mkOption {
type = <replaceable>type specification</replaceable>;
default = <replaceable>default value</replaceable>;
example = <replaceable>example value</replaceable>;
description = "<replaceable>Description for use in the NixOS manual.</replaceable>";
};
};
</programlisting>
</para>
<para>The function <varname>mkOption</varname> accepts the following arguments.
<variablelist>
<varlistentry>
<term><varname>type</varname></term>
<listitem>
<para>The type of the option (see below). It may be omitted,
but thats not advisable since it may lead to errors that are
hard to diagnose.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>default</varname></term>
<listitem>
<para>The default value used if no value is defined by any
module. A default is not required; in that case, if the option
value is ever used, an error will be thrown.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>example</varname></term>
<listitem>
<para>An example value that will be shown in the NixOS manual.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>description</varname></term>
<listitem>
<para>A textual description of the option, in DocBook format,
that will be included in the NixOS manual.</para>
</listitem>
</varlistentry>
</variablelist>
</para>
<para>Here is a non-exhaustive list of option types:
<variablelist>
<varlistentry>
<term><varname>types.bool</varname></term>
<listitem>
<para>A Boolean.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>types.int</varname></term>
<listitem>
<para>An integer.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>types.str</varname></term>
<listitem>
<para>A string.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>types.lines</varname></term>
<listitem>
<para>A string. If there are multiple definitions, they are
concatenated, with newline characters in between.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>types.path</varname></term>
<listitem>
<para>A path, defined as anything that, when coerced to a
string, starts with a slash. This includes derivations.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>types.listOf</varname> <replaceable>t</replaceable></term>
<listitem>
<para>A list of elements of type <replaceable>t</replaceable>
(e.g., <literal>types.listOf types.str</literal> is a list of
strings). Multiple definitions are concatenated together.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>types.attrsOf</varname> <replaceable>t</replaceable></term>
<listitem>
<para>A set of elements of type <replaceable>t</replaceable>
(e.g., <literal>types.attrsOf types.int</literal> is a set of
name/value pairs, the values being integers).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>types.nullOr</varname> <replaceable>t</replaceable></term>
<listitem>
<para>Either the value <literal>null</literal> or something of
type <replaceable>t</replaceable>.</para>
</listitem>
</varlistentry>
</variablelist>
You can also create new types using the function
<varname>mkOptionType</varname>. See
<filename>lib/types.nix</filename> in Nixpkgs for details.</para>
</section>
<section><title>Option definitions</title>
<para>Option definitions are generally straight-forward bindings of values to option names, like
<programlisting>
config = {
services.httpd.enable = true;
};
</programlisting>
However, sometimes you need to wrap an option definition or set of
option definitions in a <emphasis>property</emphasis> to achieve
certain effects:</para>
<simplesect><title>Delaying conditionals</title>
<para>If a set of option definitions is conditional on the value of
another option, you may need to use <varname>mkIf</varname>.
Consider, for instance:
<programlisting>
config = if config.services.httpd.enable then {
environment.systemPackages = [ <replaceable>...</replaceable> ];
<replaceable>...</replaceable>
} else {};
</programlisting>
This definition will cause Nix to fail with an “infinite recursion”
error. Why? Because the value of
<option>config.services.httpd.enable</option> depends on the value
being constructed here. After all, you could also write the clearly
circular and contradictory:
<programlisting>
config = if config.services.httpd.enable then {
services.httpd.enable = false;
} else {
services.httpd.enable = true;
};
</programlisting>
The solution is to write:
<programlisting>
config = mkIf config.services.httpd.enable {
environment.systemPackages = [ <replaceable>...</replaceable> ];
<replaceable>...</replaceable>
};
</programlisting>
The special function <varname>mkIf</varname> causes the evaluation of
the conditional to be “pushed down” into the individual definitions,
as if you had written:
<programlisting>
config = {
environment.systemPackages = if config.services.httpd.enable then [ <replaceable>...</replaceable> ] else [];
<replaceable>...</replaceable>
};
</programlisting>
</para>
</simplesect>
<simplesect><title>Setting priorities</title>
<para>A module can override the definitions of an option in other
modules by setting a <emphasis>priority</emphasis>. All option
definitions that do not have the lowest priority value are discarded.
By default, option definitions have priority 1000. You can specify an
explicit priority by using <varname>mkOverride</varname>, e.g.
<programlisting>
services.openssh.enable = mkOverride 10 false;
</programlisting>
This definition causes all other definitions with priorities above 10
to be discarded. The function <varname>mkForce</varname> is
equal to <varname>mkOverride 50</varname>.</para>
</simplesect>
<simplesect><title>Merging configurations</title>
<para>In conjunction with <literal>mkIf</literal>, it is sometimes
useful for a module to return multiple sets of option definitions, to
be merged together as if they were declared in separate modules. This
can be done using <varname>mkMerge</varname>:
<programlisting>
config = mkMerge
[ # Unconditional stuff.
{ environment.systemPackages = [ <replaceable>...</replaceable> ];
}
# Conditional stuff.
(mkIf config.services.bla.enable {
environment.systemPackages = [ <replaceable>...</replaceable> ];
})
];
</programlisting>
</para>
</simplesect>
</section>
<section><title>Important options</title>
<para>NixOS has many options, but some are of particular importance to
module writers.</para>
<variablelist>
<varlistentry>
<term><option>etc.environment</option></term>
<listitem>
<para>This set defines files in <filename>/etc</filename>. A
typical use is:
<programlisting>
environment.etc."os-release".text =
''
NAME=NixOS
<replaceable>...</replaceable>
'';
</programlisting>
which causes a file named <filename>/etc/os-release</filename>
to be created with the given contents.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>system.activationScripts</option></term>
<listitem>
<para>A set of shell script fragments that must be executed
whenever the configuration is activated (i.e., at boot time, or
after running <command>nixos-rebuild switch</command>). For instance,
<programlisting>
system.activationScripts.media =
''
mkdir -m 0755 -p /media
'';
</programlisting>
causes the directory <filename>/media</filename> to be created.
Activation scripts must be idempotent. They should not start
background processes such as daemons; use
<option>systemd.services</option> for that.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>systemd.services</option></term>
<listitem>
<para>This is the set of systemd services. Example:
<programlisting>
systemd.services.dhcpcd =
{ description = "DHCP Client";
wantedBy = [ "multi-user.target" ];
after = [ "systemd-udev-settle.service" ];
path = [ dhcpcd pkgs.nettools pkgs.openresolv ];
serviceConfig =
{ Type = "forking";
PIDFile = "/run/dhcpcd.pid";
ExecStart = "${dhcpcd}/sbin/dhcpcd --config ${dhcpcdConf}";
Restart = "always";
};
};
</programlisting>
which creates the systemd unit
<literal>dhcpcd.service</literal>. The option
<option>wantedBy</option> determined which other units pull this
one in; <literal>multi-user.target</literal> is the default
target of the system, so <literal>dhcpcd.service</literal> will
always be started. The option
<option>serviceConfig.ExecStart</option> provides the main
command for the service; its also possible to provide pre-start
actions, stop scripts, and so on.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>users.extraUsers</option></term>
<term><option>users.extraGroups</option></term>
<listitem>
<para>If your service requires special UIDs or GIDs, you can
define them with these options. See <xref
linkend="sec-user-management"/> for details.</para>
</listitem>
</varlistentry>
</variablelist>
</section>
</section>
<!--===============================================================-->
<section>
<title>Building specific parts of NixOS</title>
<para>With the command <command>nix-build</command>, you can build
specific parts of your NixOS configuration. This is done as follows:
<screen>
$ cd <replaceable>/path/to/nixpkgs/nixos</replaceable>
$ nix-build -A config.<replaceable>option</replaceable></screen>
where <replaceable>option</replaceable> is a NixOS option with type
“derivation” (i.e. something that can be built). Attributes of
interest include:
<variablelist>
<varlistentry>
<term><varname>system.build.toplevel</varname></term>
<listitem>
<para>The top-level option that builds the entire NixOS system.
Everything else in your configuration is indirectly pulled in by
this option. This is what <command>nixos-rebuild</command>
builds and what <filename>/run/current-system</filename> points
to afterwards.</para>
<para>A shortcut to build this is:
<screen>
$ nix-build -A system</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>system.build.manual.manual</varname></term>
<listitem><para>The NixOS manual.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>system.build.etc</varname></term>
<listitem><para>A tree of symlinks that form the static parts of
<filename>/etc</filename>.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>system.build.initialRamdisk</varname></term>
<term><varname>system.build.kernel</varname></term>
<listitem>
<para>The initial ramdisk and kernel of the system. This allows
a quick way to test whether the kernel and the initial ramdisk
boot correctly, by using QEMUs <option>-kernel</option> and
<option>-initrd</option> options:
<screen>
$ nix-build -A config.system.build.initialRamdisk -o initrd
$ nix-build -A config.system.build.kernel -o kernel
$ qemu-system-x86_64 -kernel ./kernel/bzImage -initrd ./initrd/initrd -hda /dev/null
</screen>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>system.build.nixos-rebuild</varname></term>
<term><varname>system.build.nixos-install</varname></term>
<term><varname>system.build.nixos-generate-config</varname></term>
<listitem>
<para>These build the corresponding NixOS commands.</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</section>
<!--===============================================================-->
<section>
<title>Building your own NixOS CD</title>
<para>Building a NixOS CD is as easy as configuring your own computer. The
idea is to use another module which will replace
your <filename>configuration.nix</filename> to configure the system that
would be installed on the CD.</para>
<para>Default CD/DVD configurations are available
inside <filename>nixos/modules/installer/cd-dvd</filename>. To build them
you have to set <envar>NIXOS_CONFIG</envar> before
running <command>nix-build</command> to build the ISO.
<screen>
$ nix-build -A config.system.build.isoImage -I nixos-config=modules/installer/cd-dvd/installation-cd-minimal.nix</screen>
</para>
<para>Before burning your CD/DVD, you can check the content of the image by mounting anywhere like
suggested by the following command:
<screen>
$ mount -o loop -t iso9660 ./result/iso/cd.iso /mnt/iso</screen>
</para>
</section>
<!--===============================================================-->
<section>
<title>Testing the installer</title>
<para>Building, burning, and
booting from an installation CD is rather
tedious, so here is a quick way to see if the installer works
properly:
<screen>
$ nix-build -A config.system.build.nixos-install
$ dd if=/dev/zero of=diskimage seek=2G count=0 bs=1
$ yes | mke2fs -j diskimage
$ mount -o loop diskimage /mnt
$ ./result/bin/nixos-install</screen>
</para>
</section>
<!--===============================================================-->
<section><title>Whole-system testing using virtual machines</title>
<para>Complete NixOS GNU/Linux systems can be tested in virtual
machines (VMs). This makes it possible to test a system upgrade or
configuration change before rebooting into it, using the
<command>nixos-rebuild build-vm</command> or <command>nixos-rebuild
build-vm-with-bootloader</command> command.</para>
<!-- The following is adapted from
http://wiki.nixos.org/wiki/NixOS_VM_tests, by Eelco Dolstra. -->
<para>The <filename>tests/</filename> directory in the NixOS source
tree contains several <emphasis>whole-system unit tests</emphasis>.
These tests can be run<footnote><para>NixOS tests can be run both from
NixOS and from a non-NixOS GNU/Linux distribution, provided the Nix
package manager is installed.</para></footnote> from the NixOS source
tree as follows:
<screen>
$ nix-build tests/ -A nfs.test
</screen>
This performs an automated test of the NFS client and server
functionality in the Linux kernel, including file locking semantics
(e.g., whether locks are maintained across server crashes). It will
first build or download all the dependencies of the test (e.g., all
packages needed to run a NixOS VM). The test is defined in <link
xlink:href="https://nixos.org/repos/nix/nixos/trunk/tests/nfs.nix">
<filename>tests/nfs.nix</filename></link>. If the test succeeds,
<command>nix-build</command> will place a symlink
<filename>./result</filename> in the current directory pointing at the
location in the Nix store of the test results (e.g., screenshots, test
reports, and so on). In particular, a pretty-printed log of the test
is written to <filename>log.html</filename>, which can be viewed using
a web browser like this:
<screen>
$ firefox result/log.html
</screen>
</para>
<para>It is also possible to run the test environment interactively,
allowing you to experiment with the VMs. For example:
<screen>
$ nix-build tests/ -A nfs.driver
$ ./result/bin/nixos-run-vms
</screen>
The script <command>nixos-run-vms</command> starts the three virtual
machines defined in the NFS test using QEMU/KVM. The root file system
of the VMs is created on the fly and kept across VM restarts in
<filename>./</filename><varname>hostname</varname><filename>.qcow2</filename>.</para>
<para>Finally, the test itself can be run interactively. This is
particularly useful when developing or debugging a test:
<screen>
$ nix-build tests/ -A nfs.driver
$ ./result/bin/nixos-test-driver
starting VDE switch for network 1
&gt;
</screen>
Perl statements can now be typed in to start or manipulate the VMs:
<screen>
&gt; startAll;
(the VMs start booting)
&gt; $server-&gt;waitForJob("nfs-kernel-nfsd");
&gt; $client1-&gt;succeed("flock -x /data/lock -c 'sleep 100000' &amp;");
&gt; $client2-&gt;fail("flock -n -s /data/lock true");
&gt; $client1-&gt;shutdown;
(this releases client1's lock)
&gt; $client2-&gt;succeed("flock -n -s /data/lock true");
</screen>
The function <command>testScript</command> executes the entire test
script and drops you back into the test driver command line upon its
completion. This allows you to inspect the state of the VMs after the
test (e.g. to debug the test script).</para>
<para>This and other tests are continuously run on <link
xlink:href="http://hydra.nixos.org/jobset/nixos/trunk">the Hydra
instance at <literal>nixos.org</literal></link>, which allows
developers to be notified of any regressions introduced by a NixOS or
Nixpkgs change.</para>
<para>The actual Nix programming interface to VM testing is in NixOS,
under <link
xlink:href="https://nixos.org/repos/nix/nixos/trunk/lib/testing.nix">
<filename>lib/testing.nix</filename></link>. This file defines a
function which takes an attribute set containing a
<literal>nixpkgs</literal> attribute (the path to a Nixpkgs checkout),
and a <literal>system</literal> attribute (the system type). It
returns an attribute set containing several utility functions, among
which the main entry point is <literal>makeTest</literal>.
</para>
<para>The <literal>makeTest</literal> function takes a function
similar to that found in <link
xlink:href="https://nixos.org/repos/nix/nixos/trunk/tests/nfs.nix">
<filename>tests/nfs.nix</filename></link> (discussed above). It
returns an attribute set containing (among others):
<variablelist>
<varlistentry>
<term><varname>test</varname></term>
<listitem><para>A derivation containing the test log as an HTML
file, as seen above, suitable for presentation in the Hydra
continuous build system.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>report</varname></term>
<listitem><para>A derivation containing a code coverage report, with
meta-data suitable for Hydra.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>driver</varname></term>
<listitem><para>A derivation containing scripts to run the VM test or
interact with the VM network interactively, as seen above.</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</section>
</chapter>

View File

@ -0,0 +1,469 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Installing NixOS</title>
<!--===============================================================-->
<section>
<title>Obtaining NixOS</title>
<para>NixOS ISO images can be downloaded from the <link
xlink:href="http://nixos.org/nixos/download.html">NixOS
homepage</link>. These can be burned onto a CD. It is also possible
to copy them onto a USB stick and install NixOS from there. For
details, see the <link
xlink:href="https://nixos.org/wiki/Installing_NixOS_from_a_USB_stick">NixOS
Wiki</link>.</para>
<para>As an alternative to installing NixOS yourself, you can get a
running NixOS system through several other means:
<itemizedlist>
<listitem>
<para>Using virtual appliances in Open Virtualization Format (OVF)
that can be imported into VirtualBox. These are available from
the <link xlink:href="http://nixos.org/nixos/download.html">NixOS
homepage</link>.</para>
</listitem>
<listitem>
<para>Using AMIs for Amazons EC2. To find one for your region
and instance type, please refer to the <link
xlink:href="https://github.com/NixOS/nixops/blob/master/nix/ec2-amis.nix">list
of most recent AMIs</link>.</para>
</listitem>
<listitem>
<para>Using NixOps, the NixOS-based cloud deployment tool, which
allows you to provision VirtualBox and EC2 NixOS instances from
declarative specifications. Check out the <link
xlink:href="https://github.com/NixOS/nixops">NixOps
homepage</link> for details.</para>
</listitem>
</itemizedlist>
</para>
</section>
<!--===============================================================-->
<section>
<title>Installation</title>
<orderedlist>
<listitem><para>Boot from the CD.</para></listitem>
<listitem><para>The CD contains a basic NixOS installation. (It
also contains Memtest86+, useful if you want to test new hardware.)
When its finished booting, it should have detected most of your
hardware and brought up networking (check
<command>ifconfig</command>). Networking is necessary for the
installer, since it will download lots of stuff (such as source
tarballs or Nixpkgs channel binaries). Its best if you have a DHCP
server on your network. Otherwise configure networking manually
using <command>ifconfig</command>.</para></listitem>
<listitem><para>The NixOS manual is available on virtual console 8
(press Alt+F8 to access).</para></listitem>
<listitem><para>Login as <literal>root</literal> and the empty
password.</para></listitem>
<listitem><para>If you downloaded the graphical ISO image, you can
run <command>start display-manager</command> to start KDE.</para></listitem>
<listitem><para>The NixOS installer doesnt do any partitioning or
formatting yet, so you need to that yourself. Use the following
commands:
<itemizedlist>
<listitem><para>For partitioning:
<command>fdisk</command>.</para></listitem>
<listitem><para>For initialising Ext4 partitions:
<command>mkfs.ext4</command>. It is recommended that you assign a
unique symbolic label to the file system using the option
<option>-L <replaceable>label</replaceable></option>, since this
makes the file system configuration independent from device
changes. For example:
<screen>
$ mkfs.ext4 -L nixos /dev/sda1</screen>
</para></listitem>
<listitem><para>For creating swap partitions:
<command>mkswap</command>. Again its recommended to assign a
label to the swap partition: <option>-L
<replaceable>label</replaceable></option>.</para></listitem>
<listitem><para>For creating LVM volumes, the LVM commands, e.g.,
<screen>
$ pvcreate /dev/sda1 /dev/sdb1
$ vgcreate MyVolGroup /dev/sda1 /dev/sdb1
$ lvcreate --size 2G --name bigdisk MyVolGroup
$ lvcreate --size 1G --name smalldisk MyVolGroup</screen>
</para></listitem>
<listitem><para>For creating software RAID devices, use
<command>mdadm</command>.</para></listitem>
</itemizedlist>
</para></listitem>
<listitem><para>Mount the target file system on which NixOS should
be installed on <filename>/mnt</filename>, e.g.
<screen>
$ mount /dev/disk/by-label/nixos /mnt
</screen>
</para></listitem>
<listitem><para>If your machine has a limited amount of memory, you
may want to activate swap devices now (<command>swapon
<replaceable>device</replaceable></command>). The installer (or
rather, the build actions that it may spawn) may need quite a bit of
RAM, depending on your configuration.</para></listitem>
<listitem>
<para>You now need to create a file
<filename>/mnt/etc/nixos/configuration.nix</filename> that
specifies the intended configuration of the system. This is
because NixOS has a <emphasis>declarative</emphasis> configuration
model: you create or edit a description of the desired
configuration of your system, and then NixOS takes care of making
it happen. The syntax of the NixOS configuration file is
described in <xref linkend="sec-configuration-syntax"/>, while a
list of available configuration options appears in <xref
linkend="ch-options"/>. A minimal example is shown in <xref
linkend="ex-config"/>.</para>
<para>The command <command>nixos-generate-config</command> can
generate an initial configuration file for you:
<screen>
$ nixos-generate-config --root /mnt</screen>
You should then edit
<filename>/mnt/etc/nixos/configuration.nix</filename> to suit your
needs:
<screen>
$ nano /mnt/etc/nixos/configuration.nix
</screen>
The <command>vim</command> text editor is also available.</para>
<para>You <emphasis>must</emphasis> set the option
<option>boot.loader.grub.device</option> to specify on which disk
the GRUB boot loader is to be installed. Without it, NixOS cannot
boot.</para>
<para>Another critical option is <option>fileSystems</option>,
specifying the file systems that need to be mounted by NixOS.
However, you typically dont need to set it yourself, because
<command>nixos-generate-config</command> sets it automatically in
<filename>/mnt/etc/nixos/hardware-configuration.nix</filename>
from your currently mounted file systems. (The configuration file
<filename>hardware-configuration.nix</filename> is included from
<filename>configuration.nix</filename> and will be overwritten by
future invocations of <command>nixos-generate-config</command>;
thus, you generally should not modify it.)</para>
<note><para>Depending on your hardware configuration or type of
file system, you may need to set the option
<option>boot.initrd.kernelModules</option> to include the kernel
modules that are necessary for mounting the root file system,
otherwise the installed system will not be able to boot. (If this
happens, boot from the CD again, mount the target file system on
<filename>/mnt</filename>, fix
<filename>/mnt/etc/nixos/configuration.nix</filename> and rerun
<filename>nixos-install</filename>.) In most cases,
<command>nixos-generate-config</command> will figure out the
required modules.</para></note>
<para>Examples of real-world NixOS configuration files can be
found at <link
xlink:href="https://nixos.org/repos/nix/configurations/trunk/"/>.</para>
</listitem>
<listitem><para>Do the installation:
<screen>
$ nixos-install</screen>
Cross fingers. If this fails due to a temporary problem (such as
a network issue while downloading binaries from the NixOS binary
cache), you can just re-run <command>nixos-install</command>.
Otherwise, fix your <filename>configuration.nix</filename> and
then re-run <command>nixos-install</command>.</para></listitem>
<listitem><para>If everything went well:
<screen>
$ reboot</screen>
</para></listitem>
<listitem>
<para>You should now be able to boot into the installed NixOS.
The GRUB boot menu shows a list of <emphasis>available
configurations</emphasis> (initially just one). Every time you
change the NixOS configuration (see <xref
linkend="sec-changing-config" />), a new item appears in the menu.
This allows you to easily roll back to another configuration if
something goes wrong.</para>
<para>You should log in and change the <literal>root</literal>
password with <command>passwd</command>.</para>
<para>Youll probably want to create some user accounts as well,
which can be done with <command>useradd</command>:
<screen>
$ useradd -c 'Eelco Dolstra' -m eelco
$ passwd eelco</screen>
</para>
<para>You may also want to install some software. For instance,
<screen>
$ nix-env -qa \*</screen>
shows what packages are available, and
<screen>
$ nix-env -i w3m</screen>
install the <literal>w3m</literal> browser.</para>
</listitem>
</orderedlist>
<para>To summarise, <xref linkend="ex-install-sequence" /> shows a
typical sequence of commands for installing NixOS on an empty hard
drive (here <filename>/dev/sda</filename>). <xref linkend="ex-config"
/> shows a corresponding configuration Nix expression.</para>
<example xml:id='ex-install-sequence'><title>Commands for installing NixOS on <filename>/dev/sda</filename></title>
<screen>
$ fdisk /dev/sda # <lineannotation>(or whatever device you want to install on)</lineannotation>
$ mkfs.ext4 -L nixos /dev/sda1
$ mkswap -L swap /dev/sda2
$ swapon /dev/sda2
$ mount /dev/disk/by-label/nixos /mnt
$ nixos-generate-config --root /mnt
$ nano /mnt/etc/nixos/configuration.nix
$ nixos-install
$ reboot</screen>
</example>
<example xml:id='ex-config'><title>NixOS configuration</title>
<screen>
{ config, pkgs, ... }:
{
imports =
[ # Include the results of the hardware scan.
./hardware-configuration.nix
];
boot.loader.grub.device = "/dev/sda";
# Note: setting fileSystems is generally not
# necessary, since nixos-generate-config figures them out
# automatically in hardware-configuration.nix.
#fileSystems."/".device = "/dev/disk/by-label/nixos";
# Enable the OpenSSH server.
services.sshd.enable = true;
}</screen>
</example>
</section>
<!--===============================================================-->
<section xml:id="sec-changing-config">
<title>Changing the configuration</title>
<para>The file <filename>/etc/nixos/configuration.nix</filename>
contains the current configuration of your machine. Whenever youve
changed something to that file, you should do
<screen>
$ nixos-rebuild switch</screen>
to build the new configuration, make it the default configuration for
booting, and try to realise the configuration in the running system
(e.g., by restarting system services).</para>
<warning><para>These commands must be executed as root, so you should
either run them from a root shell or by prefixing them with
<literal>sudo -i</literal>.</para></warning>
<para>You can also do
<screen>
$ nixos-rebuild test</screen>
to build the configuration and switch the running system to it, but
without making it the boot default. So if (say) the configuration
locks up your machine, you can just reboot to get back to a working
configuration.</para>
<para>There is also
<screen>
$ nixos-rebuild boot</screen>
to build the configuration and make it the boot default, but not
switch to it now (so it will only take effect after the next
reboot).</para>
<para>You can make your configuration show up in a different submenu
of the GRUB 2 boot screen by giving it a different <emphasis>profile
name</emphasis>, e.g.
<screen>
$ nixos-rebuild switch -p test </screen>
which causes the new configuration (and previous ones created using
<literal>-p test</literal>) to show up in the GRUB submenu “NixOS -
Profile 'test'”. This can be useful to separate test configurations
from “stable” configurations.</para>
<para>Finally, you can do
<screen>
$ nixos-rebuild build</screen>
to build the configuration but nothing more. This is useful to see
whether everything compiles cleanly.</para>
<para>If you have a machine that supports hardware virtualisation, you
can also test the new configuration in a sandbox by building and
running a QEMU <emphasis>virtual machine</emphasis> that contains the
desired configuration. Just do
<screen>
$ nixos-rebuild build-vm
$ ./result/bin/run-*-vm
</screen>
The VM does not have use any data from your host system, so your
existing user accounts and home directories will not be
available.</para>
</section>
<!--===============================================================-->
<section xml:id="sec-upgrading">
<title>Upgrading NixOS</title>
<para>The best way to keep your NixOS installation up to date is to
use one of the NixOS <emphasis>channels</emphasis>. A channel is a
Nix mechanism for distributing Nix expressions and associated
binaries. The NixOS channels are updated automatically from NixOSs
Git repository after certain tests have passed and all packages have
been built. These channels are:
<itemizedlist>
<listitem>
<para>Stable channels, such as <literal
xlink:href="http://nixos.org/channels/nixos-13.10">nixos-13.10</literal>.
These only get conservative bug fixes and package upgrades. For
instance, a channel update may cause the Linux kernel on your
system to be upgraded from 3.4.66 to 3.4.67 (a minor bug fix), but
not from 3.4.<replaceable>x</replaceable> to
3.11.<replaceable>x</replaceable> (a major change that has the
potential to break things). Stable channels are generally
maintained until the next stable branch is created.</para>
</listitem>
<listitem>
<para>The unstable channel, <literal
xlink:href="http://nixos.org/channels/nixos-unstable">nixos-unstable</literal>.
This corresponds to NixOSs main development branch, and may thus
see radical changes between channel updates. Its not recommended
for production systems.</para>
</listitem>
</itemizedlist>
To see what channels are available, go to <link
xlink:href="http://nixos.org/channels"/>. (Note that the URIs of the
various channels redirect to a directory that contains the channels
latest version and includes ISO images and VirtualBox
appliances.)</para>
<para>When you first install NixOS, youre automatically subscribed to
the NixOS channel that corresponds to your installation source. For
instance, if you installed from a 13.10 ISO, you will be subscribed to
the <literal>nixos-13.10</literal> channel. To see which NixOS
channel youre subscribed to, run the following as root:
<screen>
$ nix-channel --list | grep nixos
nixos https://nixos.org/channels/nixos-unstable
</screen>
To switch to a different NixOS channel, do
<screen>
$ nix-channel --add http://nixos.org/channels/<replaceable>channel-name</replaceable> nixos
</screen>
(Be sure to include the <literal>nixos</literal> parameter at the
end.) For instance, to use the NixOS 13.10 stable channel:
<screen>
$ nix-channel --add http://nixos.org/channels/nixos-13.10 nixos
</screen>
But it you want to live on the bleeding edge:
<screen>
$ nix-channel --add http://nixos.org/channels/nixos-unstable nixos
</screen>
</para>
<para>You can then upgrade NixOS to the latest version in your chosen
channel by running
<screen>
$ nixos-rebuild switch --upgrade
</screen>
which is equivalent to the more verbose <literal>nix-channel --update
nixos; nixos-rebuild switch</literal>.</para>
<warning><para>It is generally safe to switch back and forth between
channels. The only exception is that a newer NixOS may also have a
newer Nix version, which may involve an upgrade of Nixs database
schema. This cannot be undone easily, so in that case you will not be
able to go back to your original channel.</para></warning>
</section>
</chapter>

View File

@ -0,0 +1,38 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle><filename>configuration.nix</filename></refentrytitle>
<manvolnum>5</manvolnum>
<refmiscinfo class="source">NixOS</refmiscinfo>
<!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
</refmeta>
<refnamediv>
<refname><filename>configuration.nix</filename></refname>
<refpurpose>NixOS system configuration specification</refpurpose>
</refnamediv>
<refsection><title>Description</title>
<para>The file <filename>/etc/nixos/configuration.nix</filename>
contains the declarative specification of your NixOS system
configuration. The command <command>nixos-rebuild</command> takes
this file and realises the system configuration specified
therein.</para>
</refsection>
<refsection><title>Options</title>
<para>You can use the following options in
<filename>configuration.nix</filename>.</para>
<xi:include href="options-db.xml" />
</refsection>
</refentry>

View File

@ -0,0 +1,110 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle><command>nixos-build-vms</command></refentrytitle>
<manvolnum>8</manvolnum>
<refmiscinfo class="source">NixOS</refmiscinfo>
<!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
</refmeta>
<refnamediv>
<refname><command>nixos-build-vms</command></refname>
<refpurpose>build a network of virtual machines from a network of NixOS configurations</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>nixos-build-vms</command>
<arg><option>--show-trace</option></arg>
<arg><option>--no-out-link</option></arg>
<arg><option>--help</option></arg>
<arg choice="plain"><replaceable>network.nix</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsection><title>Description</title>
<para>This command builds a network of QEMU-KVM virtual machines of a Nix expression
specifying a network of NixOS machines. The virtual network can be started by
executing the <filename>bin/run-vms</filename> shell script that is generated by
this command. By default, a <filename>result</filename> symlink is produced that
points to the generated virtual network.
</para>
<para>A network Nix expression has the following structure:
<screen>
{
test1 = {pkgs, config, ...}:
{
services.openssh.enable = true;
nixpkgs.system = "i686-linux";
deployment.targetHost = "test1.example.net";
# Other NixOS options
};
test2 = {pkgs, config, ...}:
{
services.openssh.enable = true;
services.httpd.enable = true;
environment.systemPackages = [ pkgs.lynx ];
nixpkgs.system = "x86_64-linux";
deployment.targetHost = "test2.example.net";
# Other NixOS options
};
}
</screen>
Each attribute in the expression represents a machine in the network
(e.g. <varname>test1</varname> and <varname>test2</varname>)
referring to a function defining a NixOS configuration.
In each NixOS configuration, two attributes have a special meaning.
The <varname>deployment.targetHost</varname> specifies the address
(domain name or IP address)
of the system which is used by <command>ssh</command> to perform
remote deployment operations. The <varname>nixpkgs.system</varname>
attribute can be used to specify an architecture for the target machine,
such as <varname>i686-linux</varname> which builds a 32-bit NixOS
configuration. Omitting this property will build the configuration
for the same architecture as the host system.
</para>
</refsection>
<refsection><title>Options</title>
<para>This command accepts the following options:</para>
<variablelist>
<varlistentry>
<term><option>--show-trace</option></term>
<listitem>
<para>Shows a trace of the output.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--no-out-link</option></term>
<listitem>
<para>Do not create a 'result' symlink.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-h</option>, <option>--help</option></term>
<listitem>
<para>Shows the usage of this command to the user.</para>
</listitem>
</varlistentry>
</variablelist>
</refsection>
</refentry>

View File

@ -0,0 +1,208 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle><command>nixos-generate-config</command></refentrytitle>
<manvolnum>8</manvolnum>
<refmiscinfo class="source">NixOS</refmiscinfo>
<!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
</refmeta>
<refnamediv>
<refname><command>nixos-generate-config</command></refname>
<refpurpose>generate NixOS configuration modules</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>nixos-generate-config</command>
<arg><option>--force</option></arg>
<arg>
<arg choice='plain'><option>--root</option></arg>
<replaceable>root</replaceable>
</arg>
<arg>
<arg choice='plain'><option>--dir</option></arg>
<replaceable>dir</replaceable>
</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsection><title>Description</title>
<para>This command writes two NixOS configuration modules:
<variablelist>
<varlistentry>
<term><option>/etc/nixos/hardware-configuration.nix</option></term>
<listitem>
<para>This module sets NixOS configuration options based on your
current hardware configuration. In particular, it sets the
<option>fileSystem</option> option to reflect all currently
mounted file systems, the <option>swapDevices</option> option to
reflect active swap devices, and the
<option>boot.initrd.*</option> options to ensure that the
initial ramdisk contains any kernel modules necessary for
mounting the root file system.</para>
<para>If this file already exists, it is overwritten. Thus, you
should not modify it manually. Rather, you should include it
from your <filename>/etc/nixos/configuration.nix</filename>, and
re-run <command>nixos-generate-config</command> to update it
whenever your hardware configuration changes.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>/etc/nixos/configuration.nix</option></term>
<listitem>
<para>This is the main NixOS system configuration module. If it
already exists, its left unchanged. Otherwise,
<command>nixos-generate-config</command> will write a template
for you to customise.</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsection>
<refsection><title>Options</title>
<para>This command accepts the following options:</para>
<variablelist>
<varlistentry>
<term><option>--root</option></term>
<listitem>
<para>If this option is given, treat the directory
<replaceable>root</replaceable> as the root of the file system.
This means that configuration files will be written to
<filename><replaceable>root</replaceable>/etc/nixos</filename>,
and that any file systems outside of
<replaceable>root</replaceable> are ignored for the purpose of
generating the <option>fileSystems</option> option.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--dir</option></term>
<listitem>
<para>If this option is given, write the configuration files to
the directory <replaceable>dir</replaceable> instead of
<filename>/etc/nixos</filename>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--force</option></term>
<listitem>
<para>Overwrite
<filename>/etc/nixos/configuration.nix</filename> if it already
exists.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--no-filesystems</option></term>
<listitem>
<para>Omit everything concerning file system information
(which includes swap devices) from the hardware configuration.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--show-hardware-config</option></term>
<listitem>
<para>Don't generate <filename>configuration.nix</filename> or
<filename>hardware-configuration.nix</filename> and print the
hardware configuration to stdout only.</para>
</listitem>
</varlistentry>
</variablelist>
</refsection>
<refsection><title>Examples</title>
<para>This command is typically used during NixOS installation to
write initial configuration modules. For example, if you created and
mounted the target file systems on <filename>/mnt</filename> and
<filename>/mnt/boot</filename>, you would run:
<screen>
$ nixos-generate-config --root /mnt
</screen>
The resulting file
<filename>/mnt/etc/nixos/hardware-configuration.nix</filename> might
look like this:
<programlisting>
# Do not modify this file! It was generated by nixos-generate-config
# and may be overwritten by future invocations. Please make changes
# to /etc/nixos/configuration.nix instead.
{ config, pkgs, ... }:
{
imports =
[ &lt;nixos/modules/installer/scan/not-detected.nix>
];
boot.initrd.availableKernelModules = [ "ehci_hcd" "ahci" ];
boot.kernelModules = [ "kvm-intel" ];
boot.extraModulePackages = [ ];
fileSystems."/" =
{ device = "/dev/disk/by-label/nixos";
fsType = "ext3";
options = "rw,data=ordered,relatime";
};
fileSystems."/boot" =
{ device = "/dev/sda1";
fsType = "ext3";
options = "rw,errors=continue,user_xattr,acl,barrier=1,data=writeback,relatime";
};
swapDevices =
[ { device = "/dev/sda2"; }
];
nix.maxJobs = 8;
}
</programlisting>
It will also create a basic
<filename>/mnt/etc/nixos/configuration.nix</filename>, which you
should edit to customise the logical configuration of your system.
This file includes the result of the hardware scan as follows:
<programlisting>
imports = [ ./hardware-configuration.nix ];
</programlisting>
</para>
<para>After installation, if your hardware configuration changes, you
can run:
<screen>
$ nixos-generate-config
</screen>
to update <filename>/etc/nixos/hardware-configuration.nix</filename>.
Your <filename>/etc/nixos/configuration.nix</filename> will
<emphasis>not</emphasis> be overwritten.</para>
</refsection>
</refentry>

View File

@ -0,0 +1,78 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle><command>nixos-install</command></refentrytitle>
<manvolnum>8</manvolnum>
<refmiscinfo class="source">NixOS</refmiscinfo>
<!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
</refmeta>
<refnamediv>
<refname><command>nixos-install</command></refname>
<refpurpose>install NixOS</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>nixos-install</command>
</cmdsynopsis>
</refsynopsisdiv>
<refsection><title>Description</title>
<para>This command installs NixOS in the file system mounted on
<filename>/mnt</filename>, based on the NixOS configuration specified
in <filename>/mnt/etc/nixos/configuration.nix</filename>. It performs
the following steps:
<itemizedlist>
<listitem><para>It copies Nix and its dependencies to
<filename>/mnt/nix/store</filename>.</para></listitem>
<listitem><para>It runs Nix in <filename>/mnt</filename> to build
the NixOS configuration specified in
<filename>/mnt/etc/nixos/configuration.nix</filename>.</para></listitem>
<listitem><para>It installs the GRUB boot loader on the device
specified in the option <option>boot.loader.grub.device</option>,
and generates a GRUB configuration file that boots into the NixOS
configuration just installed.</para></listitem>
</itemizedlist>
</para>
<para>This command is idempotent: if it is interrupted or fails due to
a temporary problem (e.g. a network issue), you can safely re-run
it.</para>
</refsection>
<refsection><title>Examples</title>
<para>A typical NixOS installation is done by creating and mounting a
file system on <filename>/mnt</filename>, generating a NixOS
configuration in
<filename>/mnt/etc/nixos/configuration.nix</filename>, and running
<command>nixos-install</command>. For instance, if we want to install
NixOS on an <literal>ext4</literal> file system created in
<filename>/dev/sda1</filename>:
<screen>
$ mkfs.ext4 /dev/sda1
$ mount /dev/sda1 /mnt
$ nixos-generate-config --root /mnt
$ # edit /mnt/etc/nixos/configuration.nix
$ nixos-install
</screen>
</para>
</refsection>
</refentry>

View File

@ -0,0 +1,138 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle><command>nixos-option</command></refentrytitle>
<manvolnum>8</manvolnum>
<refmiscinfo class="source">NixOS</refmiscinfo>
<!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
</refmeta>
<refnamediv>
<refname><command>nixos-option</command></refname>
<refpurpose>inspect a NixOS configuration</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>nixos-option</command>
<group choice="opt">
<option>-v</option>
<option>-d</option>
<option>-l</option>
</group>
<arg choice='plain'><replaceable>option.name</replaceable></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsection><title>Description</title>
<para>This command evaluates the configuration specified in
<filename>/etc/nixos/configuration.nix</filename> and returns the properties
of the option name given as argument. By default, it returns the value of
the option.</para>
<para>When the option name is not an option, the command prints the list of
attributes contained in the attribute set.</para>
</refsection>
<refsection><title>Options</title>
<para>This command accepts the following options:</para>
<variablelist>
<varlistentry>
<term><option>--value</option>, <option>-v</option></term>
<listitem>
<para>Returns the value of the option. This is the default operation
if no other options are defined.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--description</option>, <option>-d</option></term>
<listitem>
<para>Return the default value, the example and the description of the
option when available.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--lookup</option>, <option>-l</option></term>
<listitem>
<para>Return the locations where the option is declared and where it
is defined. This is extremely useful to find sources of errors in
your configuration.</para>
</listitem>
</varlistentry>
</variablelist>
</refsection>
<refsection><title>Environment</title>
<variablelist>
<varlistentry>
<term><envar>NIXOS_CONFIG</envar></term>
<listitem>
<para>Path to the main NixOS configuration module. Defaults to
<filename>/etc/nixos/configuration.nix</filename>.</para>
</listitem>
</varlistentry>
</variablelist>
</refsection>
<refsection><title>Examples</title>
<para>Investigate option values:
<screen>$ nixos-option boot.loader
This attribute set contains:
generationsDir
grub
initScript
$ nixos-option boot.loader.grub.enable
true</screen></para>
<para>Prints option information:
<screen>$ nixos-option -d networking.hostName
Default: "nixos"
Description:
The name of the machine. Leave it empty if you want to obtain
it from a DHCP server (if using DHCP).</screen></para>
<para>Find the locations which are declaring and defining an option:
<screen>$ nixos-option -l hardware.firmware
Declared by:
/mnt/data/nix-sources/nixos/modules/services/hardware/udev.nix
Defined by:
/path/to/nixpkgs/nixos/modules/system/boot/kernel.nix
/path/to/nixpkgs/nixos/modules/hardware/network/rt73.nix
/path/to/nixpkgs/nixos/modules/hardware/network/intel-3945abg.nix
/path/to/nixpkgs/nixos/modules/hardware/network/intel-2200bg.nix</screen></para>
</refsection>
<refsection><title>Bugs</title>
<para>The author listed in the following section is wrong. If there is any
other bug, please report to Nicolas Pierron.</para>
</refsection>
</refentry>

View File

@ -0,0 +1,335 @@
<refentry xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude">
<refmeta>
<refentrytitle><command>nixos-rebuild</command></refentrytitle>
<manvolnum>8</manvolnum>
<refmiscinfo class="source">NixOS</refmiscinfo>
<!-- <refmiscinfo class="version"><xi:include href="version.txt" parse="text"/></refmiscinfo> -->
</refmeta>
<refnamediv>
<refname><command>nixos-rebuild</command></refname>
<refpurpose>reconfigure a NixOS machine</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>nixos-rebuild</command>
<group choice='req'>
<arg choice='plain'><option>switch</option></arg>
<arg choice='plain'><option>boot</option></arg>
<arg choice='plain'><option>test</option></arg>
<arg choice='plain'><option>build</option></arg>
<arg choice='plain'><option>dry-run</option></arg>
<arg choice='plain'><option>build-vm</option></arg>
<arg choice='plain'><option>build-vm-with-bootloader</option></arg>
</group>
<sbr />
<arg><option>--upgrade</option></arg>
<arg><option>--install-grub</option></arg>
<arg><option>--no-build-nix</option></arg>
<arg><option>--fast</option></arg>
<arg><option>--rollback</option></arg>
<sbr />
<arg>
<group choice='req'>
<arg choice='plain'><option>--profile-name</option></arg>
<arg choice='plain'><option>-p</option></arg>
</group>
<replaceable>name</replaceable>
</arg>
<sbr />
<arg><option>--show-trace</option></arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsection><title>Description</title>
<para>This command updates the system so that it corresponds to the
configuration specified in
<filename>/etc/nixos/configuration.nix</filename>. Thus, every time
you modify <filename>/etc/nixos/configuration.nix</filename> or any
NixOS module, you must run <command>nixos-rebuild</command> to make
the changes take effect. It builds the new system in
<filename>/nix/store</filename>, runs its activation script, and stop
and (re)starts any system services if needed.</para>
<para>This command has one required argument, which specifies the
desired operation. It must be one of the following:
<variablelist>
<varlistentry>
<term><option>switch</option></term>
<listitem>
<para>Build and activate the new configuration, and make it the
boot default. That is, the configuration is added to the GRUB
boot menu as the default meny entry, so that subsequent reboots
will boot the system into the new configuration. Previous
configurations activated with <command>nixos-rebuild
switch</command> or <command>nixos-rebuild boot</command> remain
available in the GRUB menu.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>boot</option></term>
<listitem>
<para>Build the new configuration and make it the boot default
(as with <command>nixos-rebuild switch</command>), but do not
activate it. That is, the system continues to run the previous
configuration until the next reboot.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>test</option></term>
<listitem>
<para>Build and activate the new configuration, but do not add
it to the GRUB boot menu. Thus, if you reboot the system (or if
it crashes), you will automatically revert to the default
configuration (i.e. the configuration resulting from the last
call to <command>nixos-rebuild switch</command> or
<command>nixos-rebuild boot</command>).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>build</option></term>
<listitem>
<para>Build the new configuration, but neither activate it nor
add it to the GRUB boot menu. It leaves a symlink named
<filename>result</filename> in the current directory, which
points to the output of the top-level “system” derivation. This
is essentially the same as doing
<screen>
$ nix-build /path/to/nixpkgs/nixos -A system
</screen>
Note that you do not need to be <literal>root</literal> to run
<command>nixos-rebuild build</command>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>dry-run</option></term>
<listitem>
<para>Simply show what store paths would be built or downloaded
by any of the operations above.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>build-vm</option></term>
<listitem>
<para>Build a script that starts a NixOS virtual machine with
the desired configuration. It leaves a symlink
<filename>result</filename> in the current directory that points
(under
<filename>result/bin/run-<replaceable>hostname</replaceable>-vm</filename>)
at the script that starts the VM. Thus, to test a NixOS
configuration in a virtual machine, you should do the following:
<screen>
$ nixos-rebuild build-vm
$ ./result/bin/run-*-vm
</screen></para>
<para>The VM is implemented using the <literal>qemu</literal>
package. For best performance, you should load the
<literal>kvm-intel</literal> or <literal>kvm-amd</literal>
kernel modules to get hardware virtualisation.</para>
<para>The VM mounts the Nix store of the host through the 9P
file system. The host Nix store is read-only, so Nix commands
that modify the Nix store will not work in the VM. This
includes commands such as <command>nixos-rebuild</command>; to
change the VMs configuration, you must halt the VM and re-run
the commands above.
</para>
<para>The VM has its own <literal>ext3</literal> root file
system, which is automatically created when the VM is first
started, and is persistent across reboots of the VM. It is
stored in
<literal>./<replaceable>hostname</replaceable>.qcow2</literal>.
<!-- The entire file system hierarchy of the host is available in
the VM under <filename>/hostfs</filename>.--></para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>build-vm-with-bootloader</option></term>
<listitem>
<para>Like <option>build-vm</option>, but boots using the
regular boot loader of your configuration (e.g., GRUB 1 or 2),
rather than booting directly into the kernel and initial ramdisk
of the system. This allows you to test whether the boot loader
works correctly. However, it does not guarantee that your NixOS
configuration will boot successfully on the host hardware (i.e.,
after running <command>nixos-rebuild switch</command>), because
the hardware and boot loader configuration in the VM are
different. The boot loader is installed on an automatically
generated virtual disk containing a <filename>/boot</filename>
partition, which is mounted read-only in the VM.</para>
</listitem>
</varlistentry>
</variablelist>
</para>
</refsection>
<refsection><title>Options</title>
<para>This command accepts the following options:</para>
<variablelist>
<varlistentry>
<term><option>--upgrade</option></term>
<listitem>
<para>Fetch the latest version of NixOS from the NixOS
channel.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--install-grub</option></term>
<listitem>
<para>Causes the GRUB boot loader to be (re)installed on the
device specified by the
<varname>boot.loader.grub.device</varname> configuration
option.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--no-build-nix</option></term>
<listitem>
<para>Normally, <command>nixos-rebuild</command> first builds
the <varname>nixUnstable</varname> attribute in Nixpkgs, and
uses the resulting instance of the Nix package manager to build
the new system configuration. This is necessary if the NixOS
modules use features not provided by the currently installed
version of Nix. This option disables building a new Nix.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--fast</option></term>
<listitem>
<para>Equivalent to <option>--no-build-nix</option>
<option>--show-trace</option>. This option is useful if you
call <command>nixos-rebuild</command> frequently (e.g. if youre
hacking on a NixOS module).</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--rollback</option></term>
<listitem>
<para>Instead of building a new configuration as specified by
<filename>/etc/nixos/configuration.nix</filename>, roll back to
the previous configuration. (The previous configuration is
defined as the one before the “current” generation of the
Nix profile <filename>/nix/var/nix/profiles/system</filename>.)</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--profile-name</option></term>
<term><option>-p</option></term>
<listitem>
<para>Instead of using the Nix profile
<filename>/nix/var/nix/profiles/system</filename> to keep track
of the current and previous system configurations, use
<filename>/nix/var/nix/profiles/system-profiles/<replaceable>name</replaceable></filename>.
When you use GRUB 2, for every system profile created with this
flag, NixOS will create a submenu named “NixOS - Profile
'<replaceable>name</replaceable>'” in GRUBs boot menu,
containing the current and previous configurations of this
profile.</para>
<para>For instance, if you want to test a configuration file
named <filename>test.nix</filename> without affecting the
default system profile, you would do:
<screen>
$ nixos-rebuild switch -p test -I nixos-config=./test.nix
</screen>
The new configuration will appear in the GRUB 2 submenu “NixOS - Profile
'test'”.</para>
</listitem>
</varlistentry>
</variablelist>
<para>In addition, <command>nixos-rebuild</command> accepts various
Nix-related flags, including <option>--max-jobs</option> /
<option>-j</option>, <option>--show-trace</option>,
<option>--keep-failed</option>, <option>--keep-going</option> and
<option>--verbose</option> / <option>-v</option>. See
the Nix manual for details.</para>
</refsection>
<refsection><title>Environment</title>
<variablelist>
<varlistentry>
<term><envar>NIXOS_CONFIG</envar></term>
<listitem>
<para>Path to the main NixOS configuration module. Defaults to
<filename>/etc/nixos/configuration.nix</filename>.</para>
</listitem>
</varlistentry>
</variablelist>
</refsection>
<refsection><title>Files</title>
<variablelist>
<varlistentry>
<term><filename>/run/current-system</filename></term>
<listitem>
<para>A symlink to the currently active system configuration in
the Nix store.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><filename>/nix/var/nix/profiles/system</filename></term>
<listitem>
<para>The Nix profile that contains the current and previous
system configurations. Used to generate the GRUB boot
menu.</para>
</listitem>
</varlistentry>
</variablelist>
</refsection>
<refsection><title>Bugs</title>
<para>This command should be renamed to something more
descriptive.</para>
</refsection>
</refentry>

View File

@ -0,0 +1,31 @@
<reference xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude">
<title>NixOS Reference Pages</title>
<info>
<author>
<personname>
<firstname>Eelco</firstname>
<surname>Dolstra</surname>
</personname>
<contrib>Author</contrib>
</author>
<copyright>
<year>2007-2013</year>
<holder>Eelco Dolstra</holder>
</copyright>
</info>
<xi:include href="man-configuration.xml" />
<xi:include href="man-nixos-build-vms.xml" />
<xi:include href="man-nixos-generate-config.xml" />
<xi:include href="man-nixos-install.xml" />
<xi:include href="man-nixos-option.xml" />
<xi:include href="man-nixos-rebuild.xml" />
</reference>

View File

@ -0,0 +1,62 @@
<book xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xi="http://www.w3.org/2001/XInclude">
<info>
<title>NixOS Manual</title>
<author>
<personname>
<firstname>Eelco</firstname>
<surname>Dolstra</surname>
</personname>
</author>
<author>
<personname>
<firstname>Nicolas</firstname>
<surname>Pierron</surname>
</personname>
</author>
<copyright>
<year>2007-2013</year>
<holder>Eelco Dolstra</holder>
</copyright>
</info>
<preface>
<title>Preface</title>
<para>This manual describes how to install, use and extend NixOS,
a Linux distribution based on the purely functional package
management system Nix.</para>
<para>If you encounter problems, please report them on the
<literal
xlink:href="http://lists.science.uu.nl/mailman/listinfo/nix-dev">nix-dev@lists.science.uu.nl</literal>
mailing list or on the <link
xlink:href="irc://irc.freenode.net/#nixos">
<literal>#nixos</literal> channel on Freenode</link>. Bugs should
be reported in <link
xlink:href="https://github.com/NixOS/nixpkgs/issues">NixOS GitHub
issue tracker</link>.</para>
</preface>
<xi:include href="installation.xml" />
<xi:include href="configuration.xml" />
<xi:include href="running.xml" />
<!-- <xi:include href="userconfiguration.xml" /> -->
<xi:include href="troubleshooting.xml" />
<xi:include href="development.xml" />
<chapter xml:id="ch-options">
<title>List of options</title>
<xi:include href="options-db.xml" />
</chapter>
</book>

View File

@ -0,0 +1,207 @@
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://docbook.org/ns/docbook"
extension-element-prefixes="str"
>
<xsl:output method='xml' encoding="UTF-8" />
<xsl:param name="revision" />
<xsl:template match="/expr/list">
<variablelist>
<xsl:for-each select="attrs">
<varlistentry>
<term>
<option>
<xsl:for-each select="attr[@name = 'name']/string">
<xsl:value-of select="@value" />
<xsl:if test="position() != last()">.</xsl:if>
</xsl:for-each>
</option>
</term>
<listitem>
<para>
<xsl:value-of disable-output-escaping="yes"
select="attr[@name = 'description']/string/@value" />
</para>
<xsl:if test="attr[@name = 'default']">
<para>
<emphasis>Default:</emphasis>
<xsl:text> </xsl:text>
<xsl:apply-templates select="attr[@name = 'default']" mode="top" />
</para>
</xsl:if>
<xsl:if test="attr[@name = 'example']">
<para>
<emphasis>Example:</emphasis>
<xsl:text> </xsl:text>
<xsl:choose>
<xsl:when test="attr[@name = 'example']/attrs[attr[@name = '_type' and string[@value = 'literalExample']]]">
<programlisting><xsl:value-of select="attr[@name = 'example']/attrs/attr[@name = 'text']/string/@value" /></programlisting>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="attr[@name = 'example']" mode="top" />
</xsl:otherwise>
</xsl:choose>
</para>
</xsl:if>
<xsl:if test="count(attr[@name = 'declarations']/list/*) != 0">
<para>
<emphasis>Declared by:</emphasis>
</para>
<xsl:apply-templates select="attr[@name = 'declarations']" />
</xsl:if>
<xsl:if test="count(attr[@name = 'definitions']/list/*) != 0">
<para>
<emphasis>Defined by:</emphasis>
</para>
<xsl:apply-templates select="attr[@name = 'definitions']" />
</xsl:if>
</listitem>
</varlistentry>
</xsl:for-each>
</variablelist>
</xsl:template>
<xsl:template match="*" mode="top">
<xsl:choose>
<xsl:when test="string[contains(@value, '&#010;')]">
<programlisting>
<xsl:text>''
</xsl:text><xsl:value-of select='str:replace(string/@value, "${", "&apos;&apos;${")' /><xsl:text>''</xsl:text></programlisting>
</xsl:when>
<xsl:otherwise>
<literal><xsl:apply-templates /></literal>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="null">
<xsl:text>null</xsl:text>
</xsl:template>
<xsl:template match="string">
<xsl:choose>
<xsl:when test="(contains(@value, '&quot;') or contains(@value, '\')) and not(contains(@value, '&#010;'))">
<xsl:text>''</xsl:text><xsl:value-of select='str:replace(@value, "${", "&apos;&apos;${")' /><xsl:text>''</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text>"</xsl:text><xsl:value-of select="str:replace(str:replace(str:replace(str:replace(@value, '\', '\\'), '&quot;', '\&quot;'), '&#010;', '\n'), '$', '\$')" /><xsl:text>"</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="int">
<xsl:value-of select="@value" />
</xsl:template>
<xsl:template match="bool[@value = 'true']">
<xsl:text>true</xsl:text>
</xsl:template>
<xsl:template match="bool[@value = 'false']">
<xsl:text>false</xsl:text>
</xsl:template>
<xsl:template match="list">
[
<xsl:for-each select="*">
<xsl:apply-templates select="." />
<xsl:text> </xsl:text>
</xsl:for-each>
]
</xsl:template>
<xsl:template match="attrs[attr[@name = '_type' and string[@value = 'literalExample']]]">
<xsl:value-of select="attr[@name = 'text']/string/@value" />
</xsl:template>
<xsl:template match="attrs">
{
<xsl:for-each select="attr">
<xsl:value-of select="@name" />
<xsl:text> = </xsl:text>
<xsl:apply-templates select="*" /><xsl:text>; </xsl:text>
</xsl:for-each>
}
</xsl:template>
<xsl:template match="derivation">
<replaceable>(build of <xsl:value-of select="attr[@name = 'name']/string/@value" />)</replaceable>
</xsl:template>
<xsl:template match="attr[@name = 'declarations' or @name = 'definitions']">
<simplelist>
<xsl:for-each select="list/string">
<member><filename>
<!-- Hyperlink the filename either to the NixOS Subversion
repository (if its a module and we have a revision number),
or to the local filesystem. -->
<xsl:choose>
<xsl:when test="not(starts-with(@value, '/'))">
<xsl:choose>
<xsl:when test="$revision = 'local'">
<xsl:attribute name="xlink:href">https://github.com/NixOS/nixpkgs/blob/master/<xsl:value-of select="@value"/></xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="xlink:href">https://github.com/NixOS/nixpkgs/blob/<xsl:value-of select="$revision"/>/<xsl:value-of select="@value"/></xsl:attribute>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:when test="$revision != 'local' and contains(@value, 'nixops') and contains(@value, '/nix/')">
<xsl:attribute name="xlink:href">https://github.com/NixOS/nixops/blob/<xsl:value-of select="$revision"/>/nix/<xsl:value-of select="substring-after(@value, '/nix/')"/></xsl:attribute>
</xsl:when>
<xsl:otherwise>
<xsl:attribute name="xlink:href">file://<xsl:value-of select="@value"/></xsl:attribute>
</xsl:otherwise>
</xsl:choose>
<!-- Print the filename and make it user-friendly by replacing the
/nix/store/<hash> prefix by the default location of nixos
sources. -->
<xsl:choose>
<xsl:when test="not(starts-with(@value, '/'))">
&lt;nixpkgs/<xsl:value-of select="@value"/>&gt;
</xsl:when>
<xsl:when test="contains(@value, 'nixops') and contains(@value, '/nix/')">
&lt;nixops/<xsl:value-of select="substring-after(@value, '/nix/')"/>&gt;
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="@value" />
</xsl:otherwise>
</xsl:choose>
</filename></member>
</xsl:for-each>
</simplelist>
</xsl:template>
</xsl:stylesheet>

View File

@ -0,0 +1,369 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xml:id="ch-running">
<title>Running NixOS</title>
<para>This chapter describes various aspects of managing a running
NixOS system, such as how to use the <command>systemd</command>
service manager.</para>
<!--===============================================================-->
<section><title>Service management</title>
<para>In NixOS, all system services are started and monitored using
the systemd program. Systemd is the “init” process of the system
(i.e. PID 1), the parent of all other processes. It manages a set of
so-called “units”, which can be things like system services
(programs), but also mount points, swap files, devices, targets
(groups of units) and more. Units can have complex dependencies; for
instance, one unit can require that another unit must be successfully
started before the first unit can be started. When the system boots,
it starts a unit named <literal>default.target</literal>; the
dependencies of this unit cause all system services to be started,
file systems to be mounted, swap files to be activated, and so
on.</para>
<para>The command <command>systemctl</command> is the main way to
interact with <command>systemd</command>. Without any arguments, it
shows the status of active units:
<screen>
$ systemctl
-.mount loaded active mounted /
swapfile.swap loaded active active /swapfile
sshd.service loaded active running SSH Daemon
graphical.target loaded active active Graphical Interface
<replaceable>...</replaceable>
</screen>
</para>
<para>You can ask for detailed status information about a unit, for
instance, the PostgreSQL database service:
<screen>
$ systemctl status postgresql.service
postgresql.service - PostgreSQL Server
Loaded: loaded (/nix/store/pn3q73mvh75gsrl8w7fdlfk3fq5qm5mw-unit/postgresql.service)
Active: active (running) since Mon, 2013-01-07 15:55:57 CET; 9h ago
Main PID: 2390 (postgres)
CGroup: name=systemd:/system/postgresql.service
├─2390 postgres
├─2418 postgres: writer process
├─2419 postgres: wal writer process
├─2420 postgres: autovacuum launcher process
├─2421 postgres: stats collector process
└─2498 postgres: zabbix zabbix [local] idle
Jan 07 15:55:55 hagbard postgres[2394]: [1-1] LOG: database system was shut down at 2013-01-07 15:55:05 CET
Jan 07 15:55:57 hagbard postgres[2390]: [1-1] LOG: database system is ready to accept connections
Jan 07 15:55:57 hagbard postgres[2420]: [1-1] LOG: autovacuum launcher started
Jan 07 15:55:57 hagbard systemd[1]: Started PostgreSQL Server.
</screen>
Note that this shows the status of the unit (active and running), all
the processes belonging to the service, as well as the most recent log
messages from the service.
</para>
<para>Units can be stopped, started or restarted:
<screen>
$ systemctl stop postgresql.service
$ systemctl start postgresql.service
$ systemctl restart postgresql.service
</screen>
These operations are synchronous: they wait until the service has
finished starting or stopping (or has failed). Starting a unit will
cause the dependencies of that unit to be started as well (if
necessary).</para>
<!-- - cgroups: each service and user session is a cgroup
- cgroup resource management -->
</section>
<!--===============================================================-->
<section><title>Rebooting and shutting down</title>
<para>The system can be shut down (and automatically powered off) by
doing:
<screen>
$ shutdown
</screen>
This is equivalent to running <command>systemctl
poweroff</command>.</para>
<para>To reboot the system, run
<screen>
$ reboot
</screen>
which is equivalent to <command>systemctl reboot</command>.
Alternatively, you can quickly reboot the system using
<literal>kexec</literal>, which bypasses the BIOS by directly loading
the new kernel into memory:
<screen>
$ systemctl kexec
</screen>
</para>
<para>The machine can be suspended to RAM (if supported) using
<command>systemctl suspend</command>, and suspended to disk using
<command>systemctl hibernate</command>.</para>
<para>These commands can be run by any user who is logged in locally,
i.e. on a virtual console or in X11; otherwise, the user is asked for
authentication.</para>
</section>
<!--===============================================================-->
<section><title>User sessions</title>
<para>Systemd keeps track of all users who are logged into the system
(e.g. on a virtual console or remotely via SSH). The command
<command>loginctl</command> allows querying and manipulating user
sessions. For instance, to list all user sessions:
<screen>
$ loginctl
SESSION UID USER SEAT
c1 500 eelco seat0
c3 0 root seat0
c4 500 alice
</screen>
This shows that two users are logged in locally, while another is
logged in remotely. (“Seats” are essentially the combinations of
displays and input devices attached to the system; usually, there is
only one seat.) To get information about a session:
<screen>
$ loginctl session-status c3
c3 - root (0)
Since: Tue, 2013-01-08 01:17:56 CET; 4min 42s ago
Leader: 2536 (login)
Seat: seat0; vc3
TTY: /dev/tty3
Service: login; type tty; class user
State: online
CGroup: name=systemd:/user/root/c3
├─ 2536 /nix/store/10mn4xip9n7y9bxqwnsx7xwx2v2g34xn-shadow-4.1.5.1/bin/login --
├─10339 -bash
└─10355 w3m nixos.org
</screen>
This shows that the user is logged in on virtual console 3. It also
lists the processes belonging to this session. Since systemd keeps
track of this, you can terminate a session in a way that ensures that
all the sessions processes are gone:
<screen>
$ loginctl terminate-session c3
</screen>
</para>
</section>
<!--===============================================================-->
<section><title>Control groups</title>
<para>To keep track of the processes in a running system, systemd uses
<emphasis>control groups</emphasis> (cgroups). A control group is a
set of processes used to allocate resources such as CPU, memory or I/O
bandwidth. There can be multiple control group hierarchies, allowing
each kind of resource to be managed independently.</para>
<para>The command <command>systemd-cgls</command> lists all control
groups in the <literal>systemd</literal> hierarchy, which is what
systemd uses to keep track of the processes belonging to each service
or user session:
<screen>
$ systemd-cgls
├─user
│ └─eelco
│ └─c1
│ ├─ 2567 -:0
│ ├─ 2682 kdeinit4: kdeinit4 Running...
│ ├─ <replaceable>...</replaceable>
│ └─10851 sh -c less -R
└─system
├─httpd.service
│ ├─2444 httpd -f /nix/store/3pyacby5cpr55a03qwbnndizpciwq161-httpd.conf -DNO_DETACH
│ └─<replaceable>...</replaceable>
├─dhcpcd.service
│ └─2376 dhcpcd --config /nix/store/f8dif8dsi2yaa70n03xir8r653776ka6-dhcpcd.conf
└─ <replaceable>...</replaceable>
</screen>
Similarly, <command>systemd-cgls cpu</command> shows the cgroups in
the CPU hierarchy, which allows per-cgroup CPU scheduling priorities.
By default, every systemd service gets its own CPU cgroup, while all
user sessions are in the top-level CPU cgroup. This ensures, for
instance, that a thousand run-away processes in the
<literal>httpd.service</literal> cgroup cannot starve the CPU for one
process in the <literal>postgresql.service</literal> cgroup. (By
contrast, it they were in the same cgroup, then the PostgreSQL process
would get 1/1001 of the cgroups CPU time.) You can limit a services
CPU share in <filename>configuration.nix</filename>:
<programlisting>
systemd.services.httpd.serviceConfig.CPUShares = 512;
</programlisting>
By default, every cgroup has 1024 CPU shares, so this will halve the
CPU allocation of the <literal>httpd.service</literal> cgroup.</para>
<para>There also is a <literal>memory</literal> hierarchy that
controls memory allocation limits; by default, all processes are in
the top-level cgroup, so any service or session can exhaust all
available memory. Per-cgroup memory limits can be specified in
<filename>configuration.nix</filename>; for instance, to limit
<literal>httpd.service</literal> to 512 MiB of RAM (excluding swap)
and 640 MiB of RAM (including swap):
<programlisting>
systemd.services.httpd.serviceConfig.MemoryLimit = "512M";
systemd.services.httpd.serviceConfig.ControlGroupAttribute = [ "memory.memsw.limit_in_bytes 640M" ];
</programlisting>
</para>
<para>The command <command>systemd-cgtop</command> shows a
continuously updated list of all cgroups with their CPU and memory
usage.</para>
</section>
<!--===============================================================-->
<section><title>Logging</title>
<para>System-wide logging is provided by systemds
<emphasis>journal</emphasis>, which subsumes traditional logging
daemons such as syslogd and klogd. Log entries are kept in binary
files in <filename>/var/log/journal/</filename>. The command
<literal>journalctl</literal> allows you to see the contents of the
journal. For example,
<screen>
$ journalctl -b
</screen>
shows all journal entries since the last reboot. (The output of
<command>journalctl</command> is piped into <command>less</command> by
default.) You can use various options and match operators to restrict
output to messages of interest. For instance, to get all messages
from PostgreSQL:
<screen>
$ journalctl -u postgresql.service
-- Logs begin at Mon, 2013-01-07 13:28:01 CET, end at Tue, 2013-01-08 01:09:57 CET. --
...
Jan 07 15:44:14 hagbard postgres[2681]: [2-1] LOG: database system is shut down
-- Reboot --
Jan 07 15:45:10 hagbard postgres[2532]: [1-1] LOG: database system was shut down at 2013-01-07 15:44:14 CET
Jan 07 15:45:13 hagbard postgres[2500]: [1-1] LOG: database system is ready to accept connections
</screen>
Or to get all messages since the last reboot that have at least a
“critical” severity level:
<screen>
$ journalctl -b -p crit
Dec 17 21:08:06 mandark sudo[3673]: pam_unix(sudo:auth): auth could not identify password for [alice]
Dec 29 01:30:22 mandark kernel[6131]: [1053513.909444] CPU6: Core temperature above threshold, cpu clock throttled (total events = 1)
</screen>
</para>
<para>The system journal is readable by root and by users in the
<literal>wheel</literal> and <literal>systemd-journal</literal>
groups. All users have a private journal that can be read using
<command>journalctl</command>.</para>
</section>
<!--===============================================================-->
<section><title>Cleaning up the Nix store</title>
<para>Nix has a purely functional model, meaning that packages are
never upgraded in place. Instead new versions of packages end up in a
different location in the Nix store (<filename>/nix/store</filename>).
You should periodically run Nixs <emphasis>garbage
collector</emphasis> to remove old, unreferenced packages. This is
easy:
<screen>
$ nix-collect-garbage
</screen>
Alternatively, you can use a systemd unit that does the same in the
background:
<screen>
$ systemctl start nix-gc.service
</screen>
You can tell NixOS in <filename>configuration.nix</filename> to run
this unit automatically at certain points in time, for instance, every
night at 03:15:
<programlisting>
nix.gc.automatic = true;
nix.gc.dates = "03:15";
</programlisting>
</para>
<para>The commands above do not remove garbage collector roots, such
as old system configurations. Thus they do not remove the ability to
roll back to previous configurations. The following command deletes
old roots, removing the ability to roll back to them:
<screen>
$ nix-collect-garbage -d
</screen>
You can also do this for specific profiles, e.g.
<screen>
$ nix-env -p /nix/var/nix/profiles/per-user/eelco/profile --delete-generations old
</screen>
Note that NixOS system configurations are stored in the profile
<filename>/nix/var/nix/profiles/system</filename>.</para>
<para>Another way to reclaim disk space (often as much as 40% of the
size of the Nix store) is to run Nixs store optimiser, which seeks
out identical files in the store and replaces them with hard links to
a single copy.
<screen>
$ nix-store --optimise
</screen>
Since this command needs to read the entire Nix store, it can take
quite a while to finish.</para>
</section>
</chapter>

268
nixos/doc/manual/style.css Normal file
View File

@ -0,0 +1,268 @@
/* Copied from http://bakefile.sourceforge.net/, which appears
licensed under the GNU GPL. */
/***************************************************************************
Basic headers and text:
***************************************************************************/
body
{
font-family: "Nimbus Sans L", sans-serif;
background: white;
margin: 2em 1em 2em 1em;
}
h1, h2, h3, h4
{
color: #005aa0;
}
h1 /* title */
{
font-size: 200%;
}
h2 /* chapters, appendices, subtitle */
{
font-size: 180%;
}
/* Extra space between chapters, appendices. */
div.chapter > div.titlepage h2, div.appendix > div.titlepage h2
{
margin-top: 1.5em;
}
div.section > div.titlepage h2 /* sections */
{
font-size: 150%;
margin-top: 1.5em;
}
h3 /* subsections */
{
font-size: 125%;
}
div.simplesect h2
{
font-size: 110%;
}
div.appendix h3
{
font-size: 150%;
margin-top: 1.5em;
}
div.refnamediv h2, div.refsynopsisdiv h2, div.refsection h2 /* refentry parts */
{
margin-top: 1.4em;
font-size: 125%;
}
div.refsection h3
{
font-size: 110%;
}
/***************************************************************************
Examples:
***************************************************************************/
div.example
{
border: 1px solid #b0b0b0;
padding: 6px 6px;
margin-left: 1.5em;
margin-right: 1.5em;
background: #f4f4f8;
border-radius: 0.4em;
box-shadow: 0.4em 0.4em 0.5em #e0e0e0;
}
div.example p.title
{
margin-top: 0em;
}
div.example pre
{
box-shadow: none;
}
/***************************************************************************
Screen dumps:
***************************************************************************/
pre.screen, pre.programlisting
{
border: 1px solid #b0b0b0;
padding: 3px 3px;
margin-left: 1.5em;
margin-right: 1.5em;
color: #600000;
background: #f4f4f8;
font-family: monospace;
border-radius: 0.4em;
box-shadow: 0.4em 0.4em 0.5em #e0e0e0;
}
div.example pre.programlisting
{
border: 0px;
padding: 0 0;
margin: 0 0 0 0;
}
/***************************************************************************
Notes, warnings etc:
***************************************************************************/
.note, .warning
{
border: 1px solid #b0b0b0;
padding: 3px 3px;
margin-left: 1.5em;
margin-right: 1.5em;
margin-bottom: 1em;
padding: 0.3em 0.3em 0.3em 0.3em;
background: #fffff5;
border-radius: 0.4em;
box-shadow: 0.4em 0.4em 0.5em #e0e0e0;
}
div.note, div.warning
{
font-style: italic;
}
div.note h3, div.warning h3
{
color: red;
font-size: 100%;
padding-right: 0.5em;
display: inline;
}
div.note p, div.warning p
{
margin-bottom: 0em;
}
div.note h3 + p, div.warning h3 + p
{
display: inline;
}
div.note h3
{
color: blue;
font-size: 100%;
}
div.navfooter *
{
font-size: 90%;
}
/***************************************************************************
Links colors and highlighting:
***************************************************************************/
a { text-decoration: none; }
a:hover { text-decoration: underline; }
a:link { color: #0048b3; }
a:visited { color: #002a6a; }
/***************************************************************************
Table of contents:
***************************************************************************/
div.toc
{
font-size: 90%;
}
div.toc dl
{
margin-top: 0em;
margin-bottom: 0em;
}
/***************************************************************************
Special elements:
***************************************************************************/
tt, code
{
color: #400000;
}
.term
{
font-weight: bold;
}
div.variablelist dd p, div.glosslist dd p
{
margin-top: 0em;
}
div.variablelist dd, div.glosslist dd
{
margin-left: 1.5em;
}
div.glosslist dt
{
font-style: italic;
}
.varname
{
color: #400000;
}
span.command strong
{
font-weight: normal;
color: #400000;
}
div.calloutlist table
{
box-shadow: none;
}
table
{
border-collapse: collapse;
box-shadow: 0.4em 0.4em 0.5em #e0e0e0;
}
table.simplelist
{
text-align: left;
color: #005aa0;
border: 0;
padding: 5px;
background: #fffff5;
font-weight: normal;
font-style: italic;
box-shadow: none;
margin-bottom: 1em;
}
div.affiliation
{
font-style: italic;
}

View File

@ -0,0 +1,198 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Troubleshooting</title>
<!--===============================================================-->
<section><title>Boot problems</title>
<para>If NixOS fails to boot, there are a number of kernel command
line parameters that may help you to identify or fix the issue. You
can add these parameters in the GRUB boot menu by pressing “e” to
modify the selected boot entry and editing the line starting with
<literal>linux</literal>. The following are some useful kernel command
line parameters that are recognised by the NixOS boot scripts or by
systemd:
<variablelist>
<varlistentry><term><literal>boot.shell_on_fail</literal></term>
<listitem><para>Start a root shell if something goes wrong in
stage 1 of the boot process (the initial ramdisk). This is
disabled by default because there is no authentication for the
root shell.</para></listitem>
</varlistentry>
<varlistentry><term><literal>boot.debug1</literal></term>
<listitem><para>Start an interactive shell in stage 1 before
anything useful has been done. That is, no modules have been
loaded and no file systems have been mounted, except for
<filename>/proc</filename> and
<filename>/sys</filename>.</para></listitem>
</varlistentry>
<varlistentry><term><literal>boot.trace</literal></term>
<listitem><para>Print every shell command executed by the stage 1
and 2 boot scripts.</para></listitem>
</varlistentry>
<varlistentry><term><literal>single</literal></term>
<listitem><para>Boot into rescue mode (a.k.a. single user mode).
This will cause systemd to start nothing but the unit
<literal>rescue.target</literal>, which runs
<command>sulogin</command> to prompt for the root password and
start a root login shell. Exiting the shell causes the system to
continue with the normal boot process.</para></listitem>
</varlistentry>
<varlistentry><term><literal>systemd.log_level=debug systemd.log_target=console</literal></term>
<listitem><para>Make systemd very verbose and send log messages to
the console instead of the journal.</para></listitem>
</varlistentry>
</variablelist>
For more parameters recognised by systemd, see
<citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>.</para>
<para>If no login prompts or X11 login screens appear (e.g. due to
hanging dependencies), you can press Alt+ArrowUp. If youre lucky,
this will start rescue mode (described above). (Also note that since
most units have a 90-second timeout before systemd gives up on them,
the <command>agetty</command> login prompts should appear eventually
unless something is very wrong.)</para>
</section>
<!--===============================================================-->
<section><title>Maintenance mode</title>
<para>You can enter rescue mode by running:
<screen>
$ systemctl rescue</screen>
This will eventually give you a single-user root shell. Systemd will
stop (almost) all system services. To get out of maintenance mode,
just exit from the rescue shell.</para>
</section>
<!--===============================================================-->
<section><title>Rolling back configuration changes</title>
<para>After running <command>nixos-rebuild</command> to switch to a
new configuration, you may find that the new configuration doesnt
work very well. In that case, there are several ways to return to a
previous configuration.</para>
<para>First, the GRUB boot manager allows you to boot into any
previous configuration that hasnt been garbage-collected. These
configurations can be found under the GRUB submenu “NixOS - All
configurations”. This is especially useful if the new configuration
fails to boot. After the system has booted, you can make the selected
configuration the default for subsequent boots:
<screen>
$ /run/current-system/bin/switch-to-configuration boot</screen>
</para>
<para>Second, you can switch to the previous configuration in a running
system:
<screen>
$ nixos-rebuild switch --rollback</screen>
This is equivalent to running:
<screen>
$ /nix/var/nix/profiles/system-<replaceable>N</replaceable>-link/bin/switch-to-configuration switch</screen>
where <replaceable>N</replaceable> is the number of the NixOS system
configuration. To get a list of the available configurations, do:
<screen>
$ ls -l /nix/var/nix/profiles/system-*-link
<replaceable>...</replaceable>
lrwxrwxrwx 1 root root 78 Aug 12 13:54 /nix/var/nix/profiles/system-268-link -> /nix/store/202b...-nixos-13.07pre4932_5a676e4-4be1055
</screen>
</para>
</section>
<!--===============================================================-->
<section><title>Nix store corruption</title>
<para>After a system crash, its possible for files in the Nix store
to become corrupted. (For instance, the Ext4 file system has the
tendency to replace un-synced files with zero bytes.) NixOS tries
hard to prevent this from happening: it performs a
<command>sync</command> before switching to a new configuration, and
Nixs database is fully transactional. If corruption still occurs,
you may be able to fix it automatically.</para>
<para>If the corruption is in a path in the closure of the NixOS
system configuration, you can fix it by doing
<screen>
$ nixos-rebuild switch --repair
</screen>
This will cause Nix to check every path in the closure, and if its
cryptographic hash differs from the hash recorded in Nixs database,
the path is rebuilt or redownloaded.</para>
<para>You can also scan the entire Nix store for corrupt paths:
<screen>
$ nix-store --verify --check-contents --repair
</screen>
Any corrupt paths will be redownloaded if theyre available in a
binary cache; otherwise, they cannot be repaired.</para>
</section>
<!--===============================================================-->
<section><title>Nix network issues</title>
<para>Nix uses a so-called <emphasis>binary cache</emphasis> to
optimise building a package from source into downloading it as a
pre-built binary. That is, whenever a command like
<command>nixos-rebuild</command> needs a path in the Nix store, Nix
will try to download that path from the Internet rather than build it
from source. The default binary cache is
<uri>http://cache.nixos.org/</uri>. If this cache is unreachable, Nix
operations may take a long time due to HTTP connection timeouts. You
can disable the use of the binary cache by adding <option>--option
use-binary-caches false</option>, e.g.
<screen>
$ nixos-rebuild switch --option use-binary-caches false
</screen>
If you have an alternative binary cache at your disposal, you can use
it instead:
<screen>
$ nixos-rebuild switch --option binary-caches http://my-cache.example.org/
</screen>
</para>
</section>
</chapter>

16
nixos/gui/README Normal file
View File

@ -0,0 +1,16 @@
This file should become a nix expression. (see modules/installer/tools/tools.nix)
you need to:
- download the latest jQuery from and copy it to chrome/content:
http://code.jquery.com/jquery-1.5.2.js
- install 'xulrunner' with nix:
nix-env -Ai nixpkgs_sys.firefox40Pkgs.xulrunner
- make sure nixos-option in your path
- have /etc/nixos/nixpkgs
- have /etc/nixos/nixos
run it:
- xulrunner /etc/nixos/nixos/gui/application.ini -jsconsole

36
nixos/gui/application.ini Normal file
View File

@ -0,0 +1,36 @@
[App]
;
; This field specifies your organization's name. This field is recommended,
; but optional.
Vendor=NixOS
;
; This field specifies your application's name. This field is required.
Name=NixOS-gui
;
; This field specifies your application's version. This field is optional.
Version=0.1
;
; This field specifies your application's build ID (timestamp). This field is
; required.
BuildID=20110424
;
; This field specifies a compact copyright notice for your application. This
; field is optional.
;Copyright=
;
; This ID is just an example. Every XUL app ought to have it's own unique ID.
; You can use the microsoft "guidgen" or "uuidgen" tools, or go on
; irc.mozilla.org and /msg botbot uuid. This field is optional.
;ID=
[Gecko]
;
; This field is required. It specifies the minimum Gecko version that this
; application requires.
MinVersion=1.9a5
;
; This field is optional. It specifies the maximum Gecko version that this
; application requires. It should be specified if your application uses
; unfrozen interfaces.
MaxVersion=2.*

View File

@ -0,0 +1 @@
manifest chrome/chrome.manifest

View File

@ -0,0 +1 @@
content nixos-gui content/

View File

@ -0,0 +1,137 @@
function inspect(obj, maxLevels, level)
{
var str = '', type, msg;
// Start Input Validations
// Don't touch, we start iterating at level zero
if(level == null) level = 0;
// At least you want to show the first level
if(maxLevels == null) maxLevels = 1;
if(maxLevels < 1)
return '<font color="red">Error: Levels number must be > 0</font>';
// We start with a non null object
if(obj == null)
return '<font color="red">Error: Object <b>NULL</b></font>';
// End Input Validations
// Each Iteration must be indented
str += '<ul>';
// Start iterations for all objects in obj
for(property in obj)
{
try
{
// Show "property" and "type property"
type = typeof(obj[property]);
str += '<li>(' + type + ') ' + property +
( (obj[property]==null)?(': <b>null</b>'):('')) + '</li>';
// We keep iterating if this property is an Object, non null
// and we are inside the required number of levels
if((type == 'object') && (obj[property] != null) && (level+1 < maxLevels))
str += inspect(obj[property], maxLevels, level+1);
}
catch(err)
{
// Is there some properties in obj we can't access? Print it red.
if(typeof(err) == 'string') msg = err;
else if(err.message) msg = err.message;
else if(err.description) msg = err.description;
else msg = 'Unknown';
str += '<li><font color="red">(Error) ' + property + ': ' + msg +'</font></li>';
}
}
// Close indent
str += '</ul>';
return str;
}
// Run xulrunner application.ini -jsconsole -console, to see messages.
function log(str)
{
Components.classes['@mozilla.org/consoleservice;1']
.getService(Components.interfaces.nsIConsoleService)
.logStringMessage(str);
}
function makeTempFile(prefix)
{
var file = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("TmpD", Components.interfaces.nsIFile);
file.append(prefix || "xulrunner");
file.createUnique(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0664);
return file;
}
function writeToFile(file, data)
{
// file is nsIFile, data is a string
var foStream = Components.classes["@mozilla.org/network/file-output-stream;1"]
.createInstance(Components.interfaces.nsIFileOutputStream);
// use 0x02 | 0x10 to open file for appending.
foStream.init(file, 0x02 | 0x08 | 0x20, 0664, 0); // write, create, truncate
foStream.write(data, data.length);
foStream.close();
}
function readFromFile(file)
{
// |file| is nsIFile
var data = "";
var fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
.createInstance(Components.interfaces.nsIFileInputStream);
var sstream = Components.classes["@mozilla.org/scriptableinputstream;1"]
.createInstance(Components.interfaces.nsIScriptableInputStream);
fstream.init(file, -1, 0, 0);
sstream.init(fstream);
var str = sstream.read(4096);
while (str.length > 0) {
data += str;
str = sstream.read(4096);
}
sstream.close();
fstream.close();
return data;
}
function runProgram(commandLine)
{
// create an nsILocalFile for the executable
var file = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
file.initWithPath("/bin/sh");
// create an nsIProcess
var process = Components.classes["@mozilla.org/process/util;1"]
.createInstance(Components.interfaces.nsIProcess);
process.init(file);
// Run the process.
// If first param is true, calling thread will be blocked until
// called process terminates.
// Second and third params are used to pass command-line arguments
// to the process.
var args = ["-c", commandLine];
process.run(true, args, args.length);
}
// only for testing...
function testIO()
{
var f = makeTempFile();
writeToFile(f, "essai\ntest");
alert(readFromFile(f));
runProgram("zenity --info");
}

View File

@ -0,0 +1,70 @@
// global variables.
var gNixOS;
var gOptionView;
/*
var gProgressBar;
function setProgress(current, max)
{
if (gProgressBar) {
gProgressBar.value = 100 * current / max;
log("progress: " + gProgressBar.value + "%");
}
else
log("unknow progress bar");
}
*/
function updateTextbox(id, value)
{
// setting the height cause an overflow which resize the textbox to its
// content due to its onoverflow attribute.
$(id).attr("value", value).attr("height", 1);
};
function updatePanel(options)
{
log("updatePanel: " + options.length);
if (options.length == 0)
return;
// FIXME: ignore the rest of the selection for now.
var o = options[0];
$("#name").attr("label", o.path);
if (o.typename != null)
$("#typename").attr("label", o.typename);
else
$("#typename").attr("label", "");
$("#desc").text(o.description);
if (o.value != null)
updateTextbox("#val", o.value);
else
updateTextbox("#val", "");
if (o.defaultValue != null)
updateTextbox("#def", o.defaultValue);
else
updateTextbox("#def", "");
if (o.example != null)
updateTextbox("#exp", o.example);
else
updateTextbox("#exp", "");
updateTextbox("#decls", o.declarations.join("\n"));
updateTextbox("#defs", o.definitions.join("\n"));
}
function onload()
{
var optionTree = document.getElementById("option-tree");
// gProgressBar = document.getElementById("progress-bar");
// setProgress(0, 1);
gNixOS = new NixOS();
gOptionView = new OptionView(gNixOS.option, updatePanel);
optionTree.view = gOptionView;
}

View File

@ -0,0 +1,63 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<!DOCTYPE window>
<!-- To edit this file I recommend you to use:
http://xulfr.org/outils/xulediteur.xul
-->
<window
id = "nixos-gui"
title = "NixOS gui"
width = "800"
height = "600"
xmlns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="jquery-1.5.2.js"/>
<script src="io.js"/>
<script src="nixos.js"/>
<script src="optionView.js"/>
<script src="main.js"/>
<hbox flex="1">
<vbox width="250">
<tree flex="1" id="option-tree" persist="height" onselect="gOptionView.selectionChanged()">
<treecols>
<treecol persist="hidden width" flex="9" id="opt-name"
label="Option" primary="true"/>
<!-- Uncomment the following column to see the number of option
printed below each options. -->
<!--
<treecol persist="hidden width" flex="1" id="dbg-size"
label="sz"/>
-->
</treecols>
<treechildren id="first-child" flex="1"/>
</tree>
</vbox>
<vbox flex="3" style="overflow: auto">
<caption id="name" label=""/>
<caption id="typename" label=""/>
<separator/>
<description id="desc" hidden="false"></description>
<separator/>
<caption label="Value:"/>
<textbox id="val" readonly="true" multiline="true" value=""
class="plain" hidden="false" onoverflow="this.height =
this.inputField.scrollHeight;" />
<separator/>
<caption label="Default:"/>
<textbox id="def" readonly="true" multiline="true" value="" class="plain" hidden="false" onoverflow="this.height = this.inputField.scrollHeight;" />
<separator/>
<caption label="Example:"/>
<textbox id="exp" readonly="true" multiline="true" value="" class="plain" hidden="false" onoverflow="this.height = this.inputField.scrollHeight;" />
<separator/>
<caption label="Declarations:"/>
<textbox id="decls" readonly="true" multiline="true" value="" class="plain" hidden="false" onoverflow="this.height = this.inputField.scrollHeight;" />
<separator/>
<caption label="Definitions:"/>
<textbox id="defs" readonly="true" multiline="true" value=""
class="plain" hidden="false" onoverflow="this.height = this.inputField.scrollHeight;" />
</vbox>
</hbox>
<!-- <progressmeter id="progress-bar" value="0%"/> -->
</window>

View File

@ -0,0 +1,255 @@
function NixOS () {
var env = Components.classes["@mozilla.org/process/environment;1"].
getService(Components.interfaces.nsIEnvironment);
if (env.exists("NIXOS"))
this.nixos = env.get("NIXOS");
if (env.exists("NIXOS_CONFIG"))
this.config = env.get("NIXOS_CONFIG");
if (env.exists("NIXPKGS"))
this.nixpkgs = env.get("NIXPKGS");
if (env.exists("mountPoint"))
this.root = env.get("mountPoint");
if (env.exists("NIXOS_OPTION"))
this.optionBin = env.get("NIXOS_OPTION");
this.option = new Option("options", this, null);
};
NixOS.prototype = {
root: "",
nixos: "/etc/nixos/nixos",
nixpkgs: "/etc/nixos/nixpkgs",
config: "/etc/nixos/configuration.nix",
instantiateBin: "/run/current-system/sw/bin/nix-instantiate",
optionBin: "/run/current-system/sw/bin/nixos-option",
tmpFile: "nixos-gui",
option: null
};
function Option (name, context, parent) {
this.name = name;
this.context_ = context;
if (parent == null)
this.path = "";
else if (parent.path == "")
this.path = name;
else
this.path = parent.path + "." + name;
};
Option.prototype = {
load: function () {
var env = "";
env += "'NIXOS=" + this.context_.root + this.context_.nixos + "' ";
env += "'NIXOS_PKGS=" + this.context_.root + this.context_.nixpkgs + "' ";
env += "'NIXOS_CONFIG=" + this.context_.config + "' ";
var out = makeTempFile(this.context_.tmpFile);
var prog = this.context_.optionBin + " 2>&1 >" + out.path + " ";
var args = " --xml " + this.path;
runProgram(/*env + */ prog + args);
var xml = readFromFile(out);
out.remove(false);
// jQuery does a stack overflow when converting a huge XML to a DOM.
var dom = DOMParser().parseFromString(xml, "text/xml");
var xmlAttrs = $("expr > attrs > attr", dom);
this.isOption = xmlAttrs.first().attr("name") == "_isOption";
if (!this.isOption)
this.loadSubOptions(xmlAttrs);
else
this.loadOption(xmlAttrs);
this.isLoaded = true;
},
loadSubOptions: function (xmlAttrs) {
var cur = this;
var attrs = new Array();
xmlAttrs.each(
function (index) {
var name = $(this).attr("name");
var attr = new Option(name, cur.context_, cur);
attrs.push(attr);
}
);
this.subOptions = attrs;
},
optionAttributeMap: {
_isOption: function (cur, v) { },
value: function (cur, v) { cur.value = xml2nix($(v).children().first()); },
default: function (cur, v) { cur.defaultValue = xml2nix($(v).children().first()); },
example: function (cur, v) { cur.example = xml2nix($(v).children().first()); },
description: function (cur, v) { cur.description = this.string(v); },
typename: function (cur, v) { cur.typename = this.string(v); },
options: function (cur, v) { cur.loadSubOptions($("attrs", v).children()); },
declarations: function (cur, v) { cur.declarations = this.pathList(v); },
definitions: function (cur, v) { cur.definitions = this.pathList(v); },
string: function (v) {
return $(v).children("string").first().attr("value");
},
pathList: function (v) {
var list = [];
$(v).children("list").first().children().each(
function (idx) {
list.push($(this).attr("value"));
}
);
return list;
}
},
loadOption: function (attrs) {
var cur = this;
attrs.each(
function (index) {
var name = $(this).attr("name");
log("loadOption: " + name);
cur.optionAttributeMap[name](cur, this);
}
);
},
// keep the context under which this option has been used.
context_: null,
// name of the option.
name: "",
// result of nixos-option.
value: null,
typename: null,
defaultValue: null,
example: null,
description: "",
declarations: [],
definitions: [],
// path to reach this option
path: "",
// list of options accessible from here.
isLoaded: false,
isOption: false,
subOptions: []
};
var xml2nix_pptable = {
attrs: function (node, depth, pp) {
var children = node.children().not(
function () {
var name = $(this).attr("name");
return name.charAt(0) == "_";
}
);
var c = 0;
var out = "";
out += "{";
depth += 1;
children.each(
function (idx) {
c += 1;
out += pp.indent(depth);
out += pp.dispatch($(this), depth, pp);
}
);
depth -= 1;
if (c > 0)
out += this.indent(depth);
else
out += " ";
out += "}";
return out;
},
list: function (node, depth, pp) {
var children = node.children();
var c = 0;
var out = "";
out += "[";
depth += 1;
children.each(
function (idx) {
c += 1;
out += pp.indent(depth);
out += pp.dispatch($(this), depth, pp);
}
);
depth -= 1;
if (c > 0)
out += this.indent(depth);
else
out += " ";
out += "]";
return out;
},
attr: function (node, depth, pp) {
var name = node.attr("name");
var out = "";
var val = "";
out += name + " = ";
depth += 1;
val = pp.dispatch(node.children().first(), depth, pp);
out += val;
depth -= 1;
out += ";";
return out;
},
string: function (node, depth, pp) {
return "\"" + node.attr("value") + "\"";
},
path: function (node, depth, pp) {
return node.attr("value");
},
bool: function (node, depth, pp) {
return node.attr("value");
},
"int": function (node, depth, pp) {
return node.attr("value");
},
null: function (node, depth, pp) {
return "null";
},
derivation: function (node, depth, pp) {
return "<derivation>";
},
function: function (node, depth, pp) {
return "<function>";
},
unevaluated: function (node, depth, pp) {
return "<unevaluated>";
},
dispatch: function (node, depth, pp) {
for (var key in pp)
{
if(node.is(key))
{
// log(this.indent(depth) + "dispatch: " + key);
var out = pp[key](node, depth, pp);
// log(this.indent(depth) + "dispatch: => " + out);
return out;
}
}
return "<dispatch-error>";
},
indent: function (depth) {
var ret = "\n";
while (depth--)
ret += " ";
return ret;
}
};
function xml2nix(node) {
var depth = 0;
var pp = xml2nix_pptable;
var out = pp.dispatch(node, depth, pp);
// log("pretty:\n" + out);
return out;
}

View File

@ -0,0 +1,242 @@
// extend NixOS options to handle the Tree View. Should be better to keep a
// separation of concern here.
Option.prototype.tv_opened = false;
Option.prototype.tv_size = 1;
Option.prototype.tv_open = function () {
this.tv_opened = true;
this.tv_size = 1;
// load an option if it is not loaded yet, and initialize them to be
// read by the Option view.
if (!this.isLoaded)
this.load();
// If this is not an option, then add it's lits of sub-options size.
if (!this.isOption)
{
for (var i = 0; i < this.subOptions.length; i++)
this.tv_size += this.subOptions[i].tv_size;
}
};
Option.prototype.tv_close = function () {
this.tv_opened = false;
this.tv_size = 1;
};
function OptionView (root, selCallback) {
root.tv_open();
this.rootOption = root;
this.selCallback = selCallback;
}
OptionView.prototype = {
rootOption: null,
selCallback: null,
// This function returns the path to option which is at the specified row.
reach_cache: null,
reachRow: function (row) {
var o = this.rootOption; // Current option.
var r = 0; // Number of rows traversed.
var c = 0; // Child index.
var path = [{ row: r, opt: o }]; // new Array();
// hypothesis: this.rootOption.tv_size is always open and bigger than
// Use the previous returned value to avoid making to many checks and to
// optimize for frequent access of near rows.
if (this.reach_cache != null)
{
for (var i = this.reach_cache.length - 2; i >= 0; i--) {
var p = this.reach_cache[i];
// If we will have to go the same path.
if (row >= p.row && row < p.row + p.opt.tv_size)
{
path.unshift(p);
r = path[0].row;
o = path[0].opt;
}
else
break;
};
}
while (r != row)
{
// Go deeper in the child which contains the requested row. The
// tv_size contains the size of the tree starting from each option.
c = 0;
while (c < o.subOptions.length && r + o.subOptions[c].tv_size < row)
{
r += o.subOptions[c].tv_size;
c += 1;
}
if (c < o.subOptions.length && r + o.subOptions[c].tv_size >= row)
{
// Count the current option as a row.
o = o.subOptions[c];
r += 1;
}
else
alert("WTF: " + o.name + " ask: " + row + " children: " + o.subOptions + " c: " + c);
path.unshift({ row: r, opt: o });
}
this.reach_cache = path;
return path;
},
// needs to return true if there is a /row/ at the same level /after/ a
// given row.
hasNextSibling: function(row, after) {
log("sibling " + row + " after " + after);
var path = reachRow(row);
if (path.length > 1)
{
var last = path[1].row + path[1].opt.tv_size;
// Has a next sibling if the row is not over the size of the
// parent and if the current one is not the last child.
return after + 1 < last && path[0].row + path[0].opt.tv_size < last;
}
else
// The top-level option has no sibling.
return false;
},
// Does the current row contain any sub-options?
isContainer: function(row) {
return !this.reachRow(row)[0].opt.isOption;
},
isContainerEmpty: function(row) {
return this.reachRow(row)[0].opt.subOptions.length == 0;
},
isContainerOpen: function(row) {
return this.reachRow(row)[0].opt.tv_opened;
},
// Open or close an option.
toggleOpenState: function (row) {
var path = this.reachRow(row);
var delta = -path[0].opt.tv_size;
if (path[0].opt.tv_opened)
path[0].opt.tv_close();
else
path[0].opt.tv_open();
delta += path[0].opt.tv_size;
// Parents are alreay opened, but we need to update the tv_size
// counters. Thus we have to invalidate the reach cache.
this.reach_cache = null;
for (var i = 1; i < path.length; i++)
path[i].opt.tv_open();
this.tree.rowCountChanged(row + 1, delta);
},
// Return the identation level of the option at the line /row/. The
// top-level level is 0.
getLevel: function(row) {
return this.reachRow(row).length - 1;
},
// Obtain the index of a parent row. If there is no parent row,
// returns -1.
getParentIndex: function(row) {
var path = this.reachRow(row);
if (path.length > 1)
return path[1].row;
else
return -1;
},
// Return the content of each row base on the column name.
getCellText: function(row, column) {
if (column.id == "opt-name")
return this.reachRow(row)[0].opt.name;
if (column.id == "dbg-size")
return this.reachRow(row)[0].opt.tv_size;
return "";
},
// We have no column with images.
getCellValue: function(row, column) { },
isSelectable: function(row, column) { return true; },
// Get the selection out of the tree and give options to the call back
// function.
selectionChanged: function() {
if (this.selCallback == null)
return;
var opts = [];
var start = new Object();
var end = new Object();
var numRanges = this.tree.view.selection.getRangeCount();
for (var t = 0; t < numRanges; t++) {
this.tree.view.selection.getRangeAt(t,start,end);
for (var v = start.value; v <= end.value; v++) {
var opt = this.reachRow(v)[0].opt;
if (!opt.isLoaded)
opt.load();
if (opt.isOption)
opts.push(opt);
// FIXME: no need to make things slowing down, because our current
// callback do not handle multiple option display.
if (!opts.empty)
break;
}
// FIXME: no need to make things slowing down, because our current
// callback do not handle multiple option display.
if (!opts.empty)
break;
}
if (!opts.empty)
this.selCallback(opts);
},
set rowCount(c) { throw "rowCount is a readonly property"; },
get rowCount() { return this.rootOption.tv_size; },
// refuse drag-n-drop of options.
canDrop: function (index, orientation, dataTransfer) { return false; },
drop: function (index, orientation, dataTransfer) { },
// ?
getCellProperties: function(row, column, prop) { },
getColumnProperties: function(column, prop) { },
getRowProperties: function(row, prop) { },
getImageSrc: function(row, column) { },
// No progress columns are used.
getProgressMode: function(row, column) { },
// Do not add options yet.
isEditable: function(row, column) { return false; },
setCellValue: function(row, column, value) { },
setCellText: function(row, column, value) { },
// ...
isSeparator: function(index) { return false; },
isSorted: function() { return false; },
performAction: function(action) { },
performActionOnCell: function(action, row, column) { },
performActionOnRow: function(action, row) { }, // ??
// ??
cycleCell: function (row, col) { },
cycleHeader: function(col) { },
selection: null,
tree: null,
setTree: function(tree) { this.tree = tree; }
};

154
nixos/gui/components/clh.js Normal file
View File

@ -0,0 +1,154 @@
const nsIAppShellService = Components.interfaces.nsIAppShellService;
const nsISupports = Components.interfaces.nsISupports;
const nsICategoryManager = Components.interfaces.nsICategoryManager;
const nsIComponentRegistrar = Components.interfaces.nsIComponentRegistrar;
const nsICommandLine = Components.interfaces.nsICommandLine;
const nsICommandLineHandler = Components.interfaces.nsICommandLineHandler;
const nsIFactory = Components.interfaces.nsIFactory;
const nsIModule = Components.interfaces.nsIModule;
const nsIWindowWatcher = Components.interfaces.nsIWindowWatcher;
// CHANGEME: to the chrome URI of your extension or application
const CHROME_URI = "chrome://nixos-gui/content/myviewer.xul";
// CHANGEME: change the contract id, CID, and category to be unique
// to your application.
const clh_contractID = "@mozilla.org/commandlinehandler/general-startup;1?type=myapp";
// use uuidgen to generate a unique ID
const clh_CID = Components.ID("{2991c315-b871-42cd-b33f-bfee4fcbf682}");
// category names are sorted alphabetically. Typical command-line handlers use a
// category that begins with the letter "m".
const clh_category = "m-myapp";
/**
* Utility functions
*/
/**
* Opens a chrome window.
* @param aChromeURISpec a string specifying the URI of the window to open.
* @param aArgument an argument to pass to the window (may be null)
*/
function openWindow(aChromeURISpec, aArgument)
{
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"].
getService(Components.interfaces.nsIWindowWatcher);
ww.openWindow(null, aChromeURISpec, "_blank",
"chrome,menubar,toolbar,status,resizable,dialog=no",
aArgument);
}
/**
* The XPCOM component that implements nsICommandLineHandler.
* It also implements nsIFactory to serve as its own singleton factory.
*/
const myAppHandler = {
/* nsISupports */
QueryInterface : function clh_QI(iid)
{
if (iid.equals(nsICommandLineHandler) ||
iid.equals(nsIFactory) ||
iid.equals(nsISupports))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
/* nsICommandLineHandler */
handle : function clh_handle(cmdLine)
{
openWindow(CHROME_URI, cmdLine);
cmdLine.preventDefault = true;
},
// CHANGEME: change the help info as appropriate, but
// follow the guidelines in nsICommandLineHandler.idl
// specifically, flag descriptions should start at
// character 24, and lines should be wrapped at
// 72 characters with embedded newlines,
// and finally, the string should end with a newline
helpInfo : " <filename> Open the file in the viewer\n",
/* nsIFactory */
createInstance : function clh_CI(outer, iid)
{
if (outer != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return this.QueryInterface(iid);
},
lockFactory : function clh_lock(lock)
{
/* no-op */
}
};
/**
* The XPCOM glue that implements nsIModule
*/
const myAppHandlerModule = {
/* nsISupports */
QueryInterface : function mod_QI(iid)
{
if (iid.equals(nsIModule) ||
iid.equals(nsISupports))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
/* nsIModule */
getClassObject : function mod_gch(compMgr, cid, iid)
{
if (cid.equals(clh_CID))
return myAppHandler.QueryInterface(iid);
throw Components.results.NS_ERROR_NOT_REGISTERED;
},
registerSelf : function mod_regself(compMgr, fileSpec, location, type)
{
compMgr.QueryInterface(nsIComponentRegistrar);
compMgr.registerFactoryLocation(clh_CID,
"myAppHandler",
clh_contractID,
fileSpec,
location,
type);
var catMan = Components.classes["@mozilla.org/categorymanager;1"].
getService(nsICategoryManager);
catMan.addCategoryEntry("command-line-handler",
clh_category,
clh_contractID, true, true);
},
unregisterSelf : function mod_unreg(compMgr, location, type)
{
compMgr.QueryInterface(nsIComponentRegistrar);
compMgr.unregisterFactoryLocation(clh_CID, location);
var catMan = Components.classes["@mozilla.org/categorymanager;1"].
getService(nsICategoryManager);
catMan.deleteCategoryEntry("command-line-handler", clh_category);
},
canUnload : function (compMgr)
{
return true;
}
};
/* The NSGetModule function is the magic entry point that XPCOM uses to find what XPCOM objects
* this component provides
*/
function NSGetModule(comMgr, fileSpec)
{
return myAppHandlerModule;
}

View File

@ -0,0 +1,11 @@
pref("toolkit.defaultChromeURI", "chrome://nixos-gui/content/myviewer.xul");
pref("general.useragent.extra.myviewer", "NixOS gui/0.0");
/* debugging prefs */
pref("browser.dom.window.dump.enabled", true); // enable output to stderr
pref("javascript.options.showInConsole", true); // show javascript errors from chrome: files in the jsconsole
pref("javascript.options.strict", true); // show javascript strict warnings in the jsconsole
/* disable xul cache so that modifications to chrome: files apply without restarting xulrunner */
pref("nglayout.debug.disable_xul_cache", true);
pref("nglayout.debug.disable_xul_fastload", true);

87
nixos/lib/build-vms.nix Normal file
View File

@ -0,0 +1,87 @@
{ system, minimal ? false }:
let pkgs = import ./nixpkgs.nix { config = {}; inherit system; }; in
with pkgs.lib;
with import ../lib/qemu-flags.nix;
rec {
inherit pkgs;
# Build a virtual network from an attribute set `{ machine1 =
# config1; ... machineN = configN; }', where `machineX' is the
# hostname and `configX' is a NixOS system configuration. Each
# machine is given an arbitrary IP address in the virtual network.
buildVirtualNetwork =
nodes: let nodesOut = mapAttrs (n: buildVM nodesOut) (assignIPAddresses nodes); in nodesOut;
buildVM =
nodes: configurations:
import ./eval-config.nix {
inherit system;
modules = configurations ++
[ ../modules/virtualisation/qemu-vm.nix
../modules/testing/test-instrumentation.nix # !!! should only get added for automated test runs
{ key = "no-manual"; services.nixosManual.enable = false; }
] ++ optional minimal ../modules/testing/minimal-kernel.nix;
extraArgs = { inherit nodes; };
};
# Given an attribute set { machine1 = config1; ... machineN =
# configN; }, sequentially assign IP addresses in the 192.168.1.0/24
# range to each machine, and set the hostname to the attribute name.
assignIPAddresses = nodes:
let
machines = attrNames nodes;
machinesNumbered = zipTwoLists machines (range 1 254);
nodes_ = flip map machinesNumbered (m: nameValuePair m.first
[ ( { config, pkgs, nodes, ... }:
let
interfacesNumbered = zipTwoLists config.virtualisation.vlans (range 1 255);
interfaces = flip map interfacesNumbered ({ first, second }:
nameValuePair "eth${toString second}"
{ ipAddress = "192.168.${toString first}.${toString m.second}";
subnetMask = "255.255.255.0";
});
in
{ key = "ip-address";
config =
{ networking.hostName = m.first;
networking.interfaces = listToAttrs interfaces;
networking.primaryIPAddress =
optionalString (interfaces != []) (head interfaces).value.ipAddress;
# Put the IP addresses of all VMs in this machine's
# /etc/hosts file. If a machine has multiple
# interfaces, use the IP address corresponding to
# the first interface (i.e. the first network in its
# virtualisation.vlans option).
networking.extraHosts = flip concatMapStrings machines
(m: let config = (getAttr m nodes).config; in
optionalString (config.networking.primaryIPAddress != "")
("${config.networking.primaryIPAddress} " +
"${config.networking.hostName}\n"));
virtualisation.qemu.options =
flip map interfacesNumbered
({ first, second }: qemuNICFlags second first m.second);
};
}
)
(getAttr m.first nodes)
] );
in listToAttrs nodes_;
}

View File

@ -0,0 +1,6 @@
{ system ? builtins.currentSystem }:
{ pkgs =
(import nixpkgs/default.nix { inherit system; })
// { recurseForDerivations = true; };
}

64
nixos/lib/eval-config.nix Normal file
View File

@ -0,0 +1,64 @@
# From an end-user configuration file (`configuration'), build a NixOS
# configuration object (`config') from which we can retrieve option
# values.
{ system ? builtins.currentSystem
, pkgs ? null
, baseModules ? import ../modules/module-list.nix
, extraArgs ? {}
, modules
, check ? true
}:
let extraArgs_ = extraArgs; pkgs_ = pkgs; system_ = system; in
rec {
# Merge the option definitions in all modules, forming the full
# system configuration.
inherit (pkgs.lib.evalModules {
modules = modules ++ baseModules;
args = extraArgs;
check = check && options.environment.checkConfigurationOptions.value;
}) config options;
# These are the extra arguments passed to every module. In
# particular, Nixpkgs is passed through the "pkgs" argument.
extraArgs = extraArgs_ // {
inherit pkgs modules baseModules;
modulesPath = ../modules;
pkgs_i686 = import ./nixpkgs.nix { system = "i686-linux"; };
utils = import ./utils.nix pkgs;
};
# Import Nixpkgs, allowing the NixOS option nixpkgs.config to
# specify the Nixpkgs configuration (e.g., to set package options
# such as firefox.enableGeckoMediaPlayer, or to apply global
# overrides such as changing GCC throughout the system), and the
# option nixpkgs.system to override the platform type. This is
# tricky, because we have to prevent an infinite recursion: "pkgs"
# is passed as an argument to NixOS modules, but the value of "pkgs"
# depends on config.nixpkgs.config, which we get from the modules.
# So we call ourselves here with "pkgs" explicitly set to an
# instance that doesn't depend on nixpkgs.config.
pkgs =
if pkgs_ != null
then pkgs_
else import ./nixpkgs.nix (
let
system = if nixpkgsOptions.system != "" then nixpkgsOptions.system else system_;
nixpkgsOptions = (import ./eval-config.nix {
inherit system extraArgs modules;
# For efficiency, leave out most NixOS modules; they don't
# define nixpkgs.config, so it's pointless to evaluate them.
baseModules = [ ../modules/misc/nixpkgs.nix ];
pkgs = import ./nixpkgs.nix { system = system_; config = {}; };
check = false;
}).config.nixpkgs;
in
{
inherit system;
inherit (nixpkgsOptions) config;
});
}

4
nixos/lib/from-env.nix Normal file
View File

@ -0,0 +1,4 @@
# TODO: remove this file. There is lib.maybeEnv now
name: default:
let value = builtins.getEnv name; in
if value == "" then default else value

View File

@ -0,0 +1,60 @@
{ stdenv, perl, cdrkit, pathsFromGraph
, # The file name of the resulting ISO image.
isoName ? "cd.iso"
, # The files and directories to be placed in the ISO file system.
# This is a list of attribute sets {source, target} where `source'
# is the file system object (regular file or directory) to be
# grafted in the file system at path `target'.
contents
, # In addition to `contents', the closure of the store paths listed
# in `packages' are also placed in the Nix store of the CD. This is
# a list of attribute sets {object, symlink} where `object' if a
# store path whose closure will be copied, and `symlink' is a
# symlink to `object' that will be added to the CD.
storeContents ? []
, # Whether this should be an El-Torito bootable CD.
bootable ? false
, # Whether this should be an efi-bootable El-Torito CD.
efiBootable ? false
, # The path (in the ISO file system) of the boot image.
bootImage ? ""
, # The path (in the ISO file system) of the efi boot image.
efiBootImage ? ""
, # Whether to compress the resulting ISO image with bzip2.
compressImage ? false
, # The volume ID.
volumeID ? ""
}:
assert bootable -> bootImage != "";
assert efiBootable -> efiBootImage != "";
stdenv.mkDerivation {
name = "iso9660-image";
builder = ./make-iso9660-image.sh;
buildInputs = [perl cdrkit];
inherit isoName bootable bootImage compressImage volumeID pathsFromGraph efiBootImage efiBootable;
# !!! should use XML.
sources = map (x: x.source) contents;
targets = map (x: x.target) contents;
# !!! should use XML.
objects = map (x: x.object) storeContents;
symlinks = map (x: x.symlink) storeContents;
# For obtaining the closure of `storeContents'.
exportReferencesGraph =
map (x: [("closure-" + baseNameOf x.object) x.object]) storeContents;
}

View File

@ -0,0 +1,91 @@
source $stdenv/setup
sources_=($sources)
targets_=($targets)
objects=($objects)
symlinks=($symlinks)
# Remove the initial slash from a path, since genisofs likes it that way.
stripSlash() {
res="$1"
if test "${res:0:1}" = /; then res=${res:1}; fi
}
stripSlash "$bootImage"; bootImage="$res"
if test -n "$bootable"; then
# The -boot-info-table option modifies the $bootImage file, so
# find it in `contents' and make a copy of it (since the original
# is read-only in the Nix store...).
for ((i = 0; i < ${#targets_[@]}; i++)); do
stripSlash "${targets_[$i]}"
if test "$res" = "$bootImage"; then
echo "copying the boot image ${sources_[$i]}"
cp "${sources_[$i]}" boot.img
chmod u+w boot.img
sources_[$i]=boot.img
fi
done
bootFlags="-b $bootImage -c .boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table"
fi
if test -n "$efiBootable"; then
bootFlags="$bootFlags -eltorito-alt-boot -e $efiBootImage -no-emul-boot"
fi
touch pathlist
# Add the individual files.
for ((i = 0; i < ${#targets_[@]}; i++)); do
stripSlash "${targets_[$i]}"
echo "$res=${sources_[$i]}" >> pathlist
done
# Add the closures of the top-level store objects.
storePaths=$(perl $pathsFromGraph closure-*)
for i in $storePaths; do
echo "${i:1}=$i" >> pathlist
done
# Also include a manifest of the closures in a format suitable for
# nix-store --load-db.
if [ -n "$object" ]; then
printRegistration=1 perl $pathsFromGraph closure-* > nix-path-registration
echo "nix-path-registration=nix-path-registration" >> pathlist
fi
# Add symlinks to the top-level store objects.
for ((n = 0; n < ${#objects[*]}; n++)); do
object=${objects[$n]}
symlink=${symlinks[$n]}
if test "$symlink" != "none"; then
mkdir -p $(dirname ./$symlink)
ln -s $object ./$symlink
echo "$symlink=./$symlink" >> pathlist
fi
done
# !!! what does this do?
cat pathlist | sed -e 's/=\(.*\)=\(.*\)=/\\=\1=\2\\=/' | tee pathlist.safer
ensureDir $out/iso
genCommand="genisoimage -iso-level 4 -r -J $bootFlags -hide-rr-moved -graft-points -path-list pathlist.safer ${volumeID:+-V $volumeID}"
if test -z "$compressImage"; then
$genCommand -o $out/iso/$isoName
else
$genCommand | bzip2 > $out/iso/$isoName.bz2
fi
ensureDir $out/nix-support
echo $system > $out/nix-support/system

View File

@ -0,0 +1,30 @@
{ stdenv, squashfsTools, perl, pathsFromGraph
, # The root directory of the squashfs filesystem is filled with the
# closures of the Nix store paths listed here.
storeContents ? []
}:
stdenv.mkDerivation {
name = "squashfs.img";
buildInputs = [perl squashfsTools];
# For obtaining the closure of `storeContents'.
exportReferencesGraph =
map (x: [("closure-" + baseNameOf x) x]) storeContents;
buildCommand =
''
# Add the closures of the top-level store objects.
storePaths=$(perl ${pathsFromGraph} closure-*)
# Also include a manifest of the closures in a format suitable
# for nix-store --load-db.
printRegistration=1 perl ${pathsFromGraph} closure-* > nix-path-registration
# Generate the squashfs image.
mksquashfs nix-path-registration $storePaths $out \
-keep-as-directory -all-root
'';
}

View File

@ -0,0 +1,38 @@
{ stdenv, perl, xz, pathsFromGraph
, # The file name of the resulting tarball
fileName ? "nixos-system-${stdenv.system}"
, # The files and directories to be placed in the tarball.
# This is a list of attribute sets {source, target} where `source'
# is the file system object (regular file or directory) to be
# grafted in the file system at path `target'.
contents
, # In addition to `contents', the closure of the store paths listed
# in `packages' are also placed in the Nix store of the tarball. This is
# a list of attribute sets {object, symlink} where `object' if a
# store path whose closure will be copied, and `symlink' is a
# symlink to `object' that will be added to the tarball.
storeContents ? []
}:
stdenv.mkDerivation {
name = "tarball";
builder = ./make-system-tarball.sh;
buildInputs = [perl xz];
inherit fileName pathsFromGraph;
# !!! should use XML.
sources = map (x: x.source) contents;
targets = map (x: x.target) contents;
# !!! should use XML.
objects = map (x: x.object) storeContents;
symlinks = map (x: x.symlink) storeContents;
# For obtaining the closure of `storeContents'.
exportReferencesGraph =
map (x: [("closure-" + baseNameOf x.object) x.object]) storeContents;
}

View File

@ -0,0 +1,58 @@
source $stdenv/setup
set -x
sources_=($sources)
targets_=($targets)
echo $objects
objects=($objects)
symlinks=($symlinks)
# Remove the initial slash from a path, since genisofs likes it that way.
stripSlash() {
res="$1"
if test "${res:0:1}" = /; then res=${res:1}; fi
}
touch pathlist
# Add the individual files.
for ((i = 0; i < ${#targets_[@]}; i++)); do
stripSlash "${targets_[$i]}"
mkdir -p "$(dirname "$res")"
cp -a "${sources_[$i]}" "$res"
done
# Add the closures of the top-level store objects.
mkdir -p nix/store
storePaths=$(perl $pathsFromGraph closure-*)
for i in $storePaths; do
cp -a "$i" "${i:1}"
done
# TODO tar ruxo
# Also include a manifest of the closures in a format suitable for
# nix-store --load-db.
printRegistration=1 perl $pathsFromGraph closure-* > nix-path-registration
# Add symlinks to the top-level store objects.
for ((n = 0; n < ${#objects[*]}; n++)); do
object=${objects[$n]}
symlink=${symlinks[$n]}
if test "$symlink" != "none"; then
mkdir -p $(dirname ./$symlink)
ln -s $object ./$symlink
fi
done
ensureDir $out/tarball
tar cvJf $out/tarball/$fileName.tar.xz *
ensureDir $out/nix-support
echo $system > $out/nix-support/system
echo "file system-tarball $out/tarball/$fileName.tar.xz" > $out/nix-support/hydra-build-products

8
nixos/lib/nixpkgs.nix Normal file
View File

@ -0,0 +1,8 @@
/* Terrible backward compatibility hack to get the path to Nixpkgs
from here. Usually, that's the relative path ../... However,
when using the NixOS channel, <nixos> resolves to a symlink to
nixpkgs/nixos, so ../.. doesn't resolve to the top-level Nixpkgs
directory but one above it. So check for that situation. */
if builtins.pathExists ../../.version then import ../..
else if builtins.pathExists ../../nixpkgs then import ../../nixpkgs
else abort "Can't find Nixpkgs, please set NIX_PATH=nixpkgs=/path/to/nixpkgs."

10
nixos/lib/qemu-flags.nix Normal file
View File

@ -0,0 +1,10 @@
# QEMU flags shared between various Nix expressions.
{
qemuNICFlags = nic: net: machine:
[ "-net nic,vlan=${toString nic},macaddr=52:54:00:12:${toString net}:${toString machine},model=virtio"
"-net vde,vlan=${toString nic},sock=$QEMU_VDE_SOCKET_${toString net}"
];
}

View File

@ -0,0 +1,70 @@
package Logger;
use strict;
use Thread::Queue;
use XML::Writer;
sub new {
my ($class) = @_;
my $logFile = defined $ENV{LOGFILE} ? "$ENV{LOGFILE}" : "/dev/null";
my $log = new XML::Writer(OUTPUT => new IO::File(">$logFile"));
my $self = {
log => $log,
logQueue => Thread::Queue->new()
};
$self->{log}->startTag("logfile");
bless $self, $class;
return $self;
}
sub close {
my ($self) = @_;
$self->{log}->endTag("logfile");
$self->{log}->end;
}
sub drainLogQueue {
my ($self) = @_;
while (defined (my $item = $self->{logQueue}->dequeue_nb())) {
$self->{log}->dataElement("line", sanitise($item->{msg}), 'machine' => $item->{machine}, 'type' => 'serial');
}
}
sub maybePrefix {
my ($msg, $attrs) = @_;
$msg = $attrs->{machine} . ": " . $msg if defined $attrs->{machine};
return $msg;
}
sub nest {
my ($self, $msg, $coderef, $attrs) = @_;
print STDERR maybePrefix("$msg\n", $attrs);
$self->{log}->startTag("nest");
$self->{log}->dataElement("head", $msg, %{$attrs});
$self->drainLogQueue();
eval { &$coderef };
my $res = $@;
$self->drainLogQueue();
$self->{log}->endTag("nest");
die $@ if $@;
}
sub sanitise {
my ($s) = @_;
$s =~ s/[[:cntrl:]\xff]//g;
return $s;
}
sub log {
my ($self, $msg, $attrs) = @_;
chomp $msg;
print STDERR maybePrefix("$msg\n", $attrs);
$self->drainLogQueue();
$self->{log}->dataElement("line", $msg, %{$attrs});
}
1;

View File

@ -0,0 +1,568 @@
package Machine;
use strict;
use threads;
use Socket;
use IO::Handle;
use POSIX qw(dup2);
use FileHandle;
use Cwd;
use File::Basename;
use File::Path qw(make_path);
my $showGraphics = defined $ENV{'DISPLAY'};
my $sharedDir;
sub new {
my ($class, $args) = @_;
my $startCommand = $args->{startCommand};
my $name = $args->{name};
if (!$name) {
$startCommand =~ /run-(.*)-vm$/ if defined $startCommand;
$name = $1 || "machine";
}
if (!$startCommand) {
# !!! merge with qemu-vm.nix.
$startCommand =
"qemu-kvm -m 384 " .
"-net nic,model=virtio \$QEMU_OPTS ";
my $iface = $args->{hdaInterface} || "virtio";
$startCommand .= "-drive file=" . Cwd::abs_path($args->{hda}) . ",if=$iface,boot=on,werror=report "
if defined $args->{hda};
$startCommand .= "-cdrom $args->{cdrom} "
if defined $args->{cdrom};
$startCommand .= $args->{qemuFlags} || "";
} else {
$startCommand = Cwd::abs_path $startCommand;
}
my $tmpDir = $ENV{'TMPDIR'} || "/tmp";
unless (defined $sharedDir) {
$sharedDir = $tmpDir . "/xchg-shared";
make_path($sharedDir, { mode => 0700, owner => $< });
}
my $allowReboot = 0;
$allowReboot = $args->{allowReboot} if defined $args->{allowReboot};
my $self = {
startCommand => $startCommand,
name => $name,
allowReboot => $allowReboot,
booted => 0,
pid => 0,
connected => 0,
socket => undef,
stateDir => "$tmpDir/vm-state-$name",
monitor => undef,
log => $args->{log},
redirectSerial => $args->{redirectSerial} // 1,
};
mkdir $self->{stateDir}, 0700;
bless $self, $class;
return $self;
}
sub log {
my ($self, $msg) = @_;
$self->{log}->log($msg, { machine => $self->{name} });
}
sub nest {
my ($self, $msg, $coderef, $attrs) = @_;
$self->{log}->nest($msg, $coderef, { %{$attrs || {}}, machine => $self->{name} });
}
sub name {
my ($self) = @_;
return $self->{name};
}
sub stateDir {
my ($self) = @_;
return $self->{stateDir};
}
sub start {
my ($self) = @_;
return if $self->{booted};
$self->log("starting vm");
# Create a socket pair for the serial line input/output of the VM.
my ($serialP, $serialC);
socketpair($serialP, $serialC, PF_UNIX, SOCK_STREAM, 0) or die;
# Create a Unix domain socket to which QEMU's monitor will connect.
my $monitorPath = $self->{stateDir} . "/monitor";
unlink $monitorPath;
my $monitorS;
socket($monitorS, PF_UNIX, SOCK_STREAM, 0) or die;
bind($monitorS, sockaddr_un($monitorPath)) or die "cannot bind monitor socket: $!";
listen($monitorS, 1) or die;
# Create a Unix domain socket to which the root shell in the guest will connect.
my $shellPath = $self->{stateDir} . "/shell";
unlink $shellPath;
my $shellS;
socket($shellS, PF_UNIX, SOCK_STREAM, 0) or die;
bind($shellS, sockaddr_un($shellPath)) or die "cannot bind shell socket: $!";
listen($shellS, 1) or die;
# Start the VM.
my $pid = fork();
die if $pid == -1;
if ($pid == 0) {
close $serialP;
close $monitorS;
close $shellS;
if ($self->{redirectSerial}) {
open NUL, "</dev/null" or die;
dup2(fileno(NUL), fileno(STDIN));
dup2(fileno($serialC), fileno(STDOUT));
dup2(fileno($serialC), fileno(STDERR));
}
$ENV{TMPDIR} = $self->{stateDir};
$ENV{SHARED_DIR} = $sharedDir;
$ENV{USE_TMPDIR} = 1;
$ENV{QEMU_OPTS} =
($self->{allowReboot} ? "" : "-no-reboot ") .
"-monitor unix:./monitor -chardev socket,id=shell,path=./shell " .
"-device virtio-serial -device virtconsole,chardev=shell " .
($showGraphics ? "-serial stdio" : "-nographic") . " " . ($ENV{QEMU_OPTS} || "");
chdir $self->{stateDir} or die;
exec $self->{startCommand};
die "running VM script: $!";
}
# Process serial line output.
close $serialC;
threads->create(\&processSerialOutput, $self, $serialP)->detach;
sub processSerialOutput {
my ($self, $serialP) = @_;
while (<$serialP>) {
chomp;
s/\r$//;
print STDERR $self->{name}, "# $_\n";
$self->{log}->{logQueue}->enqueue({msg => $_, machine => $self->{name}}); # !!!
}
}
eval {
local $SIG{CHLD} = sub { die "QEMU died prematurely\n"; };
# Wait until QEMU connects to the monitor.
accept($self->{monitor}, $monitorS) or die;
# Wait until QEMU connects to the root shell socket. QEMU
# does so immediately; this doesn't mean that the root shell
# has connected yet inside the guest.
accept($self->{socket}, $shellS) or die;
$self->{socket}->autoflush(1);
};
die "$@" if $@;
$self->waitForMonitorPrompt;
$self->log("QEMU running (pid $pid)");
$self->{pid} = $pid;
$self->{booted} = 1;
}
# Send a command to the monitor and wait for it to finish. TODO: QEMU
# also has a JSON-based monitor interface now, but it doesn't support
# all commands yet. We should use it once it does.
sub sendMonitorCommand {
my ($self, $command) = @_;
$self->log("sending monitor command: $command");
syswrite $self->{monitor}, "$command\n";
return $self->waitForMonitorPrompt;
}
# Wait until the monitor sends "(qemu) ".
sub waitForMonitorPrompt {
my ($self) = @_;
my $res = "";
my $s;
while (sysread($self->{monitor}, $s, 1024)) {
$res .= $s;
last if $res =~ s/\(qemu\) $//;
}
return $res;
}
# Call the given code reference repeatedly, with 1 second intervals,
# until it returns 1 or a timeout is reached.
sub retry {
my ($coderef) = @_;
my $n;
for ($n = 0; $n < 900; $n++) {
return if &$coderef;
sleep 1;
}
die "action timed out after $n seconds";
}
sub connect {
my ($self) = @_;
return if $self->{connected};
$self->nest("waiting for the VM to finish booting", sub {
$self->start;
local $SIG{ALRM} = sub { die "timed out waiting for the VM to connect\n"; };
alarm 300;
readline $self->{socket} or die "the VM quit before connecting\n";
alarm 0;
$self->log("connected to guest root shell");
$self->{connected} = 1;
});
}
sub waitForShutdown {
my ($self) = @_;
return unless $self->{booted};
$self->nest("waiting for the VM to power off", sub {
waitpid $self->{pid}, 0;
$self->{pid} = 0;
$self->{booted} = 0;
$self->{connected} = 0;
});
}
sub isUp {
my ($self) = @_;
return $self->{booted} && $self->{connected};
}
sub execute_ {
my ($self, $command) = @_;
$self->connect;
print { $self->{socket} } ("( $command ); echo '|!=EOF' \$?\n");
my $out = "";
while (1) {
my $line = readline($self->{socket});
die "connection to VM lost unexpectedly" unless defined $line;
#$self->log("got line: $line");
if ($line =~ /^(.*)\|\!\=EOF\s+(\d+)$/) {
$out .= $1;
$self->log("exit status $2");
return ($2, $out);
}
$out .= $line;
}
}
sub execute {
my ($self, $command) = @_;
my @res;
$self->nest("running command: $command", sub {
@res = $self->execute_($command);
});
return @res;
}
sub succeed {
my ($self, @commands) = @_;
my $res;
foreach my $command (@commands) {
$self->nest("must succeed: $command", sub {
my ($status, $out) = $self->execute_($command);
if ($status != 0) {
$self->log("output: $out");
die "command `$command' did not succeed (exit code $status)\n";
}
$res .= $out;
});
}
return $res;
}
sub mustSucceed {
succeed @_;
}
sub waitUntilSucceeds {
my ($self, $command) = @_;
$self->nest("waiting for success: $command", sub {
retry sub {
my ($status, $out) = $self->execute($command);
return 1 if $status == 0;
};
});
}
sub waitUntilFails {
my ($self, $command) = @_;
$self->nest("waiting for failure: $command", sub {
retry sub {
my ($status, $out) = $self->execute($command);
return 1 if $status != 0;
};
});
}
sub fail {
my ($self, $command) = @_;
$self->nest("must fail: $command", sub {
my ($status, $out) = $self->execute_($command);
die "command `$command' unexpectedly succeeded"
if $status == 0;
});
}
sub mustFail {
fail @_;
}
sub getUnitInfo {
my ($self, $unit) = @_;
my ($status, $lines) = $self->execute("systemctl --no-pager show '$unit'");
return undef if $status != 0;
my $info = {};
foreach my $line (split '\n', $lines) {
$line =~ /^([^=]+)=(.*)$/ or next;
$info->{$1} = $2;
}
return $info;
}
# Wait for a systemd unit to reach the "active" state.
sub waitForUnit {
my ($self, $unit) = @_;
$self->nest("waiting for unit $unit", sub {
retry sub {
my $info = $self->getUnitInfo($unit);
my $state = $info->{ActiveState};
die "unit $unit reached state $state\n" if $state eq "failed";
return 1 if $state eq "active";
};
});
}
sub waitForJob {
my ($self, $jobName) = @_;
return $self->waitForUnit($jobName);
}
# Wait until the specified file exists.
sub waitForFile {
my ($self, $fileName) = @_;
$self->nest("waiting for file $fileName", sub {
retry sub {
my ($status, $out) = $self->execute("test -e $fileName");
return 1 if $status == 0;
}
});
}
sub startJob {
my ($self, $jobName) = @_;
$self->execute("systemctl start $jobName");
# FIXME: check result
}
sub stopJob {
my ($self, $jobName) = @_;
$self->execute("systemctl stop $jobName");
}
# Wait until the machine is listening on the given TCP port.
sub waitForOpenPort {
my ($self, $port) = @_;
$self->nest("waiting for TCP port $port", sub {
retry sub {
my ($status, $out) = $self->execute("nc -z localhost $port");
return 1 if $status == 0;
}
});
}
# Wait until the machine is not listening on the given TCP port.
sub waitForClosedPort {
my ($self, $port) = @_;
retry sub {
my ($status, $out) = $self->execute("nc -z localhost $port");
return 1 if $status != 0;
}
}
sub shutdown {
my ($self) = @_;
return unless $self->{booted};
print { $self->{socket} } ("poweroff\n");
$self->waitForShutdown;
}
sub crash {
my ($self) = @_;
return unless $self->{booted};
$self->log("forced crash");
$self->sendMonitorCommand("quit");
$self->waitForShutdown;
}
# Make the machine unreachable by shutting down eth1 (the multicast
# interface used to talk to the other VMs). We keep eth0 up so that
# the test driver can continue to talk to the machine.
sub block {
my ($self) = @_;
$self->sendMonitorCommand("set_link virtio-net-pci.1 off");
}
# Make the machine reachable.
sub unblock {
my ($self) = @_;
$self->sendMonitorCommand("set_link virtio-net-pci.1 on");
}
# Take a screenshot of the X server on :0.0.
sub screenshot {
my ($self, $filename) = @_;
my $dir = $ENV{'out'} || Cwd::abs_path(".");
$filename = "$dir/${filename}.png" if $filename =~ /^\w+$/;
my $tmp = "${filename}.ppm";
my $name = basename($filename);
$self->nest("making screenshot $name", sub {
$self->sendMonitorCommand("screendump $tmp");
system("convert $tmp ${filename}") == 0
or die "cannot convert screenshot";
unlink $tmp;
}, { image => $name } );
}
# Wait until it is possible to connect to the X server. Note that
# testing the existence of /tmp/.X11-unix/X0 is insufficient.
sub waitForX {
my ($self, $regexp) = @_;
$self->nest("waiting for the X11 server", sub {
retry sub {
my ($status, $out) = $self->execute("xwininfo -root > /dev/null 2>&1");
return 1 if $status == 0;
}
});
}
sub getWindowNames {
my ($self) = @_;
my $res = $self->mustSucceed(
q{xwininfo -root -tree | sed 's/.*0x[0-9a-f]* \"\([^\"]*\)\".*/\1/; t; d'});
return split /\n/, $res;
}
sub waitForWindow {
my ($self, $regexp) = @_;
$self->nest("waiting for a window to appear", sub {
retry sub {
my @names = $self->getWindowNames;
foreach my $n (@names) {
return 1 if $n =~ /$regexp/;
}
}
});
}
sub copyFileFromHost {
my ($self, $from, $to) = @_;
my $s = `cat $from` or die;
$self->mustSucceed("echo '$s' > $to"); # !!! escaping
}
sub sendKeys {
my ($self, @keys) = @_;
foreach my $key (@keys) {
$key = "spc" if $key eq " ";
$key = "ret" if $key eq "\n";
$self->sendMonitorCommand("sendkey $key");
}
}
sub sendChars {
my ($self, $chars) = @_;
$self->nest("sending keys $chars", sub {
$self->sendKeys(split //, $chars);
});
}
# Sleep N seconds (in virtual guest time, not real time).
sub sleep {
my ($self, $time) = @_;
$self->succeed("sleep $time");
}
# Forward a TCP port on the host to a TCP port on the guest. Useful
# during interactive testing.
sub forwardPort {
my ($self, $hostPort, $guestPort) = @_;
$hostPort = 8080 unless defined $hostPort;
$guestPort = 80 unless defined $guestPort;
$self->sendMonitorCommand("hostfwd_add tcp::$hostPort-:$guestPort");
}
1;

View File

@ -0,0 +1,135 @@
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method='html' encoding="UTF-8"
doctype-public="-//W3C//DTD HTML 4.01//EN"
doctype-system="http://www.w3.org/TR/html4/strict.dtd" />
<xsl:template match="logfile">
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/jquery-ui.min.js"></script>
<script type="text/javascript" src="treebits.js" />
<link rel="stylesheet" href="logfile.css" type="text/css" />
<title>Log File</title>
</head>
<body>
<h1>VM build log</h1>
<p>
<a href="javascript:" class="logTreeExpandAll">Expand all</a> |
<a href="javascript:" class="logTreeCollapseAll">Collapse all</a>
</p>
<ul class='toplevel'>
<xsl:for-each select='line|nest'>
<li>
<xsl:apply-templates select='.'/>
</li>
</xsl:for-each>
</ul>
<xsl:if test=".//*[@image]">
<h1>Screenshots</h1>
<ul class="vmScreenshots">
<xsl:for-each select='.//*[@image]'>
<li><a href="{@image}"><xsl:value-of select="@image" /></a></li>
</xsl:for-each>
</ul>
</xsl:if>
</body>
</html>
</xsl:template>
<xsl:template match="nest">
<!-- The tree should be collapsed by default if all children are
unimportant or if the header is unimportant. -->
<xsl:variable name="collapsed" select="not(./head[@expanded]) and count(.//*[@error]) = 0"/>
<xsl:variable name="style"><xsl:if test="$collapsed">display: none;</xsl:if></xsl:variable>
<xsl:if test="line|nest">
<a href="javascript:" class="logTreeToggle">
<xsl:choose>
<xsl:when test="$collapsed"><xsl:text>+</xsl:text></xsl:when>
<xsl:otherwise><xsl:text>-</xsl:text></xsl:otherwise>
</xsl:choose>
</a>
<xsl:text> </xsl:text>
</xsl:if>
<xsl:apply-templates select='head'/>
<!-- Be careful to only generate <ul>s if there are <li>s, otherwise its malformed. -->
<xsl:if test="line|nest">
<ul class='nesting' style="{$style}">
<xsl:for-each select='line|nest'>
<!-- Is this the last line? If so, mark it as such so that it
can be rendered differently. -->
<xsl:variable name="class"><xsl:choose><xsl:when test="position() != last()">line</xsl:when><xsl:otherwise>lastline</xsl:otherwise></xsl:choose></xsl:variable>
<li class='{$class}'>
<span class='lineconn' />
<span class='linebody'>
<xsl:apply-templates select='.'/>
</span>
</li>
</xsl:for-each>
</ul>
</xsl:if>
</xsl:template>
<xsl:template match="head|line">
<code>
<xsl:if test="@error">
<xsl:attribute name="class">errorLine</xsl:attribute>
</xsl:if>
<xsl:if test="@warning">
<xsl:attribute name="class">warningLine</xsl:attribute>
</xsl:if>
<xsl:if test="@priority = 3">
<xsl:attribute name="class">prio3</xsl:attribute>
</xsl:if>
<xsl:if test="@type = 'serial'">
<xsl:attribute name="class">serial</xsl:attribute>
</xsl:if>
<xsl:if test="@machine">
<xsl:choose>
<xsl:when test="@type = 'serial'">
<span class="machine"><xsl:value-of select="@machine"/># </span>
</xsl:when>
<xsl:otherwise>
<span class="machine"><xsl:value-of select="@machine"/>: </span>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
<xsl:choose>
<xsl:when test="@image">
<a href="{@image}"><xsl:apply-templates/></a>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</code>
</xsl:template>
<xsl:template match="storeref">
<em class='storeref'>
<span class='popup'><xsl:apply-templates/></span>
<span class='elided'>/...</span><xsl:apply-templates select='name'/><xsl:apply-templates select='path'/>
</em>
</xsl:template>
</xsl:stylesheet>

View File

@ -0,0 +1,129 @@
body {
font-family: sans-serif;
background: white;
}
h1
{
color: #005aa0;
font-size: 180%;
}
a {
text-decoration: none;
}
ul.nesting, ul.toplevel {
padding: 0;
margin: 0;
}
ul.toplevel {
list-style-type: none;
}
.line, .head {
padding-top: 0em;
}
ul.nesting li.line, ul.nesting li.lastline {
position: relative;
list-style-type: none;
}
ul.nesting li.line {
padding-left: 2.0em;
}
ul.nesting li.lastline {
padding-left: 2.1em; /* for the 0.1em border-left in .lastline > .lineconn */
}
li.line {
border-left: 0.1em solid #6185a0;
}
li.line > span.lineconn, li.lastline > span.lineconn {
position: absolute;
height: 0.65em;
left: 0em;
width: 1.5em;
border-bottom: 0.1em solid #6185a0;
}
li.lastline > span.lineconn {
border-left: 0.1em solid #6185a0;
}
em.storeref {
color: #500000;
position: relative;
width: 100%;
}
em.storeref:hover {
background-color: #eeeeee;
}
*.popup {
display: none;
/* background: url('http://losser.st-lab.cs.uu.nl/~mbravenb/menuback.png') repeat; */
background: #ffffcd;
border: solid #555555 1px;
position: absolute;
top: 0em;
left: 0em;
margin: 0;
padding: 0;
z-index: 100;
}
em.storeref:hover span.popup {
display: inline;
width: 40em;
}
.logTreeToggle {
text-decoration: none;
font-family: monospace;
font-size: larger;
}
.errorLine {
color: #ff0000;
font-weight: bold;
}
.warningLine {
color: darkorange;
font-weight: bold;
}
.prio3 {
font-style: italic;
}
code {
white-space: pre-wrap;
}
.serial {
color: #56115c;
}
.machine {
color: #002399;
font-style: italic;
}
ul.vmScreenshots {
padding-left: 1em;
}
ul.vmScreenshots li {
font-family: monospace;
list-style: square;
}

View File

@ -0,0 +1,178 @@
#! /somewhere/perl -w
use strict;
use Machine;
use Term::ReadLine;
use IO::File;
use IO::Pty;
use Logger;
use Cwd;
use POSIX qw(_exit dup2);
$SIG{PIPE} = 'IGNORE'; # because Unix domain sockets may die unexpectedly
STDERR->autoflush(1);
my $log = new Logger;
# Start vde_switch for each network required by the test.
my %vlans;
foreach my $vlan (split / /, $ENV{VLANS} || "") {
next if defined $vlans{$vlan};
# Start vde_switch as a child process. We don't run it in daemon
# mode because we want the child process to be cleaned up when we
# die. Since we have to make sure that the control socket is
# ready, we send a dummy command to vde_switch (via stdin) and
# wait for a reply. Note that vde_switch requires stdin to be a
# TTY, so we create one.
$log->log("starting VDE switch for network $vlan");
my $socket = Cwd::abs_path "./vde$vlan.ctl";
my $pty = new IO::Pty;
my ($stdoutR, $stdoutW); pipe $stdoutR, $stdoutW;
my $pid = fork(); die "cannot fork" unless defined $pid;
if ($pid == 0) {
dup2(fileno($pty->slave), 0);
dup2(fileno($stdoutW), 1);
exec "vde_switch -s $socket" or _exit(1);
}
close $stdoutW;
print $pty "version\n";
readline $stdoutR or die "cannot start vde_switch";
$ENV{"QEMU_VDE_SOCKET_$vlan"} = $socket;
$vlans{$vlan} = $pty;
die unless -e "$socket/ctl";
}
my %vms;
my $context = "";
sub createMachine {
my ($args) = @_;
my $vm = Machine->new({%{$args}, log => $log, redirectSerial => ($ENV{USE_SERIAL} // "0") ne "1"});
$vms{$vm->name} = $vm;
return $vm;
}
foreach my $vmScript (@ARGV) {
my $vm = createMachine({startCommand => $vmScript});
$context .= "my \$" . $vm->name . " = \$vms{'" . $vm->name . "'}; ";
}
sub startAll {
$log->nest("starting all VMs", sub {
$_->start foreach values %vms;
});
}
# Wait until all VMs have terminated.
sub joinAll {
$log->nest("waiting for all VMs to finish", sub {
$_->waitForShutdown foreach values %vms;
});
}
# In interactive tests, this allows the non-interactive test script to
# be executed conveniently.
sub testScript {
eval "$context $ENV{testScript};\n";
warn $@ if $@;
}
my $nrTests = 0;
my $nrSucceeded = 0;
sub subtest {
my ($name, $coderef) = @_;
$log->nest("subtest: $name", sub {
$nrTests++;
eval { &$coderef };
if ($@) {
$log->log("error: $@", { error => 1 });
} else {
$nrSucceeded++;
}
});
}
sub runTests {
if (defined $ENV{tests}) {
$log->nest("running the VM test script", sub {
eval "$context $ENV{tests}";
if ($@) {
$log->log("error: $@", { error => 1 });
die $@;
}
}, { expanded => 1 });
} else {
my $term = Term::ReadLine->new('nixos-vm-test');
$term->ReadHistory;
while (defined ($_ = $term->readline("> "))) {
eval "$context $_\n";
warn $@ if $@;
}
$term->WriteHistory;
}
# Copy the kernel coverage data for each machine, if the kernel
# has been compiled with coverage instrumentation.
$log->nest("collecting coverage data", sub {
foreach my $vm (values %vms) {
my $gcovDir = "/sys/kernel/debug/gcov";
next unless $vm->isUp();
my ($status, $out) = $vm->execute("test -e $gcovDir");
next if $status != 0;
# Figure out where to put the *.gcda files so that the
# report generator can find the corresponding kernel
# sources.
my $kernelDir = $vm->mustSucceed("echo \$(dirname \$(readlink -f /run/current-system/kernel))/.build/linux-*");
chomp $kernelDir;
my $coverageDir = "/tmp/xchg/coverage-data/$kernelDir";
# Copy all the *.gcda files.
$vm->execute("for d in $gcovDir/nix/store/*/.build/linux-*; do for i in \$(cd \$d && find -name '*.gcda'); do echo \$i; mkdir -p $coverageDir/\$(dirname \$i); cp -v \$d/\$i $coverageDir/\$i; done; done");
}
});
if ($nrTests != 0) {
$log->log("$nrSucceeded out of $nrTests tests succeeded",
($nrSucceeded < $nrTests ? { error => 1 } : { }));
}
}
# Create an empty raw virtual disk with the given name and size (in
# MiB).
sub createDisk {
my ($name, $size) = @_;
system("qemu-img create -f raw $name ${size}M") == 0
or die "cannot create image of size $size";
}
END {
$log->nest("cleaning up", sub {
foreach my $vm (values %vms) {
if ($vm->{pid}) {
$log->log("killing " . $vm->{name} . " (pid " . $vm->{pid} . ")");
kill 9, $vm->{pid};
}
}
});
$log->close();
}
runTests;
exit ($nrSucceeded < $nrTests ? 1 : 0);

View File

@ -0,0 +1,30 @@
$(document).ready(function() {
/* When a toggle is clicked, show or hide the subtree. */
$(".logTreeToggle").click(function() {
if ($(this).siblings("ul:hidden").length != 0) {
$(this).siblings("ul").show();
$(this).text("-");
} else {
$(this).siblings("ul").hide();
$(this).text("+");
}
});
/* Implementation of the expand all link. */
$(".logTreeExpandAll").click(function() {
$(".logTreeToggle", $(this).parent().siblings(".toplevel")).map(function() {
$(this).siblings("ul").show();
$(this).text("-");
});
});
/* Implementation of the collapse all link. */
$(".logTreeCollapseAll").click(function() {
$(".logTreeToggle", $(this).parent().siblings(".toplevel")).map(function() {
$(this).siblings("ul").hide();
$(this).text("+");
});
});
});

249
nixos/lib/testing.nix Normal file
View File

@ -0,0 +1,249 @@
{ system, minimal ? false }:
with import ./build-vms.nix { inherit system minimal; };
with pkgs;
rec {
inherit pkgs;
testDriver = stdenv.mkDerivation {
name = "nixos-test-driver";
buildInputs = [ makeWrapper perl ];
unpackPhase = "true";
installPhase =
''
mkdir -p $out/bin
cp ${./test-driver/test-driver.pl} $out/bin/nixos-test-driver
chmod u+x $out/bin/nixos-test-driver
libDir=$out/lib/perl5/site_perl
mkdir -p $libDir
cp ${./test-driver/Machine.pm} $libDir/Machine.pm
cp ${./test-driver/Logger.pm} $libDir/Logger.pm
wrapProgram $out/bin/nixos-test-driver \
--prefix PATH : "${pkgs.qemu_kvm}/bin:${pkgs.vde2}/bin:${imagemagick}/bin:${coreutils}/bin" \
--prefix PERL5LIB : "${lib.makePerlPath [ perlPackages.TermReadLineGnu perlPackages.XMLWriter perlPackages.IOTty ]}:$out/lib/perl5/site_perl"
'';
};
# Run an automated test suite in the given virtual network.
# `driver' is the script that runs the network.
runTests = driver:
stdenv.mkDerivation {
name = "vm-test-run";
requiredSystemFeatures = [ "kvm" "nixos-test" ];
buildInputs = [ pkgs.libxslt ];
buildCommand =
''
mkdir -p $out/nix-support
LOGFILE=$out/log.xml tests='eval $ENV{testScript}; die $@ if $@;' ${driver}/bin/nixos-test-driver || failed=1
# Generate a pretty-printed log.
xsltproc --output $out/log.html ${./test-driver/log2html.xsl} $out/log.xml
ln -s ${./test-driver/logfile.css} $out/logfile.css
ln -s ${./test-driver/treebits.js} $out/treebits.js
touch $out/nix-support/hydra-build-products
echo "report testlog $out log.html" >> $out/nix-support/hydra-build-products
for i in */xchg/coverage-data; do
mkdir -p $out/coverage-data
mv $i $out/coverage-data/$(dirname $(dirname $i))
done
[ -z "$failed" ] || touch $out/nix-support/failed
''; # */
};
# Generate a coverage report from the coverage data produced by
# runTests.
makeReport = x: runCommand "report" { buildInputs = [rsync]; }
''
mkdir -p $TMPDIR/gcov/
for d in ${x}/coverage-data/*; do
echo "doing $d"
[ -n "$(ls -A "$d")" ] || continue
for i in $(cd $d/nix/store && ls); do
if ! test -e $TMPDIR/gcov/nix/store/$i; then
echo "copying $i"
mkdir -p $TMPDIR/gcov/$(echo $i | cut -c34-)
rsync -rv /nix/store/$i/.build/* $TMPDIR/gcov/
fi
done
chmod -R u+w $TMPDIR/gcov
find $TMPDIR/gcov -name "*.gcda" -exec rm {} \;
for i in $(cd $d/nix/store && ls); do
rsync -rv $d/nix/store/$i/.build/* $TMPDIR/gcov/
done
find $TMPDIR/gcov -name "*.gcda" -exec chmod 644 {} \;
echo "producing info..."
${pkgs.lcov}/bin/geninfo --ignore-errors source,gcov $TMPDIR/gcov --output-file $TMPDIR/app.info
cat $TMPDIR/app.info >> $TMPDIR/full.info
done
echo "making report..."
mkdir -p $out/coverage
${pkgs.lcov}/bin/genhtml --show-details $TMPDIR/full.info -o $out/coverage
cp $TMPDIR/full.info $out/coverage/
mkdir -p $out/nix-support
cat ${x}/nix-support/hydra-build-products >> $out/nix-support/hydra-build-products
echo "report coverage $out/coverage" >> $out/nix-support/hydra-build-products
[ ! -e ${x}/nix-support/failed ] || touch $out/nix-support/failed
''; # */
makeTest = testFun: complete (call testFun);
makeTests = testsFun: lib.mapAttrs (name: complete) (call testsFun);
apply = makeTest; # compatibility
call = f: f { inherit pkgs system; };
complete = t: t // rec {
nodes = buildVirtualNetwork (
if t ? nodes then t.nodes else
if t ? machine then { machine = t.machine; }
else { } );
testScript =
# Call the test script with the computed nodes.
if builtins.isFunction t.testScript
then t.testScript { inherit nodes; }
else t.testScript;
vlans = map (m: m.config.virtualisation.vlans) (lib.attrValues nodes);
vms = map (m: m.config.system.build.vm) (lib.attrValues nodes);
# Generate onvenience wrappers for running the test driver
# interactively with the specified network, and for starting the
# VMs from the command line.
driver = runCommand "nixos-test-driver"
{ buildInputs = [ makeWrapper];
inherit testScript;
preferLocalBuild = true;
}
''
mkdir -p $out/bin
echo "$testScript" > $out/test-script
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/
vms="$(for i in ${toString vms}; do echo $i/bin/run-*-vm; done)"
wrapProgram $out/bin/nixos-test-driver \
--add-flags "$vms" \
--run "testScript=\"\$(cat $out/test-script)\"" \
--set testScript '"$testScript"' \
--set VLANS '"${toString vlans}"'
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-run-vms
wrapProgram $out/bin/nixos-run-vms \
--add-flags "$vms" \
--set tests '"startAll; joinAll;"' \
--set VLANS '"${toString vlans}"' \
${lib.optionalString (builtins.length vms == 1) "--set USE_SERIAL 1"}
''; # "
test = runTests driver;
report = makeReport test;
};
runInMachine =
{ drv
, machine
, preBuild ? ""
, postBuild ? ""
, ... # ???
}:
let
vm = buildVM { }
[ machine
{ key = "run-in-machine";
networking.hostName = "client";
nix.readOnlyStore = false;
}
];
buildrunner = writeText "vm-build" ''
source $1
${coreutils}/bin/mkdir -p $TMPDIR
cd $TMPDIR
$origBuilder $origArgs
exit $?
'';
testscript = ''
startAll;
$client->waitForUnit("multi-user.target");
${preBuild}
$client->succeed("env -i ${pkgs.bash}/bin/bash ${buildrunner} /tmp/xchg/saved-env >&2");
${postBuild}
$client->succeed("sync"); # flush all data before pulling the plug
'';
vmRunCommand = writeText "vm-run" ''
${coreutils}/bin/mkdir $out
${coreutils}/bin/mkdir -p vm-state-client/xchg
export > vm-state-client/xchg/saved-env
export tests='${testscript}'
${testDriver}/bin/nixos-test-driver ${vm.config.system.build.vm}/bin/run-*-vm
''; # */
in
lib.overrideDerivation drv (attrs: {
requiredSystemFeatures = [ "kvm" ];
builder = "${bash}/bin/sh";
args = ["-e" vmRunCommand];
origArgs = attrs.args;
origBuilder = attrs.builder;
});
runInMachineWithX = { require ? [], ... } @ args:
let
client =
{ config, pkgs, ... }:
{
inherit require;
virtualisation.memorySize = 1024;
services.xserver.enable = true;
services.xserver.displayManager.slim.enable = false;
services.xserver.displayManager.auto.enable = true;
services.xserver.windowManager.default = "icewm";
services.xserver.windowManager.icewm.enable = true;
services.xserver.desktopManager.default = "none";
};
in
runInMachine ({
machine = client;
preBuild =
''
$client->waitForX;
'';
} // args);
simpleTest = as: (makeTest ({ ... }: as)).test;
}

10
nixos/lib/utils.nix Normal file
View File

@ -0,0 +1,10 @@
pkgs: with pkgs.lib;
rec {
# Escape a path according to the systemd rules, e.g. /dev/xyzzy
# becomes dev-xyzzy. FIXME: slow.
escapeSystemdPath = s:
replaceChars ["/" "-" " "] ["-" "\\x2d" "\\x20"] (substring 1 (stringLength s) s);
}

View File

@ -0,0 +1,99 @@
{ configuration ? import ../lib/from-env.nix "NIXOS_CONFIG" <nixos-config>
# []: display all options
# [<option names>]: display the selected options
, displayOptions ? [
"hardware.pcmcia.enable"
"environment.systemPackages"
"boot.kernelModules"
"services.udev.packages"
"jobs"
"environment.etc"
"system.activationScripts"
]
}:
# This file is used to generate a dot graph which contains all options and
# there dependencies to track problems and their sources.
let
evalFun = {
extraArgs ? {}
}: import ../lib/eval-config.nix {
modules = [ configuration ];
inherit extraArgs;
};
eval = evalFun {};
inherit (eval) pkgs;
reportNewFailures = old: new: with pkgs.lib;
let
filterChanges =
filter ({fst, snd}:
!(fst.config.success -> snd.config.success)
);
keepNames =
map ({fst, snd}:
assert fst.name == snd.name; snd.name
);
in
keepNames (
filterChanges (
zipLists (collect isOption old) (collect isOption new)
)
);
# Create a list of modules where each module contains only one failling
# options.
introspectionModules = with pkgs.lib;
let
setIntrospection = opt: rec {
name = opt.name;
path = splitString "." opt.name;
config = setAttrByPath path
(throw "Usage introspection of '${name}' by forced failure.");
};
in
map setIntrospection (collect isOption eval.options);
overrideConfig = thrower:
pkgs.lib.recursiveUpdateUntil (path: old: new:
path == thrower.path
) eval.config thrower.config;
graph = with pkgs.lib;
map (thrower: {
option = thrower.name;
usedBy = reportNewFailures eval.options (evalFun {
extraArgs = {
config = overrideConfig thrower;
};
}).options;
}) introspectionModules;
graphToDot = graph: with pkgs.lib; ''
digraph "Option Usages" {
${concatMapStrings ({option, usedBy}:
assert __trace option true;
if displayOptions == [] || elem option displayOptions then
concatMapStrings (user: ''
"${option}" -> "${user}"''
) usedBy
else ""
) graph}
}
'';
in
pkgs.texFunctions.dot2pdf {
dotGraph = pkgs.writeTextFile {
name = "option_usages.dot";
text = graphToDot graph;
};
}

View File

@ -0,0 +1,220 @@
#! /usr/bin/env python
import os
import sys
import time
import argparse
import nixops.util
from nixops import deployment
from boto.ec2.blockdevicemapping import BlockDeviceMapping, BlockDeviceType
import boto.ec2
parser = argparse.ArgumentParser(description='Create an EBS-backed NixOS AMI')
parser.add_argument('--region', dest='region', required=True, help='EC2 region to create the image in')
parser.add_argument('--keep', dest='keep', action='store_true', help='Keep NixOps machine after use')
parser.add_argument('--hvm', dest='hvm', action='store_true', help='Create HVM image')
parser.add_argument('--key', dest='key_name', action='store_true', help='Keypair used for HVM instance creation', default="rob")
args = parser.parse_args()
instance_type = "cc1.4xlarge" if args.hvm else "m1.small"
ebs_size = 8 if args.hvm else 20
# Start a NixOS machine in the given region.
f = open("ebs-creator-config.nix", "w")
f.write('''{{
resources.ec2KeyPairs.keypair.accessKeyId = "logicblox-dev";
resources.ec2KeyPairs.keypair.region = "{0}";
machine =
{{ pkgs, ... }}:
{{
deployment.ec2.accessKeyId = "logicblox-dev";
deployment.ec2.region = "{0}";
deployment.ec2.blockDeviceMapping."/dev/xvdg".size = pkgs.lib.mkOverride 10 {1};
}};
}}
'''.format(args.region, ebs_size))
f.close()
db = deployment.open_database(deployment.get_default_state_file())
try:
depl = deployment.open_deployment(db, "ebs-creator")
except Exception:
depl = deployment.create_deployment(db)
depl.name = "ebs-creator"
depl.auto_response = "y"
depl.nix_exprs = [os.path.abspath("./ebs-creator.nix"), os.path.abspath("./ebs-creator-config.nix")]
if not args.keep: depl.destroy_resources()
depl.deploy(allow_reboot=True)
m = depl.machines['machine']
# Do the installation.
device="/dev/xvdg"
if args.hvm:
m.run_command('parted -s /dev/xvdg -- mklabel msdos')
m.run_command('parted -s /dev/xvdg -- mkpart primary ext2 1M -1s')
device="/dev/xvdg1"
m.run_command("if mountpoint -q /mnt; then umount /mnt; fi")
m.run_command("mkfs.ext4 -L nixos {0}".format(device))
m.run_command("mkdir -p /mnt")
m.run_command("mount {0} /mnt".format(device))
m.run_command("touch /mnt/.ebs")
m.run_command("mkdir -p /mnt/etc/nixos")
m.run_command("nix-channel --add http://nixos.org/channels/nixos-unstable")
m.run_command("nix-channel --update")
m.run_command("nixos-rebuild switch")
version = m.run_command("nixos-version", capture_stdout=True).replace('"', '').rstrip()
print >> sys.stderr, "NixOS version is {0}".format(version)
m.upload_file("./amazon-base-config.nix", "/mnt/etc/nixos/configuration.nix")
m.run_command("nixos-install")
if args.hvm:
m.run_command('cp /mnt/nix/store/*-grub-0.97*/lib/grub/i386-pc/* /mnt/boot/grub')
m.run_command('sed -i "s|hd0|hd0,0|" /mnt/boot/grub/menu.lst')
m.run_command('echo "(hd1) /dev/xvdg" > device.map')
m.run_command('echo -e "root (hd1,0)\nsetup (hd1)" | grub --device-map=device.map --batch')
m.run_command("umount /mnt")
if args.hvm:
ami_name = "nixos-{0}-x86_64-ebs-hvm".format(version)
description = "NixOS {0} (x86_64; EBS root; hvm)".format(version)
else:
ami_name = "nixos-{0}-x86_64-ebs".format(version)
description = "NixOS {0} (x86_64; EBS root)".format(version)
# Wait for the snapshot to finish.
def check():
status = snapshot.update()
print >> sys.stderr, "snapshot status is {0}".format(status)
return status == '100%'
m.connect()
volume = m._conn.get_all_volumes([], filters={'attachment.instance-id': m.resource_id, 'attachment.device': "/dev/sdg"})[0]
if args.hvm:
instance = m._conn.run_instances( image_id="ami-6a9e4503"
, instance_type=instance_type
, key_name=args.key_name
, placement=m.zone
, security_groups=["eelco-test"]).instances[0]
nixops.util.check_wait(lambda: instance.update() == 'running', max_tries=120)
instance.stop()
nixops.util.check_wait(lambda: instance.update() == 'stopped', max_tries=120)
old_root_volume = m._conn.get_all_volumes([], filters={'attachment.instance-id': instance.id, 'attachment.device': "/dev/sda1"})[0]
old_root_volume.detach()
volume.detach()
nixops.util.check_wait(lambda: volume.update() == 'available', max_tries=120)
nixops.util.check_wait(lambda: old_root_volume.update() == 'available', max_tries=120)
volume.attach(instance.id, '/dev/sda1')
nixops.util.check_wait(lambda: volume.update() == 'in-use', max_tries=120)
ami_id = m._conn.create_image(instance.id, ami_name, description)
time.sleep(5)
image = m._conn.get_all_images([ami_id])[0]
nixops.util.check_wait(lambda: image.update() == 'available', max_tries=120)
instance.terminate()
else:
# Create a snapshot.
snapshot = volume.create_snapshot(description=description)
print >> sys.stderr, "created snapshot {0}".format(snapshot.id)
nixops.util.check_wait(check, max_tries=120)
m._conn.create_tags([snapshot.id], {'Name': ami_name})
if not args.keep: depl.destroy_resources()
# Register the image.
aki = m._conn.get_all_images(filters={'manifest-location': '*pv-grub-hd0_1.03-x86_64*'})[0]
print >> sys.stderr, "using kernel image {0} - {1}".format(aki.id, aki.location)
block_map = BlockDeviceMapping()
block_map['/dev/sda'] = BlockDeviceType(snapshot_id=snapshot.id, delete_on_termination=True)
block_map['/dev/sdb'] = BlockDeviceType(ephemeral_name="ephemeral0")
block_map['/dev/sdc'] = BlockDeviceType(ephemeral_name="ephemeral1")
block_map['/dev/sdd'] = BlockDeviceType(ephemeral_name="ephemeral2")
block_map['/dev/sde'] = BlockDeviceType(ephemeral_name="ephemeral3")
ami_id = m._conn.register_image(
name=ami_name,
description=description,
architecture="x86_64",
root_device_name="/dev/sda",
kernel_id=aki.id,
block_device_map=block_map)
print >> sys.stderr, "registered AMI {0}".format(ami_id)
print >> sys.stderr, "sleeping a bit..."
time.sleep(30)
print >> sys.stderr, "setting image name..."
m._conn.create_tags([ami_id], {'Name': ami_name})
print >> sys.stderr, "making image public..."
image = m._conn.get_all_images(image_ids=[ami_id])[0]
image.set_launch_permissions(user_ids=[], group_names=["all"])
# Do a test deployment to make sure that the AMI works.
f = open("ebs-test.nix", "w")
f.write(
'''
{{
network.description = "NixOS EBS test";
resources.ec2KeyPairs.keypair.accessKeyId = "logicblox-dev";
resources.ec2KeyPairs.keypair.region = "{0}";
machine = {{ config, pkgs, resources, ... }}: {{
deployment.targetEnv = "ec2";
deployment.ec2.accessKeyId = "logicblox-dev";
deployment.ec2.region = "{0}";
deployment.ec2.instanceType = "{2}";
deployment.ec2.keyPair = resources.ec2KeyPairs.keypair.name;
deployment.ec2.securityGroups = [ "admin" ];
deployment.ec2.ami = "{1}";
}};
}}
'''.format(args.region, ami_id, instance_type))
f.close()
test_depl = deployment.create_deployment(db)
test_depl.auto_response = "y"
test_depl.name = "ebs-creator-test"
test_depl.nix_exprs = [os.path.abspath("./ebs-test.nix")]
test_depl.deploy(create_only=True)
test_depl.machines['machine'].run_command("nixos-version")
if args.hvm:
image_type = 'hvm'
else:
image_type = 'ebs'
# Log the AMI ID.
f = open("{0}.{1}.ami-id".format(args.region, image_type), "w")
f.write("{0}".format(ami_id))
f.close()
for dest in [ 'us-east-1', 'us-west-1', 'us-west-2', 'eu-west-1']:
if args.region != dest:
print >> sys.stderr, "copying image from region {0} to {1}".format(args.region, dest)
conn = boto.ec2.connect_to_region(dest)
copy_image = conn.copy_image(args.region, ami_id, ami_name, description=None, client_token=None)
# Log the AMI ID.
f = open("{0}.{1}.ami-id".format(dest, image_type), "w")
f.write("{0}".format(copy_image.image_id))
f.close()
if not args.keep:
test_depl.destroy_resources()
test_depl.delete()

View File

@ -0,0 +1,49 @@
#! /bin/sh -e
nixos=$(nix-instantiate --find-file nixos)
export NIXOS_CONFIG=$(dirname $(readlink -f $0))/amazon-base-config.nix
version=$(nix-instantiate --eval-only '<nixos>' -A config.system.nixosVersion | sed s/'"'//g)
echo "NixOS version is $version"
buildAndUploadFor() {
system="$1"
arch="$2"
echo "building $system image..."
nix-build '<nixos>' \
-A config.system.build.amazonImage --argstr system "$system" -o ec2-ami
ec2-bundle-image -i ./ec2-ami/nixos.img --user "$AWS_ACCOUNT" --arch "$arch" \
-c "$EC2_CERT" -k "$EC2_PRIVATE_KEY"
for region in eu-west-1 us-east-1 us-west-1 us-west-2; do
echo "uploading $system image for $region..."
name=nixos-$version-$arch-s3
bucket="$(echo $name-$region | tr '[A-Z]_' '[a-z]-')"
if [ "$region" = eu-west-1 ]; then s3location=EU;
elif [ "$region" = us-east-1 ]; then s3location=US;
else s3location="$region"
fi
ec2-upload-bundle -b "$bucket" -m /tmp/nixos.img.manifest.xml \
-a "$EC2_ACCESS_KEY" -s "$EC2_SECRET_KEY" --location "$s3location" \
--url http://s3.amazonaws.com
kernel=$(ec2-describe-images -o amazon --filter "manifest-location=*pv-grub-hd0_1.03-$arch*" --region "$region" | cut -f 2)
echo "using PV-GRUB kernel $kernel"
ami=$(ec2-register "$bucket/nixos.img.manifest.xml" -n "$name" -d "NixOS $system r$revision" \
--region "$region" --kernel "$kernel" | cut -f 2)
echo "AMI ID is $ami"
echo $ami >> $region.s3.ami-id
ec2-modify-image-attribute --region "$region" "$ami" -l -a all
done
}
buildAndUploadFor x86_64-linux x86_64

View File

@ -0,0 +1,13 @@
{
network.description = "NixOS EBS creator";
machine =
{ config, pkgs, resources, ... }:
{ deployment.targetEnv = "ec2";
deployment.ec2.instanceType = "m1.large";
deployment.ec2.securityGroups = [ "admin" ];
deployment.ec2.ebsBoot = false;
deployment.ec2.keyPair = resources.ec2KeyPairs.keypair.name;
environment.systemPackages = [ pkgs.parted ];
};
}

View File

@ -0,0 +1,32 @@
{ config, pkgs, ... }:
with pkgs.lib;
{
options = {
fonts = {
enableCoreFonts = mkOption {
default = false;
description = ''
Whether to include Microsoft's proprietary Core Fonts. These fonts
are redistributable, but only verbatim, among other restrictions.
See <link xlink:href="http://corefonts.sourceforge.net/eula.htm"/>
for details.
'';
};
};
};
config = mkIf config.fonts.enableCoreFonts {
fonts.extraFonts = [ pkgs.corefonts ];
};
}

View File

@ -0,0 +1,59 @@
{ config, pkgs, ... }:
with pkgs.lib;
{
options = {
fonts = {
enableFontConfig = mkOption { # !!! should be enableFontconfig
type = types.bool;
default = true;
description = ''
If enabled, a Fontconfig configuration file will be built
pointing to a set of default fonts. If you don't care about
running X11 applications or any other program that uses
Fontconfig, you can turn this option off and prevent a
dependency on all those fonts.
'';
};
};
};
config = mkIf config.fonts.enableFontConfig {
# Bring in the default (upstream) fontconfig configuration.
environment.etc."fonts/fonts.conf".source =
pkgs.makeFontsConf { fontDirectories = config.fonts.fonts; };
environment.etc."fonts/conf.d/00-nixos.conf".text =
''
<?xml version='1.0'?>
<!DOCTYPE fontconfig SYSTEM 'fonts.dtd'>
<fontconfig>
<!-- Set the default hinting style to "slight". -->
<match target="font">
<edit mode="assign" name="hintstyle">
<const>hintslight</const>
</edit>
</match>
</fontconfig>
'';
# FIXME: This variable is no longer needed, but we'll keep it
# around for a while for applications linked against old
# fontconfig builds.
environment.variables.FONTCONFIG_FILE = "/etc/fonts/fonts.conf";
environment.systemPackages = [ pkgs.fontconfig ];
};
}

View File

@ -0,0 +1,75 @@
{ config, pkgs, ... }:
with pkgs.lib;
let
fontDirs = config.fonts.fonts;
localDefs = with pkgs.builderDefs; pkgs.builderDefs.passthru.function rec {
src = "";/* put a fetchurl here */
buildInputs = [pkgs.xorg.mkfontdir pkgs.xorg.mkfontscale];
inherit fontDirs;
installPhase = fullDepEntry ("
list='';
for i in ${toString fontDirs} ; do
if [ -d \$i/ ]; then
list=\"\$list \$i\";
fi;
done
list=\$(find \$list -name fonts.dir -o -name '*.ttf' -o -name '*.otf');
fontDirs='';
for i in \$list ; do
fontDirs=\"\$fontDirs \$(dirname \$i)\";
done;
mkdir -p \$out/share/X11-fonts/;
find \$fontDirs -type f -o -type l | while read i; do
j=\"\${i##*/}\"
if ! test -e \"\$out/share/X11-fonts/\${j}\"; then
ln -s \"\$i\" \"\$out/share/X11-fonts/\${j}\";
fi;
done;
cd \$out/share/X11-fonts/
rm fonts.dir
rm fonts.scale
rm fonts.alias
mkfontdir
mkfontscale
cat \$( find ${pkgs.xorg.fontalias}/ -name fonts.alias) >fonts.alias
") ["minInit" "addInputs"];
};
x11Fonts = with localDefs; stdenv.mkDerivation rec {
name = "X11-fonts";
builder = writeScript (name + "-builder")
(textClosure localDefs
[installPhase doForceShare doPropagate]);
};
in
{
options = {
fonts = {
enableFontDir = mkOption {
default = false;
description = ''
Whether to create a directory with links to all fonts in
<filename>/run/current-system/sw/share/X11-fonts</filename>.
'';
};
};
};
config = mkIf config.fonts.enableFontDir {
environment.systemPackages = [ x11Fonts ];
};
}

View File

@ -0,0 +1,49 @@
{ config, pkgs, ... }:
with pkgs.lib;
{
options = {
fonts = {
# TODO: find another name for it.
fonts = mkOption {
default = [
# - the user's .fonts directory
"~/.fonts"
# - the user's current profile
"~/.nix-profile/lib/X11/fonts"
"~/.nix-profile/share/fonts"
# - the default profile
"/nix/var/nix/profiles/default/lib/X11/fonts"
"/nix/var/nix/profiles/default/share/fonts"
];
description = "List of primary font paths.";
apply = list: list ++ [
# - a few statically built locations
pkgs.xorg.fontbhttf
pkgs.xorg.fontbhlucidatypewriter100dpi
pkgs.xorg.fontbhlucidatypewriter75dpi
pkgs.ttf_bitstream_vera
pkgs.freefont_ttf
pkgs.liberation_ttf
pkgs.xorg.fontbh100dpi
pkgs.xorg.fontmiscmisc
pkgs.xorg.fontcursormisc
]
++ config.fonts.extraFonts;
};
extraFonts = mkOption {
default = [];
example = [ pkgs.dejavu_fonts ];
description = "List of packages with additional fonts.";
};
};
};
}

View File

@ -0,0 +1,32 @@
{ config, pkgs, ... }:
with pkgs.lib;
{
options = {
fonts = {
enableGhostscriptFonts = mkOption {
default = false;
description = ''
Whether to add the fonts provided by Ghostscript (such as
various URW fonts and the Base-14 Postscript fonts) to the
list of system fonts, making them available to X11
applications.
'';
};
};
};
config = mkIf config.fonts.enableGhostscriptFonts {
fonts.extraFonts = [ "${pkgs.ghostscript}/share/ghostscript/fonts" ];
};
}

View File

@ -0,0 +1,46 @@
{ config, pkgs, ... }:
with pkgs.lib;
{
options = {
gnu = mkOption {
type = types.bool;
default = false;
description =
'' When enabled, GNU software is chosen by default whenever a there is
a choice between GNU and non-GNU software (e.g., GNU lsh
vs. OpenSSH).
'';
};
};
config = mkIf config.gnu {
environment.systemPackages = with pkgs;
# TODO: Adjust `requiredPackages' from `system-path.nix'.
# TODO: Add Inetutils once it has the new `ifconfig'.
[ parted
#fdisk # XXX: GNU fdisk currently fails to build and it's redundant
# with the `parted' command.
nano zile
texinfo # for the stand-alone Info reader
]
++ stdenv.lib.optional (!stdenv.isArm) grub2;
# GNU GRUB, where available.
boot.loader.grub.enable = !pkgs.stdenv.isArm;
boot.loader.grub.version = 2;
# GNU lsh.
services.openssh.enable = false;
services.lshd.enable = true;
services.xserver.startOpenSSHAgent = false;
services.xserver.startGnuPGAgent = true;
# TODO: GNU dico.
# TODO: GNU Inetutils' inetd.
# TODO: GNU Pies.
};
}

View File

@ -0,0 +1,87 @@
{ config, pkgs, ... }:
with pkgs.lib;
let
glibcLocales = pkgs.glibcLocales.override {
allLocales = any (x: x == "all") config.i18n.supportedLocales;
locales = config.i18n.supportedLocales;
};
in
{
###### interface
options = {
i18n = {
defaultLocale = mkOption {
type = types.str;
default = "en_US.UTF-8";
example = "nl_NL.UTF-8";
description = ''
The default locale. It determines the language for program
messages, the format for dates and times, sort order, and so on.
It also determines the character set, such as UTF-8.
'';
};
supportedLocales = mkOption {
type = types.listOf types.str;
default = ["all"];
example = ["en_US.UTF-8/UTF-8" "nl_NL.UTF-8/UTF-8" "nl_NL/ISO-8859-1"];
description = ''
List of locales that the system should support. The value
<literal>"all"</literal> means that all locales supported by
Glibc will be installed. A full list of supported locales
can be found at <link
xlink:href="http://sourceware.org/cgi-bin/cvsweb.cgi/libc/localedata/SUPPORTED?cvsroot=glibc"/>.
'';
};
consoleFont = mkOption {
type = types.str;
default = "lat9w-16";
example = "LatArCyrHeb-16";
description = ''
The font used for the virtual consoles. Leave empty to use
whatever the <command>setfont</command> program considers the
default font.
'';
};
consoleKeyMap = mkOption {
type = types.str;
default = "us";
example = "fr";
description = ''
The keyboard mapping table for the virtual consoles.
'';
};
};
};
###### implementation
config = {
environment.systemPackages = [ glibcLocales ];
environment.variables.LANG = config.i18n.defaultLocale;
# /etc/locale.conf is used by systemd.
environment.etc = singleton
{ target = "locale.conf";
source = pkgs.writeText "locale.conf"
''
LANG=${config.i18n.defaultLocale}
'';
};
};
}

View File

@ -0,0 +1,204 @@
{ config, pkgs, ... }:
with pkgs.lib;
let
cfg = config.krb5;
in
{
###### interface
options = {
krb5 = {
enable = mkOption {
default = false;
description = "Whether to enable Kerberos V.";
};
defaultRealm = mkOption {
default = "ATENA.MIT.EDU";
description = "Default realm.";
};
domainRealm = mkOption {
default = "atena.mit.edu";
description = "Default domain realm.";
};
kdc = mkOption {
default = "kerberos.mit.edu";
description = "Kerberos Domain Controller";
};
kerberosAdminServer = mkOption {
default = "kerberos.mit.edu";
description = "Kerberos Admin Server";
};
};
};
###### implementation
config = mkIf config.krb5.enable {
environment.systemPackages = [ pkgs.krb5 ];
environment.etc."krb5.conf".text =
''
[libdefaults]
default_realm = ${cfg.defaultRealm}
encrypt = true
# The following krb5.conf variables are only for MIT Kerberos.
krb4_config = /etc/krb.conf
krb4_realms = /etc/krb.realms
kdc_timesync = 1
ccache_type = 4
forwardable = true
proxiable = true
# The following encryption type specification will be used by MIT Kerberos
# if uncommented. In general, the defaults in the MIT Kerberos code are
# correct and overriding these specifications only serves to disable new
# encryption types as they are added, creating interoperability problems.
# default_tgs_enctypes = aes256-cts arcfour-hmac-md5 des3-hmac-sha1 des-cbc-crc des-cbc-md5
# default_tkt_enctypes = aes256-cts arcfour-hmac-md5 des3-hmac-sha1 des-cbc-crc des-cbc-md5
# permitted_enctypes = aes256-cts arcfour-hmac-md5 des3-hmac-sha1 des-cbc-crc des-cbc-md5
# The following libdefaults parameters are only for Heimdal Kerberos.
v4_instance_resolve = false
v4_name_convert = {
host = {
rcmd = host
ftp = ftp
}
plain = {
something = something-else
}
}
fcc-mit-ticketflags = true
[realms]
${cfg.defaultRealm} = {
kdc = ${cfg.kdc}
admin_server = ${cfg.kerberosAdminServer}
#kpasswd_server = ${cfg.kerberosAdminServer}
}
ATHENA.MIT.EDU = {
kdc = kerberos.mit.edu:88
kdc = kerberos-1.mit.edu:88
kdc = kerberos-2.mit.edu:88
admin_server = kerberos.mit.edu
default_domain = mit.edu
}
MEDIA-LAB.MIT.EDU = {
kdc = kerberos.media.mit.edu
admin_server = kerberos.media.mit.edu
}
ZONE.MIT.EDU = {
kdc = casio.mit.edu
kdc = seiko.mit.edu
admin_server = casio.mit.edu
}
MOOF.MIT.EDU = {
kdc = three-headed-dogcow.mit.edu:88
kdc = three-headed-dogcow-1.mit.edu:88
admin_server = three-headed-dogcow.mit.edu
}
CSAIL.MIT.EDU = {
kdc = kerberos-1.csail.mit.edu
kdc = kerberos-2.csail.mit.edu
admin_server = kerberos.csail.mit.edu
default_domain = csail.mit.edu
krb524_server = krb524.csail.mit.edu
}
IHTFP.ORG = {
kdc = kerberos.ihtfp.org
admin_server = kerberos.ihtfp.org
}
GNU.ORG = {
kdc = kerberos.gnu.org
kdc = kerberos-2.gnu.org
kdc = kerberos-3.gnu.org
admin_server = kerberos.gnu.org
}
1TS.ORG = {
kdc = kerberos.1ts.org
admin_server = kerberos.1ts.org
}
GRATUITOUS.ORG = {
kdc = kerberos.gratuitous.org
admin_server = kerberos.gratuitous.org
}
DOOMCOM.ORG = {
kdc = kerberos.doomcom.org
admin_server = kerberos.doomcom.org
}
ANDREW.CMU.EDU = {
kdc = vice28.fs.andrew.cmu.edu
kdc = vice2.fs.andrew.cmu.edu
kdc = vice11.fs.andrew.cmu.edu
kdc = vice12.fs.andrew.cmu.edu
admin_server = vice28.fs.andrew.cmu.edu
default_domain = andrew.cmu.edu
}
CS.CMU.EDU = {
kdc = kerberos.cs.cmu.edu
kdc = kerberos-2.srv.cs.cmu.edu
admin_server = kerberos.cs.cmu.edu
}
DEMENTIA.ORG = {
kdc = kerberos.dementia.org
kdc = kerberos2.dementia.org
admin_server = kerberos.dementia.org
}
stanford.edu = {
kdc = krb5auth1.stanford.edu
kdc = krb5auth2.stanford.edu
kdc = krb5auth3.stanford.edu
admin_server = krb5-admin.stanford.edu
default_domain = stanford.edu
}
[domain_realm]
.${cfg.domainRealm} = ${cfg.defaultRealm}
${cfg.domainRealm} = ${cfg.defaultRealm}
.mit.edu = ATHENA.MIT.EDU
mit.edu = ATHENA.MIT.EDU
.media.mit.edu = MEDIA-LAB.MIT.EDU
media.mit.edu = MEDIA-LAB.MIT.EDU
.csail.mit.edu = CSAIL.MIT.EDU
csail.mit.edu = CSAIL.MIT.EDU
.whoi.edu = ATHENA.MIT.EDU
whoi.edu = ATHENA.MIT.EDU
.stanford.edu = stanford.edu
[logging]
kdc = SYSLOG:INFO:DAEMON
admin_server = SYSLOG:INFO:DAEMON
default = SYSLOG:INFO:DAEMON
krb4_convert = true
krb4_get_tickets = false
[appdefaults]
pam = {
debug = false
ticket_lifetime = 36000
renew_lifetime = 36000
max_timeout = 30
timeout_shift = 2
initial_timeout = 1
}
'';
};
}

View File

@ -0,0 +1,246 @@
{ config, pkgs, ... }:
with pkgs.lib;
with pkgs;
let
cfg = config.users.ldap;
# Careful: OpenLDAP seems to be very picky about the indentation of
# this file. Directives HAVE to start in the first column!
ldapConfig = {
target = "ldap.conf";
source = writeText "ldap.conf" ''
uri ${config.users.ldap.server}
base ${config.users.ldap.base}
timelimit ${toString config.users.ldap.timeLimit}
bind_timelimit ${toString config.users.ldap.bind.timeLimit}
bind_policy ${config.users.ldap.bind.policy}
${optionalString config.users.ldap.useTLS ''
ssl start_tls
tls_checkpeer no
''}
${optionalString (config.users.ldap.bind.distinguishedName != "") ''
binddn ${config.users.ldap.bind.distinguishedName}
''}
${optionalString (cfg.extraConfig != "") cfg.extraConfig }
'';
};
nslcdConfig = {
target = "nslcd.conf";
source = writeText "nslcd.conf" ''
uid nslcd
gid nslcd
uri ${cfg.server}
base ${cfg.base}
timelimit ${toString cfg.timeLimit}
bind_timelimit ${toString cfg.bind.timeLimit}
${optionalString (cfg.bind.distinguishedName != "")
"binddn ${cfg.bind.distinguishedName}" }
${optionalString (cfg.daemon.extraConfig != "") cfg.daemon.extraConfig }
'';
};
insertLdapPassword = !config.users.ldap.daemon.enable &&
config.users.ldap.bind.distinguishedName != "";
in
{
###### interface
options = {
users.ldap = {
enable = mkOption {
default = false;
description = "Whether to enable authentication against an LDAP server.";
};
server = mkOption {
example = "ldap://ldap.example.org/";
description = "The URL of the LDAP server.";
};
base = mkOption {
example = "dc=example,dc=org";
description = "The distinguished name of the search base.";
};
useTLS = mkOption {
default = false;
description = ''
If enabled, use TLS (encryption) over an LDAP (port 389)
connection. The alternative is to specify an LDAPS server (port
636) in <option>users.ldap.server</option> or to forego
security.
'';
};
timeLimit = mkOption {
default = 0;
type = types.int;
description = ''
Specifies the time limit (in seconds) to use when performing
searches. A value of zero (0), which is the default, is to
wait indefinitely for searches to be completed.
'';
};
daemon = {
enable = mkOption {
default = false;
description = ''
Whether to let the nslcd daemon (nss-pam-ldapd) handle the
LDAP lookups for NSS and PAM. This can improve performance,
and if you need to bind to the LDAP server with a password,
it increases security, since only the nslcd user needs to
have access to the bindpw file, not everyone that uses NSS
and/or PAM. If this option is enabled, a local nscd user is
created automatically, and the nslcd service is started
automatically when the network get up.
'';
};
extraConfig = mkOption {
default = "";
type = types.string;
description = ''
Extra configuration options that will be added verbatim at
the end of the nslcd configuration file (nslcd.conf).
'' ;
} ;
};
bind = {
distinguishedName = mkOption {
default = "";
example = "cn=admin,dc=example,dc=com";
type = types.string;
description = ''
The distinguished name to bind to the LDAP server with. If this
is not specified, an anonymous bind will be done.
'';
};
password = mkOption {
default = "/etc/ldap/bind.password";
type = types.string;
description = ''
The path to a file containing the credentials to use when binding
to the LDAP server (if not binding anonymously).
'';
};
timeLimit = mkOption {
default = 30;
type = types.int;
description = ''
Specifies the time limit (in seconds) to use when connecting
to the directory server. This is distinct from the time limit
specified in <literal>users.ldap.timeLimit</literal> and affects
the initial server connection only.
'';
};
policy = mkOption {
default = "hard_open";
type = types.string;
description = ''
Specifies the policy to use for reconnecting to an unavailable
LDAP server. The default is <literal>hard_open</literal>, which
reconnects if opening the connection to the directory server
failed. By contrast, <literal>hard_init</literal> reconnects if
initializing the connection failed. Initializing may not
actually contact the directory server, and it is possible that
a malformed configuration file will trigger reconnection. If
<literal>soft</literal> is specified, then
<literal>nss_ldap</literal> will return immediately on server
failure. All hard reconnect policies block with exponential
backoff before retrying.
'';
};
};
extraConfig = mkOption {
default = "";
type = types.string;
description = ''
Extra configuration options that will be added verbatim at
the end of the ldap configuration file (ldap.conf).
If <literal>users.ldap.daemon</literal> is enabled, this
configuration will not be used. In that case, use
<literal>users.ldap.daemon.extraConfig</literal> instead.
'' ;
};
};
};
###### implementation
config = mkIf cfg.enable {
environment.etc = if cfg.daemon.enable then [nslcdConfig] else [ldapConfig];
system.activationScripts = mkIf insertLdapPassword {
ldap = stringAfter [ "etc" "groups" "users" ] ''
if test -f "${cfg.bind.password}" ; then
echo "bindpw "$(cat ${cfg.bind.password})"" | cat ${ldapConfig} - > /etc/ldap.conf.bindpw
mv -fT /etc/ldap.conf.bindpw /etc/ldap.conf
chmod 600 /etc/ldap.conf
fi
'';
};
system.nssModules = singleton (
if cfg.daemon.enable then nss_pam_ldapd else nss_ldap
);
users = mkIf cfg.daemon.enable {
extraGroups.nslcd = {
gid = config.ids.gids.nslcd;
};
extraUsers.nslcd = {
uid = config.ids.uids.nslcd;
description = "nslcd user.";
group = "nslcd";
};
};
systemd.services = mkIf cfg.daemon.enable {
nslcd = {
wantedBy = [ "nss-user-lookup.target" ];
before = [ "nss-user-lookup.target" ];
after = [ "network.target" ];
preStart = ''
mkdir -p /run/nslcd
rm -f /run/nslcd/nslcd.pid;
chown nslcd.nslcd /run/nslcd
${optionalString (cfg.bind.distinguishedName != "") ''
if test -s "${cfg.bind.password}" ; then
ln -sfT "${cfg.bind.password}" /run/nslcd/bindpw
fi
''}
'';
serviceConfig = {
ExecStart = "${nss_pam_ldapd}/sbin/nslcd";
Type = "forking";
PIDFile = "/run/nslcd/nslcd.pid";
Restart = "always";
};
};
};
};
}

View File

@ -0,0 +1,90 @@
# /etc files related to networking, such as /etc/services.
{ config, pkgs, ... }:
with pkgs.lib;
let
cfg = config.networking;
in
{
options = {
networking.extraHosts = pkgs.lib.mkOption {
type = types.lines;
default = "";
example = "192.168.0.1 lanlocalhost";
description = ''
Additional entries to be appended to <filename>/etc/hosts</filename>.
'';
};
networking.dnsSingleRequest = pkgs.lib.mkOption {
type = types.bool;
default = false;
description = ''
Recent versions of glibc will issue both ipv4 (A) and ipv6 (AAAA)
address queries at the same time, from the same port. Sometimes upstream
routers will systemically drop the ipv4 queries. The symptom of this problem is
that 'getent hosts example.com' only returns ipv6 (or perhaps only ipv4) addresses. The
workaround for this is to specify the option 'single-request' in
/etc/resolv.conf. This option enables that.
'';
};
};
config = {
environment.etc =
{ # /etc/services: TCP/UDP port assignments.
"services".source = pkgs.iana_etc + "/etc/services";
# /etc/protocols: IP protocol numbers.
"protocols".source = pkgs.iana_etc + "/etc/protocols";
# /etc/rpc: RPC program numbers.
"rpc".source = pkgs.glibc + "/etc/rpc";
# /etc/hosts: Hostname-to-IP mappings.
"hosts".text =
''
127.0.0.1 localhost
${optionalString cfg.enableIPv6 ''
::1 localhost
''}
${cfg.extraHosts}
'';
# /etc/resolvconf.conf: Configuration for openresolv.
"resolvconf.conf".text =
''
# This is the default, but we must set it here to prevent
# a collision with an apparently unrelated environment
# variable with the same name exported by dhcpcd.
interface_order='lo lo[0-9]*'
'' + optionalString config.services.nscd.enable ''
# Invalidate the nscd cache whenever resolv.conf is
# regenerated.
libc_restart='${pkgs.systemd}/bin/systemctl try-restart --no-block nscd.service'
'' + optionalString cfg.dnsSingleRequest ''
# only send one DNS request at a time
resolv_conf_options='single-request'
'' + optionalString config.services.bind.enable ''
# This hosts runs a full-blown DNS resolver.
name_servers='127.0.0.1'
'';
};
# The ip-up target is started when we have IP connectivity. So
# services that depend on IP connectivity (like ntpd) should be
# pulled in by this target.
systemd.targets.ip-up.description = "Services Requiring IP Connectivity";
};
}

View File

@ -0,0 +1,25 @@
{ config, pkgs, ... }:
with pkgs.lib;
{
options = {
environment.noXlibs = mkOption {
type = types.bool;
default = false;
description = ''
Switch off the options in the default configuration that require X libraries.
Currently this includes: ssh X11 forwarding, dbus, fonts.enableCoreFonts,
fonts.enableFontConfig
'';
};
};
config = mkIf config.environment.noXlibs {
programs.ssh.setXAuthLocation = false;
fonts = {
enableCoreFonts = false;
enableFontConfig = false;
};
};
}

Some files were not shown because too many files have changed in this diff Show More