Subject: Re: [bug#28647] [PATCHv2] services: base: Add file->udev-rule function.

From: Maxim Cournoyer <[email-masked]>
Date: Sun, 22 Oct 2017 21:53:28 -0400

Hi Ludovic, and sorry for the delayed reply!

[email-masked] (Ludovic Courtès) writes:

> Hi Maxim,
>
> Maxim Cournoyer <[email-masked]> skribis:
>
>> While experimenting with udev rules, I found a need to be able to pass
>> file-like objects containing udev rules to the udev-service. This patch
>> implements a `file->udev-rule' method that does just that.
>
> Looks useful!
>
>> From 050e96b3325f851f3118de0c881d25796d76049b Mon Sep 17 00:00:00 2001
>> From: Maxim Cournoyer <[email-masked]>
>> Date: Wed, 27 Sep 2017 21:33:25 -0400
>> Subject: [PATCH] services: base: Add file->udev-rule function.
>>
>> This function allows passing a file-like object to the udev service.
>>
>> * gnu/services/base.scm (file->udev-rule): New function.
>> * doc/guix.texi (Base Services): Document it.
>
> [...]
>
>> -@deffn {Scheme Procedure} udev-service [#:udev udev]
>> +@deffn {Scheme Procedure} udev-service [#:udev @var{eudev} #:rules @code{'()}]
>>  Run @var{udev}, which populates the @file{/dev} directory dynamically.
>> +Packages can be included in the @var{rules} list in order to extend the
>> +udev rules with the definitions found under their
>> +@file{lib/udev/rules.d} sub-directory.
>> +
>> +The functions @code{udev-rules} and @code{file->udev-rules} from
>> +@code{(gnu services base)} can be used to create rule objects based on a
>> +string or a file-like object, respectively. Those rule objects can be
>> +passed to udev-service just like packages.
>
> Could you document the procedures with @deffn so that they have an entry
> in the index?
>
>   @deffn {Scheme Procedure} udev-rules @var{rules}
>   …
>   @end deffn
>
>   @deffn {Scheme Procedure} file->udev-rules @var{files}
>   Similar to @code{udev-rules}, but…
>   @end deffn
>

I'm hoping the attached patch addresses the requested additions. Thanks
for the review and comments.

Maxim

From d2da854ad8d9d0d588c646e439ebfb7ef1c21f7c Mon Sep 17 00:00:00 2001
From: Maxim Cournoyer <[email-masked]>
Date: Wed, 27 Sep 2017 21:33:25 -0400
Subject: [PATCH] services: base: Add file->udev-rule function.

This function allows passing a file-like object to the udev service.

* gnu/services/base.scm (file->udev-rule): New function.
* doc/guix.texi (Base Services): Document it.
---
 doc/guix.texi         | 113 ++++++++++++++++++++++++++++++++++++++++++--------
 gnu/services/base.scm |  17 ++++++++
 2 files changed, 112 insertions(+), 18 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index b7f4f88f9..832ce8ac0 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -9790,35 +9790,112 @@ Return a service that runs the Guix build daemon according to
 @var{config}.
 @end deffn
 
-@cindex udev-service
-@cindex udev-rule
-@deffn {Scheme Procedure} udev-service [#:udev @var{udev}] [#:rules @var{'()}]
+@deffn {Scheme Procedure} udev-service [#:udev @var{eudev} #:rules @code{'()}]
 Run @var{udev}, which populates the @file{/dev} directory dynamically.
-Additional udev rules can be provided as a list of files through the
-@var{rules} variable.  The procedure @var{udev-rule} simplifies the
-creation of these rule files.
+udev rules can be provided as a list of files through the @var{rules}
+variable.  The procedures @var{udev-rule} and @var{file->udev-rule} from
+@code{(gnu services base)} simplify the creation of such rule files.
+
+@deffn {Scheme Procedure} udev-rule [@var{file-name} @var{contents}]
+Return a udev-rule file named @var{file-name} containing the rules
+defined by the @var{contents} literal.
 
 In the following example, a rule for a USB device is defined to be
-stored in the file @file{90-usb-thing.rules}, and the default
-@var{udev-service} is extended with it.  The rule runs a script upon
-detecting a USB device with a given product identifier.
+stored in the file @file{90-usb-thing.rules}.  The rule runs a script
+upon detecting a USB device with a given product identifier.
 
 @example
 (define %example-udev-rule
-  (udev-rule "90-usb-thing.rules"
-             "ACTION==\"add\", SUBSYSTEM==\"usb\", ATTR@{product@}==\"Example\", RUN+=\"/path/to/script\""))
+  (udev-rule
+    "90-usb-thing.rules"
+    (string-append "ACTION==\"add\", SUBSYSTEM==\"usb\", "
+                   "ATTR@{product@}==\"Example\", "
+                   "RUN+=\"/path/to/script\"")))
+@end example
+@end deffn
+
+Here we show how the default @var{udev-service} can be extended with it.
+
+@example
+(operating-system
+ ;; @dots{}
+ (services
+ (modify-services %desktop-services
+   (udev-service-type config =>
+     (udev-configuration (inherit config)
+      (rules (append (udev-configuration-rules config)
+                     (list %example-udev-rule))))))))
+@end example
+
+@deffn {Scheme Procedure} file->udev-rule [@var{file-name} @var{file}]
+Return a udev file named @var{file-name} containing the rules defined
+within @var{file}, a file-like object.
+
+The following example showcases how we can use an existing rule file.
+
+@example
+(use-modules (guix download)     ;for url-fetch
+             (guix packages)     ;for origin
+             ;; @dots{})
+
+(define %android-udev-rules
+  (file->udev-rule
+    "51-android-udev.rules"
+    (let ((version "20170910"))
+      (origin
+       (method url-fetch)
+       (uri (string-append "https://raw.githubusercontent.com/M0Rf30/"
+                           "android-udev-rules/" version "/51-android.rules"))
+       (sha256
+        (base32 "0lmmagpyb6xsq6zcr2w1cyx9qmjqmajkvrdbhjx32gqf1d9is003"))))))
+@end example
+@end deffn
+
+Additionally, Guix package definitions can be included in @var{rules} in
+order to extend the udev rules with the definitions found under their
+@file{lib/udev/rules.d} sub-directory.  In lieu of the previous
+@var{file->udev-rule} example, we could have used the
+@var{android-udev-rules} package which exists in Guix in the @code{(gnu
+packages android)} module.
+
+The following example shows how to use the @var{android-udev-rules}
+package so that the Android tool @command{adb} can detect devices
+without root privileges.  It also details how to create the
+@code{adbusers} group, which is required for the proper functioning of
+the rules defined within the @var{android-udev-rules} package.  To
+create such a group, we must define it both as part of the
+@var{supplementary-groups} of our @var{user-account} declaration, as
+well as in the @var{groups} field of the @var{operating-system} record.
+
+@example
+(use-modules (gnu packages android)  ;for android-udev-rules
+             (gnu system shadow)     ;for user-group
+             ;; @dots{})
 
 (operating-system
   ;; @dots{}
-  (services (modify-services %desktop-services
-              (udev-service-type config =>
-                (udev-configuration (inherit config)
-                  (rules (append (udev-configuration-rules config)
-                                 (list %example-udev-rule))))))))
+  (users (cons (user-acount
+                ;; @dots{}
+                (supplementary-groups
+                 '("adbusers"   ;for adb
+                   "wheel" "netdev" "audio" "video"))
+                ;; @dots{})))
+
+  (groups (cons (user-group (system? #t) (name "adbusers"))
+                %base-groups))
+
+  ;; @dots{}
+
+  (services
+    (modify-services %desktop-services
+      (udev-service-type config =>
+       (udev-configuration (inherit config)
+       (rules (cons* android-udev-rules
+              (udev-configuration-rules config))))))))
 @end example
 @end deffn
 
-@deffn {Scheme Procedure} urandom-seed-service @var{#f}
+@deffn {Scheme Procedure} urandom-seed-service
 Save some entropy in @var{%random-seed-file} to seed @file{/dev/urandom}
 when rebooting.
 @end deffn
@@ -9930,7 +10007,7 @@ to add @var{device} to the kernel's entropy pool.  The service will fail if
 @cindex session limits
 @cindex ulimit
 @cindex priority
-@deffn {Scheme Procedure} pam-limits-service [#:limits @var{limits}]
+@deffn {Scheme Procedure} pam-limits-service [#:limits @code{'()}]
 
 Return a service that installs a configuration file for the
 @uref{http://linux-pam.org/Linux-PAM-html/sag-pam_limits.html,
diff --git a/gnu/services/base.scm b/gnu/services/base.scm
index 541ca76f1..b605614ab 100644
--- a/gnu/services/base.scm
+++ b/gnu/services/base.scm
@@ -71,6 +71,7 @@
             udev-service-type
             udev-service
             udev-rule
+            file->udev-rule
 
             login-configuration
             login-configuration?
@@ -1630,6 +1631,22 @@ item of @var{packages}."
                          (lambda (port)
                            (display #$contents port)))))))
 
+(define (file->udev-rule file-name file)
+  "Return a directory with a udev rule file FILE-NAME which is a copy of FILE."
+  (computed-file file-name
+                 (with-imported-modules '((guix build utils))
+                   #~(begin
+                       (use-modules (guix build utils))
+
+                       (define rules.d
+                         (string-append #$output "/lib/udev/rules.d"))
+
+                       (define file-copy-dest
+                         (string-append rules.d "/" #$file-name))
+
+                       (mkdir-p rules.d)
+                       (copy-file #$file file-copy-dest)))))
+
 (define kvm-udev-rule
   ;; Return a directory with a udev rule that changes the group of /dev/kvm to
   ;; "kvm" and makes it #o660.  Apparently QEMU-KVM used to ship this rule,
-- 
2.14.1