github.com/symfony-cli/symfony-cli@v0.0.0-20240514161054-ece2df437dfa/local/php/fpm.go (about)

     1  /*
     2   * Copyright (c) 2021-present Fabien Potencier <fabien@symfony.com>
     3   *
     4   * This file is part of Symfony CLI project
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU Affero General Public License as
     8   * published by the Free Software Foundation, either version 3 of the
     9   * License, or (at your option) any later version.
    10   *
    11   * This program is distributed in the hope that it will be useful,
    12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    14   * GNU Affero General Public License for more details.
    15   *
    16   * You should have received a copy of the GNU Affero General Public License
    17   * along with this program. If not, see <http://www.gnu.org/licenses/>.
    18   */
    19  
    20  package php
    21  
    22  import (
    23  	"bytes"
    24  	"fmt"
    25  	"os"
    26  	"os/exec"
    27  	"os/user"
    28  	"path/filepath"
    29  	"strings"
    30  
    31  	"github.com/hashicorp/go-version"
    32  	"github.com/symfony-cli/terminal"
    33  )
    34  
    35  func (p *Server) defaultFPMConf() string {
    36  	logLevel := "notice"
    37  	if terminal.IsDebug() {
    38  		logLevel = "debug"
    39  	}
    40  	userConfig := ""
    41  	// when root, we need to configure the user in FPM configuration
    42  	if os.Geteuid() == 0 {
    43  		uid := "nobody"
    44  		gid := "nobody"
    45  		users := []string{
    46  			"www-data", // debian-like, alpine
    47  			"apache",   // fedora
    48  			"http",     // pld linux
    49  			"www",      // freebsd?
    50  			"_www",     // macOS
    51  		}
    52  		// we prefer to use the currently logged-in user (which might not be the current user when using sudo)
    53  		// also, you might not be logged in (when running in a Docker container for instance), in which case, we need to fall back to a "default" user
    54  		cmd := exec.Command("logname")
    55  		var out bytes.Buffer
    56  		cmd.Stdout = &out
    57  		err := cmd.Run()
    58  		if err == nil {
    59  			users = append([]string{strings.TrimSpace(out.String())}, users...)
    60  		}
    61  		for _, name := range users {
    62  			u, err := user.Lookup(name)
    63  			if err == nil {
    64  				uid = u.Uid
    65  				gid = u.Gid
    66  				break
    67  			}
    68  		}
    69  		userConfig = fmt.Sprintf("user = %s\ngroup = %s", uid, gid)
    70  	}
    71  	minVersion, _ := version.NewVersion("7.3.0")
    72  	workerConfig := ""
    73  	logLimit := ""
    74  	if p.Version.FullVersion.GreaterThanOrEqual(minVersion) {
    75  		workerConfig = "decorate_workers_output = no"
    76  		// see https://github.com/docker-library/php/pull/725#issuecomment-443540114
    77  		logLimit = "log_limit = 8192"
    78  	}
    79  	listen := p.addr
    80  	if listen[0] == ':' {
    81  		listen = "127.0.0.1" + listen
    82  	}
    83  	return fmt.Sprintf(`
    84  [global]
    85  error_log = /dev/fd/2
    86  log_level = %s
    87  daemonize = no
    88  %s
    89  
    90  [www]
    91  %s
    92  listen = %s
    93  listen.allowed_clients = 127.0.0.1
    94  pm = dynamic
    95  pm.max_children = 5
    96  pm.start_servers = 2
    97  pm.min_spare_servers = 1
    98  pm.max_spare_servers = 3
    99  pm.status_path = /__php-fpm-status__
   100  
   101  ; Ensure worker stdout and stderr are sent to the main error log
   102  catch_workers_output = yes
   103  %s
   104  
   105  php_admin_value[error_log] = /dev/fd/2
   106  php_admin_flag[log_errors] = on
   107  
   108  ; we want to expose env vars (like in FOO=bar symfony server:start)
   109  clear_env = no
   110  
   111  env['PGGSSENCMODE'] = disable
   112  env['LC_ALL'] = C
   113  `, logLevel, logLimit, userConfig, listen, workerConfig)
   114  }
   115  
   116  func (p *Server) fpmConfigFile() string {
   117  	path := filepath.Join(p.homeDir, fmt.Sprintf("php/%s/fpm-%s.ini", name(p.projectDir), p.Version.Version))
   118  	if _, err := os.Stat(filepath.Dir(path)); os.IsNotExist(err) {
   119  		_ = os.MkdirAll(filepath.Dir(path), 0755)
   120  	}
   121  	return path
   122  }