guix-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

bug#26099: [PATCH] services: Add inetd-service-type.


From: Thomas Danckaert
Subject: bug#26099: [PATCH] services: Add inetd-service-type.
Date: Mon, 20 Mar 2017 22:11:55 +0100 (CET)

Hi!

Thanks for the review. I've attached an improved version of the patch. Overview of what was changed:

 - some coding style changes as requested.

 - added an <inetd-configuration> type, which consists of a list and
   an optional "program" field, a file-like expression, default to
   (file-append inetutils "libexec/inetd").  I did it like this
   instead of adding a package field, because some packages put the
   inetd executable in a ".../bin" directory, others use ”.../sbin",
   en inetutils uses ".../libexec".

 - <inetd-entry> arguments field is now a list of strings/file-like
   objects.  It's much more useful like this!

- added a test (new file tests/networking.scm, I stole a lot from the
   nginx-test code, without fully understanding the details, but it
   works!). The test uses the built-in echo, and a fantasy version of
   a dict service, implemented as a bash script :-)

 - added documentation.

Some questions:

 - AFAIU, there is currently no easy way to extend /etc/services with
   custom services in the os configuration?  Should we provide
   something like "sudoers-file" for services?

 - Does it make sense to provide more defaults in the inetd-entry?
   e.g. make user default to "root", make "nowait" the default, set a
   default socket type and protocol?  I've always used inetd for a
   very specific purpose, and have no idea what is the typical way
   people use it.

- Should we provide configuration settings for all inetd command line
   arguments?  inetutils' inetd provides --environment, --pidfile,
   --resolve and --rate options.  I think other inetd's have still
   other arguments, so if we want the service to work with many
   different inetd implementations, providing the option to specifiy
   an arbitrary command line in the forkexec-constructor might be
   good?

 - Other inetd packages might (optionally) have slightly different
   configuration syntax.  e.g. openbsd-inetd (not packaged for Guix
atm) provides some extra configuration options in the "wait" field.
   Perhaps add an “escape hatch” after all?

cheers!

Thomas
From 7df61f7d442cb5f97d84ece5cc427a13c678a3c3 Mon Sep 17 00:00:00 2001
From: Thomas Danckaert <address@hidden>
Date: Tue, 14 Mar 2017 18:12:34 +0100
Subject: [PATCH] services: Add inetd-service-type.

* gnu/services/networking.scm (<inetd-configuration>, <inetd-entry>): New
record types.
(inetd-config-file, inetd-shepherd-service): New procedures.
(inetd-service-type): New variable.
* doc/guix.texi (Networking Services): Document it.
* gnu/tests/networking.scm: New file.
* gnu/local.mk: Add it.
---
 doc/guix.texi               |  93 +++++++++++++++++++++++++++-
 gnu/local.mk                |   1 +
 gnu/services/networking.scm |  89 +++++++++++++++++++++++++++
 gnu/tests/networking.scm    | 147 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 329 insertions(+), 1 deletion(-)
 create mode 100644 gnu/tests/networking.scm

diff --git a/doc/guix.texi b/doc/guix.texi
index f4cc207e7..40eb30748 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -33,7 +33,8 @@ Copyright @copyright{} 2016 Alex ter address@hidden
 Copyright @copyright{} 2017 Clément address@hidden
 Copyright @copyright{} 2017 Mathieu address@hidden
 Copyright @copyright{} 2017 Federico address@hidden
-Copyright @copyright{} 2017 Carlo Zancanaro
+Copyright @copyright{} 2017 Carlo address@hidden
+Copyright @copyright{} 2017 Thomas Danckaert
 
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -9255,6 +9256,96 @@ make an initial adjustment of more than 1,000 seconds.
 List of host names used as the default NTP servers.
 @end defvr
 
