diff mbox series

[bug#54986,v3,1/2] services: shepherd: Add support for socket activation endpoints.

Message ID 684304748f200de9493550d079d59dc8cfe2b002.camel@gmail.com
State New
Headers show
Series [bug#54986,v3,1/2] services: shepherd: Add support for socket activation endpoints. | expand

Commit Message

Liliana Marie Prikler April 23, 2022, 2:25 p.m. UTC
* gnu/services/shepherd.scm (<shepherd-endpoint>): New record type.
(shepherd-endpoint->sexp): New variable.
* doc/guix.texi (Shepherd Services): Document it.
---
 doc/guix.texi             | 32 ++++++++++++++++++++
 gnu/services/shepherd.scm | 64 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 96 insertions(+)

Comments

Maxim Cournoyer April 26, 2023, 12:33 a.m. UTC | #1
Hi!

Liliana Marie Prikler <liliana.prikler@gmail.com> writes:

> * gnu/services/shepherd.scm (<shepherd-endpoint>): New record type.
> (shepherd-endpoint->sexp): New variable.
> * doc/guix.texi (Shepherd Services): Document it.

Like Ludovic, I'm wondering what duplicating the Shepherd endpoints API
in Guix buys us?  It sometimes feel a bit contrived to have to work
inside the service's gexp expression, but other than that, I think it's
good to:

1. Avoid duplication.
2. Keep it as internal/hidden as possible from users.
Liliana Marie Prikler April 26, 2023, 4:28 a.m. UTC | #2
Am Dienstag, dem 25.04.2023 um 20:33 -0400 schrieb Maxim Cournoyer:
> Hi!
> 
> Liliana Marie Prikler <liliana.prikler@gmail.com> writes:
> 
> > * gnu/services/shepherd.scm (<shepherd-endpoint>): New record type.
> > (shepherd-endpoint->sexp): New variable.
> > * doc/guix.texi (Shepherd Services): Document it.
> 
> Like Ludovic, I'm wondering what duplicating the Shepherd endpoints
> API in Guix buys us?  It sometimes feel a bit contrived to have to
> work inside the service's gexp expression, but other than that, I
> think it's good to:
> 
> 1. Avoid duplication.
> 2. Keep it as internal/hidden as possible from users.
I agree with the point about avoiding duplication, but I want users to
be able to specify endpoints for socket activation.  This has several
benefits: It firstly allows users to specify that they want a specific
service to be started on demand rather than on boot, and it also allows
them to bind to multiple endpoints, e.g. any IPv4 address, any IPv6
address or both.  Duplicating the API here is merely a means of
allowing users to express the above in a Guixy fashion.  It also gives
us type-checking which a simple quote or quasiquote doesn't.  If the
same can be achieved by inspecting Shepherd's records and we can allow
Guix to depend on Shepherd, that'd be fine by me. 

Cheers
Maxim Cournoyer May 1, 2023, 3:53 p.m. UTC | #3
Hi Liliana,

Liliana Marie Prikler <liliana.prikler@gmail.com> writes:

> Am Dienstag, dem 25.04.2023 um 20:33 -0400 schrieb Maxim Cournoyer:
>> Hi!
>> 
>> Liliana Marie Prikler <liliana.prikler@gmail.com> writes:
>> 
>> > * gnu/services/shepherd.scm (<shepherd-endpoint>): New record type.
>> > (shepherd-endpoint->sexp): New variable.
>> > * doc/guix.texi (Shepherd Services): Document it.
>> 
>> Like Ludovic, I'm wondering what duplicating the Shepherd endpoints
>> API in Guix buys us?  It sometimes feel a bit contrived to have to
>> work inside the service's gexp expression, but other than that, I
>> think it's good to:
>> 
>> 1. Avoid duplication.
>> 2. Keep it as internal/hidden as possible from users.
> I agree with the point about avoiding duplication, but I want users to
> be able to specify endpoints for socket activation.  This has several
> benefits: It firstly allows users to specify that they want a specific
> service to be started on demand rather than on boot, and it also allows
> them to bind to multiple endpoints, e.g. any IPv4 address, any IPv6
> address or both.  Duplicating the API here is merely a means of
> allowing users to express the above in a Guixy fashion.  It also gives
> us type-checking which a simple quote or quasiquote doesn't.  If the
> same can be achieved by inspecting Shepherd's records and we can allow
> Guix to depend on Shepherd, that'd be fine by me. 

Instead of replicating the Shepherd API in Guix, could we use the
Shepherd API directly?  It's Scheme, and already depended on by Guix, so
the question arises.

It may not be a good idea, but I need to be refreshed on the reasons
:-).
Liliana Marie Prikler May 1, 2023, 4:09 p.m. UTC | #4
Am Montag, dem 01.05.2023 um 11:53 -0400 schrieb Maxim Cournoyer:
> > > Like Ludovic, I'm wondering what duplicating the Shepherd
> > > endpoints API in Guix buys us?  [...]
> 
> Instead of replicating the Shepherd API in Guix, could we use the
> Shepherd API directly?  It's Scheme, and already depended on by Guix,
> so the question arises.
In theory, it'd be possible, albeit with some caveats:
1. Shepherd doesn't (didn't) have a full guix-style records API, which
might cause discrepancies in otherwise normal-looking Scheme code.
2. It'd probably make shepherd a compile-time dependency, which is
avoided in other places in the code, i.e. (gnu build shepherd)
3. Shepherd records are (to my knowledge) not print-readable, so we'd
have to move them through G-Expressions through some of our own code
anyway; how strongly that would replicate the API is up to
debate/speculation.

