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

From: [email-masked] (GNU bug Tracking System)
Date: Tue, 12 Dec 2017 21:42:02 +0000

Your bug report

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

which was filed against the guix-patches package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
28769: http://debbugs.gnu.org/cgi/bugreport.cgi?bug=28769
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: [bug#28769] [PATCH] gnu: services: Add php-fpm.
From: Christopher Baines <[email-masked]>
To: nee <[email-masked]>
Cc: [email-masked]
Date: Tue, 12 Dec 2017 21:41:41 +0000
List-Id: <debbugs-submit.debbugs.gnu.org>

nee <[email-masked]> writes:

> Am 09.12.2017 um 23:08 schrieb Christopher Baines:
>> I've attached some changes that I thought would be good when I was
>> looking through this. To give a rough summary:
>> 
>>  - Minor improvements to the docs, either content, markup or formatting.
>>  - Removing trailing whitespace.
>>  - Removing the changes to the nginx-service, in favour of changing the
>>    default socket group.
>>  - Change some indentation to avoid long lines.
>> 
>> By changing the default socket group, the system test passes, even
>> without the changes to the nginx service. I think this is a bit better,
>> and while it's definately not perfect, I think it would be ok to merge
>> with this change.
>> 
> Looks all good to me. Thanks for all these style fixes.
>
>> To also try and move the first patch forward, I've submitted that within
>> #29629, with an additional patch to get other services using
>> version-major.
>> 
> Thanks, good to see it used at some other spots already.
>
>> It would be good to get your thoughts on the changes in the attached
>> patch, and then if you could send an updated set of patches, that would
>> be great. As far as I remember, the changes to the nginx service were
>> the only thing I felt needed addressing before merging this.
>
> The changes look good to me.
> I have no edits to add, imo the patches can be squashed merged.

Great, I've now rebased and pushed this. Good job :)

Subject: [PATCH] gnu: services: Add php-fpm.
From: nee <[email-masked]>
To: [email-masked]
Date: Mon, 9 Oct 2017 23:54:24 +0200
List-Id: <debbugs-submit.debbugs.gnu.org>

Hello, this adds a php-fpm service. php-fpm is an alternative fcgi
implementation that is already build with the php package.

About the accounts:
Nginx needs write access to the unix socket of php-fpm. I didn't want to
set nginx as default user for php-fpm in case we add other webservers,
so I added the nginx to the newly created www-data group.

Thank you for reading. I'm looking forward to the revisions,
and happy Hacking!
From c31e09144c4cb8cd02d6fdb716760d385eee10d4 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 function.
---
 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 d78ba26e322b4f12a20a94f5334d23ba2ea6d4d5 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        |  93 +++++++++++++++++++++++++++
 gnu/services/web.scm | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 264 insertions(+), 2 deletions(-)

diff --git a/doc/guix.texi b/doc/guix.texi
index f0a59a6b4..ed4336f64 100644
--- a/doc/guix.texi
+++ b/doc/guix.texi
@@ -14529,6 +14529,99 @@ 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.
+
+@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{nginx})
+User who can speak to the php-fpm socket.
+@item @code {socket-group} (default: @code{nginx})
+Group that can speak to the php-fpm socket.
+@item @code {process-manager} (default: @code{(php-fpm-process-manager-configuration)})
+Detailed settings for the php-fpm process manager.
+@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.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-process-manager-configuration
+Data Type for php-fpm worker process limits.
+@table @asis
+@item @code {type} (default: @code{"dynamic"})
+@table @asis
+@item @code{"dynamic"}
+Spare worker processes are kept around based on the set @code{php-fpm-process-manager-configuration} limits.
+@item @code{"static"}
+A static number of worker processes is created.
+@item @code{"ondemand"}
+Worker processes are only created on demand.
+@end table
+@item @code {max-children} (default: @code{5})
+Maximum of worker processes. Applies when the type is @code{"static"}, @code{"dynamic"}, or @code{"ondemand"}.
+@item @code {start-servers} (default: @code{2})
+How many worker processes should be started on start-up. Only applies when type is @code{"dynamic"}.
+@item @code {min-spare-servers} (default: @code{1})
+How many spare worker processes should be kept around at minimum. Only applies when type is @code{"dynamic"}.
+@item @code {max-spare-servers} (default: @code{3})
+How many spare worker processes should be kept around at maximum. Only applies when type is @code{"dynamic"}.
+@item @code {process-idle-timeout} (default: @code{10})
+The time in seconds after which a process with no requests is killed. Only applies when type is @code{"ondemand"}.
+@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
+                          (php-fpm-configuration))
+                 (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..fd63b15bb 100644
--- a/gnu/services/web.scm
+++ b/gnu/services/web.scm
@@ -26,8 +26,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 +79,14 @@
 
             fcgiwrap-configuration
             fcgiwrap-configuration?
-            fcgiwrap-service-type))
+            fcgiwrap-service-type
+
+            php-fpm-configuration
+            php-fpm-configuration?
+            php-fpm-process-manager-configuration
+            php-fpm-process-manager-configuration?
+            php-fpm-service-type
+            nginx-php-location))
 
 ;;; Commentary:
 ;;;
