Merge the NixOS repository into Nixpkgs

This commit is contained in:
Eelco Dolstra 2013-10-10 13:28:21 +02:00
commit 2a537fb369
481 changed files with 50774 additions and 0 deletions

2
nixos/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*~
.version-suffix

1
nixos/.version Normal file
View File

@ -0,0 +1 @@
13.09

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.

44
nixos/default.nix Normal file
View File

@ -0,0 +1,44 @@
{ configuration ? import ./lib/from-env.nix "NIXOS_CONFIG" <nixos-config>
, system ? builtins.currentSystem
, nixpkgs ? <nixpkgs>
}:
let
eval = import ./lib/eval-config.nix {
inherit system nixpkgs;
modules = [ configuration ];
};
inherit (eval) config pkgs;
# This is for `nixos-rebuild build-vm'.
vmConfig = (import ./lib/eval-config.nix {
inherit system nixpkgs;
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 nixpkgs;
modules =
[ configuration
./modules/virtualisation/qemu-vm.nix
{ virtualisation.useBootLoader = true; }
];
}).config;
in
{
inherit eval config;
system = 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;
};
}

View File

@ -0,0 +1,810 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xml:id="ch-configuration">
<title>Configuring NixOS</title>
<para>This chapter describes how to configure various aspects of a
NixOS machine through the configuration file
<filename>/etc/nixos/configuration.nix</filename>. As described in
<xref linkend="sec-changing-config" />, changes to that file only take
effect after you run <command>nixos-rebuild</command>.</para>
<!--===============================================================-->
<section><title>Package management</title>
<para>This section describes how to add additional packages to your
system. NixOS has two distinct styles of package management:
<itemizedlist>
<listitem><para><emphasis>Declarative</emphasis>, where you declare
what packages you want in your
<filename>configuration.nix</filename>. Every time you run
<command>nixos-rebuild</command>, NixOS will ensure that you get a
consistent set of binaries corresponding to your
specification.</para></listitem>
<listitem><para><emphasis>Ad hoc</emphasis>, where you install,
upgrade and uninstall packages via the <command>nix-env</command>
command. This style allows mixing packages from different Nixpkgs
versions. Its the only choice for non-root
users.</para></listitem>
</itemizedlist>
</para>
<para>The next two sections describe these two styles.</para>
<section><title>Declarative package management</title>
<para>With declarative package management, you specify which packages
you want on your system by setting the option
<option>environment.systemPackages</option>. For instance, adding the
following line to <filename>configuration.nix</filename> enables the
Mozilla Thunderbird email application:
<programlisting>
environment.systemPackages = [ pkgs.thunderbird ];
</programlisting>
The effect of this specification is that the Thunderbird package from
Nixpkgs will be built or downloaded as part of the system when you run
<command>nixos-rebuild switch</command>.</para>
<para>You can get a list of the available packages as follows:
<screen>
$ nix-env -qaP '*' --description
nixos.pkgs.firefox firefox-23.0 Mozilla Firefox - the browser, reloaded
<replaceable>...</replaceable>
</screen>
The first column in the output is the <emphasis>attribute
name</emphasis>, such as
<literal>nixos.pkgs.thunderbird</literal>. (The
<literal>nixos</literal> prefix allows distinguishing between
different channels that you might have.)</para>
<para>To “uninstall” a package, simply remove it from
<option>environment.systemPackages</option> and run
<command>nixos-rebuild switch</command>.</para>
<section xml:id="sec-customising-packages"><title>Customising packages</title>
<para>Some packages in Nixpkgs have options to enable or disable
optional functionality or change other aspects of the package. For
instance, the Firefox wrapper package (which provides Firefox with a
set of plugins such as the Adobe Flash player) has an option to enable
the Google Talk plugin. It can be set in
<filename>configuration.nix</filename> as follows:
<filename>
nixpkgs.config.firefox.enableGoogleTalkPlugin = true;
</filename>
</para>
<warning><para>Unfortunately, Nixpkgs currently lacks a way to query
available configuration options.</para></warning>
<para>Apart from high-level options, its possible to tweak a package
in almost arbitrary ways, such as changing or disabling dependencies
of a package. For instance, the Emacs package in Nixpkgs by default
has a dependency on GTK+ 2. If you want to build it against GTK+ 3,
you can specify that as follows:
<programlisting>
environment.systemPackages = [ (pkgs.emacs.override { gtk = pkgs.gtk3; }) ];
</programlisting>
The function <varname>override</varname> performs the call to the Nix
function that produces Emacs, with the original arguments amended by
the set of arguments specified by you. So here the function argument
<varname>gtk</varname> gets the value <literal>pkgs.gtk3</literal>,
causing Emacs to depend on GTK+ 3. (The parentheses are necessary
because in Nix, function application binds more weakly than list
construction, so without them,
<literal>environment.systemPackages</literal> would be a list with two
elements.)</para>
<para>Even greater customisation is possible using the function
<varname>overrideDerivation</varname>. While the
<varname>override</varname> mechanism above overrides the arguments of
a package function, <varname>overrideDerivation</varname> allows
changing the <emphasis>result</emphasis> of the function. This
permits changing any aspect of the package, such as the source code.
For instance, if you want to override the source code of Emacs, you
can say:
<programlisting>
environment.systemPackages =
[ (pkgs.lib.overrideDerivation pkgs.emacs (attrs: {
name = "emacs-25.0-pre";
src = /path/to/my/emacs/tree;
}))
];
</programlisting>
Here, <varname>overrideDerivation</varname> takes the Nix derivation
specified by <varname>pkgs.emacs</varname> and produces a new
derivation in which the originals <literal>name</literal> and
<literal>src</literal> attribute have been replaced by the given
values. The original attributes are accessible via
<varname>attrs</varname>.</para>
<para>The overrides shown above are not global. They do not affect
the original package; other packages in Nixpkgs continue to depend on
the original rather than the customised package. This means that if
another package in your system depends on the original package, you
end up with two instances of the package. If you want to have
everything depend on your customised instance, you can apply a
<emphasis>global</emphasis> override as follows:
<screen>
nixpkgs.config.packageOverrides = pkgs:
{ emacs = pkgs.emacs.override { gtk = pkgs.gtk3; };
};
</screen>
The effect of this definition is essentially equivalent to modifying
the <literal>emacs</literal> attribute in the Nixpkgs source tree.
Any package in Nixpkgs that depends on <literal>emacs</literal> will
be passed your customised instance. (However, the value
<literal>pkgs.emacs</literal> in
<varname>nixpkgs.config.packageOverrides</varname> refers to the
original rather than overriden instance, to prevent an infinite
recursion.)</para>
</section>
<section><title>Adding custom packages</title>
<para>Its possible that a package you need is not available in NixOS.
In that case, you can do two things. First, you can clone the Nixpkgs
repository, add the package to your clone, and (optionally) submit a
patch or pull request to have it accepted into the main Nixpkgs
repository. This is described in detail in the <link
xlink:href="http://nixos.org/nixpkgs/manual">Nixpkgs manual</link>.
In short, you clone Nixpkgs:
<screen>
$ git clone git://github.com/NixOS/nixpkgs.git
$ cd nixpkgs
</screen>
Then you write and test the package as described in the Nixpkgs
manual. Finally, you add it to
<literal>environment.systemPackages</literal>, e.g.
<programlisting>
environment.systemPackages = [ pkgs.my-package ];
</programlisting>
and you run <command>nixos-rebuild</command>, specifying your own
Nixpkgs tree:
<screen>
$ nixos-rebuild switch -I nixpkgs=/path/to/my/nixpkgs</screen>
</para>
<para>The second possibility is to add the package outside of the
Nixpkgs tree. For instance, here is how you specify a build of the
<link xlink:href="http://www.gnu.org/software/hello/">GNU Hello</link>
package directly in <filename>configuration.nix</filename>:
<programlisting>
environment.systemPackages =
let
my-hello = with pkgs; stdenv.mkDerivation rec {
name = "hello-2.8";
src = fetchurl {
url = "mirror://gnu/hello/${name}.tar.gz";
sha256 = "0wqd8sjmxfskrflaxywc7gqw7sfawrfvdxd9skxawzfgyy0pzdz6";
};
};
in
[ my-hello ];
</programlisting>
Of course, you can also move the definition of
<literal>my-hello</literal> into a separate Nix expression, e.g.
<programlisting>
environment.systemPackages = [ (import ./my-hello.nix) ];
</programlisting>
where <filename>my-hello.nix</filename> contains:
<programlisting>
with &lt;nixpkgs> {}; # bring all of Nixpkgs into scope
stdenv.mkDerivation rec {
name = "hello-2.8";
src = fetchurl {
url = "mirror://gnu/hello/${name}.tar.gz";
sha256 = "0wqd8sjmxfskrflaxywc7gqw7sfawrfvdxd9skxawzfgyy0pzdz6";
};
}
</programlisting>
This allows testing the package easily:
<screen>
$ nix-build my-hello.nix
$ ./result/bin/hello
Hello, world!
</screen>
</para>
</section>
</section>
<section><title>Ad hoc package management</title>
<para>With the command <command>nix-env</command>, you can install and
uninstall packages from the command line. For instance, to install
Mozilla Thunderbird:
<screen>
$ nix-env -iA nixos.pkgs.thunderbird</screen>
If you invoke this as root, the package is installed in the Nix
profile <filename>/nix/var/nix/profiles/default</filename> and visible
to all users of the system; otherwise, the package ends up in
<filename>/nix/var/nix/profiles/per-user/<replaceable>username</replaceable>/profile</filename>
and is not visible to other users. The <option>-A</option> flag
specifies the package by its attribute name; without it, the package
is installed by matching against its package name
(e.g. <literal>thunderbird</literal>). The latter is slower because
it requires matching against all available Nix packages, and is
ambiguous if there are multiple matching packages.</para>
<para>Packages come from the NixOS channel. You typically upgrade a
package by updating to the latest version of the NixOS channel:
<screen>
$ nix-channel --update nixos
</screen>
and then running <literal>nix-env -i</literal> again. Other packages
in the profile are <emphasis>not</emphasis> affected; this is the
crucial difference with the declarative style of package management,
where running <command>nixos-rebuild switch</command> causes all
packages to be updated to their current versions in the NixOS channel.
You can however upgrade all packages for which there is a newer
version by doing:
<screen>
$ nix-env -u '*'
</screen>
</para>
<para>A package can be uninstalled using the <option>-e</option>
flag:
<screen>
$ nix-env -e thunderbird
</screen>
</para>
<para>Finally, you can roll back an undesirable
<command>nix-env</command> action:
<screen>
$ nix-env --rollback
</screen>
</para>
<para><command>nix-env</command> has many more flags. For details,
see the
<citerefentry><refentrytitle>nix-env</refentrytitle><manvolnum>1</manvolnum></citerefentry>
manpage or the Nix manual.</para>
</section>
</section>
<!--===============================================================-->
<section><title>User management</title>
<para>NixOS supports both declarative and imperative styles of user
management. In the declarative style, users are specified in
<filename>configuration.nix</filename>. For instance, the following
states that a user account named <literal>alice</literal> shall exist:
<programlisting>
users.extraUsers.alice =
{ createHome = true;
home = "/home/alice";
description = "Alice Foobar";
extraGroups = [ "wheel" ];
isSystemUser = false;
useDefaultShell = true;
openssh.authorizedKeys.keys = [ "ssh-dss AAAAB3Nza... alice@foobar" ];
};
</programlisting>
Note that <literal>alice</literal> is a member of the
<literal>wheel</literal> group, which allows her to use
<command>sudo</command> to execute commands as
<literal>root</literal>. Also note the SSH public key that allows
remote logins with the corresponding private key. Users created in
this way do not have a password by default, so they cannot log in via
mechanisms that require a password. However, you can use the
<command>passwd</command> program to set a password, which is retained
across invocations of <command>nixos-rebuild</command>.</para>
<para>A user ID (uid) is assigned automatically. You can also specify
a uid manually by adding
<programlisting>
uid = 1000;
</programlisting>
to the user specification.</para>
<para>Groups can be specified similarly. The following states that a
group named <literal>students</literal> shall exist:
<programlisting>
users.extraGroups.students.gid = 1000;
</programlisting>
As with users, the group ID (gid) is optional and will be assigned
automatically if its missing.</para>
<warning><para>Currently declarative user management is not perfect:
<command>nixos-rebuild</command> does not know how to realise certain
configuration changes. This includes removing a user or group, and
removing group membership from a user.</para></warning>
<para>In the imperative style, users and groups are managed by
commands such as <command>useradd</command>,
<command>groupmod</command> and so on. For instance, to create a user
account named <literal>alice</literal>:
<screen>
$ useradd -m alice</screen>
The flag <option>-m</option> causes the creation of a home directory
for the new user, which is generally what you want. The user does not
have an initial password and therefore cannot log in. A password can
be set using the <command>passwd</command> utility:
<screen>
$ passwd alice
Enter new UNIX password: ***
Retype new UNIX password: ***
</screen>
A user can be deleted using <command>userdel</command>:
<screen>
$ userdel -r alice</screen>
The flag <option>-r</option> deletes the users home directory.
Accounts can be modified using <command>usermod</command>. Unix
groups can be managed using <command>groupadd</command>,
<command>groupmod</command> and <command>groupdel</command>.</para>
</section>
<!--===============================================================-->
<section><title>File systems</title>
<para>You can define file systems using the
<option>fileSystems</option> configuration option. For instance, the
following definition causes NixOS to mount the Ext4 file system on
device <filename>/dev/disk/by-label/data</filename> onto the mount
point <filename>/data</filename>:
<programlisting>
fileSystems."/data" =
{ device = "/dev/disk/by-label/data";
fsType = "ext4";
};
</programlisting>
Mount points are created automatically if they dont already exist.
For <option>device</option>, its best to use the topology-independent
device aliases in <filename>/dev/disk/by-label</filename> and
<filename>/dev/disk/by-uuid</filename>, as these dont change if the
topology changes (e.g. if a disk is moved to another IDE
controller).</para>
<para>You can usually omit the file system type
(<option>fsType</option>), since <command>mount</command> can usually
detect the type and load the necessary kernel module automatically.
However, if the file system is needed at early boot (in the initial
ramdisk) and is not <literal>ext2</literal>, <literal>ext3</literal>
or <literal>ext4</literal>, then its best to specify
<option>fsType</option> to ensure that the kernel module is
available.</para>
<section><title>LUKS-encrypted file systems</title>
<para>NixOS supports file systems that are encrypted using
<emphasis>LUKS</emphasis> (Linux Unified Key Setup). For example,
here is how you create an encrypted Ext4 file system on the device
<filename>/dev/sda2</filename>:
<screen>
$ cryptsetup luksFormat /dev/sda2
WARNING!
========
This will overwrite data on /dev/sda2 irrevocably.
Are you sure? (Type uppercase yes): YES
Enter LUKS passphrase: ***
Verify passphrase: ***
$ cryptsetup luksOpen /dev/sda2 crypted
Enter passphrase for /dev/sda2: ***
$ mkfs.ext4 /dev/mapper/crypted
</screen>
To ensure that this file system is automatically mounted at boot time
as <filename>/</filename>, add the following to
<filename>configuration.nix</filename>:
<programlisting>
boot.initrd.luks.devices = [ { device = "/dev/sda2"; name = "crypted"; } ];
fileSystems."/".device = "/dev/mapper/crypted";
</programlisting>
</para>
</section>
</section>
<!--===============================================================-->
<section><title>X Window System</title>
<para>The X Window System (X11) provides the basis of NixOS graphical
user interface. It can be enabled as follows:
<programlisting>
services.xserver.enable = true;
</programlisting>
The X server will automatically detect and use the appropriate video
driver from a set of X.org drivers (such as <literal>vesa</literal>
and <literal>intel</literal>). You can also specify a driver
manually, e.g.
<programlisting>
services.xserver.videoDrivers = [ "r128" ];
</programlisting>
to enable X.orgs <literal>xf86-video-r128</literal> driver.</para>
<para>You also need to enable at least one desktop or window manager.
Otherwise, you can only log into a plain undecorated
<command>xterm</command> window. Thus you should pick one or more of
the following lines:
<programlisting>
services.xserver.desktopManager.kde4.enable = true;
services.xserver.desktopManager.xfce.enable = true;
services.xserver.windowManager.xmonad.enable = true;
services.xserver.windowManager.twm.enable = true;
services.xserver.windowManager.icewm.enable = true;
</programlisting>
</para>
<para>NixOSs default <emphasis>display manager</emphasis> (the
program that provides a graphical login prompt and manages the X
server) is SLiM. You can select KDEs <command>kdm</command> instead:
<programlisting>
services.xserver.displayManager.kdm.enable = true;
</programlisting>
</para>
<para>The X server is started automatically at boot time. If you
dont want this to happen, you can set:
<programlisting>
services.xserver.autorun = false;
</programlisting>
The X server can then be started manually:
<screen>
$ systemctl start display-manager.service
</screen>
</para>
<section><title>NVIDIA graphics cards</title>
<para>NVIDIA provides a proprietary driver for its graphics cards that
has better 3D performance than the X.org drivers. It is not enabled
by default because its not free software. You can enable it as follows:
<programlisting>
services.xserver.videoDrivers = [ "nvidia" ];
</programlisting>
You may need to reboot after enabling this driver to prevent a clash
with other kernel modules.</para>
<para>On 64-bit systems, if you want full acceleration for 32-bit
programs such as Wine, you should also set the following:
<programlisting>
service.xserver.driSupport32Bit = true;
</programlisting>
</para>
</section>
<section><title>Touchpads</title>
<para>Support for Synaptics touchpads (found in many laptops such as
the Dell Latitude series) can be enabled as follows:
<programlisting>
services.xserver.synaptics.enable = true;
</programlisting>
The driver has many options (see <xref linkend="ch-options"/>). For
instance, the following enables two-finger scrolling:
<programlisting>
services.xserver.synaptics.twoFingerScroll = true;
</programlisting>
</para>
</section>
</section>
<!--===============================================================-->
<section><title>Networking</title>
<section><title>Secure shell access</title>
<para>Secure shell (SSH) access to your machine can be enabled by
setting:
<programlisting>
services.openssh.enable = true;
</programlisting>
By default, root logins using a password are disallowed. They can be
disabled entirely by setting
<literal>services.openssh.permitRootLogin</literal> to
<literal>"no"</literal>.</para>
<para>You can declaratively specify authorised RSA/DSA public keys for
a user as follows:
<!-- FIXME: this might not work if the user is unmanaged. -->
<programlisting>
users.extraUsers.alice.openssh.authorizedKeys.keys =
[ "ssh-dss AAAAB3NzaC1kc3MAAACBAPIkGWVEt4..." ];
</programlisting>
</para>
</section>
<section><title>IPv4 configuration</title>
<para>By default, NixOS uses DHCP (specifically,
(<command>dhcpcd</command>)) to automatically configure network
interfaces. However, you can configure an interface manually as
follows:
<programlisting>
networking.interfaces.eth0 = { ipAddress = "192.168.1.2"; prefixLength = 24; };
</programlisting>
(The network prefix can also be specified using the option
<literal>subnetMask</literal>,
e.g. <literal>"255.255.255.0"</literal>, but this is deprecated.)
Typically youll also want to set a default gateway and set of name
servers:
<programlisting>
networking.defaultGateway = "192.168.1.1";
networking.nameservers = [ "8.8.8.8" ];
</programlisting>
</para>
<note><para>Statically configured interfaces are set up by the systemd
service
<replaceable>interface-name</replaceable><literal>-cfg.service</literal>.
The default gateway and name server configuration is performed by
<literal>network-setup.service</literal>.</para></note>
<para>The host name is set using <option>networking.hostName</option>:
<programlisting>
networking.hostName = "cartman";
</programlisting>
The default host name is <literal>nixos</literal>. Set it to the
empty string (<literal>""</literal>) to allow the DHCP server to
provide the host name.</para>
</section>
<section><title>IPv6 configuration</title>
<para>IPv6 is enabled by default. Stateless address autoconfiguration
is used to automatically assign IPv6 addresses to all interfaces. You
can disable IPv6 support globally by setting:
<programlisting>
networking.enableIPv6 = false;
</programlisting>
</para>
</section>
<section><title>Firewall</title>
<para>NixOS has a simple stateful firewall that blocks incoming
connections and other unexpected packets. The firewall applies to
both IPv4 and IPv6 traffic. It can be enabled as follows:
<programlisting>
networking.firewall.enable = true;
</programlisting>
You can open specific TCP ports to the outside world:
<programlisting>
networking.firewall.allowedTCPPorts = [ 80 443 ];
</programlisting>
Note that TCP port 22 (ssh) is opened automatically if the SSH daemon
is enabled (<option>services.openssh.enable = true</option>). UDP
ports can be opened through
<option>networking.firewall.allowedUDPPorts</option>. Also of
interest is
<programlisting>
networking.firewall.allowPing = true;
</programlisting>
to allow the machine to respond to ping requests. (ICMPv6 pings are
always allowed.)</para>
</section>
<section><title>Wireless networks</title>
<para>
NixOS will start wpa_supplicant for you if you enable this setting:
<programlisting>
networking.wireless.enable = true;
</programlisting>
NixOS currently does not generate wpa_supplicant's
configuration file, <literal>/etc/wpa_supplicant.conf</literal>. You should edit this file
yourself to define wireless networks, WPA keys and so on (see
wpa_supplicant.conf(5)).
</para>
<para>
If you are using WPA2 the <command>wpa_passphrase</command> tool might be useful
to generate the <literal>wpa_supplicant.conf</literal>.
<screen>
$ wpa_passphrase ESSID PSK > /etc/wpa_supplicant.conf</screen>
After you have edited the <literal>wpa_supplicant.conf</literal>,
you need to restart the wpa_supplicant service.
<screen>
$ systemctl restart wpa_supplicant.service</screen>
</para>
</section>
<section><title>Ad-hoc configuration</title>
<para>You can use <option>networking.localCommands</option> to specify
shell commands to be run at the end of
<literal>network-setup.service</literal>. This is useful for doing
network configuration not covered by the existing NixOS modules. For
instance, to statically configure an IPv6 address:
<programlisting>
networking.localCommands =
''
ip -6 addr add 2001:610:685:1::1/64 dev eth0
'';
</programlisting>
</para>
</section>
<!-- TODO: OpenVPN, NAT -->
</section>
<!--===============================================================-->
<section><title>Linux kernel</title>
<para>You can override the Linux kernel and associated packages using
the option <option>boot.kernelPackages</option>. For instance, this
selects the Linux 3.10 kernel:
<programlisting>
boot.kernelPackages = pkgs.linuxPackages_3_10;
</programlisting>
Note that this not only replaces the kernel, but also packages that
are specific to the kernel version, such as the NVIDIA video drivers.
This ensures that driver packages are consistent with the
kernel.</para>
<para>The default Linux kernel configuration should be fine for most
users. You can see the configuration of your current kernel in
<filename>/run/booted-system/kernel-modules/config</filename>. If you
want to change the kernel configuration, you can use the
<option>packageOverrides</option> feature (see <xref
linkend="sec-customising-packages" />). For instance, to enable
support for the kernel debugger KGDB:
<programlisting>
nixpkgs.config.packageOverrides = pkgs:
{ linux_3_4 = pkgs.linux_3_4.override {
extraConfig =
''
KGDB y
'';
};
};
</programlisting>
<varname>extraConfig</varname> takes a list of Linux kernel
configuration options, one per line. The name of the option should
not include the prefix <literal>CONFIG_</literal>. The option value
is typically <literal>y</literal>, <literal>n</literal> or
<literal>m</literal> (to build something as a kernel module).</para>
<para>Kernel modules for hardware devices are generally loaded
automatically by <command>udev</command>. You can force a module to
be loaded via <option>boot.kernelModules</option>, e.g.
<programlisting>
boot.kernelModules = [ "fuse" "kvm-intel" "coretemp" ];
</programlisting>
If the module is required early during the boot (e.g. to mount the
root file system), you can use
<option>boot.initrd.extraKernelModules</option>:
<programlisting>
boot.initrd.extraKernelModules = [ "cifs" ];
</programlisting>
This causes the specified modules and their dependencies to be added
to the initial ramdark.</para>
<para>Kernel runtime parameters can be set through
<option>boot.kernel.sysctl</option>, e.g.
<programlisting>
boot.kernel.sysctl."net.ipv4.tcp_keepalive_time" = 120;
</programlisting>
sets the kernels TCP keepalive time to 120 seconds. To see the
available parameters, run <command>sysctl -a</command>.</para>
</section>
<!-- Apache; libvirtd virtualisation -->
</chapter>

