Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

539 lines
16 KiB

  1. // kdMonSvc.cpp : Implementation of WinMain
  2. // Note: Proxy/Stub Information
  3. // To build a separate proxy/stub DLL,
  4. // run nmake -f kdMonSvcps.mk in the project directory.
  5. #include "stdafx.h"
  6. #include "resource.h"
  7. #include <initguid.h>
  8. #include "kdMonSvc.h"
  9. #include "kdMonSvc_i.c"
  10. #include "global.h"
  11. // The name of current service
  12. // This variable is declared in global.cpp
  13. extern _TCHAR szServiceName[MAX_PATH];
  14. // just to get any kind of error through GetError() routine
  15. // This variable is declared in global.cpp
  16. extern _TCHAR szError[MAX_PATH];
  17. CServiceModule _Module;
  18. BEGIN_OBJECT_MAP(ObjectMap)
  19. END_OBJECT_MAP()
  20. // worker thread function
  21. DWORD WINAPI WorkerThread( LPVOID lpParam );
  22. LPCTSTR FindOneOf(LPCTSTR p1, LPCTSTR p2)
  23. {
  24. while (p1 != NULL && *p1 != NULL)
  25. {
  26. LPCTSTR p = p2;
  27. while (p != NULL && *p != NULL)
  28. {
  29. if (*p1 == *p)
  30. return CharNext(p1);
  31. p = CharNext(p);
  32. }
  33. p1 = CharNext(p1);
  34. }
  35. return NULL;
  36. }
  37. // Although some of these functions are big they are declared inline since they are only used once
  38. inline HRESULT CServiceModule::RegisterServer(BOOL bRegTypeLib, BOOL bService)
  39. {
  40. HRESULT hr = CoInitialize(NULL);
  41. if (FAILED(hr))
  42. return hr;
  43. // -- Added code to default --
  44. // Setup event logging
  45. //
  46. LONG lResult;
  47. lResult = SetupEventLog(TRUE);
  48. if (lResult != ERROR_SUCCESS)
  49. return lResult;
  50. // -- Added code to default --
  51. // if the service is already installed then dont do anything.
  52. // RegisterServer(..) tries to Uninstall() a service before trying to
  53. // Register it again. If you have already started a service, created a thread,
  54. // and waiting for that thread to finish and you issue command
  55. // kdMonSvc /service then RegisterServer() tries to call Uninstall().
  56. // And you can not Uninstall() in this state since MainThread is waiting for
  57. // WorkerThread to finish. So just return back from here
  58. if(IsInstalled()){
  59. MessageBox(NULL, _T("Service is already installed.\n Please unregister the service useing: kdMonSvc /unregserver"), NULL, MB_OK|MB_ICONEXCLAMATION);
  60. return ERROR_SUCCESS;
  61. }
  62. // Remove any previous service since it may point to
  63. // the incorrect file
  64. Uninstall();
  65. // Add service entries
  66. UpdateRegistryFromResource(IDR_kdMonSvc, TRUE);
  67. // Adjust the AppID for Local Server or Service
  68. CRegKey keyAppID;
  69. LONG lRes = keyAppID.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_WRITE);
  70. if (lRes != ERROR_SUCCESS)
  71. return lRes;
  72. CRegKey key;
  73. lRes = key.Open(keyAppID, _T("{6961AED3-A5FA-46EE-862F-B50433EEF17E}"), KEY_WRITE);
  74. if (lRes != ERROR_SUCCESS)
  75. return lRes;
  76. key.DeleteValue(_T("LocalService"));
  77. if (bService)
  78. {
  79. key.SetValue(_T("kdMonSvc"), _T("LocalService"));
  80. key.SetValue(_T("-Service"), _T("ServiceParameters"));
  81. // Create service
  82. Install();
  83. }
  84. // Add object entries
  85. hr = CComModule::RegisterServer(bRegTypeLib);
  86. CoUninitialize();
  87. return hr;
  88. }
  89. inline HRESULT CServiceModule::UnregisterServer()
  90. {
  91. HRESULT hr = CoInitialize(NULL);
  92. if (FAILED(hr))
  93. return hr;
  94. //
  95. // Remove eventlog stuff
  96. //
  97. SetupEventLog(FALSE);
  98. // Remove service entries
  99. UpdateRegistryFromResource(IDR_kdMonSvc, FALSE);
  100. // Remove service
  101. Uninstall();
  102. // Remove object entries
  103. CComModule::UnregisterServer(TRUE);
  104. CoUninitialize();
  105. return S_OK;
  106. }
  107. inline void CServiceModule::Init(_ATL_OBJMAP_ENTRY* p, HINSTANCE h, UINT nServiceNameID, const GUID* plibid)
  108. {
  109. CComModule::Init(p, h, plibid);
  110. m_bService = TRUE;
  111. LoadString(h, nServiceNameID, m_szServiceName, sizeof(m_szServiceName) / sizeof(_TCHAR));
  112. // -- Added code to default --
  113. // copy the service name into szServiceName, a global variable
  114. _tcscpy(szServiceName, m_szServiceName);
  115. // set up the initial service status
  116. m_hServiceStatus = NULL;
  117. m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
  118. m_status.dwCurrentState = SERVICE_STOPPED;
  119. m_status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
  120. m_status.dwWin32ExitCode = 0;
  121. m_status.dwServiceSpecificExitCode = 0;
  122. m_status.dwCheckPoint = 0;
  123. m_status.dwWaitHint = 0;
  124. }
  125. LONG CServiceModule::Unlock()
  126. {
  127. LONG l = CComModule::Unlock();
  128. if (l == 0 && !m_bService)
  129. PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
  130. return l;
  131. }
  132. BOOL CServiceModule::IsInstalled()
  133. {
  134. BOOL bResult = FALSE;
  135. SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  136. if (hSCM != NULL)
  137. {
  138. SC_HANDLE hService = ::OpenService(hSCM, m_szServiceName, SERVICE_QUERY_CONFIG);
  139. if (hService != NULL)
  140. {
  141. bResult = TRUE;
  142. ::CloseServiceHandle(hService);
  143. }
  144. ::CloseServiceHandle(hSCM);
  145. }
  146. return bResult;
  147. }
  148. inline BOOL CServiceModule::Install()
  149. {
  150. if (IsInstalled())
  151. return TRUE;
  152. SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  153. if (hSCM == NULL)
  154. {
  155. MessageBox(NULL, _T("Couldn't open service manager"), m_szServiceName, MB_OK);
  156. return FALSE;
  157. }
  158. // Get the executable file path
  159. _TCHAR szFilePath[_MAX_PATH];
  160. ::GetModuleFileName(NULL, szFilePath, _MAX_PATH);
  161. SC_HANDLE hService = ::CreateService(
  162. hSCM, m_szServiceName, m_szServiceName,
  163. SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
  164. SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
  165. szFilePath, NULL, NULL, _T("RPCSS\0"), NULL, NULL);
  166. if (hService == NULL)
  167. {
  168. ::CloseServiceHandle(hSCM);
  169. MessageBox(NULL, _T("Couldn't create service"), m_szServiceName, MB_OK);
  170. return FALSE;
  171. }
  172. ::CloseServiceHandle(hService);
  173. ::CloseServiceHandle(hSCM);
  174. return TRUE;
  175. }
  176. inline BOOL CServiceModule::Uninstall()
  177. {
  178. if (!IsInstalled())
  179. return TRUE;
  180. SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
  181. if (hSCM == NULL)
  182. {
  183. MessageBox(NULL, _T("Couldn't open service manager"), m_szServiceName, MB_OK);
  184. return FALSE;
  185. }
  186. SC_HANDLE hService = ::OpenService(hSCM, m_szServiceName, SERVICE_STOP | DELETE);
  187. if (hService == NULL)
  188. {
  189. ::CloseServiceHandle(hSCM);
  190. MessageBox(NULL, _T("Couldn't open service"), m_szServiceName, MB_OK);
  191. return FALSE;
  192. }
  193. SERVICE_STATUS status;
  194. ::ControlService(hService, SERVICE_CONTROL_STOP, &status);
  195. BOOL bDelete = ::DeleteService(hService);
  196. ::CloseServiceHandle(hService);
  197. ::CloseServiceHandle(hSCM);
  198. if (bDelete)
  199. return TRUE;
  200. MessageBox(NULL, _T("Service could not be deleted"), m_szServiceName, MB_OK);
  201. return FALSE;
  202. }
  203. //////////////////////////////////////////////////////////////////////////////////////////////
  204. // Service startup and registration
  205. inline void CServiceModule::Start()
  206. {
  207. SERVICE_TABLE_ENTRY st[] =
  208. {
  209. { m_szServiceName, _ServiceMain },
  210. { NULL, NULL }
  211. };
  212. if (m_bService && !::StartServiceCtrlDispatcher(st))
  213. {
  214. m_bService = FALSE;
  215. }
  216. if (m_bService == FALSE)
  217. Run();
  218. }
  219. inline void CServiceModule::ServiceMain(DWORD /* dwArgc */, LPTSTR* /* lpszArgv */)
  220. {
  221. // Register the control request handler
  222. m_status.dwCurrentState = SERVICE_START_PENDING;
  223. m_hServiceStatus = RegisterServiceCtrlHandler(m_szServiceName, _Handler);
  224. if (m_hServiceStatus == NULL)
  225. {
  226. LogEvent(_T("Handler not installed"));
  227. return;
  228. }
  229. SetServiceStatus(SERVICE_START_PENDING);
  230. m_status.dwWin32ExitCode = S_OK;
  231. m_status.dwCheckPoint = 0;
  232. m_status.dwWaitHint = 0;
  233. // When the Run function returns, the service has stopped.
  234. Run();
  235. SetServiceStatus(SERVICE_STOPPED);
  236. LogEvent(_T("Service stopped"));
  237. }
  238. inline void CServiceModule::Handler(DWORD dwOpcode)
  239. {
  240. switch (dwOpcode)
  241. {
  242. case SERVICE_CONTROL_STOP:
  243. SetServiceStatus(SERVICE_STOP_PENDING);
  244. // post WM_QUIT message to current thread
  245. // the GetMessage() loop will get this message and terminate the service
  246. PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
  247. break;
  248. case SERVICE_CONTROL_PAUSE:
  249. break;
  250. case SERVICE_CONTROL_CONTINUE:
  251. break;
  252. case SERVICE_CONTROL_INTERROGATE:
  253. break;
  254. case SERVICE_CONTROL_SHUTDOWN:
  255. break;
  256. default:
  257. LogEvent(_T("Bad service request"));
  258. }
  259. }
  260. void WINAPI CServiceModule::_ServiceMain(DWORD dwArgc, LPTSTR* lpszArgv)
  261. {
  262. _Module.ServiceMain(dwArgc, lpszArgv);
  263. }
  264. void WINAPI CServiceModule::_Handler(DWORD dwOpcode)
  265. {
  266. _Module.Handler(dwOpcode);
  267. }
  268. void CServiceModule::SetServiceStatus(DWORD dwState)
  269. {
  270. m_status.dwCurrentState = dwState;
  271. ::SetServiceStatus(m_hServiceStatus, &m_status);
  272. }
  273. void CServiceModule::Run()
  274. {
  275. _Module.dwThreadID = GetCurrentThreadId();
  276. HRESULT hr = CoInitialize(NULL);
  277. // If you are running on NT 4.0 or higher you can use the following call
  278. // instead to make the EXE free threaded.
  279. // This means that calls come in on a random RPC thread
  280. // HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
  281. _ASSERTE(SUCCEEDED(hr));
  282. // This provides a NULL DACL which will allow access to everyone.
  283. CSecurityDescriptor sd;
  284. sd.InitializeFromThreadToken();
  285. hr = CoInitializeSecurity(sd, -1, NULL, NULL,
  286. RPC_C_AUTHN_LEVEL_PKT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
  287. _ASSERTE(SUCCEEDED(hr));
  288. hr = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, REGCLS_MULTIPLEUSE);
  289. _ASSERTE(SUCCEEDED(hr));
  290. AddServiceLog(_T("kdMon service starting\r\n"));
  291. if (m_bService)
  292. SetServiceStatus(SERVICE_RUNNING);
  293. // create a named event which the thread will open and refer to
  294. // this event is used to signal "Stop" to WorkerThread
  295. HANDLE hStopEvent = NULL;
  296. hStopEvent = CreateEvent( NULL, // security attributes
  297. FALSE, // = Automatic reset of event by system
  298. FALSE,
  299. (LPCTSTR)_T(cszStopEvent));
  300. if ( hStopEvent == NULL ) {
  301. GetError(szError);
  302. LogFatalEvent(_T("Run->CreateEvent : %s"), szError);
  303. AddServiceLog(_T("Error: Run->CreateEvent : %s\r\n"), szError);
  304. goto done;
  305. }
  306. // -- Added code to default --
  307. //
  308. // Create worker thread here
  309. //
  310. LogEvent(_T("Creating worker thread"));
  311. AddServiceLog(_T("Creating worker thread\r\n"));
  312. DWORD dwWorkerThreadId;
  313. HANDLE hWorkerThread;
  314. hWorkerThread = CreateThread( NULL, // security descriptor
  315. 0, // initial stack size
  316. WorkerThread, // thread start address
  317. &dwThreadID, // thread arguments (current threadID)
  318. 0, // creation flags = run immediately
  319. &dwWorkerThreadId);
  320. if ( hWorkerThread == NULL ) {
  321. GetError(szError);
  322. LogFatalEvent(_T("Run->CreateThread : %s"), szError);
  323. AddServiceLog(_T("Error: Run->CreateThread : %s\r\n"), szError);
  324. goto done;
  325. }
  326. MSG msg;
  327. BOOL bRetVal;
  328. // GetMessage ():
  329. // If the function retrieves a message other than WM_QUIT, the return value is nonzero.
  330. // If the function retrieves the WM_QUIT message, the return value is zero.
  331. while ( (bRetVal = GetMessage(&msg, NULL, 0, 0)) != 0 ) {
  332. // send it to default dispatcher
  333. DispatchMessage(&msg);
  334. }
  335. AddServiceLog(_T("Main thread received WM_QUIT message\r\n"));
  336. AddServiceLog(_T("Terminating kdMon Service\r\n"));
  337. LogEvent(_T("Terminating kdMon Service"));
  338. // check if worker thread is still active.
  339. // i.e. check hWorkerThread for a signalled state
  340. DWORD dwRetVal;
  341. AddServiceLog(_T("Main thread checking if WorkerThread is still active\r\n"));
  342. dwRetVal = WaitForSingleObject( hWorkerThread, 0 );
  343. if ( dwRetVal == WAIT_FAILED ) {
  344. GetError(szError);
  345. LogFatalEvent(_T("Run->WaitForSingleObject : %s"), szError);
  346. AddServiceLog(_T("Error: Run->WaitForSingleObject : %s\r\n"), szError);
  347. goto done;
  348. }
  349. // if hWorkerThread is not in signalled state then try to signal it
  350. if ( dwRetVal != WAIT_OBJECT_0 ) {
  351. // Signal the Stop Event, so that the worker thread stops
  352. AddServiceLog(_T("Signalling Stop Event to Worker Thread\r\n"));
  353. bRetVal = SetEvent(hStopEvent);
  354. if ( bRetVal == 0 ) {
  355. GetError(szError);
  356. LogFatalEvent(_T("Run->SetEvent : %s"), szError);
  357. AddServiceLog(_T("Error: Run->SetEvent : %s\r\n"), szError);
  358. goto done;
  359. }
  360. // Now we have signalled the thread to end, wait till the thread ends gracefully
  361. // We can use WaitForSingleObject API for this purpose
  362. // CreateThread () : When a thread terminates, the thread object
  363. // attains a signaled state, satisfying any threads that were waiting on the object.
  364. // so we can make this main thread to wait for the WorkerThread to terminate
  365. AddServiceLog(_T("Main thread waiting for WorkerThread to exit\r\n"));
  366. dwRetVal = WaitForSingleObject( hWorkerThread, INFINITE );
  367. if ( dwRetVal == WAIT_FAILED ) {
  368. GetError(szError);
  369. LogFatalEvent(_T("Run->WaitForSingleObject : %s"), szError);
  370. AddServiceLog(_T("Error: Run->WaitForSingleObject : %s\r\n"), szError);
  371. goto done;
  372. }
  373. }
  374. done:
  375. if ( hWorkerThread != NULL ) CloseHandle(hWorkerThread);
  376. if ( hStopEvent != NULL ) CloseHandle(hStopEvent);
  377. _Module.RevokeClassObjects();
  378. CoUninitialize();
  379. }
  380. /////////////////////////////////////////////////////////////////////////////
  381. //
  382. extern "C" int WINAPI _tWinMain(HINSTANCE hInstance,
  383. HINSTANCE /*hPrevInstance*/, LPTSTR lpCmdLine, int /*nShowCmd*/)
  384. {
  385. lpCmdLine = GetCommandLine(); //this line necessary for _ATL_MIN_CRT
  386. _Module.Init(ObjectMap, hInstance, IDS_SERVICENAME, &LIBID_KDMONSVCLib);
  387. _Module.m_bService = TRUE;
  388. AddServiceLog(_T("Command received : %s\r\n"), lpCmdLine);
  389. // tokenize on '-' or '/' characters
  390. _TCHAR szTokens[] = _T("-/");
  391. LPCTSTR lpszToken = FindOneOf(lpCmdLine, szTokens);
  392. while (lpszToken != NULL)
  393. {
  394. if (lstrcmpi(lpszToken, _T("UnregServer"))==0)
  395. return _Module.UnregisterServer();
  396. // Register as Local Server
  397. if (lstrcmpi(lpszToken, _T("RegServer"))==0)
  398. return _Module.RegisterServer(TRUE, FALSE);
  399. // Register as Service
  400. if (lstrcmpi(lpszToken, _T("Service"))==0)
  401. return _Module.RegisterServer(TRUE, TRUE);
  402. lpszToken = FindOneOf(lpszToken, szTokens);
  403. }
  404. // Are we Service or Local Server
  405. CRegKey keyAppID;
  406. LONG lRes = keyAppID.Open(HKEY_CLASSES_ROOT, _T("AppID"), KEY_READ);
  407. if (lRes != ERROR_SUCCESS)
  408. return lRes;
  409. CRegKey key;
  410. lRes = key.Open(keyAppID, _T("{6961AED3-A5FA-46EE-862F-B50433EEF17E}"), KEY_READ);
  411. if (lRes != ERROR_SUCCESS)
  412. return lRes;
  413. _TCHAR szValue[_MAX_PATH];
  414. DWORD dwLen = _MAX_PATH;
  415. lRes = key.QueryValue(szValue, _T("LocalService"), &dwLen);
  416. _Module.m_bService = FALSE;
  417. if (lRes == ERROR_SUCCESS)
  418. _Module.m_bService = TRUE;
  419. _Module.Start();
  420. // When we get here, the service has been stopped
  421. return _Module.m_status.dwWin32ExitCode;
  422. }
  423. ///////////////////////////////////////////////////////////////////////////////////////
  424. // Worker thread. Main thread that does all the kdMon work
  425. DWORD WINAPI WorkerThread(LPVOID lpParam)
  426. {
  427. // get the parent thread ID
  428. // dwParentThreadID is the parent thread ID. This is used to signal
  429. // main thread that worker thread is ending due to some reason
  430. // and then main thread should also end and stop the service
  431. DWORD dwParentThreadID = *(DWORD*) lpParam;
  432. AddServiceLog(_T("Worker thread starting kdMon routine\r\n"));
  433. // main kdMon method which is a while(1) loop.
  434. kdMon();
  435. LogEvent(_T("Worker Thread ending"));
  436. AddServiceLog(_T("Worker Thread ending\r\n"));
  437. // signal the parent thread with WM_QUIT before exiting
  438. PostThreadMessage(dwParentThreadID, WM_QUIT, 0, 0);
  439. return GetLastError();
  440. }