Subject: [bug#41180] [PATCH] Add cachefilesd service.

From: Jean-Baptiste Note <[email-masked]>
Date: Sun, 10 May 2020 19:19:36 +0000

Dear guix developers, Mathieu,

Please find attached a first version of the cachefilesd service patch.
The second patch for documentation will be sent right after.

I have a few general newbie scheme questions:

- I gathered that #~ / #$ kinds of suspends evaluation / forces it -- is
  there documentation about this somewhere ?

- There's something that looks like a splat operator (only seen in
  conjuction with forcing evaluation in #$@) -- again i'd be interested
  in more documentation about this feature -- is this a guix-specific
  operator?

- I don't understand why there are ^L separating services in the scheme
  files -- is this necessary? A convention? What purpose does it serve?

Regarding the patch itself:

- i'm not entirely sure the service belongs to services/linux.scm

- documentation is hastily written. I have found no way to indent
  automatically the lisp code in the texi file, which is very painfull
  -- would there be an emacs solution for this?

- there are no automated tests (beyond what I have done by hand
  locally!), and there's no lint, so I don't really feel confident about
  it :) Are there tests for services to alleviate my fears?

- I've copied some other service for modprobing the required kernel
  modules before launching the daemon with a one-shot shepherd
  service. Frankly i'm not happy about this solution, it seems to me
  that it unnecessarily pollutes the shepherd configuration; maybe some
  other mechanism (graft?) adjusting the modprobe configuration could be
  better (better still, autoload the file). Any guidance would be nice
  (including, that this solution is sufficient for now :))

I had great fun writing this, it reminded me of writing cookbooks during
my 'Chef' days. I must confess that the Chef DSL embedded in ruby seemed
more concise, and that it provided a way to write a cookbook
'out-of-tree' which was kind of less daunting for newcomers; however
it's already amazing that you have this kind of functionality!

Kind regards,
Jean-Baptiste
From 729d43d541e8dcb41b36a7522ec291b0c3f8dd14 Mon Sep 17 00:00:00 2001
From: Jean-Baptiste Note <[email-masked]>
Date: Sat, 9 May 2020 15:14:26 +0000
Subject: [PATCH 1/2] gnu: Add cachefilesd-service.

* doc/guix.texi (Linux Services): Add a new subsection and document the
new service and its configuration.
* gnu/services/linux.scm (cachefilesd-service-type): New type.
(cachefilesd-configuration): New type.
---
 gnu/services/linux.scm | 210 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 209 insertions(+), 1 deletion(-)

diff --git a/gnu/services/linux.scm b/gnu/services/linux.scm
index 12934c2084..810901e0ca 100644
--- a/gnu/services/linux.scm
+++ b/gnu/services/linux.scm
@@ -42,7 +42,11 @@
             earlyoom-configuration-send-notification-command
             earlyoom-service-type
 
-            kernel-module-loader-service-type))
+            kernel-module-loader-service-type
+
+            cachefilesd-configuration
+            cachefilesd-configuration?
+            cachefilesd-service-type))
 
 
 ;;;
