summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLudovic Courtès <ludo@gnu.org>2015-09-17 23:44:26 +0200
committerLudovic Courtès <ludo@gnu.org>2015-10-10 22:55:15 +0200
commit0adfe95a3eee335847c3127edde3de550e692440 (patch)
tree1c5a059d8f261f09254c0e420e61e1344c9edb45
parente79467f63a06811ba5dd8c8b0cc79553c5dd4e3a (diff)
services: Introduce extensible services.
This patch rewrites GuixSD services to make them extensible. * gnu-system.am (GNU_SYSTEM_MODULES): Add gnu/services/dbus.scm. * gnu/services.scm (<service>): Replace with new record type. (<service-extension>, <service-type>): New record types. (write-service-type, compute-boot-script, second-argument): New procedures. (%boot-service, boot-service-type): New variables. (file-union, directory-union, modprobe-wrapper, activation-service->script, activation-script, gexps->activation-gexp): New procedures. (activation-service-type, %activation-service): New variables. (etc-directory, files->etc-directory, etc-service): New procedures. (etc-service-type, setuid-program-service, firmware-service-type): New variables. (firmware->activation-gexp): New procedure. (&service-error, &missing-target-service-error, &ambiguous-target-service-error): New condition types. (service-back-edges, fold-services): New procedures. * gnu/services/avahi.scm (<avahi-configuration>): New record type. (configuration-file): Replace keyword parameters with a single 'config' parameter. (%avahi-accounts, %avahi-activation, avahi-service-type): New variables. (avahi-dmd-service): New procedure. (avahi-service): Rewrite using 'service' and 'avahi-configuration'. * gnu/services/base.scm (%root-file-system-dmd-service, root-file-system-service-type): New variables. (root-file-system-service): Use them. (file-system->dmd-service-name): New procedure. (file-system-service-type): New variable. (file-system-service): Use it. Replace keyword parameters with a single 'file-system' object. (user-unmount-service-type): New variable. (user-unmount-service): Use it. (user-processes-service-type): New variable. (user-processes-service): Use it. (host-name-service-type): New variable. (host-name-service): Use it. (console-keymap-service-type): New variable. (console-keymap-service): Use it. (console-font-service-type): New variable. (console-font-service): Use it. (mingetty-pam-service, mingetty-dmd-service): New procedures. (mingetty-service-type): New variable. (mingetty-service): Use it. (nscd-dmd-service): New procedure. (nscd-activation, nscd-service-type): New variables. (nscd-service): Use the latter. (syslog-service-type): New variable. (syslog-service): Use it. (<guix-configuration>): New record type. (%default-guix-configuration): New variable. (guix-dmd-service, guix-accounts, guix-activation): New procedures. (guix-service-type): New variable. (guix-service): Replace list of keyword parameters with a single 'config' parameter. Rewrite using 'service'. (<udev-configuration>): New record type. (udev-dmd-service): New procedure. (udev-service-type): New variable. (udev-service): Use it. (device-mapping-service-type): New variable. (device-mapping-service): Use it. (swap-service-type): New variable. (swap-service): Use it. * gnu/services/databases.scm (<postgresql-configuration>): New record type. (%postgresql-accounts, postgresql-activation): New variables. (postgresql-dmd-service): New procedure. (postgresql-service): Rewrite using 'service' and 'postgresql-configuration'. * gnu/services/dbus.scm: New file. * gnu/services/desktop.scm (dbus-configuration-directory, dbus-service): Remove. (wrapped-dbus-service): New procedure. (<upower-configuration>): New record type. (upower-configuration-file): Replace keyword parameters with single <upower-configuration> parameter. (%upower-accounts, %upower-activation): New variables. (upower-dbus-service, upower-dmd-service): New procedures. (upower-service-type): New variable. (upower-service): Rewrite using 'service' and 'upower-configuration'. (%colord-activation, %colord-accounts): New variables. (colord-dmd-service): New procedure. (colord-service-type): New variable. (colord-service): Rewrite using 'service'. (<geoclue-configuration>): New record type. (geoclue-configuration-file): Replace keyword parameters with a single 'config' parameter. (geoclue-dbus-service, geoclue-dmd-service): New procedures. (%geoclue-accounts, geoclue-service-type): New variables. (geoclue-service): Rewrite using 'service' and 'geoclue-configuration'. (%polkit-accounts, %polkit-pam-services, polkit-service-type): New variables. (polkit-dmd-service): New procedure. (polkit-service): Rewrite using 'service'. (<elogind-configuration>)[elogind]: New field. (elogind-dmd-service): New procedure. (elogind-service-type): New variable. (elogind-service): Rewrite using 'service'. (%desktop-services): Remove argument to 'dbus-service'. Remove 'map' over %BASE-SERVICES. * gnu/services/dmd.scm (dmd-boot-gexp): New procedure. (dmd-root-service-type, %dmd-root-service): New variables. (dmd-service-type): New macro. (<dmd-service>): New record type. * gnu/services/lirc.scm (<lirc-configuration>): New record type. (%lirc-activation): New variable. (lirc-dmd-service): New procedure. (lirc-service-type): New variable. (lirc-service): Rewrite using 'service' and 'lirc-configuration'. * gnu/services/networking.scm (<static-networking>): New record type. (static-networking-service-type): New variable. (static-networking-service): Rewrite using 'service' and 'static-networking'. (dhcp-client-service-type): New variable. (dhcp-client-service): Rewrite using 'service'. (<ntp-configuration>): New record type. (ntp-dmd-service): New procedure. (ntp-service-type): New variable. (ntp-service): New procedure. (%tor-accounts, tor-service-type): New variable. (tor-dmd-service): New procedure. (tor-service): Rewrite using 'service'. (<bitlbee-configuration>): New record type. (bitlbee-dmd-service): New procedure. (%bitlbee-accounts, %bitlbee-activation, bitlbee-service-type): New variables. (bitlbee-service): Rewrite using 'service'. (%wicd-activation): New variable. (wicd-dmd-service): New procedure. (wicd-service-type): New variable. (wicd-service): Rewrite using 'service'. * gnu/services/ssh.scm (<lsh-configuration>): New record type. (activation): Rename to... (lsh-initialization): ... this. (lsh-activation, lsh-dmd-service, lsh-pam-services): New procedures. (lsh-service-type): New variable. (lsh-service): Rewrite using 'service' and 'lsh-configuration'. * gnu/services/web.scm (<nginx-configuration>): New record type. (%nginx-accounts): New variable. (nginx-activation, nginx-dmd-service): New procedures. (nginx-service-type): New variable. (nginx-service): Rewrite using 'service' and 'nginx-configuration'. * gnu/services/xorg.scm (<slim-configuration>): New record type. (slim-pam-service, slim-dmd-service): New procedures. (slim-service-type): New variable. (slim-service): Rewrite using 'service' and 'slim-configuration'. * gnu/system.scm (file-union): Remove. (other-file-system-services): Adjust to new 'file-system-service' signature. (essential-services): Add #:container? parameter. Add %DMD-ROOT-SERVICE, %ACTIVATION-SERVICE, and calls to 'pam-root-service', 'account-service', 'operating-system-etc-service', and a SETUID-PROGRAM-SERVICE instance. (operating-system-services): Pass #:container? to 'essential-services. (etc-directory): Remove. (operating-system-etc-service): New procedure. Rewrite as a call to 'etc-service'. (operating-system-accounts): Change to not return accounts required by services. (operating-system-etc-directory): Rewrite as a call to 'fold-services' and 'etc-directory'. (user-group->gexp, user-account->gexp, modprobe-wrapper): Remove. (operating-system-activation-script): Rewrite as a call to 'fold-services' and 'activation-service->script'. (operating-system-boot-script): Likewise. (operating-system-derivation): Add call to 'lower-object'. (emacs-site-file, emacs-site-directory, shells-file): Change to use 'computed-file' and 'scheme-file' instead of the monadic procedures. * gnu/system/install.scm (cow-store-service-type): New variable. (cow-store-service): Rewrite using 'service'. (/etc/configuration-files): New procedure. (configuration-template-service-type, %configuration-template-service): New variables. (configuration-template-service): Remove. (installation-services): Adjust accordingly. Adjust argument to 'guix-service'. * gnu/system/linux.scm (/etc-entry, pam-root-service): New procedures. (pam-root-service-type): New variable. * gnu/system/shadow.scm (user-group->gexp, user-account->gexp, account-activation, etc-skel, account-service): New procedures. (account-service-type): New variable. * tests/services.scm: New file. * doc/guix.texi (Base Services, Desktop Services): Adjust accordingly. (Defining Services): Rewrite. * doc/images/service-graph.dot: New file. * doc.am (DOT_FILES): Add it. * po/guix/POTFILES.in: Add gnu/services.scm.
-rw-r--r--.gitignore3
-rw-r--r--Makefile.am1
-rw-r--r--doc.am3
-rw-r--r--doc/guix.texi471
-rw-r--r--doc/images/service-graph.dot35
-rw-r--r--gnu-system.am1
-rw-r--r--gnu/services.scm455
-rw-r--r--gnu/services/avahi.scm122
-rw-r--r--gnu/services/base.scm1009
-rw-r--r--gnu/services/databases.scm144
-rw-r--r--gnu/services/dbus.scm178
-rw-r--r--gnu/services/desktop.scm645
-rw-r--r--gnu/services/dmd.scm78
-rw-r--r--gnu/services/lirc.scm80
-rw-r--r--gnu/services/networking.scm546
-rw-r--r--gnu/services/ssh.scm178
-rw-r--r--gnu/services/web.scm108
-rw-r--r--gnu/services/xorg.scm148
-rw-r--r--gnu/system.scm469
-rw-r--r--gnu/system/install.scm125
-rw-r--r--gnu/system/linux.scm30
-rw-r--r--gnu/system/shadow.scm96
-rw-r--r--po/guix/POTFILES.in1
-rw-r--r--tests/services.scm91
24 files changed, 3328 insertions, 1689 deletions
diff --git a/.gitignore b/.gitignore
index a363c074ca7..e3f2ac2c21d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -129,3 +129,6 @@ GTAGS
129/doc/images/coreutils-bag-graph.png 129/doc/images/coreutils-bag-graph.png
130/doc/images/coreutils-graph.png 130/doc/images/coreutils-graph.png
131/doc/images/coreutils-size-map.eps 131/doc/images/coreutils-size-map.eps
132/doc/images/service-graph.png
133/doc/images/service-graph.eps
134/doc/images/service-graph.pdf
diff --git a/Makefile.am b/Makefile.am
index a8dab5d3261..18fbd9d5786 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -219,6 +219,7 @@ SCM_TESTS = \
219 tests/size.scm \ 219 tests/size.scm \
220 tests/graph.scm \ 220 tests/graph.scm \
221 tests/file-systems.scm \ 221 tests/file-systems.scm \
222 tests/services.scm \
222 tests/containers.scm 223 tests/containers.scm
223 224
224if HAVE_GUILE_JSON 225if HAVE_GUILE_JSON
diff --git a/doc.am b/doc.am
index 9c1dc83e88b..71a65ba0c47 100644
--- a/doc.am
+++ b/doc.am
@@ -22,7 +22,8 @@ info_TEXINFOS = doc/guix.texi
22DOT_FILES = \ 22DOT_FILES = \
23 doc/images/bootstrap-graph.dot \ 23 doc/images/bootstrap-graph.dot \
24 doc/images/coreutils-graph.dot \ 24 doc/images/coreutils-graph.dot \
25 doc/images/coreutils-bag-graph.dot 25 doc/images/coreutils-bag-graph.dot \
26 doc/images/service-graph.dot
26 27
27DOT_VECTOR_GRAPHICS = \ 28DOT_VECTOR_GRAPHICS = \
28 $(DOT_FILES:%.dot=%.eps) \ 29 $(DOT_FILES:%.dot=%.eps) \
diff --git a/doc/guix.texi b/doc/guix.texi
index 1b25bd938fb..a3fa989d48b 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -182,6 +182,13 @@ Services
182* Web Services:: Web servers. 182* Web Services:: Web servers.
183* Various Services:: Other services. 183* Various Services:: Other services.
184 184
185Defining Services
186
187* Service Composition:: The model for composing services.
188* Service Types and Services:: Types and services.
189* Service Reference:: API reference.
190* dmd Services:: A particular type of service.
191
185Packaging Guidelines 192Packaging Guidelines
186 193
187* Software Freedom:: What may go into the distribution. 194* Software Freedom:: What may go into the distribution.
@@ -5899,23 +5906,41 @@ Return a service that runs @code{syslogd}. If configuration file name
5899settings. 5906settings.
5900@end deffn 5907@end deffn
5901 5908
5902@deffn {Scheme Procedure} guix-service [#:guix guix] @ 5909@anchor{guix-configuration-type}
5903 [#:builder-group "guixbuild"] [#:build-accounts 10] @ 5910@deftp {Data Type} guix-configuration
5904 [#:authorize-hydra-key? #t] [#:use-substitutes? #t] @ 5911This data type represents the configuration of the Guix build daemon.
5905 [#:extra-options '()] 5912@xref{Invoking guix-daemon}, for more information.
5906Return a service that runs the build daemon from @var{guix}, and has 5913
5907@var{build-accounts} user accounts available under @var{builder-group}. 5914@table @asis
5915@item @code{guix} (default: @var{guix})
5916The Guix package to use.
5908 5917
5909When @var{authorize-hydra-key?} is true, the @code{hydra.gnu.org} public key 5918@item @code{build-group} (default: @code{"guixbuild"})
5910provided by @var{guix} is authorized upon activation, meaning that substitutes 5919Name of the group for build user accounts.
5911from @code{hydra.gnu.org} are used by default.
5912 5920
5913If @var{use-substitutes?} is false, the daemon is run with 5921@item @code{build-accounts} (default: @code{10})
5914@option{--no-substitutes} (@pxref{Invoking guix-daemon, 5922Number of build user accounts to create.
5915@option{--no-substitutes}}).
5916 5923
5917Finally, @var{extra-options} is a list of additional command-line options 5924@item @code{authorize-key?} (default: @code{#t})
5918passed to @command{guix-daemon}. 5925Whether to authorize the substitute key for @code{hydra.gnu.org}
5926(@pxref{Substitutes}).
5927
5928@item @code{use-substitutes?} (default: @code{#t})
5929Whether to use substitutes.
5930
5931@item @code{extra-options} (default: @code{'()})
5932List of extra command-line options for @command{guix-daemon}.
5933
5934@item @code{lsof} (default: @var{lsof})
5935@itemx @code{lsh} (default: @var{lsh})
5936The lsof and lsh packages to use.
5937
5938@end table
5939@end deftp
5940
5941@deffn {Scheme Procedure} guix-service @var{config}
5942Return a service that runs the Guix build daemon according to
5943@var{config}.
5919@end deffn 5944@end deffn
5920 5945
5921@deffn {Scheme Procedure} udev-service [#:udev udev] 5946@deffn {Scheme Procedure} udev-service [#:udev udev]
@@ -6179,11 +6204,10 @@ The @var{%desktop-services} variable can be used as the @code{services}
6179field of an @code{operating-system} declaration (@pxref{operating-system 6204field of an @code{operating-system} declaration (@pxref{operating-system
6180Reference, @code{services}}). 6205Reference, @code{services}}).
6181 6206
6182The actual service definitions provided by @code{(gnu services desktop)} 6207The actual service definitions provided by @code{(gnu services dbus)}
6183are described below. 6208and @code{(gnu services desktop)} are described below.
6184 6209
6185@deffn {Scheme Procedure} dbus-service @var{services} @ 6210@deffn {Scheme Procedure} dbus-service [#:dbus @var{dbus}] [#:services '()]
6186 [#:dbus @var{dbus}]
6187Return a service that runs the ``system bus'', using @var{dbus}, with 6211Return a service that runs the ``system bus'', using @var{dbus}, with
6188support for @var{services}. 6212support for @var{services}.
6189 6213
@@ -6197,8 +6221,7 @@ and policy files. For example, to allow avahi-daemon to use the system bus,
6197@var{services} must be equal to @code{(list avahi)}. 6221@var{services} must be equal to @code{(list avahi)}.
6198@end deffn 6222@end deffn
6199 6223
6200@deffn {Scheme Procedure} elogind-service @ 6224@deffn {Scheme Procedure} elogind-service [#:config @var{config}]
6201 [#:elogind @var{elogind}] [#:config @var{config}]
6202Return a service that runs the @code{elogind} login and 6225Return a service that runs the @code{elogind} login and
6203seat management daemon. @uref{https://github.com/andywingo/elogind, 6226seat management daemon. @uref{https://github.com/andywingo/elogind,
6204Elogind} exposes a D-Bus interface that can be used to know which users 6227Elogind} exposes a D-Bus interface that can be used to know which users
@@ -6957,54 +6980,378 @@ build users.
6957@node Defining Services 6980@node Defining Services
6958@subsection Defining Services 6981@subsection Defining Services
6959 6982
6960The @code{(gnu services @dots{})} modules define several procedures that allow 6983The previous sections how the available services and how one can combine
6961users to declare the operating system's services (@pxref{Using the 6984them in an @code{operating-system} declaration. But how do we define
6962Configuration System}). These procedures are @emph{monadic 6985them in the first place? And what is a service anyway?
6963procedures}---i.e., procedures that return a monadic value in the store
6964monad (@pxref{The Store Monad}). For examples of such procedures,
6965@xref{Services}.
6966
6967@cindex service definition
6968The monadic value returned by those procedures is a @dfn{service
6969definition}---a structure as returned by the @code{service} form.
6970Service definitions specifies the inputs the service depends on, and an
6971expression to start and stop the service. Behind the scenes, service
6972definitions are ``translated'' into the form suitable for the
6973configuration file of dmd, the init system (@pxref{Services,,, dmd, GNU
6974dmd Manual}).
6975
6976As an example, here is what the @code{nscd-service} procedure looks
6977like:
6978 6986
6979@lisp 6987@menu
6980(define (nscd-service) 6988* Service Composition:: The model for composing services.
6981 (with-monad %store-monad 6989* Service Types and Services:: Types and services.
6982 (return (service 6990* Service Reference:: API reference.
6983 (documentation "Run libc's name service cache daemon.") 6991* dmd Services:: A particular type of service.
6984 (provision '(nscd)) 6992@end menu
6985 (activate #~(begin 6993
6986 (use-modules (guix build utils)) 6994@node Service Composition
6987 (mkdir-p "/var/run/nscd"))) 6995@subsubsection Service Composition
6988 (start #~(make-forkexec-constructor 6996
6989 (string-append #$glibc "/sbin/nscd") 6997@cindex services
6990 "-f" "/dev/null" "--foreground")) 6998@cindex daemons
6991 (stop #~(make-kill-destructor)) 6999Here we define a @dfn{service} as, broadly, something that extends the
6992 (respawn? #f))))) 7000operating system's functionality. Often a service is a process---a
6993@end lisp 7001@dfn{daemon}---started when the system boots: a secure shell server, a
7002Web server, the Guix build daemon, etc. Sometimes a service is a daemon
7003whose execution can be triggered by another daemon---e.g., an FTP server
7004started by @command{inetd} or a D-Bus service activated by
7005@command{dbus-daemon}. Occasionally, a service does not map to a
7006daemon. For instance, the ``account'' service collects user accounts
7007and makes sure they exist when the system runs; the ``udev'' service
7008collects device management rules and makes them available to the eudev
7009daemon; the @file{/etc} service populates the system's @file{/etc}
7010directory.
7011
7012GuixSD services are connected by @dfn{extensions}. For instance, the
7013secure shell service @emph{extends} dmd---GuixSD's initialization system,
7014running as PID@tie{}1---by giving it the command lines to start and stop
7015the secure shell daemon (@pxref{Networking Services,
7016@code{lsh-service}}); the UPower service extends the D-Bus service by
7017passing it its @file{.service} specification, and extends the udev
7018service by passing it device management rules (@pxref{Desktop Services,
7019@code{upower-service}}); the Guix daemon service extends dmd by passing
7020it the command lines to start and stop the daemon, and extends the
7021account service by passing it a list of required build user accounts
7022(@pxref{Base Services}).
7023
7024All in all, services and their ``extends'' relations form a directed
7025acyclic graph (DAG). If we represent services as boxes and extensions
7026as arrows, a typical system might provide something like this:
7027
7028@image{images/service-graph,,5in,Typical service extension graph.}
7029
7030At the bottom, we see the @dfn{boot service}, which produces the boot
7031script that is executed at boot time from the initial RAM disk.
7032
7033@cindex service types
7034Technically, developers can define @dfn{service types} to express these
7035relations. There can be any number of services of a given type on the
7036system---for instance, a system running two instances of the GNU secure
7037shell server (lsh) has two instances of @var{lsh-service-type}, with
7038different parameters.
7039
7040The following section describes the programming interface for service
7041types and services.
7042
7043@node Service Types and Services
7044@subsubsection Service Types and Services
7045
7046A @dfn{service type} is a node in the DAG described above. Let us start
7047with a simple example, the service type for the Guix build daemon
7048(@pxref{Invoking guix-daemon}):
7049
7050@example
7051(define guix-service-type
7052 (service-type
7053 (name 'guix)
7054 (extensions
7055 (list (service-extension dmd-root-service-type guix-dmd-service)
7056 (service-extension account-service-type guix-accounts)
7057 (service-extension activation-service-type guix-activation)))))
7058@end example
6994 7059
6995@noindent 7060@noindent
6996The @code{activate}, @code{start}, and @code{stop} fields are G-expressions 7061It defines a two things:
6997(@pxref{G-Expressions}). The @code{activate} field contains a script to 7062
6998run at ``activation'' time; it makes sure that the @file{/var/run/nscd} 7063@enumerate
6999directory exists before @command{nscd} is started. 7064@item
7065A name, whose sole purpose is to make inspection and debugging easier.
7066
7067@item
7068A list of @dfn{service extensions}, where each extension designates the
7069target service type and a procedure that, given the service's
7070parameters, returns a list of object to extend the service of that type.
7071
7072Every service type has at least one service extension. The only
7073exception is the @dfn{boot service type}, which is the ultimate service.
7074@end enumerate
7075
7076In this example, @var{guix-service-type} extends three services:
7077
7078@table @var
7079@item dmd-root-service-type
7080The @var{guix-dmd-service} procedure defines how the dmd service is
7081extended. Namely, it returns a @code{<dmd-service>} object that defines
7082how @command{guix-daemon} is started and stopped (@pxref{dmd Services}).
7083
7084@item account-service-type
7085This extension for this service is computed by @var{guix-accounts},
7086which returns a list of @code{user-group} and @code{user-account}
7087objects representing the build user accounts (@pxref{Invoking
7088guix-daemon}).
7089
7090@item activation-service-type
7091Here @var{guix-activation} is a procedure that returns a gexp, which is
7092a code snippet to run at ``activation time''---e.g., when the service is
7093booted.
7094@end table
7095
7096A service of this type is instantiated like this:
7097
7098@example
7099(service guix-service-type
7100 (guix-configuration
7101 (build-accounts 5)
7102 (use-substitutes? #f)))
7103@end example
7104
7105The second argument to the @code{service} form is a value representing
7106the parameters of this specific service instance.
7107@xref{guix-configuration-type, @code{guix-configuration}}, for
7108information about the @code{guix-configuration} data type.
7109
7110@var{guix-service-type} is quite simple because it extends other
7111services but is not extensible itself.
7112
7113@c @subsubsubsection Extensible Service Types
7114
7115The service type for an @emph{extensible} service looks like this:
7116
7117@example
7118(define udev-service-type
7119 (service-type (name 'udev)
7120 (extensions
7121 (list (service-extension dmd-root-service-type
7122 udev-dmd-service)))
7123
7124 (compose concatenate) ;concatenate the list of rules
7125 (extend (lambda (config rules)
7126 (match config
7127 (($ <udev-configuration> udev initial-rules)
7128 (udev-configuration
7129 (udev udev) ;the udev package to use
7130 (rules (append initial-rules rules)))))))))
7131@end example
7132
7133This is the service type for the
7134@uref{https://wiki.gentoo.org/wiki/Project:Eudev, eudev device
7135management daemon}. Compared to the previous example, in addition to an
7136extension of @var{dmd-root-service-type}, we see two new fields:
7137
7138@table @code
7139@item compose
7140This is the procedure to @dfn{compose} the list of extensions to
7141services of this type.
7142
7143Services can extend the udev service by passing it lists of rules; we
7144compose those extensions simply by concatenating them.
7145
7146@item extend
7147This procedure defines how the service's value is @dfn{extended} with
7148the composition of the extensions.
7149
7150Udev extensions are composed into a list of rules, but the udev service
7151value is itself a @code{<udev-configuration>} record. So here, we
7152extend that record by appending the list of rules is contains to the
7153list of contributed rules.
7154@end table
7155
7156There can be only one instance of an extensible service type such as
7157@var{udev-service-type}. If there were more, the
7158@code{service-extension} specifications would be ambiguous.
7159
7160Still here? The next section provides a reference of the programming
7161interface for services.
7162
7163@node Service Reference
7164@subsubsection Service Reference
7165
7166We have seen an overview of service types (@pxref{Service Types and
7167Services}). This section provides a reference on how to manipulate
7168services and service types. This interface is provided by the
7169@code{(gnu services)} module.
7170
7171@deffn {Scheme Procedure} service @var{type} @var{value}
7172Return a new service of @var{type}, a @code{<service-type>} object (see
7173below.) @var{value} can be any object; it represents the parameters of
7174this particular service instance.
7175@end deffn
7176
7177@deffn {Scheme Procedure} service? @var{obj}
7178Return true if @var{obj} is a service.
7179@end deffn
7180
7181@deffn {Scheme Procedure} service-kind @var{service}
7182Return the type of @var{service}---i.e., a @code{<service-type>} object.
7183@end deffn
7184
7185@deffn {Scheme Procedure} service-parameters @var{service}
7186Return the value associated with @var{service}. It represents its
7187parameters.
7188@end deffn
7189
7190Here is an example of how a service is created and manipulated:
7191
7192@example
7193(define s
7194 (service nginx-service-type
7195 (nginx-configuration
7196 (nginx nginx)
7197 (log-directory log-directory)
7198 (run-directory run-directory)
7199 (file config-file))))
7200
7201(service? s)
7202@result{} #t
7203
7204(eq? (service-kind s) nginx-service-type)
7205@result{} #t
7206@end example
7207
7208@deftp {Data Type} service-type
7209@cindex service type
7210This is the representation of a @dfn{service type} (@pxref{Service Types
7211and Services}).
7212
7213@table @asis
7214@item @code{name}
7215This is a symbol, used only to simplify inspection and debugging.
7216
7217@item @code{extensions}
7218A non-empty list of @code{<service-extension>} objects (see below.)
7219
7220@item @code{compose} (default: @code{#f})
7221If this is @code{#f}, then the service type denotes services that cannot
7222be extended---i.e., services that do not receive ``values'' from other
7223services.
7224
7225Otherwise, it must be a one-argument procedure. The procedure is called
7226by @code{fold-services} and is passed a list of values collected from
7227extensions. It must return a value that is a valid parameter value for
7228the service instance.
7229
7230@item @code{extend} (default: @code{#f})
7231If this is @code{#f}, services of this type cannot be extended.
7232
7233Otherwise, it must be a two-argument procedure: @code{fold-services}
7234calls it, passing it the service's initial value as the first argument
7235and the result of applying @code{compose} to the extension values as the
7236second argument.
7237@end table
7238
7239@xref{Service Types and Services}, for examples.
7240@end deftp
7241
7242@deffn {Scheme Procedure} service-extension @var{target-type} @
7243 @var{compute}
7244Return a new extension for services of type @var{target-type}.
7245@var{compute} must be a one-argument procedure: @code{fold-services}
7246calls it, passing it the value associated with the service that provides
7247the extension; it must return a valid value for the target service.
7248@end deffn
7249
7250@deffn {Scheme Procedure} service-extension? @var{obj}
7251Return true if @var{obj} is a service extension.
7252@end deffn
7253
7254At the core of the service abstraction lies the @code{fold-services}
7255procedure, which is responsible for ``compiling'' a list of services
7256down to a single boot script. In essence, it propagates service
7257extensions down the service graph, updating each node parameters on the
7258way, until it reaches the root node.
7259
7260@deffn {Scheme Procedure} fold-services @var{services} @
7261 [#:target-type @var{boot-service-type}]
7262Fold @var{services} by propagating their extensions down to the root of
7263type @var{target-type}; return the root service adjusted accordingly.
7264@end deffn
7265
7266Lastly, the @code{(gnu services)} module also defines several essential
7267service types, some of which are listed below.
7268
7269@defvr {Scheme Variable} boot-service-type
7270The type of the ``boot service'', which is the root of the service
7271graph.
7272@end defvr
7273
7274@defvr {Scheme Variable} etc-service-type
7275The type of the @file{/etc} service. This service can be extended by
7276passing it name/file tuples such as:
7277
7278@example
7279(list `("issue" ,(plain-file "issue" "Welcome!\n")))
7280@end example
7281
7282In this example, the effect would be to add an @file{/etc/issue} file
7283pointing to the given file.
7284@end defvr
7285
7286@defvr {Scheme Variable} setuid-program-service-type
7287Type for the ``setuid-program service''. This service collects lists of
7288executable file names, passed as gexps, and adds them to the set of
7289setuid-root programs on the system (@pxref{Setuid Programs}).
7290@end defvr
7291
7292
7293@node dmd Services
7294@subsubsection dmd Services
7295
7296@cindex PID 1
7297@cindex init system
7298The @code{(gnu services dmd)} provides a way to define services managed
7299by GNU@tie{}dmd, which is GuixSD initialization system---the first
7300process that is started when the system boots, aka. PID@tie{}1
7301(@pxref{Introduction,,, dmd, GNU dmd Manual}). The
7302@var{%dmd-root-service} represents PID@tie{}1, of type
7303@var{dmd-root-service-type}; it can be extended by passing it lists of
7304@code{<dmd-service>} objects.
7305
7306@deftp {Data Type} dmd-service
7307The data type representing a service managed by dmd.
7308
7309@table @asis
7310@item @code{provision}
7311This is a list of symbols denoting what the service provides.
7000 7312
7313These are the names that may be passed to @command{deco start},
7314@command{deco status}, and similar commands (@pxref{Invoking deco,,,
7315dmd, GNU dmd Manual}). @xref{Slots of services, the @code{provides}
7316slot,, dmd, GNU dmd Manual}, for details.
7317
7318@item @code{requirements} (default: @code{'()})
7319List of symbols denoting the dmd services this one depends on.
7320
7321@item @code{respawn?} (default: @code{#t})
7322Whether to restart the service when it stops, for instance when the
7323underlying process dies.
7324
7325@item @code{start}
7326@itemx @code{stop} (default: @code{#~(const #f)})
7001The @code{start} and @code{stop} fields refer to dmd's facilities to 7327The @code{start} and @code{stop} fields refer to dmd's facilities to
7002start and stop processes (@pxref{Service De- and Constructors,,, dmd, 7328start and stop processes (@pxref{Service De- and Constructors,,, dmd,
7003GNU dmd Manual}). The @code{provision} field specifies the name under 7329GNU dmd Manual}). They are given as G-expressions that get expanded in
7004which this service is known to dmd, and @code{documentation} specifies 7330the dmd configuration file (@pxref{G-Expressions}).
7005on-line documentation. Thus, the commands @command{deco start ncsd}, 7331
7006@command{deco stop nscd}, and @command{deco doc nscd} will do what you 7332@item @code{documentation}
7007would expect (@pxref{Invoking deco,,, dmd, GNU dmd Manual}). 7333A documentation string, as shown when running:
7334
7335@example
7336deco doc @var{service-name}
7337@end example
7338
7339where @var{service-name} is one of the symbols in @var{provision}
7340(@pxref{Invoking deco,,, dmd, GNU dmd Manual}).
7341@end table
7342@end deftp
7343
7344@defvr {Scheme Variable} dmd-root-service-type
7345The service type for the dmd ``root service''---i.e., PID@tie{}1.
7346
7347This is the service type that extensions target when they want to create
7348dmd services (@pxref{Service Types and Services}, for an example). Each
7349extension must pass a list of @code{<dmd-service>}.
7350@end defvr
7351
7352@defvr {Scheme Variable} %dmd-root-service
7353This service represents PID@tie{}1.
7354@end defvr
7008 7355
7009 7356
7010@node Installing Debugging Files 7357@node Installing Debugging Files
diff --git a/doc/images/service-graph.dot b/doc/images/service-graph.dot
new file mode 100644
index 00000000000..3397b878e96
--- /dev/null
+++ b/doc/images/service-graph.dot
@@ -0,0 +1,35 @@
1digraph "Service Type Dependencies" {
2 dmd [shape = box, fontname = Helvetica];
3 pam [shape = box, fontname = Helvetica];
4 etc [shape = box, fontname = Helvetica];
5 accounts [shape = box, fontname = Helvetica];
6 activation [shape = box, fontname = Helvetica];
7 boot [shape = house, fontname = Helvetica];
8 lshd -> dmd;
9 lshd -> pam;
10 udev -> dmd;
11 nscd -> dmd [label = "extends"];
12 "nss-mdns" -> nscd;
13 "kvm-rules" -> udev;
14 colord -> udev;
15 dbus -> dmd;
16 colord -> dbus;
17 upower -> udev;
18 upower -> dbus;
19 polkit -> dbus;
20 polkit -> pam;
21 elogind -> dbus;
22 elogind -> udev;
23 elogind -> polkit [label = "extends"];
24 dmd -> boot;
25 colord -> accounts;
26 accounts -> activation;
27 accounts -> etc;
28 etc -> activation;
29 activation -> boot;
30 pam -> etc;
31 elogind -> pam;
32 guix -> dmd;
33 guix -> activation;
34 guix -> accounts;
35}
diff --git a/gnu-system.am b/gnu-system.am
index c2dc323a617..64260dd3000 100644
--- a/gnu-system.am
+++ b/gnu-system.am
@@ -348,6 +348,7 @@ GNU_SYSTEM_MODULES = \
348 gnu/services/avahi.scm \ 348 gnu/services/avahi.scm \
349 gnu/services/base.scm \ 349 gnu/services/base.scm \
350 gnu/services/databases.scm \ 350 gnu/services/databases.scm \
351 gnu/services/dbus.scm \
351 gnu/services/desktop.scm \ 352 gnu/services/desktop.scm \
352 gnu/services/dmd.scm \ 353 gnu/services/dmd.scm \
353 gnu/services/lirc.scm \ 354 gnu/services/lirc.scm \
diff --git a/gnu/services.scm b/gnu/services.scm
index 43e51b998c9..fdfa569b239 100644
--- a/gnu/services.scm
+++ b/gnu/services.scm
@@ -1,5 +1,5 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2013, 2014 Ludovic Courtès <ludo@gnu.org> 2;;; Copyright © 2015 Ludovic Courtès <ludo@gnu.org>
3;;; 3;;;
4;;; This file is part of GNU Guix. 4;;; This file is part of GNU Guix.
5;;; 5;;;
@@ -18,49 +18,428 @@
18 18
19(define-module (gnu services) 19(define-module (gnu services)
20 #:use-module (guix gexp) 20 #:use-module (guix gexp)
21 #:use-module (guix monads)
22 #:use-module (guix store)
21 #:use-module (guix records) 23 #:use-module (guix records)
22 #:export (service? 24 #:use-module (guix sets)
25 #:use-module (guix ui)
26 #:use-module (gnu packages base)
27 #:use-module (gnu packages bash)
28 #:use-module (srfi srfi-1)
29 #:use-module (srfi srfi-9)
30 #:use-module (srfi srfi-9 gnu)
31 #:use-module (srfi srfi-26)
32 #:use-module (srfi srfi-34)
33 #:use-module (srfi srfi-35)
34 #:use-module (ice-9 vlist)
35 #:use-module (ice-9 match)
36 #:export (service-extension
37 service-extension?
38
39 service-type
40 service-type?
41
23 service 42 service
24 service-documentation 43 service?
25 service-provision 44 service-kind
26 service-requirement 45 service-parameters
27 service-respawn? 46
28 service-start 47 fold-services
29 service-stop 48
30 service-auto-start? 49 service-error?
31 service-activate 50 missing-target-service-error?
32 service-user-accounts 51 missing-target-service-error-service
33 service-user-groups 52 missing-target-service-error-target-type
34 service-pam-services)) 53 ambiguous-target-service-error?
35 54 ambiguous-target-service-error-service
36;;; Commentary: 55 ambiguous-target-service-error-target-type
56
57 boot-service-type
58 activation-service-type
59 activation-service->script
60 etc-service-type
61 etc-directory
62 setuid-program-service-type
63 firmware-service-type
64
65 %boot-service
66 %activation-service
67 etc-service
68
69 file-union)) ;XXX: for lack of a better place
70
71;;; Comment:
72;;;
73;;; This module defines a broad notion of "service types" and "services."
37;;; 74;;;
38;;; System services as cajoled by dmd. 75;;; A service type describe how its instances extend instances of other
76;;; service types. For instance, some services extend the instance of
77;;; ACCOUNT-SERVICE-TYPE by providing it with accounts and groups to create;
78;;; others extend DMD-ROOT-SERVICE-TYPE by passing it instances of
79;;; <dmd-service>.
80;;;
81;;; When applicable, the service type defines how it can itself be extended,
82;;; by providing one procedure to compose extensions, and one procedure to
83;;; extend itself.
84;;;
85;;; A notable service type is BOOT-SERVICE-TYPE, which has a single instance,
86;;; %BOOT-SERVICE. %BOOT-SERVICE constitutes the root of the service DAG. It
87;;; produces the boot script that the initrd loads.
88;;;
89;;; The 'fold-services' procedure can be passed a list of procedures, which it
90;;; "folds" by propagating extensions down the graph; it returns the root
91;;; service after the applying all its extensions.
39;;; 92;;;
40;;; Code: 93;;; Code:
41 94
42(define-record-type* <service> 95(define-record-type <service-extension>
43 service make-service 96 (service-extension target compute)
97 service-extension?
98 (target service-extension-target) ;<service-type>
99 (compute service-extension-compute)) ;params -> params
100
101(define-record-type* <service-type> service-type make-service-type
102 service-type?
103 (name service-type-name) ;symbol (for debugging)
104
105 ;; Things extended by services of this type.
106 (extensions service-type-extensions) ;list of <service-extensions>
107
108 ;; Given a list of extensions, "compose" them.
109 (compose service-type-compose ;list of Any -> Any
110 (default #f))
111
112 ;; Extend the services' own parameters with the extension composition.
113 (extend service-type-extend ;list of Any -> parameters
114 (default #f)))
115
116(define (write-service-type type port)
117 (format port "#<service-type ~a ~a>"
118 (service-type-name type)
119 (number->string (object-address type) 16)))
120
121(set-record-type-printer! <service-type> write-service-type)
122
123;; Services of a given type.
124(define-record-type <service>
125 (service type parameters)
44 service? 126 service?
45 (documentation service-documentation ; string 127 (type service-kind)
46 (default "[No documentation.]")) 128 (parameters service-parameters))
47 (provision service-provision) ; list of symbols 129
48 (requirement service-requirement ; list of symbols 130
49 (default '())) 131
50 (respawn? service-respawn? ; Boolean 132
51 (default #t)) 133;;;
52 (start service-start) ; g-expression (procedure) 134;;; Core services.
53 (stop service-stop ; g-expression (procedure) 135;;;
54 (default #~(const #f))) 136
55 (auto-start? service-auto-start? ; Boolean 137(define (compute-boot-script mexps)
56 (default #t)) 138 (mlet %store-monad ((gexps (sequence %store-monad mexps)))
57 (user-accounts service-user-accounts ; list of <user-account> 139 (gexp->file "boot"
58 (default '())) 140 #~(begin
59 (user-groups service-user-groups ; list of <user-groups> 141 (use-modules (guix build utils))
60 (default '())) 142
61 (pam-services service-pam-services ; list of <pam-service> 143 ;; Clean out /tmp and /var/run.
62 (default '())) 144 ;;
63 (activate service-activate ; gexp 145 ;; XXX This needs to happen before service activations, so
64 (default #f))) 146 ;; it has to be here, but this also implicitly assumes
147 ;; that /tmp and /var/run are on the root partition.
148 (false-if-exception (delete-file-recursively "/tmp"))
149 (false-if-exception (delete-file-recursively "/var/run"))
150 (false-if-exception (mkdir "/tmp"))
151 (false-if-exception (chmod "/tmp" #o1777))
152 (false-if-exception (mkdir "/var/run"))
153 (false-if-exception (chmod "/var/run" #o755))
154
155 ;; Activate the system and spawn dmd.
156 #$@gexps))))
157
158(define (second-argument a b) b)
159
160(define boot-service-type
161 ;; The service of this type is extended by being passed gexps as monadic
162 ;; values. It aggregates them in a single script, as a monadic value, which
163 ;; becomes its 'parameters'. It is the only service that extends nothing.
164 (service-type (name 'boot)
165 (extensions '())
166 (compose compute-boot-script)
167 (extend second-argument)))
168
169(define %boot-service
170 ;; This is the ultimate service, the root of the service DAG.
171 (service boot-service-type #t))
172
173(define* (file-union name files) ;FIXME: Factorize.
174 "Return a <computed-file> that builds a directory containing all of FILES.
175Each item in FILES must be a list where the first element is the file name to
176use in the new directory, and the second element is a gexp denoting the target
177file."
178 (computed-file name
179 #~(begin
180 (mkdir #$output)
181 (chdir #$output)
182 #$@(map (match-lambda
183 ((target source)
184 #~(symlink #$source #$target)))
185 files))))
186
187(define (directory-union name things)
188 "Return a directory that is the union of THINGS."
189 (match things
190 ((one)
191 ;; Only one thing; return it.
192 one)
193 (_
194 (computed-file name
195 #~(begin
196 (use-modules (guix build union))
197 (union-build #$output '#$things))
198 #:modules '((guix build union))))))
199
200(define (modprobe-wrapper)
201 "Return a wrapper for the 'modprobe' command that knows where modules live.
202
203This wrapper is typically invoked by the Linux kernel ('call_modprobe', in
204kernel/kmod.c), a situation where the 'LINUX_MODULE_DIRECTORY' environment
205variable is not set---hence the need for this wrapper."
206 (let ((modprobe "/run/current-system/profile/bin/modprobe"))
207 (gexp->script "modprobe"
208 #~(begin
209 (setenv "LINUX_MODULE_DIRECTORY"
210 "/run/booted-system/kernel/lib/modules")
211 (apply execl #$modprobe
212 (cons #$modprobe (cdr (command-line))))))))
213
214(define* (activation-service->script service)
215 "Return as a monadic value the activation script for SERVICE, a service of
216ACTIVATION-SCRIPT-TYPE."
217 (activation-script (service-parameters service)))
218
219(define (activation-script gexps)
220 "Return the system's activation script, which evaluates GEXPS."
221 (define %modules
222 '((gnu build activation)
223 (gnu build linux-boot)
224 (gnu build linux-modules)
225 (gnu build file-systems)
226 (guix build utils)
227 (guix build syscalls)
228 (guix elf)))
229
230 (define (service-activations)
231 ;; Return the activation scripts for SERVICES.
232 (mapm %store-monad
233 (cut gexp->file "activate-service" <>)
234 gexps))
235
236 (mlet* %store-monad ((actions (service-activations))
237 (modules (imported-modules %modules))
238 (compiled (compiled-modules %modules))
239 (modprobe (modprobe-wrapper)))
240 (gexp->file "activate"
241 #~(begin
242 (eval-when (expand load eval)
243 ;; Make sure 'use-modules' below succeeds.
244 (set! %load-path (cons #$modules %load-path))
245 (set! %load-compiled-path
246 (cons #$compiled %load-compiled-path)))
247
248 (use-modules (gnu build activation))
249
250 ;; Make sure /bin/sh is valid and current.
251 (activate-/bin/sh
252 (string-append #$(canonical-package bash) "/bin/sh"))
253
254 ;; Tell the kernel to use our 'modprobe' command.
255 (activate-modprobe #$modprobe)
256
257 ;; Let users debug their own processes!
258 (activate-ptrace-attach)
259
260 ;; Run the services' activation snippets.
261 ;; TODO: Use 'load-compiled'.
262 (for-each primitive-load '#$actions)
263
264 ;; Set up /run/current-system.
265 (activate-current-system)))))
266
267(define (gexps->activation-gexp gexps)
268 "Return a gexp that runs the activation script containing GEXPS."
269 (mlet %store-monad ((script (activation-script gexps)))
270 (return #~(primitive-load #$script))))
271
272(define activation-service-type
273 (service-type (name 'activate)
274 (extensions
275 (list (service-extension boot-service-type
276 gexps->activation-gexp)))
277 (compose append)
278 (extend second-argument)))
279
280(define %activation-service
281 ;; The activation service produces the activation script from the gexps it
282 ;; receives.
283 (service activation-service-type #t))
284
285(define (etc-directory service)
286 "Return the directory for SERVICE, a service of type ETC-SERVICE-TYPE."
287 (files->etc-directory (service-parameters service)))
288
289(define (files->etc-directory files)
290 (file-union "etc" files))
291
292(define etc-service-type
293 (service-type (name 'etc)
294 (extensions
295 (list
296 (service-extension activation-service-type
297 (lambda (files)
298 (let ((etc
299 (files->etc-directory files)))
300 #~(activate-etc #$etc))))))
301 (compose concatenate)
302 (extend append)))
303
304(define (etc-service files)
305 "Return a new service of ETC-SERVICE-TYPE that populates /etc with FILES.
306FILES must be a list of name/file-like object pairs."
307 (service etc-service-type files))
308
309(define setuid-program-service-type
310 (service-type (name 'setuid-program)
311 (extensions
312 (list (service-extension activation-service-type
313 (lambda (programs)
314 #~(activate-setuid-programs
315 (list #$@programs))))))
316 (compose concatenate)
317 (extend append)))
318
319(define (firmware->activation-gexp firmware)
320 "Return a gexp to make the packages listed in FIRMWARE loadable by the
321kernel."
322 (let ((directory (directory-union "firmware" firmware)))
323 ;; Tell the kernel where firmware is.
324 #~(activate-firmware (string-append #$directory "/lib/firmware"))))
325
326(define firmware-service-type
327 ;; The service that collects firmware.
328 (service-type (name 'firmware)
329 (extensions
330 (list (service-extension activation-service-type
331 firmware->activation-gexp)))
332 (compose concatenate)
333 (extend append)))
334
335
336;;;
337;;; Service folding.
338;;;
339
340(define-condition-type &service-error &error
341 service-error?)
342
343(define-condition-type &missing-target-service-error &service-error
344 missing-target-service-error?
345 (service missing-target-service-error-service)
346 (target-type missing-target-service-error-target-type))
347
348(define-condition-type &ambiguous-target-service-error &service-error
349 ambiguous-target-service-error?
350 (service ambiguous-target-service-error-service)
351 (target-type ambiguous-target-service-error-target-type))
352
353(define (service-back-edges services)
354 "Return a procedure that, when passed a <service>, returns the list of
355<service> objects that depend on it."
356 (define (add-edges service edges)
357 (define (add-edge extension edges)
358 (let ((target-type (service-extension-target extension)))
359 (match (filter (lambda (service)
360 (eq? (service-kind service) target-type))
361 services)
362 ((target)
363 (vhash-consq target service edges))
364 (()
365 (raise
366 (condition (&missing-target-service-error
367 (service service)
368 (target-type target-type))
369 (&message
370 (message
371 (format #f (_ "no target of type '~a' for service ~s")
372 (service-type-name target-type)
373 service))))))
374 (x
375 (raise
376 (condition (&ambiguous-target-service-error
377 (service service)
378 (target-type target-type))
379 (&message
380 (message
381 (format #f
382 (_ "more than one target service of type '~a'")
383 (service-type-name target-type))))))))))
384
385 (fold add-edge edges (service-type-extensions (service-kind service))))
386
387 (let ((edges (fold add-edges vlist-null services)))
388 (lambda (node)
389 (reverse (vhash-foldq* cons '() node edges)))))
390
391(define* (fold-services services #:key (target-type boot-service-type))
392 "Fold SERVICES by propagating their extensions down to the root of type
393TARGET-TYPE; return the root service adjusted accordingly."
394 (define dependents
395 (service-back-edges services))
396
397 (define (matching-extension target)
398 (let ((target (service-kind target)))
399 (match-lambda
400 (($ <service-extension> type)
401 (eq? type target)))))
402
403 (define (apply-extension target)
404 (lambda (service)
405 (match (find (matching-extension target)
406 (service-type-extensions (service-kind service)))
407 (($ <service-extension> _ compute)
408 (compute (service-parameters service))))))
409
410 (match (filter (lambda (service)
411 (eq? (service-kind service) target-type))
412 services)
413 ((sink)
414 (let loop ((sink sink))
415 (let* ((dependents (map loop (dependents sink)))
416 (extensions (map (apply-extension sink) dependents))
417 (extend (service-type-extend (service-kind sink)))
418 (compose (service-type-compose (service-kind sink)))
419 (params (service-parameters sink)))
420 ;; We distinguish COMPOSE and EXTEND because PARAMS typically has a
421 ;; different type than the elements of EXTENSIONS.
422 (if extend
423 (service (service-kind sink)
424 (extend params (compose extensions)))
425 sink))))
426 (()
427 (raise
428 (condition (&missing-target-service-error
429 (service #f)
430 (target-type target-type))
431 (&message
432 (message (format #f (_ "service of type '~a' not found")
433 (service-type-name target-type)))))))
434 (x
435 (raise
436 (condition (&ambiguous-target-service-error
437 (service #f)
438 (target-type target-type))
439 (&message
440 (message
441 (format #f
442 (_ "more than one target service of type '~a'")
443 (service-type-name target-type)))))))))
65 444
66;;; services.scm ends here. 445;;; services.scm ends here.
diff --git a/gnu/services/avahi.scm b/gnu/services/avahi.scm
index 929ac2fbb62..b576c395ffd 100644
--- a/gnu/services/avahi.scm
+++ b/gnu/services/avahi.scm
@@ -18,10 +18,13 @@
18 18
19(define-module (gnu services avahi) 19(define-module (gnu services avahi)
20 #:use-module (gnu services) 20 #:use-module (gnu services)
21 #:use-module (gnu services base)
22 #:use-module (gnu services dmd)
23 #:use-module (gnu services dbus)
21 #:use-module (gnu system shadow) 24 #:use-module (gnu system shadow)
22 #:use-module (gnu packages avahi) 25 #:use-module (gnu packages avahi)
23 #:use-module (gnu packages admin) 26 #:use-module (gnu packages admin)
24 #:use-module (guix store) 27 #:use-module (guix records)
25 #:use-module (guix gexp) 28 #:use-module (guix gexp)
26 #:export (avahi-service)) 29 #:export (avahi-service))
27 30
@@ -32,12 +35,27 @@
32;;; 35;;;
33;;; Code: 36;;; Code:
34 37
35(define* (configuration-file #:key host-name publish? 38 ;; TODO: Export.
36 ipv4? ipv6? wide-area? domains-to-browse) 39(define-record-type* <avahi-configuration>
37 "Return an avahi-daemon configuration file." 40 avahi-configuration make-avahi-configuration
41 avahi-configuration?
42 (avahi avahi-configuration-avahi ;<package>
43 (default avahi))
44 (host-name avahi-configuration-host-name) ;string
45 (publish? avahi-configuration-publish?) ;Boolean
46 (ipv4? avahi-configuration-ipv4?) ;Boolean
47 (ipv6? avahi-configuration-ipv6?) ;Boolean
48 (wide-area? avahi-configuration-wide-area?) ;Boolean
49 (domains-to-browse avahi-configuration-domains-to-browse)) ;list of strings
50
51(define* (configuration-file config)
52 "Return an avahi-daemon configuration file based on CONFIG, an
53<avahi-configuration>."
38 (define (bool value) 54 (define (bool value)
39 (if value "yes\n" "no\n")) 55 (if value "yes\n" "no\n"))
40 56
57 (define host-name (avahi-configuration-host-name config))
58
41 (plain-file "avahi-daemon.conf" 59 (plain-file "avahi-daemon.conf"
42 (string-append 60 (string-append
43 "[server]\n" 61 "[server]\n"
@@ -45,14 +63,63 @@
45 (string-append "host-name=" host-name "\n") 63 (string-append "host-name=" host-name "\n")
46 "") 64 "")
47 65
48 "browse-domains=" (string-join domains-to-browse) 66 "browse-domains=" (string-join
67 (avahi-configuration-domains-to-browse
68 config))
49 "\n" 69 "\n"
50 "use-ipv4=" (bool ipv4?) 70 "use-ipv4=" (bool (avahi-configuration-ipv4? config))
51 "use-ipv6=" (bool ipv6?) 71 "use-ipv6=" (bool (avahi-configuration-ipv6? config))
52 "[wide-area]\n" 72 "[wide-area]\n"
53 "enable-wide-area=" (bool wide-area?) 73 "enable-wide-area=" (bool (avahi-configuration-wide-area? config))
54 "[publish]\n" 74 "[publish]\n"
55 "disable-publishing=" (bool (not publish?))))) 75 "disable-publishing="
76 (bool (not (avahi-configuration-publish? config))))))
77
78(define %avahi-accounts
79 ;; Account and group for the Avahi daemon.
80 (list (user-group (name "avahi") (system? #t))
81 (user-account
82 (name "avahi")
83 (group "avahi")
84 (system? #t)
85 (comment "Avahi daemon user")
86 (home-directory "/var/empty")
87 (shell #~(string-append #$shadow "/sbin/nologin")))))
88
89(define %avahi-activation
90 ;; Activation gexp.
91 #~(begin
92 (use-modules (guix build utils))
93 (mkdir-p "/var/run/avahi-daemon")))
94
95(define (avahi-dmd-service config)
96 "Return a list of <dmd-service> for CONFIG."
97 (let ((config (configuration-file config))
98 (avahi (avahi-configuration-avahi config)))
99 (list (dmd-service
100 (documentation "Run the Avahi mDNS/DNS-SD responder.")
101 (provision '(avahi-daemon))
102 (requirement '(dbus-system networking))
103
104 (start #~(make-forkexec-constructor
105 (list (string-append #$avahi "/sbin/avahi-daemon")
106 "--syslog" "-f" #$config)))
107 (stop #~(make-kill-destructor))))))
108
109(define avahi-service-type
110 (service-type (name 'avahi)
111 (extensions
112 (list (service-extension dmd-root-service-type
113 avahi-dmd-service)
114 (service-extension dbus-root-service-type
115 (compose list
116 avahi-configuration-avahi))
117 (service-extension account-service-type
118 (const %avahi-accounts))
119 (service-extension activation-service-type
120 (const %avahi-activation))
121 (service-extension nscd-service-type
122 (const (list nss-mdns)))))))
56 123
57(define* (avahi-service #:key (avahi avahi) 124(define* (avahi-service #:key (avahi avahi)
58 host-name 125 host-name
@@ -75,36 +142,11 @@ When @var{wide-area?} is true, DNS-SD over unicast DNS is enabled.
75 142
76Boolean values @var{ipv4?} and @var{ipv6?} determine whether to use IPv4/IPv6 143Boolean values @var{ipv4?} and @var{ipv6?} determine whether to use IPv4/IPv6
77sockets." 144sockets."
78 (let ((config (configuration-file #:host-name host-name 145 (service avahi-service-type
79 #:publish? publish? 146 (avahi-configuration
80 #:ipv4? ipv4? 147 (avahi avahi) (host-name host-name)
81 #:ipv6? ipv6? 148 (publish? publish?) (ipv4? ipv4?) (ipv6? ipv6?)
82 #:wide-area? wide-area? 149 (wide-area? wide-area?)
83 #:domains-to-browse 150 (domains-to-browse domains-to-browse))))
84 domains-to-browse)))
85 (service
86 (documentation "Run the Avahi mDNS/DNS-SD responder.")
87 (provision '(avahi-daemon))
88 (requirement '(dbus-system networking))
89
90 (start #~(make-forkexec-constructor
91 (list (string-append #$avahi "/sbin/avahi-daemon")
92 "--syslog" "-f" #$config)))
93 (stop #~(make-kill-destructor))
94 (activate #~(begin
95 (use-modules (guix build utils))
96 (mkdir-p "/var/run/avahi-daemon")))
97
98 (user-groups (list (user-group
99 (name "avahi")
100 (system? #t))))
101 (user-accounts (list (user-account
102 (name "avahi")
103 (group "avahi")
104 (system? #t)
105 (comment "Avahi daemon user")
106 (home-directory "/var/empty")
107 (shell
108 #~(string-append #$shadow "/sbin/nologin"))))))))
109 151
110;;; avahi.scm ends here 152;;; avahi.scm ends here
diff --git a/gnu/services/base.scm b/gnu/services/base.scm
index d9d73b45973..adafe1b55ea 100644
--- a/gnu/services/base.scm
+++ b/gnu/services/base.scm
@@ -21,9 +21,11 @@
21(define-module (gnu services base) 21(define-module (gnu services base)
22 #:use-module (guix store) 22 #:use-module (guix store)
23 #:use-module (gnu services) 23 #:use-module (gnu services)
24 #:use-module (gnu services dmd)
24 #:use-module (gnu services networking) 25 #:use-module (gnu services networking)
25 #:use-module (gnu system shadow) ; 'user-account', etc. 26 #:use-module (gnu system shadow) ; 'user-account', etc.
26 #:use-module (gnu system linux) ; 'pam-service', etc. 27 #:use-module (gnu system linux) ; 'pam-service', etc.
28 #:use-module (gnu system file-systems) ; 'file-system', etc.
27 #:use-module (gnu packages admin) 29 #:use-module (gnu packages admin)
28 #:use-module ((gnu packages linux) 30 #:use-module ((gnu packages linux)
29 #:select (eudev kbd e2fsprogs lvm2 fuse alsa-utils crda)) 31 #:select (eudev kbd e2fsprogs lvm2 fuse alsa-utils crda))
@@ -49,6 +51,7 @@
49 host-name-service 51 host-name-service
50 console-keymap-service 52 console-keymap-service
51 console-font-service 53 console-font-service
54 udev-service-type
52 udev-service 55 udev-service
53 56
54 mingetty-configuration 57 mingetty-configuration
@@ -64,9 +67,14 @@
64 nscd-cache 67 nscd-cache
65 nscd-cache? 68 nscd-cache?
66 69
70 nscd-service-type
67 nscd-service 71 nscd-service
68 syslog-service 72 syslog-service
73
74 guix-configuration
75 guix-configuration?
69 guix-service 76 guix-service
77
70 %base-services)) 78 %base-services))
71 79
72;;; Commentary: 80;;; Commentary:
@@ -76,13 +84,13 @@
76;;; 84;;;
77;;; Code: 85;;; Code:
78 86
79(define (root-file-system-service) 87
80 "Return a service whose sole purpose is to re-mount read-only the root file 88;;;
81system upon shutdown (aka. cleanly \"umounting\" root.) 89;;; File systems.
90;;;
82 91
83This service must be the root of the service dependency graph so that its 92(define %root-file-system-dmd-service
84'stop' action is invoked when dmd is the only process left." 93 (dmd-service
85 (service
86 (documentation "Take care of the root file system.") 94 (documentation "Take care of the root file system.")
87 (provision '(root-file-system)) 95 (provision '(root-file-system))
88 (start #~(const #t)) 96 (start #~(const #t))
@@ -116,181 +124,230 @@ This service must be the root of the service dependency graph so that its
116 #f))))) 124 #f)))))
117 (respawn? #f))) 125 (respawn? #f)))
118 126
119(define* (file-system-service device target type 127(define root-file-system-service-type
120 #:key (flags '()) (check? #t) 128 (dmd-service-type (const %root-file-system-dmd-service)))
121 create-mount-point? options (title 'any) 129
122 (requirements '())) 130(define (root-file-system-service)
123 "Return a service that mounts DEVICE on TARGET as a file system TYPE with 131 "Return a service whose sole purpose is to re-mount read-only the root file
124OPTIONS. TITLE is a symbol specifying what kind of name DEVICE is: 'label for 132system upon shutdown (aka. cleanly \"umounting\" root.)
125a partition label, 'device for a device file name, or 'any. When CHECK? is 133
126true, check the file system before mounting it. When CREATE-MOUNT-POINT? is 134This service must be the root of the service dependency graph so that its
127true, create TARGET if it does not exist yet. FLAGS is a list of symbols, 135'stop' action is invoked when dmd is the only process left."
128such as 'read-only' etc. Optionally, REQUIREMENTS may be a list of service 136 (service root-file-system-service-type #f))
129names such as device-mapping services." 137
130 (service 138(define (file-system->dmd-service-name file-system)
131 (provision (list (symbol-append 'file-system- (string->symbol target)))) 139 "Return the symbol that denotes the service mounting and unmounting
132 (requirement `(root-file-system ,@requirements)) 140FILE-SYSTEM."
133 (documentation "Check, mount, and unmount the given file system.") 141 (symbol-append 'file-system-
134 (start #~(lambda args 142 (string->symbol (file-system-mount-point file-system))))
135 ;; FIXME: Use or factorize with 'mount-file-system'. 143
136 (let ((device (canonicalize-device-spec #$device '#$title)) 144(define file-system-service-type
137 (flags #$(mount-flags->bit-mask flags))) 145 ;; TODO(?): Make this an extensible service that takes <file-system> objects
138 #$(if create-mount-point? 146 ;; and returns a list of <dmd-service>.
139 #~(mkdir-p #$target) 147 (dmd-service-type
140 #~#t) 148 (lambda (file-system)
141 #$(if check? 149 (let ((target (file-system-mount-point file-system))
142 #~(begin 150 (device (file-system-device file-system))
143 ;; Make sure fsck.ext2 & co. can be found. 151 (type (file-system-type file-system))
144 (setenv "PATH" 152 (title (file-system-title file-system))
145 (string-append 153 (check? (file-system-check? file-system))
146 #$e2fsprogs "/sbin:" 154 (create? (file-system-create-mount-point? file-system))
147 "/run/current-system/profile/sbin:" 155 (dependencies (file-system-dependencies file-system)))
148 (getenv "PATH"))) 156 (dmd-service
149 (check-file-system device #$type)) 157 (provision (list (file-system->dmd-service-name file-system)))
150 #~#t) 158 (requirement `(root-file-system
151 159 ,@(map file-system->dmd-service-name dependencies)))
152 (mount device #$target #$type flags #$options) 160 (documentation "Check, mount, and unmount the given file system.")
153 161 (start #~(lambda args
154 ;; For read-only bind mounts, an extra remount is needed, 162 ;; FIXME: Use or factorize with 'mount-file-system'.
155 ;; as per <http://lwn.net/Articles/281157/>, which still 163 (let ((device (canonicalize-device-spec #$device '#$title))
156 ;; applies to Linux 4.0. 164 (flags #$(mount-flags->bit-mask
157 (when (and (= MS_BIND (logand flags MS_BIND)) 165 (file-system-flags file-system))))
158 (= MS_RDONLY (logand flags MS_RDONLY))) 166 #$(if create?
159 (mount device #$target #$type 167 #~(mkdir-p #$target)
160 (logior MS_BIND MS_REMOUNT MS_RDONLY)))) 168 #~#t)
161 #t)) 169 #$(if check?
162 (stop #~(lambda args 170 #~(begin
163 ;; Normally there are no processes left at this point, so 171 ;; Make sure fsck.ext2 & co. can be found.
164 ;; TARGET can be safely unmounted. 172 (setenv "PATH"
165 173 (string-append
166 ;; Make sure PID 1 doesn't keep TARGET busy. 174 #$e2fsprogs "/sbin:"
167 (chdir "/") 175 "/run/current-system/profile/sbin:"
168 176 (getenv "PATH")))
169 (umount #$target) 177 (check-file-system device #$type))
170 #f)))) 178 #~#t)
179
180 (mount device #$target #$type flags
181 #$(file-system-options file-system))
182
183 ;; For read-only bind mounts, an extra remount is needed,
184 ;; as per <http://lwn.net/Articles/281157/>, which still
185 ;; applies to Linux 4.0.
186 (when (and (= MS_BIND (logand flags MS_BIND))
187 (= MS_RDONLY (logand flags MS_RDONLY)))
188 (mount device #$target #$type
189 (logior MS_BIND MS_REMOUNT MS_RDONLY))))
190 #t))
191 (stop #~(lambda args
192 ;; Normally there are no processes left at this point, so
193 ;; TARGET can be safely unmounted.
194
195 ;; Make sure PID 1 doesn't keep TARGET busy.
196 (chdir "/")
197
198 (umount #$target)
199 #f)))))))
200
201(define* (file-system-service file-system)
202 "Return a service that mounts @var{file-system}, a @code{<file-system>}
203object."
204 (service file-system-service-type file-system))
205
206(define user-unmount-service-type
207 (dmd-service-type
208 (lambda (known-mount-points)
209 (dmd-service
210 (documentation "Unmount manually-mounted file systems.")
211 (provision '(user-unmount))
212 (start #~(const #t))
213 (stop #~(lambda args
214 (define (known? mount-point)
215 (member mount-point
216 (cons* "/proc" "/sys" '#$known-mount-points)))
217
218 ;; Make sure we don't keep the user's mount points busy.
219 (chdir "/")
220
221 (for-each (lambda (mount-point)
222 (format #t "unmounting '~a'...~%" mount-point)
223 (catch 'system-error
224 (lambda ()
225 (umount mount-point))
226 (lambda args
227 (let ((errno (system-error-errno args)))
228 (format #t "failed to unmount '~a': ~a~%"
229 mount-point (strerror errno))))))
230 (filter (negate known?) (mount-points)))
231 #f))))))
171 232
172(define (user-unmount-service known-mount-points) 233(define (user-unmount-service known-mount-points)
173 "Return a service whose sole purpose is to unmount file systems not listed 234 "Return a service whose sole purpose is to unmount file systems not listed
174in KNOWN-MOUNT-POINTS when it is stopped." 235in KNOWN-MOUNT-POINTS when it is stopped."
175 (service 236 (service user-unmount-service-type known-mount-points))
176 (documentation "Unmount manually-mounted file systems.")
177 (provision '(user-unmount))
178 (start #~(const #t))
179 (stop #~(lambda args
180 (define (known? mount-point)
181 (member mount-point
182 (cons* "/proc" "/sys"
183 '#$known-mount-points)))
184
185 ;; Make sure we don't keep the user's mount points busy.
186 (chdir "/")
187
188 (for-each (lambda (mount-point)
189 (format #t "unmounting '~a'...~%" mount-point)
190 (catch 'system-error
191 (lambda ()
192 (umount mount-point))
193 (lambda args
194 (let ((errno (system-error-errno args)))
195 (format #t "failed to unmount '~a': ~a~%"
196 mount-point (strerror errno))))))
197 (filter (negate known?) (mount-points)))
198 #f))))
199 237
200(define %do-not-kill-file 238(define %do-not-kill-file
201 ;; Name of the file listing PIDs of processes that must survive when halting 239 ;; Name of the file listing PIDs of processes that must survive when halting
202 ;; the system. Typical example is user-space file systems. 240 ;; the system. Typical example is user-space file systems.
203 "/etc/dmd/do-not-kill") 241 "/etc/dmd/do-not-kill")
204 242
205(define* (user-processes-service requirements #:key (grace-delay 4)) 243(define user-processes-service-type
244 (dmd-service-type
245 (match-lambda
246 ((requirements grace-delay)
247 (dmd-service
248 (documentation "When stopped, terminate all user processes.")
249 (provision '(user-processes))
250 (requirement (cons 'root-file-system
251 (map file-system->dmd-service-name
252 requirements)))
253 (start #~(const #t))
254 (stop #~(lambda _
255 (define (kill-except omit signal)
256 ;; Kill all the processes with SIGNAL except those listed
257 ;; in OMIT and the current process.
258 (let ((omit (cons (getpid) omit)))
259 (for-each (lambda (pid)
260 (unless (memv pid omit)
261 (false-if-exception
262 (kill pid signal))))
263 (processes))))
264
265 (define omitted-pids
266 ;; List of PIDs that must not be killed.
267 (if (file-exists? #$%do-not-kill-file)
268 (map string->number
269 (call-with-input-file #$%do-not-kill-file
270 (compose string-tokenize
271 (@ (ice-9 rdelim) read-string))))
272 '()))
273
274 (define (now)
275 (car (gettimeofday)))
276
277 (define (sleep* n)
278 ;; Really sleep N seconds.
279 ;; Work around <http://bugs.gnu.org/19581>.
280 (define start (now))
281 (let loop ((elapsed 0))
282 (when (> n elapsed)
283 (sleep (- n elapsed))
284 (loop (- (now) start)))))
285
286 (define lset= (@ (srfi srfi-1) lset=))
287
288 (display "sending all processes the TERM signal\n")
289
290 (if (null? omitted-pids)
291 (begin
292 ;; Easy: terminate all of them.
293 (kill -1 SIGTERM)
294 (sleep* #$grace-delay)
295 (kill -1 SIGKILL))
296 (begin
297 ;; Kill them all except OMITTED-PIDS. XXX: We would
298 ;; like to (kill -1 SIGSTOP) to get a fixed list of
299 ;; processes, like 'killall5' does, but that seems
300 ;; unreliable.
301 (kill-except omitted-pids SIGTERM)
302 (sleep* #$grace-delay)
303 (kill-except omitted-pids SIGKILL)
304 (delete-file #$%do-not-kill-file)))
305
306 (let wait ()
307 (let ((pids (processes)))
308 (unless (lset= = pids (cons 1 omitted-pids))
309 (format #t "waiting for process termination\
310 (processes left: ~s)~%"
311 pids)
312 (sleep* 2)
313 (wait))))
314
315 (display "all processes have been terminated\n")
316 #f))
317 (respawn? #f))))))
318
319(define* (user-processes-service file-systems #:key (grace-delay 4))
206 "Return the service that is responsible for terminating all the processes so 320 "Return the service that is responsible for terminating all the processes so
207that the root file system can be re-mounted read-only, just before 321that the root file system can be re-mounted read-only, just before
208rebooting/halting. Processes still running GRACE-DELAY seconds after SIGTERM 322rebooting/halting. Processes still running GRACE-DELAY seconds after SIGTERM
209has been sent are terminated with SIGKILL. 323has been sent are terminated with SIGKILL.
210 324
211The returned service will depend on 'root-file-system' and on all the services 325The returned service will depend on 'root-file-system' and on all the dmd
212listed in REQUIREMENTS. 326services corresponding to FILE-SYSTEMS.
213 327
214All the services that spawn processes must depend on this one so that they are 328All the services that spawn processes must depend on this one so that they are
215stopped before 'kill' is called." 329stopped before 'kill' is called."
216 (service 330 (service user-processes-service-type
217 (documentation "When stopped, terminate all user processes.") 331 (list file-systems grace-delay)))
218 (provision '(user-processes))
219 (requirement (cons 'root-file-system requirements))
220 (start #~(const #t))
221 (stop #~(lambda _
222 (define (kill-except omit signal)
223 ;; Kill all the processes with SIGNAL except those
224 ;; listed in OMIT and the current process.
225 (let ((omit (cons (getpid) omit)))
226 (for-each (lambda (pid)
227 (unless (memv pid omit)
228 (false-if-exception
229 (kill pid signal))))
230 (processes))))
231
232 (define omitted-pids
233 ;; List of PIDs that must not be killed.
234 (if (file-exists? #$%do-not-kill-file)
235 (map string->number
236 (call-with-input-file #$%do-not-kill-file
237 (compose string-tokenize
238 (@ (ice-9 rdelim) read-string))))
239 '()))
240
241 (define (now)
242 (car (gettimeofday)))
243
244 (define (sleep* n)
245 ;; Really sleep N seconds.
246 ;; Work around <http://bugs.gnu.org/19581>.
247 (define start (now))
248 (let loop ((elapsed 0))
249 (when (> n elapsed)
250 (sleep (- n elapsed))
251 (loop (- (now) start)))))
252
253 (define lset= (@ (srfi srfi-1) lset=))
254
255 (display "sending all processes the TERM signal\n")
256
257 (if (null? omitted-pids)
258 (begin
259 ;; Easy: terminate all of them.
260 (kill -1 SIGTERM)
261 (sleep* #$grace-delay)
262 (kill -1 SIGKILL))
263 (begin
264 ;; Kill them all except OMITTED-PIDS. XXX: We
265 ;; would like to (kill -1 SIGSTOP) to get a fixed
266 ;; list of processes, like 'killall5' does, but
267 ;; that seems unreliable.
268 (kill-except omitted-pids SIGTERM)
269 (sleep* #$grace-delay)
270 (kill-except omitted-pids SIGKILL)
271 (delete-file #$%do-not-kill-file)))
272
273 (let wait ()
274 (let ((pids (processes)))
275 (unless (lset= = pids (cons 1 omitted-pids))
276 (format #t "waiting for process termination\
277 (processes left: ~s)~%"
278 pids)
279 (sleep* 2)
280 (wait))))
281 332
282 (display "all processes have been terminated\n") 333
283 #f)) 334;;;
284 (respawn? #f))) 335;;; Console & co.
336;;;
337
338(define host-name-service-type
339 (dmd-service-type
340 (lambda (name)
341 (dmd-service
342 (documentation "Initialize the machine's host name.")
343 (provision '(host-name))
344 (start #~(lambda _
345 (sethostname #$name)))
346 (respawn? #f)))))
285 347
286(define (host-name-service name) 348(define (host-name-service name)
287 "Return a service that sets the host name to @var{name}." 349 "Return a service that sets the host name to @var{name}."
288 (service 350 (service host-name-service-type name))
289 (documentation "Initialize the machine's host name.")
290 (provision '(host-name))
291 (start #~(lambda _
292 (sethostname #$name)))
293 (respawn? #f)))
294 351
295(define (unicode-start tty) 352(define (unicode-start tty)
296 "Return a gexp to start Unicode support on @var{tty}." 353 "Return a gexp to start Unicode support on @var{tty}."
@@ -310,15 +367,43 @@ stopped before 'kill' is called."
310 (else 367 (else
311 (zero? (cdr (waitpid pid)))))))) 368 (zero? (cdr (waitpid pid))))))))
312 369
370(define console-keymap-service-type
371 (dmd-service-type
372 (lambda (file)
373 (dmd-service
374 (documentation (string-append "Load console keymap (loadkeys)."))
375 (provision '(console-keymap))
376 (start #~(lambda _
377 (zero? (system* (string-append #$kbd "/bin/loadkeys")
378 #$file))))
379 (respawn? #f)))))
380
313(define (console-keymap-service file) 381(define (console-keymap-service file)
314 "Return a service to load console keymap from @var{file}." 382 "Return a service to load console keymap from @var{file}."
315 (service 383 (service console-keymap-service-type file))
316 (documentation (string-append "Load console keymap (loadkeys).")) 384
317 (provision '(console-keymap)) 385(define console-font-service-type
318 (start #~(lambda _ 386 (dmd-service-type
319 (zero? (system* (string-append #$kbd "/bin/loadkeys") 387 (match-lambda
320 #$file)))) 388 ((tty font)
321 (respawn? #f))) 389 (let ((device (string-append "/dev/" tty)))
390 (dmd-service
391 (documentation "Load a Unicode console font.")
392 (provision (list (symbol-append 'console-font-
393 (string->symbol tty))))
394
395 ;; Start after mingetty has been started on TTY, otherwise the settings
396 ;; are ignored.
397 (requirement (list (symbol-append 'term-
398 (string->symbol tty))))
399
400 (start #~(lambda _
401 (and #$(unicode-start device)
402 (zero?
403 (system* (string-append #$kbd "/bin/setfont")
404 "-C" #$device #$font)))))
405 (stop #~(const #t))
406 (respawn? #f)))))))
322 407
323(define* (console-font-service tty #:optional (font "LatGrkCyr-8x16")) 408(define* (console-font-service tty #:optional (font "LatGrkCyr-8x16"))
324 "Return a service that sets up Unicode support in @var{tty} and loads 409 "Return a service that sets up Unicode support in @var{tty} and loads
@@ -326,24 +411,7 @@ stopped before 'kill' is called."
326 ;; Note: 'LatGrkCyr-8x16' has the advantage of providing three common 411 ;; Note: 'LatGrkCyr-8x16' has the advantage of providing three common
327 ;; scripts as well as glyphs for em dash, quotation marks, and other Unicode 412 ;; scripts as well as glyphs for em dash, quotation marks, and other Unicode
328 ;; codepoints notably found in the UTF-8 manual. 413 ;; codepoints notably found in the UTF-8 manual.
329 (let ((device (string-append "/dev/" tty))) 414 (service console-font-service-type (list tty font)))
330 (service
331 (documentation "Load a Unicode console font.")
332 (provision (list (symbol-append 'console-font-
333 (string->symbol tty))))
334
335 ;; Start after mingetty has been started on TTY, otherwise the
336 ;; settings are ignored.
337 (requirement (list (symbol-append 'term-
338 (string->symbol tty))))
339
340 (start #~(lambda _
341 (and #$(unicode-start device)
342 (zero?
343 (system* (string-append #$kbd "/bin/setfont")
344 "-C" #$device #$font)))))
345 (stop #~(const #t))
346 (respawn? #f))))
347 415
348(define-record-type* <mingetty-configuration> 416(define-record-type* <mingetty-configuration>
349 mingetty-configuration make-mingetty-configuration 417 mingetty-configuration make-mingetty-configuration
@@ -365,43 +433,56 @@ stopped before 'kill' is called."
365 (allow-empty-passwords? mingetty-configuration-allow-empty-passwords? 433 (allow-empty-passwords? mingetty-configuration-allow-empty-passwords?
366 (default #t))) ;Boolean 434 (default #t))) ;Boolean
367 435
368(define* (mingetty-service config) 436(define (mingetty-pam-service conf)
369 "Return a service to run mingetty according to @var{config}, a 437 "Return the list of PAM service needed for CONF."
370@code{<mingetty-configuration>} object, which specifies the tty to run, among 438 ;; Let 'login' be known to PAM. All the mingetty services will have that
371other things." 439 ;; PAM service, but that's fine because they're all identical and duplicates
372 (match config 440 ;; are removed.
441 (list (unix-pam-service "login"
442 #:allow-empty-passwords?
443 (mingetty-configuration-allow-empty-passwords? conf)
444 #:motd
445 (mingetty-configuration-motd conf))))
446
447(define mingetty-dmd-service
448 (match-lambda
373 (($ <mingetty-configuration> mingetty tty motd auto-login login-program 449 (($ <mingetty-configuration> mingetty tty motd auto-login login-program
374 login-pause? allow-empty-passwords?) 450 login-pause? allow-empty-passwords?)
375 (service 451 (list
376 (documentation "Run mingetty on an tty.") 452 (dmd-service
377 (provision (list (symbol-append 'term- (string->symbol tty)))) 453 (documentation "Run mingetty on an tty.")
378 454 (provision (list (symbol-append 'term- (string->symbol tty))))
379 ;; Since the login prompt shows the host name, wait for the 'host-name' 455
380 ;; service to be done. Also wait for udev essentially so that the tty 456 ;; Since the login prompt shows the host name, wait for the 'host-name'
381 ;; text is not lost in the middle of kernel messages (XXX). 457 ;; service to be done. Also wait for udev essentially so that the tty
382 (requirement '(user-processes host-name udev)) 458 ;; text is not lost in the middle of kernel messages (XXX).
383 459 (requirement '(user-processes host-name udev))
384 (start #~(make-forkexec-constructor 460
385 (list (string-append #$mingetty "/sbin/mingetty") 461 (start #~(make-forkexec-constructor
386 "--noclear" #$tty 462 (list (string-append #$mingetty "/sbin/mingetty")
387 #$@(if auto-login 463 "--noclear" #$tty
388 #~("--autologin" #$auto-login) 464 #$@(if auto-login
389 #~()) 465 #~("--autologin" #$auto-login)
390 #$@(if login-program 466 #~())
391 #~("--loginprog" #$login-program) 467 #$@(if login-program
392 #~()) 468 #~("--loginprog" #$login-program)
393 #$@(if login-pause? 469 #~())
394 #~("--loginpause") 470 #$@(if login-pause?
395 #~())))) 471 #~("--loginpause")
396 (stop #~(make-kill-destructor)) 472 #~()))))
397 473 (stop #~(make-kill-destructor)))))))
398 (pam-services 474
399 ;; Let 'login' be known to PAM. All the mingetty services will have 475(define mingetty-service-type
400 ;; that PAM service, but that's fine because they're all identical and 476 (service-type (name 'mingetty)
401 ;; duplicates are removed. 477 (extensions (list (service-extension dmd-root-service-type
402 (list (unix-pam-service "login" 478 mingetty-dmd-service)
403 #:allow-empty-passwords? allow-empty-passwords? 479 (service-extension pam-root-service-type
404 #:motd motd))))))) 480 mingetty-pam-service)))))
481
482(define* (mingetty-service config)
483 "Return a service to run mingetty according to @var{config}, which specifies
484the tty to run, among other things."
485 (service mingetty-service-type config))
405 486
406(define-record-type* <nscd-configuration> nscd-configuration 487(define-record-type* <nscd-configuration> nscd-configuration
407 make-nscd-configuration 488 make-nscd-configuration
@@ -506,38 +587,72 @@ other things."
506 (string-concatenate 587 (string-concatenate
507 (map cache->config caches))))))) 588 (map cache->config caches)))))))
508 589
590(define (nscd-dmd-service config)
591 "Return a dmd service for CONFIG, an <nscd-configuration> object."
592 (let ((nscd.conf (nscd.conf-file config))
593 (name-services (nscd-configuration-name-services config)))
594 (list (dmd-service
595 (documentation "Run libc's name service cache daemon (nscd).")
596 (provision '(nscd))
597 (requirement '(user-processes))
598 (start #~(make-forkexec-constructor
599 (list (string-append #$(nscd-configuration-glibc config)
600 "/sbin/nscd")
601 "-f" #$nscd.conf "--foreground")
602
603 #:environment-variables
604 (list (string-append "LD_LIBRARY_PATH="
605 (string-join
606 (map (lambda (dir)
607 (string-append dir "/lib"))
608 (list #$@name-services))
609 ":")))))
610 (stop #~(make-kill-destructor))
611
612 (respawn? #f)))))
613
614(define nscd-activation
615 ;; Actions to take before starting nscd.
616 #~(begin
617 (use-modules (guix build utils))
618 (mkdir-p "/var/run/nscd")
619 (mkdir-p "/var/db/nscd"))) ;for the persistent cache
620
621(define nscd-service-type
622 (service-type (name 'nscd)
623 (extensions
624 (list (service-extension activation-service-type
625 (const nscd-activation))
626 (service-extension dmd-root-service-type
627 nscd-dmd-service)))
628
629 ;; This can be extended by providing additional name services
630 ;; such as nss-mdns.
631 (compose concatenate)
632 (extend (lambda (config name-services)
633 (nscd-configuration
634 (inherit config)
635 (name-services (append
636 (nscd-configuration-name-services config)
637 name-services)))))))
638
509(define* (nscd-service #:optional (config %nscd-default-configuration)) 639(define* (nscd-service #:optional (config %nscd-default-configuration))
510 "Return a service that runs libc's name service cache daemon (nscd) with the 640 "Return a service that runs libc's name service cache daemon (nscd) with the
511given @var{config}---an @code{<nscd-configuration>} object. @xref{Name 641given @var{config}---an @code{<nscd-configuration>} object. @xref{Name
512Service Switch}, for an example." 642Service Switch}, for an example."
513 (let ((nscd.conf (nscd.conf-file config))) 643 (service nscd-service-type config))
514 (service 644
515 (documentation "Run libc's name service cache daemon (nscd).") 645(define syslog-service-type
516 (provision '(nscd)) 646 (dmd-service-type
517 (requirement '(user-processes)) 647 (lambda (config-file)
518 648 (dmd-service
519 (activate #~(begin 649 (documentation "Run the syslog daemon (syslogd).")
520 (use-modules (guix build utils)) 650 (provision '(syslogd))
521 (mkdir-p "/var/run/nscd") 651 (requirement '(user-processes))
522 (mkdir-p "/var/db/nscd"))) ;for the persistent cache 652 (start #~(make-forkexec-constructor
523 653 (list (string-append #$inetutils "/libexec/syslogd")
524 (start #~(make-forkexec-constructor 654 "--no-detach" "--rcfile" #$config-file)))
525 (list (string-append #$(nscd-configuration-glibc config) 655 (stop #~(make-kill-destructor))))))
526 "/sbin/nscd")
527 "-f" #$nscd.conf "--foreground")
528
529 #:environment-variables
530 (list (string-append "LD_LIBRARY_PATH="
531 (string-join
532 (map (lambda (dir)
533 (string-append dir "/lib"))
534 (list
535 #$@(nscd-configuration-name-services
536 config)))
537 ":")))))
538 (stop #~(make-kill-destructor))
539
540 (respawn? #f))))
541 656
542;; Snippet adapted from the GNU inetutils manual. 657;; Snippet adapted from the GNU inetutils manual.
543(define %default-syslog.conf 658(define %default-syslog.conf
@@ -561,18 +676,12 @@ Service Switch}, for an example."
561 # Log all the mail messages in one place. 676 # Log all the mail messages in one place.
562 mail.* /var/log/maillog 677 mail.* /var/log/maillog
563")) 678"))
679
564(define* (syslog-service #:key (config-file %default-syslog.conf)) 680(define* (syslog-service #:key (config-file %default-syslog.conf))
565 "Return a service that runs @code{syslogd}. 681 "Return a service that runs @code{syslogd}.
566If configuration file name @var{config-file} is not specified, use some 682If configuration file name @var{config-file} is not specified, use some
567reasonable default settings." 683reasonable default settings."
568 (service 684 (service syslog-service-type config-file))
569 (documentation "Run the syslog daemon (syslogd).")
570 (provision '(syslogd))
571 (requirement '(user-processes))
572 (start #~(make-forkexec-constructor
573 (list (string-append #$inetutils "/libexec/syslogd")
574 "--no-detach" "--rcfile" #$config-file)))
575 (stop #~(make-kill-destructor))))
576 685
577(define* (guix-build-accounts count #:key 686(define* (guix-build-accounts count #:key
578 (group "guixbuild") 687 (group "guixbuild")
@@ -621,61 +730,104 @@ GUIX."
621 (format (current-error-port) "warning: \ 730 (format (current-error-port) "warning: \
622failed to register hydra.gnu.org public key: ~a~%" status)))))))) 731failed to register hydra.gnu.org public key: ~a~%" status))))))))
623 732
624(define* (guix-service #:key (guix guix) (builder-group "guixbuild") 733(define-record-type* <guix-configuration>
625 (build-accounts 10) (authorize-hydra-key? #t) 734 guix-configuration make-guix-configuration
626 (use-substitutes? #t) 735 guix-configuration?
627 (extra-options '()) 736 (guix guix-configuration-guix ;<package>
628 (lsof lsof) (lsh lsh)) 737 (default guix))
629 "Return a service that runs the build daemon from @var{guix}, and has 738 (build-group guix-configuration-build-group ;string
630@var{build-accounts} user accounts available under @var{builder-group}. 739 (default "guixbuild"))
631 740 (build-accounts guix-configuration-build-accounts ;integer
632When @var{authorize-hydra-key?} is true, the @code{hydra.gnu.org} public key 741 (default 10))
633provided by @var{guix} is authorized upon activation, meaning that substitutes 742 (authorize-key? guix-configuration-authorize-key? ;Boolean
634from @code{hydra.gnu.org} are used by default. 743 (default #t))
635 744 (use-substitutes? guix-configuration-use-substitutes? ;Boolean
636If @var{use-substitutes?} is false, the daemon is run with 745 (default #t))
637@option{--no-substitutes} (@pxref{Invoking guix-daemon, 746 (extra-options guix-configuration-extra-options ;list of strings
638@option{--no-substitutes}}). 747 (default '()))
639 748 (lsof guix-configuration-lsof ;<package>
640Finally, @var{extra-options} is a list of additional command-line options 749 (default lsof))
641passed to @command{guix-daemon}." 750 (lsh guix-configuration-lsh ;<package>
642 (define activate 751 (default lsh)))
643 ;; Assume that the store has BUILDER-GROUP as its group. We could 752
644 ;; otherwise call 'chown' here, but the problem is that on a COW unionfs, 753(define %default-guix-configuration
645 ;; chown leads to an entire copy of the tree, which is a bad idea. 754 (guix-configuration))
646 755
647 ;; Optionally authorize hydra.gnu.org's key. 756(define (guix-dmd-service config)
648 (and authorize-hydra-key? 757 "Return a <dmd-service> for the Guix daemon service with CONFIG."
649 (hydra-key-authorization guix))) 758 (match config
650 759 (($ <guix-configuration> guix build-group build-accounts authorize-key?
651 (service 760 use-substitutes? extra-options lsof lsh)
652 (documentation "Run the Guix daemon.") 761 (list (dmd-service
653 (provision '(guix-daemon)) 762 (documentation "Run the Guix daemon.")
654 (requirement '(user-processes)) 763 (provision '(guix-daemon))
655 (start 764 (requirement '(user-processes))
656 #~(make-forkexec-constructor 765 (start
657 (list (string-append #$guix "/bin/guix-daemon") 766 #~(make-forkexec-constructor
658 "--build-users-group" #$builder-group 767 (list (string-append #$guix "/bin/guix-daemon")
659 #$@(if use-substitutes? 768 "--build-users-group" #$build-group
660 '() 769 #$@(if use-substitutes?
661 '("--no-substitutes")) 770 '()
662 #$@extra-options) 771 '("--no-substitutes"))
663 772 #$@extra-options)
664 ;; Add 'lsof' (for the GC) and 'lsh' (for offloading) to the 773
665 ;; daemon's $PATH. 774 ;; Add 'lsof' (for the GC) and 'lsh' (for offloading) to the
666 #:environment-variables 775 ;; daemon's $PATH.
667 (list (string-append "PATH=" #$lsof "/bin:" #$lsh "/bin")))) 776 #:environment-variables
668 (stop #~(make-kill-destructor)) 777 (list (string-append "PATH=" #$lsof "/bin:" #$lsh "/bin"))))
669 (user-accounts (guix-build-accounts build-accounts 778 (stop #~(make-kill-destructor)))))))
670 #:group builder-group)) 779
671 (user-groups (list (user-group 780(define (guix-accounts config)
672 (name builder-group) 781 "Return the user accounts and user groups for CONFIG."
673 (system? #t) 782 (match config
674 783 (($ <guix-configuration> _ build-group build-accounts)
675 ;; Use a fixed GID so that we can create the 784 (cons (user-group
676 ;; store with the right owner. 785 (name build-group)
677 (id 30000)))) 786 (system? #t)
678 (activate activate))) 787
788 ;; Use a fixed GID so that we can create the store with the right
789 ;; owner.
790 (id 30000))
791 (guix-build-accounts build-accounts
792 #:group build-group)))))
793
794(define (guix-activation config)
795 "Return the activation gexp for CONFIG."
796 (match config
797 (($ <guix-configuration> guix build-group build-accounts authorize-key?)
798 ;; Assume that the store has BUILD-GROUP as its group. We could
799 ;; otherwise call 'chown' here, but the problem is that on a COW unionfs,
800 ;; chown leads to an entire copy of the tree, which is a bad idea.
801
802 ;; Optionally authorize hydra.gnu.org's key.
803 (and authorize-key?
804 (hydra-key-authorization guix)))))
805
806(define guix-service-type
807 (service-type
808 (name 'guix)
809 (extensions
810 (list (service-extension dmd-root-service-type guix-dmd-service)
811 (service-extension account-service-type guix-accounts)
812 (service-extension activation-service-type guix-activation)))))
813
814(define* (guix-service #:optional (config %default-guix-configuration))
815 "Return a service that runs the Guix build daemon according to
816@var{config}."
817 (service guix-service-type config))
818
819
820;;;
821;;; Udev.
822;;;
823
824(define-record-type* <udev-configuration>
825 udev-configuration make-udev-configuration
826 udev-configuration?
827 (udev udev-configuration-udev ;<package>
828 (default udev))
829 (rules udev-configuration-rules ;list of <package>
830 (default '())))
679 831
680(define (udev-rules-union packages) 832(define (udev-rules-union packages)
681 "Return the union of the @code{lib/udev/rules.d} directories found in each 833 "Return the union of the @code{lib/udev/rules.d} directories found in each
@@ -727,118 +879,150 @@ item of @var{packages}."
727KERNEL==\"kvm\", GROUP=\"kvm\", MODE=\"0660\"\n" port)))) 879KERNEL==\"kvm\", GROUP=\"kvm\", MODE=\"0660\"\n" port))))
728 #:modules '((guix build utils)))) 880 #:modules '((guix build utils))))
729 881
882(define udev-dmd-service
883 ;; Return a <dmd-service> for UDEV with RULES.
884 (match-lambda
885 (($ <udev-configuration> udev rules)
886 (let* ((rules (udev-rules-union (cons* udev (kvm-udev-rule) rules)))
887 (udev.conf (computed-file "udev.conf"
888 #~(call-with-output-file #$output
889 (lambda (port)
890 (format port
891 "udev_rules=\"~a/lib/udev/rules.d\"\n"
892 #$rules))))))
893 (list
894 (dmd-service
895 (provision '(udev))
896
897 ;; Udev needs /dev to be a 'devtmpfs' mount so that new device nodes can
898 ;; be added: see
899 ;; <http://www.linuxfromscratch.org/lfs/view/development/chapter07/udev.html>.
900 (requirement '(root-file-system))
901
902 (documentation "Populate the /dev directory, dynamically.")
903 (start #~(lambda ()
904 (define find
905 (@ (srfi srfi-1) find))
906
907 (define udevd
908 ;; Choose the right 'udevd'.
909 (find file-exists?
910 (map (lambda (suffix)
911 (string-append #$udev suffix))
912 '("/libexec/udev/udevd" ;udev
913 "/sbin/udevd")))) ;eudev
914
915 (define (wait-for-udevd)
916 ;; Wait until someone's listening on udevd's control
917 ;; socket.
918 (let ((sock (socket AF_UNIX SOCK_SEQPACKET 0)))
919 (let try ()
920 (catch 'system-error
921 (lambda ()
922 (connect sock PF_UNIX "/run/udev/control")
923 (close-port sock))
924 (lambda args
925 (format #t "waiting for udevd...~%")
926 (usleep 500000)
927 (try))))))
928
929 ;; Allow udev to find the modules.
930 (setenv "LINUX_MODULE_DIRECTORY"
931 "/run/booted-system/kernel/lib/modules")
932
933 ;; The first one is for udev, the second one for eudev.
934 (setenv "UDEV_CONFIG_FILE" #$udev.conf)
935 (setenv "EUDEV_RULES_DIRECTORY"
936 (string-append #$rules "/lib/udev/rules.d"))
937
938 (let ((pid (primitive-fork)))
939 (case pid
940 ((0)
941 (exec-command (list udevd)))
942 (else
943 ;; Wait until udevd is up and running. This
944 ;; appears to be needed so that the events
945 ;; triggered below are actually handled.
946 (wait-for-udevd)
947
948 ;; Trigger device node creation.
949 (system* (string-append #$udev "/bin/udevadm")
950 "trigger" "--action=add")
951
952 ;; Wait for things to settle down.
953 (system* (string-append #$udev "/bin/udevadm")
954 "settle")
955 pid)))))
956 (stop #~(make-kill-destructor))
957
958 ;; When halting the system, 'udev' is actually killed by
959 ;; 'user-processes', i.e., before its own 'stop' method was called.
960 ;; Thus, make sure it is not respawned.
961 (respawn? #f)))))))
962
963(define udev-service-type
964 (service-type (name 'udev)
965 (extensions
966 (list (service-extension dmd-root-service-type
967 udev-dmd-service)))
968
969 (compose concatenate) ;concatenate the list of rules
970 (extend (lambda (config rules)
971 (match config
972 (($ <udev-configuration> udev initial-rules)
973 (udev-configuration
974 (udev udev)
975 (rules (append initial-rules rules)))))))))
976
730(define* (udev-service #:key (udev eudev) (rules '())) 977(define* (udev-service #:key (udev eudev) (rules '()))
731 "Run @var{udev}, which populates the @file{/dev} directory dynamically. Get 978 "Run @var{udev}, which populates the @file{/dev} directory dynamically. Get
732extra rules from the packages listed in @var{rules}." 979extra rules from the packages listed in @var{rules}."
733 (let* ((rules (udev-rules-union (cons* udev 980 (service udev-service-type
734 (kvm-udev-rule) 981 (udev-configuration (udev udev) (rules rules))))
735 rules))) 982
736 (udev.conf (computed-file "udev.conf" 983(define device-mapping-service-type
737 #~(call-with-output-file #$output 984 (dmd-service-type
738 (lambda (port) 985 (match-lambda
739 (format port 986 ((target open close)
740 "udev_rules=\"~a/lib/udev/rules.d\"\n" 987 (dmd-service
741 #$rules)))))) 988 (provision (list (symbol-append 'device-mapping- (string->symbol target))))
742 (service 989 (requirement '(udev))
743 (provision '(udev)) 990 (documentation "Map a device node using Linux's device mapper.")
744 991 (start #~(lambda () #$open))
745 ;; Udev needs /dev to be a 'devtmpfs' mount so that new device nodes can 992 (stop #~(lambda _ (not #$close)))
746 ;; be added: see 993 (respawn? #f))))))
747 ;; <http://www.linuxfromscratch.org/lfs/view/development/chapter07/udev.html>.
748 (requirement '(root-file-system))
749
750 (documentation "Populate the /dev directory, dynamically.")
751 (start #~(lambda ()
752 (define find
753 (@ (srfi srfi-1) find))
754
755 (define udevd
756 ;; Choose the right 'udevd'.
757 (find file-exists?
758 (map (lambda (suffix)
759 (string-append #$udev suffix))
760 '("/libexec/udev/udevd" ;udev
761 "/sbin/udevd")))) ;eudev
762
763 (define (wait-for-udevd)
764 ;; Wait until someone's listening on udevd's control
765 ;; socket.
766 (let ((sock (socket AF_UNIX SOCK_SEQPACKET 0)))
767 (let try ()
768 (catch 'system-error
769 (lambda ()
770 (connect sock PF_UNIX "/run/udev/control")
771 (close-port sock))
772 (lambda args
773 (format #t "waiting for udevd...~%")
774 (usleep 500000)
775 (try))))))
776
777 ;; Allow udev to find the modules.
778 (setenv "LINUX_MODULE_DIRECTORY"
779 "/run/booted-system/kernel/lib/modules")
780
781 ;; The first one is for udev, the second one for eudev.
782 (setenv "UDEV_CONFIG_FILE" #$udev.conf)
783 (setenv "EUDEV_RULES_DIRECTORY"
784 (string-append #$rules "/lib/udev/rules.d"))
785
786 (let ((pid (primitive-fork)))
787 (case pid
788 ((0)
789 (exec-command (list udevd)))
790 (else
791 ;; Wait until udevd is up and running. This
792 ;; appears to be needed so that the events
793 ;; triggered below are actually handled.
794 (wait-for-udevd)
795
796 ;; Trigger device node creation.
797 (system* (string-append #$udev "/bin/udevadm")
798 "trigger" "--action=add")
799
800 ;; Wait for things to settle down.
801 (system* (string-append #$udev "/bin/udevadm")
802 "settle")
803 pid)))))
804 (stop #~(make-kill-destructor))
805
806 ;; When halting the system, 'udev' is actually killed by
807 ;; 'user-processes', i.e., before its own 'stop' method was
808 ;; called. Thus, make sure it is not respawned.
809 (respawn? #f))))
810 994
811(define (device-mapping-service target open close) 995(define (device-mapping-service target open close)
812 "Return a service that maps device @var{target}, a string such as 996 "Return a service that maps device @var{target}, a string such as
813@code{\"home\"} (meaning @code{/dev/mapper/home}). Evaluate @var{open}, a 997@code{\"home\"} (meaning @code{/dev/mapper/home}). Evaluate @var{open}, a
814gexp, to open it, and evaluate @var{close} to close it." 998gexp, to open it, and evaluate @var{close} to close it."
815 (service 999 (service device-mapping-service-type
816 (provision (list (symbol-append 'device-mapping- (string->symbol target)))) 1000 (list target open close)))
817 (requirement '(udev)) 1001
818 (documentation "Map a device node using Linux's device mapper.") 1002(define swap-service-type
819 (start #~(lambda () #$open)) 1003 (dmd-service-type
820 (stop #~(lambda _ (not #$close))) 1004 (lambda (device)
821 (respawn? #f))) 1005 (define requirement
1006 (if (string-prefix? "/dev/mapper/" device)
1007 (list (symbol-append 'device-mapping-
1008 (string->symbol (basename device))))
1009 '()))
1010
1011 (dmd-service
1012 (provision (list (symbol-append 'swap- (string->symbol device))))
1013 (requirement `(udev ,@requirement))
1014 (documentation "Enable the given swap device.")
1015 (start #~(lambda ()
1016 (restart-on-EINTR (swapon #$device))
1017 #t))
1018 (stop #~(lambda _
1019 (restart-on-EINTR (swapoff #$device))
1020 #f))
1021 (respawn? #f)))))
822 1022
823(define (swap-service device) 1023(define (swap-service device)
824 "Return a service that uses @var{device} as a swap device." 1024 "Return a service that uses @var{device} as a swap device."
825 (define requirement 1025 (service swap-service-type device))
826 (if (string-prefix? "/dev/mapper/" device)
827 (list (symbol-append 'device-mapping-
828 (string->symbol (basename device))))
829 '()))
830
831 (service
832 (provision (list (symbol-append 'swap- (string->symbol device))))
833 (requirement `(udev ,@requirement))
834 (documentation "Enable the given swap device.")
835 (start #~(lambda ()
836 (restart-on-EINTR (swapon #$device))
837 #t))
838 (stop #~(lambda _
839 (restart-on-EINTR (swapoff #$device))
840 #f))
841 (respawn? #f)))
842 1026
843(define %base-services 1027(define %base-services
844 ;; Convenience variable holding the basic services. 1028 ;; Convenience variable holding the basic services.
@@ -873,9 +1057,6 @@ This is the GNU operating system, welcome!\n\n")))
873 ;; The LVM2 rules are needed as soon as LVM2 or the device-mapper is 1057 ;; The LVM2 rules are needed as soon as LVM2 or the device-mapper is
874 ;; used, so enable them by default. The FUSE and ALSA rules are 1058 ;; used, so enable them by default. The FUSE and ALSA rules are
875 ;; less critical, but handy. 1059 ;; less critical, but handy.
876 ;;
877 ;; XXX Keep this in sync with the 'udev-service' call in
878 ;; %desktop-services.
879 (udev-service #:rules (list lvm2 fuse alsa-utils crda))))) 1060 (udev-service #:rules (list lvm2 fuse alsa-utils crda)))))
880 1061
881;;; base.scm ends here 1062;;; base.scm ends here
diff --git a/gnu/services/databases.scm b/gnu/services/databases.scm
index 20f8a6977eb..8fdd222a3bf 100644
--- a/gnu/services/databases.scm
+++ b/gnu/services/databases.scm
@@ -19,12 +19,13 @@
19 19
20(define-module (gnu services databases) 20(define-module (gnu services databases)
21 #:use-module (gnu services) 21 #:use-module (gnu services)
22 #:use-module (gnu services dmd)
22 #:use-module (gnu system shadow) 23 #:use-module (gnu system shadow)
23 #:use-module (gnu packages admin) 24 #:use-module (gnu packages admin)
24 #:use-module (gnu packages databases) 25 #:use-module (gnu packages databases)
25 #:use-module (guix records) 26 #:use-module (guix records)
26 #:use-module (guix store)
27 #:use-module (guix gexp) 27 #:use-module (guix gexp)
28 #:use-module (ice-9 match)
28 #:export (postgresql-service)) 29 #:export (postgresql-service))
29 30
30;;; Commentary: 31;;; Commentary:
@@ -33,6 +34,14 @@
33;;; 34;;;
34;;; Code: 35;;; Code:
35 36
37(define-record-type* <postgresql-configuration>
38 postgresql-configuration make-postgresql-configuration
39 postgresql-configuration?
40 (postgresql postgresql-configuration-postgresql ;<package>
41 (default postgresql))
42 (config-file postgresql-configuration-file)
43 (data-directory postgresql-configuration-data-directory))
44
36(define %default-postgres-hba 45(define %default-postgres-hba
37 (plain-file "pg_hba.conf" 46 (plain-file "pg_hba.conf"
38 " 47 "
@@ -49,6 +58,77 @@ host all all ::1/128 trust"))
49 "hba_file = '" %default-postgres-hba "'\n" 58 "hba_file = '" %default-postgres-hba "'\n"
50 "ident_file = '" %default-postgres-ident "\n")) 59 "ident_file = '" %default-postgres-ident "\n"))
51 60
61(define %postgresql-accounts
62 (list (user-group (name "postgres") (system? #t))
63 (user-account
64 (name "postgres")
65 (group "postgres")
66 (system? #t)
67 (comment "PostgreSQL server user")
68 (home-directory "/var/empty")
69 (shell #~(string-append #$shadow "/sbin/nologin")))))
70
71(define postgresql-activation
72 (match-lambda
73 (($ <postgresql-configuration> postgresql config-file data-directory)
74 #~(begin
75 (use-modules (guix build utils)
76 (ice-9 match))
77
78 (let ((user (getpwnam "postgres"))
79 (initdb (string-append #$postgresql "/bin/initdb")))
80 ;; Create db state directory.
81 (mkdir-p #$data-directory)
82 (chown #$data-directory (passwd:uid user) (passwd:gid user))
83
84 ;; Drop privileges and init state directory in a new
85 ;; process. Wait for it to finish before proceeding.
86 (match (primitive-fork)
87 (0
88 ;; Exit with a non-zero status code if an exception is thrown.
89 (dynamic-wind
90 (const #t)
91 (lambda ()
92 (setgid (passwd:gid user))
93 (setuid (passwd:uid user))
94 (primitive-exit (system* initdb "-D" #$data-directory)))
95 (lambda ()
96 (primitive-exit 1))))
97 (pid (waitpid pid))))))))
98
99(define postgresql-dmd-service
100 (match-lambda
101 (($ <postgresql-configuration> postgresql config-file data-directory)
102 (let ((start-script
103 ;; Wrapper script that switches to the 'postgres' user before
104 ;; launching daemon.
105 (program-file "start-postgres"
106 #~(let ((user (getpwnam "postgres"))
107 (postgres (string-append #$postgresql
108 "/bin/postgres")))
109 (setgid (passwd:gid user))
110 (setuid (passwd:uid user))
111 (system* postgres
112 (string-append "--config-file="
113 #$config-file)
114 "-D" #$data-directory)))))
115 (list (dmd-service
116 (provision '(postgres))
117 (documentation "Run the PostgreSQL daemon.")
118 (requirement '(user-processes loopback))
119 (start #~(make-forkexec-constructor #$start-script))
120 (stop #~(make-kill-destructor))))))))
121
122(define postgresql-service-type
123 (service-type (name 'postgresql)
124 (extensions
125 (list (service-extension dmd-root-service-type
126 postgresql-dmd-service)
127 (service-extension activation-service-type
128 postgresql-activation)
129 (service-extension account-service-type
130 (const %postgresql-accounts))))))
131
52(define* (postgresql-service #:key (postgresql postgresql) 132(define* (postgresql-service #:key (postgresql postgresql)
53 (config-file %default-postgres-config) 133 (config-file %default-postgres-config)
54 (data-directory "/var/lib/postgresql/data")) 134 (data-directory "/var/lib/postgresql/data"))
@@ -56,60 +136,8 @@ host all all ::1/128 trust"))
56 136
57The PostgreSQL daemon loads its runtime configuration from @var{config-file} 137The PostgreSQL daemon loads its runtime configuration from @var{config-file}
58and stores the database cluster in @var{data-directory}." 138and stores the database cluster in @var{data-directory}."
59 ;; Wrapper script that switches to the 'postgres' user before launching 139 (service postgresql-service-type
60 ;; daemon. 140 (postgresql-configuration
61 (define start-script 141 (postgresql postgresql)
62 (program-file "start-postgres" 142 (config-file config-file)
63 #~(let ((user (getpwnam "postgres")) 143 (data-directory data-directory))))
64 (postgres (string-append #$postgresql
65 "/bin/postgres")))
66 (setgid (passwd:gid user))
67 (setuid (passwd:uid user))
68 (system* postgres
69 (string-append "--config-file=" #$config-file)
70 "-D" #$data-directory))))
71
72 (define activate
73 #~(begin
74 (use-modules (guix build utils)
75 (ice-9 match))
76
77 (let ((user (getpwnam "postgres"))
78 (initdb (string-append #$postgresql "/bin/initdb")))
79 ;; Create db state directory.
80 (mkdir-p #$data-directory)
81 (chown #$data-directory (passwd:uid user) (passwd:gid user))
82
83 ;; Drop privileges and init state directory in a new
84 ;; process. Wait for it to finish before proceeding.
85 (match (primitive-fork)
86 (0
87 ;; Exit with a non-zero status code if an exception is thrown.
88 (dynamic-wind
89 (const #t)
90 (lambda ()
91 (setgid (passwd:gid user))
92 (setuid (passwd:uid user))
93 (primitive-exit (system* initdb "-D" #$data-directory)))
94 (lambda ()
95 (primitive-exit 1))))
96 (pid (waitpid pid))))))
97
98 (service
99 (provision '(postgres))
100 (documentation "Run the PostgreSQL daemon.")
101 (requirement '(user-processes loopback))
102 (start #~(make-forkexec-constructor #$start-script))
103 (stop #~(make-kill-destructor))
104 (activate activate)
105 (user-groups (list (user-group
106 (name "postgres")
107 (system? #t))))
108 (user-accounts (list (user-account
109 (name "postgres")
110 (group "postgres")
111 (system? #t)
112 (comment "PostgreSQL server user")
113 (home-directory "/var/empty")
114 (shell
115 #~(string-append #$shadow "/sbin/nologin")))))))
diff --git a/gnu/services/dbus.scm b/gnu/services/dbus.scm
new file mode 100644
index 00000000000..e4ecd961c57
--- /dev/null
+++ b/gnu/services/dbus.scm
@@ -0,0 +1,178 @@
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2013, 2014, 2015 Ludovic Courtès <ludo@gnu.org>
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (gnu services dbus)
20 #:use-module (gnu services)
21 #:use-module (gnu services dmd)
22 #:use-module (gnu system shadow)
23 #:use-module (gnu packages glib)
24 #:use-module (gnu packages admin)
25 #:use-module (guix gexp)
26 #:use-module (guix records)
27 #:use-module (srfi srfi-1)
28 #:use-module (ice-9 match)
29 #:export (dbus-root-service-type
30 dbus-service))
31
32;;;
33;;; D-Bus.
34;;;
35
36(define-record-type* <dbus-configuration>
37 dbus-configuration make-dbus-configuration
38 dbus-configuration?
39 (dbus dbus-configuration-dbus ;<package>
40 (default dbus))
41 (services dbus-configuration-services ;list of <package>
42 (default '())))
43
44(define (dbus-configuration-directory dbus services)
45 "Return a configuration directory for @var{dbus} that includes the
46@code{etc/dbus-1/system.d} directories of each package listed in
47@var{services}."
48 (define build
49 #~(begin
50 (use-modules (sxml simple)
51 (srfi srfi-1))
52
53 (define (services->sxml services)
54 ;; Return the SXML 'includedir' clauses for DIRS.
55 `(busconfig
56 ,@(append-map (lambda (dir)
57 `((includedir
58 ,(string-append dir "/etc/dbus-1/system.d"))
59 (servicedir ;for '.service' files
60 ,(string-append dir "/share/dbus-1/services"))
61 (servicedir ;likewise, for auto-activation
62 ,(string-append
63 dir
64 "/share/dbus-1/system-services"))))
65 services)))
66
67 (mkdir #$output)
68 (copy-file (string-append #$dbus "/etc/dbus-1/system.conf")
69 (string-append #$output "/system.conf"))
70
71 ;; The default 'system.conf' has an <includedir> clause for
72 ;; 'system.d', so create it.
73 (mkdir (string-append #$output "/system.d"))
74
75 ;; 'system-local.conf' is automatically included by the default
76 ;; 'system.conf', so this is where we stuff our own things.
77 (call-with-output-file (string-append #$output "/system-local.conf")
78 (lambda (port)
79 (sxml->xml (services->sxml (list #$@services))
80 port)))))
81
82 (computed-file "dbus-configuration" build))
83
84(define %dbus-accounts
85 ;; Accounts used by the system bus.
86 (list (user-group (name "messagebus") (system? #t))
87 (user-account
88 (name "messagebus")
89 (group "messagebus")
90 (system? #t)
91 (comment "D-Bus system bus user")
92 (home-directory "/var/run/dbus")
93 (shell #~(string-append #$shadow "/sbin/nologin")))))
94
95(define (dbus-activation config)
96 "Return an activation gexp for D-Bus using @var{config}."
97 #~(begin
98 (use-modules (guix build utils))
99
100 (mkdir-p "/var/run/dbus")
101
102 (let ((user (getpwnam "messagebus")))
103 (chown "/var/run/dbus"
104 (passwd:uid user) (passwd:gid user)))
105
106 (unless (file-exists? "/etc/machine-id")
107 (format #t "creating /etc/machine-id...~%")
108 (let ((prog (string-append #$(dbus-configuration-dbus config)
109 "/bin/dbus-uuidgen")))
110 ;; XXX: We can't use 'system' because the initrd's
111 ;; guile system(3) only works when 'sh' is in $PATH.
112 (let ((pid (primitive-fork)))
113 (if (zero? pid)
114 (call-with-output-file "/etc/machine-id"
115 (lambda (port)
116 (close-fdes 1)
117 (dup2 (port->fdes port) 1)
118 (execl prog)))
119 (waitpid pid)))))))
120
121(define dbus-dmd-service
122 (match-lambda
123 (($ <dbus-configuration> dbus services)
124 (let ((conf (dbus-configuration-directory dbus services)))
125 (list (dmd-service
126 (documentation "Run the D-Bus system daemon.")
127 (provision '(dbus-system))
128 (requirement '(user-processes))
129 (start #~(make-forkexec-constructor
130 (list (string-append #$dbus "/bin/dbus-daemon")
131 "--nofork"
132 (string-append "--config-file=" #$conf
133 "/system.conf"))))
134 (stop #~(make-kill-destructor))))))))
135
136(define dbus-root-service-type
137 (service-type (name 'dbus)
138 (extensions
139 (list (service-extension dmd-root-service-type
140 dbus-dmd-service)
141 (service-extension activation-service-type
142 dbus-activation)
143 (service-extension account-service-type
144 (const %dbus-accounts))))
145
146 ;; Extensions consist of lists of packages (representing D-Bus
147 ;; services) that we just concatenate.
148 ;;
149 ;; FIXME: We need 'dbus-daemon-launch-helper' to be
150 ;; setuid-root for auto-activation to work.
151 (compose concatenate)
152
153 ;; The service's parameters field is extended by augmenting
154 ;; its <dbus-configuration> 'services' field.
155 (extend (lambda (config services)
156 (dbus-configuration
157 (inherit config)
158 (services
159 (append (dbus-configuration-services config)
160 services)))))))
161
162(define* (dbus-service #:key (dbus dbus) (services '()))
163 "Return a service that runs the \"system bus\", using @var{dbus}, with
164support for @var{services}.
165
166@uref{http://dbus.freedesktop.org/, D-Bus} is an inter-process communication
167facility. Its system bus is used to allow system services to communicate and
168be notified of system-wide events.
169
170@var{services} must be a list of packages that provide an
171@file{etc/dbus-1/system.d} directory containing additional D-Bus configuration
172and policy files. For example, to allow avahi-daemon to use the system bus,
173@var{services} must be equal to @code{(list avahi)}."
174 (service dbus-root-service-type
175 (dbus-configuration (dbus dbus)
176 (services services))))
177
178;;; dbus.scm ends here
diff --git a/gnu/services/desktop.scm b/gnu/services/desktop.scm
index 346f9169502..cf1ce8269d5 100644
--- a/gnu/services/desktop.scm
+++ b/gnu/services/desktop.scm
@@ -20,7 +20,9 @@
20 20
21(define-module (gnu services desktop) 21(define-module (gnu services desktop)
22 #:use-module (gnu services) 22 #:use-module (gnu services)
23 #:use-module (gnu services dmd)
23 #:use-module (gnu services base) 24 #:use-module (gnu services base)
25 #:use-module (gnu services dbus)
24 #:use-module (gnu services avahi) 26 #:use-module (gnu services avahi)
25 #:use-module (gnu services xorg) 27 #:use-module (gnu services xorg)
26 #:use-module (gnu services networking) 28 #:use-module (gnu services networking)
@@ -31,16 +33,14 @@
31 #:use-module (gnu packages freedesktop) 33 #:use-module (gnu packages freedesktop)
32 #:use-module (gnu packages gnome) 34 #:use-module (gnu packages gnome)
33 #:use-module (gnu packages avahi) 35 #:use-module (gnu packages avahi)
34 #:use-module (gnu packages wicd)
35 #:use-module (gnu packages polkit) 36 #:use-module (gnu packages polkit)
36 #:use-module ((gnu packages linux)
37 #:select (lvm2 fuse alsa-utils crda))
38 #:use-module (guix records) 37 #:use-module (guix records)
38 #:use-module (guix packages)
39 #:use-module (guix store) 39 #:use-module (guix store)
40 #:use-module (guix gexp) 40 #:use-module (guix gexp)
41 #:use-module (srfi srfi-1)
41 #:use-module (ice-9 match) 42 #:use-module (ice-9 match)
42 #:export (dbus-service 43 #:export (upower-service
43 upower-service
44 colord-service 44 colord-service
45 geoclue-application 45 geoclue-application
46 %standard-geoclue-applications 46 %standard-geoclue-applications
@@ -64,133 +64,149 @@
64(define (bool value) 64(define (bool value)
65 (if value "true\n" "false\n")) 65 (if value "true\n" "false\n"))
66 66
67
68;;;
69;;; D-Bus.
70;;;
71 67
72(define (dbus-configuration-directory dbus services) 68(define (wrapped-dbus-service service program variable value)
73 "Return a configuration directory for @var{dbus} that includes the 69 "Return a wrapper for @var{service}, a package containing a D-Bus service,
74@code{etc/dbus-1/system.d} directories of each package listed in 70where @var{program} is wrapped such that environment variable @var{variable}
75@var{services}." 71is set to @var{value} when the bus daemon launches it."
76 (define build 72 (define wrapper
77 #~(begin 73 (program-file (string-append (package-name service) "-program-wrapper")
78 (use-modules (sxml simple) 74 #~(begin
79 (srfi srfi-1)) 75 (setenv #$variable #$value)
80 76 (apply execl (string-append #$service "/" #$program)
81 (define (services->sxml services) 77 (string-append #$service "/" #$program)
82 ;; Return the SXML 'includedir' clauses for DIRS. 78 (cdr (command-line))))))
83 `(busconfig 79
84 ,@(append-map (lambda (dir) 80 (computed-file (string-append (package-name service) "-wrapper")
85 `((includedir 81 #~(begin
86 ,(string-append dir "/etc/dbus-1/system.d")) 82 (use-modules (guix build utils))
87 (servicedir ;for '.service' files 83
88 ,(string-append dir "/share/dbus-1/services")))) 84 (define service-directory
89 services))) 85 "/share/dbus-1/system-services")
90 86
91 (mkdir #$output) 87 (mkdir-p (dirname (string-append #$output
92 (copy-file (string-append #$dbus "/etc/dbus-1/system.conf") 88 service-directory)))
93 (string-append #$output "/system.conf")) 89 (copy-recursively (string-append #$service
94 90 service-directory)
95 ;; The default 'system.conf' has an <includedir> clause for 91 (string-append #$output
96 ;; 'system.d', so create it. 92 service-directory))
97 (mkdir (string-append #$output "/system.d")) 93 (symlink (string-append #$service "/etc") ;for etc/dbus-1
98 94 (string-append #$output "/etc"))
99 ;; 'system-local.conf' is automatically included by the default 95
100 ;; 'system.conf', so this is where we stuff our own things. 96 (for-each (lambda (file)
101 (call-with-output-file (string-append #$output "/system-local.conf") 97 (substitute* file
102 (lambda (port) 98 (("Exec[[:blank:]]*=[[:blank:]]*([[:graph:]]+)(.*)$"
103 (sxml->xml (services->sxml (list #$@services)) 99 _ original-program arguments)
104 port))))) 100 (string-append "Exec=" #$wrapper arguments
105 101 "\n"))))
106 (computed-file "dbus-configuration" build)) 102 (find-files #$output "\\.service$")))
107 103 #:modules '((guix build utils))))
108(define* (dbus-service services #:key (dbus dbus))
109 "Return a service that runs the \"system bus\", using @var{dbus}, with
110support for @var{services}.
111
112@uref{http://dbus.freedesktop.org/, D-Bus} is an inter-process communication
113facility. Its system bus is used to allow system services to communicate and
114be notified of system-wide events.
115
116@var{services} must be a list of packages that provide an
117@file{etc/dbus-1/system.d} directory containing additional D-Bus configuration
118and policy files. For example, to allow avahi-daemon to use the system bus,
119@var{services} must be equal to @code{(list avahi)}."
120 (let ((conf (dbus-configuration-directory dbus services)))
121 (service
122 (documentation "Run the D-Bus system daemon.")
123 (provision '(dbus-system))
124 (requirement '(user-processes))
125 (start #~(make-forkexec-constructor
126 (list (string-append #$dbus "/bin/dbus-daemon")
127 "--nofork"
128 (string-append "--config-file=" #$conf "/system.conf"))))
129 (stop #~(make-kill-destructor))
130 (user-groups (list (user-group
131 (name "messagebus")
132 (system? #t))))
133 (user-accounts (list (user-account
134 (name "messagebus")
135 (group "messagebus")
136 (system? #t)
137 (comment "D-Bus system bus user")
138 (home-directory "/var/run/dbus")
139 (shell
140 #~(string-append #$shadow "/sbin/nologin")))))
141 (activate #~(begin
142 (use-modules (guix build utils))
143
144 (mkdir-p "/var/run/dbus")
145
146 (let ((user (getpwnam "messagebus")))
147 (chown "/var/run/dbus"
148 (passwd:uid user) (passwd:gid user)))
149
150 (unless (file-exists? "/etc/machine-id")
151 (format #t "creating /etc/machine-id...~%")
152 (let ((prog (string-append #$dbus "/bin/dbus-uuidgen")))
153 ;; XXX: We can't use 'system' because the initrd's
154 ;; guile system(3) only works when 'sh' is in $PATH.
155 (let ((pid (primitive-fork)))
156 (if (zero? pid)
157 (call-with-output-file "/etc/machine-id"
158 (lambda (port)
159 (close-fdes 1)
160 (dup2 (port->fdes port) 1)
161 (execl prog)))
162 (waitpid pid))))))))))
163 104
164 105
165;;; 106;;;
166;;; Upower D-Bus service. 107;;; Upower D-Bus service.
167;;; 108;;;
168 109
169(define* (upower-configuration-file #:key watts-up-pro? poll-batteries? 110;; TODO: Export.
170 ignore-lid? use-percentage-for-policy? 111(define-record-type* <upower-configuration>
171 percentage-low percentage-critical 112 upower-configuration make-upower-configuration
172 percentage-action time-low 113 upower-configuration?
173 time-critical time-action 114 (upower upower-configuration-upower
174 critical-power-action) 115 (default upower))
175 "Return an upower-daemon configuration file." 116 (watts-up-pro? upower-configuration-watts-up-pro?)
176 (plain-file "UPower.conf" 117 (poll-batteries? upower-configuration-poll-batteries?)
177 (string-append 118 (ignore-lid? upower-configuration-ignore-lid?)
178 "[UPower]\n" 119 (use-percentage-for-policy? upower-configuration-use-percentage-for-policy?)
179 "EnableWattsUpPro=" (bool watts-up-pro?) 120 (percentage-low upower-configuration-percentage-low)
180 "NoPollBatteries=" (bool (not poll-batteries?)) 121 (percentage-critical upower-configuration-percentage-critical)
181 "IgnoreLid=" (bool ignore-lid?) 122 (percentage-action upower-configuration-percentage-action)
182 "UsePercentageForPolicy=" (bool use-percentage-for-policy?) 123 (time-low upower-configuration-time-low)
183 "PercentageLow=" (number->string percentage-low) "\n" 124 (time-critical upower-configuration-time-critical)
184 "PercentageCritical=" (number->string percentage-critical) "\n" 125 (time-action upower-configuration-time-action)
185 "PercentageAction=" (number->string percentage-action) "\n" 126 (critical-power-action upower-configuration-critical-power-action))
186 "TimeLow=" (number->string time-low) "\n" 127
187 "TimeCritical=" (number->string time-critical) "\n" 128(define* upower-configuration-file
188 "TimeAction=" (number->string time-action) "\n" 129 ;; Return an upower-daemon configuration file.
189 "CriticalPowerAction=" (match critical-power-action 130 (match-lambda
190 ('hybrid-sleep "HybridSleep") 131 (($ <upower-configuration> upower
191 ('hibernate "Hibernate") 132 watts-up-pro? poll-batteries? ignore-lid? use-percentage-for-policy?
192 ('power-off "PowerOff")) 133 percentage-low percentage-critical percentage-action time-low
193 "\n"))) 134 time-critical time-action critical-power-action)
135 (plain-file "UPower.conf"
136 (string-append
137 "[UPower]\n"
138 "EnableWattsUpPro=" (bool watts-up-pro?)
139 "NoPollBatteries=" (bool (not poll-batteries?))
140 "IgnoreLid=" (bool ignore-lid?)
141 "UsePercentageForPolicy=" (bool use-percentage-for-policy?)
142 "PercentageLow=" (number->string percentage-low) "\n"
143 "PercentageCritical=" (number->string percentage-critical) "\n"
144 "PercentageAction=" (number->string percentage-action) "\n"
145 "TimeLow=" (number->string time-low) "\n"
146 "TimeCritical=" (number->string time-critical) "\n"
147 "TimeAction=" (number->string time-action) "\n"
148 "CriticalPowerAction=" (match critical-power-action
149 ('hybrid-sleep "HybridSleep")
150 ('hibernate "Hibernate")
151 ('power-off "PowerOff"))
152 "\n")))))
153
154(define %upower-accounts ;XXX: useful?
155 (list (user-group (name "upower") (system? #t))
156 (user-account
157 (name "upower")
158 (group "upower")
159 (system? #t)
160 (comment "UPower daemon user")
161 (home-directory "/var/empty")
162 (shell #~(string-append #$shadow "/sbin/nologin")))))
163
164(define %upower-activation
165 #~(begin
166 (use-modules (guix build utils))
167 (mkdir-p "/var/lib/upower")
168 (let ((user (getpwnam "upower")))
169 (chown "/var/lib/upower"
170 (passwd:uid user) (passwd:gid user)))))
171
172
173(define (upower-dbus-service config)
174 (list (wrapped-dbus-service (upower-configuration-upower config)
175 "libexec/upowerd"
176 "UPOWER_CONF_FILE_NAME"
177 (upower-configuration-file config))))
178
179(define (upower-dmd-service config)
180 "Return a dmd service for UPower with CONFIG."
181 (let ((upower (upower-configuration-upower config))
182 (config (upower-configuration-file config)))
183 (list (dmd-service
184 (documentation "Run the UPower power and battery monitor.")
185 (provision '(upower-daemon))
186 (requirement '(dbus-system udev))
187
188 (start #~(make-forkexec-constructor
189 (list (string-append #$upower "/libexec/upowerd"))
190 #:environment-variables
191 (list (string-append "UPOWER_CONF_FILE_NAME="
192 #$config))))
193 (stop #~(make-kill-destructor))))))
194
195(define upower-service-type
196 (service-type (name 'upower)
197 (extensions
198 (list (service-extension dbus-root-service-type
199 upower-dbus-service)
200 (service-extension dmd-root-service-type
201 upower-dmd-service)
202 (service-extension account-service-type
203 (const %upower-accounts))
204 (service-extension activation-service-type
205 (const %upower-activation))
206 (service-extension udev-service-type
207 (compose
208 list
209 upower-configuration-upower))))))
194 210
195(define* (upower-service #:key (upower upower) 211(define* (upower-service #:key (upower upower)
196 (watts-up-pro? #f) 212 (watts-up-pro? #f)
@@ -208,90 +224,97 @@ and policy files. For example, to allow avahi-daemon to use the system bus,
208@command{upowerd}}, a system-wide monitor for power consumption and battery 224@command{upowerd}}, a system-wide monitor for power consumption and battery
209levels, with the given configuration settings. It implements the 225levels, with the given configuration settings. It implements the
210@code{org.freedesktop.UPower} D-Bus interface, and is notably used by GNOME." 226@code{org.freedesktop.UPower} D-Bus interface, and is notably used by GNOME."
211 (let ((config (upower-configuration-file 227 (let ((config (upower-configuration
212 #:watts-up-pro? watts-up-pro? 228 (watts-up-pro? watts-up-pro?)
213 #:poll-batteries? poll-batteries? 229 (poll-batteries? poll-batteries?)
214 #:ignore-lid? ignore-lid? 230 (ignore-lid? ignore-lid?)
215 #:use-percentage-for-policy? use-percentage-for-policy? 231 (use-percentage-for-policy? use-percentage-for-policy?)
216 #:percentage-low percentage-low 232 (percentage-low percentage-low)
217 #:percentage-critical percentage-critical 233 (percentage-critical percentage-critical)
218 #:percentage-action percentage-action 234 (percentage-action percentage-action)
219 #:time-low time-low 235 (time-low time-low)
220 #:time-critical time-critical 236 (time-critical time-critical)
221 #:time-action time-action 237 (time-action time-action)
222 #:critical-power-action critical-power-action))) 238 (critical-power-action critical-power-action))))
223 (service 239 (service upower-service-type config)))
224 (documentation "Run the UPower power and battery monitor.")
225 (provision '(upower-daemon))
226 (requirement '(dbus-system udev))
227
228 (start #~(make-forkexec-constructor
229 (list (string-append #$upower "/libexec/upowerd"))
230 #:environment-variables
231 (list (string-append "UPOWER_CONF_FILE_NAME=" #$config))))
232 (stop #~(make-kill-destructor))
233 (activate #~(begin
234 (use-modules (guix build utils))
235 (mkdir-p "/var/lib/upower")
236 (let ((user (getpwnam "upower")))
237 (chown "/var/lib/upower"
238 (passwd:uid user) (passwd:gid user)))))
239
240 (user-groups (list (user-group
241 (name "upower")
242 (system? #t))))
243 (user-accounts (list (user-account
244 (name "upower")
245 (group "upower")
246 (system? #t)
247 (comment "UPower daemon user")
248 (home-directory "/var/empty")
249 (shell
250 #~(string-append #$shadow "/sbin/nologin"))))))))
251 240
252 241
253;;; 242;;;
254;;; Colord D-Bus service. 243;;; Colord D-Bus service.
255;;; 244;;;
256 245
246(define %colord-activation
247 #~(begin
248 (use-modules (guix build utils))
249 (mkdir-p "/var/lib/colord")
250 (let ((user (getpwnam "colord")))
251 (chown "/var/lib/colord"
252 (passwd:uid user) (passwd:gid user)))))
253
254(define %colord-accounts
255 (list (user-group (name "colord") (system? #t))
256 (user-account
257 (name "colord")
258 (group "colord")
259 (system? #t)
260 (comment "colord daemon user")
261 (home-directory "/var/empty")
262 (shell #~(string-append #$shadow "/sbin/nologin")))))
263
264(define (colord-dmd-service colord)
265 "Return a dmd service for COLORD."
266 ;; TODO: Remove when D-Bus activation works.
267 (list (dmd-service
268 (documentation "Run the colord color management service.")
269 (provision '(colord-daemon))
270 (requirement '(dbus-system udev))
271 (start #~(make-forkexec-constructor
272 (list (string-append #$colord "/libexec/colord"))))
273 (stop #~(make-kill-destructor)))))
274
275(define colord-service-type
276 (service-type (name 'colord)
277 (extensions
278 (list (service-extension account-service-type
279 (const %colord-accounts))
280 (service-extension activation-service-type
281 (const %colord-activation))
282 (service-extension dmd-root-service-type
283 colord-dmd-service)
284
285 ;; Colord is a D-Bus service that dbus-daemon can
286 ;; activate.
287 (service-extension dbus-root-service-type list)
288
289 ;; Colord provides "color device" rules for udev.
290 (service-extension udev-service-type list)))))
291
257(define* (colord-service #:key (colord colord)) 292(define* (colord-service #:key (colord colord))
258 "Return a service that runs @command{colord}, a system service with a D-Bus 293 "Return a service that runs @command{colord}, a system service with a D-Bus
259interface to manage the color profiles of input and output devices such as 294interface to manage the color profiles of input and output devices such as
260screens and scanners. It is notably used by the GNOME Color Manager graphical 295screens and scanners. It is notably used by the GNOME Color Manager graphical
261tool. See @uref{http://www.freedesktop.org/software/colord/, the colord web 296tool. See @uref{http://www.freedesktop.org/software/colord/, the colord web
262site} for more information." 297site} for more information."
263 (service 298 (service colord-service-type colord))
264 (documentation "Run the colord color management service.")
265 (provision '(colord-daemon))
266 (requirement '(dbus-system udev))
267
268 (start #~(make-forkexec-constructor
269 (list (string-append #$colord "/libexec/colord"))))
270 (stop #~(make-kill-destructor))
271 (activate #~(begin
272 (use-modules (guix build utils))
273 (mkdir-p "/var/lib/colord")
274 (let ((user (getpwnam "colord")))
275 (chown "/var/lib/colord"
276 (passwd:uid user) (passwd:gid user)))))
277
278 (user-groups (list (user-group
279 (name "colord")
280 (system? #t))))
281 (user-accounts (list (user-account
282 (name "colord")
283 (group "colord")
284 (system? #t)
285 (comment "colord daemon user")
286 (home-directory "/var/empty")
287 (shell
288 #~(string-append #$shadow "/sbin/nologin")))))))
289 299
290 300
291;;; 301;;;
292;;; GeoClue D-Bus service. 302;;; GeoClue D-Bus service.
293;;; 303;;;
294 304
305;; TODO: Export.
306(define-record-type* <geoclue-configuration>
307 geoclue-configuration make-geoclue-configuration
308 geoclue-configuration?
309 (geoclue geoclue-configuration-geoclue
310 (default geoclue))
311 (whitelist geoclue-configuration-whitelist)
312 (wifi-geolocation-url geoclue-configuration-wifi-geolocation-url)
313 (submit-data? geoclue-configuration-submit-data?)
314 (wifi-submission-url geoclue-configuration-wifi-submission-url)
315 (submission-nick geoclue-configuration-submission-nick)
316 (applications geoclue-configuration-applications))
317
295(define* (geoclue-application name #:key (allowed? #t) system? (users '())) 318(define* (geoclue-application name #:key (allowed? #t) system? (users '()))
296 "Configure default GeoClue access permissions for an application. NAME is 319 "Configure default GeoClue access permissions for an application. NAME is
297the Desktop ID of the application, without the .desktop part. If ALLOWED? is 320the Desktop ID of the application, without the .desktop part. If ALLOWED? is
@@ -311,21 +334,67 @@ users are allowed."
311 (geoclue-application "epiphany" #:system? #f) 334 (geoclue-application "epiphany" #:system? #f)
312 (geoclue-application "firefox" #:system? #f))) 335 (geoclue-application "firefox" #:system? #f)))
313 336
314(define* (geoclue-configuration-file #:key whitelist wifi-geolocation-url 337(define* (geoclue-configuration-file config)
315 submit-data?
316 wifi-submission-url submission-nick
317 applications)
318 "Return a geoclue configuration file." 338 "Return a geoclue configuration file."
319 (plain-file "geoclue.conf" 339 (plain-file "geoclue.conf"
320 (string-append 340 (string-append
321 "[agent]\n" 341 "[agent]\n"
322 "whitelist=" (string-join whitelist ";") "\n" 342 "whitelist="
343 (string-join (geoclue-configuration-whitelist config)
344 ";") "\n"
323 "[wifi]\n" 345 "[wifi]\n"
324 "url=" wifi-geolocation-url "\n" 346 "url=" (geoclue-configuration-wifi-geolocation-url config) "\n"
325 "submit-data=" (bool submit-data?) 347 "submit-data=" (bool (geoclue-configuration-submit-data? config))
326 "submission-url=" wifi-submission-url "\n" 348 "submission-url="
327 "submission-nick=" submission-nick "\n" 349 (geoclue-configuration-wifi-submission-url config) "\n"
328 (string-join applications "\n")))) 350 "submission-nick="
351 (geoclue-configuration-submission-nick config)
352 "\n"
353 (string-join (geoclue-configuration-applications config)
354 "\n"))))
355
356(define (geoclue-dbus-service config)
357 (list (wrapped-dbus-service (geoclue-configuration-geoclue config)
358 "libexec/geoclue"
359 "GEOCLUE_CONFIG_FILE"
360 (geoclue-configuration-file config))))
361
362(define (geoclue-dmd-service config)
363 "Return a GeoClue dmd service for CONFIG."
364 ;; TODO: Remove when D-Bus activation works.
365 (let ((geoclue (geoclue-configuration-geoclue config))
366 (config (geoclue-configuration-file config)))
367 (list (dmd-service
368 (documentation "Run the GeoClue location service.")
369 (provision '(geoclue-daemon))
370 (requirement '(dbus-system))
371
372 (start #~(make-forkexec-constructor
373 (list (string-append #$geoclue "/libexec/geoclue"))
374 #:user "geoclue"
375 #:environment-variables
376 (list (string-append "GEOCLUE_CONFIG_FILE=" #$config))))
377 (stop #~(make-kill-destructor))))))
378
379(define %geoclue-accounts
380 (list (user-group (name "geoclue") (system? #t))
381 (user-account
382 (name "geoclue")
383 (group "geoclue")
384 (system? #t)
385 (comment "GeoClue daemon user")
386 (home-directory "/var/empty")
387 (shell "/run/current-system/profile/sbin/nologin"))))
388
389(define geoclue-service-type
390 (service-type (name 'geoclue)
391 (extensions
392 (list (service-extension dbus-root-service-type
393 geoclue-dbus-service)
394 (service-extension dmd-root-service-type
395 geoclue-dmd-service)
396 (service-extension account-service-type
397 (const %geoclue-accounts))))))
329 398
330(define* (geoclue-service #:key (geoclue geoclue) 399(define* (geoclue-service #:key (geoclue geoclue)
331 (whitelist '()) 400 (whitelist '())
@@ -345,70 +414,67 @@ and Epiphany web browsers are able to ask for the user's location, and in the
345case of Icecat and Epiphany, both will ask the user for permission first. See 414case of Icecat and Epiphany, both will ask the user for permission first. See
346@uref{https://wiki.freedesktop.org/www/Software/GeoClue/, the geoclue web 415@uref{https://wiki.freedesktop.org/www/Software/GeoClue/, the geoclue web
347site} for more information." 416site} for more information."
348 (let ((config (geoclue-configuration-file 417 (service geoclue-service-type
349 #:whitelist whitelist 418 (geoclue-configuration
350 #:wifi-geolocation-url wifi-geolocation-url 419 (geoclue geoclue)
351 #:submit-data? submit-data? 420 (whitelist whitelist)
352 #:wifi-submission-url wifi-submission-url 421 (wifi-geolocation-url wifi-geolocation-url)
353 #:submission-nick submission-nick 422 (submit-data? submit-data?)
354 #:applications applications))) 423 (wifi-submission-url wifi-submission-url)
355 (service 424 (submission-nick submission-nick)
356 (documentation "Run the GeoClue location service.") 425 (applications applications))))
357 (provision '(geoclue-daemon))
358 (requirement '(dbus-system))
359
360 (start #~(make-forkexec-constructor
361 (list (string-append #$geoclue "/libexec/geoclue"))
362 #:user "geoclue"
363 #:environment-variables
364 (list (string-append "GEOCLUE_CONFIG_FILE=" #$config))))
365 (stop #~(make-kill-destructor))
366
367 (user-groups (list (user-group
368 (name "geoclue")
369 (system? #t))))
370 (user-accounts (list (user-account
371 (name "geoclue")
372 (group "geoclue")
373 (system? #t)
374 (comment "GeoClue daemon user")
375 (home-directory "/var/empty")
376 (shell
377 "/run/current-system/profile/sbin/nologin")))))))
378 426
379 427
380;;; 428;;;
381;;; Polkit privilege management service. 429;;; Polkit privilege management service.
382;;; 430;;;
383 431
432(define %polkit-accounts
433 (list (user-group (name "polkitd") (system? #t))
434 (user-account
435 (name "polkitd")
436 (group "polkitd")
437 (system? #t)
438 (comment "Polkit daemon user")
439 (home-directory "/var/empty")
440 (shell "/run/current-system/profile/sbin/nologin"))))
441
442(define %polkit-pam-services
443 (list (unix-pam-service "polkit-1")))
444
445(define (polkit-dmd-service polkit)
446 "Return the <dmd-service> for POLKIT."
447 ;; TODO: Remove when D-Bus activation works.
448 (list (dmd-service
449 (documentation "Run the polkit privilege management service.")
450 (provision '(polkit-daemon))
451 (requirement '(dbus-system))
452
453 (start #~(make-forkexec-constructor
454 (list (string-append #$polkit "/lib/polkit-1/polkitd"))))
455 (stop #~(make-kill-destructor)))))
456
457(define polkit-service-type
458 ;; TODO: Make it extensible so it can collect policy files from other
459 ;; services.
460 (service-type (name 'polkit)
461 (extensions
462 (list (service-extension account-service-type
463 (const %polkit-accounts))
464 (service-extension pam-root-service-type
465 (const %polkit-pam-services))
466 (service-extension dbus-root-service-type
467 list)
468 (service-extension dmd-root-service-type
469 polkit-dmd-service)))))
470
384(define* (polkit-service #:key (polkit polkit)) 471(define* (polkit-service #:key (polkit polkit))
385 "Return a service that runs the @command{polkit} privilege management 472 "Return a service that runs the @command{polkit} privilege management
386service. By querying the @command{polkit} service, a privileged system 473service. By querying the @command{polkit} service, a privileged system
387component can know when it should grant additional capabilities to ordinary 474component can know when it should grant additional capabilities to ordinary
388users. For example, an ordinary user can be granted the capability to suspend 475users. For example, an ordinary user can be granted the capability to suspend
389the system if the user is logged in locally." 476the system if the user is logged in locally."
390 (service 477 (service polkit-service-type polkit))
391 (documentation "Run the polkit privilege management service.")
392 (provision '(polkit-daemon))
393 (requirement '(dbus-system))
394
395 (start #~(make-forkexec-constructor
396 (list (string-append #$polkit "/lib/polkit-1/polkitd"))))
397 (stop #~(make-kill-destructor))
398
399 (user-groups (list (user-group
400 (name "polkitd")
401 (system? #t))))
402 (user-accounts (list (user-account
403 (name "polkitd")
404 (group "polkitd")
405 (system? #t)
406 (comment "Polkit daemon user")
407 (home-directory "/var/empty")
408 (shell
409 "/run/current-system/profile/sbin/nologin"))))
410
411 (pam-services (list (unix-pam-service "polkit-1")))))
412 478
413 479
414;;; 480;;;
@@ -418,6 +484,8 @@ the system if the user is logged in locally."
418(define-record-type* <elogind-configuration> elogind-configuration 484(define-record-type* <elogind-configuration> elogind-configuration
419 make-elogind-configuration 485 make-elogind-configuration
420 elogind-configuration 486 elogind-configuration
487 (elogind elogind-package
488 (default elogind))
421 (kill-user-processes? elogind-kill-user-processes? 489 (kill-user-processes? elogind-kill-user-processes?
422 (default #f)) 490 (default #f))
423 (kill-only-users elogind-kill-only-users 491 (kill-only-users elogind-kill-only-users
@@ -547,67 +615,62 @@ the system if the user is logged in locally."
547 ("HybridSleepState" (sleep-list elogind-hybrid-sleep-state)) 615 ("HybridSleepState" (sleep-list elogind-hybrid-sleep-state))
548 ("HybridSleepMode" (sleep-list elogind-hybrid-sleep-mode)))) 616 ("HybridSleepMode" (sleep-list elogind-hybrid-sleep-mode))))
549 617
550(define* (elogind-service #:key (elogind elogind) 618(define (elogind-dmd-service config)
551 (config (elogind-configuration))) 619 "Return a dmd service for elogind, using @var{config}."
620 (let ((config-file (elogind-configuration-file config))
621 (elogind (elogind-package config)))
622 (list (dmd-service
623 (documentation "Run the elogind login and seat management service.")
624 (provision '(elogind))
625 (requirement '(dbus-system))
626
627 (start #~(make-forkexec-constructor
628 (list (string-append #$elogind "/libexec/elogind/elogind"))
629 #:environment-variables
630 (list (string-append "ELOGIND_CONF_FILE=" #$config-file))))
631 (stop #~(make-kill-destructor))))))
632
633(define elogind-service-type
634 (service-type (name 'elogind)
635 (extensions
636 (list (service-extension dmd-root-service-type
637 elogind-dmd-service)
638 (service-extension dbus-root-service-type
639 (compose list elogind-package))
640 (service-extension udev-service-type
641 (compose list elogind-package))
642 ;; TODO: Extend polkit(?) and PAM.
643 ))))
644
645(define* (elogind-service #:key (config (elogind-configuration)))
552 "Return a service that runs the @command{elogind} login and seat management 646 "Return a service that runs the @command{elogind} login and seat management
553service. The @command{elogind} service integrates with PAM to allow other 647service. The @command{elogind} service integrates with PAM to allow other
554system components to know the set of logged-in users as well as their session 648system components to know the set of logged-in users as well as their session
555types (graphical, console, remote, etc.). It can also clean up after users 649types (graphical, console, remote, etc.). It can also clean up after users
556when they log out." 650when they log out."
557 (let ((config-file (elogind-configuration-file config))) 651 (service elogind-service-type config))
558 (service
559 (documentation "Run the elogind login and seat management service.")
560 (provision '(elogind))
561 (requirement '(dbus-system))
562
563 (start #~(make-forkexec-constructor
564 (list (string-append #$elogind "/libexec/elogind/elogind"))
565 #:environment-variables
566 (list (string-append "ELOGIND_CONF_FILE=" #$config-file))))
567 (stop #~(make-kill-destructor)))))
568 652
569 653
570;;; 654;;;
571;;; The default set of desktop services. 655;;; The default set of desktop services.
572;;; 656;;;
657
573(define %desktop-services 658(define %desktop-services
574 ;; List of services typically useful for a "desktop" use case. 659 ;; List of services typically useful for a "desktop" use case.
575 (cons* (slim-service) 660 (cons* (slim-service)
576 661
662 ;; The D-Bus clique.
577 (avahi-service) 663 (avahi-service)
578 (wicd-service) 664 (wicd-service)
579 (upower-service) 665 (upower-service)
580 ;; FIXME: The colord, geoclue, and polkit services could all be
581 ;; bus-activated by default, so they don't run at program startup.
582 ;; However, user creation and /var/lib/colord creation happen at
583 ;; service activation time, so we currently add them to the set of
584 ;; default services.
585 (colord-service) 666 (colord-service)
586 (geoclue-service) 667 (geoclue-service)
587 (polkit-service) 668 (polkit-service)
588 (elogind-service) 669 (elogind-service)
589 (dbus-service (list avahi wicd upower colord geoclue polkit elogind)) 670 (dbus-service)
590 671
591 (ntp-service) 672 (ntp-service)
592 673
593 (map (lambda (service) 674 %base-services))
594 (cond
595 ;; Provide an nscd ready to use nss-mdns.
596 ((memq 'nscd (service-provision service))
597 (nscd-service (nscd-configuration
598 (name-services (list nss-mdns)))))
599
600 ;; Add more rules to udev-service.
601 ;;
602 ;; XXX Keep this in sync with the 'udev-service' call in
603 ;; %base-services. Here we intend only to add 'upower',
604 ;; 'colord', and 'elogind'.
605 ((memq 'udev (service-provision service))
606 (udev-service #:rules
607 (list lvm2 fuse alsa-utils crda
608 upower colord elogind)))
609
610 (else service)))
611 %base-services)))
612 675
613;;; desktop.scm ends here 676;;; desktop.scm ends here
diff --git a/gnu/services/dmd.scm b/gnu/services/dmd.scm
index 618df91c5e3..6020ffc8ebf 100644
--- a/gnu/services/dmd.scm
+++ b/gnu/services/dmd.scm
@@ -22,13 +22,27 @@
22 #:use-module (guix gexp) 22 #:use-module (guix gexp)
23 #:use-module (guix store) 23 #:use-module (guix store)
24 #:use-module (guix monads) 24 #:use-module (guix monads)
25 #:use-module (guix records)
25 #:use-module (guix derivations) ;imported-modules, etc. 26 #:use-module (guix derivations) ;imported-modules, etc.
26 #:use-module (gnu services) 27 #:use-module (gnu services)
28 #:use-module (gnu packages admin)
27 #:use-module (ice-9 match) 29 #:use-module (ice-9 match)
28 #:use-module (srfi srfi-1) 30 #:use-module (srfi srfi-1)
29 #:use-module (srfi srfi-34) 31 #:use-module (srfi srfi-34)
30 #:use-module (srfi srfi-35) 32 #:use-module (srfi srfi-35)
31 #:export (dmd-configuration-file)) 33 #:export (dmd-root-service-type
34 %dmd-root-service
35 dmd-service-type
36
37 dmd-service
38 dmd-service?
39 dmd-service-documentation
40 dmd-service-provision
41 dmd-service-requirement
42 dmd-service-respawn?
43 dmd-service-start
44 dmd-service-stop
45 dmd-service-auto-start?))
32 46
33;;; Commentary: 47;;; Commentary:
34;;; 48;;;
@@ -36,6 +50,68 @@
36;;; 50;;;
37;;; Code: 51;;; Code:
38 52
53
54(define (dmd-boot-gexp services)
55 (mlet %store-monad ((dmd-conf (dmd-configuration-file services)))
56 (return #~(begin
57 ;; Keep track of the booted system.
58 (false-if-exception (delete-file "/run/booted-system"))
59 (symlink (readlink "/run/current-system")
60 "/run/booted-system")
61
62 ;; Close any remaining open file descriptors to be on the safe
63 ;; side. This must be the very last thing we do, because
64 ;; Guile has internal FDs such as 'sleep_pipe' that need to be
65 ;; alive.
66 (let loop ((fd 3))
67 (when (< fd 1024)
68 (false-if-exception (close-fdes fd))
69 (loop (+ 1 fd))))
70
71 ;; Start dmd.
72 (execl (string-append #$dmd "/bin/dmd")
73 "dmd" "--config" #$dmd-conf)))))
74
75(define dmd-root-service-type
76 (service-type
77 (name 'dmd-root)
78 ;; Extending the root dmd service (aka. PID 1) happens by concatenating the
79 ;; list of services provided by the extensions.
80 (compose concatenate)
81 (extend append)
82 (extensions (list (service-extension boot-service-type dmd-boot-gexp)))))
83
84(define %dmd-root-service
85 ;; The root dmd service, aka. PID 1. Its parameter is a list of
86 ;; <dmd-service> objects.
87 (service dmd-root-service-type '()))
88
89(define-syntax-rule (dmd-service-type proc)
90 "Return a <service-type> denoting a simple dmd service--i.e., the type for a
91service that extends DMD-ROOT-SERVICE-TYPE and nothing else."
92 (service-type
93 (name 'some-dmd-service)
94 (extensions
95 (list (service-extension dmd-root-service-type
96 (compose list proc))))))
97
98(define-record-type* <dmd-service>
99 dmd-service make-dmd-service
100 dmd-service?
101 (documentation service-documentation ; string
102 (default "[No documentation.]"))
103 (provision service-provision) ; list of symbols
104 (requirement service-requirement ; list of symbols
105 (default '()))
106 (respawn? service-respawn? ; Boolean
107 (default #t))
108 (start service-start) ; g-expression (procedure)
109 (stop service-stop ; g-expression (procedure)
110 (default #~(const #f)))
111 (auto-start? service-auto-start? ; Boolean
112 (default #t)))
113
114
39(define (assert-no-duplicates services) 115(define (assert-no-duplicates services)
40 "Raise an error if SERVICES provide the same dmd service more than once. 116 "Raise an error if SERVICES provide the same dmd service more than once.
41 117
diff --git a/gnu/services/lirc.scm b/gnu/services/lirc.scm
index 368f2ba2937..6ae622579d7 100644
--- a/gnu/services/lirc.scm
+++ b/gnu/services/lirc.scm
@@ -1,5 +1,6 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2015 Alex Kost <alezost@gmail.com> 2;;; Copyright © 2015 Alex Kost <alezost@gmail.com>
3;;; Copyright © 2015 Ludovic Courtès <ludo@gnu.org>
3;;; 4;;;
4;;; This file is part of GNU Guix. 5;;; This file is part of GNU Guix.
5;;; 6;;;
@@ -18,17 +19,65 @@
18 19
19(define-module (gnu services lirc) 20(define-module (gnu services lirc)
20 #:use-module (gnu services) 21 #:use-module (gnu services)
22 #:use-module (gnu services dmd)
21 #:use-module (gnu packages lirc) 23 #:use-module (gnu packages lirc)
22 #:use-module (guix store)
23 #:use-module (guix gexp) 24 #:use-module (guix gexp)
25 #:use-module (guix records)
26 #:use-module (ice-9 match)
24 #:export (lirc-service)) 27 #:export (lirc-service))
25 28
26;;; Commentary: 29;;; Commentary:
27;;; 30;;;
28;;; LIRC services. 31;;; LIRC service.
29;;; 32;;;
30;;; Code: 33;;; Code:
31 34
35(define-record-type* <lirc-configuration>
36 lirc-configuration make-lirc-configuration
37 lirc-configuation?
38 (lirc lirc-configuration-lirc ;<package>
39 (default lirc))
40 (device lirc-configuration-device) ;string
41 (driver lirc-configuration-driver) ;string
42 (config-file lirc-configuration-file) ;string | file-like object
43 (extra-options lirc-configuration-options ;list of strings
44 (default '())))
45
46(define %lirc-activation
47 #~(begin
48 (use-modules (guix build utils))
49 (mkdir-p "/var/run/lirc")))
50
51(define lirc-dmd-service
52 (match-lambda
53 (($ <lirc-configuration> lirc device driver config-file options)
54 (list (dmd-service
55 (provision '(lircd))
56 (documentation "Run the LIRC daemon.")
57 (requirement '(user-processes))
58 (start #~(make-forkexec-constructor
59 (list (string-append #$lirc "/sbin/lircd")
60 "--nodaemon"
61 #$@(if device
62 #~("--device" #$device)
63 #~())
64 #$@(if driver
65 #~("--driver" #$driver)
66 #~())
67 #$@(if config-file
68 #~(#$config-file)
69 #~())
70 #$@options)))
71 (stop #~(make-kill-destructor)))))))
72
73(define lirc-service-type
74 (service-type (name 'lirc)
75 (extensions
76 (list (service-extension dmd-root-service-type
77 lirc-dmd-service)
78 (service-extension activation-service-type
79 (const %lirc-activation))))))
80
32(define* (lirc-service #:key (lirc lirc) 81(define* (lirc-service #:key (lirc lirc)
33 device driver config-file 82 device driver config-file
34 (extra-options '())) 83 (extra-options '()))
@@ -40,26 +89,11 @@ The daemon will use specified @var{device}, @var{driver} and
40 89
41Finally, @var{extra-options} is a list of additional command-line options 90Finally, @var{extra-options} is a list of additional command-line options
42passed to @command{lircd}." 91passed to @command{lircd}."
43 (service 92 (service lirc-service-type
44 (provision '(lircd)) 93 (lirc-configuration
45 (documentation "Run the LIRC daemon.") 94 (lirc lirc)
46 (requirement '(user-processes)) 95 (device device) (driver driver)
47 (start #~(make-forkexec-constructor 96 (config-file config-file)
48 (list (string-append #$lirc "/sbin/lircd") 97 (extra-options extra-options))))
49 "--nodaemon"
50 #$@(if device
51 #~("--device" #$device)
52 #~())
53 #$@(if driver
54 #~("--driver" #$driver)
55 #~())
56 #$@(if config-file
57 #~(#$config-file)
58 #~())
59 #$@extra-options)))
60 (stop #~(make-kill-destructor))
61 (activate #~(begin
62 (use-modules (guix build utils))
63 (mkdir-p "/var/run/lirc")))))
64 98
65;;; lirc.scm ends here 99;;; lirc.scm ends here
diff --git a/gnu/services/networking.scm b/gnu/services/networking.scm
index 50ffac57962..52a843b54b2 100644
--- a/gnu/services/networking.scm
+++ b/gnu/services/networking.scm
@@ -19,7 +19,10 @@
19 19
20(define-module (gnu services networking) 20(define-module (gnu services networking)
21 #:use-module (gnu services) 21 #:use-module (gnu services)
22 #:use-module (gnu services dmd)
23 #:use-module (gnu services dbus)
22 #:use-module (gnu system shadow) 24 #:use-module (gnu system shadow)
25 #:use-module (gnu system linux) ;PAM
23 #:use-module (gnu packages admin) 26 #:use-module (gnu packages admin)
24 #:use-module (gnu packages linux) 27 #:use-module (gnu packages linux)
25 #:use-module (gnu packages tor) 28 #:use-module (gnu packages tor)
@@ -27,8 +30,9 @@
27 #:use-module (gnu packages ntp) 30 #:use-module (gnu packages ntp)
28 #:use-module (gnu packages wicd) 31 #:use-module (gnu packages wicd)
29 #:use-module (guix gexp) 32 #:use-module (guix gexp)
30 #:use-module (guix store) 33 #:use-module (guix records)
31 #:use-module (srfi srfi-26) 34 #:use-module (srfi srfi-26)
35 #:use-module (ice-9 match)
32 #:export (%facebook-host-aliases 36 #:export (%facebook-host-aliases
33 static-networking-service 37 static-networking-service
34 dhcp-client-service 38 dhcp-client-service
@@ -78,6 +82,72 @@ fe80::1%lo0 www.connect.facebook.net
78fe80::1%lo0 apps.facebook.com\n") 82fe80::1%lo0 apps.facebook.com\n")
79 83
80 84
85(define-record-type* <static-networking>
86 static-networking make-static-networking
87 static-networking?
88 (interface static-networking-interface)
89 (ip static-networking-ip)
90 (gateway static-networking-gateway)
91 (provision static-networking-provision)
92 (name-servers static-networking-name-servers)
93 (net-tools static-networking-net-tools))
94
95(define static-networking-service-type
96 (dmd-service-type
97 (match-lambda
98 (($ <static-networking> interface ip gateway provision
99 name-servers net-tools)
100 (let ((loopback? (memq 'loopback provision)))
101
102 ;; TODO: Eventually replace 'route' with bindings for the appropriate
103 ;; ioctls.
104 (dmd-service
105
106 ;; Unless we're providing the loopback interface, wait for udev to be up
107 ;; and running so that INTERFACE is actually usable.
108 (requirement (if loopback? '() '(udev)))
109
110 (documentation
111 "Bring up the networking interface using a static IP address.")
112 (provision provision)
113 (start #~(lambda _
114 ;; Return #t if successfully started.
115 (let* ((addr (inet-pton AF_INET #$ip))
116 (sockaddr (make-socket-address AF_INET addr 0)))
117 (configure-network-interface #$interface sockaddr
118 (logior IFF_UP
119 #$(if loopback?
120 #~IFF_LOOPBACK
121 0))))
122 #$(if gateway
123 #~(zero? (system* (string-append #$net-tools
124 "/sbin/route")
125 "add" "-net" "default"
126 "gw" #$gateway))
127 #t)
128 #$(if (pair? name-servers)
129 #~(call-with-output-file "/etc/resolv.conf"
130 (lambda (port)
131 (display
132 "# Generated by 'static-networking-service'.\n"
133 port)
134 (for-each (lambda (server)
135 (format port "nameserver ~a~%"
136 server))
137 '#$name-servers)))
138 #t)))
139 (stop #~(lambda _
140 ;; Return #f is successfully stopped.
141 (let ((sock (socket AF_INET SOCK_STREAM 0)))
142 (set-network-interface-flags sock #$interface 0)
143 (close-port sock))
144 (not #$(if gateway
145 #~(system* (string-append #$net-tools
146 "/sbin/route")
147 "del" "-net" "default")
148 #t))))
149 (respawn? #f)))))))
150
81(define* (static-networking-service interface ip 151(define* (static-networking-service interface ip
82 #:key 152 #:key
83 gateway 153 gateway
@@ -87,111 +157,70 @@ fe80::1%lo0 apps.facebook.com\n")
87 "Return a service that starts @var{interface} with address @var{ip}. If 157 "Return a service that starts @var{interface} with address @var{ip}. If
88@var{gateway} is true, it must be a string specifying the default network 158@var{gateway} is true, it must be a string specifying the default network
89gateway." 159gateway."
90 (define loopback? 160 (service static-networking-service-type
91 (memq 'loopback provision)) 161 (static-networking (interface interface) (ip ip)
92 162 (gateway gateway)
93 ;; TODO: Eventually replace 'route' with bindings for the appropriate 163 (provision provision)
94 ;; ioctls. 164 (name-servers name-servers)
95 (service 165 (net-tools net-tools))))
96 166
97 ;; Unless we're providing the loopback interface, wait for udev to be up 167(define dhcp-client-service-type
98 ;; and running so that INTERFACE is actually usable. 168 (dmd-service-type
99 (requirement (if loopback? '() '(udev))) 169 (lambda (dhcp)
100 170 (define dhclient
101 (documentation 171 #~(string-append #$dhcp "/sbin/dhclient"))
102 "Bring up the networking interface using a static IP address.") 172
103 (provision provision) 173 (define pid-file
104 (start #~(lambda _ 174 "/var/run/dhclient.pid")
105 ;; Return #t if successfully started. 175
106 (let* ((addr (inet-pton AF_INET #$ip)) 176 (dmd-service
107 (sockaddr (make-socket-address AF_INET addr 0))) 177 (documentation "Set up networking via DHCP.")
108 (configure-network-interface #$interface sockaddr 178 (requirement '(user-processes udev))
109 (logior IFF_UP 179
110 #$(if loopback? 180 ;; XXX: Running with '-nw' ("no wait") avoids blocking for a minute when
111 #~IFF_LOOPBACK 181 ;; networking is unavailable, but also means that the interface is not up
112 0)))) 182 ;; yet when 'start' completes. To wait for the interface to be ready, one
113 #$(if gateway 183 ;; should instead monitor udev events.
114 #~(zero? (system* (string-append #$net-tools 184 (provision '(networking))
115 "/sbin/route") 185
116 "add" "-net" "default" 186 (start #~(lambda _
117 "gw" #$gateway)) 187 ;; When invoked without any arguments, 'dhclient' discovers all
118 #t) 188 ;; non-loopback interfaces *that are up*. However, the relevant
119 #$(if (pair? name-servers) 189 ;; interfaces are typically down at this point. Thus we perform
120 #~(call-with-output-file "/etc/resolv.conf" 190 ;; our own interface discovery here.
121 (lambda (port) 191 (define valid?
122 (display 192 (negate loopback-network-interface?))
123 "# Generated by 'static-networking-service'.\n" 193 (define ifaces
124 port) 194 (filter valid? (all-network-interface-names)))
125 (for-each (lambda (server) 195
126 (format port "nameserver ~a~%" 196 ;; XXX: Make sure the interfaces are up so that 'dhclient' can
127 server)) 197 ;; actually send/receive over them.
128 '#$name-servers))) 198 (for-each set-network-interface-up ifaces)
129 #t))) 199
130 (stop #~(lambda _ 200 (false-if-exception (delete-file #$pid-file))
131 ;; Return #f is successfully stopped. 201 (let ((pid (fork+exec-command
132 (let ((sock (socket AF_INET SOCK_STREAM 0))) 202 (cons* #$dhclient "-nw"
133 (set-network-interface-flags sock #$interface 0) 203 "-pf" #$pid-file ifaces))))
134 (close-port sock)) 204 (and (zero? (cdr (waitpid pid)))
135 (not #$(if gateway 205 (let loop ()
136 #~(system* (string-append #$net-tools 206 (catch 'system-error
137 "/sbin/route") 207 (lambda ()
138 "del" "-net" "default") 208 (call-with-input-file #$pid-file read))
139 #t)))) 209 (lambda args
140 (respawn? #f))) 210 ;; 'dhclient' returned before PID-FILE was created,
211 ;; so try again.
212 (let ((errno (system-error-errno args)))
213 (if (= ENOENT errno)
214 (begin
215 (sleep 1)
216 (loop))
217 (apply throw args))))))))))
218 (stop #~(make-kill-destructor))))))
141 219
142(define* (dhcp-client-service #:key (dhcp isc-dhcp)) 220(define* (dhcp-client-service #:key (dhcp isc-dhcp))
143 "Return a service that runs @var{dhcp}, a Dynamic Host Configuration 221 "Return a service that runs @var{dhcp}, a Dynamic Host Configuration
144Protocol (DHCP) client, on all the non-loopback network interfaces." 222Protocol (DHCP) client, on all the non-loopback network interfaces."
145 223 (service dhcp-client-service-type dhcp))
146 (define dhclient
147 #~(string-append #$dhcp "/sbin/dhclient"))
148
149 (define pid-file
150 "/var/run/dhclient.pid")
151
152 (service
153 (documentation "Set up networking via DHCP.")
154 (requirement '(user-processes udev))
155
156 ;; XXX: Running with '-nw' ("no wait") avoids blocking for a minute when
157 ;; networking is unavailable, but also means that the interface is not up
158 ;; yet when 'start' completes. To wait for the interface to be ready, one
159 ;; should instead monitor udev events.
160 (provision '(networking))
161
162 (start #~(lambda _
163 ;; When invoked without any arguments, 'dhclient' discovers all
164 ;; non-loopback interfaces *that are up*. However, the relevant
165 ;; interfaces are typically down at this point. Thus we perform
166 ;; our own interface discovery here.
167 (define valid?
168 (negate loopback-network-interface?))
169 (define ifaces
170 (filter valid? (all-network-interface-names)))
171
172 ;; XXX: Make sure the interfaces are up so that 'dhclient' can
173 ;; actually send/receive over them.
174 (for-each set-network-interface-up ifaces)
175
176 (false-if-exception (delete-file #$pid-file))
177 (let ((pid (fork+exec-command
178 (cons* #$dhclient "-nw"
179 "-pf" #$pid-file ifaces))))
180 (and (zero? (cdr (waitpid pid)))
181 (let loop ()
182 (catch 'system-error
183 (lambda ()
184 (call-with-input-file #$pid-file read))
185 (lambda args
186 ;; 'dhclient' returned before PID-FILE was created,
187 ;; so try again.
188 (let ((errno (system-error-errno args)))
189 (if (= ENOENT errno)
190 (begin
191 (sleep 1)
192 (loop))
193 (apply throw args))))))))))
194 (stop #~(make-kill-destructor))))
195 224
196(define %ntp-servers 225(define %ntp-servers
197 ;; Default set of NTP servers. 226 ;; Default set of NTP servers.
@@ -199,19 +228,30 @@ Protocol (DHCP) client, on all the non-loopback network interfaces."
199 "1.pool.ntp.org" 228 "1.pool.ntp.org"
200 "2.pool.ntp.org")) 229 "2.pool.ntp.org"))
201 230
202(define* (ntp-service #:key (ntp ntp) 231
203 (servers %ntp-servers)) 232;;;
204 "Return a service that runs the daemon from @var{ntp}, the 233;;; NTP.
205@uref{http://www.ntp.org, Network Time Protocol package}. The daemon will 234;;;
206keep the system clock synchronized with that of @var{servers}." 235
207 ;; TODO: Add authentication support. 236;; TODO: Export.
208 237(define-record-type* <ntp-configuration>
209 (define config 238 ntp-configuration make-ntp-configuration
210 (string-append "driftfile /var/run/ntp.drift\n" 239 ntp-configuration?
211 (string-join (map (cut string-append "server " <>) 240 (ntp ntp-configuration-ntp
212 servers) 241 (default ntp))
213 "\n") 242 (servers ntp-configuration-servers))
214 " 243
244(define ntp-dmd-service
245 (match-lambda
246 (($ <ntp-configuration> ntp servers)
247 (let ()
248 ;; TODO: Add authentication support.
249 (define config
250 (string-append "driftfile /var/run/ntp.drift\n"
251 (string-join (map (cut string-append "server " <>)
252 servers)
253 "\n")
254 "
215# Disable status queries as a workaround for CVE-2013-5211: 255# Disable status queries as a workaround for CVE-2013-5211:
216# <http://support.ntp.org/bin/view/Main/SecurityNotice#DRDoS_Amplification_Attack_using>. 256# <http://support.ntp.org/bin/view/Main/SecurityNotice#DRDoS_Amplification_Attack_using>.
217restrict default kod nomodify notrap nopeer noquery 257restrict default kod nomodify notrap nopeer noquery
@@ -221,55 +261,154 @@ restrict -6 default kod nomodify notrap nopeer noquery
221restrict 127.0.0.1 261restrict 127.0.0.1
222restrict -6 ::1\n")) 262restrict -6 ::1\n"))
223 263
224 (let ((ntpd.conf (plain-file "ntpd.conf" config))) 264 (define ntpd.conf
225 (service 265 (plain-file "ntpd.conf" config))
226 (provision '(ntpd)) 266
227 (documentation "Run the Network Time Protocol (NTP) daemon.") 267 (list (dmd-service
228 (requirement '(user-processes networking)) 268 (provision '(ntpd))
229 (start #~(make-forkexec-constructor 269 (documentation "Run the Network Time Protocol (NTP) daemon.")
230 (list (string-append #$ntp "/bin/ntpd") "-n" 270 (requirement '(user-processes networking))
231 "-c" #$ntpd.conf 271 (start #~(make-forkexec-constructor
232 "-u" "ntpd"))) 272 (list (string-append #$ntp "/bin/ntpd") "-n"
233 (stop #~(make-kill-destructor)) 273 "-c" #$ntpd.conf "-u" "ntpd")))
234 (user-accounts (list (user-account 274 (stop #~(make-kill-destructor))))))))
235 (name "ntpd") 275
236 (group "nogroup") 276(define %ntp-accounts
237 (system? #t) 277 (list (user-account
238 (comment "NTP daemon user") 278 (name "ntpd")
239 (home-directory "/var/empty") 279 (group "nogroup")
240 (shell 280 (system? #t)
241 #~(string-append #$shadow "/sbin/nologin")))))))) 281 (comment "NTP daemon user")
282 (home-directory "/var/empty")
283 (shell #~(string-append #$shadow "/sbin/nologin")))))
284
285(define ntp-service-type
286 (service-type (name 'ntp)
287 (extensions
288 (list (service-extension dmd-root-service-type
289 ntp-dmd-service)
290 (service-extension account-service-type
291 (const %ntp-accounts))))))
292
293(define* (ntp-service #:key (ntp ntp)
294 (servers %ntp-servers))
295 "Return a service that runs the daemon from @var{ntp}, the
296@uref{http://www.ntp.org, Network Time Protocol package}. The daemon will
297keep the system clock synchronized with that of @var{servers}."
298 (service ntp-service-type
299 (ntp-configuration (ntp ntp) (servers servers))))
300
301
302;;;
303;;; Tor.
304;;;
305
306(define %tor-accounts
307 ;; User account and groups for Tor.
308 (list (user-group (name "tor") (system? #t))
309 (user-account
310 (name "tor")
311 (group "tor")
312 (system? #t)
313 (comment "Tor daemon user")
314 (home-directory "/var/empty")
315 (shell #~(string-append #$shadow "/sbin/nologin")))))
316
317(define (tor-dmd-service tor)
318 "Return a <dmd-service> running TOR."
319 (let ((torrc (plain-file "torrc" "User tor\n")))
320 (list (dmd-service
321 (provision '(tor))
322
323 ;; Tor needs at least one network interface to be up, hence the
324 ;; dependency on 'loopback'.
325 (requirement '(user-processes loopback))
326
327 (start #~(make-forkexec-constructor
328 (list (string-append #$tor "/bin/tor") "-f" #$torrc)))
329 (stop #~(make-kill-destructor))
330 (documentation "Run the Tor anonymous network overlay.")))))
331
332(define tor-service-type
333 (service-type (name 'tor)
334 (extensions
335 (list (service-extension dmd-root-service-type
336 tor-dmd-service)
337 (service-extension account-service-type
338 (const %tor-accounts))))))
242 339
243(define* (tor-service #:key (tor tor)) 340(define* (tor-service #:key (tor tor))
244 "Return a service to run the @uref{https://torproject.org,Tor} daemon. 341 "Return a service to run the @uref{https://torproject.org,Tor} daemon.
245 342
246The daemon runs with the default settings (in particular the default exit 343The daemon runs with the default settings (in particular the default exit
247policy) as the @code{tor} unprivileged user." 344policy) as the @code{tor} unprivileged user."
248 (let ((torrc (plain-file "torrc" "User tor\n"))) 345 (service tor-service-type tor))
249 (service 346
250 (provision '(tor)) 347
251 348;;;
252 ;; Tor needs at least one network interface to be up, hence the 349;;; BitlBee.
253 ;; dependency on 'loopback'. 350;;;
254 (requirement '(user-processes loopback)) 351
255 352(define-record-type* <bitlbee-configuration>
256 (start #~(make-forkexec-constructor 353 bitlbee-configuration make-bitlbee-configuration
257 (list (string-append #$tor "/bin/tor") "-f" #$torrc))) 354 bitlbee-configuration?
258 (stop #~(make-kill-destructor)) 355 (bitlbee bitlbee-configuration-bitlbee
259 356 (default bitlbee))
260 (user-groups (list (user-group 357 (interface bitlbee-configuration-interface)
261 (name "tor") 358 (port bitlbee-configuration-port)
262 (system? #t)))) 359 (extra-settings bitlbee-configuration-extra-settings))
263 (user-accounts (list (user-account 360
264 (name "tor") 361(define bitlbee-dmd-service
265 (group "tor") 362 (match-lambda
266 (system? #t) 363 (($ <bitlbee-configuration> bitlbee interface port extra-settings)
267 (comment "Tor daemon user") 364 (let ((conf (plain-file "bitlbee.conf"
268 (home-directory "/var/empty") 365 (string-append "
269 (shell 366 [settings]
270 #~(string-append #$shadow "/sbin/nologin"))))) 367 User = bitlbee
271 368 ConfigDir = /var/lib/bitlbee
272 (documentation "Run the Tor anonymous network overlay.")))) 369 DaemonInterface = " interface "
370 DaemonPort = " (number->string port) "
371" extra-settings))))
372
373 (list (dmd-service
374 (provision '(bitlbee))
375 (requirement '(user-processes loopback))
376 (start #~(make-forkexec-constructor
377 (list (string-append #$bitlbee "/sbin/bitlbee")
378 "-n" "-F" "-u" "bitlbee" "-c" #$conf)))
379 (stop #~(make-kill-destructor))))))))
380
381(define %bitlbee-accounts
382 ;; User group and account to run BitlBee.
383 (list (user-group (name "bitlbee") (system? #t))
384 (user-account
385 (name "bitlbee")
386 (group "bitlbee")
387 (system? #t)
388 (comment "BitlBee daemon user")
389 (home-directory "/var/empty")
390 (shell #~(string-append #$shadow "/sbin/nologin")))))
391
392(define %bitlbee-activation
393 ;; Activation gexp for BitlBee.
394 #~(begin
395 (use-modules (guix build utils))
396
397 ;; This directory is used to store OTR data.
398 (mkdir-p "/var/lib/bitlbee")
399 (let ((user (getpwnam "bitlbee")))
400 (chown "/var/lib/bitlbee"
401 (passwd:uid user) (passwd:gid user)))))
402
403(define bitlbee-service-type
404 (service-type (name 'bitlbee)
405 (extensions
406 (list (service-extension dmd-root-service-type
407 bitlbee-dmd-service)
408 (service-extension account-service-type
409 (const %bitlbee-accounts))
410 (service-extension activation-service-type
411 (const %bitlbee-activation))))))
273 412
274(define* (bitlbee-service #:key (bitlbee bitlbee) 413(define* (bitlbee-service #:key (bitlbee bitlbee)
275 (interface "127.0.0.1") (port 6667) 414 (interface "127.0.0.1") (port 6667)
@@ -284,57 +423,52 @@ come from any networking interface.
284 423
285In addition, @var{extra-settings} specifies a string to append to the 424In addition, @var{extra-settings} specifies a string to append to the
286configuration file." 425configuration file."
287 (let ((conf (plain-file "bitlbee.conf" 426 (service bitlbee-service-type
288 (string-append " 427 (bitlbee-configuration
289 [settings] 428 (bitlbee bitlbee)
290 User = bitlbee 429 (interface interface) (port port)
291 ConfigDir = /var/lib/bitlbee 430 (extra-settings extra-settings))))
292 DaemonInterface = " interface " 431
293 DaemonPort = " (number->string port) " 432
294" extra-settings)))) 433;;;
295 (service 434;;; Wicd.
296 (provision '(bitlbee)) 435;;;
297 (requirement '(user-processes loopback)) 436
298 (activate #~(begin 437(define %wicd-activation
299 (use-modules (guix build utils)) 438 ;; Activation gexp for Wicd.
300 439 #~(begin
301 ;; This directory is used to store OTR data. 440 (use-modules (guix build utils))
302 (mkdir-p "/var/lib/bitlbee") 441
303 (let ((user (getpwnam "bitlbee"))) 442 (mkdir-p "/etc/wicd")
304 (chown "/var/lib/bitlbee" 443 (let ((file-name "/etc/wicd/dhclient.conf.template.default"))
305 (passwd:uid user) (passwd:gid user))))) 444 (unless (file-exists? file-name)
306 (start #~(make-forkexec-constructor 445 (copy-file (string-append #$wicd file-name)
307 (list (string-append #$bitlbee "/sbin/bitlbee") 446 file-name)))))
308 "-n" "-F" "-u" "bitlbee" "-c" #$conf))) 447
309 (stop #~(make-kill-destructor)) 448(define (wicd-dmd-service wicd)
310 (user-groups (list (user-group (name "bitlbee") (system? #t)))) 449 "Return a dmd service for WICD."
311 (user-accounts (list (user-account 450 (list (dmd-service
312 (name "bitlbee") 451 (documentation "Run the Wicd network manager.")
313 (group "bitlbee") 452 (provision '(networking))
314 (system? #t) 453 (requirement '(user-processes dbus-system loopback))
315 (comment "BitlBee daemon user") 454 (start #~(make-forkexec-constructor
316 (home-directory "/var/empty") 455 (list (string-append #$wicd "/sbin/wicd")
317 (shell #~(string-append #$shadow 456 "--no-daemon")))
318 "/sbin/nologin")))))))) 457 (stop #~(make-kill-destructor)))))
458
459(define wicd-service-type
460 (service-type (name 'wicd)
461 (extensions
462 (list (service-extension dmd-root-service-type
463 wicd-dmd-service)
464 (service-extension dbus-root-service-type
465 list)
466 (service-extension activation-service-type
467 (const %wicd-activation))))))
319 468
320(define* (wicd-service #:key (wicd wicd)) 469(define* (wicd-service #:key (wicd wicd))
321 "Return a service that runs @url{https://launchpad.net/wicd,Wicd}, a network 470 "Return a service that runs @url{https://launchpad.net/wicd,Wicd}, a network
322manager that aims to simplify wired and wireless networking." 471manager that aims to simplify wired and wireless networking."
323 (service 472 (service wicd-service-type wicd))
324 (documentation "Run the Wicd network manager.")
325 (provision '(networking))
326 (requirement '(user-processes dbus-system loopback))
327 (start #~(make-forkexec-constructor
328 (list (string-append #$wicd "/sbin/wicd")
329 "--no-daemon")))
330 (stop #~(make-kill-destructor))
331 (activate
332 #~(begin
333 (use-modules (guix build utils))
334 (mkdir-p "/etc/wicd")
335 (let ((file-name "/etc/wicd/dhclient.conf.template.default"))
336 (unless (file-exists? file-name)
337 (copy-file (string-append #$wicd file-name)
338 file-name)))))))
339 473
340;;; networking.scm ends here 474;;; networking.scm ends here
diff --git a/gnu/services/ssh.scm b/gnu/services/ssh.scm
index 3fa0976054d..d3a6cfb33ac 100644
--- a/gnu/services/ssh.scm
+++ b/gnu/services/ssh.scm
@@ -18,8 +18,9 @@
18 18
19(define-module (gnu services ssh) 19(define-module (gnu services ssh)
20 #:use-module (guix gexp) 20 #:use-module (guix gexp)
21 #:use-module (guix store) 21 #:use-module (guix records)
22 #:use-module (gnu services) 22 #:use-module (gnu services)
23 #:use-module (gnu services dmd)
23 #:use-module (gnu system linux) ; 'pam-service' 24 #:use-module (gnu system linux) ; 'pam-service'
24 #:use-module (gnu packages lsh) 25 #:use-module (gnu packages lsh)
25 #:export (lsh-service)) 26 #:export (lsh-service))
@@ -30,11 +31,32 @@
30;;; 31;;;
31;;; Code: 32;;; Code:
32 33
34;; TODO: Export.
35(define-record-type* <lsh-configuration>
36 lsh-configuration make-lsh-configuration
37 lsh-configuration?
38 (lsh lsh-configuration-lsh
39 (default lsh))
40 (daemonic? lsh-configuration-daemonic?)
41 (host-key lsh-configuration-host-key)
42 (interfaces lsh-configuration-interfaces)
43 (port-number lsh-configuration-port-number)
44 (allow-empty-passwords? lsh-configuration-allow-empty-passwords?)
45 (root-login? lsh-configuration-root-login?)
46 (syslog-output? lsh-configuration-syslog-output?)
47 (pid-file? lsh-configuration-pid-file?)
48 (pid-file lsh-configuration-pid-file)
49 (x11-forwarding? lsh-configuration-x11-forwarding?)
50 (tcp/ip-forwarding? lsh-configuration-tcp/ip-forwarding?)
51 (password-authentication? lsh-configuration-password-authentication?)
52 (public-key-authentication? lsh-configuration-public-key-authentication?)
53 (initialize? lsh-configuration-initialize?))
54
33(define %yarrow-seed 55(define %yarrow-seed
34 "/var/spool/lsh/yarrow-seed-file") 56 "/var/spool/lsh/yarrow-seed-file")
35 57
36(define (activation lsh host-key) 58(define (lsh-initialization lsh host-key)
37 "Return the gexp to activate the LSH service for HOST-KEY." 59 "Return the gexp to initialize the LSH service for HOST-KEY."
38 #~(begin 60 #~(begin
39 (unless (file-exists? #$%yarrow-seed) 61 (unless (file-exists? #$%yarrow-seed)
40 (system* (string-append #$lsh "/bin/lsh-make-seed") 62 (system* (string-append #$lsh "/bin/lsh-make-seed")
@@ -70,6 +92,88 @@
70 (waitpid keygen) 92 (waitpid keygen)
71 (waitpid write-key)))))))))) 93 (waitpid write-key))))))))))
72 94
95(define (lsh-activation config)
96 "Return the activation gexp for CONFIG."
97 #~(begin
98 (use-modules (guix build utils))
99 (mkdir-p "/var/spool/lsh")
100 #$(if (lsh-configuration-initialize? config)
101 (lsh-initialization (lsh-configuration-lsh config)
102 (lsh-configuration-host-key config))
103 #t)))
104
105(define (lsh-dmd-service config)
106 "Return a <dmd-service> for lsh with CONFIG."
107 (define lsh (lsh-configuration-lsh config))
108 (define pid-file (lsh-configuration-pid-file config))
109 (define pid-file? (lsh-configuration-pid-file? config))
110 (define daemonic? (lsh-configuration-daemonic? config))
111 (define interfaces (lsh-configuration-interfaces config))
112
113 (define lsh-command
114 (append
115 (cons #~(string-append #$lsh "/sbin/lshd")
116 (if daemonic?
117 (let ((syslog (if (lsh-configuration-syslog-output? config)
118 '()
119 (list "--no-syslog"))))
120 (cons "--daemonic"
121 (if pid-file?
122 (cons #~(string-append "--pid-file=" #$pid-file)
123 syslog)
124 (cons "--no-pid-file" syslog))))
125 (if pid-file?
126 (list #~(string-append "--pid-file=" #$pid-file))
127 '())))
128 (cons* #~(string-append "--host-key="
129 #$(lsh-configuration-host-key config))
130 #~(string-append "--password-helper=" #$lsh "/sbin/lsh-pam-checkpw")
131 #~(string-append "--subsystems=sftp=" #$lsh "/sbin/sftp-server")
132 "-p" (number->string (lsh-configuration-port-number config))
133 (if (lsh-configuration-password-authentication? config)
134 "--password" "--no-password")
135 (if (lsh-configuration-public-key-authentication? config)
136 "--publickey" "--no-publickey")
137 (if (lsh-configuration-root-login? config)
138 "--root-login" "--no-root-login")
139 (if (lsh-configuration-x11-forwarding? config)
140 "--x11-forward" "--no-x11-forward")
141 (if (lsh-configuration-tcp/ip-forwarding? config)
142 "--tcpip-forward" "--no-tcpip-forward")
143 (if (null? interfaces)
144 '()
145 (list (string-append "--interfaces="
146 (string-join interfaces ",")))))))
147
148 (define requires
149 (if (and daemonic? (lsh-configuration-syslog-output? config))
150 '(networking syslogd)
151 '(networking)))
152
153 (list (dmd-service
154 (documentation "GNU lsh SSH server")
155 (provision '(ssh-daemon))
156 (requirement requires)
157 (start #~(make-forkexec-constructor (list #$@lsh-command)))
158 (stop #~(make-kill-destructor)))))
159
160(define (lsh-pam-services config)
161 "Return a list of <pam-services> for lshd with CONFIG."
162 (list (unix-pam-service
163 "lshd"
164 #:allow-empty-passwords?
165 (lsh-configuration-allow-empty-passwords? config))))
166
167(define lsh-service-type
168 (service-type (name 'lsh)
169 (extensions
170 (list (service-extension dmd-root-service-type
171 lsh-dmd-service)
172 (service-extension pam-root-service-type
173 lsh-pam-services)
174 (service-extension activation-service-type
175 lsh-activation)))))
176
73(define* (lsh-service #:key 177(define* (lsh-service #:key
74 (lsh lsh) 178 (lsh lsh)
75 (daemonic? #t) 179 (daemonic? #t)
@@ -114,58 +218,20 @@ passwords, and @var{root-login?} specifies whether to accept log-ins as
114root. 218root.
115 219
116The other options should be self-descriptive." 220The other options should be self-descriptive."
117 (define lsh-command 221 (service lsh-service-type
118 (append 222 (lsh-configuration (lsh lsh) (daemonic? daemonic?)
119 (cons #~(string-append #$lsh "/sbin/lshd") 223 (host-key host-key) (interfaces interfaces)
120 (if daemonic? 224 (port-number port-number)
121 (let ((syslog (if syslog-output? '() 225 (allow-empty-passwords? allow-empty-passwords?)
122 (list "--no-syslog")))) 226 (root-login? root-login?)
123 (cons "--daemonic" 227 (syslog-output? syslog-output?)
124 (if pid-file? 228 (pid-file? pid-file?) (pid-file pid-file)
125 (cons #~(string-append "--pid-file=" #$pid-file) 229 (x11-forwarding? x11-forwarding?)
126 syslog) 230 (tcp/ip-forwarding? tcp/ip-forwarding?)
127 (cons "--no-pid-file" syslog)))) 231 (password-authentication?
128 (if pid-file? 232 password-authentication?)
129 (list #~(string-append "--pid-file=" #$pid-file)) 233 (public-key-authentication?
130 '()))) 234 public-key-authentication?)
131 (cons* #~(string-append "--host-key=" #$host-key) 235 (initialize? initialize?))))
132 #~(string-append "--password-helper=" #$lsh "/sbin/lsh-pam-checkpw")
133 #~(string-append "--subsystems=sftp=" #$lsh "/sbin/sftp-server")
134 "-p" (number->string port-number)
135 (if password-authentication? "--password" "--no-password")
136 (if public-key-authentication?
137 "--publickey" "--no-publickey")
138 (if root-login?
139 "--root-login" "--no-root-login")
140 (if x11-forwarding?
141 "--x11-forward" "--no-x11-forward")
142 (if tcp/ip-forwarding?
143 "--tcpip-forward" "--no-tcpip-forward")
144 (if (null? interfaces)
145 '()
146 (list (string-append "--interfaces="
147 (string-join interfaces ",")))))))
148
149 (define requires
150 (if (and daemonic? syslog-output?)
151 '(networking syslogd)
152 '(networking)))
153
154 (service
155 (documentation "GNU lsh SSH server")
156 (provision '(ssh-daemon))
157 (requirement requires)
158 (start #~(make-forkexec-constructor (list #$@lsh-command)))
159 (stop #~(make-kill-destructor))
160 (pam-services
161 (list (unix-pam-service
162 "lshd"
163 #:allow-empty-passwords? allow-empty-passwords?)))
164 (activate #~(begin
165 (use-modules (guix build utils))
166 (mkdir-p "/var/spool/lsh")
167 #$(if initialize?
168 (activation lsh host-key)
169 #t)))))
170 236
171;;; ssh.scm ends here 237;;; ssh.scm ends here
diff --git a/gnu/services/web.scm b/gnu/services/web.scm
index c6b4153d05a..84bb30d8fd0 100644
--- a/gnu/services/web.scm
+++ b/gnu/services/web.scm
@@ -1,5 +1,6 @@
1;;; GNU Guix --- Functional package management for GNU 1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2015 David Thompson <davet@gnu.org> 2;;; Copyright © 2015 David Thompson <davet@gnu.org>
3;;; Copyright © 2015 Ludovic Courtès <ludo@gnu.org>
3;;; 4;;;
4;;; This file is part of GNU Guix. 5;;; This file is part of GNU Guix.
5;;; 6;;;
@@ -18,12 +19,13 @@
18 19
19(define-module (gnu services web) 20(define-module (gnu services web)
20 #:use-module (gnu services) 21 #:use-module (gnu services)
22 #:use-module (gnu services dmd)
21 #:use-module (gnu system shadow) 23 #:use-module (gnu system shadow)
22 #:use-module (gnu packages admin) 24 #:use-module (gnu packages admin)
23 #:use-module (gnu packages web) 25 #:use-module (gnu packages web)
24 #:use-module (guix records) 26 #:use-module (guix records)
25 #:use-module (guix store)
26 #:use-module (guix gexp) 27 #:use-module (guix gexp)
28 #:use-module (ice-9 match)
27 #:export (nginx-service)) 29 #:export (nginx-service))
28 30
29;;; Commentary: 31;;; Commentary:
@@ -32,6 +34,14 @@
32;;; 34;;;
33;;; Code: 35;;; Code:
34 36
37(define-record-type* <nginx-configuration>
38 nginx-configuration make-nginx-configuration
39 nginx-configuration?
40 (nginx nginx-configuration-nginx) ;<package>
41 (log-directory nginx-configuration-log-directory) ;string
42 (run-directory nginx-configuration-run-directory) ;string
43 (file nginx-configuration-file)) ;string | file-like
44
35(define (default-nginx-config log-directory run-directory) 45(define (default-nginx-config log-directory run-directory)
36 (plain-file "nginx.conf" 46 (plain-file "nginx.conf"
37 (string-append 47 (string-append
@@ -45,6 +55,58 @@
45 "}\n" 55 "}\n"
46 "events {}\n"))) 56 "events {}\n")))
47 57
58(define %nginx-accounts
59 (list (user-group (name "nginx") (system? #t))
60 (user-account
61 (name "nginx")
62 (group "nginx")
63 (system? #t)
64 (comment "nginx server user")
65 (home-directory "/var/empty")
66 (shell #~(string-append #$shadow "/sbin/nologin")))))
67
68(define nginx-activation
69 (match-lambda
70 (($ <nginx-configuration> nginx log-directory run-directory config-file)
71 #~(begin
72 (use-modules (guix build utils))
73
74 (format #t "creating nginx log directory '~a'~%" #$log-directory)
75 (mkdir-p #$log-directory)
76 (format #t "creating nginx run directory '~a'~%" #$run-directory)
77 (mkdir-p #$run-directory)
78 ;; Check configuration file syntax.
79 (system* (string-append #$nginx "/bin/nginx")
80 "-c" #$config-file "-t")))))
81
82(define nginx-dmd-service
83 (match-lambda
84 (($ <nginx-configuration> nginx log-directory run-directory config-file)
85 (let* ((nginx-binary #~(string-append #$nginx "/sbin/nginx"))
86 (nginx-action
87 (lambda args
88 #~(lambda _
89 (zero?
90 (system* #$nginx-binary "-c" #$config-file #$@args))))))
91
92 ;; TODO: Add 'reload' action.
93 (list (dmd-service
94 (provision '(nginx))
95 (documentation "Run the nginx daemon.")
96 (requirement '(user-processes loopback))
97 (start (nginx-action "-p" run-directory))
98 (stop (nginx-action "-s" "stop"))))))))
99
100(define nginx-service-type
101 (service-type (name 'nginx)
102 (extensions
103 (list (service-extension dmd-root-service-type
104 nginx-dmd-service)
105 (service-extension activation-service-type
106 nginx-activation)
107 (service-extension account-service-type
108 (const %nginx-accounts))))))
109
48(define* (nginx-service #:key (nginx nginx) 110(define* (nginx-service #:key (nginx nginx)
49 (log-directory "/var/log/nginx") 111 (log-directory "/var/log/nginx")
50 (run-directory "/var/run/nginx") 112 (run-directory "/var/run/nginx")
@@ -54,41 +116,9 @@
54 116
55The nginx daemon loads its runtime configuration from CONFIG-FIGLE, stores log 117The nginx daemon loads its runtime configuration from CONFIG-FIGLE, stores log
56files in LOG-DIRECTORY, and stores temporary runtime files in RUN-DIRECTORY." 118files in LOG-DIRECTORY, and stores temporary runtime files in RUN-DIRECTORY."
57 (define nginx-binary 119 (service nginx-service-type
58 #~(string-append #$nginx "/sbin/nginx")) 120 (nginx-configuration
59 121 (nginx nginx)
60 (define (nginx-action . args) 122 (log-directory log-directory)
61 #~(lambda _ 123 (run-directory run-directory)
62 (zero? 124 (file config-file))))
63 (system* #$nginx-binary "-c" #$config-file #$@args))))
64
65 (define activate
66 #~(begin
67 (use-modules (guix build utils))
68 (format #t "creating nginx log directory '~a'~%" #$log-directory)
69 (mkdir-p #$log-directory)
70 (format #t "creating nginx run directory '~a'~%" #$run-directory)
71 (mkdir-p #$run-directory)
72 ;; Check configuration file syntax.
73 (system* #$nginx-binary "-c" #$config-file "-t")))
74
75 (define nologin #~(string-append #$shadow "/sbin/nologin"))
76
77 ;; TODO: Add 'reload' action.
78 (service
79 (provision '(nginx))
80 (documentation "Run the nginx daemon.")
81 (requirement '(user-processes loopback))
82 (start (nginx-action "-p" run-directory))
83 (stop (nginx-action "-s" "stop"))
84 (activate activate)
85 (user-groups (list (user-group
86 (name "nginx")
87 (system? #t))))
88 (user-accounts (list (user-account
89 (name "nginx")
90 (group "nginx")
91 (system? #t)
92 (comment "nginx server user")
93 (home-directory "/var/empty")
94 (shell nologin))))))
diff --git a/gnu/services/xorg.scm b/gnu/services/xorg.scm
index 9c96aab2b8d..812cb3f7254 100644
--- a/gnu/services/xorg.scm
+++ b/gnu/services/xorg.scm
@@ -20,6 +20,7 @@
20(define-module (gnu services xorg) 20(define-module (gnu services xorg)
21 #:use-module (gnu artwork) 21 #:use-module (gnu artwork)
22 #:use-module (gnu services) 22 #:use-module (gnu services)
23 #:use-module (gnu services dmd)
23 #:use-module (gnu system linux) ; 'pam-service' 24 #:use-module (gnu system linux) ; 'pam-service'
24 #:use-module ((gnu packages base) #:select (canonical-package)) 25 #:use-module ((gnu packages base) #:select (canonical-package))
25 #:use-module (gnu packages guile) 26 #:use-module (gnu packages guile)
@@ -212,6 +213,95 @@ which should be passed to this script as the first argument. If not, the
212 ;; contains the actual theme files. 213 ;; contains the actual theme files.
213 "0.x") 214 "0.x")
214 215
216(define-record-type* <slim-configuration>
217 slim-configuration make-slim-configuration
218 slim-configuration?
219 (slim slim-configuration-slim
220 (default slim))
221 (allow-empty-passwords? slim-configuration-allow-empty-passwords?)
222 (auto-login? slim-configuration-auto-login?)
223 (default-user slim-configuration-default-user)
224 (theme slim-configuration-theme)
225 (theme-name slim-configuration-theme-name)
226 (xauth slim-configuration-xauth
227 (default xauth))
228 (dmd slim-configuration-dmd
229 (default dmd))
230 (bash slim-configuration-bash
231 (default bash))
232 (auto-login-session slim-configuration-auto-login-session)
233 (startx slim-configuration-startx))
234
235(define (slim-pam-service config)
236 "Return a PAM service for @command{slim}."
237 (list (unix-pam-service
238 "slim"
239 #:allow-empty-passwords?
240 (slim-configuration-allow-empty-passwords? config))))
241
242(define (slim-dmd-service config)
243 (define slim.cfg
244 (let ((xinitrc (xinitrc #:fallback-session
245 (slim-configuration-auto-login-session config)))
246 (slim (slim-configuration-slim config))
247 (xauth (slim-configuration-xauth config))
248 (startx (slim-configuration-startx config))
249 (dmd (slim-configuration-dmd config))
250 (theme-name (slim-configuration-theme-name config)))
251 (mixed-text-file "slim.cfg" "
252default_path /run/current-system/profile/bin
253default_xserver " startx "
254xserver_arguments :0 vt7
255xauth_path " xauth "/bin/xauth
256authfile /var/run/slim.auth
257
258# The login command. '%session' is replaced by the chosen session name, one
259# of the names specified in the 'sessions' setting: 'wmaker', 'xfce', etc.
260login_cmd exec " xinitrc " %session
261sessiondir /run/current-system/profile/share/xsessions
262session_msg session (F1 to change):
263
264halt_cmd " dmd "/sbin/halt
265reboot_cmd " dmd "/sbin/reboot\n"
266(if (slim-configuration-auto-login? config)
267 (string-append "auto_login yes\ndefault_user "
268 (slim-configuration-default-user config) "\n")
269 "")
270(if theme-name
271 (string-append "current_theme " theme-name "\n")
272 ""))))
273
274 (define theme
275 (slim-configuration-theme config))
276
277 (list (dmd-service
278 (documentation "Xorg display server")
279 (provision '(xorg-server))
280 (requirement '(user-processes host-name udev))
281 (start
282 #~(lambda ()
283 ;; A stale lock file can prevent SLiM from starting, so remove it to
284 ;; be on the safe side.
285 (false-if-exception (delete-file "/var/run/slim.lock"))
286
287 (fork+exec-command
288 (list (string-append #$slim "/bin/slim") "-nodaemon")
289 #:environment-variables
290 (list (string-append "SLIM_CFGFILE=" #$slim.cfg)
291 #$@(if theme
292 (list #~(string-append "SLIM_THEMESDIR=" #$theme))
293 #~())))))
294 (stop #~(make-kill-destructor))
295 (respawn? #t))))
296
297(define slim-service-type
298 (service-type (name 'slim)
299 (extensions
300 (list (service-extension dmd-root-service-type
301 slim-dmd-service)
302 (service-extension pam-root-service-type
303 slim-pam-service)))))
304
215(define* (slim-service #:key (slim slim) 305(define* (slim-service #:key (slim slim)
216 (allow-empty-passwords? #t) auto-login? 306 (allow-empty-passwords? #t) auto-login?
217 (default-user "") 307 (default-user "")
@@ -246,54 +336,14 @@ If @var{theme} is @code{#f}, the use the default log-in theme; otherwise
246@var{theme} must be a gexp denoting the name of a directory containing the 336@var{theme} must be a gexp denoting the name of a directory containing the
247theme to use. In that case, @var{theme-name} specifies the name of the 337theme to use. In that case, @var{theme-name} specifies the name of the
248theme." 338theme."
249 339 (service slim-service-type
250 (define slim.cfg 340 (slim-configuration
251 (let ((xinitrc (xinitrc #:fallback-session auto-login-session))) 341 (slim slim)
252 (mixed-text-file "slim.cfg" " 342 (allow-empty-passwords? allow-empty-passwords?)
253default_path /run/current-system/profile/bin 343 (auto-login? auto-login?) (default-user default-user)
254default_xserver " startx " 344 (theme theme) (theme-name theme-name)
255xserver_arguments :0 vt7 345 (xauth xauth) (dmd dmd) (bash bash)
256xauth_path " xauth "/bin/xauth 346 (auto-login-session auto-login-session)
257authfile /var/run/slim.auth 347 (startx startx))))
258
259# The login command. '%session' is replaced by the chosen session name, one
260# of the names specified in the 'sessions' setting: 'wmaker', 'xfce', etc.
261login_cmd exec " xinitrc " %session
262sessiondir /run/current-system/profile/share/xsessions
263session_msg session (F1 to change):
264
265halt_cmd " dmd "/sbin/halt
266reboot_cmd " dmd "/sbin/reboot\n"
267 (if auto-login?
268 (string-append "auto_login yes\ndefault_user " default-user "\n")
269 "")
270 (if theme-name
271 (string-append "current_theme " theme-name "\n")
272 ""))))
273
274 (service
275 (documentation "Xorg display server")
276 (provision '(xorg-server))
277 (requirement '(user-processes host-name udev))
278 (start
279 #~(lambda ()
280 ;; A stale lock file can prevent SLiM from starting, so remove it
281 ;; to be on the safe side.
282 (false-if-exception (delete-file "/var/run/slim.lock"))
283
284 (fork+exec-command
285 (list (string-append #$slim "/bin/slim") "-nodaemon")
286 #:environment-variables
287 (list (string-append "SLIM_CFGFILE=" #$slim.cfg)
288 #$@(if theme
289 (list #~(string-append "SLIM_THEMESDIR=" #$theme))
290 #~())))))
291 (stop #~(make-kill-destructor))
292 (respawn? #t)
293 (pam-services
294 ;; Tell PAM about 'slim'.
295 (list (unix-pam-service
296 "slim"
297 #:allow-empty-passwords? allow-empty-passwords?)))))
298 348
299;;; xorg.scm ends here 349;;; xorg.scm ends here
diff --git a/gnu/system.scm b/gnu/system.scm
index 5eaafed6ae2..b32d26bc8e6 100644
--- a/gnu/system.scm
+++ b/gnu/system.scm
@@ -87,8 +87,6 @@
87 operating-system-locale-directory 87 operating-system-locale-directory
88 operating-system-boot-script 88 operating-system-boot-script
89 89
90 file-union
91
92 local-host-aliases 90 local-host-aliases
93 %setuid-programs 91 %setuid-programs
94 %base-packages 92 %base-packages
@@ -162,41 +160,6 @@
162 160
163 161
164;;; 162;;;
165;;; Derivation.
166;;;
167
168(define* (file-union name files)
169 "Return a derivation that builds a directory containing all of FILES. Each
170item in FILES must be a list where the first element is the file name to use
171in the new directory, and the second element is a gexp denoting the target
172file."
173 (define builder
174 #~(begin
175 (mkdir #$output)
176 (chdir #$output)
177 #$@(map (match-lambda
178 ((target source)
179 #~(symlink #$source #$target)))
180 files)))
181
182 (gexp->derivation name builder))
183
184(define (directory-union name things)
185 "Return a directory that is the union of THINGS."
186 (match things
187 ((one)
188 ;; Only one thing; return it.
189 (with-monad %store-monad (return one)))
190 (_
191 (gexp->derivation name
192 #~(begin
193 (use-modules (guix build union))
194 (union-build #$output '#$things))
195 #:modules '((guix build union))
196 #:local-build? #t))))
197
198
199;;;
200;;; Services. 163;;; Services.
201;;; 164;;;
202 165
@@ -244,18 +207,7 @@ as 'needed-for-boot'."
244 (string->symbol (mapped-device-target md)))) 207 (string->symbol (mapped-device-target md))))
245 (device-mappings fs)))) 208 (device-mappings fs))))
246 209
247 (map (lambda (fs) 210 (map file-system-service file-systems))
248 (match fs
249 (($ <file-system> device title target type flags opts
250 #f check? create?)
251 (file-system-service device target type
252 #:title title
253 #:requirements (requirements fs)
254 #:check? check?
255 #:create-mount-point? create?
256 #:options opts
257 #:flags flags))))
258 file-systems))
259 211
260(define (mapped-device-user device file-systems) 212(define (mapped-device-user device file-systems)
261 "Return a file system among FILE-SYSTEMS that uses DEVICE, or #f." 213 "Return a file system among FILE-SYSTEMS that uses DEVICE, or #f."
@@ -302,10 +254,11 @@ from the initrd."
302 "Return the list of swap services for OS." 254 "Return the list of swap services for OS."
303 (map swap-service (operating-system-swap-devices os))) 255 (map swap-service (operating-system-swap-devices os)))
304 256
305(define (essential-services os) 257(define* (essential-services os #:key container?)
306 "Return the list of essential services for OS. These are special services 258 "Return the list of essential services for OS. These are special services
307that implement part of what's declared in OS are responsible for low-level 259that implement part of what's declared in OS are responsible for low-level
308bookkeeping." 260bookkeeping. CONTAINER? determines whether to return the list of services for
261a container or that of a \"bare metal\" system."
309 (define known-fs 262 (define known-fs
310 (map file-system-mount-point (operating-system-file-systems os))) 263 (map file-system-mount-point (operating-system-file-systems os)))
311 264
@@ -315,17 +268,36 @@ bookkeeping."
315 (unmount (user-unmount-service known-fs)) 268 (unmount (user-unmount-service known-fs))
316 (swaps (swap-services os)) 269 (swaps (swap-services os))
317 (procs (user-processes-service 270 (procs (user-processes-service
318 (map (compose first service-provision) 271 (map service-parameters other-fs)))
319 other-fs)))
320 (host-name (host-name-service (operating-system-host-name os)))) 272 (host-name (host-name-service (operating-system-host-name os))))
321 (cons* host-name procs root-fs unmount 273 (cons* %boot-service
322 (append other-fs mappings swaps)))) 274
323 275 ;; %DMD-ROOT-SERVICE must come first so that the gexp that execs
324(define (operating-system-services os) 276 ;; dmd comes last in the boot script (XXX).
277 %dmd-root-service %activation-service
278
279 (pam-root-service (operating-system-pam-services os))
280 (account-service (append (operating-system-accounts os)
281 (operating-system-groups os))
282 (operating-system-skeletons os))
283 (operating-system-etc-service os)
284 host-name procs root-fs unmount
285 (service setuid-program-service-type
286 (operating-system-setuid-programs os))
287 (append other-fs mappings swaps
288
289 ;; Add the firmware service, unless we are building for a
290 ;; container.
291 (if container?
292 '()
293 (list (service firmware-service-type
294 (operating-system-firmware os))))))))
295
296(define* (operating-system-services os #:key container?)
325 "Return all the services of OS, including \"internal\" services that do not 297 "Return all the services of OS, including \"internal\" services that do not
326explicitly appear in OS." 298explicitly appear in OS."
327 (append (operating-system-user-services os) 299 (append (operating-system-user-services os)
328 (essential-services os))) 300 (essential-services os #:container? container?)))
329 301
330 302
331;;; 303;;;
@@ -388,28 +360,27 @@ This is the GNU system. Welcome.\n")
388(define (emacs-site-file) 360(define (emacs-site-file)
389 "Return the Emacs 'site-start.el' file. That file contains the necessary 361 "Return the Emacs 'site-start.el' file. That file contains the necessary
390settings for 'guix.el' to work out-of-the-box." 362settings for 'guix.el' to work out-of-the-box."
391 (gexp->file "site-start.el" 363 (scheme-file "site-start.el"
392 #~(progn 364 #~(progn
393 ;; Add the "normal" elisp directory to the search path; 365 ;; Add the "normal" elisp directory to the search path;
394 ;; guix.el may be there. 366 ;; guix.el may be there.
395 (add-to-list 367 (add-to-list
396 'load-path 368 'load-path
397 "/run/current-system/profile/share/emacs/site-lisp") 369 "/run/current-system/profile/share/emacs/site-lisp")
398 370
399 ;; Attempt to load guix.el. 371 ;; Attempt to load guix.el.
400 (require 'guix-init nil t) 372 (require 'guix-init nil t)
401 373
402 ;; Attempt to load geiser. 374 ;; Attempt to load geiser.
403 (require 'geiser-install nil t)))) 375 (require 'geiser-install nil t))))
404 376
405(define (emacs-site-directory) 377(define (emacs-site-directory)
406 "Return the Emacs site directory, aka. /etc/emacs." 378 "Return the Emacs site directory, aka. /etc/emacs."
407 (mlet %store-monad ((file (emacs-site-file))) 379 (computed-file "emacs"
408 (gexp->derivation "emacs" 380 #~(begin
409 #~(begin 381 (mkdir #$output)
410 (mkdir #$output) 382 (chdir #$output)
411 (chdir #$output) 383 (symlink #$(emacs-site-file) "site-start.el"))))
412 (symlink #$file "site-start.el")))))
413 384
414(define (user-shells os) 385(define (user-shells os)
415 "Return the list of all the shells used by the accounts of OS. These may be 386 "Return the list of all the shells used by the accounts of OS. These may be
@@ -417,49 +388,43 @@ gexps or strings."
417 (map user-account-shell (operating-system-accounts os))) 388 (map user-account-shell (operating-system-accounts os)))
418 389
419(define (shells-file shells) 390(define (shells-file shells)
420 "Return a derivation that builds a shell list for use as /etc/shells based 391 "Return a file-like object that builds a shell list for use as /etc/shells
421on SHELLS. /etc/shells is used by xterm, polkit, and other programs." 392based on SHELLS. /etc/shells is used by xterm, polkit, and other programs."
422 (gexp->derivation "shells" 393 (computed-file "shells"
423 #~(begin 394 #~(begin
424 (use-modules (srfi srfi-1)) 395 (use-modules (srfi srfi-1))
425 396
426 (define shells 397 (define shells
427 (delete-duplicates (list #$@shells))) 398 (delete-duplicates (list #$@shells)))
428 399
429 (call-with-output-file #$output 400 (call-with-output-file #$output
430 (lambda (port) 401 (lambda (port)
431 (display "\ 402 (display "\
432/bin/sh 403/bin/sh
433/run/current-system/profile/bin/sh 404/run/current-system/profile/bin/sh
434/run/current-system/profile/bin/bash\n" port) 405/run/current-system/profile/bin/bash\n" port)
435 (for-each (lambda (shell) 406 (for-each (lambda (shell)
436 (display shell port) 407 (display shell port)
437 (newline port)) 408 (newline port))
438 shells)))))) 409 shells))))))
439 410
440(define* (etc-directory #:key 411(define* (operating-system-etc-service os)
441 (locale "C") (timezone "Europe/Paris") 412 "Return a <service> that builds containing the static part of the /etc
442 (issue "Hello!\n") 413directory."
443 (skeletons '()) 414 (let ((login.defs (plain-file "login.defs" "# Empty for now.\n"))
444 (pam-services '()) 415
445 (profile "/run/current-system/profile") 416 (shells (shells-file (user-shells os)))
446 hosts-file nss (shells '()) 417 (emacs (emacs-site-directory))
447 (sudoers-file (plain-file "sudoers" ""))) 418 (issue (plain-file "issue" (operating-system-issue os)))
448 "Return a derivation that builds the static part of the /etc directory." 419 (nsswitch (plain-file "nsswitch.conf"
449 (mlet* %store-monad 420 (name-service-switch->string
450 ((pam.d -> (pam-services->directory pam-services)) 421 (operating-system-name-service-switch os))))
451 (login.defs (text-file "login.defs" "# Empty for now.\n")) 422
452 (shells (shells-file shells)) 423 ;; Startup file for POSIX-compliant login shells, which set system-wide
453 (emacs (emacs-site-directory)) 424 ;; environment variables.
454 (issue (text-file "issue" issue)) 425 (profile (mixed-text-file "profile" "\
455 (nsswitch (text-file "nsswitch.conf" 426export LANG=\"" (operating-system-locale os) "\"
456 (name-service-switch->string nss))) 427export TZ=\"" (operating-system-timezone os) "\"
457
458 ;; Startup file for POSIX-compliant login shells, which set system-wide
459 ;; environment variables.
460 (profile (text-file* "profile" "\
461export LANG=\"" locale "\"
462export TZ=\"" timezone "\"
463export TZDIR=\"" tzdata "/share/zoneinfo\" 428export TZDIR=\"" tzdata "/share/zoneinfo\"
464 429
465# Tell 'modprobe' & co. where to look for modules. 430# Tell 'modprobe' & co. where to look for modules.
@@ -516,7 +481,7 @@ then
516fi 481fi
517")) 482"))
518 483
519 (bashrc (text-file "bashrc" "\ 484 (bashrc (plain-file "bashrc" "\
520# Bash-specific initialization. 485# Bash-specific initialization.
521 486
522# The 'bash-completion' package. 487# The 'bash-completion' package.
@@ -526,25 +491,23 @@ then
526 # completion loader that searches its own completion files as well 491 # completion loader that searches its own completion files as well
527 # as those in ~/.guix-profile and /run/current-system/profile. 492 # as those in ~/.guix-profile and /run/current-system/profile.
528 source /run/current-system/profile/etc/profile.d/bash_completion.sh 493 source /run/current-system/profile/etc/profile.d/bash_completion.sh
529fi\n")) 494fi\n")))
530 (skel -> (skeleton-directory skeletons))) 495 (etc-service
531 (file-union "etc" 496 `(("services" ,#~(string-append #$net-base "/etc/services"))
532 `(("services" ,#~(string-append #$net-base "/etc/services")) 497 ("protocols" ,#~(string-append #$net-base "/etc/protocols"))
533 ("protocols" ,#~(string-append #$net-base "/etc/protocols")) 498 ("rpc" ,#~(string-append #$net-base "/etc/rpc"))
534 ("rpc" ,#~(string-append #$net-base "/etc/rpc")) 499 ("emacs" ,#~#$emacs)
535 ("emacs" ,#~#$emacs) 500 ("login.defs" ,#~#$login.defs)
536 ("pam.d" ,#~#$pam.d) 501 ("issue" ,#~#$issue)
537 ("login.defs" ,#~#$login.defs) 502 ("nsswitch.conf" ,#~#$nsswitch)
538 ("issue" ,#~#$issue) 503 ("shells" ,#~#$shells)
539 ("nsswitch.conf" ,#~#$nsswitch) 504 ("profile" ,#~#$profile)
540 ("skel" ,#~#$skel) 505 ("bashrc" ,#~#$bashrc)
541 ("shells" ,#~#$shells) 506 ("hosts" ,#~#$(or (operating-system-hosts-file os)
542 ("profile" ,#~#$profile) 507 (default-/etc/hosts (operating-system-host-name os))))
543 ("bashrc" ,#~#$bashrc) 508 ("localtime" ,#~(string-append #$tzdata "/share/zoneinfo/"
544 ("hosts" ,#~#$hosts-file) 509 #$(operating-system-timezone os)))
545 ("localtime" ,#~(string-append #$tzdata "/share/zoneinfo/" 510 ("sudoers" ,(operating-system-sudoers-file os))))))
546 #$timezone))
547 ("sudoers" ,sudoers-file)))))
548 511
549(define (operating-system-profile os) 512(define (operating-system-profile os)
550 "Return a derivation that builds the system profile of OS." 513 "Return a derivation that builds the system profile of OS."
@@ -561,18 +524,14 @@ fi\n"))
561 (home-directory "/root"))) 524 (home-directory "/root")))
562 525
563(define (operating-system-accounts os) 526(define (operating-system-accounts os)
564 "Return the user accounts for OS, including an obligatory 'root' account." 527 "Return the user accounts for OS, including an obligatory 'root' account,
565 (define users 528and excluding accounts requested by services."
566 ;; Make sure there's a root account. 529 ;; Make sure there's a root account.
567 (if (find (lambda (user) 530 (if (find (lambda (user)
568 (and=> (user-account-uid user) zero?)) 531 (and=> (user-account-uid user) zero?))
569 (operating-system-users os)) 532 (operating-system-users os))
570 (operating-system-users os) 533 (operating-system-users os)
571 (cons %root-account (operating-system-users os)))) 534 (cons %root-account (operating-system-users os))))
572
573 (append users
574 (append-map service-user-accounts
575 (operating-system-services os))))
576 535
577(define (maybe-string->file file-name thing) 536(define (maybe-string->file file-name thing)
578 "If THING is a string, return a <plain-file> with THING as its content. 537 "If THING is a string, return a <plain-file> with THING as its content.
@@ -607,31 +566,9 @@ use 'plain-file' instead~%")
607 566
608(define (operating-system-etc-directory os) 567(define (operating-system-etc-directory os)
609 "Return that static part of the /etc directory of OS." 568 "Return that static part of the /etc directory of OS."
610 (mlet* %store-monad 569 (etc-directory
611 ((services -> (operating-system-services os)) 570 (fold-services (operating-system-services os)
612 (pam-services -> 571 #:target-type etc-service-type)))
613 ;; Services known to PAM.
614 (append (operating-system-pam-services os)
615 (append-map service-pam-services services)))
616 (profile-drv (operating-system-profile os))
617 (skeletons (operating-system-skeletons os))
618 (/etc/hosts (maybe-file->monadic
619 "hosts"
620 (or (operating-system-hosts-file os)
621 (default-/etc/hosts (operating-system-host-name os)))))
622 (shells -> (user-shells os)))
623 (etc-directory #:pam-services pam-services
624 #:skeletons skeletons
625 #:issue (operating-system-issue os)
626 #:locale (operating-system-locale os)
627 #:nss (operating-system-name-service-switch os)
628 #:timezone (operating-system-timezone os)
629 #:hosts-file /etc/hosts
630 #:shells shells
631 #:sudoers-file (maybe-string->file
632 "sudoers"
633 (operating-system-sudoers-file os))
634 #:profile profile-drv)))
635 572
636(define %setuid-programs 573(define %setuid-programs
637 ;; Default set of setuid-root programs. 574 ;; Default set of setuid-root programs.
@@ -652,176 +589,23 @@ use 'plain-file' instead~%")
652root ALL=(ALL) ALL 589root ALL=(ALL) ALL
653%wheel ALL=(ALL) ALL\n")) 590%wheel ALL=(ALL) ALL\n"))
654 591
655(define (user-group->gexp group)
656 "Turn GROUP, a <user-group> object, into a list-valued gexp suitable for
657'active-groups'."
658 #~(list #$(user-group-name group)
659 #$(user-group-password group)
660 #$(user-group-id group)
661 #$(user-group-system? group)))
662
663(define (user-account->gexp account)
664 "Turn ACCOUNT, a <user-account> object, into a list-valued gexp suitable for
665'activate-users'."
666 #~`(#$(user-account-name account)
667 #$(user-account-uid account)
668 #$(user-account-group account)
669 #$(user-account-supplementary-groups account)
670 #$(user-account-comment account)
671 #$(user-account-home-directory account)
672 ,#$(user-account-shell account) ; this one is a gexp
673 #$(user-account-password account)
674 #$(user-account-system? account)))
675
676(define (modprobe-wrapper)
677 "Return a wrapper for the 'modprobe' command that knows where modules live.
678
679This wrapper is typically invoked by the Linux kernel ('call_modprobe', in
680kernel/kmod.c), a situation where the 'LINUX_MODULE_DIRECTORY' environment
681variable is not set---hence the need for this wrapper."
682 (let ((modprobe "/run/current-system/profile/bin/modprobe"))
683 (gexp->script "modprobe"
684 #~(begin
685 (setenv "LINUX_MODULE_DIRECTORY"
686 "/run/booted-system/kernel/lib/modules")
687 (apply execl #$modprobe
688 (cons #$modprobe (cdr (command-line))))))))
689
690(define* (operating-system-activation-script os #:key container?) 592(define* (operating-system-activation-script os #:key container?)
691 "Return the activation script for OS---i.e., the code that \"activates\" the 593 "Return the activation script for OS---i.e., the code that \"activates\" the
692stateful part of OS, including user accounts and groups, special directories, 594stateful part of OS, including user accounts and groups, special directories,
693etc." 595etc."
694 (define %modules 596 (let* ((services (operating-system-services os #:container? container?))
695 '((gnu build activation) 597 (activation (fold-services services
696 (gnu build linux-boot) 598 #:target-type activation-service-type)))
697 (gnu build linux-modules) 599 (activation-service->script activation)))
698 (gnu build file-systems)
699 (guix build utils)
700 (guix build syscalls)
701 (guix elf)))
702
703 (define (service-activations services)
704 ;; Return the activation scripts for SERVICES.
705 (let ((gexps (filter-map service-activate services)))
706 (sequence %store-monad (map (cut gexp->file "activate-service.scm" <>)
707 gexps))))
708
709 (mlet* %store-monad ((services -> (operating-system-services os))
710 (actions (service-activations services))
711 (etc (operating-system-etc-directory os))
712 (modules (imported-modules %modules))
713 (compiled (compiled-modules %modules))
714 (modprobe (modprobe-wrapper))
715 (firmware (directory-union
716 "firmware" (operating-system-firmware os)))
717 (accounts -> (operating-system-accounts os)))
718 (define setuid-progs
719 (operating-system-setuid-programs os))
720
721 (define user-specs
722 (map user-account->gexp accounts))
723
724 (define groups
725 (append (operating-system-groups os)
726 (append-map service-user-groups services)))
727
728 (define group-specs
729 (map user-group->gexp groups))
730
731 (assert-valid-users/groups accounts groups)
732
733 (gexp->file "activate"
734 #~(begin
735 (eval-when (expand load eval)
736 ;; Make sure 'use-modules' below succeeds.
737 (set! %load-path (cons #$modules %load-path))
738 (set! %load-compiled-path
739 (cons #$compiled %load-compiled-path)))
740
741 (use-modules (gnu build activation))
742
743 ;; Make sure /bin/sh is valid and current.
744 (activate-/bin/sh
745 (string-append #$(canonical-package bash)
746 "/bin/sh"))
747
748 ;; Populate /etc.
749 (activate-etc #$etc)
750
751 ;; Add users and user groups.
752 (setenv "PATH"
753 (string-append #$(@ (gnu packages admin) shadow)
754 "/sbin"))
755 (activate-users+groups (list #$@user-specs)
756 (list #$@group-specs))
757
758 ;; Activate setuid programs.
759 (activate-setuid-programs (list #$@setuid-progs))
760
761 ;; Tell the kernel to use our 'modprobe' command.
762 (activate-modprobe #$modprobe)
763
764 ;; Tell the kernel where firmware is, unless we are
765 ;; activating a container.
766 #$@(if container?
767 #~()
768 ;; Tell the kernel where firmware is.
769 #~((activate-firmware
770 (string-append #$firmware "/lib/firmware"))
771 ;; Let users debug their own processes!
772 (activate-ptrace-attach)))
773
774 ;; Run the services' activation snippets.
775 ;; TODO: Use 'load-compiled'.
776 (for-each primitive-load '#$actions)
777
778 ;; Set up /run/current-system.
779 (activate-current-system)))))
780 600
781(define* (operating-system-boot-script os #:key container?) 601(define* (operating-system-boot-script os #:key container?)
782 "Return the boot script for OS---i.e., the code started by the initrd once 602 "Return the boot script for OS---i.e., the code started by the initrd once
783we're running in the final root. When CONTAINER? is true, skip all 603we're running in the final root. When CONTAINER? is true, skip all
784hardware-related operations as necessary when booting a Linux container." 604hardware-related operations as necessary when booting a Linux container."
785 (mlet* %store-monad ((services -> (operating-system-services os)) 605 (let* ((services (operating-system-services os #:container? container?))
786 (activate (operating-system-activation-script os)) 606 (boot (fold-services services)))
787 (dmd-conf (dmd-configuration-file services))) 607 ;; BOOT is the script as a monadic value.
788 (gexp->file "boot" 608 (service-parameters boot)))
789 #~(begin
790 (use-modules (guix build utils))
791
792 ;; Clean out /tmp and /var/run.
793 ;;
794 ;; XXX This needs to happen before service activations, so
795 ;; it has to be here, but this also implicitly assumes
796 ;; that /tmp and /var/run are on the root partition.
797 (false-if-exception (delete-file-recursively "/tmp"))
798 (false-if-exception (delete-file-recursively "/var/run"))
799 (false-if-exception (mkdir "/tmp"))
800 (false-if-exception (chmod "/tmp" #o1777))
801 (false-if-exception (mkdir "/var/run"))
802 (false-if-exception (chmod "/var/run" #o755))
803
804 ;; Activate the system.
805 ;; TODO: Use 'load-compiled'.
806 (primitive-load #$activate)
807
808 ;; Keep track of the booted system.
809 (false-if-exception (delete-file "/run/booted-system"))
810 (symlink (readlink "/run/current-system")
811 "/run/booted-system")
812
813 ;; Close any remaining open file descriptors to be on the
814 ;; safe side. This must be the very last thing we do,
815 ;; because Guile has internal FDs such as 'sleep_pipe'
816 ;; that need to be alive.
817 (let loop ((fd 3))
818 (when (< fd 1024)
819 (false-if-exception (close-fdes fd))
820 (loop (+ 1 fd))))
821
822 ;; Start dmd.
823 (execl (string-append #$dmd "/bin/dmd")
824 "dmd" "--config" #$dmd-conf)))))
825 609
826(define (operating-system-root-file-system os) 610(define (operating-system-root-file-system os)
827 "Return the root file system of OS." 611 "Return the root file system of OS."
@@ -908,19 +692,20 @@ this file is the reconstruction of GRUB menu entries for old configurations."
908 "Return a derivation that builds OS." 692 "Return a derivation that builds OS."
909 (mlet* %store-monad 693 (mlet* %store-monad
910 ((profile (operating-system-profile os)) 694 ((profile (operating-system-profile os))
911 (etc (operating-system-etc-directory os)) 695 (etc -> (operating-system-etc-directory os))
912 (boot (operating-system-boot-script os)) 696 (boot (operating-system-boot-script os))
913 (kernel -> (operating-system-kernel os)) 697 (kernel -> (operating-system-kernel os))
914 (initrd (operating-system-initrd-file os)) 698 (initrd (operating-system-initrd-file os))
915 (locale (operating-system-locale-directory os)) 699 (locale (operating-system-locale-directory os))
916 (params (operating-system-parameters-file os))) 700 (params (operating-system-parameters-file os)))
917 (file-union "system" 701 (lower-object
918 `(("boot" ,#~#$boot) 702 (file-union "system"
919 ("kernel" ,#~#$kernel) 703 `(("boot" ,#~#$boot)
920 ("parameters" ,#~#$params) 704 ("kernel" ,#~#$kernel)
921 ("initrd" ,initrd) 705 ("parameters" ,#~#$params)
922 ("profile" ,#~#$profile) 706 ("initrd" ,initrd)
923 ("locale" ,#~#$locale) ;used by libc 707 ("profile" ,#~#$profile)
924 ("etc" ,#~#$etc))))) 708 ("locale" ,#~#$locale) ;used by libc
709 ("etc" ,#~#$etc))))))
925 710
926;;; system.scm ends here 711;;; system.scm ends here
diff --git a/gnu/system/install.scm b/gnu/system/install.scm
index 560d64b5d4f..a91c5c3533f 100644
--- a/gnu/system/install.scm
+++ b/gnu/system/install.scm
@@ -24,6 +24,7 @@
24 #:use-module (guix monads) 24 #:use-module (guix monads)
25 #:use-module ((guix store) #:select (%store-prefix)) 25 #:use-module ((guix store) #:select (%store-prefix))
26 #:use-module (guix profiles) 26 #:use-module (guix profiles)
27 #:use-module (gnu services dmd)
27 #:use-module (gnu packages admin) 28 #:use-module (gnu packages admin)
28 #:use-module (gnu packages bash) 29 #:use-module (gnu packages bash)
29 #:use-module (gnu packages linux) 30 #:use-module (gnu packages linux)
@@ -159,68 +160,74 @@ current store is on a RAM disk."
159 (mount "/.rw-store" #$(%store-prefix) "" MS_MOVE) 160 (mount "/.rw-store" #$(%store-prefix) "" MS_MOVE)
160 (rmdir "/.rw-store")))))) 161 (rmdir "/.rw-store"))))))
161 162
163(define cow-store-service-type
164 (dmd-service-type
165 (lambda _
166 (dmd-service
167 (requirement '(root-file-system user-processes))
168 (provision '(cow-store))
169 (documentation
170 "Make the store copy-on-write, with writes going to \
171the given target.")
172
173 ;; This is meant to be explicitly started by the user.
174 (auto-start? #f)
175
176 (start #~(case-lambda
177 ((target)
178 #$(make-cow-store #~target)
179 target)
180 (else
181 ;; Do nothing, and mark the service as stopped.
182 #f)))
183 (stop #~(lambda (target)
184 ;; Delete the temporary directory, but leave everything
185 ;; mounted as there may still be processes using it since
186 ;; 'user-processes' doesn't depend on us. The 'user-unmount'
187 ;; service will unmount TARGET eventually.
188 (delete-file-recursively
189 (string-append target #$%backing-directory))))))))
190
162(define (cow-store-service) 191(define (cow-store-service)
163 "Return a service that makes the store copy-on-write, such that writes go to 192 "Return a service that makes the store copy-on-write, such that writes go to
164the user's target storage device rather than on the RAM disk." 193the user's target storage device rather than on the RAM disk."
165 ;; See <http://bugs.gnu.org/18061> for the initial report. 194 ;; See <http://bugs.gnu.org/18061> for the initial report.
166 (service 195 (service cow-store-service-type 'mooooh!))
167 (requirement '(root-file-system user-processes)) 196
168 (provision '(cow-store)) 197
169 (documentation 198(define (/etc/configuration-files _)
170 "Make the store copy-on-write, with writes going to \ 199 "Return a list of tuples representing configuration templates to add to
171the given target.") 200/etc."
172 201 (define (file f)
173 ;; This is meant to be explicitly started by the user. 202 (local-file (search-path %load-path
174 (auto-start? #f) 203 (string-append "gnu/system/examples/" f))))
175 204
176 (start #~(case-lambda 205 (define directory
177 ((target) 206 (computed-file "configuration-templates"
178 #$(make-cow-store #~target) 207 #~(begin
179 target) 208 (mkdir #$output)
180 (else 209 (for-each (lambda (file target)
181 ;; Do nothing, and mark the service as stopped. 210 (copy-file file
182 #f))) 211 (string-append #$output "/"
183 (stop #~(lambda (target) 212 target)))
184 ;; Delete the temporary directory, but leave everything 213 '(#$(file "bare-bones.tmpl")
185 ;; mounted as there may still be processes using it 214 #$(file "desktop.tmpl"))
186 ;; since 'user-processes' doesn't depend on us. The 215 '("bare-bones.scm"
187 ;; 'user-unmount' service will unmount TARGET 216 "desktop.scm"))
188 ;; eventually. 217 #t)
189 (delete-file-recursively 218 #:modules '((guix build utils))))
190 (string-append target #$%backing-directory)))))) 219
191 220 `(("configuration" ,directory)))
192(define (configuration-template-service) 221
193 "Return a dummy service whose purpose is to install an operating system 222(define configuration-template-service-type
194configuration template file in the installation system." 223 (service-type (name 'configuration-template)
195 224 (extensions
196 (define search 225 (list (service-extension etc-service-type
197 (cut search-path %load-path <>)) 226 /etc/configuration-files)))))
198 (define templates 227
199 (map (match-lambda 228(define %configuration-template-service
200 ((file '-> target) 229 (service configuration-template-service-type #t))
201 (list (local-file (search file))
202 (string-append "/etc/configuration/" target))))
203 '(("gnu/system/examples/bare-bones.tmpl" -> "bare-bones.scm")
204 ("gnu/system/examples/desktop.tmpl" -> "desktop.scm"))))
205
206 (service
207 (requirement '(root-file-system))
208 (provision '(os-config-template))
209 (documentation
210 "This dummy service installs an OS configuration template.")
211 (start #~(const #t))
212 (stop #~(const #f))
213 (activate
214 #~(begin
215 (use-modules (ice-9 match)
216 (guix build utils))
217 230
218 (mkdir-p "/etc/configuration")
219 (for-each (match-lambda
220 ((file target)
221 (unless (file-exists? target)
222 (copy-file file target))))
223 '#$templates)))))
224 231
225(define %nscd-minimal-caches 232(define %nscd-minimal-caches
226 ;; Minimal in-memory caching policy for nscd. 233 ;; Minimal in-memory caching policy for nscd.
@@ -262,7 +269,7 @@ You have been warned. Thanks for being so brave.
262 (login-program (log-to-info)))) 269 (login-program (log-to-info))))
263 270
264 ;; Documentation add-on. 271 ;; Documentation add-on.
265 (configuration-template-service) 272 %configuration-template-service
266 273
267 ;; A bunch of 'root' ttys. 274 ;; A bunch of 'root' ttys.
268 (normal-tty "tty3") 275 (normal-tty "tty3")
@@ -276,7 +283,7 @@ You have been warned. Thanks for being so brave.
276 ;; The build daemon. Register the hydra.gnu.org key as trusted. 283 ;; The build daemon. Register the hydra.gnu.org key as trusted.
277 ;; This allows the installation process to use substitutes by 284 ;; This allows the installation process to use substitutes by
278 ;; default. 285 ;; default.
279 (guix-service #:authorize-hydra-key? #t) 286 (guix-service (guix-configuration (authorize-key? #t)))
280 287
281 ;; Start udev so that useful device nodes are available. 288 ;; Start udev so that useful device nodes are available.
282 ;; Use device-mapper rules for cryptsetup & co; enable the CRDA for 289 ;; Use device-mapper rules for cryptsetup & co; enable the CRDA for
diff --git a/gnu/system/linux.scm b/gnu/system/linux.scm
index 10e72e905a5..cd14bc97be0 100644
--- a/gnu/system/linux.scm
+++ b/gnu/system/linux.scm
@@ -20,6 +20,7 @@
20 #:use-module (guix records) 20 #:use-module (guix records)
21 #:use-module (guix derivations) 21 #:use-module (guix derivations)
22 #:use-module (guix gexp) 22 #:use-module (guix gexp)
23 #:use-module (gnu services)
23 #:use-module (ice-9 match) 24 #:use-module (ice-9 match)
24 #:use-module (srfi srfi-1) 25 #:use-module (srfi srfi-1)
25 #:use-module (srfi srfi-26) 26 #:use-module (srfi srfi-26)
@@ -28,7 +29,10 @@
28 pam-entry 29 pam-entry
29 pam-services->directory 30 pam-services->directory
30 unix-pam-service 31 unix-pam-service
31 base-pam-services)) 32 base-pam-services
33
34 pam-root-service-type
35 pam-root-service))
32 36
33;;; Commentary: 37;;; Commentary:
34;;; 38;;;
@@ -98,8 +102,8 @@ dumped in /etc/pam.d/NAME, where NAME is the name of SERVICE."
98 102
99 (mkdir #$output) 103 (mkdir #$output)
100 (for-each (match-lambda 104 (for-each (match-lambda
101 ((name file) 105 ((name file)
102 (symlink file (string-append #$output "/" name)))) 106 (symlink file (string-append #$output "/" name))))
103 107
104 ;; Since <pam-service> objects cannot be compared with 108 ;; Since <pam-service> objects cannot be compared with
105 ;; 'equal?' since they contain gexps, which contain 109 ;; 'equal?' since they contain gexps, which contain
@@ -188,4 +192,24 @@ authenticate to run COMMAND."
188 '("useradd" "userdel" "usermod" 192 '("useradd" "userdel" "usermod"
189 "groupadd" "groupdel" "groupmod")))) 193 "groupadd" "groupdel" "groupmod"))))
190 194
195
196;;;
197;;; PAM root service.
198;;;
199
200(define (/etc-entry services)
201 `(("pam.d" ,(pam-services->directory services))))
202
203(define pam-root-service-type
204 (service-type (name 'pam)
205 (extensions (list (service-extension etc-service-type
206 /etc-entry)))
207 (compose concatenate)
208 (extend append)))
209
210(define (pam-root-service base)
211 "The \"root\" PAM service, which collects <pam-service> instance and turns
212them into a /etc/pam.d directory, including the <pam-service> listed in BASE."
213 (service pam-root-service-type base))
214
191;;; linux.scm ends here 215;;; linux.scm ends here
diff --git a/gnu/system/shadow.scm b/gnu/system/shadow.scm
index ddd5f668743..3f49c1fc9f0 100644
--- a/gnu/system/shadow.scm
+++ b/gnu/system/shadow.scm
@@ -22,12 +22,14 @@
22 #:use-module (guix store) 22 #:use-module (guix store)
23 #:use-module (guix sets) 23 #:use-module (guix sets)
24 #:use-module (guix ui) 24 #:use-module (guix ui)
25 #:use-module (gnu services)
25 #:use-module ((gnu system file-systems) 26 #:use-module ((gnu system file-systems)
26 #:select (%tty-gid)) 27 #:select (%tty-gid))
27 #:use-module ((gnu packages admin) 28 #:use-module ((gnu packages admin)
28 #:select (shadow)) 29 #:select (shadow))
29 #:use-module (gnu packages bash) 30 #:use-module (gnu packages bash)
30 #:use-module (gnu packages guile-wm) 31 #:use-module (gnu packages guile-wm)
32 #:use-module (srfi srfi-1)
31 #:use-module (srfi srfi-26) 33 #:use-module (srfi srfi-26)
32 #:use-module (srfi srfi-34) 34 #:use-module (srfi srfi-34)
33 #:use-module (srfi srfi-35) 35 #:use-module (srfi srfi-35)
@@ -54,7 +56,9 @@
54 skeleton-directory 56 skeleton-directory
55 %base-groups 57 %base-groups
56 %base-user-accounts 58 %base-user-accounts
57 assert-valid-users/groups)) 59
60 account-service-type
61 account-service))
58 62
59;;; Commentary: 63;;; Commentary:
60;;; 64;;;
@@ -87,31 +91,32 @@
87 (system? user-group-system? ; Boolean 91 (system? user-group-system? ; Boolean
88 (default #f))) 92 (default #f)))
89 93
94
90(define %base-groups 95(define %base-groups
91 ;; Default set of groups. 96 ;; Default set of groups.
92 (let-syntax ((system-group (syntax-rules () 97 (let-syntax ((system-group (syntax-rules ()
93 ((_ args ...) 98 ((_ args ...)
94 (user-group (system? #t) args ...))))) 99 (user-group (system? #t) args ...)))))
95 (list (system-group (name "root") (id 0)) 100 (list (system-group (name "root") (id 0))
96 (system-group (name "wheel")) ; root-like users 101 (system-group (name "wheel")) ; root-like users
97 (system-group (name "users")) ; normal users 102 (system-group (name "users")) ; normal users
98 (system-group (name "nogroup")) ; for daemons etc. 103 (system-group (name "nogroup")) ; for daemons etc.
99 104
100 ;; The following groups are conventionally used by things like udev to 105 ;; The following groups are conventionally used by things like udev to
101 ;; control access to hardware devices. 106 ;; control access to hardware devices.
102 (system-group (name "tty") (id %tty-gid)) 107 (system-group (name "tty") (id %tty-gid))
103 (system-group (name "dialout")) 108 (system-group (name "dialout"))
104 (system-group (name "kmem")) 109 (system-group (name "kmem"))
105 (system-group (name "input")) ; input devices, from udev 110 (system-group (name "input")) ; input devices, from udev
106 (system-group (name "video")) 111 (system-group (name "video"))
107 (system-group (name "audio")) 112 (system-group (name "audio"))
108 (system-group (name "netdev")) ; used in avahi-dbus.conf 113 (system-group (name "netdev")) ; used in avahi-dbus.conf
109 (system-group (name "lp")) 114 (system-group (name "lp"))
110 (system-group (name "disk")) 115 (system-group (name "disk"))
111 (system-group (name "floppy")) 116 (system-group (name "floppy"))
112 (system-group (name "cdrom")) 117 (system-group (name "cdrom"))
113 (system-group (name "tape")) 118 (system-group (name "tape"))
114 (system-group (name "kvm"))))) ; for /dev/kvm 119 (system-group (name "kvm"))))) ; for /dev/kvm
115 120
116(define %base-user-accounts 121(define %base-user-accounts
117 ;; List of standard user accounts. Note that "root" is a special case, so 122 ;; List of standard user accounts. Note that "root" is a special case, so
@@ -224,4 +229,81 @@ of user '~a' is undeclared")
224 (user-account-supplementary-groups user))) 229 (user-account-supplementary-groups user)))
225 users))) 230 users)))
226 231
232
233;;;
234;;; Service.
235;;;
236
237(define (user-group->gexp group)
238 "Turn GROUP, a <user-group> object, into a list-valued gexp suitable for
239'active-groups'."
240 #~(list #$(user-group-name group)
241 #$(user-group-password group)
242 #$(user-group-id group)
243 #$(user-group-system? group)))
244
245(define (user-account->gexp account)
246 "Turn ACCOUNT, a <user-account> object, into a list-valued gexp suitable for
247'activate-users'."
248 #~`(#$(user-account-name account)
249 #$(user-account-uid account)
250 #$(user-account-group account)
251 #$(user-account-supplementary-groups account)
252 #$(user-account-comment account)
253 #$(user-account-home-directory account)
254 ,#$(user-account-shell account) ; this one is a gexp
255 #$(user-account-password account)
256 #$(user-account-system? account)))
257
258(define (account-activation accounts+groups)
259 "Return a gexp that activates ACCOUNTS+GROUPS, a list of <user-account> and
260<user-group> objects. Raise an error if a user account refers to a undefined
261group."
262 (define accounts
263 (filter user-account? accounts+groups))
264
265 (define user-specs
266 (map user-account->gexp accounts))
267
268 (define groups
269 (filter user-group? accounts+groups))
270
271 (define group-specs
272 (map user-group->gexp groups))
273
274 (assert-valid-users/groups accounts groups)
275
276 ;; Add users and user groups.
277 #~(begin
278 (setenv "PATH"
279 (string-append #$(@ (gnu packages admin) shadow) "/sbin"))
280 (activate-users+groups (list #$@user-specs)
281 (list #$@group-specs))))
282
283(define (etc-skel arguments)
284 "Filter out among ARGUMENTS things corresponding to skeletons, and return
285the /etc/skel directory for those."
286 (let ((skels (filter pair? arguments)))
287 `(("skel" ,(skeleton-directory skels)))))
288
289(define account-service-type
290 (service-type (name 'account)
291
292 ;; Concatenate <user-account>, <user-group>, and skeleton
293 ;; lists.
294 (compose concatenate)
295 (extend append)
296
297 (extensions
298 (list (service-extension activation-service-type
299 account-activation)
300 (service-extension etc-service-type
301 etc-skel)))))
302
303(define (account-service accounts+groups skeletons)
304 "Return a <service> that takes care of user accounts and user groups, with
305ACCOUNTS+GROUPS as its initial list of accounts and groups."
306 (service account-service-type
307 (append skeletons accounts+groups)))
308
227;;; shadow.scm ends here 309;;; shadow.scm ends here
diff --git a/po/guix/POTFILES.in b/po/guix/POTFILES.in
index c0f169eca4c..af46dac0e06 100644
--- a/po/guix/POTFILES.in
+++ b/po/guix/POTFILES.in
@@ -1,6 +1,7 @@
1# List of source files which contain translatable strings. 1# List of source files which contain translatable strings.
2# This should be source files of the various tools, and not package modules. 2# This should be source files of the various tools, and not package modules.
3gnu/packages.scm 3gnu/packages.scm
4gnu/services.scm
4gnu/system.scm 5gnu/system.scm
5gnu/services/dmd.scm 6gnu/services/dmd.scm
6gnu/system/shadow.scm 7gnu/system/shadow.scm
diff --git a/tests/services.scm b/tests/services.scm
new file mode 100644
index 00000000000..b4e2cb0b305
--- /dev/null
+++ b/tests/services.scm
@@ -0,0 +1,91 @@
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2015 Ludovic Courtès <ludo@gnu.org>
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (test-services)
20 #:use-module (gnu services)
21 #:use-module (srfi srfi-1)
22 #:use-module (srfi srfi-26)
23 #:use-module (srfi srfi-34)
24 #:use-module (srfi srfi-64))
25
26(test-begin "services")
27
28(test-equal "fold-services"
29 ;; Make sure 'fold-services' returns the right result. The numbers come
30 ;; from services of type T3; 'xyz 60' comes from the service of type T2,
31 ;; where 60 = 15 × 4 = (1 + 2 + 3 + 4 + 5) × 4.
32 '(initial-value 5 4 3 2 1 xyz 60)
33 (let* ((t1 (service-type (name 't1) (extensions '())
34 (compose concatenate)
35 (extend cons)))
36 (t2 (service-type (name 't2)
37 (extensions
38 (list (service-extension t1
39 (cut list 'xyz <>))))
40 (compose (cut reduce + 0 <>))
41 (extend *)))
42 (t3 (service-type (name 't3)
43 (extensions
44 (list (service-extension t2 identity)
45 (service-extension t1 list)))))
46 (r (fold-services (cons* (service t1 'initial-value)
47 (service t2 4)
48 (map (lambda (x)
49 (service t3 x))
50 (iota 5 1)))
51 #:target-type t1)))
52 (and (eq? (service-kind r) t1)
53 (service-parameters r))))
54
55(test-assert "fold-services, ambiguity"
56 (let* ((t1 (service-type (name 't1) (extensions '())
57 (compose concatenate)
58 (extend cons)))
59 (t2 (service-type (name 't2)
60 (extensions
61 (list (service-extension t1 list)))))
62 (s (service t2 42)))
63 (guard (c ((ambiguous-target-service-error? c)
64 (and (eq? (ambiguous-target-service-error-target-type c)
65 t1)
66 (eq? (ambiguous-target-service-error-service c)
67 s))))
68 (fold-services (list (service t1 'first)
69 (service t1 'second)
70 s)
71 #:target-type t1)
72 #f)))
73
74(test-assert "fold-services, missing target"
75 (let* ((t1 (service-type (name 't1) (extensions '())))
76 (t2 (service-type (name 't2)
77 (extensions
78 (list (service-extension t1 list)))))
79 (s (service t2 42)))
80 (guard (c ((missing-target-service-error? c)
81 (and (eq? (missing-target-service-error-target-type c)
82 t1)
83 (eq? (missing-target-service-error-service c)
84 s))))
85 (fold-services (list s) #:target-type t1)
86 #f)))
87
88(test-end)
89
90
91(exit (= (test-runner-fail-count (test-runner-current)) 0))