Cheers
Maxim Cournoyer May 3, 2023, 3:17 a.m. UTC | #5
Hi Liliana,

Liliana Marie Prikler <liliana.prikler@gmail.com> writes:

> Am Montag, dem 01.05.2023 um 11:53 -0400 schrieb Maxim Cournoyer:
>> > > Like Ludovic, I'm wondering what duplicating the Shepherd
>> > > endpoints API in Guix buys us?  [...]
>>
>> Instead of replicating the Shepherd API in Guix, could we use the
>> Shepherd API directly?  It's Scheme, and already depended on by Guix,
>> so the question arises.
> In theory, it'd be possible, albeit with some caveats:
> 1. Shepherd doesn't (didn't) have a full guix-style records API, which
> might cause discrepancies in otherwise normal-looking Scheme code.

Shepherd's code is pretty normal-looking Scheme code to me, last I
checked :-) I think Ludovic has also been working toward bringing it
closer to their standards, e.g. deprecating the use of GOOPS.

> 2. It'd probably make shepherd a compile-time dependency, which is
> avoided in other places in the code, i.e. (gnu build shepherd)

I wonder if that could be an acceptable price to pay?  It seems
reasonable to me that the close integration of Shepherd within Guix
introduces a dependency on it.

> 3. Shepherd records are (to my knowledge) not print-readable, so we'd
> have to move them through G-Expressions through some of our own code
> anyway; how strongly that would replicate the API is up to
> debate/speculation.

As far as I can tell, both are based on SRFI-9 records and use the same
representation, or I miss the fine details from the source.
Ludovic Courtès May 3, 2023, 1:27 p.m. UTC | #6
Hi,

Liliana Marie Prikler <liliana.prikler@gmail.com> skribis:

> Am Montag, dem 01.05.2023 um 11:53 -0400 schrieb Maxim Cournoyer:
>> > > Like Ludovic, I'm wondering what duplicating the Shepherd
>> > > endpoints API in Guix buys us?  [...]
>> 
>> Instead of replicating the Shepherd API in Guix, could we use the
>> Shepherd API directly?  It's Scheme, and already depended on by Guix,
>> so the question arises.
> In theory, it'd be possible, albeit with some caveats:

It’s not just possible: several services in (gnu services …) and (gnu
home services …) use endpoints for systemd or inetd-style startup.

> 1. Shepherd doesn't (didn't) have a full guix-style records API, which
> might cause discrepancies in otherwise normal-looking Scheme code.

I’m not convinced.  :-)

> 2. It'd probably make shepherd a compile-time dependency, which is
> avoided in other places in the code, i.e. (gnu build shepherd)

(gnu build shepherd) is 75% deprecated; the introduction of endpoints in
the Shepherd didn’t have any effect on it.

> 3. Shepherd records are (to my knowledge) not print-readable, so we'd
> have to move them through G-Expressions through some of our own code
> anyway; how strongly that would replicate the API is up to
> debate/speculation.

When would you want to print those <endpoint> records?

Thanks,
Ludo’.
Liliana Marie Prikler May 3, 2023, 4:54 p.m. UTC | #7
Am Mittwoch, dem 03.05.2023 um 15:27 +0200 schrieb Ludovic Courtès:
> It’s not just possible: several services in (gnu services …) and (gnu
> home services …) use endpoints for systemd or inetd-style startup.
True, but to my knowledge they don't yet allow the user to specify
those endpoints directly.  At the very least, they didn't when I
started this thread, which was shortly after shepherd itself gained
endpoints.  I'm happy to be proven wrong on this point.

> > 1. Shepherd doesn't (didn't) have a full guix-style records API,
> > which might cause discrepancies in otherwise normal-looking Scheme
> > code.
> 
> I’m not convinced.  :-)
For more information, I'm a little worried that someone would attempt
  (endpoint (inherit some-other-endpoint) (field ...))
though perhaps that's a little overengineered problem and shepherd
itself is moving towards a more declarative API as we speak.