address@hidden inetd
address@hidden {Scheme variable} inetd-service-type
+This service runs the inetd daemon.  inetd listens for connections on
+internet sockets, and can run various server programs to service
+requests on those sockets.
+
+The value of this service is an @code{inetd-configuration} object.  The
+following example configures the inetd daemon to provide the built-in
address@hidden service, as well as an smtp service forwards smtp traffic
+over ssh to a server @code{smtp-server} behind a gateway
address@hidden:
+
address@hidden
+(service
+ inetd-service-type
+ (inetd-configuration
+  (entries (list
+            (inetd-entry
+             (name "echo")
+             (socket-type 'stream)
+             (protocol "tcp")
+             (wait? #f)
+             (user "root"))
+            (inetd-entry
+             (node "127.0.0.1")
+             (name "smtp")
+             (socket-type 'stream)
+             (protocol "tcp")
+             (wait? #f)
+             (user "root")
+             (program (file-append openssh "/bin/ssh"))
+             (arguments
+              '("ssh" "-qT" "-i" "/path/to/ssh_key"
+                "-W" "smtp-server:25" "user@@hostname")))))
address@hidden example
+
+See below for more details about @code{inetd-configuration}.
address@hidden deffn
+
address@hidden {Data Type} inetd-configuration
+Data type representing the configuration of inetd.
+
address@hidden @asis
address@hidden @code{program} (default: @code{(file-append inetutils 
"/libexec/inetd")})
+The inetd executable to use.
+
address@hidden @code{entries} (default: @code{'()})
+A list of inetd service entries.  Each entry should be created by the
address@hidden constructor.
address@hidden table
address@hidden deftp
+
address@hidden {Data Type} inetd-entry
+Data type representing an entry in the inetd configuration.  Each entry
+corresponds to a socket where inetd will listen for requests.
+
address@hidden @asis
address@hidden @code{node} (default: @code{#f})
+Optional string, a comma-separated list of local addresses inetd should
+use when listening for this service.  @xref{Configuration file,,,
+inetutils, GNU Inetutils} for a complete description of all options.
address@hidden @code{name}
+A string, the name must correspond to an entry in @code{/etc/services}.
address@hidden @code{socket-type}
+One of @code{'stream}, @code{'dgram}, @code{'raw}, @code{'rdm} or
address@hidden'seqpacket}.
address@hidden @code{protocol}
+A string, must correspond to an entry in @code{/etc/protocols}.
address@hidden @code{wait?} (default: @code{#t})
+Whether inetd should wait for the server to exit before listening to new
+service requests.
address@hidden @code{user}
+A string containing the user (and, optionally, group) name of the user
+as whom the server should run.  The group name can be specified in a
+suffix, separated by a colon or period, i.e. @code{"user"},
address@hidden"user:group"} or @code{"user.group"}.
address@hidden @code{program} (default: @code{"internal"})
+The server program which will serve the requests, or @code{"internal"}
+if inetd should use a built-in service.
address@hidden @code{arguments} (default: @code{'()})
+A list strings or file-like objects, which are the server program's
+arguments, starting with the zeroth argument, i.e. the name of the
+program itself.  For inetd's internal services, this entry must be
address@hidden'()} or @code{'("internal")}.
address@hidden table
+
address@hidden file,,, inetutils, GNU Inetutils} for a more
+detailed discussion of each configuration field.
address@hidden deftp
+
 @cindex Tor
 @deffn {Scheme Procedure} tor-service address@hidden [#:tor @var{tor}]
 Return a service to run the @uref{https://torproject.org, Tor} anonymous
diff --git a/gnu/local.mk b/gnu/local.mk
index c1b076a5f..1fb1cb9d9 100644
--- a/gnu/local.mk
+++ b/gnu/local.mk
@@ -461,6 +461,7 @@ GNU_SYSTEM_MODULES =                                \
   %D%/tests/install.scm                                \
   %D%/tests/mail.scm                           \
   %D%/tests/messaging.scm                      \
+  %D%/tests/networking.scm                     \
   %D%/tests/ssh.scm                            \
   %D%/tests/web.scm
 
diff --git a/gnu/services/networking.scm b/gnu/services/networking.scm
index 18bce2a2b..553269359 100644
--- a/gnu/services/networking.scm
+++ b/gnu/services/networking.scm
@@ -4,6 +4,7 @@
 ;;; Copyright © 2016 Efraim Flashner <address@hidden>
 ;;; Copyright © 2016 John Darrington <address@hidden>
 ;;; Copyright © 2017 Clément Lassieur <address@hidden>
+;;; Copyright © 2017 Thomas Danckaert <address@hidden>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -61,6 +62,10 @@
             ntp-service
             ntp-service-type
 
+            inetd-configuration
+            inetd-entry
+            inetd-service-type
+
             tor-configuration
             tor-configuration?
             tor-hidden-service
@@ -429,6 +434,90 @@ make an initial adjustment of more than 1,000 seconds."
 
 
 ;;;
+;;; Inetd.
+;;;
+
+(define-record-type* <inetd-configuration> inetd-configuration
+  make-inetd-configuration
+  inetd-configuration?
+  (program           inetd-configuration-program   ;file-like
+                     (default (file-append inetutils "/libexec/inetd")))
+  (entries           inetd-configuration-entries   ;list of <inetd-entry>
+                     (default '())))
+
+(define-record-type* <inetd-entry> inetd-entry make-inetd-entry
+  inetd-entry?
+  (node              inetd-entry-node         ;string or #f
+                     (default #f))
+  (name              inetd-entry-name)        ;string, from /etc/services
+
+  (socket-type       inetd-entry-socket-type) ;stream | dgram | raw |
+                                              ;rdm | seqpacket
+  (protocol          inetd-entry-protocol)    ;string, from /etc/protocols
+
+  (wait?             inetd-entry-wait?        ;Boolean
+                     (default #t))
+  (user              inetd-entry-user)        ;string
+
+  (program           inetd-entry-program      ;string or file-like object
+                     (default "internal"))
+  (arguments         inetd-entry-arguments    ;list of strings or file-like 
objects
+                     (default '())))
+
+(define (inetd-config-file entries)
+  (apply mixed-text-file "inetd.conf"
+         (map
+          (lambda (entry)
+            (let* ((node (inetd-entry-node entry))
+                   (name (inetd-entry-name entry))
+                   (socket
+                    (if node (string-append node ":" name) name))
+                   (type
+                    (match (inetd-entry-socket-type entry)
+                      ((or 'stream 'dgram 'raw 'rdm 'seqpacket)
+                       (symbol->string (inetd-entry-socket-type entry)))))
+                   (protocol (inetd-entry-protocol entry))
+                   (wait (if (inetd-entry-wait? entry) "wait" "nowait"))
+                   (user (inetd-entry-user entry))
+                   (program (inetd-entry-program entry))
+                   (args (inetd-entry-arguments entry)))
+              #~(string-append
+                 (string-join
+                  (list #$@(list socket type protocol wait user program) 
address@hidden)
+                  " ") "\n")))
+          entries)))
+
+(define inetd-shepherd-service
+  (match-lambda
+    (($ <inetd-configuration> program ()) '()) ; empty list of entries -> do 
nothing
+    (($ <inetd-configuration> program entries)
+     (list
+      (shepherd-service
+       (documentation "Run inetd.")
+       (provision '(inetd))
+       (requirement '(user-processes networking syslogd))
+       (start #~(make-forkexec-constructor
+                 (list #$program #$(inetd-config-file entries))
+                 #:pid-file "/var/run/inetd.pid"))
+       (stop #~(make-kill-destructor)))))))
+
+(define-public inetd-service-type
+  (service-type
+   (name 'inetd)
+   (extensions
+    (list (service-extension shepherd-root-service-type
+                             inetd-shepherd-service)))
+
+   ;; The service can be extended with additional lists of entries.
+   (compose concatenate)
+   (extend (lambda (config entries)
+             (inetd-configuration
+              (inherit config)
+              (entries (append (inetd-configuration-entries config)
+                               entries)))))))
+
+
+;;;
 ;;; Tor.
 ;;;
 
diff --git a/gnu/tests/networking.scm b/gnu/tests/networking.scm
new file mode 100644
index 000000000..531231409
--- /dev/null
+++ b/gnu/tests/networking.scm
@@ -0,0 +1,147 @@
+;;; GNU Guix --- Functional package management for GNU
+;;; Copyright © 2017 Thomas Danckaert <address@hidden>
+;;;
+;;; This file is part of GNU Guix.
+;;;
+;;; GNU Guix is free software; you can redistribute it and/or modify it
+;;; under the terms of the GNU General Public License as published by
+;;; the Free Software Foundation; either version 3 of the License, or (at
+;;; your option) any later version.
+;;;
+;;; GNU Guix is distributed in the hope that it will be useful, but
+;;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;;; GNU General Public License for more details.
+;;;
+;;; You should have received a copy of the GNU General Public License
+;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
+
+(define-module (gnu tests networking)
+  #:use-module (gnu tests)
+  #:use-module (gnu system)
+  #:use-module (gnu system grub)
+  #:use-module (gnu system file-systems)
+  #:use-module (gnu system shadow)
+  #:use-module (gnu system vm)
+  #:use-module (gnu services)
+  #:use-module (gnu services base)
+  #:use-module (gnu services networking)
+  #:use-module (guix gexp)
+  #:use-module (guix store)
+  #:use-module (guix monads)
+  #:use-module (gnu packages bash)
+  #:export (%test-inetd))
+
+(define %inetd-os
+  ;; Operating system with 2 inetd services.
+  (operating-system
+    (host-name "komputilo")
+    (timezone "Europe/Brussels")
+    (locale "en_US.utf8")
+
+    (bootloader (grub-configuration (device "/dev/sdX")))
+    (file-systems %base-file-systems)
+    (firmware '())
+    (users %base-user-accounts)
+    (services (cons* (dhcp-client-service)
+                     (service inetd-service-type
+                              (inetd-configuration
+                               (entries (list
+                                         (inetd-entry
+                                          (name "echo")
+                                          (socket-type 'stream)
+                                          (protocol "tcp")
+                                          (wait? #f)
+                                          (user "root"))
+                                         (inetd-entry
+                                          (name "dict")
+                                          (socket-type 'stream)
+                                          (protocol "tcp")
+                                          (wait? #f)
+                                          (user "root")
+                                          (program (file-append bash
+                                                                "/bin/bash"))
+                                          (arguments
+                                           (list "bash" (plain-file 
"my-dict.sh" "\
+while read line
+do
+    if [[ $line =~ ^DEFINE\\ (.*)$ ]]
+    then
+        case ${BASH_REMATCH[1]} in
+            Guix)
+                echo GNU Guix is a package management tool for the GNU system.
+                ;;
+            G-expression)
+                echo Like an S-expression but with a G.
+                ;;
+            *)
+                echo NO DEFINITION FOUND
+                ;;
+        esac
+    else
+        echo ERROR
+    fi
+done" ))))))))
+                     %base-services))))
+
+(define* (run-inetd-test)
+  "Run tests in %INETD-OS, where the inetd service provides an echo service on
+port 7, and a dict service on port 2628."
+  (mlet* %store-monad ((os -> (marionette-operating-system %inetd-os))
+                       (command (system-qemu-image/shared-store-script
+                                 os #:graphic? #f)))
+    (define test
+      (with-imported-modules '((gnu build marionette))
+        #~(begin
+            (use-modules (ice-9 rdelim)
+                         (srfi srfi-64)
+                         (gnu build marionette))
+            (define marionette
+              ;; Forward guest ports 7 and 2628 to host ports 8007 and 8628.
+              (make-marionette (list #$command "-net"
+                                     (string-append
+                                      "user"
+                                      ",hostfwd=tcp::8007-:7"
+                                      ",hostfwd=tcp::8628-:2628"))))
+
+            (mkdir #$output)
+            (chdir #$output)
+
+            (test-begin "inetd")
+
+            ;; Make sure the PID file is created.
+            (test-assert "PID file"
+              (marionette-eval
+               '(file-exists? "/var/run/inetd.pid")
+              marionette))
+
+            ;; Test the echo service.
+            (test-equal "echo response"
+              "Hello, Guix!"
+              (let ((echo (socket PF_INET SOCK_STREAM 0))
+                    (addr (make-socket-address AF_INET INADDR_LOOPBACK 8007)))
+                (begin
+                  (connect echo addr)
+                  (display "Hello, Guix!\n" echo)
+                  (read-line echo)))) ; TODO ensure socket is closed?
+
+            ;; Test the dict service
+            (test-equal "dict response"
+              "GNU Guix is a package management tool for the GNU system."
+              (let ((dict (socket PF_INET SOCK_STREAM 0))
+                    (addr (make-socket-address AF_INET INADDR_LOOPBACK 8628)))
+                (begin
+                  (connect dict addr)
+                  (display "DEFINE Guix\n" dict)
+                  (read-line dict))))
+
+            (test-end)
+            (exit (= (test-runner-fail-count (test-runner-current)) 0)))))
+
+    (gexp->derivation "inetd-test" test)))
+
+(define %test-inetd
+  (system-test
+   (name "inetd")
+   (description "Connect to a host with an INETD server.")
+   (value (run-inetd-test))))
-- 
2.11.1


reply via email to

[Prev in Thread] Current Thread [Next in Thread]