summary refs log tree commit diff
path: root/nixos/doc/manual/from_md/development/writing-modules.chapter.xml
blob: 367731eda09003484479dbfd73f7485e75499373 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
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>%&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="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 = &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="bash">
{ 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>