PageRenderTime 22ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/dep/acelite/ace/NT_Service.cpp

https://github.com/chucho/FaceCore
C++ | 618 lines | 450 code | 102 blank | 66 comment | 95 complexity | 34bd72f991cc5fb72180a4c2f6c9507d MD5 | raw file
  1. // $Id: NT_Service.cpp 81862 2008-06-09 10:41:41Z sma $
  2. #include "ace/config-all.h"
  3. #if defined (ACE_WIN32) && !defined (ACE_LACKS_WIN32_SERVICES)
  4. #include "ace/NT_Service.h"
  5. #if !defined (__ACE_INLINE__)
  6. #include "ace/NT_Service.inl"
  7. #endif /* __ACE_INLINE__ */
  8. #include "ace/Log_Msg.h"
  9. #include "ace/Service_Object.h"
  10. #include "ace/OS_NS_errno.h"
  11. ACE_BEGIN_VERSIONED_NAMESPACE_DECL
  12. ACE_ALLOC_HOOK_DEFINE(ACE_NT_Service)
  13. // ACE_NT_Service destructor.
  14. ACE_NT_Service::~ACE_NT_Service (void)
  15. {
  16. if (this->svc_sc_handle_ != 0)
  17. {
  18. CloseServiceHandle (this->svc_sc_handle_);
  19. this->svc_sc_handle_ = 0;
  20. }
  21. delete [] this->desc_;
  22. delete [] this->name_;
  23. delete [] this->host_;
  24. }
  25. // This default implementation of ACE_NT_Service::open sets the
  26. // service's status to START_PENDING with the estimated time until
  27. // STARTED set to the value given when this object was constructed.
  28. // Then the svc function is called, which implements the guts of the
  29. // service. Note that this function is running in a thread created by
  30. // the OS, not by ACE_Thread_Manager. The thread manager does not
  31. // know anything about this thread. The service can, however, use
  32. // ACE_Thread_Manager to start more threads if desired. When the svc
  33. // function returns, the service status is set to STOPPED, and exit
  34. // codes set based on errno/GetLastError if the svc function returns
  35. // -1.
  36. //
  37. // The svc function is expected to set the service status to SERVICE_RUNNING
  38. // after it initializes.
  39. //
  40. // The handle_control function will be called for each time there is a
  41. // request for the service. It is up to that function and svc to
  42. // cooperate to both respond appropriately to the request (by at least
  43. // updating the service's status) and to fulfill the request.
  44. int
  45. ACE_NT_Service::open (void *args)
  46. {
  47. ACE_UNUSED_ARG (args);
  48. this->report_status (SERVICE_START_PENDING, 0);
  49. int svc_return = this->svc ();
  50. if (svc_return == 0)
  51. {
  52. this->svc_status_.dwWin32ExitCode = NO_ERROR;
  53. this->svc_status_.dwServiceSpecificExitCode = 0;
  54. }
  55. else
  56. {
  57. if (errno == 0)
  58. {
  59. this->svc_status_.dwWin32ExitCode = GetLastError ();
  60. }
  61. else
  62. {
  63. this->svc_status_.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
  64. this->svc_status_.dwServiceSpecificExitCode = errno;
  65. }
  66. }
  67. return svc_return;
  68. }
  69. int
  70. ACE_NT_Service::fini (void)
  71. {
  72. return this->report_status (SERVICE_STOPPED, 0);
  73. }
  74. void
  75. ACE_NT_Service::handle_control (DWORD control_code)
  76. {
  77. switch (control_code)
  78. {
  79. case SERVICE_CONTROL_SHUTDOWN:
  80. case SERVICE_CONTROL_STOP:
  81. this->stop_requested (control_code);
  82. break;
  83. case SERVICE_CONTROL_PAUSE:
  84. this->pause_requested (control_code);
  85. break;
  86. case SERVICE_CONTROL_CONTINUE:
  87. this->continue_requested (control_code);
  88. break;
  89. case SERVICE_CONTROL_INTERROGATE:
  90. this->interrogate_requested (control_code);
  91. break;
  92. }
  93. }
  94. void
  95. ACE_NT_Service::stop_requested (DWORD)
  96. {
  97. this->report_status (SERVICE_STOP_PENDING);
  98. /* how to cancel? */
  99. }
  100. void
  101. ACE_NT_Service::pause_requested (DWORD)
  102. {
  103. this->report_status (SERVICE_PAUSE_PENDING);
  104. this->suspend ();
  105. this->report_status (SERVICE_PAUSED);
  106. }
  107. void
  108. ACE_NT_Service::continue_requested (DWORD)
  109. {
  110. this->report_status (SERVICE_CONTINUE_PENDING);
  111. this->resume ();
  112. this->report_status (SERVICE_RUNNING);
  113. }
  114. void
  115. ACE_NT_Service::interrogate_requested (DWORD)
  116. {
  117. this->report_status (0);
  118. }
  119. void
  120. ACE_NT_Service::name (const ACE_TCHAR *name, const ACE_TCHAR *desc)
  121. {
  122. delete [] this->desc_;
  123. delete [] this->name_;
  124. if (desc == 0)
  125. desc = name;
  126. this->name_ = ACE::strnew (name);
  127. this->desc_ = ACE::strnew (desc);
  128. }
  129. void
  130. ACE_NT_Service::host (const ACE_TCHAR *host)
  131. {
  132. delete [] this->host_;
  133. if (this->svc_sc_handle_ != 0)
  134. {
  135. CloseServiceHandle (this->svc_sc_handle_);
  136. this->svc_sc_handle_ = 0;
  137. }
  138. if (host == 0)
  139. {
  140. this->host_ = 0;
  141. }
  142. else
  143. {
  144. this->host_ = ACE::strnew (host);
  145. }
  146. }
  147. int
  148. ACE_NT_Service::insert (DWORD start_type,
  149. DWORD error_control,
  150. const ACE_TCHAR *exe_path,
  151. const ACE_TCHAR *group_name,
  152. LPDWORD tag_id,
  153. const ACE_TCHAR *dependencies,
  154. const ACE_TCHAR *account_name,
  155. const ACE_TCHAR *password,
  156. DWORD desired_access)
  157. {
  158. ACE_TCHAR this_exe[MAXPATHLEN + 2];
  159. // Insure ACE_OS::last_error finds GetLastError unless we set errno.
  160. errno = 0;
  161. if (exe_path == 0)
  162. {
  163. if (ACE_TEXT_GetModuleFileName (0, this_exe + 1, MAXPATHLEN) == 0)
  164. return -1;
  165. // Make sure that this_exe is quoted
  166. this_exe[0] = ACE_TEXT ('\"');
  167. ACE_OS::strcat (this_exe, ACE_TEXT ("\""));
  168. exe_path = this_exe;
  169. }
  170. SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
  171. 0,
  172. SC_MANAGER_ALL_ACCESS);
  173. if (sc_mgr == 0)
  174. return -1;
  175. SC_HANDLE sh = ACE_TEXT_CreateService (sc_mgr,
  176. this->name (),
  177. this->desc (),
  178. desired_access,
  179. this->svc_status_.dwServiceType,
  180. start_type,
  181. error_control,
  182. exe_path,
  183. group_name,
  184. tag_id,
  185. dependencies,
  186. account_name,
  187. password);
  188. // If there was an error, stash GetLastError before CloseServiceHandle
  189. // smashes it. ACE_OS::last_error will find the saved error value.
  190. if (sh == 0)
  191. ACE_OS::set_errno_to_last_error ();
  192. CloseServiceHandle (sc_mgr);
  193. if (sh == 0)
  194. return -1;
  195. if (this->svc_sc_handle_ != 0)
  196. CloseServiceHandle (this->svc_sc_handle_);
  197. this->svc_sc_handle_ = sh;
  198. return 0;
  199. }
  200. int
  201. ACE_NT_Service::remove (void)
  202. {
  203. if (this->svc_sc_handle () == 0)
  204. return -1;
  205. if (DeleteService (this->svc_sc_handle()) == 0
  206. && GetLastError () != ERROR_SERVICE_MARKED_FOR_DELETE)
  207. return -1;
  208. return 0;
  209. }
  210. // Sets the startup type for the service. Returns -1 on error, 0 on
  211. // success.
  212. int
  213. ACE_NT_Service::startup (DWORD startup)
  214. {
  215. SC_HANDLE svc = this->svc_sc_handle ();
  216. if (svc == 0)
  217. return -1;
  218. BOOL ok =
  219. ChangeServiceConfig (svc,
  220. (DWORD) SERVICE_NO_CHANGE,// No change to service type
  221. startup, // New startup type
  222. (DWORD) SERVICE_NO_CHANGE,// No change to error ctrl
  223. 0, // No change to pathname
  224. 0, // No change to load group
  225. 0, // No change to tag
  226. 0, // No change to dependencies
  227. 0, 0, // No change to acct/passwd
  228. 0); // No change to name
  229. return ok ? 0 : -1;
  230. }
  231. // Returns the current startup type.
  232. DWORD
  233. ACE_NT_Service::startup (void)
  234. {
  235. // The query buffer will hold strings as well as the defined struct.
  236. // The string pointers in the struct point to other areas in the
  237. // passed memory area, so it has to be large enough to hold the
  238. // struct plus all the strings.
  239. char cfgbuff[1024];
  240. LPQUERY_SERVICE_CONFIG cfg;
  241. DWORD cfgsize, needed_size;
  242. SC_HANDLE svc = this->svc_sc_handle ();
  243. if (svc == 0)
  244. {
  245. // To distinguish this error from the QueryServiceConfig failure
  246. // below, return the DWORD equivalent of -2, rather than -1.
  247. return MAXDWORD - 1;
  248. }
  249. cfgsize = sizeof cfgbuff;
  250. cfg = (LPQUERY_SERVICE_CONFIG) cfgbuff;
  251. BOOL ok = QueryServiceConfig (svc, cfg, cfgsize, &needed_size);
  252. if (ok)
  253. return cfg->dwStartType;
  254. // Zero is a valid return value for QueryServiceConfig, so if
  255. // QueryServiceConfig fails, return the DWORD equivalent of -1.
  256. return MAXDWORD;
  257. }
  258. void
  259. ACE_NT_Service::capture_log_msg_attributes (void)
  260. {
  261. ACE_Log_Msg::init_hook (this->log_msg_attributes_);
  262. }
  263. void
  264. ACE_NT_Service::inherit_log_msg_attributes (void)
  265. {
  266. // There's no thread descriptor involved with a NT-started
  267. // thread, so the first arg is 0.
  268. ACE_Log_Msg::inherit_hook (0, this->log_msg_attributes_);
  269. }
  270. int
  271. ACE_NT_Service::start_svc (ACE_Time_Value *wait_time,
  272. DWORD *svc_state,
  273. DWORD argc, const ACE_TCHAR **argv)
  274. {
  275. SC_HANDLE svc = this->svc_sc_handle ();
  276. if (svc == 0)
  277. return -1;
  278. if (!ACE_TEXT_StartService (svc, argc, argv))
  279. return -1;
  280. this->wait_for_service_state (SERVICE_RUNNING, wait_time);
  281. if (svc_state != 0)
  282. *svc_state = this->svc_status_.dwCurrentState;
  283. return 0;
  284. }
  285. int
  286. ACE_NT_Service::stop_svc (ACE_Time_Value *wait_time,
  287. DWORD *svc_state)
  288. {
  289. SC_HANDLE svc = this->svc_sc_handle ();
  290. if (svc == 0)
  291. return -1;
  292. if (!ControlService (svc,
  293. SERVICE_CONTROL_STOP,
  294. &this->svc_status_))
  295. return -1;
  296. this->wait_for_service_state (SERVICE_STOPPED,
  297. wait_time);
  298. if (svc_state != 0)
  299. *svc_state = this->svc_status_.dwCurrentState;
  300. return 0;
  301. }
  302. int
  303. ACE_NT_Service::pause_svc (ACE_Time_Value *wait_time,
  304. DWORD *svc_state)
  305. {
  306. SC_HANDLE svc = this->svc_sc_handle ();
  307. if (svc == 0)
  308. return -1;
  309. if (!ControlService (svc,
  310. SERVICE_CONTROL_PAUSE,
  311. &this->svc_status_))
  312. return -1;
  313. this->wait_for_service_state (SERVICE_PAUSED,
  314. wait_time);
  315. if (svc_state != 0)
  316. *svc_state = this->svc_status_.dwCurrentState;
  317. return 0;
  318. }
  319. int
  320. ACE_NT_Service::continue_svc (ACE_Time_Value *wait_time,
  321. DWORD *svc_state)
  322. {
  323. SC_HANDLE svc = this->svc_sc_handle ();
  324. if (svc == 0)
  325. return -1;
  326. if (!ControlService (svc,
  327. SERVICE_CONTROL_CONTINUE,
  328. &this->svc_status_))
  329. return -1;
  330. this->wait_for_service_state (SERVICE_RUNNING,
  331. wait_time);
  332. if (svc_state != 0)
  333. *svc_state = this->svc_status_.dwCurrentState;
  334. return 0;
  335. }
  336. DWORD
  337. ACE_NT_Service::state (ACE_Time_Value *wait_hint)
  338. {
  339. DWORD curr_state;
  340. if (this->state (&curr_state,
  341. wait_hint) == -1)
  342. return 0;
  343. return curr_state;
  344. }
  345. int
  346. ACE_NT_Service::state (DWORD *pstate,
  347. ACE_Time_Value *wait_hint)
  348. {
  349. SC_HANDLE svc = this->svc_sc_handle ();
  350. if (svc == 0)
  351. return -1;
  352. // Need to create a temporary copy of this variable since the
  353. // QueryServiceStatus call will modify the setting depending on the
  354. // current state of the Service. If the service is currently
  355. // STOPPED, the value will be cleared.
  356. DWORD controls_accepted = this->svc_status_.dwControlsAccepted;
  357. if (QueryServiceStatus (svc,
  358. &this->svc_status_) == 0)
  359. return -1;
  360. if (wait_hint != 0)
  361. wait_hint->msec (static_cast<long> (this->svc_status_.dwWaitHint));
  362. *pstate = this->svc_status_.dwCurrentState;
  363. this->svc_status_.dwControlsAccepted = controls_accepted;
  364. return 0;
  365. }
  366. // test_access
  367. //
  368. // Open a new handle, ignoring any handle open in svc_sc_handle_.
  369. // This function's results are returned without leaving the handle
  370. // open.
  371. int
  372. ACE_NT_Service::test_access (DWORD desired_access)
  373. {
  374. int status = -1; // Guilty until proven innocent
  375. SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
  376. 0,
  377. GENERIC_READ);
  378. if (sc_mgr != 0)
  379. {
  380. SC_HANDLE handle = ACE_TEXT_OpenService (sc_mgr,
  381. this->name (),
  382. desired_access);
  383. CloseServiceHandle (sc_mgr);
  384. if (handle != 0)
  385. {
  386. status = 0;
  387. CloseServiceHandle (handle);
  388. }
  389. }
  390. return status;
  391. }
  392. // report_status
  393. //
  394. // Reports the current status. If new_status is not 0, it sets the
  395. // status to the new value before reporting. NOTE - this assumes that
  396. // no actual service status values have the value 0. This is true in
  397. // WinNT 4. If the status is a 'pending' type, the supplied time hint
  398. // is used unless it's 0, in which case the existing hint is used.
  399. // The dwWaitHint is not updated by this function. The checkpoint is
  400. // incremented by one after a pending report.
  401. int
  402. ACE_NT_Service::report_status (DWORD new_status,
  403. DWORD time_hint)
  404. {
  405. int bump_checkpoint = 0;
  406. int retval = 0;
  407. DWORD save_controls = 0;
  408. if (new_status != 0)
  409. this->svc_status_.dwCurrentState = new_status;
  410. switch (this->svc_status_.dwCurrentState)
  411. {
  412. case SERVICE_START_PENDING:
  413. save_controls = this->svc_status_.dwControlsAccepted;
  414. this->svc_status_.dwControlsAccepted = 0;
  415. /* Fall through */
  416. case SERVICE_STOP_PENDING:
  417. case SERVICE_CONTINUE_PENDING:
  418. case SERVICE_PAUSE_PENDING:
  419. this->svc_status_.dwWaitHint = time_hint ? time_hint : this->start_time_;
  420. bump_checkpoint = 1;
  421. break;
  422. default:
  423. this->svc_status_.dwCheckPoint = 0;
  424. }
  425. retval = SetServiceStatus (this->svc_handle_,
  426. &this->svc_status_) ? 0 : -1;
  427. if (save_controls != 0)
  428. this->svc_status_.dwControlsAccepted = save_controls;
  429. if (bump_checkpoint)
  430. ++this->svc_status_.dwCheckPoint;
  431. return retval;
  432. }
  433. SC_HANDLE
  434. ACE_NT_Service::svc_sc_handle (void)
  435. {
  436. if (this->svc_sc_handle_ == 0)
  437. {
  438. SC_HANDLE sc_mgr = ACE_TEXT_OpenSCManager (this->host (),
  439. 0,
  440. SC_MANAGER_ALL_ACCESS);
  441. if (sc_mgr != 0)
  442. {
  443. this->svc_sc_handle_ = ACE_TEXT_OpenService (sc_mgr,
  444. this->name (),
  445. SERVICE_ALL_ACCESS);
  446. if (this->svc_sc_handle_ == 0)
  447. ACE_OS::set_errno_to_last_error ();
  448. CloseServiceHandle (sc_mgr);
  449. }
  450. else
  451. ACE_OS::set_errno_to_last_error ();
  452. }
  453. return this->svc_sc_handle_;
  454. }
  455. void
  456. ACE_NT_Service::wait_for_service_state (DWORD desired_state,
  457. ACE_Time_Value *wait_time)
  458. {
  459. DWORD last_state = 0;
  460. DWORD last_check_point = 0;
  461. int first_time = 1;
  462. int service_ok;
  463. ACE_Time_Value time_out = ACE_OS::gettimeofday ();
  464. if (wait_time != 0)
  465. time_out += *wait_time;
  466. // Poll until the service reaches the desired state.
  467. for (;;)
  468. {
  469. service_ok = 0 != QueryServiceStatus (this->svc_sc_handle_,
  470. &this->svc_status_);
  471. // If we cannot query the service, we are done.
  472. if (!service_ok)
  473. break;
  474. // If the service has the desired state, we are done.
  475. if (desired_state == this->svc_status_.dwCurrentState)
  476. break;
  477. // If we time-out, we are done
  478. if (wait_time != 0 && ACE_OS::gettimeofday () > time_out )
  479. {
  480. errno = ETIME;
  481. break;
  482. }
  483. if (first_time)
  484. {
  485. // remember the service state, the first time we wait
  486. last_state = this->svc_status_.dwCurrentState;
  487. last_check_point = this->svc_status_.dwCheckPoint;
  488. first_time = 0;
  489. }
  490. else
  491. {
  492. // update the state change.
  493. if (last_state != this->svc_status_.dwCurrentState)
  494. {
  495. last_state = this->svc_status_.dwCurrentState;
  496. last_check_point = this->svc_status_.dwCheckPoint;
  497. }
  498. else
  499. {
  500. // The check-point should have increased
  501. if (this->svc_status_.dwCheckPoint > last_check_point)
  502. last_check_point = this->svc_status_.dwCheckPoint;
  503. else
  504. {
  505. // Service control failure, we are done.
  506. service_ok = 0;
  507. break;
  508. }
  509. }
  510. }
  511. ::Sleep (this->svc_status_.dwWaitHint);
  512. }
  513. return;
  514. }
  515. ACE_END_VERSIONED_NAMESPACE_DECL
  516. #endif /* ACE_WIN32 && !ACE_LACKS_WIN32_SERVICES */