Rename files in runtime/base, part 6
[hiphop-php.git] / hphp / runtime / base / program-functions.cpp
blob83a076da2bf90282cb87db5b02bd909d5e6b2676
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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/runtime/base/pprof-server.h"
26 #include "hphp/util/shared_memory_allocator.h"
27 #include "hphp/runtime/server/pagelet_server.h"
28 #include "hphp/runtime/server/xbox_server.h"
29 #include "hphp/runtime/server/http_server.h"
30 #include "hphp/runtime/server/replay_transport.h"
31 #include "hphp/runtime/server/http_request_handler.h"
32 #include "hphp/runtime/server/admin_request_handler.h"
33 #include "hphp/runtime/server/server_stats.h"
34 #include "hphp/runtime/server/server_name_indication.h"
35 #include "hphp/runtime/server/server_note.h"
36 #include "hphp/runtime/base/memory-manager.h"
37 #include "hphp/util/process.h"
38 #include "hphp/util/capability.h"
39 #include "hphp/util/embedded_data.h"
40 #include "hphp/util/timer.h"
41 #include "hphp/util/stack_trace.h"
42 #include "hphp/util/light_process.h"
43 #include "hphp/util/repo_schema.h"
44 #include "hphp/util/current_executable.h"
45 #include "hphp/runtime/base/stat-cache.h"
46 #include "hphp/runtime/ext/extension.h"
47 #include "hphp/runtime/ext/ext_fb.h"
48 #include "hphp/runtime/ext/ext_json.h"
49 #include "hphp/runtime/ext/ext_variable.h"
50 #include "hphp/runtime/ext/ext_apc.h"
51 #include "hphp/runtime/ext/ext_function.h"
52 #include "hphp/runtime/debugger/debugger.h"
53 #include "hphp/runtime/debugger/debugger_client.h"
54 #include "hphp/runtime/base/simple-counter.h"
55 #include "hphp/runtime/base/extended_logger.h"
56 #include "hphp/runtime/base/stream-wrapper-registry.h"
58 #include <boost/program_options/options_description.hpp>
59 #include <boost/program_options/positional_options.hpp>
60 #include <boost/program_options/variables_map.hpp>
61 #include <boost/program_options/parsers.hpp>
62 #include <boost/make_shared.hpp>
63 #include <libgen.h>
64 #include <oniguruma.h>
65 #include <signal.h>
66 #include "libxml/parser.h"
68 #include "hphp/runtime/base/file-repository.h"
70 #include "hphp/runtime/vm/runtime.h"
71 #include "hphp/runtime/vm/repo.h"
72 #include "hphp/runtime/vm/jit/translator.h"
73 #include "hphp/compiler/builtin_symbols.h"
75 using namespace boost::program_options;
76 using std::cout;
77 extern char **environ;
79 #define MAX_INPUT_NESTING_LEVEL 64
81 namespace HPHP {
83 extern InitFiniNode *extra_process_init, *extra_process_exit;
85 void initialize_repo();
88 * XXX: VM process initialization is handled through a function
89 * pointer so libhphp_runtime.a can be linked into programs that don't
90 * actually initialize the VM.
92 void (*g_vmProcessInit)();
94 ///////////////////////////////////////////////////////////////////////////////
95 // helpers
97 struct ProgramOptions {
98 string mode;
99 string config;
100 StringVec confStrings;
101 int port;
102 int portfd;
103 int sslportfd;
104 int admin_port;
105 string user;
106 string file;
107 string lint;
108 bool isTempFile;
109 int count;
110 bool noSafeAccessCheck;
111 StringVec args;
112 string buildId;
113 int xhprofFlags;
114 string show;
115 string parse;
117 Eval::DebuggerClientOptions debugger_options;
120 class StartTime {
121 public:
122 StartTime() : startTime(time(nullptr)) {}
123 time_t startTime;
125 static StartTime s_startTime;
126 static string tempFile;
128 time_t start_time() {
129 return s_startTime.startTime;
132 const StaticString
133 s_HPHP("HPHP"),
134 s_HHVM("HHVM"),
135 s_HHVM_JIT("HHVM_JIT"),
136 s_REQUEST_START_TIME("REQUEST_START_TIME"),
137 s_REQUEST_TIME("REQUEST_TIME"),
138 s_REQUEST_TIME_FLOAT("REQUEST_TIME_FLOAT"),
139 s_DOCUMENT_ROOT("DOCUMENT_ROOT"),
140 s_SCRIPT_FILENAME("SCRIPT_FILENAME"),
141 s_SCRIPT_NAME("SCRIPT_NAME"),
142 s_PHP_SELF("PHP_SELF"),
143 s_argc("argc"),
144 s_argv("argv"),
145 s_PWD("PWD"),
146 s_HOSTNAME("HOSTNAME"),
147 s__SERVER("_SERVER"),
148 s__ENV("_ENV");
150 static void process_cmd_arguments(int argc, char **argv) {
151 GlobalVariables *g = get_global_variables();
152 g->set(s_argc, Variant(argc), false);
153 Array argvArray = HphpArray::GetStaticEmptyArray();
154 for (int i = 0; i < argc; i++) {
155 argvArray.append(String(argv[i]));
157 g->set(s_argv, argvArray, false);
160 void process_env_variables(Variant &variables) {
161 for (std::map<string, string>::const_iterator iter =
162 RuntimeOption::EnvVariables.begin();
163 iter != RuntimeOption::EnvVariables.end(); ++iter) {
164 variables.set(String(iter->first), String(iter->second));
166 for (char **env = environ; env && *env; env++) {
167 char *p = strchr(*env, '=');
168 if (p) {
169 String name(*env, p - *env, CopyString);
170 register_variable(variables, (char*)name.data(),
171 String(p + 1, CopyString));
176 void register_variable(Variant &variables, char *name, CVarRef value,
177 bool overwrite /* = true */) {
178 // ignore leading spaces in the variable name
179 char *var = name;
180 while (*var && *var == ' ') {
181 var++;
184 // ensure that we don't have spaces or dots in the variable name
185 // (not binary safe)
186 bool is_array = false;
187 char *ip = nullptr; // index pointer
188 char *p = var;
189 for (; *p; p++) {
190 if (*p == ' ' || *p == '.') {
191 *p = '_';
192 } else if (*p == '[') {
193 is_array = true;
194 ip = p;
195 *p = 0;
196 break;
199 int var_len = p - var;
200 if (var_len == 0) {
201 // empty variable name, or variable name with a space in it
202 return;
205 vector<Variant> gpc_elements;
206 gpc_elements.reserve(MAX_INPUT_NESTING_LEVEL); // important, so no resize
207 Variant *symtable = &variables;
208 char *index = var;
209 int index_len = var_len;
211 if (is_array) {
212 int nest_level = 0;
213 while (true) {
214 if (++nest_level > MAX_INPUT_NESTING_LEVEL) {
215 Logger::Warning("Input variable nesting level exceeded");
216 return;
219 ip++;
220 char *index_s = ip;
221 int new_idx_len = 0;
222 if (isspace(*ip)) {
223 ip++;
225 if (*ip == ']') {
226 index_s = nullptr;
227 } else {
228 ip = strchr(ip, ']');
229 if (!ip) {
230 // PHP variables cannot contain '[' in their names,
231 // so we replace the character with a '_'
232 *(index_s - 1) = '_';
234 index_len = 0;
235 if (index) {
236 index_len = strlen(index);
238 goto plain_var;
240 *ip = 0;
241 new_idx_len = strlen(index_s);
244 if (!index) {
245 symtable->append(Array::Create());
246 gpc_elements.push_back(uninit_null());
247 gpc_elements.back().assignRef(
248 symtable->lvalAt((int)symtable->toArray().size() - 1));
249 } else {
250 String key(index, index_len, CopyString);
251 Variant v = symtable->rvalAt(key);
252 if (v.isNull() || !v.is(KindOfArray)) {
253 symtable->set(key, Array::Create());
255 gpc_elements.push_back(uninit_null());
256 gpc_elements.back().assignRef(symtable->lvalAt(key));
258 symtable = &gpc_elements.back();
259 /* ip pointed to the '[' character, now obtain the key */
260 index = index_s;
261 index_len = new_idx_len;
263 ip++;
264 if (*ip == '[') {
265 is_array = true;
266 *ip = 0;
267 } else {
268 goto plain_var;
271 } else {
272 plain_var:
273 if (!index) {
274 symtable->append(value);
275 } else {
276 String key(index, index_len, CopyString);
277 if (overwrite || !symtable->toArray().exists(key)) {
278 symtable->set(key, value);
284 enum class ContextOfException {
285 ReqInit = 1,
286 Invoke,
287 Handler,
290 static void handle_exception_append_bt(std::string& errorMsg,
291 const ExtendedException& e) {
292 Array bt = e.getBackTrace();
293 if (!bt.empty()) {
294 errorMsg += ExtendedLogger::StringOfStackTrace(bt);
298 static void handle_exception_helper(bool& ret,
299 ExecutionContext* context,
300 std::string& errorMsg,
301 ContextOfException where,
302 bool& error,
303 bool richErrorMsg) {
304 try {
305 throw;
306 } catch (const Eval::DebuggerException &e) {
307 throw;
308 } catch (const ExitException &e) {
309 if (where == ContextOfException::ReqInit) {
310 ret = false;
311 } else if (where != ContextOfException::Handler &&
312 !context->getExitCallback().isNull() &&
313 f_is_callable(context->getExitCallback())) {
314 Array stack = e.getBackTrace();
315 Array argv = CREATE_VECTOR2(e.ExitCode, stack);
316 vm_call_user_func(context->getExitCallback(), argv);
318 } catch (const PhpFileDoesNotExistException &e) {
319 ret = false;
320 if (where != ContextOfException::Handler) {
321 raise_notice("%s", e.getMessage().c_str());
322 } else {
323 Logger::Error("%s", e.getMessage().c_str());
325 if (richErrorMsg) {
326 handle_exception_append_bt(errorMsg, e);
328 } catch (const UncatchableException &e) {
329 ret = false;
330 error = true;
331 errorMsg = "";
332 if (RuntimeOption::ServerStackTrace) {
333 errorMsg = e.what();
334 } else if (RuntimeOption::InjectedStackTrace) {
335 errorMsg = e.getMessage();
336 errorMsg += "\n";
337 errorMsg += ExtendedLogger::StringOfStackTrace(e.getBackTrace());
339 Logger::Error("%s", errorMsg.c_str());
340 if (richErrorMsg) {
341 handle_exception_append_bt(errorMsg, e);
343 } catch (const Exception &e) {
344 bool oldRet = ret;
345 bool origError = error;
346 std::string origErrorMsg = errorMsg;
347 ret = false;
348 error = true;
349 errorMsg = "";
350 if (where == ContextOfException::Handler) {
351 errorMsg = "Exception handler threw an exception: ";
353 errorMsg += e.what();
354 if (where == ContextOfException::Invoke) {
355 bool handlerRet = context->onFatalError(e);
356 if (handlerRet) {
357 ret = oldRet;
358 error = origError;
359 errorMsg = origErrorMsg;
361 } else {
362 Logger::Error("%s", errorMsg.c_str());
364 if (richErrorMsg) {
365 const ExtendedException *ee = dynamic_cast<const ExtendedException *>(&e);
366 if (ee) {
367 handle_exception_append_bt(errorMsg, *ee);
370 } catch (const Object &e) {
371 bool oldRet = ret;
372 bool origError = error;
373 std::string origErrorMsg = errorMsg;
374 ret = false;
375 error = true;
376 errorMsg = "";
377 if (where == ContextOfException::Handler) {
378 errorMsg = "Exception handler threw an object exception: ";
380 try {
381 errorMsg += e.toString().data();
382 } catch (...) {
383 errorMsg += "(unable to call toString())";
385 if (where == ContextOfException::Invoke) {
386 bool handlerRet = context->onUnhandledException(e);
387 if (handlerRet) {
388 ret = oldRet;
389 error = origError;
390 errorMsg = origErrorMsg;
392 } else {
393 Logger::Error("%s", errorMsg.c_str());
395 } catch (...) {
396 ret = false;
397 error = true;
398 errorMsg = "(unknown exception was thrown)";
399 Logger::Error("%s", errorMsg.c_str());
403 static bool hphp_chdir_file(const string filename) {
404 bool ret = false;
405 String s = File::TranslatePath(filename);
406 char *buf = strndup(s.data(), s.size());
407 char *dir = dirname(buf);
408 assert(dir);
409 if (dir) {
410 if (File::IsVirtualDirectory(dir)) {
411 g_context->setCwd(String(dir, CopyString));
412 ret = true;
413 } else {
414 struct stat sb;
415 stat(dir, &sb);
416 if ((sb.st_mode & S_IFMT) == S_IFDIR) {
417 ret = true;
418 if (*dir != '.') {
419 g_context->setCwd(String(dir, CopyString));
424 free(buf);
425 return ret;
428 void handle_destructor_exception(const char* situation) {
429 string errorMsg;
431 try {
432 throw;
433 } catch (ExitException &e) {
434 // ExitException is fine, no need to show a warning.
435 ThreadInfo::s_threadInfo->setPendingException(e.clone());
436 return;
437 } catch (Object &e) {
438 // For user exceptions, invoke the user exception handler
439 errorMsg = situation;
440 errorMsg += " threw an object exception: ";
441 try {
442 errorMsg += e.toString().data();
443 } catch (...) {
444 errorMsg += "(unable to call toString())";
446 } catch (Exception &e) {
447 ThreadInfo::s_threadInfo->setPendingException(e.clone());
448 errorMsg = situation;
449 errorMsg += " raised a fatal error: ";
450 errorMsg += e.what();
451 } catch (...) {
452 errorMsg = situation;
453 errorMsg += " threw an unknown exception";
455 // For fatal errors and unknown exceptions, we raise a warning.
456 // If there is a user error handler it will be invoked, otherwise
457 // the default error handler will be invoked.
458 try {
459 raise_debugging("%s", errorMsg.c_str());
460 } catch (...) {
461 // The user error handler fataled or threw an exception,
462 // print out the error message directly to the log
463 Logger::Warning("%s", errorMsg.c_str());
467 void execute_command_line_begin(int argc, char **argv, int xhprof) {
468 StackTraceNoHeap::AddExtraLogging("ThreadType", "CLI");
469 string args;
470 for (int i = 0; i < argc; i++) {
471 if (i) args += " ";
472 args += argv[i];
474 StackTraceNoHeap::AddExtraLogging("Arguments", args.c_str());
476 hphp_session_init();
477 ExecutionContext *context = g_context.getNoCheck();
478 context->obSetImplicitFlush(true);
480 GlobalVariables *g = get_global_variables();
482 Variant& env = g->getRef(s__ENV);
483 process_env_variables(env);
484 env.set(s_HPHP, 1);
485 env.set(s_HHVM, 1);
486 if (RuntimeOption::EvalJit) {
487 env.set(s_HHVM_JIT, 1);
490 process_cmd_arguments(argc, argv);
492 Variant& server = g->getRef(s__SERVER);
493 process_env_variables(server);
494 time_t now;
495 struct timeval tp = {0};
496 double now_double;
497 if (!gettimeofday(&tp, nullptr)) {
498 now_double = (double)(tp.tv_sec + tp.tv_usec / 1000000.00);
499 now = tp.tv_sec;
500 } else {
501 now = time(nullptr);
502 now_double = (double)now;
504 String file = empty_string;
505 if (argc > 0) {
506 file = StringData::Make(argv[0], CopyString);
508 server.set(s_REQUEST_START_TIME, now);
509 server.set(s_REQUEST_TIME, now);
510 server.set(s_REQUEST_TIME_FLOAT, now_double);
511 server.set(s_DOCUMENT_ROOT, empty_string);
512 server.set(s_SCRIPT_FILENAME, file);
513 server.set(s_SCRIPT_NAME, file);
514 server.set(s_PHP_SELF, file);
515 server.set(s_argv, g->get(s_argv));
516 server.set(s_argc, g->get(s_argc));
517 server.set(s_PWD, g_context->getCwd());
518 char hostname[1024];
519 if (!gethostname(hostname, 1024)) {
520 server.set(s_HOSTNAME, String(hostname, CopyString));
523 for(std::map<string,string>::iterator it =
524 RuntimeOption::ServerVariables.begin(),
525 end = RuntimeOption::ServerVariables.end(); it != end; ++it) {
526 server.set(String(it->first.c_str()), String(it->second.c_str()));
529 if (xhprof) {
530 f_xhprof_enable(xhprof, uninit_null().toArray());
534 void execute_command_line_end(int xhprof, bool coverage, const char *program) {
535 ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
537 if (RuntimeOption::EvalDumpTC) {
538 HPHP::Transl::tc_dump();
541 if (xhprof) {
542 f_var_dump(f_json_encode(f_xhprof_disable()));
544 hphp_context_exit(g_context.getNoCheck(), true, true, program);
545 hphp_session_exit();
546 if (coverage && ti->m_reqInjectionData.getCoverage() &&
547 !RuntimeOption::CodeCoverageOutputFile.empty()) {
548 ti->m_coverage->Report(RuntimeOption::CodeCoverageOutputFile);
552 static void pagein_self(void) {
553 unsigned long begin, end, inode, pgoff;
554 char mapname[PATH_MAX];
555 char perm[5];
556 char dev[6];
557 char *buf;
558 int bufsz;
559 int r;
560 FILE *fp;
562 // pad due to the spaces between the inode number and the mapname
563 bufsz = sizeof(unsigned long) * 4 + sizeof(mapname) + sizeof(char) * 11 + 100;
564 buf = (char *)malloc(bufsz);
565 if (buf == nullptr)
566 return;
568 Timer timer(Timer::WallTime, "mapping self");
569 fp = fopen("/proc/self/maps", "r");
570 if (fp != nullptr) {
571 while (!feof(fp)) {
572 if (fgets(buf, bufsz, fp) == 0)
573 break;
574 r = sscanf(buf, "%lx-%lx %4s %lx %5s %ld %s",
575 &begin, &end, perm, &pgoff, dev, &inode, mapname);
577 // page in read-only segments that correspond to a file on disk
578 if (r != 7 ||
579 perm[0] != 'r' ||
580 perm[1] != '-' ||
581 access(mapname, F_OK) != 0) {
582 continue;
585 if (mlock((void *)begin, end - begin) == 0) {
586 if (!RuntimeOption::LockCodeMemory) {
587 munlock((void *)begin, end - begin);
591 fclose(fp);
593 free(buf);
596 /* Sets RuntimeOption::ExecutionMode according
597 * to commandline options prior to config load
599 static void set_execution_mode(string mode) {
600 if (mode == "daemon" || mode == "server" || mode == "replay") {
601 RuntimeOption::ExecutionMode = "srv";
602 Logger::Escape = true;
603 } else if (mode == "run" || mode == "debug") {
604 RuntimeOption::ExecutionMode = "cli";
605 Logger::Escape = false;
606 } else if (mode == "translate") {
607 RuntimeOption::ExecutionMode = "";
608 Logger::Escape = false;
609 } else {
610 // Undefined mode
611 always_assert(false);
615 static int start_server(const std::string &username) {
616 // Before we start the webserver, make sure the entire
617 // binary is paged into memory.
618 pagein_self();
620 set_execution_mode("server");
621 HttpRequestHandler::GetAccessLog().init
622 (RuntimeOption::AccessLogDefaultFormat, RuntimeOption::AccessLogs,
623 username);
624 AdminRequestHandler::GetAccessLog().init
625 (RuntimeOption::AdminLogFormat, RuntimeOption::AdminLogSymLink,
626 RuntimeOption::AdminLogFile,
627 username);
629 void *sslCTX = nullptr;
630 if (RuntimeOption::EnableSSL) {
631 #ifdef _EVENT_USE_OPENSSL
632 struct ssl_config config;
633 if (RuntimeOption::SSLCertificateFile != "" &&
634 RuntimeOption::SSLCertificateKeyFile != "") {
635 config.cert_file = (char*)RuntimeOption::SSLCertificateFile.c_str();
636 config.pk_file = (char*)RuntimeOption::SSLCertificateKeyFile.c_str();
637 sslCTX = evhttp_init_openssl(&config);
638 if (!RuntimeOption::SSLCertificateDir.empty()) {
639 ServerNameIndication::load(sslCTX, config,
640 RuntimeOption::SSLCertificateDir);
642 } else {
643 Logger::Error("Invalid certificate file or key file");
645 #else
646 Logger::Error("A SSL enabled libevent is required");
647 #endif
650 #if !defined(SKIP_USER_CHANGE)
651 if (!username.empty()) {
652 if (Logger::UseCronolog) {
653 Cronolog::changeOwner(username, RuntimeOption::LogFileSymLink);
655 Capability::ChangeUnixUser(username);
656 LightProcess::ChangeUser(username);
658 Capability::SetDumpable();
659 #endif
661 // Create the HttpServer before any warmup requests to properly
662 // initialize the process
663 HttpServer::Server = HttpServerPtr(new HttpServer(sslCTX));
665 if (memory_profiling) {
666 Logger::Info("Starting up profiling server");
667 HeapProfileServer::Server = boost::make_shared<HeapProfileServer>();
670 // If we have any warmup requests, replay them before listening for
671 // real connections
672 for (auto& file : RuntimeOption::ServerWarmupRequests) {
673 HttpRequestHandler handler(0);
674 ReplayTransport rt;
675 timespec start;
676 Timer::GetMonotonicTime(start);
677 std::string error;
678 Logger::Info("Replaying warmup request %s", file.c_str());
679 try {
680 rt.onRequestStart(start);
681 rt.replayInput(Hdf(file));
682 handler.handleRequest(&rt);
683 Logger::Info("Finished successfully");
684 } catch (std::exception& e) {
685 error = e.what();
687 if (error.size()) {
688 Logger::Info("Got exception during warmup: %s", error.c_str());
692 HttpServer::Server->run();
693 return 0;
696 string translate_stack(const char *hexencoded, bool with_frame_numbers) {
697 if (!hexencoded || !*hexencoded) {
698 return "";
701 StackTrace st(hexencoded);
702 StackTrace::FramePtrVec frames;
703 st.get(frames);
705 std::ostringstream out;
706 for (unsigned int i = 0; i < frames.size(); i++) {
707 StackTrace::FramePtr f = frames[i];
708 if (with_frame_numbers) {
709 out << "# " << (i < 10 ? " " : "") << i << ' ';
711 out << f->toString();
712 out << '\n';
714 return out.str();
717 ///////////////////////////////////////////////////////////////////////////////
719 static void prepare_args(int &argc, char **&argv, const StringVec &args,
720 const char *file) {
721 argv = (char **)malloc((args.size() + 2) * sizeof(char*));
722 argc = 0;
723 if (file && *file) {
724 argv[argc++] = (char*)file;
726 for (int i = 0; i < (int)args.size(); i++) {
727 argv[argc++] = (char*)args[i].c_str();
729 argv[argc] = nullptr;
732 static int execute_program_impl(int argc, char **argv);
733 int execute_program(int argc, char **argv) {
734 int ret_code = -1;
735 try {
736 initialize_repo();
737 init_thread_locals();
738 ret_code = execute_program_impl(argc, argv);
739 } catch (const Exception &e) {
740 Logger::Error("Uncaught exception: %s", e.what());
741 } catch (const FailedAssertion& fa) {
742 fa.print();
743 StackTraceNoHeap::AddExtraLogging("Assertion failure", fa.summary);
744 abort();
745 } catch (const std::exception &e) {
746 Logger::Error("Uncaught exception: %s", e.what());
747 } catch (...) {
748 Logger::Error("Uncaught exception: (unknown)");
750 if (tempFile.length() && boost::filesystem::exists(tempFile)) {
751 boost::filesystem::remove(tempFile);
753 return ret_code;
756 /* -1 - cannot open file
757 * 0 - no need to open file
758 * 1 - fopen
759 * 2 - popen
761 static int open_server_log_file() {
762 if (!RuntimeOption::LogFile.empty()) {
763 if (Logger::UseCronolog) {
764 if (strchr(RuntimeOption::LogFile.c_str(), '%')) {
765 Logger::cronOutput.m_template = RuntimeOption::LogFile;
766 Logger::cronOutput.setPeriodicity();
767 Logger::cronOutput.m_linkName = RuntimeOption::LogFileSymLink;
768 return 0;
769 } else {
770 Logger::Output = fopen(RuntimeOption::LogFile.c_str(), "a");
771 if (Logger::Output) return 1;
773 } else {
774 if (Logger::IsPipeOutput) {
775 Logger::Output = popen(RuntimeOption::LogFile.substr(1).c_str(), "w");
776 if (Logger::Output) return 2;
777 } else {
778 Logger::Output = fopen(RuntimeOption::LogFile.c_str(), "a");
779 if (Logger::Output) return 1;
782 Logger::Error("Cannot open log file: %s", RuntimeOption::LogFile.c_str());
783 return -1;
785 return 0;
788 static void close_server_log_file(int kind) {
789 if (kind == 1) {
790 fclose(Logger::Output);
791 } else if (kind == 2) {
792 pclose(Logger::Output);
793 } else {
794 always_assert(!Logger::Output);
798 static int execute_program_impl(int argc, char** argv) {
799 string usage = "Usage:\n\n ";
800 usage += argv[0];
801 usage += " [-m <mode>] [<options>] [<arg1>] [<arg2>] ...\n\nOptions";
803 ProgramOptions po;
804 options_description desc(usage.c_str());
805 desc.add_options()
806 ("help", "display this message")
807 ("version", "display version number")
808 ("compiler-id", "display the git hash for the compiler id")
809 ("repo-schema", "display the repo schema id used by this app")
810 ("mode,m", value<string>(&po.mode)->default_value("run"),
811 "run | debug (d) | server (s) | daemon | replay | translate (t)")
812 ("config,c", value<string>(&po.config),
813 "load specified config file")
814 ("config-value,v", value<StringVec >(&po.confStrings)->composing(),
815 "individual configuration string in a format of name=value, where "
816 "name can be any valid configuration for a config file")
817 ("port,p", value<int>(&po.port)->default_value(-1),
818 "start an HTTP server at specified port")
819 ("port-fd", value<int>(&po.portfd)->default_value(-1),
820 "use specified fd instead of creating a socket")
821 ("ssl-port-fd", value<int>(&po.sslportfd)->default_value(-1),
822 "use specified fd for SSL instead of creating a socket")
823 ("admin-port", value<int>(&po.admin_port)->default_value(-1),
824 "start admin listener at specified port")
825 ("debug-config", value<string>(&po.debugger_options.configFName),
826 "load specified debugger config file")
827 ("debug-host,h",
828 value<string>(&po.debugger_options.host)->implicit_value("localhost"),
829 "connect to debugger server at specified address")
830 ("debug-port", value<int>(&po.debugger_options.port)->default_value(-1),
831 "connect to debugger server at specified port")
832 ("debug-extension", value<string>(&po.debugger_options.extension),
833 "PHP file that extends y command")
834 ("debug-cmd", value<StringVec>(&po.debugger_options.cmds)->composing(),
835 "executes this debugger command and returns its output in stdout")
836 ("debug-sandbox",
837 value<string>(&po.debugger_options.sandbox)->default_value("default"),
838 "initial sandbox to attach to when debugger is started")
839 ("user,u", value<string>(&po.user),
840 "run server under this user account")
841 ("file,f", value<string>(&po.file),
842 "executing specified file")
843 ("lint,l", value<string>(&po.lint),
844 "lint specified file")
845 ("show,w", value<string>(&po.show),
846 "output specified file and do nothing else")
847 ("parse", value<string>(&po.parse),
848 "parse specified file and dump the AST")
849 ("temp-file",
850 "file specified is temporary and removed after execution")
851 ("count", value<int>(&po.count)->default_value(1),
852 "how many times to repeat execution")
853 ("no-safe-access-check",
854 value<bool>(&po.noSafeAccessCheck)->default_value(false),
855 "whether to ignore safe file access check")
856 ("arg", value<StringVec >(&po.args)->composing(),
857 "arguments")
858 ("extra-header", value<string>(&Logger::ExtraHeader),
859 "extra-header to add to log lines")
860 ("build-id", value<string>(&po.buildId),
861 "unique identifier of compiled server code")
862 ("xhprof-flags", value<int>(&po.xhprofFlags)->default_value(0),
863 "Set XHProf flags")
866 positional_options_description p;
867 p.add("arg", -1);
868 variables_map vm;
869 try {
870 // We use the boost command line parser to do the initial parsing,
871 // and then we do a manual pass to find the first occurrence of
872 // either "--" or a non-option argument so that we can properly
873 // determine which arguments should be consumed by HHVM and which
874 // arguments should be passed to the PHP application.
876 // We instruct the boost command line parser to allow unrecognized
877 // options and we manually deal with raising an error when there is
878 // an unrecognized option; we need to do this because otherwise the
879 // boost parser may choke on arguments intended for PHP application.
880 // Also, during the manual pass we keep track of our position in the
881 // original argv array so that we can correctly detect the first
882 // occurrence of "--" (since the boost parser swallows this token).
883 auto opts = command_line_parser(argc, argv)
884 .options(desc)
885 .positional(p)
886 .allow_unregistered()
887 .run();
888 // argvPos will track where we are in the original argv array
889 int argvPos = 1;
890 for (unsigned i = 0; i < opts.options.size(); ++i) {
891 const auto& option = opts.options[i];
892 if (option.unregistered) {
893 Logger::Error("Error in command line: unknown option %s",
894 option.original_tokens[0].c_str());
895 cout << desc << "n";
896 return -1;
898 // Check if we've encountered "--" and make sure that argvPos
899 // accounts for it
900 bool foundDashDash = !strcmp(argv[argvPos], "--");
901 if (foundDashDash) {
902 ++argvPos;
904 // If we haven't encountered "--" or the first non-option
905 // argument, update argvPos appropriately and keep scanning
906 if (!foundDashDash && option.position_key == -1) {
907 argvPos += option.original_tokens.size();
908 continue;
910 // We've found the first occurrence of "--" or a non-option
911 // argument. Truncate opts.options, and then take all the
912 // remaining arguments from the original argv array and insert
913 // them into opts.options.
914 opts.options.resize(i);
915 std::vector<basic_option<char> > vec;
916 int pos = 0;
917 for (int m = argvPos; m < argc; ++m) {
918 string str = argv[m];
919 basic_option<char> bo;
920 bo.string_key = "arg";
921 bo.position_key = pos++;
922 bo.value.push_back(str);
923 bo.original_tokens.push_back(str);
924 bo.unregistered = false;
925 bo.case_insensitive = false;
926 opts.options.push_back(bo);
928 break;
930 store(opts, vm);
931 notify(vm);
932 if (po.mode == "d") po.mode = "debug";
933 if (po.mode == "s") po.mode = "server";
934 if (po.mode == "t") po.mode = "translate";
935 if (po.mode == "") po.mode = "run";
936 if (po.mode == "daemon" || po.mode == "server" || po.mode == "replay" ||
937 po.mode == "run" || po.mode == "debug"|| po.mode == "translate") {
938 set_execution_mode(po.mode);
939 } else {
940 Logger::Error("Error in command line: invalid mode: %s", po.mode.c_str());
941 cout << desc << "\n";
942 return -1;
944 } catch (error &e) {
945 Logger::Error("Error in command line: %s", e.what());
946 cout << desc << "\n";
947 return -1;
948 } catch (...) {
949 Logger::Error("Error in command line.");
950 cout << desc << "\n";
951 return -1;
953 if (vm.count("help")) {
954 cout << desc << "\n";
955 return 0;
957 if (vm.count("version")) {
958 #ifdef HPHP_VERSION
959 #undefine HPHP_VERSION
960 #endif
961 #define HPHP_VERSION(v) const char *version = #v;
962 #include "../../version"
964 cout << "HipHop VM";
965 cout << " v" << version << " (" << (debug ? "dbg" : "rel") << ")\n";
966 cout << "Compiler: " << kCompilerId << "\n";
967 cout << "Repo schema: " << kRepoSchemaId << "\n";
968 return 0;
970 if (vm.count("compiler-id")) {
971 cout << kCompilerId << "\n";
972 return 0;
975 if (vm.count("repo-schema")) {
976 cout << kRepoSchemaId << "\n";
977 return 0;
980 if (!po.show.empty()) {
981 PlainFile f;
982 f.open(po.show, "r");
983 if (!f.valid()) {
984 Logger::Error("Unable to open file %s", po.show.c_str());
985 return 1;
987 f.print();
988 f.close();
989 return 0;
992 po.isTempFile = vm.count("temp-file");
994 // forget the source for systemlib.php unless we are debugging
995 if (po.mode != "debug") SystemLib::s_source = "";
997 // we need to initialize pcre cache table very early
998 pcre_init();
1000 Hdf config;
1001 if (!po.config.empty()) {
1002 config.open(po.config);
1004 RuntimeOption::Load(config, &po.confStrings);
1005 vector<string> badnodes;
1006 config.lint(badnodes);
1007 for (unsigned int i = 0; i < badnodes.size(); i++) {
1008 Logger::Error("Possible bad config node: %s", badnodes[i].c_str());
1011 vector<int> inherited_fds;
1012 RuntimeOption::BuildId = po.buildId;
1013 if (po.port != -1) {
1014 RuntimeOption::ServerPort = po.port;
1016 if (po.portfd != -1) {
1017 RuntimeOption::ServerPortFd = po.portfd;
1018 inherited_fds.push_back(po.portfd);
1020 if (po.sslportfd != -1) {
1021 RuntimeOption::SSLPortFd = po.sslportfd;
1022 inherited_fds.push_back(po.sslportfd);
1024 if (po.admin_port != -1) {
1025 RuntimeOption::AdminServerPort = po.admin_port;
1027 if (po.noSafeAccessCheck) {
1028 RuntimeOption::SafeFileAccess = false;
1031 if (po.mode == "daemon") {
1032 if (RuntimeOption::LogFile.empty()) {
1033 Logger::Error("Log file not specified under daemon mode.\n\n");
1035 int ret = open_server_log_file();
1036 Process::Daemonize();
1037 close_server_log_file(ret);
1040 open_server_log_file();
1042 // Defer the initialization of light processes until the log file handle is
1043 // created, so that light processes can log to the right place. If we ever
1044 // lose a light process, stop the server instead of proceeding in an
1045 // uncertain state.
1046 LightProcess::SetLostChildHandler([](pid_t child) {
1047 if (!HttpServer::Server) return;
1048 if (!HttpServer::Server->isStopped()) {
1049 HttpServer::Server->stop("lost light process child");
1052 LightProcess::Initialize(RuntimeOption::LightProcessFilePrefix,
1053 RuntimeOption::LightProcessCount,
1054 inherited_fds);
1057 const size_t stackSizeMinimum = 8 * 1024 * 1024;
1058 struct rlimit rlim;
1059 if (getrlimit(RLIMIT_STACK, &rlim) == 0 &&
1060 (rlim.rlim_cur == RLIM_INFINITY ||
1061 rlim.rlim_cur < stackSizeMinimum)) {
1062 rlim.rlim_cur = stackSizeMinimum;
1063 if (stackSizeMinimum > rlim.rlim_max) {
1064 rlim.rlim_max = stackSizeMinimum;
1066 if (setrlimit(RLIMIT_STACK, &rlim)) {
1067 Logger::Error("failed to set stack limit to %zd\n", stackSizeMinimum);
1072 ShmCounters::initialize(true, Logger::Error);
1073 // Initialize compiler state
1074 compile_file(0, 0, MD5(), 0);
1076 if (!po.lint.empty()) {
1077 if (po.isTempFile) {
1078 tempFile = po.lint;
1081 hphp_process_init();
1082 try {
1083 HPHP::Eval::PhpFile* phpFile = g_vmContext->lookupPhpFile(
1084 StringData::GetStaticString(po.lint.c_str()), "", nullptr);
1085 if (phpFile == nullptr) {
1086 throw FileOpenException(po.lint.c_str());
1088 Unit* unit = phpFile->unit();
1089 const StringData* msg;
1090 int line;
1091 if (unit->compileTimeFatal(msg, line)) {
1092 VMParserFrame parserFrame;
1093 parserFrame.filename = po.lint.c_str();
1094 parserFrame.lineNumber = line;
1095 Array bt = g_vmContext->debugBacktrace(false, true,
1096 false, &parserFrame);
1097 throw FatalErrorException(msg->data(), bt);
1099 } catch (FileOpenException &e) {
1100 Logger::Error("%s", e.getMessage().c_str());
1101 return 1;
1102 } catch (const FatalErrorException& e) {
1103 RuntimeOption::CallUserHandlerOnFatals = false;
1104 RuntimeOption::AlwaysLogUnhandledExceptions = true;
1105 g_context->onFatalError(e);
1106 return 1;
1108 Logger::Info("No syntax errors detected in %s", po.lint.c_str());
1109 return 0;
1112 if (!po.parse.empty()) {
1113 Logger::Error("The 'parse' command line option is not supported\n\n");
1114 return 1;
1117 if (argc <= 1 || po.mode == "run" || po.mode == "debug") {
1118 if (po.isTempFile) {
1119 tempFile = po.file;
1122 set_execution_mode("run");
1124 int new_argc;
1125 char **new_argv;
1126 prepare_args(new_argc, new_argv, po.args, po.file.c_str());
1128 if (!po.file.empty()) {
1129 Repo::setCliFile(po.file);
1130 } else if (new_argc > 0) {
1131 Repo::setCliFile(new_argv[0]);
1134 int ret = 0;
1135 hphp_process_init();
1137 string file;
1138 if (new_argc > 0) {
1139 file = new_argv[0];
1142 if (po.mode == "debug") {
1143 StackTraceNoHeap::AddExtraLogging("IsDebugger", "True");
1144 RuntimeOption::EnableDebugger = true;
1145 po.debugger_options.fileName = file;
1146 po.debugger_options.user = po.user;
1147 Eval::DebuggerProxyPtr localProxy =
1148 Eval::Debugger::StartClient(po.debugger_options);
1149 if (!localProxy) {
1150 Logger::Error("Failed to start debugger client\n\n");
1151 return 1;
1153 Eval::Debugger::RegisterSandbox(localProxy->getDummyInfo());
1154 Eval::Debugger::RegisterThread();
1155 StringVecPtr client_args;
1156 bool restart = false;
1157 ret = 0;
1158 while (true) {
1159 try {
1160 execute_command_line_begin(new_argc, new_argv, po.xhprofFlags);
1161 // Set the proxy for this thread to be the localProxy we just
1162 // created. If we're script debugging, this will be the proxy that
1163 // does all of our work. If we're remote debugging, this proxy will
1164 // go unused until we finally stop it when the user quits the
1165 // debugger.
1166 g_context->setSandboxId(localProxy->getDummyInfo().id());
1167 Eval::Debugger::DebuggerSession(po.debugger_options, restart);
1168 restart = false;
1169 execute_command_line_end(po.xhprofFlags, true, file.c_str());
1170 } catch (const Eval::DebuggerRestartException &e) {
1171 execute_command_line_end(0, false, nullptr);
1173 if (!e.m_args->empty()) {
1174 file = e.m_args->at(0);
1175 client_args = e.m_args;
1176 free(new_argv);
1177 prepare_args(new_argc, new_argv, *client_args, nullptr);
1179 // Systemlib.php is not loaded again, so we need this if we
1180 // are to hit any breakpoints in systemlib.
1181 phpSetBreakPoints(localProxy.get());
1182 restart = true;
1183 } catch (const Eval::DebuggerClientExitException &e) {
1184 execute_command_line_end(0, false, nullptr);
1185 break; // end user quitting debugger
1189 } else {
1190 ret = 0;
1191 for (int i = 0; i < po.count; i++) {
1192 execute_command_line_begin(new_argc, new_argv, po.xhprofFlags);
1193 ret = 1;
1194 if (hphp_invoke_simple(file)) {
1195 ret = ExitException::ExitCode;
1197 execute_command_line_end(po.xhprofFlags, true, file.c_str());
1201 free(new_argv);
1202 hphp_process_exit();
1204 return ret;
1207 if (po.mode == "daemon" || po.mode == "server") {
1208 if (!po.user.empty()) RuntimeOption::ServerUser = po.user;
1209 return start_server(RuntimeOption::ServerUser);
1212 if (po.mode == "replay" && !po.args.empty()) {
1213 RuntimeOption::RecordInput = false;
1214 set_execution_mode("server");
1215 HttpServer server; // so we initialize runtime properly
1216 HttpRequestHandler handler(0);
1217 for (int i = 0; i < po.count; i++) {
1218 for (unsigned int j = 0; j < po.args.size(); j++) {
1219 ReplayTransport rt;
1220 rt.replayInput(po.args[j].c_str());
1221 handler.handleRequest(&rt);
1222 printf("%s\n", rt.getResponse().c_str());
1225 return 0;
1228 if (po.mode == "translate" && !po.args.empty()) {
1229 printf("%s", translate_stack(po.args[0].c_str()).c_str());
1230 return 0;
1233 cout << desc << "\n";
1234 return -1;
1237 String canonicalize_path(CStrRef p, const char* root, int rootLen) {
1238 String path(Util::canonicalize(p.c_str(), p.size()), AttachString);
1239 if (path.charAt(0) == '/') {
1240 const string &sourceRoot = RuntimeOption::SourceRoot;
1241 int len = sourceRoot.size();
1242 if (len && strncmp(path.data(), sourceRoot.c_str(), len) == 0) {
1243 return path.substr(len);
1245 if (root && rootLen && strncmp(path.data(), root, rootLen) == 0) {
1246 return path.substr(rootLen);
1249 return path;
1252 static string systemlib_split(string slib, string* hhas) {
1253 auto pos = slib.find("\n<?hhas\n");
1254 if (pos != string::npos) {
1255 if (hhas) *hhas = slib.substr(pos + 8);
1256 return slib.substr(0, pos);
1258 return slib;
1261 // Search for systemlib.php in the following places:
1262 // 1) ${HHVM_SYSTEMLIB}
1263 // 2) section "systemlib" in the current executable
1264 // and return its contents
1265 string get_systemlib(string* hhas) {
1266 if (char *file = getenv("HHVM_SYSTEMLIB")) {
1267 std::ifstream ifs(file);
1268 if (ifs.good()) {
1269 return systemlib_split(std::string(
1270 std::istreambuf_iterator<char>(ifs),
1271 std::istreambuf_iterator<char>()), hhas);
1275 Util::embedded_data desc;
1276 if (!Util::get_embedded_data("systemlib", &desc)) return "";
1278 std::ifstream ifs(desc.m_filename);
1279 if (!ifs.good()) return "";
1280 ifs.seekg(desc.m_start, std::ios::beg);
1281 std::unique_ptr<char[]> data(new char[desc.m_len]);
1282 ifs.read(data.get(), desc.m_len);
1283 string ret = systemlib_split(string(data.get(), desc.m_len), hhas);
1284 return ret;
1287 ///////////////////////////////////////////////////////////////////////////////
1288 // C++ ffi
1290 extern "C" void hphp_fatal_error(const char *s) {
1291 throw_fatal(s);
1294 static void on_timeout(int sig, siginfo_t* info, void* context) {
1295 if (sig == SIGVTALRM && info && info->si_code == SI_TIMER) {
1296 auto data = (RequestInjectionData*)info->si_value.sival_ptr;
1297 data->onTimeout();
1301 void hphp_process_init() {
1302 pthread_attr_t attr;
1303 #if defined(_GNU_SOURCE) && defined(__linux__) // Linux+GNU extension
1304 pthread_getattr_np(pthread_self(), &attr);
1305 #else
1306 pthread_attr_init(&attr);
1307 #endif
1308 Util::init_stack_limits(&attr);
1309 pthread_attr_destroy(&attr);
1311 struct sigaction action = {};
1312 action.sa_sigaction = on_timeout;
1313 action.sa_flags = SA_SIGINFO | SA_NODEFER;
1314 sigaction(SIGVTALRM, &action, nullptr);
1316 init_thread_locals();
1317 ClassInfo::Load();
1318 Process::InitProcessStatics();
1320 // reinitialize pcre table
1321 pcre_reinit();
1323 // the liboniguruma docs say this isnt needed,
1324 // but the implementation of init is not
1325 // thread safe due to bugs
1326 onig_init();
1328 // simple xml also needs one time init
1329 xmlInitParser();
1331 g_vmProcessInit();
1333 PageletServer::Restart();
1334 XboxServer::Restart();
1335 Stream::RegisterCoreWrappers();
1336 Extension::InitModules();
1337 for (InitFiniNode *in = extra_process_init; in; in = in->next) {
1338 in->func();
1340 int64_t save = RuntimeOption::SerializationSizeLimit;
1341 RuntimeOption::SerializationSizeLimit = StringData::MaxSize;
1342 apc_load(apcExtension::LoadThread);
1343 RuntimeOption::SerializationSizeLimit = save;
1345 Transl::TargetCache::requestExit();
1346 // Reset the preloaded g_context
1347 ExecutionContext *context = g_context.getNoCheck();
1348 context->~ExecutionContext();
1349 new (context) ExecutionContext();
1352 static void handle_exception(bool& ret, ExecutionContext* context,
1353 std::string& errorMsg, ContextOfException where,
1354 bool& error, bool richErrorMsg) {
1355 assert(where == ContextOfException::Invoke ||
1356 where == ContextOfException::ReqInit);
1357 try {
1358 handle_exception_helper(ret, context, errorMsg, where, error, richErrorMsg);
1359 } catch (const ExitException &e) {
1360 // Got an ExitException during exception handling, handle
1361 // similarly to the case below but don't call obEndAll().
1362 } catch (...) {
1363 handle_exception_helper(ret, context, errorMsg, ContextOfException::Handler,
1364 error, richErrorMsg);
1365 context->obEndAll();
1369 static void handle_reqinit_exception(bool &ret, ExecutionContext *context,
1370 std::string &errorMsg, bool &error) {
1371 handle_exception(ret, context, errorMsg, ContextOfException::ReqInit, error,
1372 false);
1375 static void handle_invoke_exception(bool &ret, ExecutionContext *context,
1376 std::string &errorMsg, bool &error,
1377 bool richErrorMsg) {
1378 handle_exception(ret, context, errorMsg, ContextOfException::Invoke, error,
1379 richErrorMsg);
1382 static bool hphp_warmup(ExecutionContext *context,
1383 const string &reqInitFunc,
1384 const string &reqInitDoc, bool &error) {
1385 bool ret = true;
1386 error = false;
1387 std::string errorMsg;
1389 MemoryManager *mm = MemoryManager::TheMemoryManager();
1390 if (mm->isEnabled()) {
1391 ServerStatsHelper ssh("reqinit");
1392 try {
1393 if (!reqInitDoc.empty()) {
1394 include_impl_invoke(reqInitDoc, true);
1396 if (!reqInitFunc.empty()) {
1397 invoke(reqInitFunc.c_str(), Array());
1399 context->backupSession();
1400 } catch (...) {
1401 handle_reqinit_exception(ret, context, errorMsg, error);
1405 return ret;
1408 void hphp_session_init() {
1409 init_thread_locals();
1410 ThreadInfo::s_threadInfo->onSessionInit();
1411 MemoryManager::TheMemoryManager()->resetStats();
1413 #ifdef ENABLE_SIMPLE_COUNTER
1414 SimpleCounter::Enabled = true;
1415 StackTrace::Enabled = true;
1416 #endif
1418 // Ordering is sensitive; StatCache::requestInit produces work that
1419 // must be done in VMExecutionContext::requestInit.
1420 StatCache::requestInit();
1422 g_vmContext->requestInit();
1424 EnvConstants *g = get_env_constants();
1425 g->k_PHP_SAPI = StringData::GetStaticString(RuntimeOption::ExecutionMode);
1426 g->k_PHP_BINARY = current_executable_path();
1427 g->k_PHP_BINDIR = current_executable_directory();
1430 bool hphp_is_warmup_enabled() {
1431 MemoryManager *mm = MemoryManager::TheMemoryManager();
1432 return mm->isEnabled();
1435 ExecutionContext *hphp_context_init() {
1436 ExecutionContext *context = g_context.getNoCheck();
1437 context->obStart();
1438 context->obProtect(true);
1439 return context;
1442 bool hphp_invoke_simple(const std::string &filename,
1443 bool warmupOnly /* = false */) {
1444 bool error;
1445 string errorMsg;
1446 return hphp_invoke(g_context.getNoCheck(), filename, false, null_array,
1447 uninit_null(), "", "", error, errorMsg, true, warmupOnly);
1450 bool hphp_invoke(ExecutionContext *context, const std::string &cmd,
1451 bool func, CArrRef funcParams, VRefParam funcRet,
1452 const string &reqInitFunc, const string &reqInitDoc,
1453 bool &error, string &errorMsg,
1454 bool once /* = true */, bool warmupOnly /* = false */,
1455 bool richErrorMsg /* = false */) {
1456 bool isServer = RuntimeOption::ServerExecutionMode();
1457 error = false;
1459 String oldCwd;
1460 if (isServer) {
1461 oldCwd = context->getCwd();
1463 if (!hphp_warmup(context, reqInitFunc, reqInitDoc, error)) {
1464 if (isServer) context->setCwd(oldCwd);
1465 return false;
1468 bool ret = true;
1469 if (!warmupOnly) {
1470 try {
1471 ServerStatsHelper ssh("invoke");
1472 if (func) {
1473 funcRet->assignVal(invoke(cmd.c_str(), funcParams));
1474 } else {
1475 if (isServer) hphp_chdir_file(cmd);
1476 include_impl_invoke(cmd.c_str(), once);
1478 } catch (...) {
1479 handle_invoke_exception(ret, context, errorMsg, error, richErrorMsg);
1483 try {
1484 context->onShutdownPreSend();
1485 } catch (...) {
1486 handle_invoke_exception(ret, context, errorMsg, error, richErrorMsg);
1489 if (isServer) context->setCwd(oldCwd);
1490 return ret;
1493 void hphp_context_exit(ExecutionContext *context, bool psp,
1494 bool shutdown /* = true */,
1495 const char *program /* = NULL */) {
1496 if (psp) {
1497 context->onShutdownPostSend();
1499 if (RuntimeOption::EnableDebugger) {
1500 try {
1501 Eval::Debugger::InterruptPSPEnded(program);
1502 } catch (const Eval::DebuggerException &e) {}
1505 // Run shutdown handlers. This may cause user code to run.
1506 static_cast<VMExecutionContext*>(context)->destructObjects();
1507 if (shutdown) {
1508 context->onRequestShutdown();
1511 // Clean up a bunch of request state. No user code after this point.
1512 context->requestExit();
1513 context->obProtect(false);
1514 context->obEndAll();
1517 void hphp_thread_exit() {
1518 finish_thread_locals();
1521 void hphp_session_exit() {
1522 // Server note has to live long enough for the access log to fire.
1523 // RequestLocal is too early.
1524 ServerNote::Reset();
1525 g_context.destroy();
1527 ThreadInfo::s_threadInfo->clearPendingException();
1529 MemoryManager *mm = MemoryManager::TheMemoryManager();
1530 if (RuntimeOption::CheckMemory) {
1531 mm->checkMemory();
1533 mm->resetStats();
1535 if (mm->isEnabled()) {
1536 ServerStatsHelper ssh("rollback");
1537 // sweep may call g_context->, which is a noCheck, so we need to
1538 // reinitialize g_context here
1539 g_context.getCheck();
1540 // MemoryManager::sweepAll() will handle sweeping for PHP objects and
1541 // PHP resources (ex. File, Collator, XmlReader, etc.)
1542 mm->sweepAll();
1543 // Destroy g_context again because ExecutionContext has SmartAllocated
1544 // data members. These members cannot survive over rollback(), so we need
1545 // to destroy g_context before calling rollback().
1546 g_context.destroy();
1547 // MemoryManager::rollback() will handle sweeping for all types that have
1548 // dedicated allocators (ex. StringData, HphpArray, etc.) and it reset all
1549 // of the allocators in preparation for the next request.
1550 mm->rollback();
1551 // Do any post-sweep cleanup necessary for global variables
1552 free_global_variables_after_sweep();
1553 g_context.getCheck();
1554 } else {
1555 g_context.getCheck();
1556 ServerStatsHelper ssh("free");
1557 free_global_variables();
1560 ThreadInfo::s_threadInfo->onSessionExit();
1563 void hphp_process_exit() {
1564 PageletServer::Stop();
1565 XboxServer::Stop();
1566 Eval::Debugger::Stop();
1567 Extension::ShutdownModules();
1568 LightProcess::Close();
1569 for (InitFiniNode *in = extra_process_exit; in; in = in->next) {
1570 in->func();
1574 ///////////////////////////////////////////////////////////////////////////////