diff options
Diffstat (limited to 'nixos/doc/manual/from_md/development/writing-modules.chapter.xml')
-rw-r--r-- | nixos/doc/manual/from_md/development/writing-modules.chapter.xml | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/nixos/doc/manual/from_md/development/writing-modules.chapter.xml b/nixos/doc/manual/from_md/development/writing-modules.chapter.xml new file mode 100644 index 00000000000..367731eda09 --- /dev/null +++ b/nixos/doc/manual/from_md/development/writing-modules.chapter.xml @@ -0,0 +1,245 @@ +<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="bash"> +{ 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="bash"> +{ 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="bash"> +{ 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="bash"> +{ 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> |