Subject: Re: [bug#28769] [PATCH] gnu: services: Add php-fpm.

From: nee <[email-masked]>
Date: Tue, 24 Oct 2017 00:26:35 +0200

Hello again,
I fixed the empty log-file. Here are the updated patches.

The problem was this line:
>+                        "--nodaemonize" "-p" "/var" "--fpm-config"

When --nodaemonize is set, the log is printed to stdout instead.
I also removed -p "/var". It is not required when the log, pid and
socket locations are defined in the config.
Both were caused, because I didn't use pid-files from the start.

Happy Hacking!
From 894b6e3476bbf7c38427dd80438badba64830a98 Mon Sep 17 00:00:00 2001
From: nee <[email-masked]>
Date: Mon, 9 Oct 2017 23:02:05 +0200
Subject: [PATCH 1/2] guix: utils: add version-major.

* guix/utils.scm (version-major): New procedure.
---
 guix/utils.scm | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/guix/utils.scm b/guix/utils.scm
index de4aa6531..cec209a8f 100644
--- a/guix/utils.scm
+++ b/guix/utils.scm
@@ -72,6 +72,7 @@
             version>=?
             version-prefix
             version-major+minor
+            version-major
             guile-version>?
             string-replace-substring
             arguments-from-environment-variable
@@ -488,6 +489,10 @@ For example, (version-prefix \"2.1.47.4.23\" 3) returns \"2.1.47\""
 minor version numbers from version-string."
   (version-prefix version-string 2))
 
+(define (version-major version-string)
+  "Return the major version number as string from the version-string."
+  (version-prefix version-string 1))
+
 (define (version>? a b)
   "Return #t when A denotes a version strictly newer than B."
   (eq? '> (version-compare a b)))
-- 
2.14.1

From 62d1d7627e8a83015abfc9d055a44333dfa0d46e Mon Sep 17 00:00:00 2001
From: nee <[email-masked]>
Date: Mon, 9 Oct 2017 23:06:05 +0200
Subject: [PATCH 2/2] gnu: services: Add php-fpm.

* gnu/services/web.scm (<php-fpm-configuration>,
  <php-fpm-process-manager-configuration>): New record types.
  (php-fpm-configuration?,
   php-fpm-process-manager-configuration?,
   php-fpm-service-type,
   nginx-php-location): New procedures.
* doc/guix.texi (Web-Services): Document php-fpm service.
---
 doc/guix.texi        | 137 +++++++++++++++++++++++++++-
 gnu/services/web.scm | 248 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 382 insertions(+), 3 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index f0a59a6b4..280ab9930 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -40,7 +40,8 @@ Copyright @copyright{} 2017 Christopher Allan Webber@*
 Copyright @copyright{} 2017 Marius Bakke@*
 Copyright @copyright{} 2017 Hartmut Goebel@*
 Copyright @copyright{} 2017 Maxim Cournoyer@*
-Copyright @copyright{} 2017 Tobias Geerinckx-Rice
+Copyright @copyright{} 2017 Tobias Geerinckx-Rice@*
+Copyright @copyright{} 2017 nee
 
 Permission is granted to copy, distribute and/or modify this document
 under the terms of the GNU Free Documentation License, Version 1.3 or
@@ -14529,6 +14530,140 @@ capability also has to be configured on the front-end as well.
 @end deftp

+@cindex php-fpm
+PHP-FPM (FastCGI Process Manager) is an alternative PHP FastCGI implementation
+with some additional features useful for sites of any size.
+
+These features include:
+@itemize @bullet
+@item Adaptive process spawning
+@item Basic statistics (ala Apache's mod_status)
+@item Advanced process management with graceful stop/start
+@item Ability to start workers with different uid/gid/chroot/environment
+and different php.ini (replaces safe_mode)
+@item Stdout & stderr logging
+@item Emergency restart in case of accidental opcode cache destruction
+@item Accelerated upload support
+@item Support for a "slowlog"
+@item Enhancements to FastCGI, such as fastcgi_finish_request() -
+a special function to finish request & flush all data while continuing to do
+something time-consuming (video converting, stats processing, etc.)
+@end itemize
+... and much more.
+
+@defvr {Scheme Variable} php-fpm-service-type
+A Service type for @code{php-fpm}.
+@end defvr
+
+@deftp {Data Type} php-fpm-configuration
+Data Type for php-fpm service configuration.
+@table @asis
+@item @code {socket} (default: @code{(string-append "/var/run/php" (version-major (package-version php)) "-fpm.sock")})
+The address on which to accept FastCGI requests.  Valid syntaxes are:
+@table @asis
+@item @code{"ip.add.re.ss:port"}
+Listen on a TCP socket to a specific address on a specific port.
+@item @code{"port"}
+Listen on a TCP socket to all addresses on a specific port.
+@item @code{"/path/to/unix/socket"}
+Listen on a unix socket.
+@end table
+
+@item @code {user} (default: @code{php-fpm})
+User who will own the php worker processes.
+@item @code {group} (default: @code{php-fpm})
+Group of the worker processes.
+@item @code {socket-user} (default: @code{php-fpm})
+User who can speak to the php-fpm socket.
+@item @code {socket-group} (default: @code{php-fpm})
+Group that can speak to the php-fpm socket.
+@item @code {pid-file} (default: @code{(string-append "/var/log/php" (version-major (package-version php)) "-fpm.pid")})
+The process id of the php-fpm process is written to this file
+once the service has started.
+@item @code {log-file} (default: @code{(string-append "/var/log/php" (version-major (package-version php)) "-fpm.log")})
+Log for the php-fpm master process.
+@item @code {process-manager} (default: @code{(php-fpm-dynamic-process-manager-configuration)})
+Detailed settings for the php-fpm process manager.
+Must be either:
+@table @asis
+@item @code{<php-fpm-dynamic-process-manager-configuration>}
+@item @code{<php-fpm-static-process-manager-configuration>}
+@item @code{<php-fpm-on-demand-process-manager-configuration>}
+@end table
+@item @code {display-errors} (default @code{#f})
+Determines wether php errors and warning should be sent to clients
+and displayed in their browsers.
+This is useful for local php development, but a security risk for public sites,
+as error messages can reveal passwords and personal data.
+@item @code {workers-logfile} (default @code{(string-append "/var/log/php" (version-major (package-version php)) "-fpm.www.log")})
+This file will log the @code{stderr} outputs of php worker processes.
+Can be set to @code{#f} to disable logging.
+@item @code {file} (default @code{#f})
+An optional override of the whole configuration.
+You can use the @code{mixed-text-file} function or an absolute filepath for it.
+@end table
+@end deftp
+
+@deftp {Data type} php-fpm-dynamic-process-manager-configuration
+Data Type for the @code{dynamic} php-fpm process manager.
+With the @code{dynamic} process manager spare worker processes are kept around
+based on it's configured limits.
+@table @asis
+@item @code {max-children} (default: @code{5})
+Maximum of worker processes.
+@item @code {start-servers} (default: @code{2})
+How many worker processes should be started on start-up.
+@item @code {min-spare-servers} (default: @code{1})
+How many spare worker processes should be kept around at minimum.
+@item @code {max-spare-servers} (default: @code{3})
+How many spare worker processes should be kept around at maximum.
+@end table
+@end deftp
+
+@deftp {Data type} php-fpm-static-process-manager-configuration
+Data Type for the @code{static} php-fpm process manager.
+With the @code{static} process manager an unchanging number
+of worker processes is created.
+@table @asis
+@item @code {max-children} (default: @code{5})
+Maximum of worker processes.
+@end table
+@end deftp
+
+@deftp {Data type} php-fpm-on-demand-process-manager-configuration
+Data Type for the @code{on-demand} php-fpm process manager.
+With the @code{on-demand} process manager worker processes are only created
+as requests arrive.
+@table @asis
+@item @code {max-children} (default: @code{5})
+Maximum of worker processes.
+@item @code {process-idle-timeout} (default: @code{10})
+The time in seconds after which a process with no requests is killed.
+@end table
+@end deftp
+
+
+@defvr {Scheme Variable} nginx-php-fpm-location
+A helper function to quickly add php to an @code{nginx-server-configuration}.
+@end defvr
+
+A simple services setup for nginx with php can look like this:
+@example
+(services (cons* (dhcp-client-service)
+                 (service php-fpm-service-type)
+                 (service nginx-service-type
+                          (nginx-server-configuration
+                           (server-name '("example.com"))
+                           (root "/srv/http/")
+                           (locations
+                            (list (nginx-php-location)))
+                           (https-port #f)
+                           (ssl-certificate #f)
+                           (ssl-certificate-key #f)))
+                 %base-services))
+@end example
+
+
 @node DNS Services
 @subsubsection DNS Services
 @cindex DNS (domain name system)
diff --git a/gnu/services/web.scm b/gnu/services/web.scm
index 9d713003c..d30f3af2a 100644
--- a/gnu/services/web.scm
+++ b/gnu/services/web.scm
@@ -4,6 +4,7 @@
 ;;; Copyright © 2016 ng0 <[email-masked]>
 ;;; Copyright © 2016, 2017 Julien Lepiller <[email-masked]>
 ;;; Copyright © 2017 Christopher Baines <[email-masked]>
+;;; Copyright © 2017 nee <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -26,8 +27,11 @@
   #:use-module (gnu system shadow)
   #:use-module (gnu packages admin)
   #:use-module (gnu packages web)
+  #:use-module (gnu packages php)
   #:use-module (guix records)
   #:use-module (guix gexp)
+  #:use-module ((guix utils) #:select (version-major))
+  #:use-module ((guix packages) #:select (package-version))
   #:use-module (srfi srfi-1)
   #:use-module (ice-9 match)
   #:export (<nginx-configuration>
@@ -76,7 +80,49 @@
 
             fcgiwrap-configuration
             fcgiwrap-configuration?
-            fcgiwrap-service-type))
+            fcgiwrap-service-type
+
+            <php-fpm-configuration>
+            php-fpm-configuration
+            make-php-fpm-configuration
+            php-fpm-configuration?
+            php-fpm-configuration-php
+            php-fpm-configuration-socket
+            php-fpm-configuration-user
+            php-fpm-configuration-group
+            php-fpm-configuration-socket-user
+            php-fpm-configuration-socket-group
+            php-fpm-configuration-pid-file
+            php-fpm-configuration-log-file
+            php-fpm-configuration-process-manager
+            php-fpm-configuration-display-errors
+            php-fpm-configuration-workers-log-file
+            php-fpm-configuration-file
+            
+            <php-fpm-dynamic-process-manager-configuration>
+            php-fpm-dynamic-process-manager-configuration
+            make-php-fpm-dynamic-process-manager-configuration
+            php-fpm-dynamic-process-manager-configuration?
+            php-fpm-dynamic-process-manager-configuration-max-children
+            php-fpm-dynamic-process-manager-configuration-start-servers
+            php-fpm-dynamic-process-manager-configuration-min-spare-servers
+            php-fpm-dynamic-process-manager-configuration-max-spare-servers
+            
+            <php-fpm-static-process-manager-configuration>
+            php-fpm-static-process-manager-configuration
+            make-php-fpm-static-process-manager-configuration
+            php-fpm-static-process-manager-configuration?
+            php-fpm-static-process-manager-configuration-max-children
+            
+            <php-fpm-static-process-manager-configuration>
+            php-fpm-on-demand-process-manager-configuration
+            make-php-fpm-on-demand-process-manager-configuration
+            php-fpm-on-demand-process-manager-configuration?
+            php-fpm-on-demand-process-manager-configuration-max-children
+            php-fpm-on-demand-process-manager-configuration-process-idle-timeout
+
+            php-fpm-service-type
+            nginx-php-location))
 
 ;;; Commentary:
 ;;;
@@ -256,10 +302,12 @@ of index files."
           "events {}\n")))
 
 (define %nginx-accounts
-  (list (user-group (name "nginx") (system? #t))
+  (list (user-group (name "php-fpm") (system? #t))
+        (user-group (name "nginx") (system? #t))
         (user-account
          (name "nginx")
          (group "nginx")
+         (supplementary-groups '("php-fpm"))
          (system? #t)
          (comment "nginx server user")
          (home-directory "/var/empty")
@@ -385,3 +433,199 @@ of index files."
 		       (service-extension account-service-type
                                           fcgiwrap-accounts)))
                 (default-value (fcgiwrap-configuration))))
+
+(define-record-type* <php-fpm-configuration> php-fpm-configuration
+  make-php-fpm-configuration
+  php-fpm-configuration?
+  (php              php-fpm-configuration-php ;<package>
+                    (default php))
+  (socket           php-fpm-configuration-socket
+                    (default (string-append "/var/run/php"
+                                            (version-major (package-version php))
+                                            "-fpm.sock")))
+  (user             php-fpm-configuration-user
+                    (default "php-fpm"))
+  (group            php-fpm-configuration-group
+                    (default "php-fpm"))
+  (socket-user      php-fpm-configuration-socket-user
+                    (default "php-fpm"))
+  (socket-group     php-fpm-configuration-socket-group
+                    (default "php-fpm"))
+  (pid-file         php-fpm-configuration-pid-file
+                    (default (string-append "/var/run/php"
+                                            (version-major (package-version php))
+                                            "-fpm.pid")))
+  (log-file         php-fpm-configuration-log-file
+                    (default (string-append "/var/log/php"
+                                            (version-major (package-version php))
+                                            "-fpm.log")))
+  (process-manager  php-fpm-configuration-process-manager
+                    (default (php-fpm-dynamic-process-manager-configuration)))
+  (display-errors   php-fpm-configuration-display-errors
+                    (default #f))
+  (workers-log-file php-fpm-configuration-workers-log-file
+                    (default (string-append "/var/log/php"
+                                            (version-major (package-version php))
+                                            "-fpm.www.log")))
+  (file             php-fpm-configuration-file ;#f | file-like
+                    (default #f)))
+
+(define-record-type* <php-fpm-dynamic-process-manager-configuration>
+  php-fpm-dynamic-process-manager-configuration
+  make-php-fpm-dynamic-process-manager-configuration
+  php-fpm-dynamic-process-manager-configuration?
+  (max-children         php-fpm-dynamic-process-manager-configuration-max-children
+                        (default 5))
+  (start-servers        php-fpm-dynamic-process-manager-configuration-start-servers
+                        (default 2))
+  (min-spare-servers    php-fpm-dynamic-process-manager-configuration-min-spare-servers
+                        (default 1))
+  (max-spare-servers    php-fpm-dynamic-process-manager-configuration-max-spare-servers
+                        (default 3)))
+
+(define-record-type* <php-fpm-static-process-manager-configuration>
+  php-fpm-static-process-manager-configuration
+  make-php-fpm-static-process-manager-configuration
+  php-fpm-static-process-manager-configuration?
+  (max-children         php-fpm-static-process-manager-configuration-max-children
+                        (default 5)))
+
+(define-record-type* <php-fpm-static-process-manager-configuration>
+  php-fpm-on-demand-process-manager-configuration
+  make-php-fpm-on-demand-process-manager-configuration
+  php-fpm-on-demand-process-manager-configuration?
+  (max-children         php-fpm-on-demand-process-manager-configuration-max-children
+                        (default 5))
+  (process-idle-timeout php-fpm-on-demand-process-manager-configuration-process-idle-timeout
+                        (default 10)))
+
+(define php-fpm-accounts
+  (match-lambda
+    (($ <php-fpm-configuration> php socket user group socket-user socket-group _ _ _ _ _ _)
+     (list
+      (user-group (name "php-fpm") (system? #t))
+      (user-group
+       (name group)
+       (system? #t))
+      (user-account
+       (name user)
+       (group group)
+       (supplementary-groups '("php-fpm"))
+       (system? #t)
+       (comment "php-fpm daemon user")
+       (home-directory "/var/empty")
+       (shell (file-append shadow "/sbin/nologin")))))))
+
+(define (default-php-fpm-config socket user group socket-user socket-group
+          pid-file log-file pm display-errors workers-log-file)
+  (apply mixed-text-file "php-fpm.conf"
+         (flatten
+          "[global]\n"
+          "pid =" pid-file "\n"
+          "error_log =" log-file "\n"
+          "[www]\n"
+          "user =" user "\n"
+          "group =" group "\n"
+          "listen =" socket "\n"
+          "listen.owner =" socket-user "\n"
+          "listen.group =" socket-group "\n"
+
+          (match pm
+            (($ <php-fpm-dynamic-process-manager-configuration>
+                pm.max-children
+                pm.start-servers
+                pm.min-spare-servers
+                pm.max-spare-servers)
+             (list
+              "pm = dynamic\n"
+              "pm.max_children =" (number->string pm.max-children) "\n"
+              "pm.start_servers =" (number->string pm.start-servers) "\n"
+              "pm.min_spare_servers =" (number->string pm.min-spare-servers) "\n"
+              "pm.max_spare_servers =" (number->string pm.max-spare-servers) "\n"))
+            
+            (($ <php-fpm-static-process-manager-configuration>
+                pm.max-children)
+             (list
+              "pm = static\n"
+              "pm.max_children =" (number->string pm.max-children) "\n"))
+            
+            (($ <php-fpm-on-demand-process-manager-configuration>
+                pm.max-children
+                pm.process-idle-timeout)
+             (list
+              "pm = ondemand\n"
+              "pm.max_children =" (number->string pm.max-children) "\n"
+              "pm.process_idle_timeout =" (number->string pm.process-idle-timeout) "s\n")))
+
+
+          "php_flag[display_errors] = " (if display-errors "on" "off") "\n"
+
+          (if workers-log-file
+              (list "catch_workers_output = yes\n"
+                    "php_admin_value[error_log] =" workers-log-file "\n"
+                    "php_admin_flag[log_errors] = on\n")
+              (list "catch_workers_output = no\n")))))
+
+(define php-fpm-shepherd-service
+  (match-lambda
+    (($ <php-fpm-configuration> php socket user group socket-user socket-group
+                                pid-file log-file pm display-errors workers-log-file file)
+     (list (shepherd-service
+            (provision '(php-fpm))
+            (documentation "Run the php-fpm daemon.")
+            (requirement '(networking))
+            (start #~(make-forkexec-constructor
+                      '(#$(file-append php "/sbin/php-fpm")
+                        "--fpm-config"
+                        #$(or file
+                              (default-php-fpm-config socket user group
+                                socket-user socket-group pid-file log-file
+                                pm display-errors workers-log-file)))
+                      #:pid-file #$pid-file))
+            (stop #~(make-kill-destructor)))))))
+
+(define php-fpm-activation
+  (match-lambda
+    (($ <php-fpm-configuration> _ _ user _ _ _ _ log-file _ _ workers-log-file _)
+     #~(begin
+         (use-modules (guix build utils))
+         (let* ((user (getpwnam #$user))
+                (touch (lambda (file-name)
+                         (call-with-output-file file-name (const #t))))
+                (init-log-file
+                 (lambda (file-name)
+                   (when #$workers-log-file
+                     (when (not (file-exists? file-name))
+                       (touch file-name))
+                     (chown file-name (passwd:uid user) (passwd:gid user))
+                     (chmod file-name #o660)))))
+           (init-log-file #$log-file)
+           (init-log-file #$workers-log-file))))))
+
+
+(define php-fpm-service-type
+  (service-type (name 'php-fpm)
+                (description "The php-fpm service-type.")
+                (extensions
+                 (list (service-extension shepherd-root-service-type
+                                          php-fpm-shepherd-service)
+                       (service-extension activation-service-type
+                                          php-fpm-activation)
+                       (service-extension account-service-type
+                                          php-fpm-accounts)))
+                (default-value (php-fpm-configuration))))
+
+(define* (nginx-php-location
+          #:key
+          (nginx-package nginx)
+          (socket (string-append "/var/run/php"
+                                 (version-major (package-version php))
+                                 "-fpm.sock")))
+  "Return a nginx-location-configuration that makes nginx run .php files."
+  (nginx-location-configuration
+   (uri "~ \\.php$")
+   (body (list
+          "fastcgi_split_path_info ^(.+\\.php)(/.+)$;"
+          (string-append "fastcgi_pass unix:" socket ";")
+          "fastcgi_index index.php;"
+          (list "include " nginx-package "/share/nginx/conf/fastcgi.conf;")))))
-- 
2.14.1