View File

@ -0,0 +1,98 @@
{ pkgs, options
# revision can have multiple values: local, HEAD or any revision number.
, revision ? "HEAD"
}:
let
# To prevent infinite recursion, remove system.path from the
# options. Not sure why this happens.
options_ =
options //
{ system = removeAttrs options.system ["path"]; };
optionsXML = builtins.toFile "options.xml" (builtins.unsafeDiscardStringContext
(builtins.toXML (pkgs.lib.optionAttrSetToDocList "" options_)));
optionsDocBook = pkgs.runCommand "options-db.xml" {} ''
${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 = pkgs.lib.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
ensureDir $out/nix-support
echo "doc manual $dst manual.html" >> $out/nix-support/hydra-build-products
''; # */
};
# Generate the NixOS manpages.
manpages = pkgs.stdenv.mkDerivation {
name = "nixos-manpages";
sources = pkgs.lib.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.
ensureDir $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,599 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Development</title>
<para>This chapter has some random notes on hacking on
NixOS.</para>
<!--===============================================================-->
<section>
<title>Hacking on NixOS</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/nixos.git
$ git clone git://github.com/NixOS/nixpkgs.git
</screen>
This will check out the latest NixOS sources to
<filename><replaceable>/my/sources</replaceable>/nixos</filename> and
the Nixpkgs sources to
<filename><replaceable>/my/sources</replaceable>/nixpkgs</filename>.
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 <replaceable>/my/sources</replaceable>
</screen>
</para>
<para><command>nixos-rebuild</command> affects only the system profile.
To install packages to your user profile from expressions in
<replaceable>/my/sources</replaceable>, use
<command>nix-env -f <replaceable>/my/sources</replaceable>/nixpkgs</command>,
or change the default by replacing the symlink in
<filename>~/.nix-defexpr</filename>:
<screen>
$ rm -f ~/.nix-defexpr/channels
$ ln -s <replaceable>/my/sources</replaceable>/nixpkgs ~/.nix-defexpr/nixpkgs
</screen>
</para>
<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>Extending NixOS</title>
<para>NixOS is based on a modular system for declarative configuration.
This system combines multiple <emphasis>modules</emphasis> to produce one
configuration. One of the module which compose your computer
configuration is <filename>/etc/nixos/configuration.nix</filename>. Other
modules are available under NixOS <filename>modules</filename>
directory</para>
<para>A module is a file which handles one specific part of the
configuration. This part of the configuration could correspond to
hardware, a service, network settings, or preferences. A module
configuration does not have to handle everything from scratch, it can base
its configuration on other configurations provided by other modules. Thus
a module can <emphasis>define</emphasis> options to setup its
configuration, and it can also <emphasis>declare</emphasis> options to be
fed by other modules.</para>
<!-- module syntax -->
<para xml:id="para-module-syn">A module is a file which contains a Nix
expression. This expression should be either an expression which gets
evaluated into an attribute set or a function which returns an attribute
set.</para>
<para>When the expression is a function, it should expect only one argument
which is an attribute set containing an attribute
named <varname>config</varname> and another attribute
named <varname>pkgs</varname>. The <varname>config</varname> attribute
contains the result of the merge of all modules. This attribute is
evaluated lazily, such as any Nix expression. For more details on how
options are merged, see the details in <xref linkend="para-opt-decl"/>.
The <varname>pkgs</varname> attribute
contains <emphasis>nixpkgs</emphasis> attribute set of packages. This
attribute is necessary for declaring options.</para>
<example xml:id='module-syntax'><title>Usual module content</title>
<programlisting>
{ config, pkgs, ... }: <co xml:id='module-syntax-1' />
{
imports =
[ <co xml:id='module-syntax-2' />
];
options = {
<co xml:id='module-syntax-3' />
};
config = {
<co xml:id='module-syntax-4' />
};
}</programlisting>
</example>
<para><xref linkend='module-syntax' /> Illustrates
a <emphasis>module</emphasis> skeleton.
<calloutlist>
<callout arearefs='module-syntax-1'>
<para>This line makes the current Nix expression a function. 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 is used to enumerate path to other modules which are
declaring options used by the current module. In NixOS, default modules
are listed in the file <filename>modules/module-list.nix</filename>.
The default modules don't need to be added in the import list.</para>
</callout>
<callout arearefs='module-syntax-3'>
<para>This attribute set contains an attribute set of <emphasis>option
declaration</emphasis>.</para>
</callout>
<callout arearefs='module-syntax-4'>
<para>This attribute set contains an attribute set of <emphasis>option
definitions</emphasis>. If the module does not have any imported
modules or any option declarations, then this attribute set can be used
in place of its parent attribute set. This is a common case for simple
modules such
as <filename>/etc/nixos/configuration.nix</filename>.</para>
</callout>
</calloutlist>
</para>
<!-- option definitions -->
<para xml:id="para-opt-def">A module defines a configuration which would be
interpreted by other modules. To define a configuration, a module needs
to provide option definitions. An option definition is a simple
attribute assignment.</para>
<para>Option definitions are made in a declarative manner. Without
properties, options will always be defined with the same value. To
introduce more flexibility in the system, option definitions are guarded
by <emphasis>properties</emphasis>.</para>
<para>Properties are means to introduce conditional values inside option
definitions. This conditional values can be distinguished in two
categories. The condition which are local to the current configuration
and conditions which are dependent on others configurations. Local
properties are <varname>mkIf</varname>
and <varname>mkAssert</varname>. Global properties
are <varname>mkOverride</varname>, <varname>mkDefault</varname>
and <varname>mkOrder</varname>.</para>
<para><varname>mkIf</varname> is used to remove the option definitions which
are below it if the condition is evaluated to
false. <varname>mkAssert</varname> expects the condition to be evaluated
to true otherwise it raises an error message.</para>
<para><varname>mkOverride</varname> is used to mask previous definitions if
the current value has a lower mask number. The mask value is 100 (default)
for any option definition which does not use this property.
Thus, <varname>mkDefault</varname> is just a short-cut with a higher mask
(1000) than the default mask value. This means that a module can set an
option definition as a preference, and still let another module defining
it with a different value without using any property.</para>
<para><varname>mkOrder</varname> is used to sort definitions based on the
rank number. The rank number will sort all options definitions before
giving the sorted list of option definition to the merge function defined
in the option declaration. A lower rank will move the definition to the
beginning and a higher rank will move the option toward the end. The
default rank is 100.</para>
<!-- option declarations -->
<para xml:id="para-opt-decl">A module may declare options which are used by
other module to change the configuration provided by the current module.
Changes to the option definitions are made with properties which are using
values extracted from the result of the merge of all modules
(the <varname>config</varname> argument).</para>
<para>The <varname>config</varname> argument reproduce the same hierarchy of
all options declared in all modules. For each option, the result of the
option is available, it is either the default value or the merge of all
definitions of the option.</para>
<para>Options are declared with the
function <varname>pkgs.lib.mkOption</varname>. This function expects an
attribute set which at least provides a description. A default value, an
example, a type, a merge function and a post-process function can be
added.</para>
<para>Types are used to provide a merge strategy for options and to ensure
the type of each option definitions. They are defined
in <varname>pkgs.lib.types</varname>.</para>
<para>The merge function expects a list of option definitions and merge
them to obtain one result of the same type.</para>
<para>The post-process function (named <varname>apply</varname>) takes the
result of the merge or of the default value, and produce an output which
could have a different type than the type expected by the option.</para>
<!-- end -->
<example xml:id='locate-example'><title>Locate Module Example</title>
<programlisting>
{ config, pkgs, ... }:
with pkgs.lib;
let
cfg = config.services.locate;
locatedb = "/var/cache/locatedb";
logfile = "/var/log/updatedb";
cmd =''root updatedb --localuser=nobody --output=${locatedb} > ${logfile}'';
in
{
imports = [ /etc/nixos/nixos/modules/services/scheduling/cron.nix ];
options = {
services.locate = {
enable = mkOption {
default = false;
example = true;
type = with types; bool;
description = ''
If enabled, NixOS will periodically update the database of
files used by the <command>locate</command> command.
'';
};
period = mkOption {
default = "15 02 * * *";
type = with types; uniq string;
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 = mkIf cfg.enable {
services.cron = {
enable = true;
systemCronJobs = "${cfg.period} root ${cmd}";
};
};
}</programlisting>
</example>
<para><xref linkend='locate-example' /> illustrates a module which handles
the regular update of the database which index all files on the file
system. This modules has option definitions to rely on the cron service
to run the command at predefined dates. In addition, this modules
provides option declarations to enable the indexing and to use different
period of time to run the indexing. Properties are used to prevent
ambiguous definitions of option (enable locate service and disable cron
services) and to ensure that no options would be defined if the locate
service is not enabled.</para>
</section>
<!--===============================================================-->
<section>
<title>Building specific parts of NixOS</title>
<para>
<screen>
$ nix-build /etc/nixos/nixos -A <replaceable>attr</replaceable></screen>
where <replaceable>attr</replaceable> is an attribute in
<filename>/etc/nixos/nixos/default.nix</filename>. Attributes of interest include:
<variablelist>
<varlistentry>
<term><varname>config</varname></term>
<listitem><para>The computer configuration generated from
the <envar>NIXOS_CONFIG</envar> environment variable (default
is <filename>/etc/nixos/configuration.nix</filename>) with the NixOS
default set of modules.</para></listitem>
</varlistentry>
<varlistentry>
<term><varname>system</varname></term>
<listitem><para>The derivation which build your computer system. It is
built by the command <command>nixos-rebuild
build</command></para></listitem>
</varlistentry>
<varlistentry>
<term><varname>vm</varname></term>
<listitem><para>The derivation which build your computer system inside a
virtual machine. It is built by the command <command>nixos-rebuild
build-vm</command></para></listitem>
</varlistentry>
</variablelist>
</para>
<para>
Most parts of NixOS can be built through the <varname>config</varname>
attribute set. This attribute set allows you to have a view of the merged
option definitions and all its derivations. Important derivations are store
inside the option <option>system.build</option> and can be listed with the
command <command>nix-instantiate --xml --eval-only /etc/nixos/nixos -A
config.system.build</command>
</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>
$ export NIXOS_CONFIG=/etc/nixos/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix
$ nix-build /etc/nixos/nixos -A config.system.build.isoImage</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/building the NixOS Manual</title>
<para>A quick way to see if your documentation improvements
or option descriptions look good:
<screen>
$ nix-build -A config.system.build.manual</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>
$ export NIXOS_CONFIG=/etc/nixos/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix
$ nix-build /etc/nixos/nixos -A config.system.build.nixosInstall
$ 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>Testing the <literal>initrd</literal></title>
<para>A quick way to test whether the kernel and the initial ramdisk
boot correctly is to use QEMUs <option>-kernel</option> and
<option>-initrd</option> options:
<screen>
$ nix-build /etc/nixos/nixos -A config.system.build.initialRamdisk -o initrd
$ nix-build /etc/nixos/nixos -A config.system.build.kernel -o kernel
$ qemu-system-x86_64 -kernel ./kernel/bzImage -initrd ./initrd/initrd -hda /dev/null
</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>
<para>
<!-- The following is adapted from
http://wiki.nixos.org/wiki/NixOS_VM_tests, by Eelco Dolstra. -->
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,340 @@
<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>
</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>, 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>. This will
make the file system configuration independent from device
changes.</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>.</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 configuration that
you want to be built and activated, and then NixOS takes care of
realising that configuration. The command
<command>nixos-option</command> can generate an initial
configuration file for you:
<screen>
$ nixos-option --install</screen>
It tries to figure out the kernel modules necessary for mounting
the root device, as well as various other hardware
characteristics. However, it doesnt try to figure out the
<option>fileSystems</option> option yet.</para>
<para>You should edit
<filename>/mnt/etc/nixos/configuration.nix</filename> to suit your
needs. The text editors <command>nano</command> and
<command>vim</command> are available.</para>
<para>You need to specify a root file system in
<option>fileSystems</option> and the target device for the Grub boot
loader in <option>boot.loader.grub.device</option>. See
<xref linkend="ch-options"/> for a list of the available configuration
options.</para>
<note><para>It is very important that you specify in the option
<option>boot.initrd.kernelModules</option> all 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-option --install</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>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>Optionally, you can run
<screen>
$ nixos-checkout</screen>
to make the installer use the latest NixOS/Nixpkgs sources from the
Git repository, rather than the sources on CD.</para></listitem>
-->
<listitem><para>Do the installation:
<screen>
$ nixos-install</screen>
Cross fingers.</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><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 <lineannotation>(idem)</lineannotation>
$ mkswap -L swap /dev/sda2 <lineannotation>(idem)</lineannotation>
$ mount LABEL=nixos /mnt
$ nixos-option --install
$ nano /mnt/etc/nixos/configuration.nix
<lineannotation>(in particular, set the fileSystems and swapDevices options)</lineannotation>
$ nixos-install
$ reboot</screen>
</example>
<example xml:id='ex-config'><title>NixOS configuration</title>
<screen>
{
boot.loader.grub.device = "/dev/sda";
fileSystems."/".device = "/dev/disk/by-label/nixos";
swapDevices =
[ { device = "/dev/disk/by-label/swap"; } ];
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>
<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>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 <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 the <literal>nixos-unstable</literal> channel. (A channel is a
Nix mechanism for distributing Nix expressions and associated
binaries.) The NixOS channel is updated automatically from NixOSs
Git repository after running certain tests and building most
packages.</para>
<para>NixOS automatically subscribes you to the NixOS channel. If for
some reason this is not the case, just do
<screen>
$ nix-channel --add http://nixos.org/channels/nixos-unstable
</screen>
You can then upgrade NixOS to the latest version in the channel by
running
<screen>
$ nix-channel --update nixos
</screen>
and running the <command>nixos-rebuild</command> command as described
in <xref linkend="sec-changing-config"/>.</para>
</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,170 @@
<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>-i</option>
<option>v</option>
<option>d</option>
<option>l</option>
</group>
<arg choice='plain'><replaceable>option.name</replaceable></arg>
</cmdsynopsis>
<cmdsynopsis>
<command>nixos-option</command>
<arg choice='plain'><option>--install</option></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 in contained in the attribute set. This could used to provide
completion in some editors.</para>
<para>When the option <option>--install</option> (or <option>-i</option>) is
used with no option name, this command generates a template configuration
with a scan of the target system. It produces a template configuration
in <filename>/etc/nixos/configuration.nix</filename>, and a scan of the
machine in <filename>/etc/nixos/hardware-configuration.nix</filename>. The
scan of the machine is produced
by <command>nixos-hardware-scan</command>.</para>
</refsection>
<refsection><title>Options</title>
<para>This command accepts the following options:</para>
<variablelist>
<varlistentry>
<term><option>--install</option>, <option>-i</option></term>
<listitem>
<para>Use the installation configuration instead of current system
configuration. Generate a template configuration if no option name is
specified.</para>
</listitem>
</varlistentry>
<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>mountPoint</envar></term>
<listitem>
<para>Location of the target file system. Defaults to
<filename>/mnt</filename>. This environment variable is only used in
combinaison with <option>--install</option> option.</para>
</listitem>
</varlistentry>
<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:
/etc/nixos/nixos/modules/system/boot/kernel.nix
/etc/nixos/nixos/modules/hardware/network/rt73.nix
/etc/nixos/nixos/modules/hardware/network/intel-3945abg.nix
/etc/nixos/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,300 @@
<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><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 /etc/nixos/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
profile <filename>/nix/var/nix/profiles/system</filename>.)</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</year>
<year>2008</year>
<year>2009</year>
<holder>Eelco Dolstra</holder>
</copyright>
</info>
<xi:include href="man-configuration.xml" />
<xi:include href="man-nixos-rebuild.xml" />
<xi:include href="man-nixos-option.xml" />
<xi:include href="man-nixos-build-vms.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/nixos/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,187 @@
<?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>
<para>
<emphasis>Default:</emphasis>
<xsl:text> </xsl:text>
<xsl:choose>
<xsl:when test="attr[@name = 'default']">
<literal>
<xsl:apply-templates select="attr[@name = 'default']" />
</literal>
</xsl:when>
<xsl:otherwise>
none
</xsl:otherwise>
</xsl:choose>
</para>
<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>
<literal>
<xsl:apply-templates select="attr[@name = 'example']" />
</literal>
</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="string">
<!-- !!! escaping -->
<xsl:text>"</xsl:text><xsl:value-of select="str:replace(str:replace(str:replace(@value, '\', '\\'), '&quot;', '\&quot;'), '&#010;', '\n')" /><xsl:text>"</xsl:text>
</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">
{
<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">
<xsl:choose>
<xsl:when test="attr[@name = 'url']/string/@value">
<replaceable>(download of <xsl:value-of select="attr[@name = 'url']/string/@value" />)</replaceable>
</xsl:when>
<xsl:otherwise>
<replaceable>(build of <xsl:value-of select="attr[@name = 'name']/string/@value" />)</replaceable>
</xsl:otherwise>
</xsl:choose>
</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="$revision != 'local' and contains(@value, '/modules/')">
<xsl:attribute name="xlink:href">https://github.com/NixOS/nixos/blob/<xsl:value-of select="$revision"/>/modules/<xsl:value-of select="substring-after(@value, '/modules/')"/></xsl:attribute>
</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="contains(@value, '/modules/')">
&lt;nixos/modules/<xsl:value-of select="substring-after(@value, '/modules/')"/>&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>

View File

@ -0,0 +1,80 @@
<chapter xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Configuration in home directory</title>
<!--===============================================================-->
<section>
<title>Compiz Fusion</title>
<para>
Compiz Fusion is just a set of plugins for Compiz. Your best interest is to have
them found both by Compiz and by Compiz Configuration Settings (also in Compiz Fusion
distribution). By default they look in Compiz installation path and in home directory.
You do not need to track /nix/store manually - everything is already in
/run/current-system/sw/share.
<orderedlist>
<listitem><para><filename>$HOME/.compiz/plugins</filename>
should contain plugins you want to load. All the installed
plugins are available in
<filename>/run/current-system/sw/share/compiz-plugins/compiz/</filename>,
so you can use symlinks to this directory.
</para></listitem>
<listitem><para><filename>$HOME/.compiz/metadata</filename>
should contain metadata (definition of configuration options) for plugins
you want to load. All the installed metadata is available in
<filename>/run/current-system/sw/share/compiz/</filename>,
so you can use symlinks to this directory.
</para></listitem>
<listitem><para>
Probably a way to load <literal>GConf</literal> configuration backend by default
should be found, but if you run <literal>Compiz</literal> with
<literal>GConf</literal> configuration (default for <literal>X server</literal> job
for now), you have to link
<filename>/run/current-system/sw/share/compizconfig/backends/</filename>
into <filename>$HOME/.compizconfig/backends</filename> directory.
</para></listitem>
</orderedlist>
To summarize the above, these are the commands you have to execute
<command>ln -s /run/current-system/sw/share/compiz/ $HOME/.compiz/metadata</command>
<command>ln -s /run/current-system/sw/share/compiz-plugins/compiz/ $HOME/.compiz/plugins</command>
<command>ln -s /run/current-system/sw/share/compizconfig/backends/ $HOME/.compizconfig/backends</command>
Now you can launch <literal>ccsm</literal> and configure everything. You should select
GConf as a backend in the preferences menu of <literal>ccsm</literal>
</para>
</section>
<section>
<title>Pidgin-LaTeX</title>
<para>
To have pidgin-latex plugin working after installation, you need the following:
<orderedlist>
<listitem><para>
Symlink <filename>/run/current-system/sw/share/pidgin-latex/pidgin-latex.so</filename>
to <filename>$HOME/.purple/plugins/pidgin-latex.so</filename>
</para></listitem>
<listitem><para>
Enable smileys. If you do not want to, you can create
<filename>$HOME/.purple/smileys/empty/theme</filename> with the following contents:
<programlisting>
Name=Empty
Description=No predefined smileys
Author=Nobody
</programlisting>
Enabling this theme will enable smileys, but define none.
</para></listitem>
<listitem><para>
Enable the plugin.
</para></listitem>
</orderedlist>
</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> { 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; };
}

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

@ -0,0 +1,73 @@
# 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
, nixpkgs ? <nixpkgs>
}:
let extraArgs_ = extraArgs; pkgs_ = pkgs; system_ = system; in
rec {
# These are the NixOS modules that constitute the system configuration.
configComponents = modules ++ baseModules;
# Merge the option definitions in all modules, forming the full
# system configuration. It's not checked for undeclared options.
systemModule =
pkgs.lib.fixMergeModules configComponents extraArgs;
optionDefinitions = systemModule.config;
optionDeclarations = systemModule.options;
inherit (systemModule) 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 { 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 (
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 { system = system_; config = {}; };
}).optionDefinitions.nixpkgs;
in
{
inherit system;
inherit (nixpkgsOptions) config;
});
# Optionally check wether all config values have corresponding
# option declarations.
config =
let doCheck = optionDefinitions.environment.checkConfigurationOptions; in
assert doCheck -> pkgs.lib.checkModule "" systemModule;
systemModule.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

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("+");
});
});
});

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

