1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2018 The PHP Group |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available at through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt. |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: George Wang <gwang@litespeedtech.com> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "php.h"
20 #include "SAPI.h"
21 #include "php_main.h"
22 #include "php_ini.h"
23 #include "php_variables.h"
24 #include "zend_highlight.h"
25 #include "zend.h"
26 #include "ext/standard/basic_functions.h"
27 #include "ext/standard/info.h"
28 #include "lsapilib.h"
29
30 #include <stdio.h>
31
32 #if HAVE_STDLIB_H
33 #include <stdlib.h>
34 #endif
35
36 #if HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39
40 #ifdef PHP_WIN32
41
42 #include <io.h>
43 #include <fcntl.h>
44 #include "win32/php_registry.h"
45
46 #else
47
48 #include <sys/wait.h>
49
50 #endif
51
52 #include <sys/stat.h>
53
54 #if HAVE_SYS_TYPES_H
55
56 #include <sys/types.h>
57
58 #endif
59
60 #if HAVE_SIGNAL_H
61
62 #include <signal.h>
63
64 #endif
65
66 #include <sys/socket.h>
67 #include <arpa/inet.h>
68 #include <netinet/in.h>
69 #include <sys/time.h>
70
71 #if defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__)
72 #include "lscriu.c"
73 #endif
74
75 #define SAPI_LSAPI_MAX_HEADER_LENGTH 2048
76
77 /* Key for each cache entry is dirname(PATH_TRANSLATED).
78 *
79 * NOTE: Each cache entry config_hash contains the combination from all user ini files found in
80 * the path starting from doc_root throught to dirname(PATH_TRANSLATED). There is no point
81 * storing per-file entries as it would not be possible to detect added / deleted entries
82 * between separate files.
83 */
84 typedef struct _user_config_cache_entry {
85 time_t expires;
86 HashTable user_config;
87 } user_config_cache_entry;
88 static HashTable user_config_cache;
89
90 static int lsapi_mode = 0;
91 static char *php_self = "";
92 static char *script_filename = "";
93 static int source_highlight = 0;
94 static int ignore_php_ini = 0;
95 static char * argv0 = NULL;
96 static int engine = 1;
97 static int parse_user_ini = 0;
98
99 #ifdef ZTS
100 zend_compiler_globals *compiler_globals;
101 zend_executor_globals *executor_globals;
102 php_core_globals *core_globals;
103 sapi_globals_struct *sapi_globals;
104 void ***tsrm_ls;
105 #endif
106
107 zend_module_entry litespeed_module_entry;
108
init_sapi_from_env(sapi_module_struct * sapi_module)109 static void init_sapi_from_env(sapi_module_struct *sapi_module)
110 {
111 char *p;
112 p = getenv("LSPHPRC");
113 if (p)
114 sapi_module->php_ini_path_override = p;
115 }
116
117 /* {{{ php_lsapi_startup
118 */
php_lsapi_startup(sapi_module_struct * sapi_module)119 static int php_lsapi_startup(sapi_module_struct *sapi_module)
120 {
121 if (php_module_startup(sapi_module, NULL, 0)==FAILURE) {
122 return FAILURE;
123 }
124 argv0 = sapi_module->executable_location;
125 return SUCCESS;
126 }
127 /* }}} */
128
129 /* {{{ sapi_lsapi_ini_defaults */
130
131 /* overwriteable ini defaults must be set in sapi_cli_ini_defaults() */
132 #define INI_DEFAULT(name,value)\
133 ZVAL_STRING(tmp, value, 0);\
134 zend_hash_update(configuration_hash, name, sizeof(name), tmp, sizeof(zval), (void**)&entry);\
135 Z_STRVAL_P(entry) = zend_strndup(Z_STRVAL_P(entry), Z_STRLEN_P(entry))
136
sapi_lsapi_ini_defaults(HashTable * configuration_hash)137 static void sapi_lsapi_ini_defaults(HashTable *configuration_hash)
138 {
139 #if PHP_MAJOR_VERSION > 4
140 /*
141 zval *tmp, *entry;
142
143 MAKE_STD_ZVAL(tmp);
144
145 INI_DEFAULT("register_long_arrays", "0");
146
147 FREE_ZVAL(tmp);
148 */
149 #endif
150
151 }
152 /* }}} */
153
154
155 /* {{{ sapi_lsapi_ub_write
156 */
sapi_lsapi_ub_write(const char * str,size_t str_length)157 static size_t sapi_lsapi_ub_write(const char *str, size_t str_length)
158 {
159 int ret;
160 int remain;
161 if ( lsapi_mode ) {
162 ret = LSAPI_Write( str, str_length );
163 if ( ret < str_length ) {
164 php_handle_aborted_connection();
165 return str_length - ret;
166 }
167 } else {
168 remain = str_length;
169 while( remain > 0 ) {
170 ret = write( 1, str, remain );
171 if ( ret <= 0 ) {
172 php_handle_aborted_connection();
173 return str_length - remain;
174 }
175 str += ret;
176 remain -= ret;
177 }
178 }
179 return str_length;
180 }
181 /* }}} */
182
183
184 /* {{{ sapi_lsapi_flush
185 */
sapi_lsapi_flush(void * server_context)186 static void sapi_lsapi_flush(void * server_context)
187 {
188 if ( lsapi_mode ) {
189 if ( LSAPI_Flush() == -1) {
190 php_handle_aborted_connection();
191 }
192 }
193 }
194 /* }}} */
195
196
197 /* {{{ sapi_lsapi_deactivate
198 */
sapi_lsapi_deactivate(void)199 static int sapi_lsapi_deactivate(void)
200 {
201 if ( SG(request_info).path_translated ) {
202 efree( SG(request_info).path_translated );
203 SG(request_info).path_translated = NULL;
204 }
205
206 return SUCCESS;
207 }
208 /* }}} */
209
210
211
212
213 /* {{{ sapi_lsapi_getenv
214 */
sapi_lsapi_getenv(char * name,size_t name_len)215 static char *sapi_lsapi_getenv( char * name, size_t name_len )
216 {
217 if ( lsapi_mode ) {
218 return LSAPI_GetEnv( name );
219 } else {
220 return getenv( name );
221 }
222 }
223 /* }}} */
224
225
add_variable(const char * pKey,int keyLen,const char * pValue,int valLen,void * arg)226 static int add_variable( const char * pKey, int keyLen, const char * pValue, int valLen,
227 void * arg )
228 {
229 int filter_arg = (Z_ARR_P((zval *)arg) == Z_ARR(PG(http_globals)[TRACK_VARS_ENV]))
230 ? PARSE_ENV : PARSE_SERVER;
231 char * new_val = (char *) pValue;
232 size_t new_val_len;
233
234 if (sapi_module.input_filter(filter_arg, (char *)pKey, &new_val, valLen, &new_val_len)) {
235 php_register_variable_safe((char *)pKey, new_val, new_val_len, (zval *)arg );
236 }
237 return 1;
238 }
239
litespeed_php_import_environment_variables(zval * array_ptr)240 static void litespeed_php_import_environment_variables(zval *array_ptr)
241 {
242 char buf[128];
243 char **env, *p, *t = buf;
244 size_t alloc_size = sizeof(buf);
245 unsigned long nlen; /* ptrdiff_t is not portable */
246
247 if (Z_TYPE(PG(http_globals)[TRACK_VARS_ENV]) == IS_ARRAY &&
248 Z_ARR_P(array_ptr) != Z_ARR(PG(http_globals)[TRACK_VARS_ENV]) &&
249 zend_hash_num_elements(Z_ARRVAL(PG(http_globals)[TRACK_VARS_ENV])) > 0
250 ) {
251 zval_dtor(array_ptr);
252 ZVAL_DUP(array_ptr, &PG(http_globals)[TRACK_VARS_ENV]);
253 return;
254 } else if (Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY &&
255 Z_ARR_P(array_ptr) != Z_ARR(PG(http_globals)[TRACK_VARS_SERVER]) &&
256 zend_hash_num_elements(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER])) > 0
257 ) {
258 zval_dtor(array_ptr);
259 ZVAL_DUP(array_ptr, &PG(http_globals)[TRACK_VARS_SERVER]);
260 return;
261 }
262
263 for (env = environ; env != NULL && *env != NULL; env++) {
264 p = strchr(*env, '=');
265 if (!p) { /* malformed entry? */
266 continue;
267 }
268 nlen = p - *env;
269 if (nlen >= alloc_size) {
270 alloc_size = nlen + 64;
271 t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size));
272 }
273 memcpy(t, *env, nlen);
274 t[nlen] = '\0';
275 add_variable(t, nlen, p + 1, strlen( p + 1 ), array_ptr);
276 }
277 if (t != buf && t != NULL) {
278 efree(t);
279 }
280 }
281
282 /* {{{ sapi_lsapi_register_variables
283 */
sapi_lsapi_register_variables(zval * track_vars_array)284 static void sapi_lsapi_register_variables(zval *track_vars_array)
285 {
286 char * php_self = "";
287 if ( lsapi_mode ) {
288 if ( (SG(request_info).request_uri ) )
289 php_self = (SG(request_info).request_uri );
290
291 litespeed_php_import_environment_variables(track_vars_array);
292
293 LSAPI_ForeachHeader( add_variable, track_vars_array );
294 LSAPI_ForeachEnv( add_variable, track_vars_array );
295 add_variable("PHP_SELF", 8, php_self, strlen( php_self ), track_vars_array );
296 } else {
297 php_import_environment_variables(track_vars_array);
298
299 php_register_variable("PHP_SELF", php_self, track_vars_array);
300 php_register_variable("SCRIPT_NAME", php_self, track_vars_array);
301 php_register_variable("SCRIPT_FILENAME", script_filename, track_vars_array);
302 php_register_variable("PATH_TRANSLATED", script_filename, track_vars_array);
303 php_register_variable("DOCUMENT_ROOT", "", track_vars_array);
304
305 }
306 }
307 /* }}} */
308
309
310 /* {{{ sapi_lsapi_read_post
311 */
sapi_lsapi_read_post(char * buffer,size_t count_bytes)312 static size_t sapi_lsapi_read_post(char *buffer, size_t count_bytes)
313 {
314 if ( lsapi_mode ) {
315 ssize_t rv = LSAPI_ReadReqBody(buffer, (unsigned long long)count_bytes);
316 return (rv >= 0) ? (size_t)rv : 0;
317 } else {
318 return 0;
319 }
320 }
321 /* }}} */
322
323
324
325
326 /* {{{ sapi_lsapi_read_cookies
327 */
sapi_lsapi_read_cookies(void)328 static char *sapi_lsapi_read_cookies(void)
329 {
330 if ( lsapi_mode ) {
331 return LSAPI_GetHeader( H_COOKIE );
332 } else {
333 return NULL;
334 }
335 }
336 /* }}} */
337
338
339 typedef struct _http_error {
340 int code;
341 const char* msg;
342 } http_error;
343
344 static const http_error http_error_codes[] = {
345 {100, "Continue"},
346 {101, "Switching Protocols"},
347 {200, "OK"},
348 {201, "Created"},
349 {202, "Accepted"},
350 {203, "Non-Authoritative Information"},
351 {204, "No Content"},
352 {205, "Reset Content"},
353 {206, "Partial Content"},
354 {300, "Multiple Choices"},
355 {301, "Moved Permanently"},
356 {302, "Moved Temporarily"},
357 {303, "See Other"},
358 {304, "Not Modified"},
359 {305, "Use Proxy"},
360 {400, "Bad Request"},
361 {401, "Unauthorized"},
362 {402, "Payment Required"},
363 {403, "Forbidden"},
364 {404, "Not Found"},
365 {405, "Method Not Allowed"},
366 {406, "Not Acceptable"},
367 {407, "Proxy Authentication Required"},
368 {408, "Request Time-out"},
369 {409, "Conflict"},
370 {410, "Gone"},
371 {411, "Length Required"},
372 {412, "Precondition Failed"},
373 {413, "Request Entity Too Large"},
374 {414, "Request-URI Too Large"},
375 {415, "Unsupported Media Type"},
376 {428, "Precondition Required"},
377 {429, "Too Many Requests"},
378 {431, "Request Header Fields Too Large"},
379 {451, "Unavailable For Legal Reasons"},
380 {500, "Internal Server Error"},
381 {501, "Not Implemented"},
382 {502, "Bad Gateway"},
383 {503, "Service Unavailable"},
384 {504, "Gateway Time-out"},
385 {505, "HTTP Version not supported"},
386 {511, "Network Authentication Required"},
387 {0, NULL}
388 };
389
390
sapi_lsapi_send_headers_like_cgi(sapi_headers_struct * sapi_headers)391 static int sapi_lsapi_send_headers_like_cgi(sapi_headers_struct *sapi_headers)
392 {
393 char buf[SAPI_LSAPI_MAX_HEADER_LENGTH];
394 sapi_header_struct *h;
395 zend_llist_position pos;
396 zend_bool ignore_status = 0;
397 int response_status = SG(sapi_headers).http_response_code;
398
399 if (SG(request_info).no_headers == 1) {
400 LSAPI_FinalizeRespHeaders();
401 return SAPI_HEADER_SENT_SUCCESSFULLY;
402 }
403
404 if (SG(sapi_headers).http_response_code != 200)
405 {
406 int len;
407 zend_bool has_status = 0;
408
409 char *s;
410
411 if (SG(sapi_headers).http_status_line &&
412 (s = strchr(SG(sapi_headers).http_status_line, ' ')) != 0 &&
413 (s - SG(sapi_headers).http_status_line) >= 5 &&
414 strncasecmp(SG(sapi_headers).http_status_line, "HTTP/", 5) == 0
415 ) {
416 len = slprintf(buf, sizeof(buf), "Status:%s", s);
417 response_status = atoi((s + 1));
418 } else {
419 h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
420 while (h) {
421 if (h->header_len > sizeof("Status:")-1 &&
422 strncasecmp(h->header, "Status:", sizeof("Status:")-1) == 0
423 ) {
424 has_status = 1;
425 break;
426 }
427 h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
428 }
429 if (!has_status) {
430 http_error *err = (http_error*)http_error_codes;
431
432 while (err->code != 0) {
433 if (err->code == SG(sapi_headers).http_response_code) {
434 break;
435 }
436 err++;
437 }
438 if (err->msg) {
439 len = slprintf(buf, sizeof(buf), "Status: %d %s", SG(sapi_headers).http_response_code, err->msg);
440 } else {
441 len = slprintf(buf, sizeof(buf), "Status: %d", SG(sapi_headers).http_response_code);
442 }
443 }
444 }
445
446 if (!has_status) {
447 LSAPI_AppendRespHeader( buf, len );
448 ignore_status = 1;
449 }
450 }
451
452 h = (sapi_header_struct*)zend_llist_get_first_ex(&sapi_headers->headers, &pos);
453 while (h) {
454 /* prevent CRLFCRLF */
455 if (h->header_len) {
456 if (h->header_len > sizeof("Status:")-1 &&
457 strncasecmp(h->header, "Status:", sizeof("Status:")-1) == 0
458 ) {
459 if (!ignore_status) {
460 ignore_status = 1;
461 LSAPI_AppendRespHeader(h->header, h->header_len);
462 }
463 } else if (response_status == 304 && h->header_len > sizeof("Content-Type:")-1 &&
464 strncasecmp(h->header, "Content-Type:", sizeof("Content-Type:")-1) == 0
465 ) {
466 h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
467 continue;
468 } else {
469 LSAPI_AppendRespHeader(h->header, h->header_len);
470 }
471 }
472 h = (sapi_header_struct*)zend_llist_get_next_ex(&sapi_headers->headers, &pos);
473 }
474
475 LSAPI_FinalizeRespHeaders();
476 return SAPI_HEADER_SENT_SUCCESSFULLY;
477 }
478
479 /*
480 mod_lsapi mode or legacy LS mode
481 */
482 static int mod_lsapi_mode = 0;
483
484
485 /* {{{ sapi_lsapi_send_headers
486 */
sapi_lsapi_send_headers(sapi_headers_struct * sapi_headers)487 static int sapi_lsapi_send_headers(sapi_headers_struct *sapi_headers)
488 {
489 sapi_header_struct *h;
490 zend_llist_position pos;
491
492 if ( mod_lsapi_mode ) {
493 /* mod_lsapi mode */
494 return sapi_lsapi_send_headers_like_cgi(sapi_headers);
495 }
496
497 /* Legacy mode */
498 if ( lsapi_mode ) {
499 LSAPI_SetRespStatus( SG(sapi_headers).http_response_code );
500
501 h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);
502 while (h) {
503 if ( h->header_len > 0 ) {
504 LSAPI_AppendRespHeader(h->header, h->header_len);
505 }
506 h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);
507 }
508 if (SG(sapi_headers).send_default_content_type) {
509 char *hd;
510 int len;
511 char headerBuf[SAPI_LSAPI_MAX_HEADER_LENGTH];
512
513 hd = sapi_get_default_content_type();
514 len = snprintf( headerBuf, SAPI_LSAPI_MAX_HEADER_LENGTH - 1,
515 "Content-type: %s", hd );
516 efree(hd);
517
518 LSAPI_AppendRespHeader( headerBuf, len );
519 }
520 }
521 LSAPI_FinalizeRespHeaders();
522 return SAPI_HEADER_SENT_SUCCESSFULLY;
523
524
525 }
526 /* }}} */
527
528
529 /* {{{ sapi_lsapi_send_headers
530 */
sapi_lsapi_log_message(char * message,int syslog_type_int)531 static void sapi_lsapi_log_message(char *message, int syslog_type_int)
532 {
533 char buf[8192];
534 int len = strlen( message );
535 if ( *(message + len - 1 ) != '\n' )
536 {
537 snprintf( buf, 8191, "%s\n", message );
538 message = buf;
539 if (len > 8191)
540 len = 8191;
541 ++len;
542 }
543 LSAPI_Write_Stderr( message, len);
544 }
545 /* }}} */
546
547 /* Set to 1 to turn on log messages useful during development:
548 */
549 #if 0
550 static void log_message (const char *fmt, ...)
551 {
552 va_list ap;
553 va_start(ap, fmt);
554 char buf[0x100];
555 vsnprintf(buf, sizeof(buf), fmt, ap);
556 va_end(ap);
557 sapi_lsapi_log_message(buf
558 #if PHP_MAJOR_VERSION > 7 || (PHP_MAJOR_VERSION == 7 && PHP_MINOR_VERSION >= 1)
559 , 0
560 #endif
561 );
562 }
563 #define DEBUG_MESSAGE(fmt, ...) log_message("LS:%d " fmt "\n", __LINE__, ##__VA_ARGS__)
564 #else
565 #define DEBUG_MESSAGE(fmt, ...)
566 #endif
567
568 static int lsapi_activate_user_ini();
569
sapi_lsapi_activate()570 static int sapi_lsapi_activate()
571 {
572 char *path, *doc_root, *server_name;
573 size_t path_len, doc_root_len, server_name_len;
574
575 /* PATH_TRANSLATED should be defined at this stage but better safe than sorry :) */
576 if (!SG(request_info).path_translated) {
577 return FAILURE;
578 }
579
580 if (php_ini_has_per_host_config()) {
581 server_name = sapi_lsapi_getenv("SERVER_NAME", 0);
582 /* SERVER_NAME should also be defined at this stage..but better check it anyway */
583 if (server_name) {
584 server_name_len = strlen(server_name);
585 server_name = estrndup(server_name, server_name_len);
586 zend_str_tolower(server_name, server_name_len);
587 php_ini_activate_per_host_config(server_name, server_name_len);
588 efree(server_name);
589 }
590 }
591
592 if (php_ini_has_per_dir_config()) {
593 /* Prepare search path */
594 path_len = strlen(SG(request_info).path_translated);
595
596 /* Make sure we have trailing slash! */
597 if (!IS_SLASH(SG(request_info).path_translated[path_len])) {
598 path = emalloc(path_len + 2);
599 memcpy(path, SG(request_info).path_translated, path_len + 1);
600 path_len = zend_dirname(path, path_len);
601 path[path_len++] = DEFAULT_SLASH;
602 } else {
603 path = estrndup(SG(request_info).path_translated, path_len);
604 path_len = zend_dirname(path, path_len);
605 }
606 path[path_len] = 0;
607
608 /* Activate per-dir-system-configuration defined in php.ini and stored into configuration_hash during startup */
609 php_ini_activate_per_dir_config(path, path_len); /* Note: for global settings sake we check from root to path */
610
611 efree(path);
612 }
613
614 if (parse_user_ini && lsapi_activate_user_ini() == FAILURE) {
615 return FAILURE;
616 }
617 return SUCCESS;
618 }
619 /* {{{ sapi_module_struct cgi_sapi_module
620 */
621 static sapi_module_struct lsapi_sapi_module =
622 {
623 "litespeed",
624 "LiteSpeed V7.6",
625
626 php_lsapi_startup, /* startup */
627 php_module_shutdown_wrapper, /* shutdown */
628
629 sapi_lsapi_activate, /* activate */
630 sapi_lsapi_deactivate, /* deactivate */
631
632 sapi_lsapi_ub_write, /* unbuffered write */
633 sapi_lsapi_flush, /* flush */
634 NULL, /* get uid */
635 sapi_lsapi_getenv, /* getenv */
636
637 php_error, /* error handler */
638
639 NULL, /* header handler */
640 sapi_lsapi_send_headers, /* send headers handler */
641 NULL, /* send header handler */
642
643 sapi_lsapi_read_post, /* read POST data */
644 sapi_lsapi_read_cookies, /* read Cookies */
645
646 sapi_lsapi_register_variables, /* register server variables */
647 sapi_lsapi_log_message, /* Log message */
648 NULL, /* Get request time */
649 NULL, /* Child terminate */
650
651 STANDARD_SAPI_MODULE_PROPERTIES
652
653 };
654 /* }}} */
655
init_request_info(void)656 static void init_request_info( void )
657 {
658 char * pContentType = LSAPI_GetHeader( H_CONTENT_TYPE );
659 char * pAuth;
660
661 SG(request_info).content_type = pContentType ? pContentType : "";
662 SG(request_info).request_method = LSAPI_GetRequestMethod();
663 SG(request_info).query_string = LSAPI_GetQueryString();
664 SG(request_info).request_uri = LSAPI_GetScriptName();
665 SG(request_info).content_length = LSAPI_GetReqBodyLen();
666 SG(request_info).path_translated = estrdup( LSAPI_GetScriptFileName());
667
668 /* It is not reset by zend engine, set it to 200. */
669 SG(sapi_headers).http_response_code = 200;
670
671 pAuth = LSAPI_GetHeader( H_AUTHORIZATION );
672 php_handle_auth_data(pAuth);
673 }
674
lsapi_execute_script(zend_file_handle * file_handle)675 static int lsapi_execute_script( zend_file_handle * file_handle)
676 {
677 char *p;
678 int len;
679 file_handle->type = ZEND_HANDLE_FILENAME;
680 file_handle->handle.fd = 0;
681 file_handle->filename = SG(request_info).path_translated;
682 file_handle->free_filename = 0;
683 file_handle->opened_path = NULL;
684
685 p = argv0;
686 *p++ = ':';
687 len = strlen( SG(request_info).path_translated );
688 if ( len > 45 )
689 len = len - 45;
690 else
691 len = 0;
692 memccpy( p, SG(request_info).path_translated + len, 0, 46 );
693
694 php_execute_script(file_handle);
695 return 0;
696
697 }
698
lsapi_sigsegv(int signal)699 static void lsapi_sigsegv( int signal )
700 {
701 //fprintf(stderr, "lsapi_sigsegv: %d: Segmentation violation signal is caught during request shutdown\n", getpid());
702 _exit(1);
703 }
704
705 static int do_clean_shutdown = 1;
706
707 static int clean_onexit = 1;
708
709
lsapi_clean_shutdown()710 static void lsapi_clean_shutdown()
711 {
712 struct sigaction act;
713 int sa_rc;
714 struct itimerval tmv;
715 #if PHP_MAJOR_VERSION >= 7
716 zend_string * key;
717 #endif
718 clean_onexit = 1;
719 sigemptyset(&act.sa_mask);
720 act.sa_flags = 0;
721 act.sa_handler = lsapi_sigsegv;
722 sa_rc = sigaction(SIGINT, &act, NULL);
723 sa_rc = sigaction(SIGQUIT, &act, NULL);
724 sa_rc = sigaction(SIGILL, &act, NULL);
725 sa_rc = sigaction(SIGABRT, &act, NULL);
726 sa_rc = sigaction(SIGBUS, &act, NULL);
727 sa_rc = sigaction(SIGSEGV, &act, NULL);
728 sa_rc = sigaction(SIGTERM, &act, NULL);
729
730 sa_rc = sigaction(SIGPROF, &act, NULL);
731 memset(&tmv, 0, sizeof(struct itimerval));
732 tmv.it_value.tv_sec = 0;
733 tmv.it_value.tv_usec = 100000;
734 setitimer(ITIMER_PROF, &tmv, NULL);
735
736 #if PHP_MAJOR_VERSION >= 7
737 key = zend_string_init("error_reporting", 15, 1);
738 zend_alter_ini_entry_chars_ex(key, "0", 1,
739 PHP_INI_SYSTEM, PHP_INI_STAGE_SHUTDOWN, 1);
740 zend_string_release(key);
741 #else
742 zend_alter_ini_entry("error_reporting", 16, "0", 1,
743 PHP_INI_SYSTEM, PHP_INI_STAGE_SHUTDOWN);
744 #endif
745
746 zend_try {
747 php_request_shutdown(NULL);
748 } zend_end_try();
749 }
750
lsapi_sigterm(int signal)751 static void lsapi_sigterm(int signal)
752 {
753
754 // fprintf(stderr, "lsapi_sigterm: %d: clean_onexit %d\n", getpid(), clean_onexit );
755 if(!clean_onexit)
756 {
757 lsapi_clean_shutdown();
758 }
759 exit(1);
760 }
761
lsapi_atexit(void)762 static void lsapi_atexit(void)
763 {
764 //fprintf(stderr, "lsapi_atexit: %d: clean_onexit %d\n", getpid(), clean_onexit );
765 if(!clean_onexit)
766 {
767 lsapi_clean_shutdown();
768 }
769 }
770
771
lsapi_module_main(int show_source)772 static int lsapi_module_main(int show_source)
773 {
774 struct sigaction act;
775 int sa_rc;
776 zend_file_handle file_handle;
777 memset(&file_handle, 0, sizeof(file_handle));
778 if (php_request_startup() == FAILURE ) {
779 return -1;
780 }
781
782 if (do_clean_shutdown) {
783 sigemptyset(&act.sa_mask);
784 act.sa_flags = SA_NODEFER;
785 act.sa_handler = lsapi_sigterm;
786 sa_rc = sigaction( SIGINT, &act, NULL);
787 sa_rc = sigaction( SIGQUIT, &act, NULL);
788 sa_rc = sigaction( SIGILL, &act, NULL);
789 sa_rc = sigaction( SIGABRT, &act, NULL);
790 sa_rc = sigaction( SIGBUS, &act, NULL);
791 sa_rc = sigaction( SIGSEGV, &act, NULL);
792 sa_rc = sigaction( SIGTERM, &act, NULL);
793
794 clean_onexit = 0;
795 }
796
797 if (show_source) {
798 zend_syntax_highlighter_ini syntax_highlighter_ini;
799
800 php_get_highlight_struct(&syntax_highlighter_ini);
801 highlight_file(SG(request_info).path_translated, &syntax_highlighter_ini);
802 } else {
803 lsapi_execute_script( &file_handle);
804 }
805 zend_try {
806 php_request_shutdown(NULL);
807
808 clean_onexit = 1;
809
810 memset( argv0, 0, 46 );
811 } zend_end_try();
812 return 0;
813 }
814
815
alter_ini(const char * pKey,int keyLen,const char * pValue,int valLen,void * arg)816 static int alter_ini( const char * pKey, int keyLen, const char * pValue, int valLen,
817 void * arg )
818 {
819 zend_string * psKey;
820
821 int type = ZEND_INI_PERDIR;
822 int stage = PHP_INI_STAGE_RUNTIME;
823 if ( '\001' == *pKey ) {
824 ++pKey;
825 if ( *pKey == 4 ) {
826 type = ZEND_INI_SYSTEM;
827 /*
828 Use ACTIVATE stage in legacy mode only.
829
830 RUNTIME stage should be used here,
831 as with ACTIVATE it's impossible to change the option from script with ini_set
832 */
833 if(!mod_lsapi_mode)
834 {
835 stage = PHP_INI_STAGE_ACTIVATE;
836 }
837 }
838 else
839 {
840 stage = PHP_INI_STAGE_HTACCESS;
841 }
842 ++pKey;
843 --keyLen;
844 if (( keyLen == 7 )&&( strncasecmp( pKey, "engine", 6 )== 0 ))
845 {
846 if ( *pValue == '0' )
847 engine = 0;
848 }
849 else
850 {
851 --keyLen;
852 psKey = zend_string_init(pKey, keyLen, 1);
853 zend_alter_ini_entry_chars(psKey,
854 (char *)pValue, valLen,
855 type, stage);
856 zend_string_release(psKey);
857 }
858 }
859 return 1;
860 }
861
user_config_cache_entry_dtor(zval * el)862 static void user_config_cache_entry_dtor(zval *el)
863 {
864 user_config_cache_entry *entry = (user_config_cache_entry *)Z_PTR_P(el);
865 zend_hash_destroy(&entry->user_config);
866 free(entry);
867 }
868
user_config_cache_init()869 static void user_config_cache_init()
870 {
871 zend_hash_init(&user_config_cache, 0, NULL, user_config_cache_entry_dtor, 1);
872 }
873
pathlen_without_trailing_slash(char * path)874 static int pathlen_without_trailing_slash(char *path)
875 {
876 int len = (int)strlen(path);
877 while (len > 1 && /* mind "/" as root dir */
878 path[len-1] == DEFAULT_SLASH)
879 {
880 --len;
881 }
882 return len;
883 }
884
skip_slash(char * s)885 static inline char* skip_slash(char *s)
886 {
887 while (*s == DEFAULT_SLASH) {
888 ++s;
889 }
890 return s;
891 }
892
893 /**
894 * Walk down the path_stop starting at path_start.
895 *
896 * If path_start = "/path1" and path_stop = "/path1/path2/path3"
897 * the callback will be called 3 times with the next args:
898 *
899 * 1. "/path1/path2/path3"
900 * ^ end
901 * ^ start
902 * 2. "/path1/path2/path3"
903 * ^ end
904 * ^ start
905 * 3. "/path1/path2/path3"
906 * ^ end
907 * ^ start
908 *
909 * path_stop has to be a subdir of path_start
910 * or to be path_start itself.
911 *
912 * Both path args have to be absolute.
913 * Trailing slashes are allowed.
914 * NULL or empty string args are not allowed.
915 */
walk_down_the_path(char * path_start,char * path_stop,void (* cb)(char * begin,char * end,void * data),void * data)916 static void walk_down_the_path(char* path_start,
917 char* path_stop,
918 void (*cb)(char* begin,
919 char* end,
920 void* data),
921 void* data)
922 {
923 char *pos = path_stop + pathlen_without_trailing_slash(path_start);
924 cb(path_stop, pos, data);
925
926 while ((pos = skip_slash(pos))[0]) {
927 pos = strchr(pos, DEFAULT_SLASH);
928 if (!pos) {
929 /* The last token without trailing slash
930 */
931 cb(path_stop, path_stop + strlen(path_stop), data);
932 return;
933 }
934 cb(path_stop, pos, data);
935 }
936 }
937
938
939 typedef struct {
940 char *path;
941 uint32_t path_len;
942 char *doc_root;
943 user_config_cache_entry *entry;
944 } _lsapi_activate_user_ini_ctx;
945
946 typedef int (*fn_activate_user_ini_chain_t)
947 (_lsapi_activate_user_ini_ctx *ctx, void* next);
948
lsapi_activate_user_ini_basic_checks(_lsapi_activate_user_ini_ctx * ctx,void * next)949 static int lsapi_activate_user_ini_basic_checks(_lsapi_activate_user_ini_ctx *ctx,
950 void* next)
951 {
952 int rc = SUCCESS;
953 fn_activate_user_ini_chain_t *fn_next = next;
954
955 if (!PG(user_ini_filename) || !*PG(user_ini_filename)) {
956 return SUCCESS;
957 }
958
959 /* PATH_TRANSLATED should be defined at this stage */
960 ctx->path = SG(request_info).path_translated;
961 if (!ctx->path || !*ctx->path) {
962 return FAILURE;
963 }
964
965 ctx->doc_root = sapi_lsapi_getenv("DOCUMENT_ROOT", 0);
966 DEBUG_MESSAGE("doc_root: %s", ctx->doc_root);
967
968 if (*fn_next) {
969 rc = (*fn_next)(ctx, fn_next + 1);
970 }
971
972 return rc;
973 }
974
lsapi_activate_user_ini_mk_path(_lsapi_activate_user_ini_ctx * ctx,void * next)975 static int lsapi_activate_user_ini_mk_path(_lsapi_activate_user_ini_ctx *ctx,
976 void* next)
977 {
978 char *path;
979 int rc = SUCCESS;
980 fn_activate_user_ini_chain_t *fn_next = next;
981
982 /* Extract dir name from path_translated * and store it in 'path' */
983 ctx->path_len = strlen(ctx->path);
984 path = ctx->path = estrndup(SG(request_info).path_translated, ctx->path_len);
985 ctx->path_len = zend_dirname(path, ctx->path_len);
986
987 if (*fn_next) {
988 rc = (*fn_next)(ctx, fn_next + 1);
989 }
990
991 efree(path);
992 return rc;
993 }
994
lsapi_activate_user_ini_mk_realpath(_lsapi_activate_user_ini_ctx * ctx,void * next)995 static int lsapi_activate_user_ini_mk_realpath(_lsapi_activate_user_ini_ctx *ctx,
996 void* next)
997 {
998 char *real_path;
999 int rc = SUCCESS;
1000 fn_activate_user_ini_chain_t *fn_next = next;
1001
1002 if (!IS_ABSOLUTE_PATH(ctx->path, ctx->path_len)) {
1003 real_path = tsrm_realpath(ctx->path, NULL);
1004 if (!real_path) {
1005 return SUCCESS;
1006 }
1007 ctx->path = real_path;
1008 ctx->path_len = strlen(ctx->path);
1009 } else {
1010 real_path = NULL;
1011 }
1012
1013 if (*fn_next) {
1014 rc = (*fn_next)(ctx, fn_next + 1);
1015 }
1016
1017 if (real_path)
1018 efree(real_path);
1019 return rc;
1020 }
1021
lsapi_activate_user_ini_mk_user_config(_lsapi_activate_user_ini_ctx * ctx,void * next)1022 static int lsapi_activate_user_ini_mk_user_config(_lsapi_activate_user_ini_ctx *ctx,
1023 void* next)
1024 {
1025 fn_activate_user_ini_chain_t *fn_next = next;
1026
1027 /* Find cached config entry: If not found, create one */
1028 ctx->entry = zend_hash_str_find_ptr(&user_config_cache, ctx->path, ctx->path_len);
1029
1030 if (!ctx->entry)
1031 {
1032 ctx->entry = pemalloc(sizeof(user_config_cache_entry), 1);
1033 ctx->entry->expires = 0;
1034 zend_hash_init(&ctx->entry->user_config, 0, NULL,
1035 config_zval_dtor, 1);
1036 zend_hash_str_update_ptr(&user_config_cache, ctx->path,
1037 ctx->path_len, ctx->entry);
1038 }
1039
1040 if (*fn_next) {
1041 return (*fn_next)(ctx, fn_next + 1);
1042 } else {
1043 return SUCCESS;
1044 }
1045 }
1046
walk_down_the_path_callback(char * begin,char * end,void * data)1047 static void walk_down_the_path_callback(char* begin,
1048 char* end,
1049 void* data)
1050 {
1051 _lsapi_activate_user_ini_ctx *ctx = data;
1052 char tmp = end[0];
1053 end[0] = 0;
1054 php_parse_user_ini_file(begin, PG(user_ini_filename), &ctx->entry->user_config);
1055 end[0] = tmp;
1056 }
1057
lsapi_activate_user_ini_walk_down_the_path(_lsapi_activate_user_ini_ctx * ctx,void * next)1058 static int lsapi_activate_user_ini_walk_down_the_path(_lsapi_activate_user_ini_ctx *ctx,
1059 void* next)
1060 {
1061 time_t request_time = sapi_get_request_time();
1062 uint32_t docroot_len;
1063 int rc = SUCCESS;
1064 fn_activate_user_ini_chain_t *fn_next = next;
1065
1066 if (!ctx->entry->expires || request_time > ctx->entry->expires)
1067 {
1068 docroot_len = ctx->doc_root && ctx->doc_root[0]
1069 ? pathlen_without_trailing_slash(ctx->doc_root)
1070 : 0;
1071
1072 int is_outside_of_docroot = !docroot_len ||
1073 ctx->path_len < docroot_len ||
1074 strncmp(ctx->path, ctx->doc_root, docroot_len) != 0;
1075
1076 if (is_outside_of_docroot) {
1077 php_parse_user_ini_file(ctx->path, PG(user_ini_filename),
1078 &ctx->entry->user_config);
1079 } else {
1080 walk_down_the_path(ctx->doc_root, ctx->path,
1081 &walk_down_the_path_callback, ctx);
1082 }
1083
1084 ctx->entry->expires = request_time + PG(user_ini_cache_ttl);
1085 }
1086
1087 if (*fn_next) {
1088 rc = (*fn_next)(ctx, fn_next + 1);
1089 }
1090
1091 return rc;
1092 }
1093
lsapi_activate_user_ini_finally(_lsapi_activate_user_ini_ctx * ctx,void * next)1094 static int lsapi_activate_user_ini_finally(_lsapi_activate_user_ini_ctx *ctx,
1095 void* next)
1096 {
1097 int rc = SUCCESS;
1098 fn_activate_user_ini_chain_t *fn_next = next;
1099
1100 php_ini_activate_config(&ctx->entry->user_config, PHP_INI_PERDIR,
1101 PHP_INI_STAGE_HTACCESS);
1102
1103 if (*fn_next) {
1104 rc = (*fn_next)(ctx, fn_next + 1);
1105 }
1106
1107 return rc;
1108 }
1109
lsapi_activate_user_ini(void)1110 static int lsapi_activate_user_ini( void )
1111 {
1112 _lsapi_activate_user_ini_ctx ctx;
1113 /**
1114 * The reason to have this function list stacked
1115 * is each function now can define a scoped destructor.
1116 *
1117 * Passing control via function pointer is a sign of low coupling,
1118 * which means dependencies between these functions are to be
1119 * controlled from a single place
1120 * (here below, by modifying this function list order)
1121 */
1122 static const fn_activate_user_ini_chain_t fn_chain[] = {
1123 &lsapi_activate_user_ini_basic_checks,
1124 &lsapi_activate_user_ini_mk_path,
1125 &lsapi_activate_user_ini_mk_realpath,
1126 &lsapi_activate_user_ini_mk_user_config,
1127 &lsapi_activate_user_ini_walk_down_the_path,
1128 &lsapi_activate_user_ini_finally,
1129 NULL
1130 };
1131
1132 return fn_chain[0](&ctx, (fn_activate_user_ini_chain_t*)(fn_chain + 1));
1133 }
1134
1135
override_ini()1136 static void override_ini()
1137 {
1138
1139 LSAPI_ForeachSpecialEnv( alter_ini, NULL );
1140
1141 }
1142
1143
processReq(void)1144 static int processReq(void)
1145 {
1146 int ret = 0;
1147 zend_first_try {
1148
1149 /* avoid server_context==NULL checks */
1150 SG(server_context) = (void *) 1;
1151
1152 engine = 1;
1153 override_ini();
1154
1155 if ( engine ) {
1156 init_request_info();
1157
1158 if ( lsapi_module_main( source_highlight ) == -1 ) {
1159 ret = -1;
1160 }
1161 } else {
1162 LSAPI_AppendRespHeader( "status: 403", 11 );
1163 LSAPI_AppendRespHeader( "content-type: text/html", 23 );
1164 LSAPI_Write( "Forbidden: PHP engine is disable.\n", 34 );
1165 }
1166 } zend_end_try();
1167 return ret;
1168 }
1169
cli_usage(void)1170 static void cli_usage(void)
1171 {
1172 static const char * usage =
1173 "Usage: php\n"
1174 " php -[b|c|n|h|i|q|s|v|?] [<file>] [args...]\n"
1175 " Run in LSAPI mode, only '-b', '-s' and '-c' are effective\n"
1176 " Run in Command Line Interpreter mode when parameters are specified\n"
1177 "\n"
1178 " -b <address:port>|<port> Bind Path for external LSAPI Server mode\n"
1179 " -c <path>|<file> Look for php.ini file in this directory\n"
1180 " -n No php.ini file will be used\n"
1181 " -h This help\n"
1182 " -i PHP information\n"
1183 " -l Syntax check\n"
1184 " -q Quiet-mode. Suppress HTTP Header output.\n"
1185 " -s Display colour syntax highlighted source.\n"
1186 " -v Version number\n"
1187 " -? This help\n"
1188 "\n"
1189 " args... Arguments passed to script.\n";
1190 php_output_startup();
1191 php_output_activate();
1192 php_printf( "%s", usage );
1193 #ifdef PHP_OUTPUT_NEWAPI
1194 php_output_end_all();
1195 #else
1196 php_end_ob_buffers(1);
1197 #endif
1198 }
1199
parse_opt(int argc,char * argv[],int * climode,char ** php_ini_path,char ** php_bind)1200 static int parse_opt( int argc, char * argv[], int *climode,
1201 char **php_ini_path, char ** php_bind )
1202 {
1203 char ** p = &argv[1];
1204 char ** argend= &argv[argc];
1205 int c;
1206 while (( p < argend )&&(**p == '-' )) {
1207 c = *((*p)+1);
1208 ++p;
1209 switch( c ) {
1210 case 'b':
1211 if ( p >= argend ) {
1212 fprintf( stderr, "TCP or socket address must be specified following '-b' option.\n");
1213 return -1;
1214 }
1215 *php_bind = strdup(*p++);
1216 break;
1217
1218 case 'c':
1219 if ( p >= argend ) {
1220 fprintf( stderr, "<path> or <file> must be specified following '-c' option.\n");
1221
1222 return -1;
1223 }
1224 *php_ini_path = strdup( *p++ );
1225 break;
1226 case 's':
1227 source_highlight = 1;
1228 break;
1229 case 'n':
1230 ignore_php_ini = 1;
1231 break;
1232 case '?':
1233 if ( *((*(p-1))+2) == 's' )
1234 exit( 99 );
1235 case 'h':
1236 case 'i':
1237 case 'l':
1238 case 'q':
1239 case 'v':
1240 default:
1241 *climode = 1;
1242 break;
1243 }
1244 }
1245 if ( p - argv < argc ) {
1246 *climode = 1;
1247 }
1248 return 0;
1249 }
1250
cli_main(int argc,char * argv[])1251 static int cli_main( int argc, char * argv[] )
1252 {
1253
1254 static const char * ini_defaults[] = {
1255 "report_zend_debug", "0",
1256 "display_errors", "1",
1257 "register_argc_argv", "1",
1258 "html_errors", "0",
1259 "implicit_flush", "1",
1260 "output_buffering", "0",
1261 "max_execution_time", "0",
1262 "max_input_time", "-1",
1263 NULL
1264 };
1265
1266 const char ** ini;
1267 char ** p = &argv[1];
1268 char ** argend= &argv[argc];
1269 int ret = -1;
1270 int c;
1271 zend_string *psKey;
1272 lsapi_mode = 0; /* enter CLI mode */
1273
1274 #ifdef PHP_WIN32
1275 _fmode = _O_BINARY; /*sets default for file streams to binary */
1276 setmode(_fileno(stdin), O_BINARY); /* make the stdio mode be binary */
1277 setmode(_fileno(stdout), O_BINARY); /* make the stdio mode be binary */
1278 setmode(_fileno(stderr), O_BINARY); /* make the stdio mode be binary */
1279 #endif
1280
1281 zend_first_try {
1282 SG(server_context) = (void *) 1;
1283
1284 zend_uv.html_errors = 0; /* tell the engine we're in non-html mode */
1285 CG(in_compilation) = 0; /* not initialized but needed for several options */
1286 SG(options) |= SAPI_OPTION_NO_CHDIR;
1287
1288 for( ini = ini_defaults; *ini; ini+=2 ) {
1289 psKey = zend_string_init(*ini, strlen( *ini ), 1);
1290 zend_alter_ini_entry_chars(psKey,
1291 (char *)*(ini+1), strlen( *(ini+1) ),
1292 PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE);
1293 zend_string_release(psKey);
1294 }
1295
1296 while (( p < argend )&&(**p == '-' )) {
1297 c = *((*p)+1);
1298 ++p;
1299 switch( c ) {
1300 case 'q':
1301 break;
1302 case 'i':
1303 if (php_request_startup() != FAILURE) {
1304 php_print_info(0xFFFFFFFF);
1305 #ifdef PHP_OUTPUT_NEWAPI
1306 php_output_end_all();
1307 #else
1308 php_end_ob_buffers(1);
1309 #endif
1310 php_request_shutdown( NULL );
1311 ret = 0;
1312 }
1313 break;
1314 case 'v':
1315 if (php_request_startup() != FAILURE) {
1316 #if ZEND_DEBUG
1317 php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2018 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
1318 #else
1319 php_printf("PHP %s (%s) (built: %s %s)\nCopyright (c) 1997-2018 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
1320 #endif
1321 #ifdef PHP_OUTPUT_NEWAPI
1322 php_output_end_all();
1323 #else
1324 php_end_ob_buffers(1);
1325 #endif
1326 php_request_shutdown( NULL );
1327 ret = 0;
1328 }
1329 break;
1330 case 'c':
1331 ++p;
1332 /* fall through */
1333 case 's':
1334 break;
1335 case 'l':
1336 source_highlight = 2;
1337 break;
1338 case 'h':
1339 case '?':
1340 default:
1341 cli_usage();
1342 ret = 0;
1343 break;
1344
1345 }
1346 }
1347 if ( ret == -1 ) {
1348 if ( *p ) {
1349 zend_file_handle file_handle;
1350 memset(&file_handle, 0, sizeof(file_handle));
1351 file_handle.type = ZEND_HANDLE_FP;
1352 file_handle.handle.fp = VCWD_FOPEN(*p, "rb");
1353
1354 if ( file_handle.handle.fp ) {
1355 script_filename = *p;
1356 php_self = *p;
1357
1358 SG(request_info).path_translated = estrdup(*p);
1359 SG(request_info).argc = argc - (p - argv);
1360 SG(request_info).argv = p;
1361
1362 if (php_request_startup() == FAILURE ) {
1363 fclose( file_handle.handle.fp );
1364 ret = 2;
1365 } else {
1366 if (source_highlight == 1) {
1367 zend_syntax_highlighter_ini syntax_highlighter_ini;
1368
1369 php_get_highlight_struct(&syntax_highlighter_ini);
1370 highlight_file(SG(request_info).path_translated, &syntax_highlighter_ini);
1371 } else if (source_highlight == 2) {
1372 file_handle.filename = *p;
1373 file_handle.free_filename = 0;
1374 file_handle.opened_path = NULL;
1375 ret = php_lint_script(&file_handle);
1376 if (ret==SUCCESS) {
1377 zend_printf("No syntax errors detected in %s\n", file_handle.filename);
1378 } else {
1379 zend_printf("Errors parsing %s\n", file_handle.filename);
1380 }
1381
1382 } else {
1383 file_handle.filename = *p;
1384 file_handle.free_filename = 0;
1385 file_handle.opened_path = NULL;
1386
1387 php_execute_script(&file_handle);
1388 ret = EG(exit_status);
1389 }
1390
1391 php_request_shutdown( NULL );
1392 }
1393 } else {
1394 php_printf("Could not open input file: %s.\n", *p);
1395 }
1396 } else {
1397 cli_usage();
1398 }
1399 }
1400
1401 }zend_end_try();
1402
1403 php_module_shutdown();
1404
1405 #ifdef ZTS
1406 tsrm_shutdown();
1407 #endif
1408 return ret;
1409 }
1410
1411 static int s_stop;
litespeed_cleanup(int signal)1412 void litespeed_cleanup(int signal)
1413 {
1414 s_stop = signal;
1415 }
1416
1417
start_children(int children)1418 void start_children( int children )
1419 {
1420 struct sigaction act, old_term, old_quit, old_int, old_usr1;
1421 int running = 0;
1422 int status;
1423 pid_t pid;
1424
1425 /* Create a process group */
1426 setsid();
1427
1428 /* Set up handler to kill children upon exit */
1429 sigemptyset(&act.sa_mask);
1430 act.sa_flags = 0;
1431 act.sa_handler = litespeed_cleanup;
1432 if( sigaction( SIGTERM, &act, &old_term ) ||
1433 sigaction( SIGINT, &act, &old_int ) ||
1434 sigaction( SIGUSR1, &act, &old_usr1 ) ||
1435 sigaction( SIGQUIT, &act, &old_quit )) {
1436 perror( "Can't set signals" );
1437 exit( 1 );
1438 }
1439 s_stop = 0;
1440 while( 1 ) {
1441 while((!s_stop )&&( running < children )) {
1442 pid = fork();
1443 switch( pid ) {
1444 case 0: /* children process */
1445
1446 /* don't catch our signals */
1447 sigaction( SIGTERM, &old_term, 0 );
1448 sigaction( SIGQUIT, &old_quit, 0 );
1449 sigaction( SIGINT, &old_int, 0 );
1450 sigaction( SIGUSR1, &old_usr1, 0 );
1451 return ;
1452 case -1:
1453 perror( "php (pre-forking)" );
1454 exit( 1 );
1455 break;
1456 default: /* parent process */
1457 running++;
1458 break;
1459 }
1460 }
1461 if ( s_stop ) {
1462 break;
1463 }
1464 pid = wait( &status );
1465 running--;
1466 }
1467 kill( -getpgrp(), SIGUSR1 );
1468 exit( 0 );
1469 }
1470
setArgv0(int argc,char * argv[])1471 void setArgv0( int argc, char * argv[] )
1472 {
1473 char * p;
1474 int i;
1475 argv0 = argv[0] + strlen( argv[0] );
1476 p = argv0;
1477 while(( p > argv[0] )&&( p[-1] != '/'))
1478 --p;
1479 if ( p > argv[0] )
1480 {
1481 memmove( argv[0], p, argv0 - p );
1482 memset( argv[0] + ( argv0 - p ), 0, p - argv[0] );
1483 argv0 = argv[0] + (argv0 - p);
1484 }
1485 for( i = 1; i < argc; ++i )
1486 {
1487 memset( argv[i], 0, strlen( argv[i] ) );
1488 }
1489 }
1490
1491 #include <fcntl.h>
main(int argc,char * argv[])1492 int main( int argc, char * argv[] )
1493 {
1494 int ret;
1495 int bindFd;
1496
1497 char * php_ini_path = NULL;
1498 char * php_bind = NULL;
1499 int n;
1500 int climode = 0;
1501 struct timeval tv_req_begin;
1502 struct timeval tv_req_end;
1503 int slow_script_msec = 0;
1504 char time_buf[40];
1505
1506
1507 #ifdef HAVE_SIGNAL_H
1508 #if defined(SIGPIPE) && defined(SIG_IGN)
1509 signal(SIGPIPE, SIG_IGN);
1510 #endif
1511 #endif
1512
1513 #ifdef ZTS
1514 tsrm_startup(1, 1, 0, NULL);
1515 #endif
1516
1517 #if PHP_MAJOR_VERSION >= 7
1518 #if defined(ZEND_SIGNALS) || PHP_MINOR_VERSION > 0
1519 zend_signal_startup();
1520 #endif
1521 #endif
1522
1523 if (argc > 1 ) {
1524 if ( parse_opt( argc, argv, &climode,
1525 &php_ini_path, &php_bind ) == -1 ) {
1526 return 1;
1527 }
1528 }
1529 if ( climode ) {
1530 lsapi_sapi_module.phpinfo_as_text = 1;
1531 } else {
1532 setArgv0(argc, argv );
1533 }
1534
1535 sapi_startup(&lsapi_sapi_module);
1536
1537 #ifdef ZTS
1538 compiler_globals = ts_resource(compiler_globals_id);
1539 executor_globals = ts_resource(executor_globals_id);
1540 core_globals = ts_resource(core_globals_id);
1541 sapi_globals = ts_resource(sapi_globals_id);
1542 tsrm_ls = ts_resource(0);
1543
1544 SG(request_info).path_translated = NULL;
1545 #endif
1546
1547 lsapi_sapi_module.executable_location = argv[0];
1548
1549 /* Initialize from environment variables before processing command-line
1550 * options: the latter override the former.
1551 */
1552 init_sapi_from_env(&lsapi_sapi_module);
1553
1554 if ( ignore_php_ini )
1555 lsapi_sapi_module.php_ini_ignore = 1;
1556
1557 if ( php_ini_path ) {
1558 lsapi_sapi_module.php_ini_path_override = php_ini_path;
1559 }
1560
1561
1562 lsapi_sapi_module.ini_defaults = sapi_lsapi_ini_defaults;
1563
1564 if (php_module_startup(&lsapi_sapi_module, &litespeed_module_entry, 1) == FAILURE) {
1565 #ifdef ZTS
1566 tsrm_shutdown();
1567 #endif
1568 return FAILURE;
1569 }
1570
1571 if ( climode ) {
1572 return cli_main(argc, argv);
1573 }
1574
1575 if ( php_bind ) {
1576 bindFd = LSAPI_CreateListenSock( php_bind, 10 );
1577 if ( bindFd == -1 ) {
1578 fprintf( stderr,
1579 "Failed to bind socket [%s]: %s\n", php_bind, strerror( errno ) );
1580 exit( 2 );
1581 }
1582 if ( bindFd != 0 ) {
1583 dup2( bindFd, 0 );
1584 close( bindFd );
1585 }
1586 }
1587
1588 LSAPI_Init();
1589
1590 #if defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__)
1591 int is_criu = LSCRIU_Init(); // Must be called before the regular init as it unsets the parameters.
1592 #endif
1593
1594 LSAPI_Init_Env_Parameters( NULL );
1595 lsapi_mode = 1;
1596
1597 slow_script_msec = LSAPI_Get_Slow_Req_Msecs();
1598
1599 if ( php_bind ) {
1600 LSAPI_No_Check_ppid();
1601 free( php_bind );
1602 php_bind = NULL;
1603 }
1604
1605 int iRequestsProcessed = 0;
1606 int result;
1607
1608 if (do_clean_shutdown)
1609 atexit(lsapi_atexit);
1610
1611 while( ( result = LSAPI_Prefork_Accept_r( &g_req )) >= 0 ) {
1612 #if defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__)
1613 if (is_criu && !result) {
1614 LSCRIU_inc_req_procssed();
1615 }
1616 #endif
1617 if ( slow_script_msec ) {
1618 gettimeofday( &tv_req_begin, NULL );
1619 }
1620 ret = processReq();
1621 if ( slow_script_msec ) {
1622 gettimeofday( &tv_req_end, NULL );
1623 n = ((long) tv_req_end.tv_sec - tv_req_begin.tv_sec ) * 1000
1624 + (tv_req_end.tv_usec - tv_req_begin.tv_usec) / 1000;
1625 if ( n > slow_script_msec )
1626 {
1627 strftime( time_buf, 30, "%d/%b/%Y:%H:%M:%S", localtime( &tv_req_end.tv_sec ) );
1628 fprintf( stderr, "[%s] Slow PHP script: %d ms\n URL: %s %s\n Query String: %s\n Script: %s\n",
1629 time_buf, n, LSAPI_GetRequestMethod(),
1630 LSAPI_GetScriptName(), LSAPI_GetQueryString(),
1631 LSAPI_GetScriptFileName() );
1632
1633 }
1634 }
1635 LSAPI_Finish();
1636 if ( ret ) {
1637 break;
1638 }
1639 }
1640
1641 php_module_shutdown();
1642
1643 #ifdef ZTS
1644 tsrm_shutdown();
1645 #endif
1646 return ret;
1647 }
1648
1649
1650 /* LiteSpeed PHP module starts here */
1651
1652 /* {{{ arginfo */
1653 ZEND_BEGIN_ARG_INFO(arginfo_litespeed__void, 0)
1654 ZEND_END_ARG_INFO()
1655 /* }}} */
1656
1657 PHP_FUNCTION(litespeed_request_headers);
1658 PHP_FUNCTION(litespeed_response_headers);
1659 PHP_FUNCTION(apache_get_modules);
1660 PHP_FUNCTION(litespeed_finish_request);
1661
1662 PHP_MINFO_FUNCTION(litespeed);
1663
1664 zend_function_entry litespeed_functions[] = {
1665 PHP_FE(litespeed_request_headers, arginfo_litespeed__void)
1666 PHP_FE(litespeed_response_headers, arginfo_litespeed__void)
1667 PHP_FE(apache_get_modules, arginfo_litespeed__void)
1668 PHP_FE(litespeed_finish_request, arginfo_litespeed__void)
1669 PHP_FALIAS(getallheaders, litespeed_request_headers, arginfo_litespeed__void)
1670 PHP_FALIAS(apache_request_headers, litespeed_request_headers, arginfo_litespeed__void)
1671 PHP_FALIAS(apache_response_headers, litespeed_response_headers, arginfo_litespeed__void)
1672 {NULL, NULL, NULL}
1673 };
1674
PHP_MINIT_FUNCTION(litespeed)1675 static PHP_MINIT_FUNCTION(litespeed)
1676 {
1677 user_config_cache_init();
1678
1679 const char *p = getenv("LSPHP_ENABLE_USER_INI");
1680 if (p && 0 == strcasecmp(p, "on"))
1681 parse_user_ini = 1;
1682
1683 p = getenv("LSAPI_CLEAN_SHUTDOWN");
1684 if (p) {
1685 if (*p == '1' || 0 == strcasecmp(p, "on"))
1686 do_clean_shutdown = 1;
1687 else if (*p == '0' || 0 == strcasecmp(p, "off"))
1688 do_clean_shutdown = 0;
1689 }
1690 /*
1691 * mod_lsapi always sets this env var,
1692 * so we can detect mod_lsapi mode with its presense.
1693 */
1694 mod_lsapi_mode = ( getenv("LSAPI_DISABLE_CPAN_BEHAV") != NULL );
1695
1696 /* REGISTER_INI_ENTRIES(); */
1697 return SUCCESS;
1698 }
1699
1700
PHP_MSHUTDOWN_FUNCTION(litespeed)1701 static PHP_MSHUTDOWN_FUNCTION(litespeed)
1702 {
1703 zend_hash_destroy(&user_config_cache);
1704
1705 /* UNREGISTER_INI_ENTRIES(); */
1706 return SUCCESS;
1707 }
1708
1709 zend_module_entry litespeed_module_entry = {
1710 STANDARD_MODULE_HEADER,
1711 "litespeed",
1712 litespeed_functions,
1713 PHP_MINIT(litespeed),
1714 PHP_MSHUTDOWN(litespeed),
1715 NULL,
1716 NULL,
1717 NULL,
1718 NO_VERSION_YET,
1719 STANDARD_MODULE_PROPERTIES
1720 };
1721
add_associate_array(const char * pKey,int keyLen,const char * pValue,int valLen,void * arg)1722 static int add_associate_array( const char * pKey, int keyLen, const char * pValue, int valLen,
1723 void * arg )
1724 {
1725 add_assoc_string_ex((zval *)arg, (char *)pKey, keyLen, (char *)pValue);
1726 return 1;
1727 }
1728
1729
1730 /* {{{ proto array litespeed_request_headers(void)
1731 Fetch all HTTP request headers */
PHP_FUNCTION(litespeed_request_headers)1732 PHP_FUNCTION(litespeed_request_headers)
1733 {
1734 /* TODO: */
1735 if (ZEND_NUM_ARGS() > 0) {
1736 WRONG_PARAM_COUNT;
1737 }
1738 array_init(return_value);
1739
1740 LSAPI_ForeachOrgHeader( add_associate_array, return_value );
1741
1742 }
1743 /* }}} */
1744
1745
1746
1747 /* {{{ proto array litespeed_response_headers(void)
1748 Fetch all HTTP response headers */
PHP_FUNCTION(litespeed_response_headers)1749 PHP_FUNCTION(litespeed_response_headers)
1750 {
1751 sapi_header_struct *h;
1752 zend_llist_position pos;
1753 char * p;
1754 int len;
1755 char headerBuf[SAPI_LSAPI_MAX_HEADER_LENGTH];
1756
1757 if (ZEND_NUM_ARGS() > 0) {
1758 WRONG_PARAM_COUNT;
1759 }
1760
1761 if (!&SG(sapi_headers).headers) {
1762 RETURN_FALSE;
1763 }
1764 array_init(return_value);
1765
1766 h = zend_llist_get_first_ex(&SG(sapi_headers).headers, &pos);
1767 while (h) {
1768 if ( h->header_len > 0 ) {
1769 p = strchr( h->header, ':' );
1770 len = p - h->header;
1771 if (( p )&&( len > 0 )) {
1772 memmove( headerBuf, h->header, len );
1773 while( len > 0 && (isspace( headerBuf[len-1])) ) {
1774 --len;
1775 }
1776 headerBuf[len] = 0;
1777 if ( len ) {
1778 while( isspace(*++p));
1779 add_assoc_string_ex(return_value, headerBuf, len, p);
1780 }
1781 }
1782 }
1783 h = zend_llist_get_next_ex(&SG(sapi_headers).headers, &pos);
1784 }
1785 }
1786
1787 /* }}} */
1788
1789
1790 /* {{{ proto array apache_get_modules(void)
1791 Fetch all loaded module names */
PHP_FUNCTION(apache_get_modules)1792 PHP_FUNCTION(apache_get_modules)
1793 {
1794 static const char * mod_names[] =
1795 {
1796 "mod_rewrite", "mod_mime", "mod_headers", "mod_expires", "mod_auth_basic", NULL
1797 };
1798 const char **name = mod_names;
1799 /* TODO: */
1800 if (ZEND_NUM_ARGS() > 0) {
1801 WRONG_PARAM_COUNT;
1802 }
1803 array_init(return_value);
1804 while( *name )
1805 {
1806 add_next_index_string(return_value, *name);
1807 ++name;
1808 }
1809 }
1810 /* }}} */
1811
1812
1813 /* {{{ proto array litespeed_finish_request(void)
1814 Flushes all response data to the client */
PHP_FUNCTION(litespeed_finish_request)1815 PHP_FUNCTION(litespeed_finish_request)
1816 {
1817 if (ZEND_NUM_ARGS() > 0) {
1818 WRONG_PARAM_COUNT;
1819 }
1820
1821 php_output_end_all();
1822 php_header();
1823
1824 if (LSAPI_End_Response() != -1) {
1825 RETURN_TRUE;
1826 }
1827 RETURN_FALSE;
1828 }
1829 /* }}} */
1830
1831 /*
1832 * Local variables:
1833 * tab-width: 4
1834 * c-basic-offset: 4
1835 * End:
1836 * vim600: sw=4 ts=4 fdm=marker
1837 * vim<600: sw=4 ts=4
1838 */
1839