Source code of Windows XP (NT5)
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.

654 lines
15 KiB

  1. /*++
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. service.cpp
  5. Abstract:
  6. SIS Groveler support for running as a system service
  7. Authors:
  8. John Douceur, 1998
  9. Environment:
  10. User Mode
  11. Revision History:
  12. --*/
  13. #include "all.hxx"
  14. static _TCHAR *service_name = _T("Groveler");
  15. static _TCHAR *service_path = _T("%SystemRoot%\\System32\\grovel.exe");
  16. #if SERVICE
  17. SERVICE_STATUS Service::status;
  18. SERVICE_STATUS_HANDLE Service::status_handle = 0;
  19. int Service::num_partitions = 0;
  20. int Service::partition_indices[num_drive_letters];
  21. unsigned int Service::max_response_time = 0;
  22. volatile bool Service::pause_commanded = false;
  23. volatile bool Service::grovel_paused = false;
  24. volatile bool * Service::full_volume_scan_commanded;
  25. volatile bool * Service::demarcate_foreground_batch;
  26. volatile bool * Service::foreground_batch_in_progress;
  27. volatile bool * Service::foreground_commanded;
  28. volatile bool * Service::foreground_acknowledged;
  29. volatile int Service::foreground_count = 0;
  30. volatile bool Service::controller_suspended = false;
  31. volatile bool Service::exhorter_suspended = true;
  32. #endif // SERVICE
  33. extern "C" __cdecl _tmain(int argc, _TCHAR **argv)
  34. {
  35. #if SERVICE
  36. return Service::start();
  37. #else // SERVICE
  38. return _main(argc, argv);
  39. #endif // SERVICE
  40. }
  41. #if SERVICE
  42. int
  43. Service::start()
  44. {
  45. #if DBG
  46. HKEY path_key;
  47. _TCHAR scm_path[1024];
  48. //
  49. // See if the TYPE is interactive, if so then create a visible console
  50. //
  51. _stprintf(scm_path,
  52. _T("SYSTEM\\CurrentControlSet\\Services\\%s"), service_name);
  53. long result =
  54. RegOpenKeyEx(HKEY_LOCAL_MACHINE, scm_path, 0, KEY_READ, &path_key);
  55. if (result == ERROR_SUCCESS)
  56. {
  57. ASSERT(path_key != 0);
  58. DWORD service_type = 0;
  59. DWORD type_size = sizeof(DWORD);
  60. result = RegQueryValueEx(path_key, _T("Type"), 0, 0,
  61. (BYTE *)&service_type, &type_size);
  62. if (result == ERROR_SUCCESS)
  63. {
  64. ASSERT(type_size == sizeof(DWORD));
  65. if (service_type & SERVICE_INTERACTIVE_PROCESS)
  66. {
  67. FreeConsole();
  68. BOOL ok = AllocConsole();
  69. if (ok)
  70. {
  71. //
  72. // fixup "stdout" to the new console
  73. //
  74. HANDLE out_fs_handle = GetStdHandle(STD_OUTPUT_HANDLE);
  75. if (out_fs_handle != INVALID_HANDLE_VALUE)
  76. {
  77. int out_crt_handle =
  78. _open_osfhandle((LONG_PTR)out_fs_handle, _O_TEXT);
  79. if (out_crt_handle != -1)
  80. {
  81. //*stdout = *(_tfdopen(out_crt_handle, _T("w"))); //Fixing PREFIX bug
  82. FILE *myStdout = _tfdopen(out_crt_handle, _T("w"));
  83. if (myStdout != 0)
  84. {
  85. *stdout = *myStdout;
  86. setvbuf(stdout, NULL, _IONBF, 0);
  87. }
  88. else
  89. {
  90. PRINT_DEBUG_MSG((_T("GROVELER: _tfdopen() failed\n")));
  91. }
  92. }
  93. else
  94. {
  95. PRINT_DEBUG_MSG((_T("GROVELER: _open_osfhandle() failed\n")));
  96. }
  97. }
  98. else
  99. {
  100. PRINT_DEBUG_MSG((_T("GROVELER: GetStdHandle() failed\n")));
  101. }
  102. //
  103. // fixup "stderr" to the new console
  104. //
  105. HANDLE err_fs_handle = GetStdHandle(STD_ERROR_HANDLE);
  106. if (err_fs_handle != INVALID_HANDLE_VALUE)
  107. {
  108. int err_crt_handle =
  109. _open_osfhandle((LONG_PTR)err_fs_handle, _O_TEXT);
  110. if (err_crt_handle != -1)
  111. {
  112. //*stderr = *(_tfdopen(err_crt_handle, _T("w"))); //fixing PREFIX bug
  113. FILE *myStderr = _tfdopen(err_crt_handle, _T("w"));
  114. if (myStderr != 0)
  115. {
  116. *stderr = *myStderr;
  117. setvbuf(stderr, NULL, _IONBF, 0);
  118. }
  119. else
  120. {
  121. PRINT_DEBUG_MSG((_T("GROVELER: _tfdopen() failed\n")));
  122. }
  123. }
  124. else
  125. {
  126. PRINT_DEBUG_MSG((_T("GROVELER: _open_osfhandle() failed\n")));
  127. }
  128. }
  129. else
  130. {
  131. PRINT_DEBUG_MSG((_T("GROVELER: GetStdHandle() failed\n")));
  132. }
  133. }
  134. else
  135. {
  136. DWORD err = GetLastError();
  137. PRINT_DEBUG_MSG((_T("GROVELER: AllocConsole() failed with error %d\n"),
  138. err));
  139. }
  140. }
  141. }
  142. else
  143. {
  144. PRINT_DEBUG_MSG((_T("GROVELER: RegQueryValueEx() failed with error %d\n"),
  145. result));
  146. }
  147. ASSERT(path_key != 0);
  148. RegCloseKey(path_key);
  149. path_key = 0;
  150. }
  151. else
  152. {
  153. PRINT_DEBUG_MSG((_T("GROVELER: RegOpenKeyEx() failed with error %d\n"), result));
  154. }
  155. #endif
  156. static SERVICE_TABLE_ENTRY dispatch_table[] =
  157. {
  158. {service_name, service_main},
  159. {0, 0}
  160. };
  161. int ok = StartServiceCtrlDispatcher(dispatch_table);
  162. if (!ok)
  163. {
  164. DWORD err = GetLastError();
  165. PRINT_DEBUG_MSG((_T("GROVELER: StartServiceCtrlDispatcher() failed with error %d\n"),
  166. err));
  167. }
  168. return !ok;
  169. }
  170. void
  171. Service::record_partition_indices()
  172. {
  173. //
  174. // Get how many total partitions there are
  175. //
  176. num_partitions = sis_drives.partition_count();
  177. //
  178. // Allocate structures based on the number of partitions
  179. //
  180. full_volume_scan_commanded = new bool[num_partitions];
  181. demarcate_foreground_batch = new bool[num_partitions];
  182. foreground_batch_in_progress = new bool[num_partitions];
  183. foreground_commanded = new bool[num_partitions];
  184. foreground_acknowledged = new bool[num_partitions];
  185. //
  186. // Allocate those structures
  187. //
  188. for (int index = 0; index < num_partitions; index++)
  189. {
  190. full_volume_scan_commanded[index] = false;
  191. demarcate_foreground_batch[index] = false;
  192. foreground_batch_in_progress[index] = false;
  193. foreground_commanded[index] = false;
  194. foreground_acknowledged[index] = false;
  195. }
  196. //
  197. // Initializes indexes for each "Drive Letter" partition
  198. //
  199. for (index = 0; index < num_drive_letters; index++)
  200. {
  201. partition_indices[index] = -1;
  202. }
  203. //
  204. // This initilaizes an array that is indexed by drive letter
  205. // that maps that drive letter to the internal order they are
  206. // stored in.
  207. //
  208. int num_lettered_partitions = sis_drives.lettered_partition_count();
  209. for (index = 0; index < num_lettered_partitions; index++)
  210. {
  211. _TCHAR drive_letter = sis_drives.partition_mount_name(index)[0];
  212. int drive_letter_index = _totlower(drive_letter) - _T('a');
  213. ASSERT(drive_letter_index >= 0);
  214. ASSERT(drive_letter_index < num_drive_letters);
  215. ASSERT(partition_indices[drive_letter_index] == -1);
  216. partition_indices[drive_letter_index] = index;
  217. }
  218. }
  219. void
  220. Service::set_max_response_time(
  221. unsigned int max_response_time)
  222. {
  223. ASSERT(max_response_time > 0);
  224. Service::max_response_time = max_response_time;
  225. }
  226. void
  227. Service::checkpoint()
  228. {
  229. status.dwCheckPoint++;
  230. int ok = SetServiceStatus(status_handle, &status);
  231. if (!ok)
  232. {
  233. DWORD err = GetLastError();
  234. PRINT_DEBUG_MSG((_T("GROVELER: SetServiceStatus() failed with error %d\n"), err));
  235. eventlog.report_event(GROVMSG_SET_STATUS_FAILURE, 0);
  236. }
  237. }
  238. void
  239. Service::report_start()
  240. {
  241. ASSERT(status.dwCurrentState == SERVICE_START_PENDING);
  242. status.dwCurrentState = SERVICE_RUNNING;
  243. int ok = SetServiceStatus(status_handle, &status);
  244. if (!ok)
  245. {
  246. DWORD err = GetLastError();
  247. PRINT_DEBUG_MSG((_T("GROVELER: SetServiceStatus() failed with error %d\n"), err));
  248. eventlog.report_event(GROVMSG_SET_STATUS_FAILURE, 0);
  249. }
  250. }
  251. bool
  252. Service::groveling_paused()
  253. {
  254. return grovel_paused;
  255. }
  256. bool
  257. Service::foreground_groveling()
  258. {
  259. ASSERT(foreground_count >= 0);
  260. ASSERT(foreground_count <= num_partitions);
  261. return foreground_count > 0;
  262. }
  263. void
  264. Service::suspending_controller()
  265. {
  266. controller_suspended = true;
  267. }
  268. void
  269. Service::suspending_exhorter()
  270. {
  271. exhorter_suspended = true;
  272. }
  273. bool
  274. Service::partition_in_foreground(
  275. int partition_index)
  276. {
  277. ASSERT(partition_index >= 0);
  278. ASSERT(partition_index < num_partitions);
  279. return foreground_batch_in_progress[partition_index]
  280. && foreground_acknowledged[partition_index];
  281. }
  282. void
  283. Service::set_foreground_batch_in_progress(
  284. int partition_index,
  285. bool value)
  286. {
  287. ASSERT(partition_index >= 0);
  288. ASSERT(partition_index < num_partitions);
  289. ASSERT(foreground_count >= 0);
  290. ASSERT(foreground_count <= num_partitions);
  291. if (value)
  292. {
  293. if (!foreground_batch_in_progress[partition_index] &&
  294. foreground_acknowledged[partition_index])
  295. {
  296. foreground_count++;
  297. }
  298. }
  299. else
  300. {
  301. if (foreground_batch_in_progress[partition_index] &&
  302. foreground_acknowledged[partition_index])
  303. {
  304. foreground_count--;
  305. }
  306. }
  307. ASSERT(foreground_count >= 0);
  308. ASSERT(foreground_count <= num_partitions);
  309. foreground_batch_in_progress[partition_index] = value;
  310. if (!grovel_paused)
  311. {
  312. if (foreground_count == 0 && controller_suspended)
  313. {
  314. controller_suspended = false;
  315. CentralController::control_groveling((void *)controller);
  316. }
  317. if (foreground_count > 0 && exhorter_suspended)
  318. {
  319. exhorter_suspended = false;
  320. CentralController::exhort_groveling((void *)controller);
  321. }
  322. }
  323. }
  324. void
  325. Service::follow_command()
  326. {
  327. //
  328. // If pause has been requested and we have not pause yet, do it
  329. //
  330. if (pause_commanded && !grovel_paused)
  331. {
  332. eventlog.report_event(GROVMSG_SERVICE_PAUSED, 0);
  333. grovel_paused = true;
  334. status.dwCurrentState = SERVICE_PAUSED;
  335. int ok = SetServiceStatus(status_handle, &status);
  336. if (!ok)
  337. {
  338. DWORD err = GetLastError();
  339. PRINT_DEBUG_MSG((_T("GROVELER: SetServiceStatus() failed with error %d\n"),
  340. err));
  341. eventlog.report_event(GROVMSG_SET_STATUS_FAILURE, 0);
  342. }
  343. }
  344. //
  345. // If stop pausing has been requested and we are paused, unpause
  346. //
  347. if (!pause_commanded && grovel_paused)
  348. {
  349. eventlog.report_event(GROVMSG_SERVICE_CONTINUED, 0);
  350. grovel_paused = false;
  351. status.dwCurrentState = SERVICE_RUNNING;
  352. int ok = SetServiceStatus(status_handle, &status);
  353. if (!ok)
  354. {
  355. DWORD err = GetLastError();
  356. PRINT_DEBUG_MSG((_T("GROVELER: SetServiceStatus() failed with error %d\n"),
  357. err));
  358. eventlog.report_event(GROVMSG_SET_STATUS_FAILURE, 0);
  359. }
  360. }
  361. //
  362. //
  363. //
  364. for (int index = 0; index < num_partitions; index++)
  365. {
  366. ASSERT(foreground_count >= 0);
  367. ASSERT(foreground_count <= num_partitions);
  368. if (foreground_commanded[index] &&
  369. !foreground_acknowledged[index])
  370. {
  371. foreground_acknowledged[index] = true;
  372. if (foreground_batch_in_progress[index])
  373. {
  374. foreground_count++;
  375. }
  376. }
  377. if (!foreground_commanded[index] &&
  378. foreground_acknowledged[index])
  379. {
  380. foreground_acknowledged[index] = false;
  381. if (foreground_batch_in_progress[index])
  382. {
  383. foreground_count--;
  384. }
  385. }
  386. if (full_volume_scan_commanded[index])
  387. {
  388. controller->command_full_volume_scan(index);
  389. full_volume_scan_commanded[index] = false;
  390. }
  391. if (demarcate_foreground_batch[index])
  392. {
  393. controller->demarcate_foreground_batch(index);
  394. demarcate_foreground_batch[index] = false;
  395. }
  396. }
  397. ASSERT(foreground_count >= 0);
  398. ASSERT(foreground_count <= num_partitions);
  399. if (!grovel_paused)
  400. {
  401. if (foreground_count == 0 && controller_suspended)
  402. {
  403. controller_suspended = false;
  404. CentralController::control_groveling((void *)controller);
  405. }
  406. if (foreground_count > 0 && exhorter_suspended)
  407. {
  408. exhorter_suspended = false;
  409. CentralController::exhort_groveling((void *)controller);
  410. }
  411. }
  412. }
  413. void WINAPI
  414. Service::control_handler(
  415. DWORD opcode)
  416. {
  417. if (opcode == SERVICE_CONTROL_STOP || opcode == SERVICE_CONTROL_SHUTDOWN)
  418. {
  419. event_timer.halt();
  420. status.dwCurrentState = SERVICE_STOP_PENDING;
  421. status.dwWin32ExitCode = 0;
  422. status.dwWaitHint = max_response_time;
  423. }
  424. else if (opcode == SERVICE_CONTROL_PAUSE)
  425. {
  426. pause_commanded = true;
  427. status.dwCurrentState = SERVICE_PAUSE_PENDING;
  428. status.dwWaitHint = max_response_time;
  429. }
  430. else if (opcode == SERVICE_CONTROL_CONTINUE)
  431. {
  432. pause_commanded = false;
  433. status.dwCurrentState = SERVICE_CONTINUE_PENDING;
  434. status.dwWaitHint = max_response_time;
  435. }
  436. else if ((opcode & SERVICE_CONTROL_COMMAND_MASK) ==
  437. SERVICE_CONTROL_FOREGROUND)
  438. {
  439. int drive_letter_index = opcode & SERVICE_CONTROL_PARTITION_MASK;
  440. if (drive_letter_index == SERVICE_CONTROL_ALL_PARTITIONS)
  441. {
  442. for (int index = 0; index < num_partitions; index++)
  443. {
  444. demarcate_foreground_batch[index] = true;
  445. foreground_commanded[index] = true;
  446. }
  447. }
  448. else if (drive_letter_index < num_drive_letters)
  449. {
  450. int partition_index = partition_indices[drive_letter_index];
  451. if (partition_index >= 0)
  452. {
  453. demarcate_foreground_batch[partition_index] = true;
  454. foreground_commanded[partition_index] = true;
  455. }
  456. }
  457. }
  458. else if ((opcode & SERVICE_CONTROL_COMMAND_MASK) ==
  459. SERVICE_CONTROL_BACKGROUND)
  460. {
  461. int drive_letter_index = opcode & SERVICE_CONTROL_PARTITION_MASK;
  462. if (drive_letter_index == SERVICE_CONTROL_ALL_PARTITIONS)
  463. {
  464. for (int index = 0; index < num_partitions; index++)
  465. {
  466. foreground_commanded[index] = false;
  467. }
  468. }
  469. else if (drive_letter_index < num_drive_letters)
  470. {
  471. int partition_index = partition_indices[drive_letter_index];
  472. if (partition_index >= 0)
  473. {
  474. foreground_commanded[partition_index] = false;
  475. }
  476. }
  477. }
  478. else if ((opcode & SERVICE_CONTROL_COMMAND_MASK) ==
  479. SERVICE_CONTROL_VOLSCAN)
  480. {
  481. int drive_letter_index = opcode & SERVICE_CONTROL_PARTITION_MASK;
  482. if (drive_letter_index == SERVICE_CONTROL_ALL_PARTITIONS)
  483. {
  484. for (int index = 0; index < num_partitions; index++)
  485. {
  486. full_volume_scan_commanded[index] = true;
  487. }
  488. }
  489. else if (drive_letter_index < num_drive_letters)
  490. {
  491. int partition_index = partition_indices[drive_letter_index];
  492. if (partition_index >= 0)
  493. {
  494. full_volume_scan_commanded[partition_index] = true;
  495. }
  496. }
  497. }
  498. else if (opcode != SERVICE_CONTROL_INTERROGATE)
  499. {
  500. PRINT_DEBUG_MSG((_T("GROVELER: Unrecognized SCM opcode: %lx\n"), opcode));
  501. }
  502. //
  503. // Return our current status
  504. //
  505. int ok = SetServiceStatus(status_handle, &status);
  506. if (!ok)
  507. {
  508. DWORD err = GetLastError();
  509. PRINT_DEBUG_MSG((_T("GROVELER: SetServiceStatus() failed with error %d\n"), err));
  510. eventlog.report_event(GROVMSG_SET_STATUS_FAILURE, 0);
  511. }
  512. sync_event.set();
  513. }
  514. void WINAPI
  515. Service::service_main(
  516. DWORD argc,
  517. LPTSTR *argv)
  518. {
  519. //
  520. // Register the control handler
  521. //
  522. status_handle = RegisterServiceCtrlHandler(service_name, control_handler);
  523. if (status_handle == 0)
  524. {
  525. DWORD err = GetLastError();
  526. PRINT_DEBUG_MSG((_T("GROVELER: RegisterServiceCtrlHandler() failed with error %d\n"),
  527. err));
  528. eventlog.report_event(GROVMSG_SERVICE_NOSTART, 0);
  529. return;
  530. }
  531. //
  532. // Set Service status
  533. //
  534. status.dwServiceType = SERVICE_WIN32;
  535. status.dwCurrentState = SERVICE_START_PENDING;
  536. status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
  537. SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
  538. status.dwWin32ExitCode = 0;
  539. status.dwServiceSpecificExitCode = 0;
  540. status.dwCheckPoint = 0;
  541. status.dwWaitHint = 0;
  542. int ok = SetServiceStatus(status_handle, &status);
  543. if (!ok)
  544. {
  545. DWORD err = GetLastError();
  546. PRINT_DEBUG_MSG((_T("GROVELER: SetServiceStatus() failed with error %d\n"), err));
  547. eventlog.report_event(GROVMSG_SET_STATUS_FAILURE, 0);
  548. }
  549. //
  550. // Start the main program of the service
  551. //
  552. int exit_code = _main(argc, argv);
  553. //
  554. // When it returns, we are done
  555. //
  556. status.dwWin32ExitCode = exit_code;
  557. status.dwCurrentState = SERVICE_STOPPED;
  558. ok = SetServiceStatus(status_handle, &status);
  559. if (!ok)
  560. {
  561. DWORD err = GetLastError();
  562. PRINT_DEBUG_MSG((_T("GROVELER: SetServiceStatus() failed with error %d\n"), err));
  563. eventlog.report_event(GROVMSG_SET_STATUS_FAILURE, 0);
  564. }
  565. }
  566. #endif // SERVICE