@@ -177,3 +181,207 @@ representation."
    (compose concatenate)
    (extend append)
    (default-value '())))
+
+
+;;;
+;;; cachefilesd.
+;;;
+
+(define-record-type* <cachefilesd-configuration>
+  cachefilesd-configuration make-cachefilesd-configuration
+  cachefilesd-configuration?
+
+  ;; <package-path>
+  (cachefilesd           cachefilesd-configuration-cachefilesd
+                         (default cachefilesd))
+
+  ;; cmdline flags
+  ;; Boolean
+  (daemonic?             cachefilesd-configuration-daemonic?
+                         (default #t))
+
+  ;; string
+  (pid-file              cachefilesd-configuration-pid-file
+                         (default "/var/run/cachefilesd.pid"))
+
+  ;; Boolean
+  (debug?                cachefilesd-configuration-debug?
+                         (default #f))
+  ;; Boolean
+  (syslog?               cachefilesd-configuration-syslog?
+                         (default #t))
+  ;; Boolean
+  (culling-and-scanning? cachefilesd-configuration-culling-and-scanning?
+                         (default #t))
+
+  ;; configuration file contents
+  ;; String
+  (dir                   cachefilesd-configuration-dir
+                         (default "/var/cache/fscache"))
+
+  ;; String
+  (tag                   cachefilesd-configuration-tag
+                         (default "CacheFiles"))
+
+  ;; String
+  (secctx                cachefilesd-configuration-secctx
+                         (default #f))
+
+  ;; integers
+  (brun                  cachefilesd-configuration-brun
+                         (default 7))
+  (frun                  cachefilesd-configuration-frun
+                         (default 7))
+  (bcull                 cachefilesd-configuration-bcull
+                         (default 5))
+  (fcull                 cachefilesd-configuration-fcull
+                         (default 5))
+  (bstop                 cachefilesd-configuration-bstop
+                         (default 1))
+  (fstop                 cachefilesd-configuration-fstop
+                         (default 1))
+
+  ;; integer
+  (culltable             cachefilesd-configuration-culltable
+                         (default 12))
+
+  ;; integer / debug mask
+  (kernel-debug          cachefilesd-configuration-kernel-debug
+                         (default 0))
+
+  ;; boolean
+  (nocull?               cachefilesd-configuration-nocull?
+                         (default #f))
+  ;; Boolean
+  ;; XXX: This should really be handled in an orthogonal way, for instance as
+  ;; proposed in <https://bugs.gnu.org/27155>.  Keep it internal/undocumented
+  ;; for now.
+  (%auto-start?          cachefilesd-auto-start?
+                         (default #t)))
+
+(define (cachefilesd-configuration-file config)
+  "Return the cachefilesd configuration file corresponding to CONFIG."
+  (define secctx
+    (cachefilesd-configuration-dir config))
+
+  (computed-file
+   "cachefilesd.conf"
+   #~(begin
+       (use-modules (ice-9 match))
+       (call-with-output-file #$output
+         (lambda (port)
+           (display "# Generated by 'cachefilesd-service'.\n" port)
+           (format port "dir ~a\n" #$(cachefilesd-configuration-dir config))
+
+           (let ((secctx #$(cachefilesd-configuration-secctx config)))
+             (if secctx (format port "secctx ~a" secctx)))
+
+           ;; XXX factor this
+           (format port "brun ~a%\n"
+                   #$(number->string
+                      (cachefilesd-configuration-brun config)))
+           (format port "frun ~a%\n"
+                   #$(number->string
+                      (cachefilesd-configuration-frun config)))
+           (format port "bcull ~a%\n"
+                   #$(number->string
+                      (cachefilesd-configuration-bcull config)))
+           (format port "fcull ~a%\n"
+                   #$(number->string
+                      (cachefilesd-configuration-fcull config)))
+           (format port "bstop ~a%\n"
+                   #$(number->string
+                      (cachefilesd-configuration-bstop config)))
+           (format port "fstop ~a%\n"
+                   #$(number->string
+                      (cachefilesd-configuration-fstop config)))
+
+           (format port "tag ~a\n" #$(cachefilesd-configuration-tag config))
+
+           (format port "culltable ~a\n"
+                   #$(number->string
+                      (cachefilesd-configuration-culltable config)))
+
+           (if #$(cachefilesd-configuration-nocull? config)
+               (display "nocull\n" port))
+
+           (format port "debug ~a\n"
+                   #$(number->string
+                      (cachefilesd-configuration-kernel-debug config)))
+
+           #t)))))
+
+(define (cachefilesd-activation config)
+  "Return cachefilesd's activation GEXP for CONFIG."
+  (with-imported-modules '((guix build utils))
+    #~(begin
+        (use-modules (guix build utils))
+        ;; Make sure the cache directory and pid dir exists
+        (mkdir-p #$(cachefilesd-configuration-dir config))
+        (mkdir-p (dirname #$(cachefilesd-configuration-pid-file config))))))
+
+(define (cachefilesd-shepherd-service config)
+  "Return a <shepherd-service> for cachefilesd with CONFIG."
+
+  (define cachefilesdpath
+    (cachefilesd-configuration-cachefilesd config))
+  (define pid-file
+    (cachefilesd-configuration-pid-file config))
+  (define syslog?
+    (cachefilesd-configuration-syslog? config))
+  (define culling-and-scanning?
+    (cachefilesd-configuration-culling-and-scanning? config))
+  (define debug?
+    (cachefilesd-configuration-debug? config))
+
+  (define cachefilesd-command
+    #~(list #$(file-append cachefilesdpath "/sbin/cachefilesd")
+            #$@(if (cachefilesd-configuration-daemonic? config) '() '("-n"))
+            ;; XXX shepherd pid file handling: no idea how shepherd does it
+            ;; and if it's going to conflict with cachefilesd's
+            #$@(if debug? '("-d") '())
+            #$@(if syslog? '() '("-s"))
+            #$@(if culling-and-scanning? '() '("-N"))
+            "-p" #$pid-file
+            "-f" #$(cachefilesd-configuration-file config)))
+
+  (list (shepherd-service
+         (documentation "Start cachefilesd daemon.")
+         (requirement (append '(file-systems cachefiles-module)
+                              (if syslog? '(syslogd) '())))
+         (provision '(cachefilesd))
+         (start #~(make-forkexec-constructor #$cachefilesd-command
+                                             #:pid-file #$pid-file))
+         (stop #~(make-kill-destructor))
+         (auto-start? (cachefilesd-auto-start? config)))
+
+        (shepherd-service
+         (provision '(cachefiles-module))
+         (requirement '(file-systems))
+         (modules '((guix build utils)))
+         (documentation
+          "Load the cachefiles Linux kernel module.")
+         (start (with-imported-modules '((guix build utils))
+                  #~(lambda _
+                      ;; XXX: duplicated from networking
+                      ;; -- factor this into a modprobe command
+                      ;; XXX: We can't use 'load-linux-module*' here because it
+                      ;; expects a flat module directory.
+                      (setenv "LINUX_MODULE_DIRECTORY"
+                              "/run/booted-system/kernel/lib/modules")
+                      (invoke #$(file-append kmod "/bin/modprobe")
+                              "cachefiles"))))
+         (one-shot? #t))))
+
+(define cachefilesd-service-type
+  (service-type (name 'cachefilesd)
+                (description
+                 "Run the CacheFile backend daemon, @command{cachefilesd}.")
+                (extensions
+                 (list
+                  (service-extension shepherd-root-service-type
+                                     cachefilesd-shepherd-service)
+                  (service-extension activation-service-type
+                                     cachefilesd-activation)))
+                (compose concatenate)
+                (default-value (cachefilesd-configuration))))
-- 
2.26.2