1 /* $Id: fpm_php.c,v 1.22.2.4 2008/12/13 03:21:18 anight Exp $ */
2 /* (c) 2007,2008 Andrei Nigmatulin */
3
4 #include "fpm_config.h"
5
6 #include <stdlib.h>
7 #include <string.h>
8 #include <stdio.h>
9
10 #include "php.h"
11 #include "php_main.h"
12 #include "php_ini.h"
13 #include "ext/standard/dl.h"
14
15 #include "fastcgi.h"
16
17 #include "fpm.h"
18 #include "fpm_php.h"
19 #include "fpm_cleanup.h"
20 #include "fpm_worker_pool.h"
21 #include "zlog.h"
22
23 static char **limit_extensions = NULL;
24
fpm_php_zend_ini_alter_master(char * name,int name_length,char * new_value,int new_value_length,int mode,int stage)25 static int fpm_php_zend_ini_alter_master(char *name, int name_length, char *new_value, int new_value_length, int mode, int stage) /* {{{ */
26 {
27 zend_ini_entry *ini_entry;
28 zend_string *duplicate;
29
30 if ((ini_entry = zend_hash_str_find_ptr(EG(ini_directives), name, name_length)) == NULL) {
31 return FAILURE;
32 }
33
34 duplicate = zend_string_init(new_value, new_value_length, 1);
35
36 if (!ini_entry->on_modify
37 || ini_entry->on_modify(ini_entry, duplicate,
38 ini_entry->mh_arg1, ini_entry->mh_arg2, ini_entry->mh_arg3, stage) == SUCCESS) {
39 ini_entry->value = duplicate;
40 /* when mode == ZEND_INI_USER keep unchanged to allow ZEND_INI_PERDIR (.user.ini) */
41 if (mode == ZEND_INI_SYSTEM) {
42 ini_entry->modifiable = mode;
43 }
44 } else {
45 zend_string_release(duplicate);
46 }
47
48 return SUCCESS;
49 }
50 /* }}} */
51
fpm_php_disable(char * value,int (* zend_disable)(char *,size_t))52 static void fpm_php_disable(char *value, int (*zend_disable)(char *, size_t)) /* {{{ */
53 {
54 char *s = 0, *e = value;
55
56 while (*e) {
57 switch (*e) {
58 case ' ':
59 case ',':
60 if (s) {
61 *e = '\0';
62 zend_disable(s, e - s);
63 s = 0;
64 }
65 break;
66 default:
67 if (!s) {
68 s = e;
69 }
70 break;
71 }
72 e++;
73 }
74
75 if (s) {
76 zend_disable(s, e - s);
77 }
78 }
79 /* }}} */
80
fpm_php_apply_defines_ex(struct key_value_s * kv,int mode)81 int fpm_php_apply_defines_ex(struct key_value_s *kv, int mode) /* {{{ */
82 {
83
84 char *name = kv->key;
85 char *value = kv->value;
86 int name_len = strlen(name);
87 int value_len = strlen(value);
88
89 if (!strcmp(name, "extension") && *value) {
90 zval zv;
91 php_dl(value, MODULE_PERSISTENT, &zv, 1);
92 return Z_TYPE(zv) == IS_TRUE;
93 }
94
95 if (fpm_php_zend_ini_alter_master(name, name_len, value, value_len, mode, PHP_INI_STAGE_ACTIVATE) == FAILURE) {
96 return -1;
97 }
98
99 if (!strcmp(name, "disable_functions") && *value) {
100 char *v = strdup(value);
101 PG(disable_functions) = v;
102 fpm_php_disable(v, zend_disable_function);
103 return 1;
104 }
105
106 if (!strcmp(name, "disable_classes") && *value) {
107 char *v = strdup(value);
108 PG(disable_classes) = v;
109 fpm_php_disable(v, zend_disable_class);
110 return 1;
111 }
112
113 return 1;
114 }
115 /* }}} */
116
fpm_php_apply_defines(struct fpm_worker_pool_s * wp)117 static int fpm_php_apply_defines(struct fpm_worker_pool_s *wp) /* {{{ */
118 {
119 struct key_value_s *kv;
120
121 for (kv = wp->config->php_values; kv; kv = kv->next) {
122 if (fpm_php_apply_defines_ex(kv, ZEND_INI_USER) == -1) {
123 zlog(ZLOG_ERROR, "Unable to set php_value '%s'", kv->key);
124 }
125 }
126
127 for (kv = wp->config->php_admin_values; kv; kv = kv->next) {
128 if (fpm_php_apply_defines_ex(kv, ZEND_INI_SYSTEM) == -1) {
129 zlog(ZLOG_ERROR, "Unable to set php_admin_value '%s'", kv->key);
130 }
131 }
132
133 return 0;
134 }
135 /* }}} */
136
fpm_php_set_allowed_clients(struct fpm_worker_pool_s * wp)137 static int fpm_php_set_allowed_clients(struct fpm_worker_pool_s *wp) /* {{{ */
138 {
139 if (wp->listen_address_domain == FPM_AF_INET) {
140 fcgi_set_allowed_clients(wp->config->listen_allowed_clients);
141 }
142 return 0;
143 }
144 /* }}} */
145
146 #if 0 /* Comment out this non used function. It could be used later. */
147 static int fpm_php_set_fcgi_mgmt_vars(struct fpm_worker_pool_s *wp) /* {{{ */
148 {
149 char max_workers[10 + 1]; /* 4294967295 */
150 int len;
151
152 len = sprintf(max_workers, "%u", (unsigned int) wp->config->pm_max_children);
153
154 fcgi_set_mgmt_var("FCGI_MAX_CONNS", sizeof("FCGI_MAX_CONNS")-1, max_workers, len);
155 fcgi_set_mgmt_var("FCGI_MAX_REQS", sizeof("FCGI_MAX_REQS")-1, max_workers, len);
156 return 0;
157 }
158 /* }}} */
159 #endif
160
fpm_php_script_filename(void)161 char *fpm_php_script_filename(void) /* {{{ */
162 {
163 return SG(request_info).path_translated;
164 }
165 /* }}} */
166
fpm_php_request_uri(void)167 char *fpm_php_request_uri(void) /* {{{ */
168 {
169 return (char *) SG(request_info).request_uri;
170 }
171 /* }}} */
172
fpm_php_request_method(void)173 char *fpm_php_request_method(void) /* {{{ */
174 {
175 return (char *) SG(request_info).request_method;
176 }
177 /* }}} */
178
fpm_php_query_string(void)179 char *fpm_php_query_string(void) /* {{{ */
180 {
181 return SG(request_info).query_string;
182 }
183 /* }}} */
184
fpm_php_auth_user(void)185 char *fpm_php_auth_user(void) /* {{{ */
186 {
187 return SG(request_info).auth_user;
188 }
189 /* }}} */
190
fpm_php_content_length(void)191 size_t fpm_php_content_length(void) /* {{{ */
192 {
193 return SG(request_info).content_length;
194 }
195 /* }}} */
196
fpm_php_cleanup(int which,void * arg)197 static void fpm_php_cleanup(int which, void *arg) /* {{{ */
198 {
199 php_module_shutdown();
200 sapi_shutdown();
201 }
202 /* }}} */
203
fpm_php_soft_quit()204 void fpm_php_soft_quit() /* {{{ */
205 {
206 fcgi_terminate();
207 }
208 /* }}} */
209
fpm_php_init_main()210 int fpm_php_init_main() /* {{{ */
211 {
212 if (0 > fpm_cleanup_add(FPM_CLEANUP_PARENT, fpm_php_cleanup, 0)) {
213 return -1;
214 }
215 return 0;
216 }
217 /* }}} */
218
fpm_php_init_child(struct fpm_worker_pool_s * wp)219 int fpm_php_init_child(struct fpm_worker_pool_s *wp) /* {{{ */
220 {
221 if (0 > fpm_php_apply_defines(wp) ||
222 0 > fpm_php_set_allowed_clients(wp)) {
223 return -1;
224 }
225
226 if (wp->limit_extensions) {
227 limit_extensions = wp->limit_extensions;
228 }
229 return 0;
230 }
231 /* }}} */
232
fpm_php_limit_extensions(char * path)233 int fpm_php_limit_extensions(char *path) /* {{{ */
234 {
235 char **p;
236 size_t path_len;
237
238 if (!path || !limit_extensions) {
239 return 0; /* allowed by default */
240 }
241
242 p = limit_extensions;
243 path_len = strlen(path);
244 while (p && *p) {
245 size_t ext_len = strlen(*p);
246 if (path_len > ext_len) {
247 char *path_ext = path + path_len - ext_len;
248 if (strcmp(*p, path_ext) == 0) {
249 return 0; /* allow as the extension has been found */
250 }
251 }
252 p++;
253 }
254
255
256 zlog(ZLOG_NOTICE, "Access to the script '%s' has been denied (see security.limit_extensions)", path);
257 return 1; /* extension not found: not allowed */
258 }
259 /* }}} */
260
fpm_php_get_string_from_table(zend_string * table,char * key)261 char* fpm_php_get_string_from_table(zend_string *table, char *key) /* {{{ */
262 {
263 zval *data, *tmp;
264 zend_string *str;
265 if (!table || !key) {
266 return NULL;
267 }
268
269 /* inspired from ext/standard/info.c */
270
271 zend_is_auto_global(table);
272
273 /* find the table and ensure it's an array */
274 data = zend_hash_find(&EG(symbol_table), table);
275 if (!data || Z_TYPE_P(data) != IS_ARRAY) {
276 return NULL;
277 }
278
279 ZEND_HASH_FOREACH_STR_KEY_VAL(Z_ARRVAL_P(data), str, tmp) {
280 if (str && !strncmp(ZSTR_VAL(str), key, ZSTR_LEN(str))) {
281 return Z_STRVAL_P(tmp);
282 }
283 } ZEND_HASH_FOREACH_END();
284
285 return NULL;
286 }
287 /* }}} */
288