unofficial mirror of guix-devel@gnu.org 
 help / color / mirror / code / Atom feed
blob e45b54fbb2a87c7cec99179ad141362f7661e2fa 20494 bytes (raw)
name: gnu/services/php.scm 	 # note: path name is non-authoritative(*)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
 
;;; GNU Guix --- Functional package management for GNU
;;; Copyright © 2016 Hartmut Goebel <h.goebel@crazy-compilers.com>
;;;
;;; This file is part of GNU Guix.
;;;
;;; GNU Guix is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or (at
;;; your option) any later version.
;;;
;;; GNU Guix is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;;; GNU General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with GNU Guix.  If not, see <http://www.gnu.org/licenses/>.
;;;
;;; Some of the help text was taken from the default php-fpm.conf files.

(define-module (gnu services php)
  #:use-module (gnu services)
  #:use-module (gnu services base)
  #:use-module (gnu services configuration)
  #:use-module (gnu services shepherd)
  #:use-module (gnu system shadow)
  #:use-module (gnu packages php)
  #:use-module (guix records)
  #:use-module (guix packages)
  #:use-module (guix gexp)
  #:use-module (ice-9 match)
  #:use-module (ice-9 regex) ;; for uglify-field-name
  #:export (php-fpm-service
            php-ini
            php-fpm-configuration
            php-fpm-pool))