> > 2. It'd probably make shepherd a compile-time dependency, which is
> > avoided in other places in the code, i.e. (gnu build shepherd)
> 
> (gnu build shepherd) is 75% deprecated; the introduction of endpoints
> in the Shepherd didn’t have any effect on it.
Good to know.

> > 3. Shepherd records are (to my knowledge) not print-readable, so
> > we'd have to move them through G-Expressions through some of our
> > own code anyway; how strongly that would replicate the API is up to
> > debate/speculation.
> 
> When would you want to print those <endpoint> records?
When writing them to a shepherd init.scm :)

Cheers
Ludovic Courtès May 10, 2023, 3:14 p.m. UTC | #8
Hi,

Liliana Marie Prikler <liliana.prikler@gmail.com> skribis:

> Am Mittwoch, dem 03.05.2023 um 15:27 +0200 schrieb Ludovic Courtès:
>> It’s not just possible: several services in (gnu services …) and (gnu
>> home services …) use endpoints for systemd or inetd-style startup.
> True, but to my knowledge they don't yet allow the user to specify
> those endpoints directly.  At the very least, they didn't when I
> started this thread, which was shortly after shepherd itself gained
> endpoints.  I'm happy to be proven wrong on this point.

They don’t let users specify the endpoints as such, but closely enough.
For instance, the ‘interface’ field of <bitlbee-configuration> is used
to build it endpoint, and similarly for ‘openssh’ and ‘dicod’.

Overall, it seems to me we don’t need a first-class <endpoint> type in
Guix System itself.

I hope that makes sense!

Ludo’.
Liliana Marie Prikler May 10, 2023, 6:32 p.m. UTC | #9
Am Mittwoch, dem 10.05.2023 um 17:14 +0200 schrieb Ludovic Courtès:
> Hi,
> 
> Liliana Marie Prikler <liliana.prikler@gmail.com> skribis:
> 
> > Am Mittwoch, dem 03.05.2023 um 15:27 +0200 schrieb Ludovic Courtès:
> > > It’s not just possible: several services in (gnu services …) and
> > > (gnu home services …) use endpoints for systemd or inetd-style
> > > startup.
> > True, but to my knowledge they don't yet allow the user to specify
> > those endpoints directly.  At the very least, they didn't when I
> > started this thread, which was shortly after shepherd itself gained
> > endpoints.  I'm happy to be proven wrong on this point.
> 
> They don’t let users specify the endpoints as such, but closely
> enough.  For instance, the ‘interface’ field of <bitlbee-
> configuration> is used to build it endpoint, and similarly for
> ‘openssh’ and ‘dicod’.
> 
> Overall, it seems to me we don’t need a first-class <endpoint> type
> in Guix System itself.
It's funny you would argue that, because imho openssh would actually be
a good candidate for supporting first-class endpoints:  Doing so would
allow the user to specify whether IPv4, IPv6 or both (default) should
be allowed for connections.  For other service that support sockets as
well as TCP/IP ports, the benefit would be even greater.

I understand that copypasting all the fields into Guix records is a bit
of a non-starter, but I don't think it's a good idea to simply give up.
I just need some pointers in which direction to continue.

Cheers
Ludovic Courtès May 11, 2023, 10:52 a.m. UTC | #10
Hi,

Liliana Marie Prikler <liliana.prikler@gmail.com> skribis:

> Am Mittwoch, dem 10.05.2023 um 17:14 +0200 schrieb Ludovic Courtès:
>> Hi,
>> 
>> Liliana Marie Prikler <liliana.prikler@gmail.com> skribis:
>> 
>> > Am Mittwoch, dem 03.05.2023 um 15:27 +0200 schrieb Ludovic Courtès:
>> > > It’s not just possible: several services in (gnu services …) and
>> > > (gnu home services …) use endpoints for systemd or inetd-style
>> > > startup.
>> > True, but to my knowledge they don't yet allow the user to specify
>> > those endpoints directly.  At the very least, they didn't when I
>> > started this thread, which was shortly after shepherd itself gained
>> > endpoints.  I'm happy to be proven wrong on this point.
>> 
>> They don’t let users specify the endpoints as such, but closely
>> enough.  For instance, the ‘interface’ field of <bitlbee-
>> configuration> is used to build it endpoint, and similarly for
>> ‘openssh’ and ‘dicod’.
>> 
>> Overall, it seems to me we don’t need a first-class <endpoint> type
>> in Guix System itself.
> It's funny you would argue that, because imho openssh would actually be
> a good candidate for supporting first-class endpoints:  Doing so would
> allow the user to specify whether IPv4, IPv6 or both (default) should
> be allowed for connections.  For other service that support sockets as
> well as TCP/IP ports, the benefit would be even greater.

For the services I mentioned, I don’t feel that lack of first-class
endpoints is a hindrance in terms of flexibility.  We’re trading
expressivity for ease of use.

