ZLMediaKit/server/win32main.cpp

683 lines
22 KiB
C++
Raw Normal View History

2019-11-12 12:33:16 +08:00
/*
* MIT License
*
* Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <map>
#include <signal.h>
#include <stdio.h>
#include <iostream>
#include "Util/MD5.h"
#include "Util/File.h"
#include "Util/logger.h"
#include "Util/SSLBox.h"
#include "Util/onceToken.h"
#include "Util/CMD.h"
#include "Network/TcpServer.h"
#include "Poller/EventPoller.h"
#include "Common/config.h"
#include "Rtsp/UDPServer.h"
#include "Rtsp/RtspSession.h"
#include "Rtmp/RtmpSession.h"
#include "Shell/ShellSession.h"
#include "Rtmp/FlvMuxer.h"
#include "Player/PlayerProxy.h"
#include "Http/WebSocketSession.h"
#include "WebApi.h"
#include "WebHook.h"
#include "MiniDumper.h"
#include "MediaServer.h"
using namespace std;
using namespace toolkit;
using namespace mediakit;
namespace mediakit {
typedef enum { rConsole = 0, rServer, rInstall, rUninstall} RunMode;
////////////HTTP配置///////////
namespace Http {
#define HTTP_FIELD "http."
#define HTTP_PORT 10080
const string kPort = HTTP_FIELD"port";
#define HTTPS_PORT 10443
const string kSSLPort = HTTP_FIELD"sslport";
onceToken token1([](){
mINI::Instance()[kPort] = HTTP_PORT;
mINI::Instance()[kSSLPort] = HTTPS_PORT;
},nullptr);
}//namespace Http
////////////SHELL配置///////////
namespace Shell {
#define SHELL_FIELD "shell."
#define SHELL_PORT 9000
const string kPort = SHELL_FIELD"port";
onceToken token1([](){
mINI::Instance()[kPort] = SHELL_PORT;
},nullptr);
} //namespace Shell
////////////RTSP服务器配置///////////
namespace Rtsp {
#define RTSP_FIELD "rtsp."
#define RTSP_PORT 10554
#define RTSPS_PORT 322
const string kPort = RTSP_FIELD"port";
const string kSSLPort = RTSP_FIELD"sslport";
onceToken token1([](){
mINI::Instance()[kPort] = RTSP_PORT;
mINI::Instance()[kSSLPort] = RTSPS_PORT;
},nullptr);
} //namespace Rtsp
////////////RTMP服务器配置///////////
namespace Rtmp {
#define RTMP_FIELD "rtmp."
#define RTMP_PORT 10935
const string kPort = RTMP_FIELD"port";
onceToken token1([](){
mINI::Instance()[kPort] = RTMP_PORT;
},nullptr);
} //namespace RTMP
} // namespace mediakit
class CMD_main;
2019-11-19 15:11:05 +08:00
TcpServer* shellSrv = 0;
TcpServer* rtspSrv = 0;
TcpServer* rtmpSrv = 0;
TcpServer* httpSrv = 0;
//如果支持ssl还可以开启https服务器
TcpServer* httpsSrv = 0;
//支持ssl加密的rtsp服务器可用于诸如亚马逊echo show这样的设备访问
TcpServer* rtspSSLSrv = 0;
2019-11-12 12:33:16 +08:00
static MediaServer_ServerState sServerState = MediaServerStartingUpState;
const char* theXMLFilePath = 0;
char* theServerName = "";
static SERVICE_STATUS_HANDLE sServiceStatusHandle = 0;
static void ReportStatus(DWORD inCurrentState, DWORD inExitCode);
static void InstallService(char* inServiceName);
static void RemoveService(char *inServiceName);
static void RunAsService(char* inServiceName);
void WINAPI ServiceControl(DWORD);
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
MediaServer_ServerState StartServer(CMD_main& cmd_main);
void RunServer();
static void signalHandler(int signo) {
std::cerr << "Shutting down" << std::endl;
sServerState = MediaServerShuttingDownState;
}
class CMD_main : public CMD {
public:
CMD_main() {
_parser.reset(new OptionParser(nullptr));
(*_parser) << Option('r',/*该选项简称,如果是\x00则说明无简称*/
"runmode",/*该选项全称,每个选项必须有全称不得为null或空字符串*/
Option::ArgRequired,/*该选项后面必须跟值*/
to_string(rConsole).data(),/*该选项默认值*/
true,/*该选项是否必须赋值如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
"控制台运行、服务方式运行、安装到服务中从服务中卸载0~3",/*该选项说明文字*/
nullptr);
(*_parser) << Option('l',/*该选项简称,如果是\x00则说明无简称*/
"level",/*该选项全称,每个选项必须有全称不得为null或空字符串*/
Option::ArgRequired,/*该选项后面必须跟值*/
to_string(LTrace).data(),/*该选项默认值*/
false,/*该选项是否必须赋值如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
"日志等级,LTrace~LError(0~4)",/*该选项说明文字*/
nullptr);
(*_parser) << Option('m',/*该选项简称,如果是\x00则说明无简称*/
"max_day",/*该选项全称,每个选项必须有全称不得为null或空字符串*/
Option::ArgRequired,/*该选项后面必须跟值*/
"30",/*该选项默认值*/
false,/*该选项是否必须赋值如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
"日志最多保存天数",/*该选项说明文字*/
nullptr);
(*_parser) << Option('c',/*该选项简称,如果是\x00则说明无简称*/
"config",/*该选项全称,每个选项必须有全称不得为null或空字符串*/
Option::ArgRequired,/*该选项后面必须跟值*/
(exeDir() + "Config.ini").data(),/*该选项默认值*/
false,/*该选项是否必须赋值如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
"配置文件路径",/*该选项说明文字*/
nullptr);
(*_parser) << Option('s',/*该选项简称,如果是\x00则说明无简称*/
"ssl",/*该选项全称,每个选项必须有全称不得为null或空字符串*/
Option::ArgRequired,/*该选项后面必须跟值*/
(exeDir() + "ssl.p12").data(),/*该选项默认值*/
false,/*该选项是否必须赋值如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
"ssl证书文件或文件夹,支持p12/pem类型",/*该选项说明文字*/
nullptr);
(*_parser) << Option('t',/*该选项简称,如果是\x00则说明无简称*/
"threads",/*该选项全称,每个选项必须有全称不得为null或空字符串*/
Option::ArgRequired,/*该选项后面必须跟值*/
to_string(2*thread::hardware_concurrency()).data(),/*该选项默认值*/
false,/*该选项是否必须赋值如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
"启动事件触发线程数",/*该选项说明文字*/
nullptr);
}
virtual ~CMD_main() {}
virtual const char *description() const {
return "主程序命令参数";
}
};
//全局变量在WebApi中用于保存配置文件用
string g_ini_file;
CMD_main cmd_main;
int main(int argc,char *argv[]) {
bool notAService = false;
try {
cmd_main.operator()(argc, argv);
} catch (std::exception &ex) {
cout << ex.what() << endl;
return -1;
}
g_ini_file = cmd_main["config"];
RunMode rMode = (RunMode)cmd_main["runmode"].as<int>();
switch (rMode) {
case rConsole:
notAService = true;
break;
case rServer:
break;
case rInstall:
::InstallService("MediaServer");
printf("Starting the MediaServer service...\n");
::RunAsService("MediaServer");
::exit(0);
break;
case rUninstall:
printf("Removing the MediaServer service...\n");
::RemoveService("MediaServer");
::exit(0);
break;
default:
::exit(-1);
break;
}
if (notAService) {
if (signal(SIGINT, signalHandler) == SIG_ERR) {
std::cerr << "Couldn't install signal handler for SIGINT" << std::endl;
::exit(-1);
}
if (signal(SIGTERM, signalHandler) == SIG_ERR) {
std::cerr << "Couldn't install signal handler for SIGTERM" << std::endl;
::exit(-1);
}
// If we're running off the command-line, don't do the service initiation crap.^M
sServerState = ::StartServer(cmd_main); // No stats update interval for now
if (sServerState != MediaServerFatalErrorState) {
::RunServer();
::exit(0);
}
::exit(-1);
}
SERVICE_TABLE_ENTRY dispatchTable[] =
{
{ "", ServiceMain },
{ NULL, NULL }
};
// In case someone runs the server improperly, print out a friendly message.
printf("MediaServer must either be started from the DOS Console\n");
printf("using the -r command-line option, or using the Service Control Manager\n\n");
printf("Waiting for the Service Control Manager to start MediaServer...\n");
BOOL theErr = ::StartServiceCtrlDispatcher(dispatchTable);
if (!theErr)
{
printf("Fatal Error: Couldn't start Service\n");
::exit(-1);
}
return 0;
}
void __stdcall ServiceMain(DWORD /*argc*/, LPTSTR *argv)
{
theServerName = argv[0];
sServiceStatusHandle = ::RegisterServiceCtrlHandler(theServerName, &ServiceControl);
if (sServiceStatusHandle == 0)
{
printf("Failure registering service handler");
::exit(-1);
}
//
// Report our status
::ReportStatus(SERVICE_START_PENDING, NO_ERROR);
// Start & Run the server - no stats update interval for now
sServerState = ::StartServer(cmd_main);
if (sServerState != MediaServerFatalErrorState)
{
::ReportStatus(SERVICE_RUNNING, NO_ERROR);
::RunServer(); // This function won't return until the server has died
//
// Ok, server is done...
::ReportStatus(SERVICE_STOPPED, NO_ERROR);
::exit(0);
}
else {
::ReportStatus(SERVICE_STOPPED, ERROR_BAD_COMMAND); // I dunno... report some error
::exit(-1);
}
}
void WINAPI ServiceControl(DWORD inControlCode)
{
DWORD theStatusReport = SERVICE_START_PENDING;
switch (inControlCode)
{
// Stop the service.
//
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
{
if (sServerState == MediaServerStartingUpState)
break;
// Signal the server to shut down.
sServerState = MediaServerShuttingDownState;
break;
}
case SERVICE_CONTROL_PAUSE:
{
#if 0
if (sServerState != RunningState)
break;
// Signal the server to refuse new connections.
theState = qtssRefusingConnectionsState;
if (theServer != NULL)
theServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState));
#endif
break;
}
case SERVICE_CONTROL_CONTINUE:
{
#if 0
if (theState != qtssRefusingConnectionsState)
break;
// Signal the server to refuse new connections.
theState = qtssRefusingConnectionsState;
if (theServer != NULL)
theServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState));
#endif
break;
}
case SERVICE_CONTROL_INTERROGATE:
break; // Just update our status
default:
break;
}
// Convert a QTSS state to a Win32 Service state^M
switch (sServerState)
{
case MediaServerStartingUpState: theStatusReport = SERVICE_START_PENDING; break;
case MediaServerRunningState: theStatusReport = SERVICE_RUNNING; break;
case MediaServerRefusingConnectionsState: theStatusReport = SERVICE_PAUSED; break;
case MediaServerFatalErrorState: theStatusReport = SERVICE_STOP_PENDING; break;
case MediaServerShuttingDownState: theStatusReport = SERVICE_STOP_PENDING; break;
default: theStatusReport = SERVICE_RUNNING; break;
}
printf("Reporting status from ServiceControl function\n");
::ReportStatus(theStatusReport, NO_ERROR);
}
void ReportStatus(DWORD inCurrentState, DWORD inExitCode)
{
static bool sFirstTime = true;
static unsigned long sCheckpoint = 0;
static SERVICE_STATUS sStatus;
if (sFirstTime)
{
sFirstTime = false;
// Setup the status structure
sStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
sStatus.dwCurrentState = SERVICE_START_PENDING;
sStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
//sStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
sStatus.dwWin32ExitCode = 0;
sStatus.dwServiceSpecificExitCode = 0;
sStatus.dwCheckPoint = 0;
sStatus.dwWaitHint = 0;
}
if (sStatus.dwCurrentState == SERVICE_START_PENDING)
sStatus.dwCheckPoint = ++sCheckpoint;
else
sStatus.dwCheckPoint = 0;
sStatus.dwCurrentState = inCurrentState;
sStatus.dwServiceSpecificExitCode = inExitCode;
BOOL theErr = SetServiceStatus(sServiceStatusHandle, &sStatus);
if (theErr == 0)
{
DWORD theerrvalue = ::GetLastError();
}
}
void RunAsService(char* inServiceName)
{
SC_HANDLE theService;
SC_HANDLE theSCManager;
theSCManager = ::OpenSCManager(
NULL, // machine (NULL == local)
NULL, // database (NULL == default)
SC_MANAGER_ALL_ACCESS // access required
);
if (!theSCManager)
return;
theService = ::OpenService(
theSCManager, // SCManager database
inServiceName, // name of service
SERVICE_ALL_ACCESS);
SERVICE_STATUS lpServiceStatus;
if (theService)
{
const int kNotRunning = 1062;
bool stopped = ::ControlService(theService, SERVICE_CONTROL_STOP, &lpServiceStatus);
if (!stopped && ((int) ::GetLastError() != kNotRunning))
printf("Stopping Service Error: %d\n", ::GetLastError());
bool started = ::StartService(theService, 0, NULL);
if (!started)
printf("Starting Service Error: %d\n", ::GetLastError());
::CloseServiceHandle(theService);
}
::CloseServiceHandle(theSCManager);
}
void InstallService(char* inServiceName)
{
SC_HANDLE theService;
SC_HANDLE theSCManager;
TCHAR thePath[512];
TCHAR theAppBaseName[512];
TCHAR theQuotedPath[522];
BOOL theErr = ::GetModuleFileName(NULL, thePath, 512);
if (!theErr)
return;
char* pSlash = strrchr((char*)thePath, '\\');
if (pSlash) {
strcpy((char*)theAppBaseName, (const char*)pSlash + 1);
*(pSlash + 1) = 0;
}
sprintf(theQuotedPath, "\"%s%s\" -r 1 -c \"%s\"", thePath, theAppBaseName, g_ini_file.c_str());
theSCManager = ::OpenSCManager(
NULL, // machine (NULL == local)
NULL, // database (NULL == default)
SC_MANAGER_ALL_ACCESS // access required
);
if (!theSCManager)
{
printf("Failed to install Service\n");
return;
}
theService = CreateService(
theSCManager, // SCManager database
inServiceName, // name of service
inServiceName, // name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type
SERVICE_AUTO_START, // start type
SERVICE_ERROR_NORMAL, // error control type
theQuotedPath, // service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // dependencies
NULL, // LocalSystem account
NULL); // no password
if (theService)
{
SERVICE_DESCRIPTION desc;
desc.lpDescription = "流媒体";
if (ChangeServiceConfig2(theService, SERVICE_CONFIG_DESCRIPTION, &desc))
{
printf("Added MediaServer Service Description\n");
}
SERVICE_DELAYED_AUTO_START_INFO info = { true };
if (ChangeServiceConfig2(theService, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &info))
{
printf("Added MediaServer Service Delayed Auto Start\n");
}
SERVICE_FAILURE_ACTIONS failact = { 0 };
SC_ACTION act[3];
act[0].Delay = act[1].Delay = act[2].Delay = 1 * 1000;
act[0].Type = act[1].Type = act[2].Type = SC_ACTION_RESTART;
failact.cActions = 3;
failact.lpsaActions = act;
failact.dwResetPeriod = 0;
if (ChangeServiceConfig2(theService, SERVICE_CONFIG_FAILURE_ACTIONS, &failact))
{
printf("Seted MediaServer Service failure actions\n");
}
::CloseServiceHandle(theService);
printf("Installed MediaServer Service\n");
}
else
printf("Failed to install MediaServer Service\n");
::CloseServiceHandle(theSCManager);
}
void RemoveService(char *inServiceName)
{
SC_HANDLE theSCManager;
SC_HANDLE theService;
SERVICE_STATUS sStatus;
theSCManager = ::OpenSCManager(
NULL, // machine (NULL == local)
NULL, // database (NULL == default)
SC_MANAGER_ALL_ACCESS // access required
);
if (!theSCManager)
{
printf("Failed to remove MediaServer Service\n");
return;
}
theService = ::OpenService(theSCManager, inServiceName, SERVICE_ALL_ACCESS);
if (theService) {
if (::ControlService(theService, SERVICE_CONTROL_STOP, &sStatus)) {
printf("Stopping Service: %s", inServiceName);
Sleep(1000);
while (::QueryServiceStatus(theService, &sStatus)) {
if (SERVICE_STOP_PENDING == sStatus.dwCurrentState) {
printf(".");
Sleep(1000);
}
else
break;
}
if (SERVICE_STOPPED == sStatus.dwCurrentState)
printf("%s stopped.\n", inServiceName);
else
printf("%s failed to stopp.\n", inServiceName);
}
if (::DeleteService(theService))
printf("Removed MediaServer Service!\n");
else
printf("Remove MediaServer Service failed!\n");
::CloseServiceHandle(theService);
}
else
printf("Failed to remove iDAS Service\n");
::CloseServiceHandle(theSCManager);
}
MediaServer_ServerState StartServer(CMD_main& cmd_main) {
MediaServer_ServerState theServerState = MediaServerStartingUpState;
LogLevel logLevel = (LogLevel)cmd_main["level"].as<int>();
logLevel = MIN(MAX(logLevel, LTrace), LError);
string ssl_file = cmd_main["ssl"];
int threads = cmd_main["threads"];
//设置日志
Logger::Instance().add(std::make_shared<ConsoleChannel>("ConsoleChannel", logLevel));
#ifndef ANDROID
auto fileChannel = std::make_shared<FileChannel>("FileChannel", exeDir() + "log/", logLevel);
//日志最多保存天数
fileChannel->setMaxDay(cmd_main["max_day"]);
Logger::Instance().add(fileChannel);
#endif//
//启动异步日志线程
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
//加载配置文件,如果配置文件不存在就创建一个
loadIniConfig(g_ini_file.data());
if (!File::is_dir(ssl_file.data())) {
//不是文件夹,加载证书,证书包含公钥和私钥
SSL_Initor::Instance().loadCertificate(ssl_file.data());
}
else {
//加载文件夹下的所有证书
File::scanDir(ssl_file, [](const string &path, bool isDir) {
if (!isDir) {
//最后的一个证书会当做默认证书(客户端ssl握手时未指定主机)
SSL_Initor::Instance().loadCertificate(path.data());
}
return true;
});
}
uint16_t shellPort = mINI::Instance()[Shell::kPort];
uint16_t rtspPort = mINI::Instance()[Rtsp::kPort];
uint16_t rtspsPort = mINI::Instance()[Rtsp::kSSLPort];
uint16_t rtmpPort = mINI::Instance()[Rtmp::kPort];
uint16_t httpPort = mINI::Instance()[Http::kPort];
uint16_t httpsPort = mINI::Instance()[Http::kSSLPort];
//设置poller线程数,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效
EventPollerPool::setPoolSize(threads);
//简单的telnet服务器可用于服务器调试但是不能使用23端口否则telnet上了莫名其妙的现象
//测试方法:telnet 127.0.0.1 9000
2019-11-19 15:11:05 +08:00
shellSrv = new TcpServer();
rtspSrv = new TcpServer();
rtmpSrv = new TcpServer();
httpSrv = new TcpServer();
2019-11-12 12:33:16 +08:00
shellSrv->start<ShellSession>(shellPort);
rtspSrv->start<RtspSession>(rtspPort);//默认10554
rtmpSrv->start<RtmpSession>(rtmpPort);//默认10935
//http服务器
httpSrv->start<HttpSession>(httpPort);//默认80
//如果支持ssl还可以开启https服务器
2019-11-19 15:11:05 +08:00
httpsSrv = new TcpServer();
2019-11-12 12:33:16 +08:00
//https服务器,支持websocket
httpsSrv->start<HttpsSession>(httpsPort);//默认443
//支持ssl加密的rtsp服务器可用于诸如亚马逊echo show这样的设备访问
2019-11-19 15:11:05 +08:00
rtspSSLSrv = new TcpServer();
2019-11-12 12:33:16 +08:00
rtspSSLSrv->start<RtspSessionWithSSL>(rtspsPort);//默认322
installWebApi();
InfoL << "已启动http api 接口";
installWebHook();
InfoL << "已启动http hook 接口";
theServerState = MediaServerRunningState;
return theServerState;
}
void RunServer() {
#ifdef WIN32
CMiniDumper _miniDumper(true);
#endif
while ((sServerState != MediaServerShuttingDownState) &&
(sServerState != MediaServerFatalErrorState)) {
#ifdef WIN32
Sleep(1000);
#else
usleep(1000 * 1000);
#endif
}
unInstallWebApi();
unInstallWebHook();
2019-11-19 15:11:05 +08:00
if (shellSrv) delete shellSrv; shellSrv = 0;
if (rtspSrv) delete rtspSrv; rtspSrv = 0;
if (rtmpSrv) delete rtmpSrv; rtmpSrv = 0;
if (httpSrv) delete httpSrv; httpSrv = 0;
if (httpsSrv) delete httpsSrv; httpsSrv = 0;
if (rtspSSLSrv) delete rtspSSLSrv; rtspSSLSrv = 0;
2019-11-12 12:33:16 +08:00
//休眠1秒再退出防止资源释放顺序错误
InfoL << "程序退出中,请等待...";
sleep(1);
InfoL << "程序退出完毕!";
}