(define (uglify-field-name field-name)
  (let ((str (symbol->string field-name)))
    (if (string-suffix? "?" str)
        (substring str 0 (1- (string-length str)))
        (regexp-substitute/global #f "-"  str 'pre "_" 'post))))
(define (serialize-field field-name val)
  (format #t "~a = ~a\n" (uglify-field-name field-name) val))

(define (serialize-string field-name val)
  (serialize-field field-name val))

(define (serialize-space-separated-string-list field-name val)
  (serialize-field field-name (string-join val " ")))

(define (serialize-boolean field-name val)
  (serialize-string field-name (if val "on" "off")))

(define (non-negative-integer? val)
  (and (exact-integer? val) (not (negative? val))))
(define (serialize-non-negative-integer field-name val)
  (serialize-field field-name val))

(define (optional-string? val)
  (or (and (boolean? val) (not val)) (string? val)))
(define (serialize-optional-string field-name val)
  (when (not (boolean? val))
        (serialize-field field-name val)))

(define (section? val) #t)
(define (serialize-section field-name val)
  (format #t "\n[~a]\n" val))

(define (production? val)
  (boolean? val))
(define (serialize-production field-name val)
  (serialize-boolean 'display-errors (not val))
  (serialize-boolean 'display-startup-errors (not val))
  (serialize-boolean 'track-errors (not val))
  (serialize-string  'error-reporting
                     (if val "E_ALL & ~E_DEPRECATED & ~E_STRICT" 
                             "E_ALL")))

(define (pools-list? val)
  (and (list? val) (and-map php-fpm-pool? val)))
(define (serialize-pools-list field-name val)
  (for-each (lambda (val) (serialize-php-fpm-pool field-name val)) val))

(define (serialize-php-fpm-pool field-name val)
  (format #t "\n\n; Start a new pool.")
  (serialize-configuration val php-fpm-pool-fields))

;;; ---- php.ini ---

(define-configuration php-ini
  (php
   (package php)
   "The php package to use.")

  (PHP (section "PHP") "The global options section")

  (production
   (production #t)
   "This variable determines if the @file{php.ini} configuration
will be configured towards 'production' systems (don't display
errors), or 'development' systems (display all errors).")

 (max-execution-time
  (non-negative-integer 30)
  "Specify default maximum execution time, in seconds.")

 (max-input-time
  (non-negative-integer 60)
  "Specify default maximum input time, in seconds.")

 (memory-limit 
  (string "128M")
  "Specify maximum memory limit for PHP processes, in megabytes.")

 (post-max-size 
  (string "8M")
  "Specify maximum size of the POST data, in megabytes.")

 (file-uploads 
  (boolean #t)
  "Enable or disable file uploading in PHP applications.")

 (upload-max-filesize 
  (string "8M") ;; TODO default to what has been passed to post-max-size
  "Specify maximum size of uploaded files, in megabytes.")

 (max-file-uploads 
  (non-negative-integer 20)
  "Specify maximum number of files uploaded at once.")

 (default-charset 
   (string "UTF-8")
   "Specify default charset used in PHP environment.")

 (allow-url-fopen 
  (boolean #f)
  "Enable or disable access to remote URLs in PHP applications.")

 ;; CGI
 (CGI (section "CGI") "The CGI section")

 (cgi.fix-pathinfo
  (boolean #f)
  "Enable or disable 'cgi.fix_pathinfo' option in PHP.  This is highly
dependent on the used webserver (nginx should have the option disabled,
apache2 needs it to be enabled).")

 ;; Date
 (Date (section "Date") "The Date section")

 (date.timezone 
  (string "Etc/UTC")  ;; TODO: Take from system timezone
  "Configure the PHP timezone."))

;;; ---- php-fpm.conf ----

(define-configuration php-fpm-configuration
  ;; All relative paths in this configuration file are relative to PHP's
  ;; install prefix.
  (global (section "global") "The global options section")

  ;;(pid "/var/run/php/php-fpm.pid") configured on command line

  (error-log
   (string "/var/log/php-fpm.log")
   "Path to the 'error.log' file which is used by PHP-FPM to log
error messages.  If it's set to 'syslog', error logs are sent to the
local log daemon.")

  (syslog.ident
   (string "php-fpm")
   "When 'syslog' logging is enabled, specify the program identification
string used by PHP-FPM.  This should be one word string, without spaces.")

  (syslog.facility
   (string "daemon")
   "When 'syslog' logging is enabled, specify the 'syslog' facility to use.")

  (log-level
   (string "notice")
   "When 'syslog' logging is enabled, specify the log level used by PHP-FPM.")

  (emergency-restart-threshold
   (non-negative-integer 0)
   "Specify number of PHP-FPM child processes that exit with errors
during a given interval (see below) that will trigger an automatic
restart of the master PHP-FPM process.")

  (emergency-restart-interval
   (non-negative-integer 0)
   "Specify the interval which is used to determine
emergency-restart-threshold.")

  (process-control-timeout
   (non-negative-integer 0)
   "Specify maximum wait time the master PHP-FPM process waits for a reaction
on the signals sent to the child processes.")

  (process.max 
   (non-negative-integer 128)
   "Maximum number of PHP-FPM child processes.")

  ;; not yet implemented
  ;; ("process.priority"  "-19")
  ;; ("daemonize  "yes"
  ;; ("rlimit_files"  "1024")
  ;; (rlimit_core  0")
  ;; ("events.mechanism"  "epoll")
  ;; ("systemd_interval"  "10")

  (pools
   (pools-list (error "At least one pool is required."))
   "Pools defined by this service."))

;;; ---- pool-configuration ----

(define-configuration php-fpm-pool
  (name
   (section (error "pool name is required"))
   "Name for this pool.")

  ;; TODO: prefix (think about whether we want it or not)

  (user
   (string "$pool")
   ;; TODO: Rethink: The username of the user that owns the site files.
   "The user to run the processes.")
  (group
   (string "$pool") ;; TODO: check what happens if empty
   "Unix group of processes.  If the group is not set, the default user's group
will be used.")

  (listen
   (string "/var/run/php-fpm-$pool.sock")
    "The address on which to accept FastCGI requests.
Valid syntaxes are:
  'ip.add.re.ss:port'    - to listen on a TCP socket to a specific IPv4
                            address on a specific port;
  '[ip:6:addr:ess]:port' - to listen on a TCP socket to a specific IPv6
                            address on a specific port;
  'port'                 - to listen on a TCP socket to all addresses
                           (IPv6 and IPv4-mapped) on a specific port;
  '/path/to/unix/socket' - to listen on a unix socket.")

  (listen.owner
   (string "nginx")
   "The system user that will be the owner of the PHP-FPM socket.  This should
be the username of the webserver account, so that it can use the socket to
communicate with the PHP-FPM process.  This account needs to exist before the
PHP-FPM process is started (the 'nginx' account is created by default on
GuixSD systems when using the nginx-service).")

  (listen.group
   (string "nginx")
   "The system group that will be the primary group of the PHP-FPM socket.
This should be the group that the webserver belongs to, so that it can use the
socket to communicate with the PHP-FPM process.  This group needs to exist
before the PHP-FPM process is started (the 'nginx' group is created by default
on GuixSD systems when using the nginx-service).")

  (listen.mode
   (string "0660")
   "The default permissions applied to the PHP-FPM pool sockets.")

  ;; TODO:
  ;; When POSIX Access Control Lists are supported you can set them using
  ;; these options, value is a comma separated list of user/group names.
  ;; When set, listen.owner and listen.group are ignored
  ;(listen.acl-users  '())
  ;(listen.acl-groups '())

  ;; TODO: listen.allowed-clients = 127.0.0.1
  ;; TODO: listen.backlog = -1
  ;; TODO: process.priority = -19

  (pm ;; TODO: nicer name: process-manager
   (string "ondemand")
   "Select the default way the PHP-FPM master process will manage pool child
processes.  Possible values: 'static', 'dynamic' or 'ondemand'.")

  (pm.max-children  ;; TODO: nicer name
   (non-negative-integer 75) ;; TODO: processor-vcpus
   "Maximum number of child processes to be created when pm is set to
'static' and the maximum number of child processes when pm is set to
'dynamic' or 'ondemand'.")

  (pm.start-servers  ;; TODO: nicer name
   (non-negative-integer 10) ;; TODO: processor-vcpus
   "The number of pool child processes created at startup, used by the
'dynamic' management mode.")

  (pm.min-spare-servers   ;; TODO: nicer name
   (non-negative-integer 5)
   "Number of minimum idle spare servers that should be kept around,
used by the 'dynamic' management mode.")

  (pm.max-spare-servers    ;; TODO: nicer name
   (non-negative-integer 20) ;; TODO: default to pm.max-children
   "Number of maximum idle spare servers that should be kept around,
used by the 'dynamic' management mode.")

  (pm.process-idle-timeout ;; TODO: nicer name
   (string "10s")
   "Timeout in seconds for the PHP-FPM pool child processes, used by
the 'ondemand' management mode.")

  (pm.max-requests ;; TODO: nicer name
   (non-negative-integer 500)
   "Maximum number of requests after which PHP-FPM pool child
processes will be respawned.")

  (pm.status-path   ;; TODO: nicer name
   ;; FIXME: should be unset by default for security reasons
   (optional-string "/php_status")
   "Enable or disable pool status page in all PHP-FPM pools.  If this value is
not set (#f), no URI will be recognized as a status page.  The value must
start with a leading slash (/).  You might need to configure the webserver to
allow access to this page as well.  The value can be anything, but it may not
be a good idea to use the .php extension or it may conflict with a real PHP
file.")

  (ping.path   ;; TODO: nicer name
   ;; FIXME: should be unset by default for security reasons
   (optional-string "/php_ping")
   "The ping URI to call the monitoring page of FPM.  If this value is not set,
no URI will be recognized as a ping page.  This could be used to test
from outside that FPM is alive and responding, or to - create a graph
of FPM availability (rrd or such); - remove a server from a group if
it is not responding (load balancing); - trigger alerts for the
operating team (24/7).

The value must start with a leading slash (/).  The value can be
anything, but it may not be a good idea to use the .php extension or
it may conflict with a real PHP file.")

  (ping.response  ;; TODO: nicer name
   (string "pong")
   "A string that defines the expected response of the 'ping' request.")

  (access.log
   (optional-string "/var/log/php-fpm/$pool_access.log")
    "The access log file for this PHP-FPM pool .  Set to #f to disable
logging.")

  (access.format ;; TODO: should be privacy enhanced
   (string "%R - %u %t \"%m %r%Q%q\" %s %f %{mili}d %{kilo}M %C%%")
   "The access log format.")

  (slowlog
   (string "/var/log/php-fpm/$pool_slow.log")
   "The log file for slow requests.  Note: slowlog is mandatory if
request_slowlog_timeout is set.")

  (request-slowlog-timeout
   (string "0")
   "The timeout for serving a single request after which a PHP backtrace will
be dumped to the 'slowlog' file.  A value of '0s' means 'off'.  Available
units: s(econds)(default), m(inutes), h(ours), or d(ays)")

  (request-terminate-timeout
   (string "0")
   "The timeout for serving a single request after which the worker
process will be killed.  This option should be used when the
'max-execution-time' ini option does not stop script execution for
some reason.  A value of '0' means 'off'.  Available units:
s(econds)(default), m(inutes), h(ours), or d(ays)")

  (rlimit-files
   (non-negative-integer 1024) ; TODO: Check; what if the default is set to 0?
  "Maximum number of opened file descriptors.")
  
  (rlimit-core
   (non-negative-integer 0) ; TODO: Check; what if the default is set to 0?
   ;; TODO: Possible Values: 'unlimited' or an integer greater or equal to 0
   "Specify maximum size of the 'core' files.")

  (chroot
   (optional-string #f)
   "Chroot to this directory at the start.  This value must be defined as an
absolute path.  When this value is not set, chroot is not used.
Note: you can prefix with '$prefix' to chroot to the pool prefix or one
of its subdirectories.  If the pool prefix is not set, the global prefix
will be used instead.
Note: chrooting is a great security feature and should be used whenever
      possible.  However, all PHP paths will be relative to the chroot
      (error_log, sessions.save_path, ...).")

  (chdir
   (string "/")
   "Chdir to this directory at the start.")

  (catch-workers-output
   (boolean #f)
   "Redirect worker stdout and stderr into main error log.  If not set, stdout
and stderr will be redirected to /dev/null according to FastCGI specs.  Note:
on highloaded environement, this can cause some delay in the page process
time (several ms).")

   (clear-env
    (boolean #t)
    "Clear environment in FPM workers. Prevents arbitrary environment
variables from reaching FPM worker processes by clearing the environment in
workers before env vars specified in this pool configuration are added.
Setting to #f will make all environment variables available to PHP code
via getenv(), $_ENV and $_SERVER.")

  (security.limit-extensions
   (space-separated-string-list '(".php"))
   "Limits the extensions of the main script FPM will allow to parse.
This can prevent configuration mistakes on the web server side.  You
should only limit FPM to .php extensions to prevent malicious users to
use other extensions to exectute php code.  Note: set an empty list to
allow all extensions.")

  ;; TODO: environment:
  ;; These values are taken from the nginx example configuration:
  ;; ; Pass environment variables
  ;; env[HOSTNAME] = $HOSTNAME
  ;; env[PATH] = /usr/local/bin:/usr/bin:/bin
  ;; env[TMP] = /tmp
  ;; env[TMPDIR] = /tmp
  ;; env[TEMP] = /tmp

  ;; host-specific php ini settings here (from the nginx example configuration)
  ;; php_admin_value[open_basedir] = /var/www/DOMAINNAME/htdocs:/tmp

  ;; TODO: php_flag[ key ] =  value
  ;; TODO: php_value[ key ] =  value
  ;; TODO: php_admin_flag[ key ] =  value
  ;; TODO: php_admin_value[ key ] =  value
)

;;; --- obaque configs ---

(define-configuration opaque-php-ini
  (php
   (package php)
   "The php package to use.")

  (string
   (string (configuration-missing-field 'opaque-php-ini 'string))
   "The contents of the @code{php.ini} to use."))

(define-configuration opaque-php-fpm-configuration
  (string
   (string (configuration-missing-field 'opaque-fpm 'string))
   "The contents of the @code{php-fpm.conf} to use."))

(define-configuration opaque-php-fpm-pool
  (name
   (section (configuration-missing-field 'opaque-php-fpm-pool 'name))
   "The FPM pool name.")
  (string
   (string (configuration-missing-field 'opaque-php-fpm-pool 'string))
   "The contents of the @code{pool} to use."))

;;; --- service definition ---

(define %php-fpm-activation
  ;; Activation gexp.
  #~(begin
      (use-modules (guix build utils))
      (define (mkdir-p/perms directory owner perms)
        (mkdir-p directory)
        (chown directory (passwd:uid owner) (passwd:gid owner))
        (chmod directory perms))
      (let ((root (getpwnam "root")))
        (mkdir-p/perms "/etc/php/fpm" root #o755)
        (mkdir-p/perms "/var/log/php-fpm" root #o700)
        )
      ;; TODO: Check configuration file syntax.
      ;; How can both the php-ini-file and the fpm-config-file be passed here?
      ;; (system* (string-append #$php-pkg "/sbin/php-fpm")
      ;;          "--test"
      ;;          "--php-ini" #$php-ini-file
      ;;          "--fpm-config" #$fpm-config-file)
      ))

(define (php-fpm-shepherd-service configs)
  "Return a list of <shepherd-service> for PHP-INI and FPM-CONFIG."
  (let* ((php-ini (car configs))
         (fpm-config (car (cdr configs)))
         (fpm-config-str
          (cond
           ((opaque-php-fpm-configuration? fpm-config)
            (opaque-php-fpm-configuration-string fpm-config))
           (else
            (with-output-to-string
              (lambda ()
                (serialize-configuration fpm-config
                                         php-fpm-configuration-fields))))))
         (php-ini-str
          (cond
           ((opaque-php-ini? php-ini)
            (opaque-php-ini-string php-ini))
           (else
            (with-output-to-string
              (lambda ()
                (serialize-configuration php-ini
                                         php-ini-fields))))))
         (fpm-config-file (plain-file "php-fpm.conf" fpm-config-str))
         (php-ini-file (plain-file "php.ini" php-ini-str))
         (php-pkg (if (opaque-php-ini? php-ini)
                      (opaque-php-ini-php php-ini)
                      (php-ini-php php-ini))))
    (list (shepherd-service
           (documentation "Run the PHP-FPM server.")
           (provision '(php-fpm))
           (requirement '(networking))
           (start #~(make-forkexec-constructor
                     (list (string-append #$php-pkg "/sbin/php-fpm")
                           "--php-ini" #$php-ini-file
 			   "--pid" "/var/run/php-fpm.pid"
                           "--fpm-config" #$fpm-config-file)
                     ;; Wait for the PID file.
                     #:pid-file "/var/run/php-fpm.pid"))
           (stop #~(make-kill-destructor))))))


(define php-fpm-service-type
  (service-type (name 'php-fpm)
                (extensions
                 (list (service-extension shepherd-root-service-type
                                          php-fpm-shepherd-service)
                       ;; TODO: Collect the user ids from the pools
                       ;;       How can this be done?
                       ;;(service-extension account-service-type
                       ;;                   (const %php-accounts))
                       (service-extension activation-service-type
                                          (const %php-fpm-activation))))))

(define* (php-fpm-service #:key (config (php-fpm-configuration))
                          (php-ini (php-ini)))
  (validate-configuration php-ini
                          (if (opaque-php-ini? php-ini)
                              opaque-php-ini-fields
                              php-ini-fields))
  (validate-configuration config
                          (if (opaque-php-fpm-configuration? config)
                              opaque-php-fpm-configuration-fields
                              php-fpm-configuration-fields))
  (service php-fpm-service-type (list php-ini config)))


;; --testt --fpm-config /tmp/fpm.conf

;; /tmp/fpm.conf
;; ;;[global]
;; pid = /run/php-fpm.pid
;; error_log = /tmp/php-fpm.log
;;
;; [www]
;; include = /tmp/pool-defaults.conf
;; ;pm.status_path = False
;; user = hartmut
;; pm.max_children = 7
;; catch_workers_output = On
;;
;; ----
;; /tmp/pool-defaults.conf
;;
;; pm = dynamic
;; pm.max_children = 10
;; pm.min_spare_servers = 1
;; pm.max_spare_servers = 5
;;
;; listen = /tmp/fpm/$pool.sock

debug log:

solving e45b54f ...
found e45b54f in https://yhetil.org/guix-devel/1481219530-786-2-git-send-email-h.goebel@crazy-compilers.com/

applying [1/1] https://yhetil.org/guix-devel/1481219530-786-2-git-send-email-h.goebel@crazy-compilers.com/
diff --git a/gnu/services/php.scm b/gnu/services/php.scm
new file mode 100644
index 0000000..e45b54f

1:83: trailing whitespace.
                     (if val "E_ALL & ~E_DEPRECATED & ~E_STRICT" 
1:118: trailing whitespace.
 (memory-limit 
1:122: trailing whitespace.
 (post-max-size 
1:126: trailing whitespace.
 (file-uploads 
1:130: trailing whitespace.
 (upload-max-filesize 
Checking patch gnu/services/php.scm...
Applied patch gnu/services/php.scm cleanly.
warning: squelched 7 whitespace errors
warning: 12 lines add whitespace errors.

index at:
100644 e45b54fbb2a87c7cec99179ad141362f7661e2fa	gnu/services/php.scm

(*) Git path names are given by the tree(s) the blob belongs to.
    Blobs themselves have no identifier aside from the hash of its contents.^

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/guix.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).