2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) |
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 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 +----------------------------------------------------------------------+
16 #include "hphp/runtime/base/program_functions.h"
18 #include "hphp/runtime/base/types.h"
19 #include "hphp/runtime/base/type_conversions.h"
20 #include "hphp/runtime/base/builtin_functions.h"
21 #include "hphp/runtime/base/execution_context.h"
22 #include "hphp/runtime/base/thread_init_fini.h"
23 #include "hphp/runtime/base/code_coverage.h"
24 #include "hphp/runtime/base/runtime_option.h"
25 #include "hphp/util/shared_memory_allocator.h"
26 #include "hphp/runtime/base/server/pagelet_server.h"
27 #include "hphp/runtime/base/server/xbox_server.h"
28 #include "hphp/runtime/base/server/http_server.h"
29 #include "hphp/runtime/base/server/replay_transport.h"
30 #include "hphp/runtime/base/server/http_request_handler.h"
31 #include "hphp/runtime/base/server/admin_request_handler.h"
32 #include "hphp/runtime/base/server/server_stats.h"
33 #include "hphp/runtime/base/server/server_note.h"
34 #include "hphp/runtime/base/memory/memory_manager.h"
35 #include "hphp/util/process.h"
36 #include "hphp/util/capability.h"
37 #include "hphp/util/embedded_data.h"
38 #include "hphp/util/timer.h"
39 #include "hphp/util/stack_trace.h"
40 #include "hphp/util/light_process.h"
41 #include "hphp/util/repo_schema.h"
42 #include "hphp/runtime/base/stat_cache.h"
43 #include "hphp/runtime/ext/extension.h"
44 #include "hphp/runtime/ext/ext_fb.h"
45 #include "hphp/runtime/ext/ext_json.h"
46 #include "hphp/runtime/ext/ext_variable.h"
47 #include "hphp/runtime/ext/ext_apc.h"
48 #include "hphp/runtime/ext/ext_function.h"
49 #include "hphp/runtime/debugger/debugger.h"
50 #include "hphp/runtime/debugger/debugger_client.h"
51 #include "hphp/runtime/base/util/simple_counter.h"
52 #include "hphp/runtime/base/util/extended_logger.h"
53 #include "hphp/runtime/base/file/stream_wrapper_registry.h"
55 #include <boost/program_options/options_description.hpp>
56 #include <boost/program_options/positional_options.hpp>
57 #include <boost/program_options/variables_map.hpp>
58 #include <boost/program_options/parsers.hpp>
60 #include <oniguruma.h>
61 #include "libxml/parser.h"
63 #include "hphp/runtime/base/file_repository.h"
65 #include "hphp/runtime/vm/runtime.h"
66 #include "hphp/runtime/vm/repo.h"
67 #include "hphp/runtime/vm/jit/translator.h"
68 #include "hphp/compiler/builtin_symbols.h"
70 using namespace boost::program_options
;
72 extern char **environ
;
74 #define MAX_INPUT_NESTING_LEVEL 64
78 extern InitFiniNode
*extra_process_init
, *extra_process_exit
;
80 void initialize_repo();
83 * XXX: VM process initialization is handled through a function
84 * pointer so libhphp_runtime.a can be linked into programs that don't
85 * actually initialize the VM.
87 void (*g_vmProcessInit
)();
89 ///////////////////////////////////////////////////////////////////////////////
92 struct ProgramOptions
{
95 StringVec confStrings
;
105 bool noSafeAccessCheck
;
112 Eval::DebuggerClientOptions debugger_options
;
117 StartTime() : startTime(time(nullptr)) {}
120 static StartTime s_startTime
;
121 static string tempFile
;
123 time_t start_time() {
124 return s_startTime
.startTime
;
127 static void process_cmd_arguments(int argc
, char **argv
) {
128 SystemGlobals
*g
= (SystemGlobals
*)get_global_variables();
130 for (int i
= 0; i
< argc
; i
++) {
131 g
->GV(argv
).lvalAt() = String(argv
[i
]);
135 void process_env_variables(Variant
&variables
) {
136 for (std::map
<string
, string
>::const_iterator iter
=
137 RuntimeOption::EnvVariables
.begin();
138 iter
!= RuntimeOption::EnvVariables
.end(); ++iter
) {
139 variables
.set(String(iter
->first
), String(iter
->second
));
141 for (char **env
= environ
; env
&& *env
; env
++) {
142 char *p
= strchr(*env
, '=');
144 String
name(*env
, p
- *env
, CopyString
);
145 register_variable(variables
, (char*)name
.data(),
146 String(p
+ 1, CopyString
));
151 void register_variable(Variant
&variables
, char *name
, CVarRef value
,
152 bool overwrite
/* = true */) {
153 // ignore leading spaces in the variable name
155 while (*var
&& *var
== ' ') {
159 // ensure that we don't have spaces or dots in the variable name
161 bool is_array
= false;
162 char *ip
= nullptr; // index pointer
165 if (*p
== ' ' || *p
== '.') {
167 } else if (*p
== '[') {
174 int var_len
= p
- var
;
176 // empty variable name, or variable name with a space in it
180 vector
<Variant
> gpc_elements
;
181 gpc_elements
.reserve(MAX_INPUT_NESTING_LEVEL
); // important, so no resize
182 Variant
*symtable
= &variables
;
184 int index_len
= var_len
;
189 if (++nest_level
> MAX_INPUT_NESTING_LEVEL
) {
190 Logger::Warning("Input variable nesting level exceeded");
203 ip
= strchr(ip
, ']');
205 // PHP variables cannot contain '[' in their names,
206 // so we replace the character with a '_'
207 *(index_s
- 1) = '_';
211 index_len
= strlen(index
);
216 new_idx_len
= strlen(index_s
);
220 symtable
->append(Array::Create());
221 gpc_elements
.push_back(uninit_null());
222 gpc_elements
.back().assignRef(
223 symtable
->lvalAt((int)symtable
->toArray().size() - 1));
225 String
key(index
, index_len
, CopyString
);
226 Variant v
= symtable
->rvalAt(key
);
227 if (v
.isNull() || !v
.is(KindOfArray
)) {
228 symtable
->set(key
, Array::Create());
230 gpc_elements
.push_back(uninit_null());
231 gpc_elements
.back().assignRef(symtable
->lvalAt(key
));
233 symtable
= &gpc_elements
.back();
234 /* ip pointed to the '[' character, now obtain the key */
236 index_len
= new_idx_len
;
249 symtable
->append(value
);
251 String
key(index
, index_len
, CopyString
);
252 if (overwrite
|| !symtable
->toArray().exists(key
)) {
253 symtable
->set(key
, value
);
259 enum ContextOfException
{
260 ReqInitException
= 1,
265 static void handle_exception_append_bt(std::string
& errorMsg
,
266 const ExtendedException
& e
) {
267 Array bt
= e
.getBackTrace();
269 errorMsg
+= ExtendedLogger::StringOfStackTrace(bt
);
273 static void handle_exception_helper(bool& ret
,
274 ExecutionContext
* context
,
275 std::string
& errorMsg
,
276 ContextOfException where
,
281 } catch (const Eval::DebuggerException
&e
) {
283 } catch (const ExitException
&e
) {
284 if (where
== ReqInitException
) {
286 } else if (where
!= HandlerException
&&
287 !context
->getExitCallback().isNull() &&
288 f_is_callable(context
->getExitCallback())) {
289 Array stack
= e
.getBackTrace();
290 Array argv
= CREATE_VECTOR2(e
.ExitCode
, stack
);
291 vm_call_user_func(context
->getExitCallback(), argv
);
293 } catch (const PhpFileDoesNotExistException
&e
) {
295 if (where
!= HandlerException
) {
296 raise_notice("%s", e
.getMessage().c_str());
298 Logger::Error("%s", e
.getMessage().c_str());
301 handle_exception_append_bt(errorMsg
, e
);
303 } catch (const UncatchableException
&e
) {
307 if (RuntimeOption::ServerStackTrace
) {
309 } else if (RuntimeOption::InjectedStackTrace
) {
310 errorMsg
= e
.getMessage();
312 errorMsg
+= ExtendedLogger::StringOfStackTrace(e
.getBackTrace());
314 Logger::Error("%s", errorMsg
.c_str());
316 handle_exception_append_bt(errorMsg
, e
);
318 } catch (const Exception
&e
) {
320 bool origError
= error
;
321 std::string origErrorMsg
= errorMsg
;
325 if (where
== HandlerException
) {
326 errorMsg
= "Exception handler threw an exception: ";
328 errorMsg
+= e
.what();
329 if (where
== InvokeException
) {
330 bool handlerRet
= context
->onFatalError(e
);
334 errorMsg
= origErrorMsg
;
337 Logger::Error("%s", errorMsg
.c_str());
340 const ExtendedException
*ee
= dynamic_cast<const ExtendedException
*>(&e
);
342 handle_exception_append_bt(errorMsg
, *ee
);
345 } catch (const Object
&e
) {
347 bool origError
= error
;
348 std::string origErrorMsg
= errorMsg
;
352 if (where
== HandlerException
) {
353 errorMsg
= "Exception handler threw an object exception: ";
356 errorMsg
+= e
.toString().data();
358 errorMsg
+= "(unable to call toString())";
360 if (where
== InvokeException
) {
361 bool handlerRet
= context
->onUnhandledException(e
);
365 errorMsg
= origErrorMsg
;
368 Logger::Error("%s", errorMsg
.c_str());
373 errorMsg
= "(unknown exception was thrown)";
374 Logger::Error("%s", errorMsg
.c_str());
378 static bool hphp_chdir_file(const string filename
) {
380 String s
= File::TranslatePath(filename
);
381 char *buf
= strndup(s
.data(), s
.size());
382 char *dir
= dirname(buf
);
385 if (File::IsVirtualDirectory(dir
)) {
386 g_context
->setCwd(String(dir
, CopyString
));
391 if ((sb
.st_mode
& S_IFMT
) == S_IFDIR
) {
394 g_context
->setCwd(String(dir
, CopyString
));
403 void handle_destructor_exception(const char* situation
) {
408 } catch (ExitException
&e
) {
409 // ExitException is fine, no need to show a warning.
410 ThreadInfo::s_threadInfo
->setPendingException(e
.clone());
412 } catch (Object
&e
) {
413 // For user exceptions, invoke the user exception handler
414 errorMsg
= situation
;
415 errorMsg
+= " threw an object exception: ";
417 errorMsg
+= e
.toString().data();
419 errorMsg
+= "(unable to call toString())";
421 } catch (Exception
&e
) {
422 ThreadInfo::s_threadInfo
->setPendingException(e
.clone());
423 errorMsg
= situation
;
424 errorMsg
+= " raised a fatal error: ";
425 errorMsg
+= e
.what();
427 errorMsg
= situation
;
428 errorMsg
+= " threw an unknown exception";
430 // For fatal errors and unknown exceptions, we raise a warning.
431 // If there is a user error handler it will be invoked, otherwise
432 // the default error handler will be invoked.
434 raise_debugging("%s", errorMsg
.c_str());
436 // The user error handler fataled or threw an exception,
437 // print out the error message directly to the log
438 Logger::Warning("%s", errorMsg
.c_str());
442 static const StaticString
445 s_HHVM_JIT("HHVM_JIT"),
446 s_REQUEST_START_TIME("REQUEST_START_TIME"),
447 s_REQUEST_TIME("REQUEST_TIME"),
448 s_REQUEST_TIME_FLOAT("REQUEST_TIME_FLOAT"),
449 s_DOCUMENT_ROOT("DOCUMENT_ROOT"),
450 s_SCRIPT_FILENAME("SCRIPT_FILENAME"),
451 s_SCRIPT_NAME("SCRIPT_NAME"),
452 s_PHP_SELF("PHP_SELF"),
456 s_HOSTNAME("HOSTNAME");
458 void execute_command_line_begin(int argc
, char **argv
, int xhprof
) {
459 StackTraceNoHeap::AddExtraLogging("ThreadType", "CLI");
461 for (int i
= 0; i
< argc
; i
++) {
465 StackTraceNoHeap::AddExtraLogging("Arguments", args
.c_str());
468 ExecutionContext
*context
= g_context
.getNoCheck();
469 context
->obSetImplicitFlush(true);
471 SystemGlobals
*g
= (SystemGlobals
*)get_global_variables();
473 process_env_variables(g
->GV(_ENV
));
474 g
->GV(_ENV
).set(s_HPHP
, 1);
475 g
->GV(_ENV
).set(s_HHVM
, 1);
476 if (RuntimeOption::EvalJit
) {
477 g
->GV(_ENV
).set(s_HHVM_JIT
, 1);
480 process_cmd_arguments(argc
, argv
);
482 Variant
&server
= g
->GV(_SERVER
);
483 process_env_variables(server
);
485 struct timeval tp
= {0};
487 if (!gettimeofday(&tp
, nullptr)) {
488 now_double
= (double)(tp
.tv_sec
+ tp
.tv_usec
/ 1000000.00);
492 now_double
= (double)now
;
494 String file
= empty_string
;
496 file
= NEW(StringData
)(argv
[0], AttachLiteral
);
498 server
.set(s_REQUEST_START_TIME
, now
);
499 server
.set(s_REQUEST_TIME
, now
);
500 server
.set(s_REQUEST_TIME_FLOAT
, now_double
);
501 server
.set(s_DOCUMENT_ROOT
, empty_string
);
502 server
.set(s_SCRIPT_FILENAME
, file
);
503 server
.set(s_SCRIPT_NAME
, file
);
504 server
.set(s_PHP_SELF
, file
);
505 server
.set(s_argv
, g
->GV(argv
));
506 server
.set(s_argc
, g
->GV(argc
));
507 server
.set(s_PWD
, g_context
->getCwd());
509 if (!gethostname(hostname
, 1024)) {
510 server
.set(s_HOSTNAME
, String(hostname
, CopyString
));
513 for(std::map
<string
,string
>::iterator it
=
514 RuntimeOption::ServerVariables
.begin(),
515 end
= RuntimeOption::ServerVariables
.end(); it
!= end
; ++it
) {
516 server
.set(String(it
->first
.c_str()), String(it
->second
.c_str()));
520 f_xhprof_enable(xhprof
, uninit_null());
524 void execute_command_line_end(int xhprof
, bool coverage
, const char *program
) {
525 ThreadInfo
*ti
= ThreadInfo::s_threadInfo
.getNoCheck();
527 if (RuntimeOption::EvalJit
&& RuntimeOption::EvalDumpTC
) {
528 HPHP::Transl::tc_dump();
532 f_var_dump(f_json_encode(f_xhprof_disable()));
534 hphp_context_exit(g_context
.getNoCheck(), true, true, program
);
536 if (coverage
&& ti
->m_reqInjectionData
.getCoverage() &&
537 !RuntimeOption::CodeCoverageOutputFile
.empty()) {
538 ti
->m_coverage
->Report(RuntimeOption::CodeCoverageOutputFile
);
542 static void pagein_self(void) {
543 unsigned long begin
, end
, inode
, pgoff
;
544 char mapname
[PATH_MAX
];
552 // pad due to the spaces between the inode number and the mapname
553 bufsz
= sizeof(unsigned long) * 4 + sizeof(mapname
) + sizeof(char) * 11 + 100;
554 buf
= (char *)malloc(bufsz
);
558 Timer
timer(Timer::WallTime
, "mapping self");
559 fp
= fopen("/proc/self/maps", "r");
562 if (fgets(buf
, bufsz
, fp
) == 0)
564 r
= sscanf(buf
, "%lx-%lx %4s %lx %5s %ld %s",
565 &begin
, &end
, perm
, &pgoff
, dev
, &inode
, mapname
);
567 // page in read-only segments that correspond to a file on disk
571 access(mapname
, F_OK
) != 0) {
575 if (mlock((void *)begin
, end
- begin
) == 0) {
576 if (!RuntimeOption::LockCodeMemory
) {
577 munlock((void *)begin
, end
- begin
);
586 /* Sets RuntimeOption::ExecutionMode according
587 * to commandline options prior to config load
589 static void set_execution_mode(string mode
) {
590 if (mode
== "daemon" || mode
== "server" || mode
== "replay") {
591 RuntimeOption::ExecutionMode
= "srv";
592 Logger::Escape
= true;
593 } else if (mode
== "run" || mode
== "debug") {
594 RuntimeOption::ExecutionMode
= "cli";
595 Logger::Escape
= false;
596 } else if (mode
== "translate") {
597 RuntimeOption::ExecutionMode
= "";
598 Logger::Escape
= false;
601 always_assert(false);
605 static int start_server(const std::string
&username
) {
606 // Before we start the webserver, make sure the entire
607 // binary is paged into memory.
610 set_execution_mode("server");
611 HttpRequestHandler::GetAccessLog().init
612 (RuntimeOption::AccessLogDefaultFormat
, RuntimeOption::AccessLogs
,
614 AdminRequestHandler::GetAccessLog().init
615 (RuntimeOption::AdminLogFormat
, RuntimeOption::AdminLogSymLink
,
616 RuntimeOption::AdminLogFile
,
619 void *sslCTX
= nullptr;
620 if (RuntimeOption::EnableSSL
) {
621 #ifdef _EVENT_USE_OPENSSL
622 struct ssl_config config
;
623 if (RuntimeOption::SSLCertificateFile
!= "" &&
624 RuntimeOption::SSLCertificateKeyFile
!= "") {
625 config
.cert_file
= (char*)RuntimeOption::SSLCertificateFile
.c_str();
626 config
.pk_file
= (char*)RuntimeOption::SSLCertificateKeyFile
.c_str();
627 sslCTX
= evhttp_init_openssl(&config
);
628 if (!RuntimeOption::SSLCertificateDir
.empty()) {
629 ServerNameIndication::load(sslCTX
, config
,
630 RuntimeOption::SSLCertificateDir
);
633 Logger::Error("Invalid certificate file or key file");
636 Logger::Error("A SSL enabled libevent is required");
640 #if !defined(SKIP_USER_CHANGE)
641 if (!username
.empty()) {
642 if (Logger::UseCronolog
) {
643 Cronolog::changeOwner(username
, RuntimeOption::LogFileSymLink
);
645 Capability::ChangeUnixUser(username
);
646 LightProcess::ChangeUser(username
);
650 Capability::SetDumpable();
652 // Create the HttpServer before any warmup requests to properly
653 // initialize the process
654 HttpServer::Server
= HttpServerPtr(new HttpServer(sslCTX
));
656 // If we have any warmup requests, replay them before listening for
658 for (auto& file
: RuntimeOption::ServerWarmupRequests
) {
659 HttpRequestHandler handler
;
662 gettime(CLOCK_MONOTONIC
, &start
);
664 Logger::Info("Replaying warmup request %s", file
.c_str());
666 rt
.onRequestStart(start
);
667 rt
.replayInput(Hdf(file
));
668 handler
.handleRequest(&rt
);
669 Logger::Info("Finished successfully");
670 } catch (std::exception
& e
) {
674 Logger::Info("Got exception during warmup: %s", error
.c_str());
678 HttpServer::Server
->run();
682 string
translate_stack(const char *hexencoded
, bool with_frame_numbers
) {
683 if (!hexencoded
|| !*hexencoded
) {
687 StackTrace
st(hexencoded
);
688 StackTrace::FramePtrVec frames
;
691 std::ostringstream out
;
692 for (unsigned int i
= 0; i
< frames
.size(); i
++) {
693 StackTrace::FramePtr f
= frames
[i
];
694 if (with_frame_numbers
) {
695 out
<< "# " << (i
< 10 ? " " : "") << i
<< ' ';
697 out
<< f
->toString();
703 ///////////////////////////////////////////////////////////////////////////////
705 static void prepare_args(int &argc
, char **&argv
, const StringVec
&args
,
707 argv
= (char **)malloc((args
.size() + 2) * sizeof(char*));
710 argv
[argc
++] = (char*)file
;
712 for (int i
= 0; i
< (int)args
.size(); i
++) {
713 argv
[argc
++] = (char*)args
[i
].c_str();
715 argv
[argc
] = nullptr;
718 static int execute_program_impl(int argc
, char **argv
);
719 int execute_program(int argc
, char **argv
) {
723 init_thread_locals();
724 ret_code
= execute_program_impl(argc
, argv
);
725 } catch (const Exception
&e
) {
726 Logger::Error("Uncaught exception: %s", e
.what());
727 } catch (const FailedAssertion
& fa
) {
729 StackTraceNoHeap::AddExtraLogging("Assertion failure", fa
.summary
);
731 } catch (const std::exception
&e
) {
732 Logger::Error("Uncaught exception: %s", e
.what());
734 Logger::Error("Uncaught exception: (unknown)");
736 if (tempFile
.length() && boost::filesystem::exists(tempFile
)) {
737 boost::filesystem::remove(tempFile
);
742 /* -1 - cannot open file
743 * 0 - no need to open file
747 static int open_server_log_file() {
748 if (!RuntimeOption::LogFile
.empty()) {
749 if (Logger::UseCronolog
) {
750 if (strchr(RuntimeOption::LogFile
.c_str(), '%')) {
751 Logger::cronOutput
.m_template
= RuntimeOption::LogFile
;
752 Logger::cronOutput
.setPeriodicity();
753 Logger::cronOutput
.m_linkName
= RuntimeOption::LogFileSymLink
;
756 Logger::Output
= fopen(RuntimeOption::LogFile
.c_str(), "a");
757 if (Logger::Output
) return 1;
760 if (Logger::IsPipeOutput
) {
761 Logger::Output
= popen(RuntimeOption::LogFile
.substr(1).c_str(), "w");
762 if (Logger::Output
) return 2;
764 Logger::Output
= fopen(RuntimeOption::LogFile
.c_str(), "a");
765 if (Logger::Output
) return 1;
768 Logger::Error("Cannot open log file: %s", RuntimeOption::LogFile
.c_str());
774 static void close_server_log_file(int kind
) {
776 fclose(Logger::Output
);
777 } else if (kind
== 2) {
778 pclose(Logger::Output
);
780 always_assert(!Logger::Output
);
784 static int execute_program_impl(int argc
, char **argv
) {
785 string usage
= "Usage:\n\n\t";
787 usage
+= " [-m <mode>] [<options>] [<arg1>] [<arg2>] ...\n\nOptions";
790 options_description
desc(usage
.c_str());
792 ("help", "display this message")
793 ("version", "display version number")
794 ("compiler-id", "display the git hash for the compiler id")
795 ("repo-schema", "display the repo schema id used by this app")
796 ("mode,m", value
<string
>(&po
.mode
)->default_value("run"),
797 "run | debug (d) | server (s) | daemon | replay | translate (t)")
798 ("config,c", value
<string
>(&po
.config
),
799 "load specified config file")
800 ("config-value,v", value
<StringVec
>(&po
.confStrings
)->composing(),
801 "individual configuration string in a format of name=value, where "
802 "name can be any valid configuration for a config file")
803 ("port,p", value
<int>(&po
.port
)->default_value(-1),
804 "start an HTTP server at specified port")
805 ("port-fd", value
<int>(&po
.portfd
)->default_value(-1),
806 "use specified fd instead of creating a socket")
807 ("ssl-port-fd", value
<int>(&po
.sslportfd
)->default_value(-1),
808 "use specified fd for SSL instead of creating a socket")
809 ("admin-port", value
<int>(&po
.admin_port
)->default_value(-1),
810 "start admin listener at specified port")
811 ("debug-host,h", value
<string
>(&po
.debugger_options
.host
),
812 "connect to debugger server at specified address")
813 ("debug-port", value
<int>(&po
.debugger_options
.port
)->default_value(-1),
814 "connect to debugger server at specified port")
815 ("debug-extension", value
<string
>(&po
.debugger_options
.extension
),
816 "PHP file that extends y command")
817 ("debug-cmd", value
<StringVec
>(&po
.debugger_options
.cmds
)->composing(),
818 "executes this debugger command and returns its output in stdout")
820 value
<string
>(&po
.debugger_options
.sandbox
)->default_value("default"),
821 "initial sandbox to attach to when debugger is started")
822 ("user,u", value
<string
>(&po
.user
),
823 "run server under this user account")
824 ("file,f", value
<string
>(&po
.file
),
825 "executing specified file")
826 ("lint,l", value
<string
>(&po
.lint
),
827 "lint specified file")
828 ("show,w", value
<string
>(&po
.show
),
829 "output specified file and do nothing else")
830 ("parse", value
<string
>(&po
.parse
),
831 "parse specified file and dump the AST")
833 "file specified is temporary and removed after execution")
834 ("count", value
<int>(&po
.count
)->default_value(1),
835 "how many times to repeat execution")
836 ("no-safe-access-check",
837 value
<bool>(&po
.noSafeAccessCheck
)->default_value(false),
838 "whether to ignore safe file access check")
839 ("arg", value
<StringVec
>(&po
.args
)->composing(),
841 ("extra-header", value
<string
>(&Logger::ExtraHeader
),
842 "extra-header to add to log lines")
843 ("build-id", value
<string
>(&po
.buildId
),
844 "unique identifier of compiled server code")
845 ("xhprof-flags", value
<int>(&po
.xhprofFlags
)->default_value(0),
849 positional_options_description p
;
853 store(command_line_parser(argc
, argv
).options(desc
).positional(p
).run(),
856 if (po
.mode
== "d") po
.mode
= "debug";
857 if (po
.mode
== "s") po
.mode
= "server";
858 if (po
.mode
== "t") po
.mode
= "translate";
859 if (po
.mode
== "") po
.mode
= "run";
860 set_execution_mode(po
.mode
);
862 Logger::Error("Error in command line: %s\n\n", e
.what());
863 cout
<< desc
<< "\n";
866 Logger::Error("Error in command line:\n\n");
867 cout
<< desc
<< "\n";
870 if (vm
.count("help")) {
871 cout
<< desc
<< "\n";
874 if (vm
.count("version")) {
876 #undefine HPHP_VERSION
878 #define HPHP_VERSION(v) const char *version = #v;
879 #include "../../version"
882 cout
<< " v" << version
<< " (" << (debug
? "dbg" : "rel") << ")\n";
883 cout
<< "Compiler: " << kCompilerId
<< "\n";
884 cout
<< "Repo schema: " << kRepoSchemaId
<< "\n";
887 if (vm
.count("compiler-id")) {
888 cout
<< kCompilerId
<< "\n";
892 if (vm
.count("repo-schema")) {
893 cout
<< kRepoSchemaId
<< "\n";
897 if (!po
.show
.empty()) {
899 f
.open(po
.show
, "r");
901 Logger::Error("Unable to open file %s", po
.show
.c_str());
909 po
.isTempFile
= vm
.count("temp-file");
911 // we need to initialize pcre cache table very early
915 if (!po
.config
.empty()) {
916 config
.open(po
.config
);
918 RuntimeOption::Load(config
, &po
.confStrings
);
919 vector
<string
> badnodes
;
920 config
.lint(badnodes
);
921 for (unsigned int i
= 0; i
< badnodes
.size(); i
++) {
922 Logger::Error("Possible bad config node: %s", badnodes
[i
].c_str());
925 vector
<int> inherited_fds
;
926 RuntimeOption::BuildId
= po
.buildId
;
928 RuntimeOption::ServerPort
= po
.port
;
930 if (po
.portfd
!= -1) {
931 RuntimeOption::ServerPortFd
= po
.portfd
;
932 inherited_fds
.push_back(po
.portfd
);
934 if (po
.sslportfd
!= -1) {
935 RuntimeOption::SSLPortFd
= po
.sslportfd
;
936 inherited_fds
.push_back(po
.sslportfd
);
938 if (po
.admin_port
!= -1) {
939 RuntimeOption::AdminServerPort
= po
.admin_port
;
941 if (po
.noSafeAccessCheck
) {
942 RuntimeOption::SafeFileAccess
= false;
945 if (po
.mode
== "daemon") {
946 if (RuntimeOption::LogFile
.empty()) {
947 Logger::Error("Log file not specified under daemon mode.\n\n");
949 int ret
= open_server_log_file();
950 Process::Daemonize();
951 close_server_log_file(ret
);
954 open_server_log_file();
956 // Defer the initialization of light processes until the log file handle is
957 // created, so that light processes can log to the right place. If we ever
958 // lose a light process, stop the server instead of proceeding in an
960 LightProcess::SetLostChildHandler([](pid_t child
) {
961 if (!HttpServer::Server
) return;
962 if (!HttpServer::Server
->isStopped()) {
963 HttpServer::Server
->stop("lost light process child");
966 LightProcess::Initialize(RuntimeOption::LightProcessFilePrefix
,
967 RuntimeOption::LightProcessCount
,
971 const size_t stackSizeMinimum
= 8 * 1024 * 1024;
973 if (getrlimit(RLIMIT_STACK
, &rlim
) == 0 &&
974 (rlim
.rlim_cur
== RLIM_INFINITY
||
975 rlim
.rlim_cur
< stackSizeMinimum
)) {
976 rlim
.rlim_cur
= stackSizeMinimum
;
977 if (stackSizeMinimum
> rlim
.rlim_max
) {
978 rlim
.rlim_max
= stackSizeMinimum
;
980 if (setrlimit(RLIMIT_STACK
, &rlim
)) {
981 Logger::Error("failed to set stack limit to %lld\n", stackSizeMinimum
);
986 ShmCounters::initialize(true, Logger::Error
);
987 // Initialize compiler state
988 compile_file(0, 0, MD5(), 0);
990 if (!po
.lint
.empty()) {
997 HPHP::Eval::PhpFile
* phpFile
= g_vmContext
->lookupPhpFile(
998 StringData::GetStaticString(po
.lint
.c_str()), "", nullptr);
999 if (phpFile
== nullptr) {
1000 throw FileOpenException(po
.lint
.c_str());
1002 Unit
* unit
= phpFile
->unit();
1003 const StringData
* msg
;
1005 if (unit
->compileTimeFatal(msg
, line
)) {
1006 VMParserFrame parserFrame
;
1007 parserFrame
.filename
= po
.lint
.c_str();
1008 parserFrame
.lineNumber
= line
;
1009 Array bt
= g_vmContext
->debugBacktrace(false, true,
1010 false, &parserFrame
);
1011 throw FatalErrorException(msg
->data(), bt
);
1013 } catch (FileOpenException
&e
) {
1014 Logger::Error("%s", e
.getMessage().c_str());
1016 } catch (const FatalErrorException
& e
) {
1017 RuntimeOption::CallUserHandlerOnFatals
= false;
1018 RuntimeOption::AlwaysLogUnhandledExceptions
= true;
1019 g_context
->onFatalError(e
);
1022 Logger::Info("No syntax errors detected in %s", po
.lint
.c_str());
1026 if (!po
.parse
.empty()) {
1027 Logger::Error("The 'parse' command line option is not supported\n\n");
1031 if (argc
<= 1 || po
.mode
== "run" || po
.mode
== "debug") {
1032 if (po
.isTempFile
) {
1036 set_execution_mode("run");
1040 prepare_args(new_argc
, new_argv
, po
.args
, po
.file
.c_str());
1042 if (!po
.file
.empty()) {
1043 Repo::setCliFile(po
.file
);
1044 } else if (new_argc
> 0) {
1045 Repo::setCliFile(new_argv
[0]);
1049 hphp_process_init();
1056 if (po
.mode
== "debug") {
1057 StackTraceNoHeap::AddExtraLogging("IsDebugger", "True");
1058 RuntimeOption::EnableDebugger
= true;
1059 Eval::DebuggerProxyPtr proxy
=
1060 Eval::Debugger::StartClient(po
.debugger_options
);
1062 Logger::Error("Failed to start debugger client\n\n");
1065 Eval::Debugger::RegisterSandbox(proxy
->getDummyInfo());
1066 Eval::Debugger::RegisterThread();
1067 StringVecPtr client_args
;
1068 bool restart
= false;
1072 execute_command_line_begin(new_argc
, new_argv
, po
.xhprofFlags
);
1073 g_context
->setSandboxId(proxy
->getDummyInfo().id());
1074 Eval::Debugger::DebuggerSession(po
.debugger_options
, file
, restart
);
1076 execute_command_line_end(po
.xhprofFlags
, true, file
.c_str());
1077 } catch (const Eval::DebuggerRestartException
&e
) {
1078 execute_command_line_end(0, false, nullptr);
1080 if (!e
.m_args
->empty()) {
1081 file
= e
.m_args
->at(0);
1082 client_args
= e
.m_args
;
1084 prepare_args(new_argc
, new_argv
, *client_args
, nullptr);
1087 } catch (const Eval::DebuggerClientExitException
&e
) {
1088 execute_command_line_end(0, false, nullptr);
1089 break; // end user quitting debugger
1095 for (int i
= 0; i
< po
.count
; i
++) {
1096 execute_command_line_begin(new_argc
, new_argv
, po
.xhprofFlags
);
1098 if (hphp_invoke_simple(file
)) {
1099 ret
= ExitException::ExitCode
;
1101 execute_command_line_end(po
.xhprofFlags
, true, file
.c_str());
1106 Eval::DebuggerClient::Shutdown();
1107 hphp_process_exit();
1112 if (po
.mode
== "daemon" || po
.mode
== "server") {
1113 if (!po
.user
.empty()) RuntimeOption::ServerUser
= po
.user
;
1114 return start_server(RuntimeOption::ServerUser
);
1117 if (po
.mode
== "replay" && !po
.args
.empty()) {
1118 RuntimeOption::RecordInput
= false;
1119 set_execution_mode("server");
1120 HttpServer server
; // so we initialize runtime properly
1121 HttpRequestHandler handler
;
1122 for (int i
= 0; i
< po
.count
; i
++) {
1123 for (unsigned int j
= 0; j
< po
.args
.size(); j
++) {
1125 rt
.replayInput(po
.args
[j
].c_str());
1126 handler
.handleRequest(&rt
);
1127 printf("%s\n", rt
.getResponse().c_str());
1133 if (po
.mode
== "translate" && !po
.args
.empty()) {
1134 printf("%s", translate_stack(po
.args
[0].c_str()).c_str());
1138 cout
<< desc
<< "\n";
1142 String
canonicalize_path(CStrRef p
, const char* root
, int rootLen
) {
1143 String
path(Util::canonicalize(p
.c_str(), p
.size()), AttachString
);
1144 if (path
.charAt(0) == '/') {
1145 const string
&sourceRoot
= RuntimeOption::SourceRoot
;
1146 int len
= sourceRoot
.size();
1147 if (len
&& strncmp(path
.data(), sourceRoot
.c_str(), len
) == 0) {
1148 return path
.substr(len
);
1150 if (root
&& rootLen
&& strncmp(path
.data(), root
, rootLen
) == 0) {
1151 return path
.substr(rootLen
);
1157 static string
systemlib_split(string slib
, string
* hhas
) {
1158 auto pos
= slib
.find("\n<?hhas\n");
1159 if (pos
!= string::npos
) {
1160 if (hhas
) *hhas
= slib
.substr(pos
+ 8);
1161 return slib
.substr(0, pos
);
1166 // Search for systemlib.php in the following places:
1167 // 1) ${HHVM_SYSTEMLIB}
1168 // 2) section "systemlib" in the current executable
1169 // and return its contents
1170 string
get_systemlib(string
* hhas
) {
1171 if (char *file
= getenv("HHVM_SYSTEMLIB")) {
1172 std::ifstream
ifs(file
);
1174 return systemlib_split(std::string(
1175 std::istreambuf_iterator
<char>(ifs
),
1176 std::istreambuf_iterator
<char>()), hhas
);
1180 Util::embedded_data desc
;
1181 if (!Util::get_embedded_data("systemlib", &desc
)) return "";
1183 std::ifstream
ifs(desc
.m_filename
);
1184 if (!ifs
.good()) return "";
1185 ifs
.seekg(desc
.m_start
, std::ios::beg
);
1186 std::unique_ptr
<char[]> data(new char[desc
.m_len
]);
1187 ifs
.read(data
.get(), desc
.m_len
);
1188 return systemlib_split(string(data
.get(), desc
.m_len
), hhas
);
1191 ///////////////////////////////////////////////////////////////////////////////
1194 extern "C" void hphp_fatal_error(const char *s
) {
1198 void hphp_process_init() {
1199 pthread_attr_t attr
;
1200 pthread_getattr_np(pthread_self(), &attr
);
1201 Util::init_stack_limits(&attr
);
1202 pthread_attr_destroy(&attr
);
1204 init_thread_locals();
1206 Process::InitProcessStatics();
1208 // reinitialize pcre table
1211 // the liboniguruma docs say this isnt needed,
1212 // but the implementation of init is not
1213 // thread safe due to bugs
1216 // simple xml also needs one time init
1221 PageletServer::Restart();
1222 XboxServer::Restart();
1223 Stream::RegisterCoreWrappers();
1224 Extension::InitModules();
1225 for (InitFiniNode
*in
= extra_process_init
; in
; in
= in
->next
) {
1228 int64_t save
= RuntimeOption::SerializationSizeLimit
;
1229 RuntimeOption::SerializationSizeLimit
= StringData::MaxSize
;
1230 apc_load(RuntimeOption::ApcLoadThread
);
1231 RuntimeOption::SerializationSizeLimit
= save
;
1233 Transl::TargetCache::requestExit();
1234 // Reset the preloaded g_context
1235 ExecutionContext
*context
= g_context
.getNoCheck();
1236 context
->~ExecutionContext();
1237 new (context
) ExecutionContext();
1240 static void handle_exception(bool& ret
, ExecutionContext
* context
,
1241 std::string
& errorMsg
, ContextOfException where
,
1242 bool& error
, bool richErrorMsg
) {
1243 assert(where
== InvokeException
|| where
== ReqInitException
);
1245 handle_exception_helper(ret
, context
, errorMsg
, where
, error
, richErrorMsg
);
1246 } catch (const ExitException
&e
) {
1247 // Got an ExitException during exception handling, handle
1248 // similarly to the case below but don't call obEndAll().
1250 handle_exception_helper(ret
, context
, errorMsg
, HandlerException
, error
,
1252 context
->obEndAll();
1256 static void handle_reqinit_exception(bool &ret
, ExecutionContext
*context
,
1257 std::string
&errorMsg
, bool &error
) {
1258 handle_exception(ret
, context
, errorMsg
, ReqInitException
, error
, false);
1261 static void handle_invoke_exception(bool &ret
, ExecutionContext
*context
,
1262 std::string
&errorMsg
, bool &error
,
1263 bool richErrorMsg
) {
1264 handle_exception(ret
, context
, errorMsg
, InvokeException
, error
,
1268 static bool hphp_warmup(ExecutionContext
*context
,
1269 const string
&reqInitFunc
,
1270 const string
&reqInitDoc
, bool &error
) {
1273 std::string errorMsg
;
1275 MemoryManager
*mm
= MemoryManager::TheMemoryManager();
1276 if (mm
->isEnabled()) {
1277 ServerStatsHelper
ssh("reqinit");
1279 if (!reqInitDoc
.empty()) {
1280 include_impl_invoke(reqInitDoc
, true);
1282 if (!reqInitFunc
.empty()) {
1283 invoke(reqInitFunc
.c_str(), Array());
1285 context
->backupSession();
1287 handle_reqinit_exception(ret
, context
, errorMsg
, error
);
1294 void hphp_session_init() {
1295 init_thread_locals();
1296 ThreadInfo::s_threadInfo
->onSessionInit();
1297 MemoryManager::TheMemoryManager()->resetStats();
1299 #ifdef ENABLE_SIMPLE_COUNTER
1300 SimpleCounter::Enabled
= true;
1301 StackTrace::Enabled
= true;
1304 // Ordering is sensitive; StatCache::requestInit produces work that
1305 // must be done in VMExecutionContext::requestInit.
1306 StatCache::requestInit();
1308 g_vmContext
->requestInit();
1310 SystemGlobals
*g
= (SystemGlobals
*)get_global_variables();
1311 g
->k_PHP_SAPI
= StringData::GetStaticString(RuntimeOption::ExecutionMode
);
1314 bool hphp_is_warmup_enabled() {
1315 MemoryManager
*mm
= MemoryManager::TheMemoryManager();
1316 return mm
->isEnabled();
1319 ExecutionContext
*hphp_context_init() {
1320 ExecutionContext
*context
= g_context
.getNoCheck();
1322 context
->obProtect(true);
1326 bool hphp_invoke_simple(const std::string
&filename
,
1327 bool warmupOnly
/* = false */) {
1330 return hphp_invoke(g_context
.getNoCheck(), filename
, false, null_array
,
1331 uninit_null(), "", "", error
, errorMsg
, true, warmupOnly
);
1334 bool hphp_invoke(ExecutionContext
*context
, const std::string
&cmd
,
1335 bool func
, CArrRef funcParams
, VRefParam funcRet
,
1336 const string
&reqInitFunc
, const string
&reqInitDoc
,
1337 bool &error
, string
&errorMsg
,
1338 bool once
/* = true */, bool warmupOnly
/* = false */,
1339 bool richErrorMsg
/* = false */) {
1340 bool isServer
= RuntimeOption::ServerExecutionMode();
1345 oldCwd
= context
->getCwd();
1347 if (!hphp_warmup(context
, reqInitFunc
, reqInitDoc
, error
)) {
1348 if (isServer
) context
->setCwd(oldCwd
);
1355 ServerStatsHelper
ssh("invoke");
1357 funcRet
->assignVal(invoke(cmd
.c_str(), funcParams
));
1359 if (isServer
) hphp_chdir_file(cmd
);
1360 include_impl_invoke(cmd
.c_str(), once
);
1363 handle_invoke_exception(ret
, context
, errorMsg
, error
, richErrorMsg
);
1368 context
->onShutdownPreSend();
1370 handle_invoke_exception(ret
, context
, errorMsg
, error
, richErrorMsg
);
1373 if (isServer
) context
->setCwd(oldCwd
);
1377 void hphp_context_exit(ExecutionContext
*context
, bool psp
,
1378 bool shutdown
/* = true */,
1379 const char *program
/* = NULL */) {
1381 context
->onShutdownPostSend();
1383 if (RuntimeOption::EnableDebugger
) {
1385 Eval::Debugger::InterruptPSPEnded(program
);
1386 } catch (const Eval::DebuggerException
&e
) {}
1388 context
->requestExit();
1391 context
->onRequestShutdown();
1393 context
->obProtect(false);
1394 context
->obEndAll();
1397 void hphp_thread_exit() {
1398 finish_thread_locals();
1401 void hphp_session_exit() {
1402 // Server note has to live long enough for the access log to fire.
1403 // RequestLocal is too early.
1404 ServerNote::Reset();
1405 g_context
.destroy();
1407 ThreadInfo::s_threadInfo
->clearPendingException();
1409 MemoryManager
*mm
= MemoryManager::TheMemoryManager();
1410 if (RuntimeOption::CheckMemory
) {
1413 if (RuntimeOption::EnableStats
&& RuntimeOption::EnableMemoryStats
) {
1418 if (mm
->isEnabled()) {
1419 ServerStatsHelper
ssh("rollback");
1420 // sweep may call g_context->, which is a noCheck, so we need to
1421 // reinitialize g_context here
1422 g_context
.getCheck();
1423 // MemoryManager::sweepAll() will handle sweeping for PHP objects and
1424 // PHP resources (ex. File, Collator, XmlReader, etc.)
1426 // Destroy g_context again because ExecutionContext has SmartAllocated
1427 // data members. These members cannot survive over rollback(), so we need
1428 // to destroy g_context before calling rollback().
1429 g_context
.destroy();
1430 // MemoryManager::rollback() will handle sweeping for all types that have
1431 // dedicated allocators (ex. StringData, HphpArray, etc.) and it reset all
1432 // of the allocators in preparation for the next request.
1434 // Do any post-sweep cleanup necessary for global variables
1435 free_global_variables_after_sweep();
1436 g_context
.getCheck();
1438 g_context
.getCheck();
1439 ServerStatsHelper
ssh("free");
1440 free_global_variables();
1443 ThreadInfo::s_threadInfo
->onSessionExit();
1446 void hphp_process_exit() {
1448 Eval::Debugger::Stop();
1449 Extension::ShutdownModules();
1450 LightProcess::Close();
1451 for (InitFiniNode
*in
= extra_process_exit
; in
; in
= in
->next
) {
1456 ///////////////////////////////////////////////////////////////////////////////