nixpkgs/nixos/doc/manual/from_md/development/writing-modules.chapter.xml
Naïm Favier 07cb3bf3cc
nixos/doc: bump Pandoc
Update the pinned channel in `md-to-db`, which bumps the Pandoc version,
which fixes https://github.com/NixOS/nixpkgs/issues/125511 maybe.
2022-12-27 17:23:25 +01:00

246 lines
9.1 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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 dont
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 users
<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>%&lt;char&gt;</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 = &quot;02:15&quot;;
example = &quot;hourly&quot;;
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 = &quot;Update Locate Database&quot;;
path = [ pkgs.su ];
script =
''
mkdir -m 0755 -p $(dirname ${toString cfg.output})
exec updatedb \
--localuser=${cfg.localuser} \
${optionalString (!cfg.includeStore) &quot;--prunepaths='/nix/store'&quot;} \
--output=${toString cfg.output} ${concatStringsSep &quot; &quot; cfg.extraFlags}
'';
};
systemd.timers.update-locatedb = mkIf cfg.enable
{ description = &quot;Update timer for locate database&quot;;
partOf = [ &quot;update-locatedb.service&quot; ];
wantedBy = [ &quot;timers.target&quot; ];
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 &quot;echo-all&quot; ''
#! ${pkgs.runtimeShell}
for s in &quot;$@&quot;; do
printf '%s\n' &quot;$s&quot;
done
'';
args = [ &quot;a%Nything&quot; &quot;lang=\${LANG}&quot; &quot;;&quot; &quot;/bin/sh -c date&quot; ];
in {
systemd.services.echo =
{ description = &quot;Echo to the journal&quot;;
wantedBy = [ &quot;multi-user.target&quot; ];
serviceConfig.Type = &quot;oneshot&quot;;
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>