#include "ts_env.h" #include "ts_ver.h" #include #include // 命令行参数说明(不带参数运行则以服务方式启动) // tp_web [-i|-u|--version] [ [-d] start] [...] // -d 启动程序并输出调试信息(不会运行为守护进程/服务模式) // -i 安装服务然后退出(仅限Win平台) // -u 卸载服务然后退出(仅限Win平台) // --version 打印版本号然后退出 // start 以服务方式运行 // ... 剩余的所有参数均传递给python脚本 // // // 执行指定的Python脚本: // tp_web --py [-f FuncName] script_file.py ... // --py 必须为第一个参数,表示本次执行为执行指定脚本 // -f FuncName 指定入口函数,默认为main。 // script-file.py 被执行的脚本文件 // ... 剩余的所有参数均传递给Python脚本 ExLogger g_ex_logger; bool g_is_debug = false; static ex_wstrs g_py_args; // 如果是执行指定脚本 static ex_wstr g_py_script_file; static ex_wstr g_py_main_func; #define RUN_UNKNOWN 0 #define RUN_WEB 1 #define RUN_PY_SCRIPT 2 #define RUN_INSTALL_SRV 3 #define RUN_UNINST_SRV 4 static ex_u8 g_run_type = RUN_UNKNOWN; #define EOM_WEB_SERVICE_NAME L"Teleport Web Service" static bool _run_daemon(void); // 导出函数给Python脚本使用(主要是为了记录日志) // Windows平台上,tp_web程序打开日志文件写之后,Python脚本尝试写入方式打开此日志文件时会失败。 PyObject* init_web_builtin_module(void); #ifdef EX_OS_WIN32 static int service_install() { ex_wstr exec_file(g_env.m_exec_file); exec_file += L" start"; if (EXRV_OK == ex_winsrv_install(EOM_WEB_SERVICE_NAME, EOM_WEB_SERVICE_NAME, exec_file)) return 0; else return 1; } static int service_uninstall() { if (EXRV_OK != ex_winsrv_stop(EOM_WEB_SERVICE_NAME)) return 1; if (EXRV_OK != ex_winsrv_uninstall(EOM_WEB_SERVICE_NAME)) return 2; return 0; } #endif static bool _process_cmd_line(int argc, wchar_t** argv) { if (argc <= 1) { EXLOGE("[tpweb] nothing to do.\n\n"); return false; } g_run_type = RUN_UNKNOWN; bool is_py_arg = false; bool is_py_func = false; if (0 == wcscmp(argv[1], L"--version")) { EXLOGV("\nTeleport Web Server, version %ls.\n\n", TP_SERVER_VER); return false; } else if (0 == wcscmp(argv[1], L"--py")) { g_run_type = RUN_PY_SCRIPT; for (int i = 2; i < argc; ++i) { if (is_py_arg) { g_py_args.push_back(argv[i]); continue; } if(is_py_func) { g_py_main_func = argv[i]; is_py_func = false; continue; } if (0 == wcscmp(argv[i], L"-f")) { is_py_func = true; continue; } if (g_py_script_file.length() == 0) { g_py_script_file = argv[i]; is_py_arg = true; continue; } } } else if (0 == wcscmp(argv[1], L"-i")) { g_run_type = RUN_INSTALL_SRV; } else if (0 == wcscmp(argv[1], L"-u")) { g_run_type = RUN_UNINST_SRV; } else { for (int i = 1; i < argc; ++i) { if (is_py_arg) { g_py_args.push_back(argv[i]); continue; } if (0 == wcscmp(argv[i], L"start")) { g_run_type = RUN_WEB; is_py_arg = true; continue; } if (0 == wcscmp(argv[i], L"-d")) { g_is_debug = true; continue; } EXLOGE(L"[tpweb] Unknown option: %ls\n", argv[i]); return false; } } if (g_run_type == RUN_UNKNOWN) { EXLOGE("[tpweb] nothing to do.\n\n"); return false; } return true; } static int _main_loop(void) { PYS_HANDLE pysh = pys_create(); if (NULL == pysh) { EXLOGE("pys_create() failed.\n"); return 1; } PysHandleHolder hh(pysh); ex_wstr pysrt_path(g_env.m_exec_path); if(!ex_path_join(pysrt_path, false, L"pysrt", NULL)) { EXLOGE("pysrt not exists.\n"); return 1; } if (!pys_init_runtime(pysh, g_env.m_exec_file.c_str(), pysrt_path.c_str())) { EXLOGE("pys_init_runtime() failed.\n"); return 1; } // 设置web的路径 ex_wstr sf_path; if (g_run_type == RUN_WEB) { sf_path = g_env.m_www_path; if (!ex_path_join(sf_path, false, L"teleport", L"app_bootstrap.py", NULL)) { EXLOGE(L"[tpweb] invalid path [%ls].\n", sf_path.c_str()); return 1; } if (ex_is_file_exists(sf_path.c_str())) { pys_set_startup_file(pysh, sf_path.c_str()); } else { EXLOGE(L"[tpweb] teleport web app not found at [%ls].\n", sf_path.c_str()); return 1; } } else if (g_run_type == RUN_PY_SCRIPT) { sf_path = g_env.m_exec_path; if (!ex_is_file_exists(g_py_script_file.c_str())) { EXLOGE("[tpweb] file not found: [%s].\n", g_py_script_file.c_str()); return 1; } if (g_py_main_func.length() == 0) { pys_set_startup_file(pysh, g_py_script_file.c_str()); } else { // ex_astr file_name; ex_astr func_name; // ex_wstr2astr(g_py_script_file, file_name); ex_wstr2astr(g_py_main_func, func_name); // // pys_set_bootstrap_module(pysh, file_name.c_str(), func_name.c_str()); pys_set_bootstrap_module(pysh, NULL, func_name.c_str()); pys_set_startup_file(pysh, g_py_script_file.c_str()); } // ex_astr add_path("/home/apex/work/tp-py37/server/www/teleport"); // ex_wstr add_path_w; // ex_astr2wstr(add_path, add_path_w); // // pys_add_search_path(pysh, add_path_w.c_str()); } ex_wstrs::const_iterator it = g_py_args.begin(); for (; it != g_py_args.end(); ++it) { pys_add_arg(pysh, it->c_str()); } if (!pys_add_builtin_module(pysh, "tpweb", init_web_builtin_module)) { EXLOGE("[tpweb] can not add builtin module for python script.\n"); return 1; } return pys_run(pysh); } int _app_main(int argc, wchar_t** argv) { EXLOG_USE_LOGGER(&g_ex_logger); if (!_process_cmd_line(argc, argv)) return 1; if (g_run_type == RUN_PY_SCRIPT) { if (!g_env.init(false)) { EXLOGE("[tpweb] env init failed.\n"); return 1; } return _main_loop(); } #ifdef EX_DEBUG EXLOG_LEVEL(EX_LOG_LEVEL_DEBUG); #endif #ifdef EX_OS_WIN32 if (g_run_type == RUN_INSTALL_SRV) { if (!g_env.init(false)) { EXLOGE("[tpweb] env init failed.\n"); return 1; } return service_install(); } else if(g_run_type == RUN_UNINST_SRV) { if (!g_env.init(false)) { EXLOGE("[tpweb] env init failed.\n"); return 1; } return service_uninstall(); } #endif if (!g_env.init(true)) { EXLOGE("[tpweb] env init failed.\n"); return 1; } if (!g_is_debug) { if (!_run_daemon()) { EXLOGE("[tpweb] can not run in daemon mode.\n"); return 1; } #ifdef EX_OS_WIN32 return 0; #endif } return _main_loop(); } #ifdef EX_OS_WIN32 // #ifdef EX_DEBUG // #include // #endif static SERVICE_STATUS g_ServiceStatus = { 0 }; static SERVICE_STATUS_HANDLE g_hServiceStatusHandle = NULL; HANDLE g_hWorkerThread = NULL; VOID WINAPI service_main(DWORD argc, wchar_t** argv); void WINAPI service_handler(DWORD fdwControl); static DWORD WINAPI service_thread_func(LPVOID lpParam); int main() { int ret = 0; LPWSTR szCmdLine = (LPWSTR)::GetCommandLineW(); //获取命令行参数; int _argc = 0; wchar_t** _argv = ::CommandLineToArgvW(szCmdLine, &_argc); //拆分命令行参数字符串; ret = _app_main(_argc, _argv); LocalFree(_argv); _argv = NULL; return ret; } static bool _run_daemon(void) { SERVICE_TABLE_ENTRY DispatchTable[2]; DispatchTable[0].lpServiceName = EOM_WEB_SERVICE_NAME; DispatchTable[0].lpServiceProc = service_main; DispatchTable[1].lpServiceName = NULL; DispatchTable[1].lpServiceProc = NULL; if (!StartServiceCtrlDispatcher(DispatchTable)) { EXLOGE_WIN("StartServiceCtrlDispatcher()"); return false; } return true; } static DWORD WINAPI service_thread_func(LPVOID lpParam) { int ret = _main_loop(); // 更新服务状态(如果服务还在运行,将其设置为停止状态) g_ServiceStatus.dwWin32ExitCode = 0; g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; g_ServiceStatus.dwCheckPoint = 0; g_ServiceStatus.dwWaitHint = 0; if (!SetServiceStatus(g_hServiceStatusHandle, &g_ServiceStatus)) EXLOGE_WIN("SetServiceStatus()"); return ret; } static void WINAPI service_handler(DWORD fdwControl) { switch (fdwControl) { case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: { if (g_hWorkerThread) { TerminateThread(g_hWorkerThread, 1); g_hWorkerThread = NULL; } g_ServiceStatus.dwWin32ExitCode = 0; g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; g_ServiceStatus.dwCheckPoint = 0; g_ServiceStatus.dwWaitHint = 0; }break; default: return; }; if (!SetServiceStatus(g_hServiceStatusHandle, &g_ServiceStatus)) { EXLOGE_WIN("SetServiceStatus(STOP)"); return; } } VOID WINAPI service_main(DWORD argc, wchar_t** argv) { g_ServiceStatus.dwServiceType = SERVICE_WIN32; g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; g_ServiceStatus.dwWin32ExitCode = 0; g_ServiceStatus.dwServiceSpecificExitCode = 0; g_ServiceStatus.dwCheckPoint = 0; g_ServiceStatus.dwWaitHint = 0; g_hServiceStatusHandle = RegisterServiceCtrlHandler(EOM_WEB_SERVICE_NAME, service_handler); if (g_hServiceStatusHandle == 0) { EXLOGE_WIN("RegisterServiceCtrlHandler()"); return; } DWORD tid = 0; g_hWorkerThread = CreateThread(NULL, 0, service_thread_func, NULL, 0, &tid); if (NULL == g_hWorkerThread) { EXLOGE_WIN("CreateThread(python)"); g_ServiceStatus.dwWin32ExitCode = 0; g_ServiceStatus.dwCurrentState = SERVICE_STOPPED; g_ServiceStatus.dwCheckPoint = 0; g_ServiceStatus.dwWaitHint = 0; if (!SetServiceStatus(g_hServiceStatusHandle, &g_ServiceStatus)) EXLOGE_WIN("SetServiceStatus()"); return; } g_ServiceStatus.dwCurrentState = SERVICE_RUNNING; g_ServiceStatus.dwCheckPoint = 0; g_ServiceStatus.dwWaitHint = 9000; if (!SetServiceStatus(g_hServiceStatusHandle, &g_ServiceStatus)) { EXLOGE_WIN("SetServiceStatus()"); return; } } //====================================================== #else // Linux or MacOS #include #include #include static void _sig_handler(int signum, siginfo_t* info, void* ptr); //static int _daemon(int nochdir, int noclose); int main(int argc, char** argv) { struct sigaction act; memset(&act, 0, sizeof(act)); act.sa_sigaction = _sig_handler; act.sa_flags = SA_SIGINFO; sigaction(SIGINT, &act, NULL); wchar_t** wargv = ex_make_wargv(argc, argv); int ret = _app_main(argc, wargv); ex_free_wargv(argc, wargv); return ret; } void _sig_handler(int signum, siginfo_t* info, void* ptr) { if (signum == SIGINT || signum == SIGTERM) { EXLOGW("[tpweb] received signal SIGINT, exit now.\n"); exit(1); } } static bool _run_daemon(void) { pid_t pid = fork(); if (pid < 0) { EXLOGE("[tpweb] can not fork daemon.\n"); exit(EXIT_FAILURE); } else if (pid > 0) { exit(EXIT_SUCCESS); // parent exit. } // now I'm first children. if (setsid() == -1) { EXLOGE("[tpweb] setsid() failed.\n"); assert(0); exit(EXIT_FAILURE); } umask(0); pid = fork(); if (pid < 0) { EXLOGE("[tpweb] can not fork daemon.\n"); exit(EXIT_FAILURE); } else if (pid > 0) { exit(0); // first children exit. } // now I'm second children. int ret = chdir("/"); close(STDIN_FILENO); int stdfd = open("/dev/null", O_RDWR); close(STDOUT_FILENO); close(STDERR_FILENO); dup2(stdfd, STDOUT_FILENO); dup2(stdfd, STDERR_FILENO); return true; } #endif //=============================================================== // 加入内建模块供脚本调用 //=============================================================== PyObject* _py_log_output(PyObject* self, PyObject* args) { // UNUSED(self); // UNUSED(args); int level = 0; const char* msg = NULL; if (!pylib_PyArg_ParseTuple(args, "is", &level, &msg)) { EXLOGE("invalid args for _py_log_output().\n"); PYLIB_RETURN_FALSE; } ex_wstr tmp; ex_astr2wstr(msg, tmp, EX_CODEPAGE_UTF8); // EXLOGE(L"(%d) %ls.\n", level, tmp.c_str()); switch (level) { case EX_LOG_LEVEL_DEBUG: ex_printf_d(tmp.c_str()); break; case EX_LOG_LEVEL_VERBOSE: ex_printf_v(tmp.c_str()); break; case EX_LOG_LEVEL_INFO: ex_printf_i(tmp.c_str()); break; case EX_LOG_LEVEL_WARN: ex_printf_w(tmp.c_str()); break; case EX_LOG_LEVEL_ERROR: ex_printf_e(tmp.c_str()); break; default: PYLIB_RETURN_FALSE; // break; } //return pylib_PyLong_FromLong(0x010001); PYLIB_RETURN_TRUE; } PyObject* _py_log_level(PyObject* self, PyObject* args) { int level = 0; if (!pylib_PyArg_ParseTuple(args, "i", &level)) { EXLOGE("invalid args for _py_log_level().\n"); PYLIB_RETURN_FALSE; } EXLOG_LEVEL(level); PYLIB_RETURN_TRUE; } PyObject* _py_log_console(PyObject* self, PyObject* args) { bool to_console = false; if (!pylib_PyArg_ParseTuple(args, "p", &to_console)) { EXLOGE("invalid args for _py_log_console().\n"); PYLIB_RETURN_FALSE; } // EXLOGE("to_console=%s\n", to_console?"true":"false"); EXLOG_CONSOLE(to_console); PYLIB_RETURN_TRUE; } PYS_BUILTIN_FUNC _demo_funcs[] = { { "log_output", // 脚本函数名,在脚本中使用 _py_log_output, // 对应的C代码函数名 PYS_TRUE, // 函数的基本信息(是否需要参数,等等) "write log." // 函数的说明文档,可选(可以是空字符串) }, { "log_level", _py_log_level, PYS_TRUE, "set log level." }, { "log_console", _py_log_console, PYS_TRUE, "set log to console or not." }, // 最后一组,第一个成员为空指针,表示结束 { NULL, NULL, 0, NULL } }; PyObject* init_web_builtin_module(void) { PyObject* mod = NULL; mod = pys_create_module("_tpweb", _demo_funcs); pys_builtin_const_long(mod, "EX_LOG_LEVEL_DEBUG", EX_LOG_LEVEL_DEBUG); pys_builtin_const_long(mod, "EX_LOG_LEVEL_VERBOSE", EX_LOG_LEVEL_VERBOSE); pys_builtin_const_long(mod, "EX_LOG_LEVEL_INFO", EX_LOG_LEVEL_INFO); pys_builtin_const_long(mod, "EX_LOG_LEVEL_WARN", EX_LOG_LEVEL_WARN); pys_builtin_const_long(mod, "EX_LOG_LEVEL_ERROR", EX_LOG_LEVEL_ERROR); // pys_builtin_const_bool(mod, "DEMO_CONST_2", PYS_TRUE); // //pys_builtin_const_wcs(mod, "DEMO_CONST_3", L"STRING 中文测试 this is string."); // pys_builtin_const_wcs(mod, "DEMO_CONST_3", L"STRING this is string."); // pys_builtin_const_utf8(mod, "DEMO_CONST_4", "this is string."); // // ex_u8 test_buf[12] = { 0x01, 0x02, 0x03, 0x04, 0x0a, 0x0b, 0x0c, 0x0d, 0x12, 0x34, 0xab, 0xcd }; // pys_builtin_const_bin(mod, "DEMO_CONST_5", test_buf, 12); return mod; }