projects/guix·blob python-team · refs

952 lines · 36.1 KiB

1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2016, 2025 John Darrington <[email protected]>
3;;; Copyright © 2018, 2019, 2020 Ricardo Wurmus <[email protected]>
4;;; Copyright © 2020, 2021 Maxim Cournoyer <[email protected]>
5;;; Copyright © 2023-2025 Ian Eure <[email protected]>
6;;;
7;;; This file is part of GNU Guix.
8;;;
9;;; GNU Guix is free software; you can redistribute it and/or modify it
10;;; under the terms of the GNU General Public License as published by
11;;; the Free Software Foundation; either version 3 of the License, or (at
12;;; your option) any later version.
13;;;
14;;; GNU Guix is distributed in the hope that it will be useful, but
15;;; WITHOUT ANY WARRANTY; without even the implied warranty of
16;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;;; GNU General Public License for more details.
18;;;
19;;; You should have received a copy of the GNU General Public License
20;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
21
22(define-module (gnu services nfs)
23 #:use-module (gnu build file-systems)
24 #:use-module (gnu packages file-systems)
25 #:use-module (gnu packages linux)
26 #:use-module (gnu packages nfs)
27 #:use-module (gnu packages onc-rpc)
28 #:use-module (gnu services configuration)
29 #:use-module (gnu services shepherd)
30 #:use-module (gnu)
31 #:use-module (guix gexp)
32 #:use-module (guix modules)
33 #:use-module (guix records)
34 #:use-module (guix)
35 #:use-module (srfi srfi-1)
36 #:use-module (srfi srfi-26)
37 #:use-module (srfi srfi-171)
38 #:use-module (ice-9 match)
39 #:export (rpcbind-service-type
40 rpcbind-configuration
41 rpcbind-configuration?
42
43 pipefs-service-type
44 pipefs-configuration
45 pipefs-configuration?
46
47 idmap-service-type
48 idmap-configuration
49 idmap-configuration?
50
51 gss-service-type
52 gss-configuration
53 gss-configuration?
54
55 nfs-service-type
56 nfs-configuration
57 nfs-configuration?
58
59 autofs-service-type
60 autofs-configuration
61 autofs-configuration?
62
63 autofs-indirect-map
64 autofs-indirect-map?
65
66 autofs-map-entry
67 autofs-map-entry?))
68
69(define default-pipefs-directory "/var/lib/nfs/rpc_pipefs")
70
71
72
73(define-record-type* <rpcbind-configuration>
74 rpcbind-configuration make-rpcbind-configuration
75 rpcbind-configuration?
76 (rpcbind rpcbind-configuration-rpcbind
77 (default rpcbind))
78 (warm-start? rpcbind-configuration-warm-start?
79 (default #t)))
80
81(define rpcbind-service-type
82 (let ((proc
83 (lambda (config)
84 (define rpcbind
85 (rpcbind-configuration-rpcbind config))
86
87 (define rpcbind-command
88 #~(list (string-append #$rpcbind "/sbin/rpcbind") "-f"
89 #$@(if (rpcbind-configuration-warm-start? config) '("-w") '())))
90
91 (shepherd-service
92 (documentation "Start the RPC bind daemon.")
93 (requirement '(user-processes networking))
94 (provision '(rpcbind-daemon))
95
96 (start #~(make-forkexec-constructor #$rpcbind-command))
97 (stop #~(make-kill-destructor))))))
98 (service-type
99 (name 'rpcbind)
100 (extensions
101 (list (service-extension shepherd-root-service-type
102 (compose list proc))))
103 ;; We use the extensions feature to allow other services to automatically
104 ;; configure and start this service. Only one value can be provided. We
105 ;; override it with the value returned by the extending service.
106 (compose identity)
107 (extend (lambda (config values)
108 (match values
109 ((first . rest) first)
110 (_ config))))
111 (default-value (rpcbind-configuration))
112 (description "Run the RPC Bind service, which provides a facility to map
113ONC RPC program numbers into universal addresses. Many NFS related services
114use this facility."))))
115
116
117
118(define-record-type* <pipefs-configuration>
119 pipefs-configuration make-pipefs-configuration
120 pipefs-configuration?
121 (mount-point pipefs-configuration-mount-point
122 (default default-pipefs-directory)))
123
124(define pipefs-service-type
125 (let ((proc
126 (lambda (config)
127 (define pipefs-directory (pipefs-configuration-mount-point config))
128
129 (shepherd-service
130 (documentation "Mount the pipefs pseudo file system.")
131 (provision '(rpc-pipefs))
132
133 (start #~(lambda ()
134 (mkdir-p #$pipefs-directory)
135 (mount "rpc_pipefs" #$pipefs-directory "rpc_pipefs")
136 (member #$pipefs-directory (mount-points))))
137
138 (stop #~(lambda (pid . args)
139 (umount #$pipefs-directory MNT_DETACH)
140 (not (member #$pipefs-directory (mount-points)))))))))
141 (service-type
142 (name 'pipefs)
143 (extensions
144 (list (service-extension shepherd-root-service-type
145 (compose list proc))))
146 ;; We use the extensions feature to allow other services to automatically
147 ;; configure and start this service. Only one value can be provided. We
148 ;; override it with the value returned by the extending service.
149 (compose identity)
150 (extend (lambda (config values)
151 (match values
152 ((first . rest) first)
153 (_ config))))
154 (default-value (pipefs-configuration))
155 (description "Mount the pipefs file system, which is used to transfer
156NFS-related data between the kernel and user-space programs."))))
157
158
159
160(define-record-type* <gss-configuration>
161 gss-configuration make-gss-configuration
162 gss-configuration?
163 (pipefs-directory gss-configuration-pipefs-directory
164 (default default-pipefs-directory))
165 (nfs-utils gss-configuration-gss
166 (default nfs-utils)))
167
168(define gss-service-type
169 (let ((proc
170 (lambda (config)
171 (define nfs-utils
172 (gss-configuration-gss config))
173
174 (define pipefs-directory
175 (gss-configuration-pipefs-directory config))
176
177 (define gss-command
178 #~(list (string-append #$nfs-utils "/sbin/rpc.gssd") "-f"
179 "-p" #$pipefs-directory))
180
181 (shepherd-service
182 (documentation "Start the RPC GSS daemon.")
183 (requirement '(user-processes rpcbind-daemon rpc-pipefs))
184 (provision '(gss-daemon))
185
186 (start #~(make-forkexec-constructor #$gss-command))
187 (stop #~(make-kill-destructor))))))
188 (service-type
189 (name 'gss)
190 (extensions
191 (list (service-extension shepherd-root-service-type
192 (compose list proc))))
193 ;; We use the extensions feature to allow other services to automatically
194 ;; configure and start this service. Only one value can be provided. We
195 ;; override it with the value returned by the extending service.
196 (compose identity)
197 (extend (lambda (config values)
198 (match values
199 ((first . rest) first)
200 (_ config))))
201 (default-value (gss-configuration))
202 (description "Run the @dfn{global security system} (GSS) daemon, which
203provides strong security for protocols based on remote procedure calls (ONC
204RPC)."))))
205
206
207
208(define-record-type* <idmap-configuration>
209 idmap-configuration make-idmap-configuration
210 idmap-configuration?
211 (pipefs-directory idmap-configuration-pipefs-directory
212 (default default-pipefs-directory))
213 (domain idmap-configuration-domain
214 (default #f))
215 (nfs-utils idmap-configuration-nfs-utils
216 (default nfs-utils))
217 (verbosity idmap-configuration-verbosity
218 (default 0)))
219
220(define idmap-service-type
221 (let ((proc
222 (lambda (config)
223
224 (define nfs-utils
225 (idmap-configuration-nfs-utils config))
226
227 (define pipefs-directory
228 (idmap-configuration-pipefs-directory config))
229
230 (define domain (idmap-configuration-domain config))
231
232 (define (idmap-config-file config)
233 (plain-file "idmapd.conf"
234 (string-append
235 "\n[General]\n"
236 "Verbosity = "
237 (number->string
238 (idmap-configuration-verbosity config))
239 "\n"
240 (if domain
241 (format #f "Domain = ~a\n" domain)
242 "")
243 "\n[Mapping]\n"
244 "Nobody-User = nobody\n"
245 "Nobody-Group = nogroup\n")))
246
247 (define idmap-command
248 #~(list (string-append #$nfs-utils "/sbin/rpc.idmapd") "-f"
249 "-p" #$pipefs-directory
250 ;; TODO: this is deprecated
251 "-c" #$(idmap-config-file config)))
252
253 (shepherd-service
254 (documentation "Start the RPC IDMAP daemon.")
255 (requirement '(user-processes rpcbind-daemon rpc-pipefs))
256 (provision '(idmap-daemon))
257 (start #~(make-forkexec-constructor #$idmap-command))
258 (stop #~(make-kill-destructor))))))
259 (service-type
260 (name 'idmap)
261 (extensions
262 (list (service-extension shepherd-root-service-type
263 (compose list proc))))
264 ;; We use the extensions feature to allow other services to automatically
265 ;; configure and start this service. Only one value can be provided. We
266 ;; override it with the value returned by the extending service.
267 (compose identity)
268 (extend (lambda (config values) (first values)))
269 (default-value (idmap-configuration))
270 (description "Run the idmap daemon, which provides a mapping between user
271IDs and user names. It is typically required to access file systems mounted
272via NFSv4."))))
273
274(define-record-type* <nfs-configuration>
275 nfs-configuration make-nfs-configuration
276 nfs-configuration?
277 (nfs-utils nfs-configuration-nfs-utils
278 (default nfs-utils))
279 (nfs-versions nfs-configuration-nfs-versions
280 (default '("4.2" "4.1" "4.0")))
281 (exports nfs-configuration-exports
282 (default '()))
283 (rpcmountd-port nfs-configuration-rpcmountd-port
284 (default #f))
285 (rpcstatd-port nfs-configuration-rpcstatd-port
286 (default #f))
287 (rpcbind nfs-configuration-rpcbind
288 (default rpcbind))
289 (idmap-domain nfs-configuration-idmap-domain
290 (default "localdomain"))
291 (nfsd-port nfs-configuration-nfsd-port
292 (default 2049))
293 (nfsd-threads nfs-configuration-nfsd-threads
294 (default 8))
295 (nfsd-tcp? nfs-configuration-nfsd-tcp?
296 (default #t))
297 (nfsd-udp? nfs-configuration-nfsd-udp?
298 (default #f))
299 (pipefs-directory nfs-configuration-pipefs-directory
300 (default default-pipefs-directory))
301 ;; List of modules to debug; any of nfsd, nfs, rpc, idmap, statd, or mountd.
302 (debug nfs-configuration-debug
303 (default '())))
304
305(define (nfs-shepherd-services config)
306 "Return a list of <shepherd-service> for the NFS daemons with CONFIG."
307 (match-record config <nfs-configuration>
308 (nfs-utils nfs-versions exports
309 rpcmountd-port rpcstatd-port nfsd-port nfsd-threads
310 nfsd-tcp? nfsd-udp?
311 pipefs-directory debug)
312 (list (shepherd-service
313 (documentation "Mount the nfsd pseudo file system.")
314 (provision '(/proc/fs/nfsd))
315 (start #~(lambda ()
316 (mount "nfsd" "/proc/fs/nfsd" "nfsd")
317 (member "/proc/fs/nfsd" (mount-points))))
318
319 (stop #~(lambda (pid . args)
320 (umount "/proc/fs/nfsd" MNT_DETACH)
321 (not (member "/proc/fs/nfsd" (mount-points))))))
322 (shepherd-service
323 (documentation "Run the NFS statd daemon.")
324 (provision '(rpc.statd))
325 (requirement '(/proc/fs/nfsd rpcbind-daemon))
326 (start
327 #~(make-forkexec-constructor
328 (list #$(file-append nfs-utils "/sbin/rpc.statd")
329 ;; TODO: notification support may require a little more
330 ;; configuration work.
331 "--no-notify"
332 #$@(if (member 'statd debug)
333 '("--no-syslog") ; verbose logging to stderr
334 '())
335 "--foreground"
336 #$@(if rpcstatd-port
337 #~("--port" #$(number->string rpcstatd-port))
338 '()))
339 #:pid-file "/var/run/rpc.statd.pid"))
340 (stop #~(make-kill-destructor)))
341 (shepherd-service
342 (documentation "Run the NFS mountd daemon.")
343 (provision '(rpc.mountd))
344 (requirement '(/proc/fs/nfsd rpc.statd))
345 (start
346 #~(make-forkexec-constructor
347 (list #$(file-append nfs-utils "/sbin/rpc.mountd")
348 "--foreground"
349 #$@(if (member 'mountd debug)
350 '("--debug" "all")
351 '())
352 #$@(if rpcmountd-port
353 #~("--port" #$(number->string rpcmountd-port))
354 '()))))
355 (stop #~(make-kill-destructor)))
356 (shepherd-service
357 (documentation "Run the NFS daemon.")
358 (provision '(rpc.nfsd))
359 (requirement '(/proc/fs/nfsd rpc.statd networking))
360 (start
361 #~(lambda _
362 (zero? (apply system* #$(file-append nfs-utils "/sbin/rpc.nfsd")
363 (list
364 #$@(if (member 'nfsd debug)
365 '("--debug")
366 '())
367 "--port" #$(number->string nfsd-port)
368 #$@(map (lambda (version)
369 (string-append "--nfs-version=" version))
370 nfs-versions)
371 #$(number->string nfsd-threads)
372 #$(if nfsd-tcp?
373 "--tcp"
374 "--no-tcp")
375 #$(if nfsd-udp?
376 "--udp"
377 "--no-udp"))))))
378 (stop
379 #~(lambda _
380 (zero?
381 (system* #$(file-append nfs-utils "/sbin/rpc.nfsd") "0")))))
382 (shepherd-service
383 (documentation "Run the NFS mountd daemon and refresh exports.")
384 (provision '(nfs))
385 (requirement '(/proc/fs/nfsd rpc.nfsd rpc.mountd rpc.statd rpcbind-daemon))
386 (start
387 #~(lambda _
388 (let ((rpcdebug #$(file-append nfs-utils "/sbin/rpcdebug")))
389 (cond
390 ((member 'nfsd '#$debug)
391 (system* rpcdebug "-m" "nfsd" "-s" "all"))
392 ((member 'nfs '#$debug)
393 (system* rpcdebug "-m" "nfs" "-s" "all"))
394 ((member 'rpc '#$debug)
395 (system* rpcdebug "-m" "rpc" "-s" "all"))))
396 (zero? (system*
397 #$(file-append nfs-utils "/sbin/exportfs")
398 "-r" ; re-export
399 "-a" ; everthing
400 "-v" ; be verbose
401 "-d" "all" ; debug
402 ))))
403 (stop
404 #~(lambda _
405 (let ((rpcdebug #$(file-append nfs-utils "/sbin/rpcdebug")))
406 (cond
407 ((member 'nfsd '#$debug)
408 (system* rpcdebug "-m" "nfsd" "-c" "all"))
409 ((member 'nfs '#$debug)
410 (system* rpcdebug "-m" "nfs" "-c" "all"))
411 ((member 'rpc '#$debug)
412 (system* rpcdebug "-m" "rpc" "-c" "all"))))
413 #t))
414 (respawn? #f)))))
415
416(define %nfs-activation
417 (with-imported-modules '((guix build utils))
418 #~(begin
419 (use-modules (guix build utils))
420
421 ;; directory containing monitor list
422 (mkdir-p "/var/lib/nfs/sm")
423 ;; Needed for client recovery tracking
424 (mkdir-p "/var/lib/nfs/v4recovery")
425 (let ((user (getpw "nobody")))
426 (chown "/var/lib/nfs"
427 (passwd:uid user)
428 (passwd:gid user))
429 (chown "/var/lib/nfs/v4recovery"
430 (passwd:uid user)
431 (passwd:gid user)))
432 #t)))
433
434(define nfs-service-type
435 (service-type
436 (name 'nfs)
437 (extensions
438 (list
439 (service-extension shepherd-root-service-type nfs-shepherd-services)
440 (service-extension activation-service-type (const %nfs-activation))
441 (service-extension etc-service-type
442 (lambda (config)
443 `(("exports"
444 ,(plain-file "exports"
445 (string-join
446 (map string-join
447 (nfs-configuration-exports config))
448 "\n"))))))
449 ;; The NFS service depends on these other services. They are extended so
450 ;; that users don't need to configure them manually.
451 (service-extension idmap-service-type
452 (lambda (config)
453 (idmap-configuration
454 (domain (nfs-configuration-idmap-domain config))
455 (verbosity
456 (if (member 'idmap (nfs-configuration-debug config))
457 10 0))
458 (pipefs-directory (nfs-configuration-pipefs-directory config))
459 (nfs-utils (nfs-configuration-nfs-utils config)))))
460 (service-extension pipefs-service-type
461 (lambda (config)
462 (pipefs-configuration
463 (mount-point (nfs-configuration-pipefs-directory config)))))
464 (service-extension rpcbind-service-type
465 (lambda (config)
466 (rpcbind-configuration
467 (rpcbind (nfs-configuration-rpcbind config)))))))
468 (description
469 "Run all NFS daemons and refresh the list of exported file systems.")))
470
471 ;; Autofs
472
473(define %autofs-pid-file "/var/run/autofs.pid")
474
475(define (value->string _ value)
476 (object->string value display))
477
478(define (option-flag? value)
479 "Is @var{value} a mount option flag?
480Option flags are value like @var{ro}, @var{noatime}, @var{nosuid}, etc."
481 (or (string? value)
482 (symbol? value)))
483
484(define (option-value? value)
485 (or (option-flag? value)
486 (integer? value)))
487
488(define (option-pair? value)
489 "Is @var{value} an option pair?
490Option pairs are cons cells of (option-flag . option-value), used for
491mount options like @{var errors=remount-ro}, @var{timeo=600}, etc."
492 (match value
493 (((? option-flag? _) . (? option-value? _)) #t)
494 (_ #f)))
495
496(define (serialize-option-pair name value)
497 (match value
498 ((option-flag . option-value)
499 (string-append (value->string name option-flag)
500 "="
501 (value->string name option-value)))))
502
503(define (file-system-option? value)
504 (or (option-flag? value)
505 (option-pair? value)))
506
507(define (serialize-file-system-option name value)
508 (cond
509 ((option-flag? value) (value->string name value))
510 ((option-pair? value) (serialize-option-pair name value))))
511
512(define (file-system-options? value)
513 (list-of file-system-option?))
514
515(define (serialize-file-system-options name value)
516 (string-join (map (cut serialize-file-system-option name <>) value) ","))
517
518(define-configuration autofs-map-entry
519 (type (string "auto")
520 "The type of the filesystem."
521 (serializer value->string))
522 (device string
523 "Device or remote host to mount. May contain special
524character @code{&}, which can be referenced in the @var{mount-point}
525field."
526 (serializer value->string))
527 (mount-point string
528 "Directory to mount this device on.
529
530Map entries come in two flavors: direct and indirect. Direct entries
531map a single device to a single mountpoint, while indirect entries can
532map multiple devices to multiple mountpoints.
533
534A direct entry has a @var{mount-point} beginning with @code{/}, representing
535the absolute path of the directory to mount the device on. For example:
536
537 (autofs-map-entry
538 (type \"ext4\")
539 (device \"/dev/sdb1\")
540 (mount-point \"/mnt/external-disk\"))
541
542An indirect entry has a @var{mount-point} not beginning with @code{/},
543representing the subdirectory within the parent indirect map for this
544entry. Indirect maps may also use the special character @code{*},
545which will be replaced with the value of special character @code{&} in
546the @var{device} field of this entry. For example:
547
548 (autofs-indirect-map
549 (mount-point \"/devices\")
550 (entries
551 (list
552 ;; Automount any block device r/o by ID.
553 (autofs-map-entry
554 (type \"auto\")
555 (mount-point \"ro/uuid/*\")
556 (device \"/dev/disk/by-id/&\")
557 (options '(ro)))
558 ;; Automount any block device by UUID.
559 (autofs-map-entry
560 (type \"auto\")
561 (mount-point \"rw/uuid/*\")
562 (device \"/dev/disk/by-uuid/&\")))))"
563 (serializer value->string))
564 (options (file-system-options '())
565 "List of mount options.
566
567Some options are simple flags, such as ro, noexec, nosuid, etc. These
568may be expressed as strings or symbols.
569
570Other options also accept a value. These are expressed as pairs of
571@code{(option . value)}. @code{option} may be a string or symbol, as
572with flags. @code{value} may be a string, symbol, or number.
573
574Example: @code{(ro (errors . remount-ro) noexec)}"))
575
576(define (serialize-autofs-map-entry _ value)
577 (let ((all-options
578 (serialize-file-system-options
579 #f
580 `((fstype . ,(autofs-map-entry-type value))
581 ,@(autofs-map-entry-options value)))))
582 (string-join (list (autofs-map-entry-mount-point value)
583 (string-append "-" all-options)
584 (value->string #f (autofs-map-entry-device value)))
585 " ")))
586
587(define autofs-map-entries? (list-of autofs-map-entry?))
588
589(define (serialize-autofs-map-entries name value)
590 (string-join
591 (map (cut serialize-autofs-map-entry name <>) value)
592 "\n"
593 'suffix))
594
595(define-configuration autofs-indirect-map
596 (mount-point string "Where to mount the indirect map."
597 (serializer value->string))
598 (entries (autofs-map-entries '()) "Entries in this map."))
599
600(define (autofs-serialize-indirect-map name value)
601 (serialize-autofs-map-entries name (autofs-indirect-map-entries value)))
602
603(define (autofs-direct-mount-point? mount-point)
604 (string= "/" (substring mount-point 0 1)))
605
606(define (autofs-direct-map? value)
607 (and (autofs-map-entry? value)
608 (autofs-direct-mount-point? (autofs-map-entry-mount-point value))))
609
610(define (mount-map? value)
611 (or (autofs-direct-map? value)
612 (autofs-indirect-map? value)))
613
614(define (mount-maps? value)
615 (list-of mount-map?))
616
617(define (scheme->autofs name-sym)
618 (string-replace-substring
619 (string-replace-substring (symbol->string name-sym) "-" "_")
620 "?" ""))
621
622(define (autofs-serialize-integer name value)
623 (format #f "~a = ~a" (scheme->autofs name) value))
624
625(define (autofs-serialize-boolean name value)
626 (format #f "~a = ~a" (scheme->autofs name) (if value "yes" "no")))
627
628(define (log-level? value)
629 (and (keyword? value)
630 (member value '(#:none #:verbose #:debug))))
631
632(define (autofs-serialize-log-level name value)
633 (format #f "~a = ~a" (scheme->autofs name)
634 (symbol->string (keyword->symbol value))))
635
636(define-configuration autofs-configuration
637 (autofs
638 (package autofs)
639 "The autofs package to use."
640 (serializer empty-serializer))
641
642 (mounts
643 (mount-maps '())
644 "Mount maps to manage.
645
646This is a list of either direct map entries or indirect mount maps."
647 (serializer empty-serializer))
648
649 (timeout
650 (integer 300)
651 "Sets the default mount timeout in seconds.")
652
653 (master-wait
654 (integer 10)
655 "Sets the default maximum number of retries (actual iterations is half
656this, each is delayed by 2 seconds before retrying) waiting for the master map
657to become available if it cannot be read at program start. This can be longer
658if the map source itself waits for availability (such as sss).")
659
660 (negative-timeout
661 (integer 60)
662 "Set the default timeout for caching failed key lookups.")
663
664 (mount-verbose?
665 (boolean #f)
666 "Use the verbose flag when spawning mount(8), and log some process info
667about the requestor and its parent.")
668
669 (mount-wait
670 (integer -1)
671 "Set the default time to wait for a response from a spawned mount(8) before
672sending it a SIGTERM. Note that we still need to wait for the RPC layer to
673timeout before the sub-process exits so this isn't ideal but it is the best we
674can do. The default is to wait until mount(8) returns without intervention.")
675
676 (umount-wait
677 (integer 12)
678 "Set the default time to wait for a response from a spawned umount(8)
679before sending it a SIGTERM. Note that we still need to wait for the RPC layer
680to timeout before the sub-process exits so this isn't ideal but it is the best
681we can do.")
682
683 (browse-mode?
684 (boolean #t)
685 "Should maps be browsable by default?")
686
687 (mount-nfs-default-protocol
688 (integer 3)
689 "Set the default protocol that mount.nfs(8) uses when performing a mount.
690Autofs needs to know the default NFS protocol that mount.nfs(8) uses so it can
691do special case handling for its availability probe for different NFS
692protocols. Since we can't identify this default automatically we need to set
693it in the autofs configuration.")
694
695 (append-options?
696 (boolean #t)
697 "When #t, global options are appended to map entry options. When #f, map
698entry options replace the global options.")
699
700 (logging
701 (log-level #:none)
702 "Default log level. May be #:none, #:verbose, or #:debug.")
703
704 (force-standard-program-map-env?
705 (boolean #f)
706 "Override the use of a prefix with standard environment variables when a
707program map is executed. Since program maps are run as the privileged user
708setting these standard environment variables opens automount(8) to potential
709user privilege escalation when the program map is written in a language that
710can load components from, for example, a user home directory.")
711
712 (map-hash-table-size
713 (integer 1024)
714 "This configuration option may be used to change the number of hash table
715slots.
716
717This configuration option affects the overhead of searching the map entry
718cache for map entries when there are a large number of entries. It affects
719the number of entries that must be looked at to locate a map entry in the map
720entry cache. For example, the default of 1024 and a direct map with 8000
721entries would result in each slot containing an average of 8 entries, which
722should be acceptable.
723
724However, if excessive CPU usage is observed during automount lookups
725increasing this option can reduce the CPU overhead considerably because it
726reduces the length of the search chains.
727
728Note that the number of entries in a map doesn't necessarily relate to the
729number of entries used in the map entry cache.
730
731There are two distinct cases where the map hash table can make a significant
732difference, direct maps and indirect maps that use the \"browse\" option.
733
734For indirect maps that do not use the \"browse\" option entries are added to
735the map entry cache at lookup so the number of active cache entries, in this
736case, is usually much less than the number of entries in the map. In this
737last case it would be unusual for the map entry cache to grow large enough to
738warrant increasing the default before an event that cleans stale entries, a
739map re-read for example.")
740
741 (use-hostname-for-mounts?
742 (boolean #f)
743 "NFS mounts where the host name resolves to more than one IP address are
744probed for availability and to establish the order in which mounts to them
745should be tried. To ensure that mount attempts are made only to hosts that
746are responding and are tried in the order of hosts with the quickest response
747the IP address of the host needs to be used for the mount.
748
749If it is necessary to use the hostname given in the map entry for the mount
750regardless, then set this option to #t.
751
752Be aware that if this is done there is no defense against the host name
753resolving to one that isn't responding and while the number of attempts at a
754successful mount will correspond to the number of addresses the host name
755resolves to the order will also not correspond to fastest responding hosts.")
756
757 (disable-not-found-message?
758 (boolean #f)
759 "The original request to add this log message needed it to be
760unconditional. That produces, IMHO, unnecessary noise in the log so a
761configuration option has been added to provide the ability to turn it off.")
762
763 (use-ignore-mount-option?
764 (boolean #f)
765 "An option to enable the use of autofs pseudo option \"disable\". This
766option is used as a hint to user space that the mount entry should be omitted
767from mount table listings. The default is #f to avoid unexpected changes in
768behaviour and so is an opt-in setting.")
769
770 (sss-master-map-wait
771 (integer 0)
772 "Set the time to wait and retry if sssd is unable to read the master map at
773program start. Default is 0 (don't wait) or 10 if sss supports returning
774EHSTDOWN when the provider isn't available.
775
776If the sss library upports returning EHOSTDOWN when the provider is down then
777this value is how long to wait between re‐ tries reading the master map. When
778reading dependent maps or looking up a map key this value is multiplied by the
779number of retries that would be used when reading the master map.")
780
781 (use-mount-request-log-id?
782 (boolean #f)
783 "Set whether to use a mount request log id so that log entries for specific
784mount requests can be easily identified in logs that have multiple concurrent
785requests.")
786 (prefix autofs-))
787
788(define (indirect-map->file-name indirect-map)
789 (string-append
790 (string-replace-substring
791 (substring (autofs-indirect-map-mount-point indirect-map) 1)
792 "/" "-") ".map"))
793
794(define (autofs-build-config config)
795 (let* ((mounts (autofs-configuration-mounts config))
796 (autofs-conf (list-transduce (base-transducer config) rcons
797 autofs-configuration-fields))
798 ;; List of serialized direct maps.
799 (direct-maps
800 (serialize-autofs-map-entries
801 #f (filter autofs-direct-map? mounts)))
802
803 ;; List of (file-name mount-point serialized-map).
804 (indirect-maps
805 (map
806 (lambda (indirect-map)
807 (list (indirect-map->file-name indirect-map)
808 (autofs-indirect-map-mount-point indirect-map)
809 (autofs-serialize-indirect-map #f indirect-map)))
810 (filter autofs-indirect-map? mounts))))
811 (computed-file
812 "autofs-config"
813 (with-imported-modules
814 (source-module-closure '((guix build utils) (ice-9 match)))
815 #~(begin
816 (use-modules (guix build utils) (ice-9 match))
817
818 (mkdir-p #$output)
819
820 (call-with-output-file (string-append #$output "/autofs.conf")
821 (lambda (autofs-conf)
822 ;; Write out the serialized config.
823 (display (string-join
824 (cons*
825 "[ autofs ]"
826 (string-append "master_map_name = "
827 #$output "/auto.master")
828 '#$autofs-conf)
829 "\n" 'suffix)
830 autofs-conf)))
831
832 ;; Write out the master map.
833 (call-with-output-file (string-append #$output "/auto.master")
834 (lambda (master-map)
835 ;; Write the direct entries to the master map.
836 (display '#$direct-maps master-map)
837
838 ;; Write indirect maps to their own files.
839 (for-each
840 (match-lambda
841 ((file-name mount-point content)
842 ;; Write the indirect map.
843 (call-with-output-file
844 (string-append #$output "/" file-name)
845 (lambda (indirect-map)
846 (display content indirect-map)))
847 ;; Reference it in the master map.
848 (format master-map "~a ~a/~a~%"
849 mount-point #$output file-name)))
850 '#$indirect-maps))))))))
851
852(define (autofs-activation config)
853 (let* ((config-dir (autofs-build-config config))
854 (autofs-conf (file-append config-dir "/autofs.conf"))
855 (conf-link "/etc/autofs.conf")
856 (no-mounts? (null? (autofs-configuration-mounts config)))
857 (direct-mount-points
858 (map
859 autofs-map-entry-mount-point
860 (filter autofs-direct-map?
861 (autofs-configuration-mounts config))))
862 (indirect-mount-points
863 (map
864 autofs-indirect-map-mount-point
865 (filter
866 autofs-indirect-map?
867 (autofs-configuration-mounts config))))
868 (mount-points (append direct-mount-points indirect-mount-points)))
869 #~(begin
870 (use-modules (guix build utils))
871 (for-each mkdir-p (cons "/var/lib/nfs/sm" '#$mount-points))
872 (when (or (false-if-exception (lstat #$conf-link))
873 (stat #$conf-link #f))
874 (delete-file #$conf-link))
875 (unless #$no-mounts?
876 (symlink #$autofs-conf #$conf-link)))))
877
878(define (autofs-configuration->raw-entries config)
879 (fold
880 (lambda (mount acc)
881 (cond
882 ((autofs-direct-map? mount)
883 (cons mount acc))
884 ((autofs-indirect-map? mount)
885 (append (autofs-indirect-map-entries mount) acc))))
886 '()
887 (autofs-configuration-mounts config)))
888
889(define (autofs-configuration->requirements config)
890 "Compute Shepherd service requirements for @var{config}.
891
892If @var{config} contains NFS mounts, adds rpc.statd to the service
893requirements.
894
895If @var{config} contains SMB mounts, adds samba-nmbd and samba-winbindd to the
896service requirements.
897"
898 (fold
899 (lambda (fs-type acc)
900 (cond
901 ((string= "nfs" fs-type)
902 (append acc '(rpc.statd)))
903 ((string= "smb" fs-type)
904 (append acc '(samba-nmbd samba-winbindd)))
905 (else acc)))
906 '()
907 (delete-duplicates
908 (map autofs-map-entry-type
909 (autofs-configuration->raw-entries config)))))
910
911(define (autofs-shepherd-service config)
912 (match-record config <autofs-configuration> (autofs timeout)
913 (begin
914 (define autofs-command
915 ;; Autofs doesn't let us specify the config file, so we rely on
916 ;; autofs-activation linking /etc/autofs.conf to the store.
917 #~(list
918 #$(file-append autofs "/sbin/automount")
919 "-f"
920 "-t" (number->string #$timeout)
921 "-p" #$%autofs-pid-file))
922
923 (list
924 (shepherd-service
925 (provision '(autofs automount))
926 (documentation "Run the autofs daemon.")
927 (requirement (autofs-configuration->requirements config))
928 (start
929 #~(make-forkexec-constructor
930 #$autofs-command
931 #:pid-file #$%autofs-pid-file))
932 (stop #~(make-kill-destructor)))))))
933
934(define (autofs-configuration-merge a b)
935 (autofs-configuration
936 (inherit b)
937 (mounts (append (autofs-configuration-mounts a)
938 (autofs-configuration-mounts b)))))
939
940(define-public autofs-service-type
941 (service-type
942 (name 'autofs)
943 (description "Run autofs")
944 (extensions
945 (list
946 (service-extension activation-service-type
947 autofs-activation)
948 (service-extension shepherd-root-service-type
949 autofs-shepherd-service)))
950 (compose
951 (cut reduce autofs-configuration-merge (autofs-configuration) <>))
952 (default-value (autofs-configuration))))