···11521152 <literal>coursier</literal>, you can create a shell alias.
11531153 </para>
11541154 </listitem>
11551155+ <listitem>
11561156+ <para>
11571157+ The <literal>services.mosquitto</literal> module has been
11581158+ rewritten to support multiple listeners and per-listener
11591159+ configuration. Module configurations from previous releases
11601160+ will no longer work and must be updated.
11611161+ </para>
11621162+ </listitem>
11551163 </itemizedlist>
11561164 </section>
11571165 <section xml:id="sec-release-21.11-notable-changes">
+3
nixos/doc/manual/release-notes/rl-2111.section.md
···355355356356- The `coursier` package's binary was renamed from `coursier` to `cs`. Completions which haven't worked for a while should now work with the renamed binary. To keep using `coursier`, you can create a shell alias.
357357358358+- The `services.mosquitto` module has been rewritten to support multiple listeners and per-listener configuration.
359359+ Module configurations from previous releases will no longer work and must be updated.
360360+358361## Other Notable Changes {#sec-release-21.11-notable-changes}
359362360363
+35-8
nixos/modules/services/backup/borgbackup.nix
···4242 ${cfg.postInit}
4343 fi
4444 '' + ''
4545- borg create $extraArgs \
4646- --compression ${cfg.compression} \
4747- --exclude-from ${mkExcludeFile cfg} \
4848- $extraCreateArgs \
4949- "::$archiveName$archiveSuffix" \
5050- ${escapeShellArgs cfg.paths}
4545+ (
4646+ set -o pipefail
4747+ ${optionalString (cfg.dumpCommand != null) ''${escapeShellArg cfg.dumpCommand} | \''}
4848+ borg create $extraArgs \
4949+ --compression ${cfg.compression} \
5050+ --exclude-from ${mkExcludeFile cfg} \
5151+ $extraCreateArgs \
5252+ "::$archiveName$archiveSuffix" \
5353+ ${if cfg.paths == null then "-" else escapeShellArgs cfg.paths}
5454+ )
5155 '' + optionalString cfg.appendFailedSuffix ''
5256 borg rename $extraArgs \
5357 "::$archiveName$archiveSuffix" "$archiveName"
···182186 + " without at least one public key";
183187 };
184188189189+ mkSourceAssertions = name: cfg: {
190190+ assertion = count isNull [ cfg.dumpCommand cfg.paths ] == 1;
191191+ message = ''
192192+ Exactly one of borgbackup.jobs.${name}.paths or borgbackup.jobs.${name}.dumpCommand
193193+ must be set.
194194+ '';
195195+ };
196196+185197 mkRemovableDeviceAssertions = name: cfg: {
186198 assertion = !(isLocalPath cfg.repo) -> !cfg.removableDevice;
187199 message = ''
···240252 options = {
241253242254 paths = mkOption {
243243- type = with types; coercedTo str lib.singleton (listOf str);
244244- description = "Path(s) to back up.";
255255+ type = with types; nullOr (coercedTo str lib.singleton (listOf str));
256256+ default = null;
257257+ description = ''
258258+ Path(s) to back up.
259259+ Mutually exclusive with <option>dumpCommand</option>.
260260+ '';
245261 example = "/home/user";
262262+ };
263263+264264+ dumpCommand = mkOption {
265265+ type = with types; nullOr path;
266266+ default = null;
267267+ description = ''
268268+ Backup the stdout of this program instead of filesystem paths.
269269+ Mutually exclusive with <option>paths</option>.
270270+ '';
271271+ example = "/path/to/createZFSsend.sh";
246272 };
247273248274 repo = mkOption {
···657683 assertions =
658684 mapAttrsToList mkPassAssertion jobs
659685 ++ mapAttrsToList mkKeysAssertion repos
686686+ ++ mapAttrsToList mkSourceAssertions jobs
660687 ++ mapAttrsToList mkRemovableDeviceAssertions jobs;
661688662689 system.activationScripts = mapAttrs' mkActivationScript jobs;
+511-167
nixos/modules/services/networking/mosquitto.nix
···55let
66 cfg = config.services.mosquitto;
7788- listenerConf = optionalString cfg.ssl.enable ''
99- listener ${toString cfg.ssl.port} ${cfg.ssl.host}
1010- cafile ${cfg.ssl.cafile}
1111- certfile ${cfg.ssl.certfile}
1212- keyfile ${cfg.ssl.keyfile}
1313- '';
88+ # note that mosquitto config parsing is very simplistic as of may 2021.
99+ # often times they'll e.g. strtok() a line, check the first two tokens, and ignore the rest.
1010+ # there's no escaping available either, so we have to prevent any being necessary.
1111+ str = types.strMatching "[^\r\n]*" // {
1212+ description = "single-line string";
1313+ };
1414+ path = types.addCheck types.path (p: str.check "${p}");
1515+ configKey = types.strMatching "[^\r\n\t ]+";
1616+ optionType = with types; oneOf [ str path bool int ] // {
1717+ description = "string, path, bool, or integer";
1818+ };
1919+ optionToString = v:
2020+ if isBool v then boolToString v
2121+ else if path.check v then "${v}"
2222+ else toString v;
14231515- passwordConf = optionalString cfg.checkPasswords ''
1616- password_file ${cfg.dataDir}/passwd
1717- '';
2424+ assertKeysValid = prefix: valid: config:
2525+ mapAttrsToList
2626+ (n: _: {
2727+ assertion = valid ? ${n};
2828+ message = "Invalid config key ${prefix}.${n}.";
2929+ })
3030+ config;
18311919- mosquittoConf = pkgs.writeText "mosquitto.conf" ''
2020- acl_file ${aclFile}
2121- persistence true
2222- allow_anonymous ${boolToString cfg.allowAnonymous}
2323- listener ${toString cfg.port} ${cfg.host}
2424- ${passwordConf}
2525- ${listenerConf}
2626- ${cfg.extraConf}
2727- '';
3232+ formatFreeform = { prefix ? "" }: mapAttrsToList (n: v: "${prefix}${n} ${optionToString v}");
28332929- userAcl = (concatStringsSep "\n\n" (mapAttrsToList (n: c:
3030- "user ${n}\n" + (concatStringsSep "\n" c.acl)) cfg.users
3131- ));
3434+ userOptions = with types; submodule {
3535+ options = {
3636+ password = mkOption {
3737+ type = uniq (nullOr str);
3838+ default = null;
3939+ description = ''
4040+ Specifies the (clear text) password for the MQTT User.
4141+ '';
4242+ };
32433333- aclFile = pkgs.writeText "mosquitto.acl" ''
3434- ${cfg.aclExtraConf}
3535- ${userAcl}
3636- '';
4444+ passwordFile = mkOption {
4545+ type = uniq (nullOr types.path);
4646+ example = "/path/to/file";
4747+ default = null;
4848+ description = ''
4949+ Specifies the path to a file containing the
5050+ clear text password for the MQTT user.
5151+ '';
5252+ };
37533838-in
5454+ hashedPassword = mkOption {
5555+ type = uniq (nullOr str);
5656+ default = null;
5757+ description = ''
5858+ Specifies the hashed password for the MQTT User.
5959+ To generate hashed password install <literal>mosquitto</literal>
6060+ package and use <literal>mosquitto_passwd</literal>.
6161+ '';
6262+ };
39634040-{
6464+ hashedPasswordFile = mkOption {
6565+ type = uniq (nullOr types.path);
6666+ example = "/path/to/file";
6767+ default = null;
6868+ description = ''
6969+ Specifies the path to a file containing the
7070+ hashed password for the MQTT user.
7171+ To generate hashed password install <literal>mosquitto</literal>
7272+ package and use <literal>mosquitto_passwd</literal>.
7373+ '';
7474+ };
41754242- ###### Interface
7676+ acl = mkOption {
7777+ type = listOf str;
7878+ example = [ "read A/B" "readwrite A/#" ];
7979+ default = [];
8080+ description = ''
8181+ Control client access to topics on the broker.
8282+ '';
8383+ };
8484+ };
8585+ };
8686+8787+ userAsserts = prefix: users:
8888+ mapAttrsToList
8989+ (n: _: {
9090+ assertion = builtins.match "[^:\r\n]+" n != null;
9191+ message = "Invalid user name ${n} in ${prefix}";
9292+ })
9393+ users
9494+ ++ mapAttrsToList
9595+ (n: u: {
9696+ assertion = count (s: s != null) [
9797+ u.password u.passwordFile u.hashedPassword u.hashedPasswordFile
9898+ ] <= 1;
9999+ message = "Cannot set more than one password option for user ${n} in ${prefix}";
100100+ }) users;
101101+102102+ makePasswordFile = users: path:
103103+ let
104104+ makeLines = store: file:
105105+ mapAttrsToList
106106+ (n: u: "addLine ${escapeShellArg n} ${escapeShellArg u.${store}}")
107107+ (filterAttrs (_: u: u.${store} != null) users)
108108+ ++ mapAttrsToList
109109+ (n: u: "addFile ${escapeShellArg n} ${escapeShellArg "${u.${file}}"}")
110110+ (filterAttrs (_: u: u.${file} != null) users);
111111+ plainLines = makeLines "password" "passwordFile";
112112+ hashedLines = makeLines "hashedPassword" "hashedPasswordFile";
113113+ in
114114+ pkgs.writeScript "make-mosquitto-passwd"
115115+ (''
116116+ #! ${pkgs.runtimeShell}
117117+118118+ set -eu
119119+120120+ file=${escapeShellArg path}
431214444- options = {
4545- services.mosquitto = {
4646- enable = mkEnableOption "the MQTT Mosquitto broker";
122122+ rm -f "$file"
123123+ touch "$file"
471244848- host = mkOption {
4949- default = "127.0.0.1";
5050- example = "0.0.0.0";
5151- type = types.str;
125125+ addLine() {
126126+ echo "$1:$2" >> "$file"
127127+ }
128128+ addFile() {
129129+ if [ $(wc -l <"$2") -gt 1 ]; then
130130+ echo "invalid mosquitto password file $2" >&2
131131+ return 1
132132+ fi
133133+ echo "$1:$(cat "$2")" >> "$file"
134134+ }
135135+ ''
136136+ + concatStringsSep "\n"
137137+ (plainLines
138138+ ++ optional (plainLines != []) ''
139139+ ${pkgs.mosquitto}/bin/mosquitto_passwd -U "$file"
140140+ ''
141141+ ++ hashedLines));
142142+143143+ makeACLFile = idx: users: supplement:
144144+ pkgs.writeText "mosquitto-acl-${toString idx}.conf"
145145+ (concatStringsSep
146146+ "\n"
147147+ (flatten [
148148+ supplement
149149+ (mapAttrsToList
150150+ (n: u: [ "user ${n}" ] ++ map (t: "topic ${t}") u.acl)
151151+ users)
152152+ ]));
153153+154154+ authPluginOptions = with types; submodule {
155155+ options = {
156156+ plugin = mkOption {
157157+ type = path;
52158 description = ''
5353- Host to listen on without SSL.
159159+ Plugin path to load, should be a <literal>.so</literal> file.
54160 '';
55161 };
561625757- port = mkOption {
5858- default = 1883;
5959- type = types.int;
163163+ denySpecialChars = mkOption {
164164+ type = bool;
60165 description = ''
6161- Port on which to listen without SSL.
166166+ Automatically disallow all clients using <literal>#</literal>
167167+ or <literal>+</literal> in their name/id.
62168 '';
169169+ default = true;
63170 };
641716565- ssl = {
6666- enable = mkEnableOption "SSL listener";
172172+ options = mkOption {
173173+ type = attrsOf optionType;
174174+ description = ''
175175+ Options for the auth plugin. Each key turns into a <literal>auth_opt_*</literal>
176176+ line in the config.
177177+ '';
178178+ default = {};
179179+ };
180180+ };
181181+ };
671826868- cafile = mkOption {
6969- type = types.nullOr types.path;
7070- default = null;
7171- description = "Path to PEM encoded CA certificates.";
7272- };
183183+ authAsserts = prefix: auth:
184184+ mapAttrsToList
185185+ (n: _: {
186186+ assertion = configKey.check n;
187187+ message = "Invalid auth plugin key ${prefix}.${n}";
188188+ })
189189+ auth;
731907474- certfile = mkOption {
7575- type = types.nullOr types.path;
7676- default = null;
7777- description = "Path to PEM encoded server certificate.";
7878- };
191191+ formatAuthPlugin = plugin:
192192+ [
193193+ "auth_plugin ${plugin.plugin}"
194194+ "auth_plugin_deny_special_chars ${optionToString plugin.denySpecialChars}"
195195+ ]
196196+ ++ formatFreeform { prefix = "auth_opt_"; } plugin.options;
791978080- keyfile = mkOption {
8181- type = types.nullOr types.path;
8282- default = null;
8383- description = "Path to PEM encoded server key.";
8484- };
198198+ freeformListenerKeys = {
199199+ allow_anonymous = 1;
200200+ allow_zero_length_clientid = 1;
201201+ auto_id_prefix = 1;
202202+ cafile = 1;
203203+ capath = 1;
204204+ certfile = 1;
205205+ ciphers = 1;
206206+ "ciphers_tls1.3" = 1;
207207+ crlfile = 1;
208208+ dhparamfile = 1;
209209+ http_dir = 1;
210210+ keyfile = 1;
211211+ max_connections = 1;
212212+ max_qos = 1;
213213+ max_topic_alias = 1;
214214+ mount_point = 1;
215215+ protocol = 1;
216216+ psk_file = 1;
217217+ psk_hint = 1;
218218+ require_certificate = 1;
219219+ socket_domain = 1;
220220+ tls_engine = 1;
221221+ tls_engine_kpass_sha1 = 1;
222222+ tls_keyform = 1;
223223+ tls_version = 1;
224224+ use_identity_as_username = 1;
225225+ use_subject_as_username = 1;
226226+ use_username_as_clientid = 1;
227227+ };
852288686- host = mkOption {
8787- default = "0.0.0.0";
8888- example = "localhost";
8989- type = types.str;
9090- description = ''
9191- Host to listen on with SSL.
9292- '';
9393- };
229229+ listenerOptions = with types; submodule {
230230+ options = {
231231+ port = mkOption {
232232+ type = port;
233233+ description = ''
234234+ Port to listen on. Must be set to 0 to listen on a unix domain socket.
235235+ '';
236236+ default = 1883;
237237+ };
942389595- port = mkOption {
9696- default = 8883;
9797- type = types.int;
9898- description = ''
9999- Port on which to listen with SSL.
100100- '';
101101- };
239239+ address = mkOption {
240240+ type = nullOr str;
241241+ description = ''
242242+ Address to listen on. Listen on <literal>0.0.0.0</literal>/<literal>::</literal>
243243+ when unset.
244244+ '';
245245+ default = null;
102246 };
103247104104- dataDir = mkOption {
105105- default = "/var/lib/mosquitto";
106106- type = types.path;
248248+ authPlugins = mkOption {
249249+ type = listOf authPluginOptions;
107250 description = ''
108108- The data directory.
251251+ Authentication plugin to attach to this listener.
252252+ Refer to the <link xlink:href="https://mosquitto.org/man/mosquitto-conf-5.html">
253253+ mosquitto.conf documentation</link> for details on authentication plugins.
109254 '';
255255+ default = [];
110256 };
111257112258 users = mkOption {
113113- type = types.attrsOf (types.submodule {
114114- options = {
115115- password = mkOption {
116116- type = with types; uniq (nullOr str);
117117- default = null;
118118- description = ''
119119- Specifies the (clear text) password for the MQTT User.
120120- '';
121121- };
259259+ type = attrsOf userOptions;
260260+ example = { john = { password = "123456"; acl = [ "topic readwrite john/#" ]; }; };
261261+ description = ''
262262+ A set of users and their passwords and ACLs.
263263+ '';
264264+ default = {};
265265+ };
122266123123- passwordFile = mkOption {
124124- type = with types; uniq (nullOr str);
125125- example = "/path/to/file";
126126- default = null;
127127- description = ''
128128- Specifies the path to a file containing the
129129- clear text password for the MQTT user.
130130- '';
131131- };
267267+ acl = mkOption {
268268+ type = listOf str;
269269+ description = ''
270270+ Additional ACL items to prepend to the generated ACL file.
271271+ '';
272272+ default = [];
273273+ };
132274133133- hashedPassword = mkOption {
134134- type = with types; uniq (nullOr str);
135135- default = null;
136136- description = ''
137137- Specifies the hashed password for the MQTT User.
138138- To generate hashed password install <literal>mosquitto</literal>
139139- package and use <literal>mosquitto_passwd</literal>.
140140- '';
141141- };
275275+ settings = mkOption {
276276+ type = submodule {
277277+ freeformType = attrsOf optionType;
278278+ };
279279+ description = ''
280280+ Additional settings for this listener.
281281+ '';
282282+ default = {};
283283+ };
284284+ };
285285+ };
142286143143- hashedPasswordFile = mkOption {
144144- type = with types; uniq (nullOr str);
145145- example = "/path/to/file";
146146- default = null;
287287+ listenerAsserts = prefix: listener:
288288+ assertKeysValid prefix freeformListenerKeys listener.settings
289289+ ++ userAsserts prefix listener.users
290290+ ++ imap0
291291+ (i: v: authAsserts "${prefix}.authPlugins.${toString i}" v)
292292+ listener.authPlugins;
293293+294294+ formatListener = idx: listener:
295295+ [
296296+ "listener ${toString listener.port} ${toString listener.address}"
297297+ "password_file ${cfg.dataDir}/passwd-${toString idx}"
298298+ "acl_file ${makeACLFile idx listener.users listener.acl}"
299299+ ]
300300+ ++ formatFreeform {} listener.settings
301301+ ++ concatMap formatAuthPlugin listener.authPlugins;
302302+303303+ freeformBridgeKeys = {
304304+ bridge_alpn = 1;
305305+ bridge_attempt_unsubscribe = 1;
306306+ bridge_bind_address = 1;
307307+ bridge_cafile = 1;
308308+ bridge_capath = 1;
309309+ bridge_certfile = 1;
310310+ bridge_identity = 1;
311311+ bridge_insecure = 1;
312312+ bridge_keyfile = 1;
313313+ bridge_max_packet_size = 1;
314314+ bridge_outgoing_retain = 1;
315315+ bridge_protocol_version = 1;
316316+ bridge_psk = 1;
317317+ bridge_require_ocsp = 1;
318318+ bridge_tls_version = 1;
319319+ cleansession = 1;
320320+ idle_timeout = 1;
321321+ keepalive_interval = 1;
322322+ local_cleansession = 1;
323323+ local_clientid = 1;
324324+ local_password = 1;
325325+ local_username = 1;
326326+ notification_topic = 1;
327327+ notifications = 1;
328328+ notifications_local_only = 1;
329329+ remote_clientid = 1;
330330+ remote_password = 1;
331331+ remote_username = 1;
332332+ restart_timeout = 1;
333333+ round_robin = 1;
334334+ start_type = 1;
335335+ threshold = 1;
336336+ try_private = 1;
337337+ };
338338+339339+ bridgeOptions = with types; submodule {
340340+ options = {
341341+ addresses = mkOption {
342342+ type = listOf (submodule {
343343+ options = {
344344+ address = mkOption {
345345+ type = str;
147346 description = ''
148148- Specifies the path to a file containing the
149149- hashed password for the MQTT user.
150150- To generate hashed password install <literal>mosquitto</literal>
151151- package and use <literal>mosquitto_passwd</literal>.
347347+ Address of the remote MQTT broker.
152348 '';
153349 };
154350155155- acl = mkOption {
156156- type = types.listOf types.str;
157157- example = [ "topic read A/B" "topic A/#" ];
351351+ port = mkOption {
352352+ type = port;
158353 description = ''
159159- Control client access to topics on the broker.
354354+ Port of the remote MQTT broker.
160355 '';
356356+ default = 1883;
161357 };
162358 };
163359 });
164164- example = { john = { password = "123456"; acl = [ "topic readwrite john/#" ]; }; };
360360+ default = [];
165361 description = ''
166166- A set of users and their passwords and ACLs.
362362+ Remote endpoints for the bridge.
167363 '';
168364 };
169365170170- allowAnonymous = mkOption {
171171- default = false;
172172- type = types.bool;
366366+ topics = mkOption {
367367+ type = listOf str;
173368 description = ''
174174- Allow clients to connect without authentication.
369369+ Topic patterns to be shared between the two brokers.
370370+ Refer to the <link xlink:href="https://mosquitto.org/man/mosquitto-conf-5.html">
371371+ mosquitto.conf documentation</link> for details on the format.
175372 '';
373373+ default = [];
374374+ example = [ "# both 2 local/topic/ remote/topic/" ];
176375 };
177376178178- checkPasswords = mkOption {
179179- default = false;
180180- example = true;
181181- type = types.bool;
377377+ settings = mkOption {
378378+ type = submodule {
379379+ freeformType = attrsOf optionType;
380380+ };
182381 description = ''
183183- Refuse connection when clients provide incorrect passwords.
382382+ Additional settings for this bridge.
184383 '';
384384+ default = {};
185385 };
386386+ };
387387+ };
186388187187- extraConf = mkOption {
188188- default = "";
189189- type = types.lines;
190190- description = ''
191191- Extra config to append to `mosquitto.conf` file.
192192- '';
193193- };
389389+ bridgeAsserts = prefix: bridge:
390390+ assertKeysValid prefix freeformBridgeKeys bridge.settings
391391+ ++ [ {
392392+ assertion = length bridge.addresses > 0;
393393+ message = "Bridge ${prefix} needs remote broker addresses";
394394+ } ];
395395+396396+ formatBridge = name: bridge:
397397+ [
398398+ "connection ${name}"
399399+ "addresses ${concatMapStringsSep " " (a: "${a.address}:${toString a.port}") bridge.addresses}"
400400+ ]
401401+ ++ map (t: "topic ${t}") bridge.topics
402402+ ++ formatFreeform {} bridge.settings;
403403+404404+ freeformGlobalKeys = {
405405+ allow_duplicate_messages = 1;
406406+ autosave_interval = 1;
407407+ autosave_on_changes = 1;
408408+ check_retain_source = 1;
409409+ connection_messages = 1;
410410+ log_facility = 1;
411411+ log_timestamp = 1;
412412+ log_timestamp_format = 1;
413413+ max_inflight_bytes = 1;
414414+ max_inflight_messages = 1;
415415+ max_keepalive = 1;
416416+ max_packet_size = 1;
417417+ max_queued_bytes = 1;
418418+ max_queued_messages = 1;
419419+ memory_limit = 1;
420420+ message_size_limit = 1;
421421+ persistence_file = 1;
422422+ persistence_location = 1;
423423+ persistent_client_expiration = 1;
424424+ pid_file = 1;
425425+ queue_qos0_messages = 1;
426426+ retain_available = 1;
427427+ set_tcp_nodelay = 1;
428428+ sys_interval = 1;
429429+ upgrade_outgoing_qos = 1;
430430+ websockets_headers_size = 1;
431431+ websockets_log_level = 1;
432432+ };
433433+434434+ globalOptions = with types; {
435435+ enable = mkEnableOption "the MQTT Mosquitto broker";
436436+437437+ bridges = mkOption {
438438+ type = attrsOf bridgeOptions;
439439+ default = {};
440440+ description = ''
441441+ Bridges to build to other MQTT brokers.
442442+ '';
443443+ };
444444+445445+ listeners = mkOption {
446446+ type = listOf listenerOptions;
447447+ default = {};
448448+ description = ''
449449+ Listeners to configure on this broker.
450450+ '';
451451+ };
452452+453453+ includeDirs = mkOption {
454454+ type = listOf path;
455455+ description = ''
456456+ Directories to be scanned for further config files to include.
457457+ Directories will processed in the order given,
458458+ <literal>*.conf</literal> files in the directory will be
459459+ read in case-sensistive alphabetical order.
460460+ '';
461461+ default = [];
462462+ };
463463+464464+ logDest = mkOption {
465465+ type = listOf (either path (enum [ "stdout" "stderr" "syslog" "topic" "dlt" ]));
466466+ description = ''
467467+ Destinations to send log messages to.
468468+ '';
469469+ default = [ "stderr" ];
470470+ };
471471+472472+ logType = mkOption {
473473+ type = listOf (enum [ "debug" "error" "warning" "notice" "information"
474474+ "subscribe" "unsubscribe" "websockets" "none" "all" ]);
475475+ description = ''
476476+ Types of messages to log.
477477+ '';
478478+ default = [];
479479+ };
480480+481481+ persistence = mkOption {
482482+ type = bool;
483483+ description = ''
484484+ Enable persistent storage of subscriptions and messages.
485485+ '';
486486+ default = true;
487487+ };
194488195195- aclExtraConf = mkOption {
196196- default = "";
197197- type = types.lines;
198198- description = ''
199199- Extra config to prepend to the ACL file.
200200- '';
201201- };
489489+ dataDir = mkOption {
490490+ default = "/var/lib/mosquitto";
491491+ type = types.path;
492492+ description = ''
493493+ The data directory.
494494+ '';
495495+ };
202496497497+ settings = mkOption {
498498+ type = submodule {
499499+ freeformType = attrsOf optionType;
500500+ };
501501+ description = ''
502502+ Global configuration options for the mosquitto broker.
503503+ '';
504504+ default = {};
203505 };
204506 };
205507508508+ globalAsserts = prefix: cfg:
509509+ flatten [
510510+ (assertKeysValid prefix freeformGlobalKeys cfg.settings)
511511+ (imap0 (n: l: listenerAsserts "${prefix}.listener.${toString n}" l) cfg.listeners)
512512+ (mapAttrsToList (n: b: bridgeAsserts "${prefix}.bridge.${n}" b) cfg.bridges)
513513+ ];
514514+515515+ formatGlobal = cfg:
516516+ [
517517+ "per_listener_settings true"
518518+ "persistence ${optionToString cfg.persistence}"
519519+ ]
520520+ ++ map
521521+ (d: if path.check d then "log_dest file ${d}" else "log_dest ${d}")
522522+ cfg.logDest
523523+ ++ map (t: "log_type ${t}") cfg.logType
524524+ ++ formatFreeform {} cfg.settings
525525+ ++ concatLists (imap0 formatListener cfg.listeners)
526526+ ++ concatLists (mapAttrsToList formatBridge cfg.bridges)
527527+ ++ map (d: "include_dir ${d}") cfg.includeDirs;
528528+529529+ configFile = pkgs.writeText "mosquitto.conf"
530530+ (concatStringsSep "\n" (formatGlobal cfg));
531531+532532+in
533533+534534+{
535535+536536+ ###### Interface
537537+538538+ options.services.mosquitto = globalOptions;
206539207540 ###### Implementation
208541209542 config = mkIf cfg.enable {
210543211211- assertions = mapAttrsToList (name: cfg: {
212212- assertion = length (filter (s: s != null) (with cfg; [
213213- password passwordFile hashedPassword hashedPasswordFile
214214- ])) <= 1;
215215- message = "Cannot set more than one password option";
216216- }) cfg.users;
544544+ assertions = globalAsserts "services.mosquitto" cfg;
217545218546 systemd.services.mosquitto = {
219547 description = "Mosquitto MQTT Broker Daemon";
···227555 RuntimeDirectory = "mosquitto";
228556 WorkingDirectory = cfg.dataDir;
229557 Restart = "on-failure";
230230- ExecStart = "${pkgs.mosquitto}/bin/mosquitto -c ${mosquittoConf}";
558558+ ExecStart = "${pkgs.mosquitto}/bin/mosquitto -c ${configFile}";
231559 ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
232560233561 # Hardening
···252580 ReadWritePaths = [
253581 cfg.dataDir
254582 "/tmp" # mosquitto_passwd creates files in /tmp before moving them
255255- ];
256256- ReadOnlyPaths = with cfg.ssl; lib.optionals (enable) [
257257- certfile
258258- keyfile
259259- cafile
260260- ];
583583+ ] ++ filter path.check cfg.logDest;
584584+ ReadOnlyPaths =
585585+ map (p: "${p}")
586586+ (cfg.includeDirs
587587+ ++ filter
588588+ (v: v != null)
589589+ (flatten [
590590+ (map
591591+ (l: [
592592+ (l.settings.psk_file or null)
593593+ (l.settings.http_dir or null)
594594+ (l.settings.cafile or null)
595595+ (l.settings.capath or null)
596596+ (l.settings.certfile or null)
597597+ (l.settings.crlfile or null)
598598+ (l.settings.dhparamfile or null)
599599+ (l.settings.keyfile or null)
600600+ ])
601601+ cfg.listeners)
602602+ (mapAttrsToList
603603+ (_: b: [
604604+ (b.settings.bridge_cafile or null)
605605+ (b.settings.bridge_capath or null)
606606+ (b.settings.bridge_certfile or null)
607607+ (b.settings.bridge_keyfile or null)
608608+ ])
609609+ cfg.bridges)
610610+ ]));
261611 RemoveIPC = true;
262612 RestrictAddressFamilies = [
263613 "AF_UNIX" # for sd_notify() call
···275625 ];
276626 UMask = "0077";
277627 };
278278- preStart = ''
279279- rm -f ${cfg.dataDir}/passwd
280280- touch ${cfg.dataDir}/passwd
281281- '' + concatStringsSep "\n" (
282282- mapAttrsToList (n: c:
283283- if c.hashedPasswordFile != null then
284284- "echo '${n}:'$(cat '${c.hashedPasswordFile}') >> ${cfg.dataDir}/passwd"
285285- else if c.passwordFile != null then
286286- "${pkgs.mosquitto}/bin/mosquitto_passwd -b ${cfg.dataDir}/passwd ${n} $(cat '${c.passwordFile}')"
287287- else if c.hashedPassword != null then
288288- "echo '${n}:${c.hashedPassword}' >> ${cfg.dataDir}/passwd"
289289- else optionalString (c.password != null)
290290- "${pkgs.mosquitto}/bin/mosquitto_passwd -b ${cfg.dataDir}/passwd ${n} '${c.password}'"
291291- ) cfg.users);
628628+ preStart =
629629+ concatStringsSep
630630+ "\n"
631631+ (imap0
632632+ (idx: listener: makePasswordFile listener.users "${cfg.dataDir}/passwd-${toString idx}")
633633+ cfg.listeners);
292634 };
293635294636 users.users.mosquitto = {
···302644 users.groups.mosquitto.gid = config.ids.gids.mosquitto;
303645304646 };
647647+648648+ meta.maintainers = with lib.maintainers; [ pennae ];
305649}
···11-WGET_ARGS=( http://download.kde.org/stable/plasma-mobile/21.05 -A '*.tar.xz' )
11+WGET_ARGS=( https://download.kde.org/stable/plasma-mobile/21.08/ -A '*.tar.xz' )
···8989 for system in SYSTEMS:
9090 for release_type in RELEASE_TYPES:
9191 for release_channel in RELEASE_CHANNELS:
9292- version = factorio_versions[release_channel.name][release_type.name]
9292+ version = factorio_versions[release_channel.name].get(release_type.name)
9393+ if version == None:
9494+ continue
9395 this_release = {
9496 "name": f"factorio_{release_type.name}_{system.tar_name}-{version}.tar.xz",
9597 "url": f"https://factorio.com/get-download/{version}/{release_type.name}/{system.url_name}",
···11{ stdenv
22, alsa-lib
33+, addOpenGLRunpath
34, autoPatchelfHook
45, cairo
56, fetchurl
···2829 ${rpmextract}/bin/rpmextract $src
2930 '';
30313131- nativeBuildInputs = [ autoPatchelfHook rpmextract ];
3232+ nativeBuildInputs = [ addOpenGLRunpath autoPatchelfHook rpmextract ];
32333334 buildInputs = [
3435 alsa-lib
···8687 --replace "NetworkManager-wait-online.service" ""
8788 '';
88898989- postFixup = ''
9090- patchelf --replace-needed libomp.so.5 libomp.so $out/bin/hqplayerd
9090+ # NB: addOpenGLRunpath needs to run _after_ autoPatchelfHook, which runs in
9191+ # postFixup, so we tack it on here.
9292+ doInstallCheck = true;
9393+ installCheckPhase = ''
9494+ addOpenGLRunpath $out/bin/hqplayerd
9595+ $out/bin/hqplayerd --version
9196 '';
92979398 meta = with lib; {
+1-3
pkgs/servers/sql/postgresql/packages.nix
···11self: super: {
2233- age = super.callPackage ./ext/age.nix {
44- bison = self.bison_3_5;
55- };
33+ age = super.callPackage ./ext/age.nix { };
6475 periods = super.callPackage ./ext/periods.nix { };
86
+5
pkgs/servers/web-apps/discourse/default.nix
···266266267267 # Make sure the notification email setting applies
268268 ./notification_email.patch
269269+270270+ # Change the path to the public directory reported by Discourse
271271+ # to its real path instead of the symlink in the store, since
272272+ # the store path won't be matched by any nginx rules
273273+ ./public_dir_path.patch
269274 ];
270275271276 postPatch = ''