at 23.05-pre 9.3 kB view raw
1<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"> 2 <title>Writing NixOS Modules</title> 3 <para> 4 NixOS has a modular system for declarative configuration. This 5 system combines multiple <emphasis>modules</emphasis> to produce the 6 full system configuration. One of the modules that constitute the 7 configuration is <literal>/etc/nixos/configuration.nix</literal>. 8 Most of the others live in the 9 <link xlink:href="https://github.com/NixOS/nixpkgs/tree/master/nixos/modules"><literal>nixos/modules</literal></link> 10 subdirectory of the Nixpkgs tree. 11 </para> 12 <para> 13 Each NixOS module is a file that handles one logical aspect of the 14 configuration, such as a specific kind of hardware, a service, or 15 network settings. A module configuration does not have to handle 16 everything from scratch; it can use the functionality provided by 17 other modules for its implementation. Thus a module can 18 <emphasis>declare</emphasis> options that can be used by other 19 modules, and conversely can <emphasis>define</emphasis> options 20 provided by other modules in its own implementation. For example, 21 the module 22 <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/security/pam.nix"><literal>pam.nix</literal></link> 23 declares the option <literal>security.pam.services</literal> that 24 allows other modules (e.g. 25 <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/networking/ssh/sshd.nix"><literal>sshd.nix</literal></link>) 26 to define PAM services; and it defines the option 27 <literal>environment.etc</literal> (declared by 28 <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/system/etc/etc.nix"><literal>etc.nix</literal></link>) 29 to cause files to be created in <literal>/etc/pam.d</literal>. 30 </para> 31 <para> 32 In <xref linkend="sec-configuration-syntax" />, we saw the following 33 structure of NixOS modules: 34 </para> 35 <programlisting language="bash"> 36{ config, pkgs, ... }: 37 38{ option definitions 39} 40</programlisting> 41 <para> 42 This is actually an <emphasis>abbreviated</emphasis> form of module 43 that only defines options, but does not declare any. The structure 44 of full NixOS modules is shown in 45 <link linkend="ex-module-syntax">Example: Structure of NixOS 46 Modules</link>. 47 </para> 48 <anchor xml:id="ex-module-syntax" /> 49 <para> 50 <emphasis role="strong">Example: Structure of NixOS 51 Modules</emphasis> 52 </para> 53 <programlisting language="bash"> 54{ config, pkgs, ... }: 55 56{ 57 imports = 58 [ paths of other modules 59 ]; 60 61 options = { 62 option declarations 63 }; 64 65 config = { 66 option definitions 67 }; 68} 69</programlisting> 70 <para> 71 The meaning of each part is as follows. 72 </para> 73 <itemizedlist> 74 <listitem> 75 <para> 76 The first line makes the current Nix expression a function. The 77 variable <literal>pkgs</literal> contains Nixpkgs (by default, 78 it takes the <literal>nixpkgs</literal> entry of 79 <literal>NIX_PATH</literal>, see the 80 <link xlink:href="https://nixos.org/manual/nix/stable/#sec-common-env">Nix 81 manual</link> for further details), while 82 <literal>config</literal> contains the full system 83 configuration. This line can be omitted if there is no reference 84 to <literal>pkgs</literal> and <literal>config</literal> inside 85 the module. 86 </para> 87 </listitem> 88 <listitem> 89 <para> 90 This <literal>imports</literal> list enumerates the paths to 91 other NixOS modules that should be included in the evaluation of 92 the system configuration. A default set of modules is defined in 93 the file <literal>modules/module-list.nix</literal>. These don't 94 need to be added in the import list. 95 </para> 96 </listitem> 97 <listitem> 98 <para> 99 The attribute <literal>options</literal> is a nested set of 100 <emphasis>option declarations</emphasis> (described below). 101 </para> 102 </listitem> 103 <listitem> 104 <para> 105 The attribute <literal>config</literal> is a nested set of 106 <emphasis>option definitions</emphasis> (also described below). 107 </para> 108 </listitem> 109 </itemizedlist> 110 <para> 111 <link linkend="locate-example">Example: NixOS Module for the 112 <quote>locate</quote> Service</link> shows a module that handles the 113 regular update of the <quote>locate</quote> database, an index of 114 all files in the file system. This module declares two options that 115 can be defined by other modules (typically the user’s 116 <literal>configuration.nix</literal>): 117 <literal>services.locate.enable</literal> (whether the database 118 should be updated) and <literal>services.locate.interval</literal> 119 (when the update should be done). It implements its functionality by 120 defining two options declared by other modules: 121 <literal>systemd.services</literal> (the set of all systemd 122 services) and <literal>systemd.timers</literal> (the list of 123 commands to be executed periodically by <literal>systemd</literal>). 124 </para> 125 <para> 126 Care must be taken when writing systemd services using 127 <literal>Exec*</literal> directives. By default systemd performs 128 substitution on <literal>%&lt;char&gt;</literal> specifiers in these 129 directives, expands environment variables from 130 <literal>$FOO</literal> and <literal>${FOO}</literal>, splits 131 arguments on whitespace, and splits commands on 132 <literal>;</literal>. All of these must be escaped to avoid 133 unexpected substitution or splitting when interpolating into an 134 <literal>Exec*</literal> directive, e.g. when using an 135 <literal>extraArgs</literal> option to pass additional arguments to 136 the service. The functions 137 <literal>utils.escapeSystemdExecArg</literal> and 138 <literal>utils.escapeSystemdExecArgs</literal> are provided for 139 this, see <link linkend="exec-escaping-example">Example: Escaping in 140 Exec directives</link> for an example. When using these functions 141 system environment substitution should <emphasis>not</emphasis> be 142 disabled explicitly. 143 </para> 144 <anchor xml:id="locate-example" /> 145 <para> 146 <emphasis role="strong">Example: NixOS Module for the 147 <quote>locate</quote> Service</emphasis> 148 </para> 149 <programlisting language="bash"> 150{ config, lib, pkgs, ... }: 151 152with lib; 153 154let 155 cfg = config.services.locate; 156in { 157 options.services.locate = { 158 enable = mkOption { 159 type = types.bool; 160 default = false; 161 description = '' 162 If enabled, NixOS will periodically update the database of 163 files used by the locate command. 164 ''; 165 }; 166 167 interval = mkOption { 168 type = types.str; 169 default = &quot;02:15&quot;; 170 example = &quot;hourly&quot;; 171 description = '' 172 Update the locate database at this interval. Updates by 173 default at 2:15 AM every day. 174 175 The format is described in 176 systemd.time(7). 177 ''; 178 }; 179 180 # Other options omitted for documentation 181 }; 182 183 config = { 184 systemd.services.update-locatedb = 185 { description = &quot;Update Locate Database&quot;; 186 path = [ pkgs.su ]; 187 script = 188 '' 189 mkdir -m 0755 -p $(dirname ${toString cfg.output}) 190 exec updatedb \ 191 --localuser=${cfg.localuser} \ 192 ${optionalString (!cfg.includeStore) &quot;--prunepaths='/nix/store'&quot;} \ 193 --output=${toString cfg.output} ${concatStringsSep &quot; &quot; cfg.extraFlags} 194 ''; 195 }; 196 197 systemd.timers.update-locatedb = mkIf cfg.enable 198 { description = &quot;Update timer for locate database&quot;; 199 partOf = [ &quot;update-locatedb.service&quot; ]; 200 wantedBy = [ &quot;timers.target&quot; ]; 201 timerConfig.OnCalendar = cfg.interval; 202 }; 203 }; 204} 205</programlisting> 206 <anchor xml:id="exec-escaping-example" /> 207 <para> 208 <emphasis role="strong">Example: Escaping in Exec 209 directives</emphasis> 210 </para> 211 <programlisting language="bash"> 212{ config, lib, pkgs, utils, ... }: 213 214with lib; 215 216let 217 cfg = config.services.echo; 218 echoAll = pkgs.writeScript &quot;echo-all&quot; '' 219 #! ${pkgs.runtimeShell} 220 for s in &quot;$@&quot;; do 221 printf '%s\n' &quot;$s&quot; 222 done 223 ''; 224 args = [ &quot;a%Nything&quot; &quot;lang=\${LANG}&quot; &quot;;&quot; &quot;/bin/sh -c date&quot; ]; 225in { 226 systemd.services.echo = 227 { description = &quot;Echo to the journal&quot;; 228 wantedBy = [ &quot;multi-user.target&quot; ]; 229 serviceConfig.Type = &quot;oneshot&quot;; 230 serviceConfig.ExecStart = '' 231 ${echoAll} ${utils.escapeSystemdExecArgs args} 232 ''; 233 }; 234} 235</programlisting> 236 <xi:include href="option-declarations.section.xml" /> 237 <xi:include href="option-types.section.xml" /> 238 <xi:include href="option-def.section.xml" /> 239 <xi:include href="assertions.section.xml" /> 240 <xi:include href="meta-attributes.section.xml" /> 241 <xi:include href="importing-modules.section.xml" /> 242 <xi:include href="replace-modules.section.xml" /> 243 <xi:include href="freeform-modules.section.xml" /> 244 <xi:include href="settings-options.section.xml" /> 245</chapter>