07cb3bf3cc
Update the pinned channel in `md-to-db`, which bumps the Pandoc version, which fixes https://github.com/NixOS/nixpkgs/issues/125511 maybe.
245 lines
9.1 KiB
XML
245 lines
9.1 KiB
XML
<chapter xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude" xml:id="sec-writing-modules">
|
||
<title>Writing NixOS Modules</title>
|
||
<para>
|
||
NixOS has a modular system for declarative configuration. This
|
||
system combines multiple <emphasis>modules</emphasis> to produce the
|
||
full system configuration. One of the modules that constitute the
|
||
configuration is <literal>/etc/nixos/configuration.nix</literal>.
|
||
Most of the others live in the
|
||
<link xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/modules"><literal>nixos/modules</literal></link>
|
||
subdirectory of the Nixpkgs tree.
|
||
</para>
|
||
<para>
|
||
Each NixOS module is a file that handles one logical aspect of the
|
||
configuration, such as a specific kind of hardware, a service, or
|
||
network settings. A module configuration does not have to handle
|
||
everything from scratch; it can use the functionality provided by
|
||
other modules for its implementation. Thus a module can
|
||
<emphasis>declare</emphasis> options that can be used by other
|
||
modules, and conversely can <emphasis>define</emphasis> options
|
||
provided by other modules in its own implementation. For example,
|
||
the module
|
||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/security/pam.nix"><literal>pam.nix</literal></link>
|
||
declares the option <literal>security.pam.services</literal> that
|
||
allows other modules (e.g.
|
||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/ssh/sshd.nix"><literal>sshd.nix</literal></link>)
|
||
to define PAM services; and it defines the option
|
||
<literal>environment.etc</literal> (declared by
|
||
<link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/system/etc/etc.nix"><literal>etc.nix</literal></link>)
|
||
to cause files to be created in <literal>/etc/pam.d</literal>.
|
||
</para>
|
||
<para>
|
||
In <xref linkend="sec-configuration-syntax" />, we saw the following
|
||
structure of NixOS modules:
|
||
</para>
|
||
<programlisting language="nix">
|
||
{ config, pkgs, ... }:
|
||
|
||
{ option definitions
|
||
}
|
||
</programlisting>
|
||
<para>
|
||
This is actually an <emphasis>abbreviated</emphasis> form of module
|
||
that only defines options, but does not declare any. The structure
|
||
of full NixOS modules is shown in
|
||
<link linkend="ex-module-syntax">Example: Structure of NixOS
|
||
Modules</link>.
|
||
</para>
|
||
<anchor xml:id="ex-module-syntax" />
|
||
<para>
|
||
<emphasis role="strong">Example: Structure of NixOS
|
||
Modules</emphasis>
|
||
</para>
|
||
<programlisting language="nix">
|
||
{ config, pkgs, ... }:
|
||
|
||
{
|
||
imports =
|
||
[ paths of other modules
|
||
];
|
||
|
||
options = {
|
||
option declarations
|
||
};
|
||
|
||
config = {
|
||
option definitions
|
||
};
|
||
}
|
||
</programlisting>
|
||
<para>
|
||
The meaning of each part is as follows.
|
||
</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>
|
||
The first line makes the current Nix expression a function. The
|
||
variable <literal>pkgs</literal> contains Nixpkgs (by default,
|
||
it takes the <literal>nixpkgs</literal> entry of
|
||
<literal>NIX_PATH</literal>, see the
|
||
<link xlink:href="https://nixos.org/manual/nix/stable/#sec-common-env">Nix
|
||
manual</link> for further details), while
|
||
<literal>config</literal> contains the full system
|
||
configuration. This line can be omitted if there is no reference
|
||
to <literal>pkgs</literal> and <literal>config</literal> inside
|
||
the module.
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
This <literal>imports</literal> list enumerates the paths to
|
||
other NixOS modules that should be included in the evaluation of
|
||
the system configuration. A default set of modules is defined in
|
||
the file <literal>modules/module-list.nix</literal>. These don’t
|
||
need to be added in the import list.
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
The attribute <literal>options</literal> is a nested set of
|
||
<emphasis>option declarations</emphasis> (described below).
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>
|
||
The attribute <literal>config</literal> is a nested set of
|
||
<emphasis>option definitions</emphasis> (also described below).
|
||
</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<para>
|
||
<link linkend="locate-example">Example: NixOS Module for the
|
||
<quote>locate</quote> Service</link> shows a module that handles the
|
||
regular update of the <quote>locate</quote> database, an index of
|
||
all files in the file system. This module declares two options that
|
||
can be defined by other modules (typically the user’s
|
||
<literal>configuration.nix</literal>):
|
||
<literal>services.locate.enable</literal> (whether the database
|
||
should be updated) and <literal>services.locate.interval</literal>
|
||
(when the update should be done). It implements its functionality by
|
||
defining two options declared by other modules:
|
||
<literal>systemd.services</literal> (the set of all systemd
|
||
services) and <literal>systemd.timers</literal> (the list of
|
||
commands to be executed periodically by <literal>systemd</literal>).
|
||
</para>
|
||
<para>
|
||
Care must be taken when writing systemd services using
|
||
<literal>Exec*</literal> directives. By default systemd performs
|
||
substitution on <literal>%<char></literal> specifiers in these
|
||
directives, expands environment variables from
|
||
<literal>$FOO</literal> and <literal>${FOO}</literal>, splits
|
||
arguments on whitespace, and splits commands on
|
||
<literal>;</literal>. All of these must be escaped to avoid
|
||
unexpected substitution or splitting when interpolating into an
|
||
<literal>Exec*</literal> directive, e.g. when using an
|
||
<literal>extraArgs</literal> option to pass additional arguments to
|
||
the service. The functions
|
||
<literal>utils.escapeSystemdExecArg</literal> and
|
||
<literal>utils.escapeSystemdExecArgs</literal> are provided for
|
||
this, see <link linkend="exec-escaping-example">Example: Escaping in
|
||
Exec directives</link> for an example. When using these functions
|
||
system environment substitution should <emphasis>not</emphasis> be
|
||
disabled explicitly.
|
||
</para>
|
||
<anchor xml:id="locate-example" />
|
||
<para>
|
||
<emphasis role="strong">Example: NixOS Module for the
|
||
<quote>locate</quote> Service</emphasis>
|
||
</para>
|
||
<programlisting language="nix">
|
||
{ config, lib, pkgs, ... }:
|
||
|
||
with lib;
|
||
|
||
let
|
||
cfg = config.services.locate;
|
||
in {
|
||
options.services.locate = {
|
||
enable = mkOption {
|
||
type = types.bool;
|
||
default = false;
|
||
description = ''
|
||
If enabled, NixOS will periodically update the database of
|
||
files used by the locate command.
|
||
'';
|
||
};
|
||
|
||
interval = mkOption {
|
||
type = types.str;
|
||
default = "02:15";
|
||
example = "hourly";
|
||
description = ''
|
||
Update the locate database at this interval. Updates by
|
||
default at 2:15 AM every day.
|
||
|
||
The format is described in
|
||
systemd.time(7).
|
||
'';
|
||
};
|
||
|
||
# Other options omitted for documentation
|
||
};
|
||
|
||
config = {
|
||
systemd.services.update-locatedb =
|
||
{ description = "Update Locate Database";
|
||
path = [ pkgs.su ];
|
||
script =
|
||
''
|
||
mkdir -m 0755 -p $(dirname ${toString cfg.output})
|
||
exec updatedb \
|
||
--localuser=${cfg.localuser} \
|
||
${optionalString (!cfg.includeStore) "--prunepaths='/nix/store'"} \
|
||
--output=${toString cfg.output} ${concatStringsSep " " cfg.extraFlags}
|
||
'';
|
||
};
|
||
|
||
systemd.timers.update-locatedb = mkIf cfg.enable
|
||
{ description = "Update timer for locate database";
|
||
partOf = [ "update-locatedb.service" ];
|
||
wantedBy = [ "timers.target" ];
|
||
timerConfig.OnCalendar = cfg.interval;
|
||
};
|
||
};
|
||
}
|
||
</programlisting>
|
||
<anchor xml:id="exec-escaping-example" />
|
||
<para>
|
||
<emphasis role="strong">Example: Escaping in Exec
|
||
directives</emphasis>
|
||
</para>
|
||
<programlisting language="nix">
|
||
{ config, lib, pkgs, utils, ... }:
|
||
|
||
with lib;
|
||
|
||
let
|
||
cfg = config.services.echo;
|
||
echoAll = pkgs.writeScript "echo-all" ''
|
||
#! ${pkgs.runtimeShell}
|
||
for s in "$@"; do
|
||
printf '%s\n' "$s"
|
||
done
|
||
'';
|
||
args = [ "a%Nything" "lang=\${LANG}" ";" "/bin/sh -c date" ];
|
||
in {
|
||
systemd.services.echo =
|
||
{ description = "Echo to the journal";
|
||
wantedBy = [ "multi-user.target" ];
|
||
serviceConfig.Type = "oneshot";
|
||
serviceConfig.ExecStart = ''
|
||
${echoAll} ${utils.escapeSystemdExecArgs args}
|
||
'';
|
||
};
|
||
}
|
||
</programlisting>
|
||
<xi:include href="option-declarations.section.xml" />
|
||
<xi:include href="option-types.section.xml" />
|
||
<xi:include href="option-def.section.xml" />
|
||
<xi:include href="assertions.section.xml" />
|
||
<xi:include href="meta-attributes.section.xml" />
|
||
<xi:include href="importing-modules.section.xml" />
|
||
<xi:include href="replace-modules.section.xml" />
|
||
<xi:include href="freeform-modules.section.xml" />
|
||
<xi:include href="settings-options.section.xml" />
|
||
</chapter>
|