@@ -256,10 +266,12 @@ of index files."
           "events {}\n")))
 
 (define %nginx-accounts
-  (list (user-group (name "nginx") (system? #t))
+  (list (user-group (name "www-data") (system? #t))
+        (user-group (name "nginx") (system? #t))
         (user-account
          (name "nginx")
          (group "nginx")
+         (supplementary-groups '("www-data"))
          (system? #t)
          (comment "nginx server user")
          (home-directory "/var/empty")
@@ -385,3 +397,160 @@ 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 "nginx"))
+  (socket-group    php-fpm-configuration-socket-group
+                   (default "nginx"))
+  (process-manager php-fpm-configuration-process-manager
+                   (default (php-fpm-process-manager-configuration)))
+  (display-errors  php-fpm-configuration-display-errors
+                   (default #f))
+  (workers-logfile php-fpm-configuration-workers-logfile
+                   (default (string-append "/var/log/php"
+                                         (version-major (package-version php))
+                                         "-fpm.log")))
+  (file          php-fpm-configuration-file ;#f | file-like
+                 (default #f)))
+
+(define-record-type* <php-fpm-process-manager-configuration> php-fpm-process-manager-configuration
+  make-php-fpm-process-manager-configuration
+  php-fpm-process-manager-configuration?
+  (type                 php-fpm-process-manager-configuration-type
+                        (default "dynamic"))
+  (max-children         php-fpm-process-manager-configuration-max-children
+                        (default 5))
+  (start-servers        php-fpm-process-manager-configuration-start-servers
+                        (default 2))
+  (min-spare-servers    php-fpm-process-manager-configuration-min-spare-servers
+                        (default 1))
+  (max-spare-servers    php-fpm-process-manager-configuration-max-spare-servers
+                        (default 3))
+  (process-idle-timeout php-fpm-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 _ _ _ _)
+     (filter identity
+             (list
+              (user-group (name "www-data") (system? #t))
+              (and (equal? group "php-fpm")
+                   (user-group
+                    (name "php-fpm")
+                    (system? #t)))
+              (and (equal? user "php-fpm")
+                   (user-account
+                    (name "php-fpm")
+                    (group group)
+                    (supplementary-groups '("www-data"))
+                    (system? #t)
+                    (comment "web services group")
+                    (home-directory "/var/empty")
+                    (shell (file-append shadow "/sbin/nologin")))))))))
+
+(define (default-php-fpm-config socket user group socket-user socket-group
+          pm display-errors workers-logfile)
+  (match
+      pm
+    (($ <php-fpm-process-manager-configuration> pm.type
+                                                pm.max-children
+                                                pm.start-servers
+                                                pm.min-spare-servers
+                                                pm.max-spare-servers
+                                                pm.process-idle-timeout)
+     (apply mixed-text-file "php-fpm.conf"
+            "[global]\n"
+            "[www]\n"
+            "user =" user "\n"
+            "group =" group "\n"
+            "listen =" socket "\n"
+            "listen.owner =" socket-user "\n"
+            "listen.group =" socket-group "\n"
+
+            "pm =" pm.type "\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"
+            "pm.process_idle_timeout =" (number->string pm.process-idle-timeout) "s\n"
+
+            "php_flag[display_errors] = " (if display-errors "on" "off") "\n"
+
+            (if workers-logfile
+                (list "catch_workers_output = yes\n"
+                      "php_admin_value[error_log] =" workers-logfile "\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
+                                pm display-errors workers-logfile 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")
+                        "--nodaemonize" "-p" "/var" "--fpm-config"
+                        #$(or file
+                              (default-php-fpm-config socket user group
+                                socket-user socket-group pm display-errors
+                                workers-logfile)))))
+            (stop #~(make-kill-destructor)))))))
+
+(define php-fpm-activation
+  (match-lambda
+    (($ <php-fpm-configuration> _ _ user _ _ _ _ _ workers-logfile _)
+     #~(begin
+         (use-modules (guix build utils))
+         (let ((user (getpwnam #$user))
+               (touch (lambda (file-name)
+                        (call-with-output-file file-name (const #t)))))
+           ;; prepare writable logfile
+           (when #$workers-logfile
+             (when (not (file-exists? #$workers-logfile))
+               (touch #$workers-logfile))
+             (chown #$workers-logfile (passwd:uid user) (passwd:gid user))
+             (chmod #$workers-logfile #o660)))))))
+
+
+(define php-fpm-service-type
+  (service-type (name 'php-fpm)
+                (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