teleport/server/tp_web/src/main.cpp

678 lines
14 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "ts_env.h"
#include "ts_ver.h"
#include <ex.h>
#include <pys.h>
// 命令行参数说明(不带参数运行则以服务方式启动)
// 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 <vld.h>
// #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 <fcntl.h>
#include <signal.h>
#include <sys/stat.h>
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;
}