diff --git a/sapi/fpm/config.m4 b/sapi/fpm/config.m4 index e6a1a4985734a..acd4a78f2e542 100644 --- a/sapi/fpm/config.m4 +++ b/sapi/fpm/config.m4 @@ -586,6 +586,9 @@ if test "$PHP_FPM" != "no"; then PHP_ARG_WITH(fpm-acl,, [ --with-fpm-acl Use POSIX Access Control Lists], no, no) + PHP_ARG_WITH(fpm-cgroup,, + [ --with-fpm-cgroup Use CGroups], no, no) + if test "$PHP_FPM_SYSTEMD" != "no" ; then if test -z "$PKG_CONFIG"; then AC_PATH_PROG(PKG_CONFIG, pkg-config, no) @@ -638,6 +641,17 @@ if test "$PHP_FPM" != "no"; then ]) fi + if test "$PHP_FPM_CGROUP" != "no" ; then + AC_CHECK_HEADERS([libcgroup.h]) + AC_CHECK_LIB(cgroup, cgroup_init, [ + PHP_ADD_LIBRARY(cgroup) + AC_DEFINE(HAVE_FPM_CGROUP, 1, [ CGroups support ]) + ],[ + AC_MSG_ERROR(libcgroup required not found) + ]) + fi + + PHP_SUBST_OLD(php_fpm_systemd) AC_DEFINE_UNQUOTED(PHP_FPM_SYSTEMD, "$php_fpm_systemd", [fpm systemd service type]) diff --git a/sapi/fpm/fpm/fpm_conf.c b/sapi/fpm/fpm/fpm_conf.c index b497d2c82c947..2f206785f8459 100644 --- a/sapi/fpm/fpm/fpm_conf.c +++ b/sapi/fpm/fpm/fpm_conf.c @@ -156,6 +156,9 @@ static struct ini_value_parser_s ini_fpm_pool_options[] = { { "security.limit_extensions", &fpm_conf_set_string, WPO(security_limit_extensions) }, #ifdef HAVE_APPARMOR { "apparmor_hat", &fpm_conf_set_string, WPO(apparmor_hat) }, +#endif +#ifdef HAVE_FPM_CGROUP + { "cgroup", &fpm_conf_set_string, WPO(cgroup) }, #endif { 0, 0, 0 } }; @@ -658,6 +661,9 @@ int fpm_worker_pool_config_free(struct fpm_worker_pool_config_s *wpc) /* {{{ */ #ifdef HAVE_APPARMOR free(wpc->apparmor_hat); #endif +#ifdef HAVE_FPM_CGROUP + free(wpc->cgroup); +#endif for (kv = wpc->php_values; kv; kv = kv_next) { kv_next = kv->next; @@ -1606,6 +1612,9 @@ static void fpm_conf_dump() /* {{{ */ zlog(ZLOG_NOTICE, "\tprefix = %s", STR2STR(wp->config->prefix)); zlog(ZLOG_NOTICE, "\tuser = %s", STR2STR(wp->config->user)); zlog(ZLOG_NOTICE, "\tgroup = %s", STR2STR(wp->config->group)); +#ifdef HAVE_FPM_CGROUP + zlog(ZLOG_NOTICE, "\tcgroup = %s", STR2STR(wp->config->cgroup)); +#endif zlog(ZLOG_NOTICE, "\tlisten = %s", STR2STR(wp->config->listen_address)); zlog(ZLOG_NOTICE, "\tlisten.backlog = %d", wp->config->listen_backlog); #ifdef HAVE_FPM_ACL diff --git a/sapi/fpm/fpm/fpm_conf.h b/sapi/fpm/fpm/fpm_conf.h index 540b22795df34..ac40357289ac8 100644 --- a/sapi/fpm/fpm/fpm_conf.h +++ b/sapi/fpm/fpm/fpm_conf.h @@ -97,6 +97,9 @@ struct fpm_worker_pool_config_s { char *listen_acl_users; char *listen_acl_groups; #endif +#ifdef HAVE_FPM_CGROUP + char *cgroup; +#endif }; struct ini_value_parser_s { diff --git a/sapi/fpm/fpm/fpm_unix.c b/sapi/fpm/fpm/fpm_unix.c index 6089e3109edf7..e1daae5187516 100644 --- a/sapi/fpm/fpm/fpm_unix.c +++ b/sapi/fpm/fpm/fpm_unix.c @@ -25,6 +25,10 @@ #include #endif +#ifdef HAVE_FPM_CGROUP +#include +#endif + #include "fpm.h" #include "fpm_conf.h" #include "fpm_cleanup.h" @@ -322,6 +326,33 @@ static int fpm_unix_conf_wp(struct fpm_worker_pool_s *wp) /* {{{ */ wp->home = strdup(pwd->pw_dir); } } +#ifdef HAVE_FPM_CGROUP + if (wp->config->cgroup && *wp->config->cgroup) { + wp->cgroup = cgroup_new_cgroup(wp->config->cgroup); + if (wp->cgroup == NULL) { + zlog(ZLOG_ERROR, "[pool %s] cannot init cgroup structure", wp->config->name); + return -1; + } + + int ret = cgroup_get_cgroup(wp->cgroup); + if (ret != 0) { + zlog(ZLOG_ERROR, "[pool %s] cannot read cgroup '%s': %s.", wp->config->name, wp->config->cgroup, cgroup_strerror(ret)); + return -1; + } + + if (!is_root) { + uid_t tasks_uid, control_uid; + gid_t tasks_gid, control_gid; + cgroup_get_uid_gid(wp->cgroup, &tasks_uid, &tasks_gid, &control_uid, &control_gid); + + /* We also need to check file mode, but libcgroup does not provide the way to get it. + * So continue to proceed with a notice. */ + if (tasks_uid != geteuid() && tasks_gid != getegid()) { + zlog(ZLOG_NOTICE, "[pool %s] may have no access to cgroup '%s'.", wp->config->name, wp->config->cgroup); + } + } + } +#endif return 0; } /* }}} */ @@ -370,6 +401,16 @@ int fpm_unix_init_child(struct fpm_worker_pool_s *wp) /* {{{ */ } } +#ifdef HAVE_FPM_CGROUP + if (wp->cgroup) { + int ret = cgroup_attach_task(wp->cgroup); + if (ret != 0) { + zlog(ZLOG_SYSERROR, "[pool %s] failed change cgroup to (%s): %s.", wp->config->name, wp->config->cgroup, cgroup_strerror(ret)); + return -1; + } + } +#endif + if (is_root) { if (wp->config->process_priority != 64) { @@ -569,6 +610,17 @@ int fpm_unix_init_main() /* {{{ */ } } +#ifdef HAVE_FPM_CGROUP + for (wp = fpm_worker_all_pools; wp && !(wp->config->cgroup && *wp->config->cgroup); wp = wp->next); + if (wp) { + int ret = cgroup_init(); + if (ret != 0) { + zlog(ZLOG_SYSERROR, "libcgroup initialization failed: %s", cgroup_strerror(ret)); + return -1; + } + } +#endif + fpm_globals.parent_pid = getpid(); for (wp = fpm_worker_all_pools; wp; wp = wp->next) { if (0 > fpm_unix_conf_wp(wp)) { diff --git a/sapi/fpm/fpm/fpm_worker_pool.c b/sapi/fpm/fpm/fpm_worker_pool.c index a0022915cdce7..95a6fdb20652e 100644 --- a/sapi/fpm/fpm/fpm_worker_pool.c +++ b/sapi/fpm/fpm/fpm_worker_pool.c @@ -30,6 +30,9 @@ void fpm_worker_pool_free(struct fpm_worker_pool_s *wp) /* {{{ */ if (wp->home) { free(wp->home); } +#ifdef HAVE_FPM_CGROUP + cgroup_free(&wp->cgroup); +#endif fpm_unix_free_socket_premissions(wp); free(wp); } diff --git a/sapi/fpm/fpm/fpm_worker_pool.h b/sapi/fpm/fpm/fpm_worker_pool.h index 6b2bc908dc7e8..ecab6dd41fec4 100644 --- a/sapi/fpm/fpm/fpm_worker_pool.h +++ b/sapi/fpm/fpm/fpm_worker_pool.h @@ -8,6 +8,10 @@ #include "fpm_conf.h" #include "fpm_shm.h" +#ifdef HAVE_FPM_CGROUP +#include +#endif + struct fpm_worker_pool_s; struct fpm_child_s; struct fpm_child_stat_s; @@ -46,6 +50,9 @@ struct fpm_worker_pool_s { #ifdef HAVE_FPM_ACL void *socket_acl; #endif +#ifdef HAVE_FPM_CGROUP + struct cgroup *cgroup; +#endif }; struct fpm_worker_pool_s *fpm_worker_pool_alloc(); diff --git a/sapi/fpm/www.conf.in b/sapi/fpm/www.conf.in index beddb1e2ef61e..9c518fa2a916d 100644 --- a/sapi/fpm/www.conf.in +++ b/sapi/fpm/www.conf.in @@ -23,6 +23,11 @@ user = @php_fpm_user@ group = @php_fpm_group@ +; When Cgroups are supported you can use this option to put worker processes to +; specified cgroup. +; Default Value: no set +;cgroup = webserver/fpm + ; 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