> I understand that copypasting all the fields into Guix records is a bit
> of a non-starter, but I don't think it's a good idea to simply give up.
> I just need some pointers in which direction to continue.

Like I wrote, I’m kinda skeptical about the idea.  :-)

Now, if you find good motivating examples and find a way to express
endpoints that remain concise in common cases, that may be more
appealing to me!

Ludo’.
diff mbox series

Patch

diff --git a/doc/guix.texi b/doc/guix.texi
index c21235f28d..30cc3b6d45 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -39332,6 +39332,38 @@  This, as you can see, is a fairly sophisticated way to say hello.
 info on actions.
 @end deftp
 
+@deftp {Data Type} shepherd-endpoint
+This data type represents an endpoint for inetd-style and systemd-style
+services.  Its fields reflect the arguments to shepherd's
+@code{endpoint} procedure.
+
+@table @code
+@item address
+The socket address to bind via shepherd and forward to the service.
+
+@item name
+A ``descriptive'' name to forward to the service along with the socket.
+
+@item style
+The socket style to use.
+@xref{Network Sockets and Communication,,, guile, GNU Guile Reference
+Manual}.
+
+@item backlog
+The maximum length of the queue for pending connections.
+
+@item socket-owner
+The user ID owning the socket.
+
+@item socket-group
+The group ID owning the socket.
+
+@item socket-directory-permissions
+The UNIX permissions to set for the directory the socket resides in.
+
+@end table
+@end deftp
+
 @defvr {Scheme Variable} shepherd-root-service-type
 The service type for the Shepherd ``root service''---i.e., PID@tie{}1.
 
diff --git a/gnu/services/shepherd.scm b/gnu/services/shepherd.scm
index 4fd4b2a497..f0d204ff3a 100644
--- a/gnu/services/shepherd.scm
+++ b/gnu/services/shepherd.scm
@@ -66,6 +66,17 @@  (define-module (gnu services shepherd)
             shepherd-action-documentation
             shepherd-action-procedure
 
+            shepherd-endpoint
+            shepherd-endpoint?
+            shepherd-endpoint-address
+            shepherd-endpoint-name
+            shepherd-endpoint-style
+            shepherd-endpoint-backlog
+            shepherd-endpoint-socket-owner
+            shepherd-endpoint-socket-group
+            shepherd-endpoint-socket-directory-permissions
+            shepherd-endpoint->sexp
+
             %default-modules
 
             shepherd-service-file
@@ -183,6 +194,59 @@  (define %default-modules
     ((guix build utils) #:hide (delete))
     (guix build syscalls)))
 
+(define-record-type* <shepherd-endpoint>
+  shepherd-endpoint make-shepherd-endpoint
+  shepherd-endpoint?
+  (address                      shepherd-endpoint-address)      ; sockaddr
+  (name                         shepherd-endpoint-name          ; string | #f
+                                (default #f))
+  (style                        shepherd-endpoint-style         ; int | #f
+                                (default #f))
+  (backlog                      shepherd-endpoint-backlog       ; int | #f
+                                (default #f))
+  (socket-owner                 shepherd-endpoint-socket-owner  ; uid | #f
+                                (default #f))
+  (socket-group                 shepherd-endpoint-socket-group  ; gid | #f
+                                (default #f))
+  (socket-directory-permissions
+   shepherd-endpoint-socket-directory-permissions ; chmod pattern (int) | #f
+   (default #f)))
+
+(define (shepherd-endpoint->sexp endpoint)
+  (match endpoint
+    (($ <shepherd-endpoint> address
+                            name style backlog socket-owner socket-group
+                            socket-directory-permissions)
+     `(endpoint
+       ,(match (sockaddr:fam address)
+          ((? (cute = <> AF_INET) _)
+           `((@ (guile) make-socket-addr)
+             AF_INET
+             ,(sockaddr:addr address)
+             ,(sockaddr:port address)))
+          ((? (cute = <> AF_INET6) _)
+           `((@ (guile) make-socket-addr)
+             AF_INET6
+             ,(sockaddr:addr address)
+             ,(sockaddr:port address)
+             ,(sockaddr:flowinfo address)
+             ,(sockaddr:scopeid address)))
+          ((? (cute = <> AF_UNIX) _)
+           `((@ (guile) make-socket-addr)
+             AF_UNIX
+             ,(sockaddr:path address))))
+       ,@(fold
+          (match-lambda*
+            (((key value) seed)
+             (if value (cons* key value seed) seed)))
+          '()
+          `((#:name ,name)
+            (#:style ,style)
+            (#:backlog ,backlog)
+            (#:socket-owner ,socket-owner)
+            (#:socket-group ,socket-group)
+            (#:socket-directory-permissions ,socket-directory-permissions)))))))
+
 (define-record-type* <shepherd-service>
   shepherd-service make-shepherd-service
   shepherd-service?