/*++ Copyright (c) 1998 Microsoft Corporation Module Name: service.cpp Abstract: SIS Groveler support for running as a system service Authors: John Douceur, 1998 Environment: User Mode Revision History: --*/ #include "all.hxx" static _TCHAR *service_name = _T("Groveler"); static _TCHAR *service_path = _T("%SystemRoot%\\System32\\grovel.exe"); #if SERVICE SERVICE_STATUS Service::status; SERVICE_STATUS_HANDLE Service::status_handle = 0; int Service::num_partitions = 0; int Service::partition_indices[num_drive_letters]; unsigned int Service::max_response_time = 0; volatile bool Service::pause_commanded = false; volatile bool Service::grovel_paused = false; volatile bool * Service::full_volume_scan_commanded; volatile bool * Service::demarcate_foreground_batch; volatile bool * Service::foreground_batch_in_progress; volatile bool * Service::foreground_commanded; volatile bool * Service::foreground_acknowledged; volatile int Service::foreground_count = 0; volatile bool Service::controller_suspended = false; volatile bool Service::exhorter_suspended = true; #endif // SERVICE extern "C" __cdecl _tmain(int argc, _TCHAR **argv) { #if SERVICE return Service::start(); #else // SERVICE return _main(argc, argv); #endif // SERVICE } #if SERVICE int Service::start() { #if DBG HKEY path_key; _TCHAR scm_path[1024]; // // See if the TYPE is interactive, if so then create a visible console // (void)StringCbPrintf(scm_path,sizeof(scm_path), L"SYSTEM\\CurrentControlSet\\Services\\%s", service_name); long result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, scm_path, 0, KEY_READ, &path_key); if (result == ERROR_SUCCESS) { ASSERT(path_key != 0); DWORD service_type = 0; DWORD type_size = sizeof(DWORD); result = RegQueryValueEx(path_key, _T("Type"), 0, 0, (BYTE *)&service_type, &type_size); if (result == ERROR_SUCCESS) { ASSERT(type_size == sizeof(DWORD)); if (service_type & SERVICE_INTERACTIVE_PROCESS) { FreeConsole(); BOOL ok = AllocConsole(); if (ok) { // // fixup "stdout" to the new console // HANDLE out_fs_handle = GetStdHandle(STD_OUTPUT_HANDLE); if (out_fs_handle != INVALID_HANDLE_VALUE) { int out_crt_handle = _open_osfhandle((LONG_PTR)out_fs_handle, _O_TEXT); if (out_crt_handle != -1) { //*stdout = *(_tfdopen(out_crt_handle, _T("w"))); //Fixing PREFIX bug FILE *myStdout = _tfdopen(out_crt_handle, _T("w")); if (myStdout != 0) { *stdout = *myStdout; setvbuf(stdout, NULL, _IONBF, 0); } else { PRINT_DEBUG_MSG((_T("GROVELER: _tfdopen() failed\n"))); } } else { PRINT_DEBUG_MSG((_T("GROVELER: _open_osfhandle() failed\n"))); } } else { PRINT_DEBUG_MSG((_T("GROVELER: GetStdHandle() failed\n"))); } // // fixup "stderr" to the new console // HANDLE err_fs_handle = GetStdHandle(STD_ERROR_HANDLE); if (err_fs_handle != INVALID_HANDLE_VALUE) { int err_crt_handle = _open_osfhandle((LONG_PTR)err_fs_handle, _O_TEXT); if (err_crt_handle != -1) { //*stderr = *(_tfdopen(err_crt_handle, _T("w"))); //fixing PREFIX bug FILE *myStderr = _tfdopen(err_crt_handle, _T("w")); if (myStderr != 0) { *stderr = *myStderr; setvbuf(stderr, NULL, _IONBF, 0); } else { PRINT_DEBUG_MSG((_T("GROVELER: _tfdopen() failed\n"))); } } else { PRINT_DEBUG_MSG((_T("GROVELER: _open_osfhandle() failed\n"))); } } else { PRINT_DEBUG_MSG((_T("GROVELER: GetStdHandle() failed\n"))); } } else { DWORD err = GetLastError(); PRINT_DEBUG_MSG((_T("GROVELER: AllocConsole() failed with error %d\n"), err)); } } } else { PRINT_DEBUG_MSG((_T("GROVELER: RegQueryValueEx() failed with error %d\n"), result)); } ASSERT(path_key != 0); RegCloseKey(path_key); path_key = 0; } else { PRINT_DEBUG_MSG((_T("GROVELER: RegOpenKeyEx() failed with error %d\n"), result)); } #endif static SERVICE_TABLE_ENTRY dispatch_table[] = { {service_name, service_main}, {0, 0} }; int ok = StartServiceCtrlDispatcher(dispatch_table); if (!ok) { DWORD err = GetLastError(); PRINT_DEBUG_MSG((_T("GROVELER: StartServiceCtrlDispatcher() failed with error %d\n"), err)); } return !ok; } void Service::record_partition_indices() { // // Get how many total partitions there are // num_partitions = sis_drives.partition_count(); // // Allocate structures based on the number of partitions // full_volume_scan_commanded = new bool[num_partitions]; demarcate_foreground_batch = new bool[num_partitions]; foreground_batch_in_progress = new bool[num_partitions]; foreground_commanded = new bool[num_partitions]; foreground_acknowledged = new bool[num_partitions]; // // Allocate those structures // for (int index = 0; index < num_partitions; index++) { full_volume_scan_commanded[index] = false; demarcate_foreground_batch[index] = false; foreground_batch_in_progress[index] = false; foreground_commanded[index] = false; foreground_acknowledged[index] = false; } // // Initializes indexes for each "Drive Letter" partition // for (index = 0; index < num_drive_letters; index++) { partition_indices[index] = -1; } // // This initilaizes an array that is indexed by drive letter // that maps that drive letter to the internal order they are // stored in. // int num_lettered_partitions = sis_drives.lettered_partition_count(); for (index = 0; index < num_lettered_partitions; index++) { _TCHAR drive_letter = sis_drives.partition_mount_name(index)[0]; int drive_letter_index = _totlower(drive_letter) - _T('a'); ASSERT(drive_letter_index >= 0); ASSERT(drive_letter_index < num_drive_letters); ASSERT(partition_indices[drive_letter_index] == -1); partition_indices[drive_letter_index] = index; } } void Service::set_max_response_time( unsigned int max_response_time) { ASSERT(max_response_time > 0); Service::max_response_time = max_response_time; } void Service::checkpoint() { status.dwCheckPoint++; int ok = SetServiceStatus(status_handle, &status); if (!ok) { DWORD err = GetLastError(); PRINT_DEBUG_MSG((_T("GROVELER: SetServiceStatus() failed with error %d\n"), err)); eventlog.report_event(GROVMSG_SET_STATUS_FAILURE, err, 0); } } void Service::report_start() { ASSERT(status.dwCurrentState == SERVICE_START_PENDING); status.dwCurrentState = SERVICE_RUNNING; int ok = SetServiceStatus(status_handle, &status); if (!ok) { DWORD err = GetLastError(); PRINT_DEBUG_MSG((_T("GROVELER: SetServiceStatus() failed with error %d\n"), err)); eventlog.report_event(GROVMSG_SET_STATUS_FAILURE, err, 0); } } bool Service::groveling_paused() { return grovel_paused; } bool Service::foreground_groveling() { ASSERT(foreground_count >= 0); ASSERT(foreground_count <= num_partitions); return foreground_count > 0; } void Service::suspending_controller() { controller_suspended = true; } void Service::suspending_exhorter() { exhorter_suspended = true; } bool Service::partition_in_foreground( int partition_index) { ASSERT(partition_index >= 0); ASSERT(partition_index < num_partitions); return foreground_batch_in_progress[partition_index] && foreground_acknowledged[partition_index]; } void Service::set_foreground_batch_in_progress( int partition_index, bool value) { ASSERT(partition_index >= 0); ASSERT(partition_index < num_partitions); ASSERT(foreground_count >= 0); ASSERT(foreground_count <= num_partitions); if (value) { if (!foreground_batch_in_progress[partition_index] && foreground_acknowledged[partition_index]) { foreground_count++; } } else { if (foreground_batch_in_progress[partition_index] && foreground_acknowledged[partition_index]) { foreground_count--; } } ASSERT(foreground_count >= 0); ASSERT(foreground_count <= num_partitions); foreground_batch_in_progress[partition_index] = value; if (!grovel_paused) { if (foreground_count == 0 && controller_suspended) { controller_suspended = false; CentralController::control_groveling((void *)controller); } if (foreground_count > 0 && exhorter_suspended) { exhorter_suspended = false; CentralController::exhort_groveling((void *)controller); } } } void Service::follow_command() { // // If pause has been requested and we have not pause yet, do it // if (pause_commanded && !grovel_paused) { eventlog.report_event(GROVMSG_SERVICE_PAUSED, ERROR_SUCCESS, 0); grovel_paused = true; status.dwCurrentState = SERVICE_PAUSED; int ok = SetServiceStatus(status_handle, &status); if (!ok) { DWORD err = GetLastError(); PRINT_DEBUG_MSG((_T("GROVELER: SetServiceStatus() failed with error %d\n"), err)); eventlog.report_event(GROVMSG_SET_STATUS_FAILURE, err, 0); } } // // If stop pausing has been requested and we are paused, unpause // if (!pause_commanded && grovel_paused) { eventlog.report_event(GROVMSG_SERVICE_CONTINUED, ERROR_SUCCESS, 0); grovel_paused = false; status.dwCurrentState = SERVICE_RUNNING; int ok = SetServiceStatus(status_handle, &status); if (!ok) { DWORD err = GetLastError(); PRINT_DEBUG_MSG((_T("GROVELER: SetServiceStatus() failed with error %d\n"), err)); eventlog.report_event(GROVMSG_SET_STATUS_FAILURE, err, 0); } } // // // for (int index = 0; index < num_partitions; index++) { ASSERT(foreground_count >= 0); ASSERT(foreground_count <= num_partitions); if (foreground_commanded[index] && !foreground_acknowledged[index]) { foreground_acknowledged[index] = true; if (foreground_batch_in_progress[index]) { foreground_count++; } } if (!foreground_commanded[index] && foreground_acknowledged[index]) { foreground_acknowledged[index] = false; if (foreground_batch_in_progress[index]) { foreground_count--; } } if (full_volume_scan_commanded[index]) { controller->command_full_volume_scan(index); full_volume_scan_commanded[index] = false; } if (demarcate_foreground_batch[index]) { controller->demarcate_foreground_batch(index); demarcate_foreground_batch[index] = false; } } ASSERT(foreground_count >= 0); ASSERT(foreground_count <= num_partitions); if (!grovel_paused) { if (foreground_count == 0 && controller_suspended) { controller_suspended = false; CentralController::control_groveling((void *)controller); } if (foreground_count > 0 && exhorter_suspended) { exhorter_suspended = false; CentralController::exhort_groveling((void *)controller); } } } void WINAPI Service::control_handler( DWORD opcode) { if (opcode == SERVICE_CONTROL_STOP || opcode == SERVICE_CONTROL_SHUTDOWN) { event_timer.halt(); status.dwCurrentState = SERVICE_STOP_PENDING; status.dwWin32ExitCode = 0; status.dwWaitHint = max_response_time; } else if (opcode == SERVICE_CONTROL_PAUSE) { pause_commanded = true; status.dwCurrentState = SERVICE_PAUSE_PENDING; status.dwWaitHint = max_response_time; } else if (opcode == SERVICE_CONTROL_CONTINUE) { pause_commanded = false; status.dwCurrentState = SERVICE_CONTINUE_PENDING; status.dwWaitHint = max_response_time; } else if ((opcode & SERVICE_CONTROL_COMMAND_MASK) == SERVICE_CONTROL_FOREGROUND) { int drive_letter_index = opcode & SERVICE_CONTROL_PARTITION_MASK; if (drive_letter_index == SERVICE_CONTROL_ALL_PARTITIONS) { for (int index = 0; index < num_partitions; index++) { demarcate_foreground_batch[index] = true; foreground_commanded[index] = true; } } else if (drive_letter_index < num_drive_letters) { int partition_index = partition_indices[drive_letter_index]; if (partition_index >= 0) { demarcate_foreground_batch[partition_index] = true; foreground_commanded[partition_index] = true; } } } else if ((opcode & SERVICE_CONTROL_COMMAND_MASK) == SERVICE_CONTROL_BACKGROUND) { int drive_letter_index = opcode & SERVICE_CONTROL_PARTITION_MASK; if (drive_letter_index == SERVICE_CONTROL_ALL_PARTITIONS) { for (int index = 0; index < num_partitions; index++) { foreground_commanded[index] = false; } } else if (drive_letter_index < num_drive_letters) { int partition_index = partition_indices[drive_letter_index]; if (partition_index >= 0) { foreground_commanded[partition_index] = false; } } } else if ((opcode & SERVICE_CONTROL_COMMAND_MASK) == SERVICE_CONTROL_VOLSCAN) { int drive_letter_index = opcode & SERVICE_CONTROL_PARTITION_MASK; if (drive_letter_index == SERVICE_CONTROL_ALL_PARTITIONS) { for (int index = 0; index < num_partitions; index++) { full_volume_scan_commanded[index] = true; } } else if (drive_letter_index < num_drive_letters) { int partition_index = partition_indices[drive_letter_index]; if (partition_index >= 0) { full_volume_scan_commanded[partition_index] = true; } } } else if (opcode != SERVICE_CONTROL_INTERROGATE) { PRINT_DEBUG_MSG((_T("GROVELER: Unrecognized SCM opcode: %lx\n"), opcode)); } // // Return our current status // int ok = SetServiceStatus(status_handle, &status); if (!ok) { DWORD err = GetLastError(); PRINT_DEBUG_MSG((_T("GROVELER: SetServiceStatus() failed with error %d\n"), err)); eventlog.report_event(GROVMSG_SET_STATUS_FAILURE, err, 0); } sync_event.set(); } void WINAPI Service::service_main( DWORD argc, LPTSTR *argv) { // // Register the control handler // status_handle = RegisterServiceCtrlHandler(service_name, control_handler); if (status_handle == 0) { DWORD err = GetLastError(); PRINT_DEBUG_MSG((_T("GROVELER: RegisterServiceCtrlHandler() failed with error %d\n"), err)); eventlog.report_event(GROVMSG_SERVICE_NOSTART, err, 0); return; } // // Set Service status // status.dwServiceType = SERVICE_WIN32; status.dwCurrentState = SERVICE_START_PENDING; status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN; status.dwWin32ExitCode = 0; status.dwServiceSpecificExitCode = 0; status.dwCheckPoint = 0; status.dwWaitHint = 0; int ok = SetServiceStatus(status_handle, &status); if (!ok) { DWORD err = GetLastError(); PRINT_DEBUG_MSG((_T("GROVELER: SetServiceStatus() failed with error %d\n"), err)); eventlog.report_event(GROVMSG_SET_STATUS_FAILURE, err, 0); } // // Start the main program of the service // int exit_code = _main(argc, argv); // // When it returns, we are done // status.dwWin32ExitCode = exit_code; status.dwCurrentState = SERVICE_STOPPED; ok = SetServiceStatus(status_handle, &status); if (!ok) { DWORD err = GetLastError(); PRINT_DEBUG_MSG((_T("GROVELER: SetServiceStatus() failed with error %d\n"), err)); eventlog.report_event(GROVMSG_SET_STATUS_FAILURE, err, 0); } } #endif // SERVICE