一、实现基础
????????在win下,在#include <windows.h>提供了服务控制管理器相关操作函数,如OpenSCManager、CreateService、OpenService、DeleteService、CloseServiceHandle,可以实现服务的创建、打开、关闭、删除等操作。
??????? 在linux下,系统启用后台应用服务是按照先后次序去扫描特定文件目录去加载这些服务配置文件来启动服务,对于服务的管理本质上就是对于管道(文件句柄)的管理,因此借用popen、pclose等实现对进程的启停操作。
二、源码示例
文件目录结构:
svr_test
bin
install_linux.sh
uinstall_linux.sh
build_linux
build_win
include
appexitio.h
svc.h
src
main.cpp
svc
appexitio.cpp
linuxSVC.cpp
WinSVC.cpp
CMakeLists.txt
文件源码:
appexitio.h
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#ifndef _APP_EXIT_IO_H_
#define _APP_EXIT_IO_H_
/***********************************************************************
*Copyright 2020-03-06, pyfree
*
*File Name : appexitio.h
*File Mark :
*Summary :
*程序退出信号处理接口
*
*Current Version : 1.00
*Author : pyfree
*FinishDate :
*
*Replace Version :
*Author :
*FinishDate :
************************************************************************/
#ifdef WIN32
#include <windows.h>
#else
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#endif
#ifdef WIN32
bool ctrlhandler(unsigned long fdwctrltype);
#else
class SignalHandler {
public:
SignalHandler();
void printf_out();
public:
// 程序退出时的函数操作
static void handle_signal(int n, siginfo_t *siginfo, void *myact);
};
#endif // WIN32
#endif
svc.h
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
/***********************************************************************
*Copyright 2020-04-21, pyfree
*
*File Name : svc.h
*File Mark :
*Summary : 服务管理函数集
*
*Current Version : 1.00
*Author : pyfree
*FinishDate :
*
*Replace Version :
*Author :
*FinishDate :
************************************************************************/
#ifndef _SVC_H_
#define _SVC_H_
/**
* 启动服务
* @param svr {char*} 服务名
* @retuan {void}
*/
void SvcStart(char *svr);
/**
* 停止服务
* @param svr {char*} 服务名
* @retuan {void}
*/
void SvcStop(char *svr);
/**
* 查询服务
* @param svr {char*} 服务名
* @param svc_state {int&} 服务状态
* @retuan {void}
*/
void SvcQuery(char* svr, int &svc_state);
/**
* 重启服务
* @param svr {char*} 服务名
* @retuan {void}
*/
void SvcRestart(char *svr);
#endif //_SVC_H_
appexitio.cpp
#include "appexitio.h"
#ifdef WIN32
#include <windows.h>
#else
#include <stdlib.h>
#endif
#include <string>
namespace GlobalVar {
extern bool exitFlag;
};
#ifdef WIN32
bool ctrlhandler(unsigned long fdwctrltype)
{
bool exitEvent = false;
switch (fdwctrltype)
{
// handle the ctrl-c signal.
case CTRL_C_EVENT:
printf("ctrl-c event\n\n");
exitEvent = true;
break;
// ctrl-close: confirm that the user wants to exit.
case CTRL_CLOSE_EVENT:
printf("ctrl-close event\n\n");
exitEvent = true;
break;
// pass other signals to the next handler.
case CTRL_BREAK_EVENT:
printf("ctrl-break event\n\n");
break;
case CTRL_LOGOFF_EVENT:
printf("ctrl-logoff event\n\n");
break;
case CTRL_SHUTDOWN_EVENT:
printf("ctrl-shutdown event\n\n");
exitEvent = true;
//清理
break;
default:
break;
}
if (exitEvent)
{
Sleep(10);
}
GlobalVar::exitFlag = exitEvent;
return exitEvent;
}
#else
SignalHandler::SignalHandler()
{
/** install signal use sigaction **/
struct sigaction act;
sigemptyset(&act.sa_mask); /** 清空阻塞信号 **/
act.sa_flags = SA_SIGINFO; /** 设置SA_SIGINFO 表示传递附加信息到触发函数 **/
act.sa_sigaction = handle_signal;
if (sigaction(SIGHUP, &act, NULL) < 0 // 1
|| sigaction(SIGINT, &act, NULL) < 0 // 2
|| sigaction(SIGQUIT, &act, NULL) < 0 // 3
//|| sigaction(SIGKILL,&act,NULL) < 0 // 9
|| sigaction(SIGTERM, &act, NULL) < 0 // 15
)
{
printf("install signal handler error");
}
};
void SignalHandler::printf_out()
{
}
void SignalHandler::handle_signal(int n, siginfo_t *siginfo, void *myact)
{
printf("SIGNAL received: signo=%d errno=%d code=%d "
, siginfo->si_signo, siginfo->si_errno, siginfo->si_code);
if (siginfo->si_signo == 1
|| siginfo->si_signo == 2
|| siginfo->si_signo == 3
|| siginfo->si_signo == 9
|| siginfo->si_signo == 15)
{
//程序退出,进行退出处理操作
//G_VALUE::watchDogRunning = false;
usleep(10000);
exit(0);
}
};
#endif // WIN32
linuxSVC.cpp ?
#ifdef __linux__
#include <string.h>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include "svc.h"
/**
* 启动服务
* @param svr {char*} 服务名
* @retuan {void}
*/
void SvcStart(char *svr)
{
FILE* fp = NULL;
char command[128] = { 0 };
sprintf(command, "systemctl start %s", svr);
if ((fp = popen(command, "r")) == NULL)
{
return;
}
char buf[512] = { 0 };
if ((fgets(buf, 512, fp)) == NULL)
{
pclose(fp);
return;
}
printf("start ret:%s\n",buf);
pclose(fp);
};
/**
* 停止服务
* @param svr {char*} 服务名
* @retuan {void}
*/
void SvcStop(char *svr)
{
FILE* fp = NULL;
char command[128] = { 0 };
sprintf(command, "systemctl stop %s", svr);
if ((fp = popen(command, "r")) == NULL)
{
return;
}
char buf[512] = { 0 };
if ((fgets(buf, 512, fp)) == NULL)
{
pclose(fp);
return;
}
printf("stop ret:%s\n",buf);
pclose(fp);
};
/**
* 查询服务
* @param svr {char*} 服务名
* @param svc_state {int&} 服务状态
* @retuan {void}
*/
void SvcQuery(char* svr, int &svc_state)
{
svc_state = 0;
FILE* fp = NULL;
char command[128] = { 0 };
sprintf(command, "systemctl status %s | grep Active", svr);
if ((fp = popen(command, "r")) == NULL)
{
return;
}
char buf[512] = { 0 };
if ((fgets(buf, 512, fp)) == NULL)
{
pclose(fp);
return;
}
std::string comment = std::string(buf,strlen(buf));
//std::string::size_type _pos = comment.find("running");
//开机启动的状态,static不可被管理,disable未启动,enable启动
//dead关闭,exited已读取完系统配置,闲置 waiting等待, running正在进行, mounted挂载, plugged加载插件, failed系统配置错误
if(std::string::npos != comment.find("running"))
{
svc_state = 2;
}else if(std::string::npos != comment.find("listening")){
svc_state = 2;
}else{
svc_state = 1;
}
// printf("ret:%s,state:%d\n",buf,svc_state);
pclose(fp);
};
/**
* 重启服务
* @param svr {char*} 服务名
* @retuan {void}
*/
void SvcRestart(char *svr)
{
SvcStop(svr);
SvcStart(svr);
}
#endif
WinSVC.cpp
#ifdef WIN32
#pragma warning(disable: 4995) //for swprintf
#include <windows.h>
#include <tchar.h>
#include <strsafe.h>
#include "atlcomtime.h"
#pragma comment(lib, "advapi32.lib")
#include "svc.h"
int MyMain(int argc, char * argv[]); //Actual Main function, should be defined
extern char SVCNAME[128]; //SVC Name, should be defined
extern char SVCDESC[256]; //SVC Desc, should be defined
namespace WINSVC
{
SERVICE_STATUS gSvcStatus;
SERVICE_STATUS_HANDLE gSvcStatusHandle;
HANDLE ghSvcStopEvent = NULL;
/**
* 本服务安装
* @param {void}
* @retuan {void}
*/
VOID SvcInstall(void);
/**
* 本服务卸载
* @param {void}
* @retuan {void}
*/
VOID DoDeleteSvc(void);
/**
* 查询服务
* @param svr {char*} 服务名
* @param svc_state {int&} 服务状态
* @retuan {void}
*/
VOID SvcQuery(char* svr, int &svc_state);
/**
* 启动服务
* @param svr {char*} 服务名
* @retuan {void}
*/
VOID SvcStart(char *svr);
/**
* 停止服务
* @param svr {char*} 服务名
* @retuan {void}
*/
VOID SvcStop(char *svr);
/**
* 重启服务
* @param svr {char*} 服务名
* @retuan {void}
*/
VOID SvcRestart(char *svr);
VOID WINAPI SvcCtrlHandler( DWORD );
VOID WINAPI SvcMain( DWORD, LPTSTR * );
VOID ReportSvcStatus( DWORD, DWORD, DWORD );
/**
* 本服务初始化
* @param svr {char*} 服务名
* @retuan {void}
*/
VOID SvcInit( DWORD, LPTSTR * );
VOID SvcReportEvent( LPTSTR );
VOID CALLBACK MainThread(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime);
HANDLE m_HThread;
DWORD m_dwThreadID;
BOOL g_bRun = FALSE;
}
void __cdecl _tmain(int argc, TCHAR *argv[])
{
if( lstrcmpi( argv[1], TEXT("install")) == 0 )
{
WINSVC::SvcInstall();
return;
}
else if( lstrcmpi( argv[1], TEXT("uninstall")) == 0 )
{
WINSVC::DoDeleteSvc();
return;
}
SERVICE_TABLE_ENTRY DispatchTable[] =
{
{ SVCNAME, (LPSERVICE_MAIN_FUNCTION) WINSVC::SvcMain },
{ NULL, NULL }
};
WINSVC::g_bRun = TRUE;
if (!StartServiceCtrlDispatcher( DispatchTable ))
{
WINSVC::SvcReportEvent(TEXT("StartServiceCtrlDispatcher"));
MyMain(argc, argv);
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
DispatchMessage(&msg);
}
};
void SvcQuery(char* svr, int &svc_state)
{
WINSVC::SvcQuery(svr, svc_state);
};
void SvcStart(char *svr)
{
WINSVC::SvcStart(svr);
};
void SvcStop(char *svr)
{
WINSVC::SvcStop(svr);
};
void SvcRestart(char *svr)
{
WINSVC::SvcRestart(svr);
};
VOID WINSVC::SvcInstall()
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
TCHAR szPath[MAX_PATH];
if( !GetModuleFileName( NULL, szPath, MAX_PATH ) )
{
printf("Install service fail (%d), %s %s %d!"
, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
return;
}
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager)
{
printf("Failed to open service manager (%d), %s %s %d!"
, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
return;
}
// Create the service
schService = CreateService(
schSCManager, // SCM database
SVCNAME, // name of service
SVCNAME, // service 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
szPath, // path to service's binary
NULL, // no load ordering group
NULL, // no tag identifier
NULL, // no dependencies
NULL, // LocalSystem account
NULL); // no password
if (schService == NULL)
{
printf("Failed to create service (%d), %s %s %d!"
, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
CloseServiceHandle(schSCManager);
return;
}
else{
printf("%s\n",SVCDESC);
}
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
}
VOID WINSVC::DoDeleteSvc()
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
//SERVICE_STATUS ssStatus;
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager)
{
printf("Failed to open service manager (%d), %s %s %d!"
, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
return;
}
// Get a handle to the service.
schService = OpenService(
schSCManager, // SCM database
SVCNAME, // name of service
DELETE); // need delete access
if (schService == NULL)
{
printf("Failed to get service (%d), %s %s %d!"
, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
CloseServiceHandle(schSCManager);
return;
}
// Delete the service.
if (! DeleteService(schService) )
{
printf("Failed to delete service (%d), %s %s %d!"
, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
}
else{
printf("Service successfully removed !\n");//log输出不及时造成无显示
printf("Service successfully removed !");
}
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
}
VOID WINSVC::SvcQuery(char *svr, int &svc_state)
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager)
{
printf("Failed to open service manager (%d), %s %s %d!"
, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
return;
}
// Get a handle to the service.
schService = OpenService(
schSCManager, // SCM database
svr, // name of service
SERVICE_QUERY_STATUS); // need query access
if (schService == NULL)
{
printf("Failed to get service(%s) and error(%d), %s %s %d!"
, svr, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
CloseServiceHandle(schSCManager);
return;
}
//调用QueryServiceStatus函数
SERVICE_STATUS sStatus = { 0 };
if (!QueryServiceStatus(schService, &sStatus))
{
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return;
}
switch (sStatus.dwCurrentState)
{
case SERVICE_STOP_PENDING: case SERVICE_STOPPED:
svc_state = 1;
break;
case SERVICE_START_PENDING: case SERVICE_RUNNING: case SERVICE_CONTINUE_PENDING:
svc_state = 2;
break;
case SERVICE_PAUSE_PENDING: case SERVICE_PAUSED:
svc_state = 3;
break;
default:
svc_state = 0;
break;
}
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
};
VOID WINSVC::SvcStart(char *svr)
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager)
{
printf("Failed to open service manager (%d), %s %s %d!"
, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
return;
}
// Get a handle to the service.
schService = OpenService(
schSCManager, // SCM database
svr, // name of service
SERVICE_START | SERVICE_QUERY_STATUS); // need start and query access
if (schService == NULL)
{
printf("Failed to get service(%s) and error(%d), %s %s %d!"
, svr,GetLastError(), __FILE__, __FUNCTION__, __LINE__);
CloseServiceHandle(schSCManager);
return;
}
StartService(schService, 0, NULL);//开始Service
//调用QueryServiceStatus函数
SERVICE_STATUS sStatus = { 0 };
if (!QueryServiceStatus(schService, &sStatus))
{
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return;
}
if (SERVICE_RUNNING == sStatus.dwCurrentState || SERVICE_START_PENDING == sStatus.dwCurrentState)
{
printf("start service(%s) success, %s %s %d!"
, svr, __FILE__, __FUNCTION__, __LINE__);
}
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
};
VOID WINSVC::SvcStop(char *svr)
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager)
{
printf("Failed to open service manager(%d), %s %s %d!"
, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
return;
}
// Get a handle to the service.
schService = OpenService(
schSCManager, // SCM database
svr, // name of service
SERVICE_STOP | SERVICE_QUERY_STATUS); // need stop or query access
if (schService == NULL)
{
printf("Failed to get service(%s) and error(%d), %s %s %d!"
, svr,GetLastError(), __FILE__, __FUNCTION__, __LINE__);
CloseServiceHandle(schSCManager);
return;
}
//调用QueryServiceStatus函数
SERVICE_STATUS sStatus = { 0 };
if (!QueryServiceStatus(schService, &sStatus))
{
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return;
}
if (SERVICE_RUNNING == sStatus.dwCurrentState || SERVICE_PAUSED == sStatus.dwCurrentState)
{
ControlService(schService, SERVICE_CONTROL_STOP, &sStatus);
}
if (!QueryServiceStatus(schService, &sStatus))
{
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return;
}
if (SERVICE_STOPPED == sStatus.dwCurrentState || SERVICE_STOP_PENDING == sStatus.dwCurrentState)
{
printf("stop service(%s) success, %s %s %d!"
, svr, __FILE__, __FUNCTION__, __LINE__);
}
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
};
VOID WINSVC::SvcRestart(char *svr)
{
SC_HANDLE schSCManager;
SC_HANDLE schService;
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
NULL, // ServicesActive database
SC_MANAGER_ALL_ACCESS); // full access rights
if (NULL == schSCManager)
{
printf("Failed to open service manager and error(%d), %s %s %d!"
, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
return;
}
// Get a handle to the service.
schService = OpenService(
schSCManager, // SCM database
svr, // name of service
SERVICE_START | SERVICE_STOP | SERVICE_QUERY_STATUS); // need pause and query access
if (schService == NULL)
{
printf("Failed to get service(%s) and error(%d), %s %s %d!"
, svr,GetLastError(), __FILE__, __FUNCTION__, __LINE__);
CloseServiceHandle(schSCManager);
return;
}
//调用QueryServiceStatus函数
SERVICE_STATUS sStatus = { 0 };
if (!QueryServiceStatus(schService, &sStatus))
{
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return;
}
if (SERVICE_RUNNING == sStatus.dwCurrentState)
{
ControlService(schService, SERVICE_CONTROL_STOP, &sStatus);
}
if (!QueryServiceStatus(schService, &sStatus))
{
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return;
}
if (SERVICE_STOPPED == sStatus.dwCurrentState || SERVICE_STOP_PENDING == sStatus.dwCurrentState)
{
printf("stop service (%s) success, %s %s %d!"
, svr, __FILE__, __FUNCTION__, __LINE__);
StartService(schService, 0, NULL);//开始Service
}
if (!QueryServiceStatus(schService, &sStatus))
{
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
return;
}
if (SERVICE_RUNNING == sStatus.dwCurrentState || SERVICE_START_PENDING == sStatus.dwCurrentState)
{
printf("restart service (%s) success, %s %s %d!"
, svr, __FILE__, __FUNCTION__, __LINE__);
}
CloseServiceHandle(schService);
CloseServiceHandle(schSCManager);
};
//
VOID WINAPI WINSVC::SvcMain( DWORD dwArgc, LPTSTR *lpszArgv )
{
// Register the handler function for the service
gSvcStatusHandle = RegisterServiceCtrlHandler(
SVCNAME,
SvcCtrlHandler);
if( !gSvcStatusHandle )
{
SvcReportEvent(TEXT("RegisterServiceCtrlHandler"));
return;
}
// These SERVICE_STATUS members remain as set here
gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
gSvcStatus.dwServiceSpecificExitCode = 0;
// Report initial status to the SCM
ReportSvcStatus( SERVICE_START_PENDING, NO_ERROR, 3000 );
// Perform service-specific initialization and work.
SvcInit( dwArgc, lpszArgv );
}
//
VOID WINSVC::SvcInit( DWORD dwArgc, LPTSTR *lpszArgv)
{
// TO_DO: Declare and set any required variables.
// Be sure to periodically call ReportSvcStatus() with
// SERVICE_START_PENDING. If initialization fails, call
// ReportSvcStatus with SERVICE_STOPPED.
// Create an event. The control handler function, SvcCtrlHandler,
// signals this event when it receives the stop control code.
TCHAR szPath[MAX_PATH];
GetModuleFileName( NULL, szPath, MAX_PATH );
TCHAR drive[MAX_PATH],dir[MAX_PATH],fname[MAX_PATH],ext[MAX_PATH];
_tsplitpath_s( szPath,drive,dir,fname,ext );
strcpy_s( szPath, drive );
strcat_s( szPath, dir );
SetCurrentDirectory( szPath );
ghSvcStopEvent = CreateEvent(
NULL, // default security attributes
TRUE, // manual reset event
FALSE, // not signaled
NULL); // no name
if ( ghSvcStopEvent == NULL)
{
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
return;
}
m_HThread = CreateThread( (LPSECURITY_ATTRIBUTES)NULL, 0, (LPTHREAD_START_ROUTINE)MainThread, 0, 0, &m_dwThreadID);
if( m_HThread != NULL )
{
g_bRun = TRUE;
}
// Report running status when initialization is complete.
ReportSvcStatus( SERVICE_RUNNING, NO_ERROR, 0 );
bool bWriteFlag = false;
while(1)
{
// Check whether to stop the service.
WaitForSingleObject(ghSvcStopEvent, INFINITE);
if(!bWriteFlag)
{
printf("Service normal stop: %s %s %d"
, __FILE__, __FUNCTION__, __LINE__);
bWriteFlag = true;
}
g_bRun = FALSE;
ReportSvcStatus( SERVICE_STOPPED, NO_ERROR, 0 );
}
}
//
VOID WINSVC::ReportSvcStatus( DWORD dwCurrentState,
DWORD dwWin32ExitCode,
DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;
// Fill in the SERVICE_STATUS structure.
gSvcStatus.dwCurrentState = dwCurrentState;
gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
gSvcStatus.dwWaitHint = dwWaitHint;
if (dwCurrentState == SERVICE_START_PENDING)
gSvcStatus.dwControlsAccepted = 0;
else gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
if ( (dwCurrentState == SERVICE_RUNNING) ||
(dwCurrentState == SERVICE_STOPPED) )
gSvcStatus.dwCheckPoint = 0;
else gSvcStatus.dwCheckPoint = dwCheckPoint++;
// Report the status of the service to the SCM.
SetServiceStatus( gSvcStatusHandle, &gSvcStatus );
}
//
VOID WINAPI WINSVC::SvcCtrlHandler( DWORD dwCtrl )
{
// Handle the requested control code.
switch(dwCtrl)
{
case SERVICE_CONTROL_STOP:
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
// Signal the service to stop.
SetEvent(ghSvcStopEvent);
ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
return;
case SERVICE_CONTROL_INTERROGATE:
break;
default:
break;
}
}
//
VOID WINSVC::SvcReportEvent(LPTSTR szFunction)
{
HANDLE hEventSource;
LPCTSTR lpszStrings[2];
TCHAR Buffer[80];
hEventSource = RegisterEventSource(NULL, SVCNAME);
if( NULL != hEventSource )
{
StringCchPrintf(Buffer, 80, TEXT("%s failed with %d, %s %s %d"), szFunction
, GetLastError(), __FILE__, __FUNCTION__, __LINE__);
lpszStrings[0] = SVCNAME;
lpszStrings[1] = Buffer;
ReportEvent(hEventSource, // event log handle
EVENTLOG_ERROR_TYPE, // event type
0, // event category
0, // event identifier
NULL, // no security identifier
2, // size of lpszStrings array
0, // no binary data
lpszStrings, // array of strings
NULL); // no binary data
DeregisterEventSource(hEventSource);
}
}
VOID CALLBACK WINSVC::MainThread(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime)
{
int argc = 0;
char * pArgv = NULL;
MyMain(argc, &pArgv);
printf("Main thread quit successfully");
}
#endif
?main.cpp
//
#include <stdio.h>
#include <stdlib.h>
#include <string>
#ifdef __linux__
#include <memory>
#endif // __linux__
#include "appexitio.h"
namespace GlobalVar {
bool exitFlag = false;
};
#ifdef WIN32
#include <windows.h>
//server conf
char SVCNAME[128] = "pyfreeMgr";
char SVCDESC[256] =
"\r\n pyfree Technology Ltd \r\n "
"https://www.pyfree.com \r\n "
"email:pyxxx@163.com \r\n "
"pyfree-xxx system service \r\n "
"Service installation success";
#endif // WIN32
//linux服务名依赖于安装脚本配置,详情参考demo下的脚本
#ifdef WIN32
int MyMain(int argc, char* argv[])
#else
int main(int argc, char *argv[])
#endif
{
#ifdef WIN32
if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)ctrlhandler, true))
{
printf("install signal handler error!");
}
#else
SignalHandler * g_exit_handler = NULL;
g_exit_handler = new SignalHandler();
if(!g_exit_handler){
printf("install signal handler error!");
}
#endif // WIN32
//这里做真正业务处理
//...
//
while (!GlobalVar::exitFlag)
{
sleep(10);
}
//
return 0;
}
三、编译及测试
本文编译采用win:cmake+Visual Studio,Linux:cmake+gcc
CMakeLists.txt
# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (server_test)
#
if(WIN32)
message(STATUS "windows compiling...")
add_definitions(-D_PLATFORM_IS_WINDOWS_)
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
set(WIN_OS true)
else(WIN32)
message(STATUS "linux compiling...")
add_definitions( -D_PLATFORM_IS_LINUX_)
add_definitions("-Wno-invalid-source-encoding")
# add_definitions("-O2")
set(UNIX_OS true)
set(_DEBUG true)
endif(WIN32)
#
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
# 指定源文件的目录,并将名称保存到变量
SET(source_h
#
${PROJECT_SOURCE_DIR}/include/appexitio.h
${PROJECT_SOURCE_DIR}/include/svc.h
)
SET(source_cpp
#
${PROJECT_SOURCE_DIR}/svc/appexitio.cpp
${PROJECT_SOURCE_DIR}/src/main.cpp
)
#头文件目录
include_directories(${PROJECT_SOURCE_DIR}/include)
if (${UNIX_OS})
#execute_process(COMMAND ${PROJECT_SOURCE_DIR}/build.sh)
SET(source_cpp_linux
${PROJECT_SOURCE_DIR}/svc/linuxSVC.cpp
)
add_definitions(
"-W"
"-fPIC"
"-Wall"
# "-Wall -g"
"-Werror"
"-Wshadow"
"-Wformat"
"-Wpointer-arith"
"-D_REENTRANT"
"-D_USE_FAST_MACRO"
"-Wno-long-long"
"-Wuninitialized"
"-D_POSIX_PTHREAD_SEMANTICS"
"-DACL_PREPARE_COMPILE"
"-Wno-unused-parameter"
"-fexceptions"
)
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")
link_directories()
# 指定生成目标
add_executable(server_test ${source_h} ${source_cpp} ${source_cpp_linux})
#link
target_link_libraries(server_test
-lpthread -pthread -lz -lrt -ldl
)
endif(${UNIX_OS})
if (${WIN_OS})
SET(source_cpp_win
${PROJECT_SOURCE_DIR}/svc/WinSVC.cpp
)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819")
add_definitions(
"-D_CRT_SECURE_NO_WARNINGS"
"-D_WINSOCK_DEPRECATED_NO_WARNINGS"
"-DNO_WARN_MBCS_MFC_DEPRECATION"
"-DWIN32_LEAN_AND_MEAN"
)
link_directories()
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/bin)
# 指定生成目标
add_executable(server_testd ${source_h} ${source_cpp} ${source_cpp_win})
#target_link_libraries(server_testd *.lib)
else(CMAKE_BUILD_TYPE)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/bin)
# 指定生成目标
add_executable(server_test ${source_h} ${source_cpp} ${source_cpp_win})
#target_link_libraries(server_test *.lib)
endif (CMAKE_BUILD_TYPE)
endif(${WIN_OS})
3.1 win下编译及实现:
【1】cmake编译,进入svr_test\build_win编译:
cmake -G "Visual Studio 10 2010 Win64" -DCMAKE_BUILD_TYPE=Release ..
msbuild server_test.sln /p:Configuration="Release" /p:Platform="x64"
Visual Studio不同版本写法如下:
Visual Studio 15 2017 [arch] = Generates Visual Studio 2017 project files.
Optional [arch] can be "Win64" or "ARM".
Visual Studio 14 2015 [arch] = Generates Visual Studio 2015 project files.
Optional [arch] can be "Win64" or "ARM".
Visual Studio 12 2013 [arch] = Generates Visual Studio 2013 project files.
Optional [arch] can be "Win64" or "ARM".
Visual Studio 11 2012 [arch] = Generates Visual Studio 2012 project files.
Optional [arch] can be "Win64" or "ARM".
Visual Studio 10 2010 [arch] = Generates Visual Studio 2010 project files.
Optional [arch] can be "Win64" or "IA64".
Visual Studio 9 2008 [arch] = Generates Visual Studio 2008 project files.
Optional [arch] can be "Win64" or "IA64".
Visual Studio 8 2005 [arch] = Generates Visual Studio 2005 project files.
注:超出列表版本请自行查阅资料
本文编译release版本详细过程如下:
d:\workForMy\workspace\svr_test\build_win>cmake -G "Visual Studio 10 2010 Win64" -DCMAKE_BUILD_TYPE=Release ..
-- windows compiling...
CMake Warning (dev) in CMakeLists.txt:
A logical block opening on the line
D:/workForMy/workspace/svr_test/CMakeLists.txt:95 (if)
closes on the line
D:/workForMy/workspace/svr_test/CMakeLists.txt:111 (endif)
with mis-matching arguments.
This warning is for project developers. Use -Wno-dev to suppress it.
-- Configuring done
-- Generating done
-- Build files have been written to: D:/workForMy/workspace/svr_test/build_win
d:\workForMy\workspace\svr_test\build_win>msbuild server_test.sln /p:Configuration="Release" /p:Platform="x64"
Microsoft(R) 生成引擎版本 4.8.4084.0
[Microsoft .NET Framework 版本 4.0.30319.42000]
版权所有 (C) Microsoft Corporation。保留所有权利。
在此解决方案中一次生成一个项目。若要启用并行生成,请添加“/m”开关。
生成启动时间为 2022/9/1 13:47:15。
节点 1 上的项目“d:\workForMy\workspace\svr_test\build_win\server_test.sln”(默认目标)。
ValidateSolutionConfiguration:
正在生成解决方案配置“Release|x64”。
ValidateProjects:
在解决方案配置“Release|x64”中未选定生成项目“ALL_BUILD”。
项目“d:\workForMy\workspace\svr_test\build_win\server_test.sln”(1)正在节点 1 上生成“d:\workForMy\workspace\svr_test\build_win\ZERO_CHECK.vcxproj”(2) (默认目标)。
InitializeBuildStatus:
正在创建“x64\Release\ZERO_CHECK\ZERO_CHECK.unsuccessfulbuild”,因为已指定“AlwaysCreate”。
CustomBuild:
所有输出均为最新。
FinalizeBuildStatus:
正在删除文件“x64\Release\ZERO_CHECK\ZERO_CHECK.unsuccessfulbuild”。
正在对“x64\Release\ZERO_CHECK\ZERO_CHECK.lastbuildstate”执行 Touch 任务。
已完成生成项目“d:\workForMy\workspace\svr_test\build_win\ZERO_CHECK.vcxproj”(默认目标)的操作。
项目“d:\workForMy\workspace\svr_test\build_win\server_test.sln”(1)正在节点 1 上生成“d:\workForMy\workspace\svr_test\build_win\server_test.vcxproj.metaproj”(3) (默认目标)。
项目“d:\workForMy\workspace\svr_test\build_win\server_test.vcxproj.metaproj”(3)正在节点 1 上生成“d:\workForMy\workspace\svr_test\build_win\server_test.vcxproj”(4) (默认目标)。
InitializeBuildStatus:
正在创建“server_test.dir\Release\server_test.unsuccessfulbuild”,因为已指定“AlwaysCreate”。
CustomBuild:
所有输出均为最新。
ClCompile:
所有输出均为最新。
Link:
所有输出均为最新。
d:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\bin\x86_amd64\link.exe /ERRORREPORT:QUEUE /OUT:"D:\workForMy\workspace\svr_test\bin\server_test.exe" /INCREMENTAL:NO /NOLOGO /LIBPA
TH:"D:\workForSoftware\Visual Leak Detector\lib\Win64" kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.lib uuid.lib comdlg32.lib advapi32.lib /MANIFEST /Man
ifestFile:"server_test.dir\Release\server_test.exe.intermediate.manifest" /MANIFESTUAC:"level='asInvoker' uiAccess='false'" /PDB:"D:/workForMy/workspace/svr_test/bin/server_test.pdb" /SU
BSYSTEM:CONSOLE /TLBID:1 /DYNAMICBASE /NXCOMPAT /IMPLIB:"D:/workForMy/workspace/svr_test/bin/Release/server_test.lib" /MACHINE:X64 server_test.dir\Release\appexitio.obj
server_test.dir\Release\main.obj
server_test.dir\Release\WinSVC.obj /machine:x64
server_test.vcxproj -> D:\workForMy\workspace\svr_test\bin\server_test.exe
Manifest:
C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\bin\mt.exe /nologo /verbose /outputresource:"D:\workForMy\workspace\svr_test\bin\server_test.exe;#1" /manifest server_test.dir\Release
\server_test.exe.intermediate.manifest
FinalizeBuildStatus:
正在删除文件“server_test.dir\Release\server_test.unsuccessfulbuild”。
正在对“server_test.dir\Release\server_test.lastbuildstate”执行 Touch 任务。
已完成生成项目“d:\workForMy\workspace\svr_test\build_win\server_test.vcxproj”(默认目标)的操作。
已完成生成项目“d:\workForMy\workspace\svr_test\build_win\server_test.vcxproj.metaproj”(默认目标)的操作。
已完成生成项目“d:\workForMy\workspace\svr_test\build_win\server_test.sln”(默认目标)的操作。
已成功生成。
0 个警告
0 个错误
已用时间 00:00:01.07
d:\workForMy\workspace\svr_test\build_win>
win安装编译服务及测试:
进入D:\workForMy\workspace\svr_test\bin
本文安装及卸载服务示例:
D:\workForMy\workspace\svr_test\bin>server_test.exe install
pyfree Technology Ltd
https://www.pyfree.com
email:pyxxx@163.com
pyfree-xxx system service
Service installation success
D:\workForMy\workspace\svr_test\bin>server_test.exe uninstall
Service successfully removed !
Service successfully removed !
D:\workForMy\workspace\svr_test\bin>
?安装成功示例:
?3.2 Linux下编译及实现
【1】本文是在centos7编译的,进入svr_test/build_linux,具体编译过程如下:
cmake ..
make
[***@pyfree build_linux]$ make
Scanning dependencies of target server_test
[ 25%] Building CXX object CMakeFiles/server_test.dir/svc/linuxSVC.cpp.o
[ 50%] Linking CXX executable ../bin/server_test
[100%] Built target server_test
[***@pyfree build_linux]$ cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- linux compiling...
-- Configuring done
-- Generating done
-- Build files have been written to: /mnt/hgfs/workForMy/workspace/svr_test/build_linux
[***@pyfree build_linux]$ make
Scanning dependencies of target server_test
[ 25%] Building CXX object CMakeFiles/server_test.dir/svc/appexitio.cpp.o
[ 50%] Building CXX object CMakeFiles/server_test.dir/src/main.cpp.o
[ 75%] Building CXX object CMakeFiles/server_test.dir/svc/linuxSVC.cpp.o
[100%] Linking CXX executable ../bin/server_test
[100%] Built target server_test
[***@pyfree build_linux]$
【2】服务安全及部署:
前面说过,在Linux下,所谓的内部服务启动就是将进程文件加载到特性文件目录并配置好这些文件,系统在启动过程中会自动加载启动,并linux有自己的服务管理器,就像win下的任务管理器一样,差别在于win提供界面工具,linux采用命令。
我们提供两个脚本实现本进程的服务安装及卸载:
install_linux.sh
#!/bin/bash
#@pyfree 2022-08-26,pyXXX@163.com
#创建程序目录
#run for root
#定义程序全局路径变量
#程序根目录
dir='/usr/local/sysmgr'
curdir=$(pwd)
if [ ! -d $dir ]; then
mkdir -p $dir
fi
#
echo "appdir="$dir
echo "curdir="$curdir
#复制程序文件到程序目录
/bin/cp -f server_test $dir
#
ls $dir
#
if [ ! -f "$dir/sysmgrStart.sh" ];then
#创建程序启动脚本sysmgr
cat > $dir/sysmgrStart.sh << eof
#!/bin/bash
pid=\`ps -ef | grep "server_test*" | grep -v "grep" | wc -l\`
if [ \$pid -eq 0 ];then
cd $dir
echo "The sysmgr server will be start"
nohup ./server_test & > /dev/null
else
echo "The sysmgr server is alreadly running"
fi
eof
fi
if [ ! -f "$dir/sysmgrStop.sh" ];then
#创建程序关闭脚本sysmgr
cat > $dir/sysmgrStop.sh << eof
#!/bin/bash
pid=\`ps -ef | grep "server_test*" | grep -v "grep" | wc -l\`
if [ \$pid -ne 0 ];then
echo "The sysmgr server will be stop"
killall -9 server_test
else
echo "The sysmgr server is stoping, don't stop it"
fi
eof
fi
#给服务启动脚本添加执行权限
chmod o+x $dir/{sysmgrStart.sh,sysmgrStop.sh}
if [ ! -f "/usr/lib/systemd/system/pyfreeMgr.service" ];then
#创建程序启动文件到centos7服务启动路径/usr/lib/systemd/system
cat > /usr/lib/systemd/system/pyfreeMgr.service <<eof
[Unit]
Description=server_test
After=syslog.target network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
ExecStart=$dir/sysmgrStart.sh
ExecReload=
ExecStop=$dir/sysmgrStop.sh
PrivateTmp=true
[Install]
WantedBy=multi-user.target
eof
fi
cd $curdir
#设置开机启动gather服务
systemctl daemon-reload
chmod o-x /usr/lib/systemd/system/pyfreeMgr.service
systemctl enable pyfreeMgr.service
#查看服务是否开机启动:
#systemctl is-enabled pyfreeMgr.service
#设置开机时禁用服务
#systemctl disable pyfreeMgr.service
#启动服务
#systemctl start pyfreeMgr.service
#停止服务
#systemctl stop pyfreeMgr.service
uninstall_linux.sh
#!/bin/bash
#@pyfree 2022-08-26,pyXXX@163.com
#创建程序目录
#run for root
#定义程序全局路径变量
#程序根目录
dir='/usr/local/sysmgr'
systemctl stop pyfreeMgr.service
systemctl disable pyfreeMgr.service
rm -f /usr/lib/systemd/system/pyfreeMgr.service
rm -rf $dir
脚本运行过程如下:
[root@pyfree bin]# ./install_linux.sh
appdir=/usr/local/sysmgr
curdir=/mnt/hgfs/workForMy/workspace/svr_test/bin
server_test sysmgrStart.sh sysmgrStop.sh
[root@pyfree bin]# systemctl is-enabled pyfreeMgr.service
enabled
[root@pyfree bin]# systemctl status pyfreeMgr.service
● pyfreeMgr.service - server_test
Loaded: loaded (/usr/lib/systemd/system/pyfreeMgr.service; enabled; vendor preset: disabled)
Active: inactive (dead)
[root@pyfree bin]# ./uinstall_linux.sh
Removed symlink /etc/systemd/system/multi-user.target.wants/pyfreeMgr.service.
[root@pyfree bin]#
四、服务管理函数使用说明
在svc.h中提供里四个服务管理函数,本服务程序可以通过这些函式实现对其他应有函数的启停、查询状态等管理,前提是自身服务具有相关权限。大家可以在main.cpp的业务应用代码段测试实现对系统内其他程序进行管理。
|