@ -0,0 +1,244 @@
{ 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 = "hostname"; networking.hostName = "client"; }
];
buildrunner = writeText "vm-build" ''
source $1
${coreutils}/bin/mkdir -p $TMPDIR
cd $TMPDIR
$origBuilder $origArgs
exit $?
'';
testscript = ''
startAll;
${preBuild}
$client->succeed("env -i ${pkgs.bash}/bin/bash ${buildrunner} /tmp/xchg/saved-env >&2");
${postBuild}
'';
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,58 @@
{ config, pkgs, ... }:
with pkgs.lib;
{
options = {
fonts = {
enableFontConfig = mkOption { # !!! should be enableFontconfig
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,45 @@
{ config, pkgs, ... }:
with pkgs.lib;
{
options = {
gnu = mkOption {
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,84 @@
{ 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 {
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 {
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 {
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 {
default = "us";
example = "fr";
description = "
The keyboard mapping table for the virtual consoles.
";
type = types.uniq types.string;
};
};
};
###### 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,88 @@
# /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 {
default = "";
example = "192.168.0.1 lanlocalhost";
description = ''
Additional entries to be appended to <filename>/etc/hosts</filename>.
'';
};
networking.dnsSingleRequest = pkgs.lib.mkOption {
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,23 @@
{ config, pkgs, ... }:
{
options = {
environment.noXlibs = pkgs.lib.mkOption {
default = false;
example = true;
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 = pkgs.lib.mkIf config.environment.noXlibs {
programs.ssh.setXAuthLocation = false;
fonts = {
enableCoreFonts = false;
enableFontConfig = false;
};
};
}

View File

@ -0,0 +1,63 @@
# Configuration for the Name Service Switch (/etc/nsswitch.conf).
{ config, pkgs, ... }:
with pkgs.lib;
let
inherit (config.services.avahi) nssmdns;
inherit (config.services.samba) nsswins;
in
{
options = {
# NSS modules. Hacky!
system.nssModules = mkOption {
internal = true;
default = [];
description = ''
Search path for NSS (Name Service Switch) modules. This allows
several DNS resolution methods to be specified via
<filename>/etc/nsswitch.conf</filename>.
'';
merge = mergeListOption;
apply = list:
{
inherit list;
path = makeLibraryPath list;
};
};
};
config = {
environment.etc =
[ # Name Service Switch configuration file. Required by the C library.
# !!! Factor out the mdns stuff. The avahi module should define
# an option used by this module.
{ source = pkgs.writeText "nsswitch.conf"
''
passwd: files ldap
group: files ldap
shadow: files ldap
hosts: files ${optionalString nssmdns "mdns_minimal [NOTFOUND=return]"} dns ${optionalString nssmdns "mdns"} ${optionalString nsswins "wins"} myhostname
networks: files dns
ethers: files
services: files
protocols: files
'';
target = "nsswitch.conf";
}
];
# Use nss-myhostname to ensure that our hostname always resolves to
# a valid IP address. It returns all locally configured IP
# addresses, or ::1 and 127.0.0.2 as fallbacks.
system.nssModules = [ pkgs.systemd ];
};
}

View File

@ -0,0 +1,108 @@
{ config, pkgs, ... }:
with pkgs.lib;
let
cfg = config.powerManagement;
in
{
###### interface
options = {
powerManagement = {
enable = mkOption {
default = true;
description =
''
Whether to enable power management. This includes support
for suspend-to-RAM and powersave features on laptops.
'';
};
resumeCommands = mkOption {
default = "";
description = "Commands executed after the system resumes from suspend-to-RAM.";
};
powerUpCommands = mkOption {
default = "";
example = "${pkgs.hdparm}/sbin/hdparm -B 255 /dev/sda";
description =
''
Commands executed when the machine powers up. That is,
they're executed both when the system first boots and when
it resumes from suspend or hibernation.
'';
};
powerDownCommands = mkOption {
default = "";
example = "${pkgs.hdparm}/sbin/hdparm -B 255 /dev/sda";
description =
''
Commands executed when the machine powers down. That is,
they're executed both when the system shuts down and when
it goes to suspend or hibernation.
'';
};
};
};
###### implementation
config = mkIf cfg.enable {
# Enable the ACPI daemon. Not sure whether this is essential.
services.acpid.enable = true;
boot.kernelModules =
[ "acpi_cpufreq" "powernow-k8" "cpufreq_performance" "cpufreq_powersave" "cpufreq_ondemand"
"cpufreq_conservative"
];
powerManagement.cpuFreqGovernor = mkDefault "ondemand";
powerManagement.scsiLinkPolicy = mkDefault "min_power";
systemd.targets.post-resume = {
description = "Post-Resume Actions";
requires = [ "post-resume.service" ];
after = [ "post-resume.service" ];
wantedBy = [ "sleep.target" ];
unitConfig.StopWhenUnneeded = true;
};
# Service executed before suspending/hibernating.
systemd.services."pre-sleep" =
{ description = "Pre-Sleep Actions";
wantedBy = [ "sleep.target" ];
before = [ "sleep.target" ];
script =
''
${cfg.powerDownCommands}
'';
serviceConfig.Type = "oneshot";
};
systemd.services."post-resume" =
{ description = "Post-Resume Actions";
after = [ "suspend.target" "hibernate.target" "hybrid-sleep.target" ];
script =
''
${cfg.resumeCommands}
${cfg.powerUpCommands}
'';
serviceConfig.Type = "oneshot";
};
};
}

View File

@ -0,0 +1,148 @@
{ config, pkgs, ... }:
with pkgs.lib;
with pkgs;
let
cfg = config.hardware.pulseaudio;
uid = config.ids.uids.pulseaudio;
gid = config.ids.gids.pulseaudio;
pulseRuntimePath = "/var/run/pulse";
# Create pulse/client.conf even if PulseAudio is disabled so
# that we can disable the autospawn feature in programs that
# are built with PulseAudio support (like KDE).
clientConf = writeText "client.conf" ''
autospawn=${if (cfg.enable && !cfg.systemWide) then "yes" else "no"}
${optionalString (cfg.enable && !cfg.systemWide)
"daemon-binary=${cfg.package}/bin/pulseaudio"}
'';
# Write an /etc/asound.conf that causes all ALSA applications to
# be re-routed to the PulseAudio server through ALSA's Pulse
# plugin.
alsaConf = writeText "asound.conf" ''
pcm_type.pulse {
lib ${alsaPlugins}/lib/alsa-lib/libasound_module_pcm_pulse.so
}
pcm.!default {
type pulse
hint.description "Default Audio Device (via PulseAudio)"
}
ctl_type.pulse {
lib ${alsaPlugins}/lib/alsa-lib/libasound_module_ctl_pulse.so
}
ctl.!default {
type pulse
}
'';
in {
options = {
hardware.pulseaudio = {
enable = mkOption {
default = false;
description = ''
Whether to enable the PulseAudio sound server.
'';
};
systemWide = mkOption {
type = types.bool;
default = false;
description = ''
If false, a PulseAudio server is launched automatically for
each user that tries to use the sound system. The server runs
with user privileges. This is the recommended and most secure
way to use PulseAudio. If true, one system-wide PulseAudio
server is launched on boot, running as the user "pulse".
Please read the PulseAudio documentation for more details.
'';
};
configFile = mkOption {
type = types.uniq types.path;
default = "${pulseaudio}/etc/pulse/default.pa";
description = ''
The path to the configuration the PulseAudio server
should use. By default, the "default.pa" configuration
from the PulseAudio distribution is used.
'';
};
package = mkOption {
default = pulseaudio;
example = "pulseaudio.override { jackaudioSupport = true; }";
description = ''
The PulseAudio derivation to use. This can be used to enable
features (such as JACK support) that are not enabled in the
default PulseAudio in Nixpkgs.
'';
};
};
};
config = mkMerge [
{
environment.etc = singleton {
target = "pulse/client.conf";
source = clientConf;
};
}
(mkIf cfg.enable {
environment.systemPackages = [ cfg.package ];
environment.etc = singleton {
target = "asound.conf";
source = alsaConf;
};
# Allow PulseAudio to get realtime priority using rtkit.
security.rtkit.enable = true;
})
(mkIf (cfg.enable && !cfg.systemWide) {
environment.etc = singleton {
target = "pulse/default.pa";
source = cfg.configFile;
};
})
(mkIf (cfg.enable && cfg.systemWide) {
users.extraUsers.pulse = {
# For some reason, PulseAudio wants UID == GID.
uid = assert uid == gid; uid;
group = "pulse";
extraGroups = [ "audio" ];
description = "PulseAudio system service user";
home = pulseRuntimePath;
};
users.extraGroups.pulse.gid = gid;
systemd.services.pulseaudio = {
description = "PulseAudio system-wide server";
wantedBy = [ "sound.target" ];
before = [ "sound.target" ];
path = [ cfg.package ];
environment.PULSE_RUNTIME_PATH = pulseRuntimePath;
preStart = ''
mkdir -p --mode 755 ${pulseRuntimePath}
chown -R pulse:pulse ${pulseRuntimePath}
'';
script = ''
exec pulseaudio --system -n --file="${cfg.configFile}"
'';
};
})
];
}

View File

@ -0,0 +1,179 @@
# This module defines a global environment configuration and
# a common configuration for all shells.
{ config, pkgs, ... }:
with pkgs.lib;
let
cfg = config.environment;
in
{
options = {
environment.variables = mkOption {
default = {};
description = ''
A set of environment variables used in the global environment.
The value of each variable can be either a string or a list of
strings. The latter is concatenated, interspersed with colon
characters.
'';
type = types.attrsOf (mkOptionType {
name = "a string or a list of strings";
merge = xs:
let xs' = evalProperties xs; in
if isList (head xs') then concatLists xs'
else if builtins.lessThan 1 (length xs') then abort "variable in environment.variables has multiple values"
else if !builtins.isString (head xs') then abort "variable in environment.variables does not have a string value"
else head xs';
});
apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else v);
};
environment.profiles = mkOption {
default = [];
description = ''
A list of profiles used to setup the global environment.
'';
type = types.listOf types.string;
};
environment.profileVariables = mkOption {
default = (p: {});
description = ''
A function which given a profile path should give back
a set of environment variables for that profile.
'';
# !!! this should be of the following type:
#type = types.functionTo (types.attrsOf (types.optionSet envVar));
# and envVar should be changed to something more like environOpts.
# Having unique `value' _or_ multiple `list' is much more useful
# than just sticking everything together with ':' unconditionally.
# Anyway, to have this type mentioned above
# types.optionSet needs to be transformed into a type constructor
# (it has a !!! mark on that in nixpkgs)
# for now we hack all this to be
type = types.functionTo (types.attrsOf (types.listOf types.string));
};
# !!! isn't there a better way?
environment.extraInit = mkOption {
default = "";
description = ''
Shell script code called during global environment initialisation
after all variables and profileVariables have been set.
This code is asumed to be shell-independent, which means you should
stick to pure sh without sh word split.
'';
type = types.lines;
};
environment.shellInit = mkOption {
default = "";
description = ''
Shell script code called during shell initialisation.
This code is asumed to be shell-independent, which means you should
stick to pure sh without sh word split.
'';
type = types.lines;
};
environment.loginShellInit = mkOption {
default = "";
description = ''
Shell script code called during login shell initialisation.
This code is asumed to be shell-independent, which means you should
stick to pure sh without sh word split.
'';
type = types.lines;
};
environment.interactiveShellInit = mkOption {
default = "";
description = ''
Shell script code called during interactive shell initialisation.
This code is asumed to be shell-independent, which means you should
stick to pure sh without sh word split.
'';
type = types.lines;
};
environment.shellAliases = mkOption {
default = {};
example = { ll = "ls -l"; };
description = ''
An attribute set that maps aliases (the top level attribute names in
this option) to command strings or directly to build outputs. The
aliases are added to all users' shells.
'';
type = types.attrs; # types.attrsOf types.stringOrPath;
};
environment.binsh = mkOption {
default = "${config.system.build.binsh}/bin/sh";
example = "\${pkgs.dash}/bin/dash";
type = types.path;
description = ''
The shell executable that is linked system-wide to
<literal>/bin/sh</literal>. Please note that NixOS assumes all
over the place that shell to be Bash, so override the default
setting only if you know exactly what you're doing.
'';
};
environment.shells = mkOption {
default = [];
example = [ "/run/current-system/sw/bin/zsh" ];
description = ''
A list of permissible login shells for user accounts.
No need to mention <literal>/bin/sh</literal>
here, it is placed into this list implicitly.
'';
type = types.listOf types.path;
};
};
config = {
system.build.binsh = pkgs.bashInteractive;
environment.etc."shells".text =
''
${concatStringsSep "\n" cfg.shells}
/bin/sh
'';
system.build.setEnvironment = pkgs.writeText "set-environment"
''
${concatStringsSep "\n" (
(mapAttrsToList (n: v: ''export ${n}="${concatStringsSep ":" v}"'')
# This line is a kind of a hack because of !!! note above
(zipAttrsWith (const concatLists) ([ (mapAttrs (n: v: [ v ]) cfg.variables) ] ++ map cfg.profileVariables cfg.profiles))))}
${cfg.extraInit}
# The setuid wrappers override other bin directories.
export PATH="${config.security.wrapperDir}:$PATH"
# ~/bin if it exists overrides other bin directories.
export PATH="$HOME/bin:$PATH"
'';
system.activationScripts.binsh = stringAfter [ "stdio" ]
''
# Create the required /bin/sh symlink; otherwise lots of things
# (notably the system() function) won't work.
mkdir -m 0755 -p /bin
ln -sfn "${cfg.binsh}" /bin/.sh.tmp
mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh
'';
};
}

View File

@ -0,0 +1,124 @@
{ config, pkgs, utils, ... }:
with pkgs.lib;
with utils;
{
###### interface
options = {
swapDevices = mkOption {
default = [];
example = [
{ device = "/dev/hda7"; }
{ device = "/var/swapfile"; }
{ label = "bigswap"; }
];
description = ''
The swap devices and swap files. These must have been
initialised using <command>mkswap</command>. Each element
should be an attribute set specifying either the path of the
swap device or file (<literal>device</literal>) or the label
of the swap device (<literal>label</literal>, see
<command>mkswap -L</command>). Using a label is
recommended.
'';
type = types.listOf types.optionSet;
options = {config, options, ...}: {
options = {
device = mkOption {
example = "/dev/sda3";
type = types.uniq types.string;
description = "Path of the device.";
};
label = mkOption {
example = "swap";
type = types.uniq types.string;
description = ''
Label of the device. Can be used instead of <varname>device</varname>.
'';
};
size = mkOption {
default = null;
example = 2048;
type = types.nullOr types.int;
description = ''
If this option is set, device is interpreted as the
path of a swapfile that will be created automatically
with the indicated size (in megabytes) if it doesn't
exist.
'';
};
priority = mkOption {
default = null;
example = 2048;
type = types.nullOr types.int;
description = ''
Specify the priority of the swap device. Priority is a value between 0 and 32767.
Higher numbers indicate higher priority.
null lets the kernel choose a priority, which will show up as a negative value.
'';
};
};
config = {
device =
if options.label.isDefined then
"/dev/disk/by-label/${config.label}"
else
mkNotdef;
};
};
};
};
config = mkIf ((length config.swapDevices) != 0) {
system.requiredKernelConfig = with config.lib.kernelConfig; [
(isYes "SWAP")
];
# Create missing swapfiles.
# FIXME: support changing the size of existing swapfiles.
systemd.services =
let
createSwapDevice = sw:
assert sw.device != "";
let device' = escapeSystemdPath sw.device; in
nameValuePair "mkswap-${escapeSystemdPath sw.device}"
{ description = "Initialisation of Swapfile ${sw.device}";
wantedBy = [ "${device'}.swap" ];
before = [ "${device'}.swap" ];
path = [ pkgs.utillinux ];
script =
''
if [ ! -e "${sw.device}" ]; then
fallocate -l ${toString sw.size}M "${sw.device}" ||
dd if=/dev/zero of="${sw.device}" bs=1M count=${toString sw.size}
mkswap ${sw.device}
fi
'';
unitConfig.RequiresMountsFor = [ "${dirOf sw.device}" ];
unitConfig.DefaultDependencies = false; # needed to prevent a cycle
serviceConfig.Type = "oneshot";
};
in listToAttrs (map createSwapDevice (filter (sw: sw.size != null) config.swapDevices));
};
}

View File

@ -0,0 +1,69 @@
{ config, pkgs, ... }:
with pkgs.lib;
let
sysctlOption = mkOptionType {
name = "sysctl option value";
check = x: builtins.isBool x || builtins.isString x || builtins.isInt x;
merge = xs: last xs; # FIXME: hacky way to allow overriding in configuration.nix.
};
in
{
options = {
boot.kernel.sysctl = mkOption {
default = {};
example = {
"net.ipv4.tcp_syncookies" = false;
"vm.swappiness" = 60;
};
type = types.attrsOf sysctlOption;
description = ''
Runtime parameters of the Linux kernel, as set by
<citerefentry><refentrytitle>sysctl</refentrytitle>
<manvolnum>8</manvolnum></citerefentry>. Note that sysctl
parameters names must be enclosed in quotes
(e.g. <literal>"vm.swappiness"</literal> instead of
<literal>vm.swappiness</literal>). The value of each parameter
may be a string, integer or Boolean.
'';
};
};
config = {
environment.etc."sysctl.d/nixos.conf".text =
concatStrings (mapAttrsToList (n: v: "${n}=${if v == false then "0" else toString v}\n") config.boot.kernel.sysctl);
systemd.services.systemd-sysctl =
{ description = "Apply Kernel Variables";
before = [ "sysinit.target" "shutdown.target" ];
wantedBy = [ "sysinit.target" "multi-user.target" ];
restartTriggers = [ config.environment.etc."sysctl.d/nixos.conf".source ];
unitConfig.DefaultDependencies = false; # needed to prevent a cycle
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
ExecStart = "${config.systemd.package}/lib/systemd/systemd-sysctl";
};
};
# Enable hardlink and symlink restrictions. See
# https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=800179c9b8a1e796e441674776d11cd4c05d61d7
# for details.
boot.kernel.sysctl."fs.protected_hardlinks" = true;
boot.kernel.sysctl."fs.protected_symlinks" = true;
# Hide kernel pointers (e.g. in /proc/modules) for unprivileged
# users as these make it easier to exploit kernel vulnerabilities.
boot.kernel.sysctl."kernel.kptr_restrict" = 1;
};
}

View File

@ -0,0 +1,142 @@
# This module defines the packages that appear in
# /run/current-system/sw.
{ config, pkgs, ... }:
with pkgs.lib;
let
cfg = config.environment;
extraManpages = pkgs.runCommand "extra-manpages" { buildInputs = [ pkgs.help2man ]; }
''
mkdir -p $out/share/man/man1
help2man ${pkgs.gnutar}/bin/tar > $out/share/man/man1/tar.1
'';
requiredPackages =
[ config.environment.nix
pkgs.acl
pkgs.attr
pkgs.bashInteractive # bash with ncurses support
pkgs.bzip2
pkgs.coreutils
pkgs.cpio
pkgs.curl
pkgs.diffutils
pkgs.eject # HAL depends on it anyway
pkgs.findutils
pkgs.gawk
pkgs.glibc # for ldd, getent
pkgs.gnugrep
pkgs.gnupatch
pkgs.gnused
pkgs.gnutar
pkgs.gzip
pkgs.xz
pkgs.less
pkgs.libcap
pkgs.man
pkgs.nano
pkgs.ncurses
pkgs.netcat
pkgs.openssh
pkgs.pciutils
pkgs.perl
pkgs.procps
pkgs.rsync
pkgs.strace
pkgs.sysvtools
pkgs.time
pkgs.usbutils
pkgs.utillinux
extraManpages
];
in
{
options = {
environment = {
systemPackages = mkOption {
default = [];
example = "[ pkgs.icecat3 pkgs.thunderbird ]";
description = ''
The set of packages that appear in
/run/current-system/sw. These packages are
automatically available to all users, and are
automatically updated every time you rebuild the system
configuration. (The latter is the main difference with
installing them in the default profile,
<filename>/nix/var/nix/profiles/default</filename>.
'';
};
pathsToLink = mkOption {
# Note: We need `/lib' to be among `pathsToLink' for NSS modules
# to work.
default = [];
example = ["/"];
description = "List of directories to be symlinked in `/run/current-system/sw'.";
};
};
system = {
path = mkOption {
default = cfg.systemPackages;
description = ''
The packages you want in the boot environment.
'';
apply = list: pkgs.buildEnv {
name = "system-path";
paths = list;
inherit (cfg) pathsToLink;
ignoreCollisions = true;
# !!! Hacky, should modularise.
postBuild =
''
if [ -x $out/bin/update-mime-database -a -w $out/share/mime/packages ]; then
$out/bin/update-mime-database -V $out/share/mime
fi
if [ -x $out/bin/gtk-update-icon-cache -a -f $out/share/icons/hicolor/index.theme ]; then
$out/bin/gtk-update-icon-cache $out/share/icons/hicolor
fi
if [ -x $out/bin/glib-compile-schemas -a -w $out/share/glib-2.0/schemas ]; then
$out/bin/glib-compile-schemas $out/share/glib-2.0/schemas
fi
'';
};
};
};
};
config = {
environment.systemPackages = requiredPackages;
environment.pathsToLink =
[ "/bin"
"/etc/xdg"
"/info"
"/lib"
"/man"
"/sbin"
"/share/emacs"
"/share/org"
"/share/info"
"/share/terminfo"
"/share/man"
];
};
}

View File

@ -0,0 +1,36 @@
{ config, pkgs, ... }:
with pkgs.lib;
{
options = {
time = {
timeZone = mkOption {
default = "CET";
type = with types; uniq string;
example = "America/New_York";
description = "The time zone used when displaying times and dates.";
};
hardwareClockInLocalTime = mkOption {
default = false;
description = "If set, keep the hardware clock in local time instead of UTC.";
};
};
};
config = {
environment.variables.TZDIR = "/etc/zoneinfo";
environment.variables.TZ = config.time.timeZone;
environment.etc.localtime.source = "${pkgs.tzdata}/share/zoneinfo/${config.time.timeZone}";
environment.etc.zoneinfo.source = "${pkgs.tzdata}/share/zoneinfo";
};
}

View File

@ -0,0 +1,34 @@
{ config, pkgs, ... }:
with pkgs.lib;
# unixODBC drivers (this solution is not perfect.. Because the user has to
# ask the admin to add a driver.. but it's simple and works
{
###### interface
options = {
environment.unixODBCDrivers = mkOption {
default = [];
example = literalExample "map (x : x.ini) (with pkgs.unixODBCDrivers; [ mysql psql psqlng ] )";
description = ''
Specifies Unix ODBC drivers to be registered in
<filename>/etc/odbcinst.ini</filename>. You may also want to
add <literal>pkgs.unixODBC</literal> to the system path to get
a command line client to connnect to ODBC databases.
'';
};
};
###### implementation
config = mkIf (config.environment.unixODBCDrivers != []) {
environment.etc."odbcinst.ini".text =
let inis = config.environment.unixODBCDrivers;
in pkgs.lib.concatStringsSep "\n" inis;
};
}

View File

@ -0,0 +1,315 @@
{pkgs, config, ...}:
with pkgs.lib;
let
ids = config.ids;
users = config.users;
userOpts = { name, config, ... }: {
options = {
name = mkOption {
type = with types; uniq string;
description = "The name of the user account. If undefined, the name of the attribute set will be used.";
};
description = mkOption {
type = with types; uniq string;
default = "";
description = "A short description of the user account.";
};
uid = mkOption {
type = with types; uniq (nullOr int);
default = null;
description = "The account UID. If undefined, NixOS will select a free UID.";
};
group = mkOption {
type = with types; uniq string;
default = "nogroup";
description = "The user's primary group.";
};
extraGroups = mkOption {
type = types.listOf types.string;
default = [];
description = "The user's auxiliary groups.";
};
home = mkOption {
type = with types; uniq string;
default = "/var/empty";
description = "The user's home directory.";
};
shell = mkOption {
type = with types; uniq string;
default = "/run/current-system/sw/sbin/nologin";
description = "The path to the user's shell.";
};
createHome = mkOption {
type = types.bool;
default = false;
description = "If true, the home directory will be created automatically.";
};
useDefaultShell = mkOption {
type = types.bool;
default = false;
description = "If true, the user's shell will be set to <literal>users.defaultUserShell</literal>.";
};
password = mkOption {
type = with types; uniq (nullOr string);
default = null;
description = "The user's password. If undefined, no password is set for the user. Warning: do not set confidential information here because this data would be readable by all. This option should only be used for public account such as guest.";
};
isSystemUser = mkOption {
type = types.bool;
default = true;
description = "Indicates if the user is a system user or not.";
};
createUser = mkOption {
type = types.bool;
default = true;
description = "
Indicates if the user should be created automatically as a local user.
Set this to false if the user for instance is an LDAP user. NixOS will
then not modify any of the basic properties for the user account.
";
};
isAlias = mkOption {
type = types.bool;
default = false;
description = "If true, the UID of this user is not required to be unique and can thus alias another user.";
};
};
config = {
name = mkDefault name;
uid = mkDefault (attrByPath [name] null ids.uids);
shell = mkIf config.useDefaultShell (mkDefault users.defaultUserShell);
};
};
groupOpts = { name, config, ... }: {
options = {
name = mkOption {
type = with types; uniq string;
description = "The name of the group. If undefined, the name of the attribute set will be used.";
};
gid = mkOption {
type = with types; uniq (nullOr int);
default = null;
description = "The GID of the group. If undefined, NixOS will select a free GID.";
};
};
config = {
name = mkDefault name;
gid = mkDefault (attrByPath [name] null ids.gids);
};
};
# Note: the 'X' in front of the password is to distinguish between
# having an empty password, and not having a password.
serializedUser = u: "${u.name}\n${u.description}\n${if u.uid != null then toString u.uid else ""}\n${u.group}\n${toString (concatStringsSep "," u.extraGroups)}\n${u.home}\n${u.shell}\n${toString u.createHome}\n${if u.password != null then "X" + u.password else ""}\n${toString u.isSystemUser}\n${toString u.createUser}\n${toString u.isAlias}\n";
usersFile = pkgs.writeText "users" (
let
p = partition (u: u.isAlias) (attrValues config.users.extraUsers);
in concatStrings (map serializedUser p.wrong ++ map serializedUser p.right));
in
{
###### interface
options = {
users.extraUsers = mkOption {
default = {};
type = types.loaOf types.optionSet;
example = {
alice = {
uid = 1234;
description = "Alice";
home = "/home/alice";
createHome = true;
group = "users";
extraGroups = ["wheel"];
shell = "/bin/sh";
password = "foobar";
};
};
description = ''
Additional user accounts to be created automatically by the system.
This can also be used to set options for root.
'';
options = [ userOpts ];
};
users.extraGroups = mkOption {
default = {};
example =
{ students.gid = 1001;
hackers = { };
};
type = types.loaOf types.optionSet;
description = ''
Additional groups to be created automatically by the system.
'';
options = [ groupOpts ];
};
};
###### implementation
config = {
users.extraUsers = {
root = {
description = "System administrator";
home = "/root";
shell = config.users.defaultUserShell;
group = "root";
};
nobody = {
description = "Unprivileged account (don't use!)";
};
};
users.extraGroups = {
root = { };
wheel = { };
disk = { };
kmem = { };
tty = { };
floppy = { };
uucp = { };
lp = { };
cdrom = { };
tape = { };
audio = { };
video = { };
dialout = { };
nogroup = { };
users = { };
nixbld = { };
utmp = { };
adm = { }; # expected by journald
};
system.activationScripts.rootPasswd = stringAfter [ "etc" ]
''
# If there is no password file yet, create a root account with an
# empty password.
if ! test -e /etc/passwd; then
rootHome=/root
touch /etc/passwd; chmod 0644 /etc/passwd
touch /etc/group; chmod 0644 /etc/group
touch /etc/shadow; chmod 0600 /etc/shadow
# Can't use useradd, since it complains that it doesn't know us
# (bootstrap problem!).
echo "root:x:0:0:System administrator:$rootHome:${config.users.defaultUserShell}" >> /etc/passwd
echo "root::::::::" >> /etc/shadow
fi
'';
system.activationScripts.users = stringAfter [ "groups" ]
''
echo "updating users..."
cat ${usersFile} | while true; do
read name || break
read description
read uid
read group
read extraGroups
read home
read shell
read createHome
read password
read isSystemUser
read createUser
read isAlias
if [ -z "$createUser" ]; then
continue
fi
if ! curEnt=$(getent passwd "$name"); then
useradd ''${isSystemUser:+--system} \
--comment "$description" \
''${uid:+--uid $uid} \
--gid "$group" \
--groups "$extraGroups" \
--home "$home" \
--shell "$shell" \
''${createHome:+--create-home} \
''${isAlias:+--non-unique} \
"$name"
if test "''${password:0:1}" = 'X'; then
(echo "''${password:1}"; echo "''${password:1}") | ${pkgs.shadow}/bin/passwd "$name"
fi
else
#echo "updating user $name..."
oldIFS="$IFS"; IFS=:; set -- $curEnt; IFS="$oldIFS"
prevUid=$3
prevHome=$6
# Don't change the home directory if it's the same to prevent
# unnecessary warnings about logged in users.
if test "$prevHome" = "$home"; then unset home; fi
usermod \
--comment "$description" \
--gid "$group" \
--groups "$extraGroups" \
''${home:+--home "$home"} \
--shell "$shell" \
"$name"
fi
done
'';
system.activationScripts.groups = stringAfter [ "rootPasswd" "binsh" "etc" "var" ]
''
echo "updating groups..."
createGroup() {
name="$1"
gid="$2"
if ! curEnt=$(getent group "$name"); then
groupadd --system \
''${gid:+--gid $gid} \
"$name"
fi
}
${flip concatMapStrings (attrValues config.users.extraGroups) (g: ''
createGroup '${g.name}' '${toString g.gid}'
'')}
'';
};
}

View File

@ -0,0 +1,26 @@
{pkgs, config, ...}:
{
###### interface
options = {
hardware.enableAllFirmware = pkgs.lib.mkOption {
default = false;
type = pkgs.lib.types.bool;
description = ''
Turn on this option if you want to enable all the firmware shipped with Debian/Ubuntu.
'';
};
};
###### implementation
config = pkgs.lib.mkIf config.hardware.enableAllFirmware {
hardware.firmware = [ "${pkgs.firmwareLinuxNonfree}/lib/firmware" ];
};
}

View File

@ -0,0 +1,29 @@
{ config, pkgs, ... }:
with pkgs.lib;
{
###### interface
options = {
hardware.cpu.amd.updateMicrocode = mkOption {
default = false;
type = types.bool;
description = ''
Update the CPU microcode for AMD processors.
'';
};
};
###### implementation
config = mkIf config.hardware.cpu.amd.updateMicrocode {
hardware.firmware = [ "${pkgs.amdUcode}/lib/firmware" ];
boot.kernelModules = [ "microcode" ];
};
}

View File

@ -0,0 +1,29 @@
{ config, pkgs, ... }:
with pkgs.lib;
{
###### interface
options = {
hardware.cpu.intel.updateMicrocode = mkOption {
default = false;
type = types.bool;
description = ''
Update the CPU microcode for Intel processors.
'';
};
};
###### implementation
config = mkIf config.hardware.cpu.intel.updateMicrocode {
hardware.firmware = [ "${pkgs.microcodeIntel}/lib/firmware" ];
boot.kernelModules = [ "microcode" ];
};
}

View File

@ -0,0 +1,32 @@
{pkgs, config, ...}:
let kernelVersion = config.boot.kernelPackages.kernel.version; in
{
###### interface
options = {
networking.enableB43Firmware = pkgs.lib.mkOption {
default = false;
type = pkgs.lib.types.bool;
description = ''
Turn on this option if you want firmware for the NICs supported by the b43 module.
'';
};
};
###### implementation
config = pkgs.lib.mkIf config.networking.enableB43Firmware {
assertions = [ {
assertion = builtins.lessThan 0 (builtins.compareVersions kernelVersion "3.2");
message = "b43 firmware for kernels older than 3.2 not packaged yet!";
} ];
hardware.firmware = [ pkgs.b43Firmware_5_1_138 ];
};
}

View File

@ -0,0 +1,3 @@
{
hardware.enableAllFirmware = true;
}

View File

@ -0,0 +1,3 @@
{
hardware.enableAllFirmware = true;
}

View File

@ -0,0 +1,30 @@
{ config, pkgs, ... }:
{
###### interface
options = {
networking.enableIntel2100BGFirmware = pkgs.lib.mkOption {
default = false;
type = pkgs.lib.types.bool;
description = ''
Turn on this option if you want firmware for the Intel
PRO/Wireless 2100BG to be loaded automatically. This is
required if you want to use this device.
'';
};
};
###### implementation
config = pkgs.lib.mkIf config.networking.enableIntel2100BGFirmware {
hardware.enableAllFirmware = true;
};
}

View File

@ -0,0 +1,30 @@
{ config, pkgs, ... }:
{
###### interface
options = {
networking.enableIntel2200BGFirmware = pkgs.lib.mkOption {
default = false;
type = pkgs.lib.types.bool;
description = ''
Turn on this option if you want firmware for the Intel
PRO/Wireless 2200BG to be loaded automatically. This is
required if you want to use this device.
'';
};
};
###### implementation
config = pkgs.lib.mkIf config.networking.enableIntel2200BGFirmware {
hardware.enableAllFirmware = true;
};
}

View File

@ -0,0 +1,29 @@
{ config, pkgs, ... }:
{
###### interface
options = {
networking.enableIntel3945ABGFirmware = pkgs.lib.mkOption {
default = false;
type = pkgs.lib.types.bool;
description = ''
This option enables automatic loading of the firmware for the Intel
PRO/Wireless 3945ABG.
'';
};
};
###### implementation
config = pkgs.lib.mkIf config.networking.enableIntel3945ABGFirmware {
hardware.enableAllFirmware = true;
};
}

View File

@ -0,0 +1,3 @@
{
hardware.enableAllFirmware = true;
}

View File

@ -0,0 +1,3 @@
{
hardware.enableAllFirmware = true;
}

View File

@ -0,0 +1,3 @@
{
hardware.enableAllFirmware = true;
}

View File

@ -0,0 +1,3 @@
{
hardware.enableAllFirmware = true;
}

View File

@ -0,0 +1,3 @@
{
hardware.enableAllFirmware = true;
}

View File

@ -0,0 +1,3 @@
{
hardware.enableAllFirmware = true;
}

View File

@ -0,0 +1,26 @@
{pkgs, config, ...}:
{
###### interface
options = {
networking.enableRalinkFirmware = pkgs.lib.mkOption {
default = false;
type = pkgs.lib.types.bool;
description = ''
Turn on this option if you want firmware for the RT73 NIC.
'';
};
};
###### implementation
config = pkgs.lib.mkIf config.networking.enableRalinkFirmware {
hardware.enableAllFirmware = true;
};
}

View File

@ -0,0 +1,26 @@
{pkgs, config, ...}:
{
###### interface
options = {
networking.enableRTL8192cFirmware = pkgs.lib.mkOption {
default = false;
type = pkgs.lib.types.bool;
description = ''
Turn on this option if you want firmware for the RTL8192c (and related) NICs.
'';
};
};
###### implementation
config = pkgs.lib.mkIf config.networking.enableRTL8192cFirmware {
hardware.enableAllFirmware = true;
};
}

View File

@ -0,0 +1,9 @@
{pkgs, config, ...}:
{
hardware = {
pcmcia = {
firmware = [ (pkgs.lib.cleanSource ./firmware) ];
};
};
}

View File

@ -0,0 +1,8 @@
vers_1 5.0, "SMC", "SMC2632W", "Version 01.02", ""
manfid 0x0156, 0x0002
funcid network_adapter
cftable_entry 0x01 [default]
Vcc Vmin 3000mV Vmax 3300mV Iavg 300mA Ipeak 300mA
Idown 10mA
io 0x0000-0x003f [lines=6] [16bit]
irq mask 0xffff [level] [pulse]

View File

@ -0,0 +1,5 @@
{pkgs, config, ...}:
{
hardware.firmware = [ pkgs.zd1211fw ];
}

View File

@ -0,0 +1,59 @@
{ config, pkgs, ... }:
with pkgs.lib;
let
pcmciaUtils = pkgs.pcmciaUtils.passthru.function {
inherit (config.hardware.pcmcia) firmware config;
};
in
{
###### interface
options = {
hardware.pcmcia = {
enable = mkOption {
default = false;
merge = mergeEnableOption;
description = ''
Enable this option to support PCMCIA card.
'';
};
firmware = mkOption {
default = [];
merge = mergeListOption;
description = ''
List of firmware used to handle specific PCMCIA card.
'';
};
config = mkOption {
default = null;
description = ''
Path to the configuration file which map the memory, irq
and ports used by the PCMCIA hardware.
'';
};
};
};
###### implementation
config = mkIf config.hardware.pcmcia.enable {
boot.kernelModules = [ "pcmcia" ];
services.udev.packages = [ pcmciaUtils ];
environment.systemPackages = [ pcmciaUtils ];
};
}

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