#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <limits.h> #include <zend.h> #include <zend_extensions.h> #include <embed/php_embed.h> #include <standard/php_var.h> #include <standard/info.h> #include "func.h" #ifndef ZEND_CONSTANT_SET_FLAGS #define ZEND_CONSTANT_SET_FLAGS(z,c,n) do { \ (z)->flags = c; \ (z)->module_number = n; \ } while(0) #endif #if PHP_VERSION_ID < 70400 void zend_stream_init_filename(zend_file_handle *file_handle, const char *script_file) { int c; memset(file_handle, 0, sizeof(zend_file_handle)); file_handle->type = ZEND_HANDLE_FP; file_handle->opened_path = NULL; file_handle->free_filename = 0; if (!(file_handle->handle.fp = VCWD_FOPEN(script_file, "rb"))) { fprintf(stderr, "Could not open input file: %s\n", script_file); zend_bailout(); return; } file_handle->filename = script_file; CG(start_lineno) = 1; /* #!php support */ c = fgetc(file_handle->handle.fp); if (c == '#' && (c = fgetc(file_handle->handle.fp)) == '!') { while (c != '\n' && c != '\r' && c != EOF) { c = fgetc(file_handle->handle.fp); /* skip to end of line */ } /* handle situations where line is terminated by \r\n */ if (c == '\r') { if (fgetc(file_handle->handle.fp) != '\n') { zend_long pos = zend_ftell(file_handle->handle.fp); zend_fseek(file_handle->handle.fp, pos - 1, SEEK_SET); } } CG(start_lineno) = 2; } else { rewind(file_handle->handle.fp); } } #endif void cli_register_file_handles(void) { php_stream *s_in, *s_out, *s_err; php_stream_context *sc_in=NULL, *sc_out=NULL, *sc_err=NULL; zend_constant ic, oc, ec; s_in = php_stream_open_wrapper_ex("php://stdin", "rb", 0, NULL, sc_in); s_out = php_stream_open_wrapper_ex("php://stdout", "wb", 0, NULL, sc_out); s_err = php_stream_open_wrapper_ex("php://stderr", "wb", 0, NULL, sc_err); if (s_in==NULL || s_out==NULL || s_err==NULL) { if (s_in) php_stream_close(s_in); if (s_out) php_stream_close(s_out); if (s_err) php_stream_close(s_err); return; } #if PHP_DEBUG /* do not close stdout and stderr */ s_out->flags |= PHP_STREAM_FLAG_NO_CLOSE; s_err->flags |= PHP_STREAM_FLAG_NO_CLOSE; #endif php_stream_to_zval(s_in, &ic.value); php_stream_to_zval(s_out, &oc.value); php_stream_to_zval(s_err, &ec.value); ZEND_CONSTANT_SET_FLAGS(&ic, CONST_CS, 0); ic.name = zend_string_init_interned("STDIN", sizeof("STDIN")-1, 0); zend_register_constant(&ic); ZEND_CONSTANT_SET_FLAGS(&oc, CONST_CS, 0); oc.name = zend_string_init_interned("STDOUT", sizeof("STDOUT")-1, 0); zend_register_constant(&oc); ZEND_CONSTANT_SET_FLAGS(&ec, CONST_CS, 0); ec.name = zend_string_init_interned("STDERR", sizeof("STDERR")-1, 0); zend_register_constant(&ec); } static void sapi_cli_register_variables(zval *var) { char *filename = SG(request_info).path_translated; if(!filename) filename = ""; //printf("filename = %s\n", filename); php_import_environment_variables(var); php_register_variable("PHP_SELF", filename, var); php_register_variable("SCRIPT_NAME", filename, var); php_register_variable("SCRIPT_FILENAME", filename, var); php_register_variable("PATH_TRANSLATED", filename, var); php_register_variable("DOCUMENT_ROOT", "", var); //php_var_dump(var, 0); } static int php_threadtask_startup(sapi_module_struct *sapi_module) { if (php_module_startup(sapi_module, &threadtask_module_entry, 1) == FAILURE) { return FAILURE; } return SUCCESS; } static int module_name_cmp(Bucket *f, Bucket *s) { return strcasecmp(((zend_module_entry *)Z_PTR(f->val))->name, ((zend_module_entry *)Z_PTR(s->val))->name); } static void print_modules(void) { HashTable sorted_registry; zend_module_entry *module; zend_hash_init(&sorted_registry, 50, NULL, NULL, 0); zend_hash_copy(&sorted_registry, &module_registry, NULL); zend_hash_sort(&sorted_registry, module_name_cmp, 0); ZEND_HASH_FOREACH_PTR(&sorted_registry, module) { php_printf("%s\n", module->name); } ZEND_HASH_FOREACH_END(); zend_hash_destroy(&sorted_registry); } static void print_extension_info(zend_extension *ext) { php_printf("%s\n", ext->name); } static int extension_name_cmp(const zend_llist_element **f, const zend_llist_element **s) { zend_extension *fe = (zend_extension*)(*f)->data; zend_extension *se = (zend_extension*)(*s)->data; return strcmp(fe->name, se->name); } static void print_extensions(void) { zend_llist sorted_exts; zend_llist_copy(&sorted_exts, &zend_extensions); sorted_exts.dtor = NULL; zend_llist_sort(&sorted_exts, extension_name_cmp); zend_llist_apply(&sorted_exts, (llist_apply_func_t) print_extension_info); zend_llist_destroy(&sorted_exts); } int main(int argc, char *argv[]) { zend_file_handle file_handle; int opt; char path[PATH_MAX]; size_t sz = readlink("/proc/self/exe", path, PATH_MAX); path[sz] = '\0'; char *ini_path_override = NULL; int is_module_list = 0, is_print_info = 0; while((opt = getopt(argc, argv, "Dd:t:rc:mivh?")) != -1) { switch(opt) { case 'D': isDebug = 1; break; case 'd': delay = atoi(optarg); if(delay < 1) delay = 1; break; case 't': maxthreads = atoi(optarg); if(maxthreads < 1) maxthreads = 1; break; case 'r': isReload = 1; break; case 'c': ini_path_override = optarg; break; case 'm': is_module_list = 1; break; case 'i': is_print_info = 1; break; case 'v': printf("%s\n", PHP_VERSION); return 0; break; case 'h': case '?': default: goto usage; } } if(is_module_list == 0 && is_print_info == 0 && optind >= argc) { goto usage; } thread_init(); php_embed_module.executable_location = path; php_embed_module.php_ini_path_override = ini_path_override; #ifndef SAPI_NAME php_embed_module.name = "cli"; #else php_embed_module.name = SAPI_NAME; #endif php_embed_module.startup = php_threadtask_startup; php_embed_module.register_server_variables = sapi_cli_register_variables; php_embed_module.phpinfo_as_text = is_print_info; old_ub_write_handler = php_embed_module.ub_write; old_flush_handler = php_embed_module.flush; php_embed_module.ub_write = php_thread_ub_write_handler; php_embed_module.flush = php_thread_flush_handler; if(php_embed_init(argc-optind, argv+optind) == FAILURE) { fprintf(stderr, "php_embed_init failure\n"); return 1; } zend_register_string_constant(ZEND_STRL("THREAD_TASK_NAME"), "main", CONST_CS, PHP_USER_CONSTANT); cli_register_file_handles(); if(is_module_list) { php_printf("[PHP Modules]\n"); print_modules(); php_printf("\n[Zend Modules]\n"); print_extensions(); php_printf("\n"); php_output_end_all(); EG(exit_status) = 0; goto out; } if(is_print_info) { php_print_info(PHP_INFO_ALL & ~PHP_INFO_CREDITS); php_output_end_all(); EG(exit_status) = 0; goto out; } thread_running(); #if PHP_VERSION_ID >= 70400 CG(skip_shebang) = 1; #endif SG(request_info).path_translated = argv[optind]; zend_register_long_constant(ZEND_STRL("THREAD_TASK_NUM"), maxthreads, CONST_CS, PHP_USER_CONSTANT); zend_register_long_constant(ZEND_STRL("THREAD_TASK_DELAY"), delay, CONST_CS, PHP_USER_CONSTANT); dprintf("BEGIN THREADTASK\n"); zend_first_try { zend_stream_init_filename(&file_handle, argv[optind]); php_execute_script(&file_handle); } zend_catch { if(EG(exit_status)) { isReload = 1; } } zend_end_try(); dprintf("END THREADTASK\n"); if(isReload) { dprintf("RELOAD THREADTASK\n"); char **args = (char**) malloc(sizeof(char*)*(argc+1)); memcpy(args, argv, sizeof(char*)*argc); args[argc] = NULL; execv(path, args); perror("execv"); } out: php_embed_shutdown(); thread_destroy(); return 0; usage: fprintf(stderr, "usage: %s [[-D] [-d <delay>] [-t <threads>] [-r] [ -c <path|file>] [-m | -v | -i] --] <phpfile> args...\n" " -D Debug info\n" " -d <delay> Delay seconds\n" " -t <threads> Max threads\n" " -r Auto reload\n" " -c <path|file> Look for php.ini file in this directory\n" " -m PHP extension list\n" " -v PHP Version\n" " -i PHP information\n" , argv[0]); return 255; }