|
|
/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
grovel.cpp
Abstract:
SIS Groveler main function
Authors:
John Douceur, 1998
Environment:
User Mode
Revision History:
--*/
#include "all.hxx"
/*
* The core of the groveler executable is an object of the EventTimer class. * All periodic operations are registered with the global event_timer object, * and they are called at appropriate times during the execution of the * event_timer.run() function. * * Errors are written to the system event log, which is accessed through * member functions of the EventLog class. The eventlog object is a global * so that any function or member function of any class can log an event if * necessary. * * The service control thread synchronizes with the main groveler thread via * a Windows event. This event is encapsulated in an object of the SyncEvent * class. * * The SISDrives class determines which drives have SIS installed. * * The SharedData class is used to write values that are read by the groveler * performance DLL, so that the groveler's operation can be monitored by * PerfMon. This object needs to be global so that any function or member * function of any class can record performance information. * * The CentralController class is instantiated into a global object, rather * than an object local to the main() function, so that the service controller * can invoke CentralController member functions in order to affect its * operation. * * Initially, the shared_data and controller pointers are set to null, so that * if an exception occurs, the code that deletes allocated objects can check * for a null to determine whether or not the object has been instantiated. * */
EventTimer event_timer; EventLog eventlog; SyncEvent sync_event(false, false); SISDrives sis_drives; LogDrive *log_drive = 0; SharedData *shared_data = 0; CentralController *controller = 0;
/*
* Ordinarily, the groveler does not stop operation until it is told to by * a command from the service control manager. However, for testing, it can * sometimes be useful to specify a time limit for running. The groveler thus * accepts a first argument that indicates such a time limit. If an argument * is supplied, an invokation of the halt() function is scheduled in the * event_timer object for the specified time. * */
void halt( void *context) { event_timer.halt(); };
/*
* The function groveler_new_handler() is installed as a new handler by the * _set_new_handler() function. Whenever a memory allocation failure occurs, * it throws an exception_memory_allocation, which is caught by the catch * clause in the main() function. * */
int __cdecl groveler_new_handler( size_t bytes) { throw exception_memory_allocation; return 0; }
/*
* This file contains the main() function and declarations for global objects * for the groveler.exe program, as well as a couple of simple ancillary * functions, halt() and groveler_new_handler(). * * The main() function reads configuration information, instantiates a set of * primary objects -- the most significant of which are instances of the * classes Groveler and CentralController -- and enters the run() member * function of the event_timer object, which periodically invokes member * functions of other objects, most notably those of the clasess * CentralController and PartitionController. * * Configuration information comes from objects of three classes: * ReadParameters, ReadDiskInformation, and PathList. The ReadParameters * and PathList classes provide configuration information that applies to * grovelers on all partitions. The ReadDiskInformation class provides * configuration information that applies to a single disk partition. One * object of the ReadDiskInformation class is instantiated for each drive * that has SIS installed, as explained above. * * For each SIS drive, the main() function instantiates an object of the * ReadDiskInformation class to determine the configuration options (which * ReadDiskInformation obtains from the registry) for the given disk * partition. If the drive is configured to enable groveling, then an object * of the Groveler class is instantiated for that drive. * * The main() function then instantiates an object of class CentralController, * which in turn instantiates an object of class PartitionController for each * SIS-enabled disk partition. Each partition controller is assigned to one * object of the Groveler class, and it controls the groveler by calling its * member functions at appropriate times. * * Nearly all of of the processing done by the groveler executable is * performed within a try clause, the purpose of which is to catch errors of * terminal severity. There are two such errors (defined in all.hxx) that are * expected to throw such exceptions: a memory allocation failure and a * failure to create an Windows event. If either of these conditions occurs, * the program terminates. * */
_main(int argc, _TCHAR **argv) { _set_new_handler(groveler_new_handler); SetErrorMode(SEM_FAILCRITICALERRORS); int exit_code = NO_ERROR; int num_partitions = 0; int index;
//
// Initially, these pointers are set to null, so that if an exception
// occurs, the code that deletes allocated objects can check for a null
// to determine whether or not the object has been instantiated.
//
Groveler *grovelers = 0; GrovelStatus *groveler_statuses = 0; ReadDiskInformation **read_disk_info = 0; WriteDiskInformation **write_disk_info = 0;
//
// If program tracing is being performed, and if the traces are being sent
// to a file, the file is opened. This call is made through a macro
// so that no code will be generated for released builds. Since this call
// is made before the try clause, it is important that the function not
// perform any operation that could throw an exception, such as a memory
// allocation.
//
OPEN_TRACE_FILE();
try { //
// If a first argument is provided, it is the run period.
//
if (argc > 1) { int run_period = _ttoi(argv[1]); if (run_period <= 0) { PRINT_DEBUG_MSG((_T("GROVELER: run period must be greater than zero\n"))); return ERROR_BAD_ARGUMENTS; } unsigned int start_time = GET_TICK_COUNT(); event_timer.schedule(start_time + run_period, 0, halt); }
#if DEBUG_WAIT
// When debugging the groveler as a service, if the process is attached
// to a debugger after it has started, then the initialization code
// will usually be executed before the debugger has a chance to break.
// However, by defining DEBUG_WAIT to a non-zero value, the code will
// get stuck in the following infinite loop before doing the bulk of
// its initialization. (The event_timer, eventlog, and sync_event
// objects will have been constructed, because they are declared as
// globals.) The debugger can then be used to set debug_wait to false,
// and debugging can commence with the subsequent code.
bool debug_wait = true; while (debug_wait) { SLEEP(100); };
#endif // DEBUG_WAIT
eventlog.report_event(GROVMSG_SERVICE_STARTED, 0); sis_drives.open();
//
// See if there were any partions to scan. If not, quit
//
num_partitions = sis_drives.partition_count(); if (num_partitions == 0) { PRINT_DEBUG_MSG((_T("GROVELER: No local partitions have SIS installed.\n"))); eventlog.report_event(GROVMSG_NO_PARTITIONS, 0); eventlog.report_event(GROVMSG_SERVICE_STOPPED, 0); return ERROR_SERVICE_NOT_ACTIVE; }
//
// Report the service is running
//
SERVICE_REPORT_START();
//
// Setup shared data are between all the worker threads
//
num_partitions = sis_drives.partition_count();
_TCHAR **drive_names = new _TCHAR *[num_partitions]; for (index = 0; index < num_partitions; index++) { drive_names[index] = sis_drives.partition_mount_name(index); }
shared_data = new SharedData(num_partitions, drive_names);
delete drive_names;
//
// Get READ parameters
//
ReadParameters read_parameters; ASSERT(read_parameters.parameter_backup_interval >= 0);
//
// Get WRITE parameters
//
WriteParameters write_parameters(read_parameters.parameter_backup_interval);
//
// Get excluded path list
//
PathList excluded_paths;
//
// Setup LogDrive
//
log_drive = new LogDrive;
Groveler::set_log_drive(sis_drives.partition_mount_name(log_drive->drive_index()));
//
// Setup Groveler objects
//
grovelers = new Groveler[num_partitions]; groveler_statuses = new GrovelStatus[num_partitions];
//
// Initially, the status of each partition is set to Grovel_disable so
// that the close() member function of each Groveler object will not
// be called unless the open() function is called first.
//
for (index = 0; index < num_partitions; index++) { groveler_statuses[index] = Grovel_disable; }
//
// Initially, the read_disk_info[] and write_disk_info[] arrays are set
// to null, so that if an exception occurs, the code that deletes
// allocated objects can check for a null to determine whether or not
// the object has been instantiated.
//
read_disk_info = new ReadDiskInformation *[num_partitions]; ZeroMemory(read_disk_info, sizeof(ReadDiskInformation *) * num_partitions);
write_disk_info = new WriteDiskInformation *[num_partitions]; ZeroMemory(read_disk_info, sizeof(WriteDiskInformation *) * num_partitions);
//
// Now initilaize each partition
//
for (index = 0; index < num_partitions; index++) { read_disk_info[index] = new ReadDiskInformation(sis_drives.partition_guid_name(index));
write_disk_info[index] = new WriteDiskInformation(sis_drives.partition_guid_name(index),read_parameters.parameter_backup_interval);
if (read_disk_info[index]->enable_groveling) { groveler_statuses[index] = grovelers[index].open( sis_drives.partition_guid_name(index), sis_drives.partition_mount_name(index), index == log_drive->drive_index(), read_parameters.read_report_discard_threshold, read_disk_info[index]->min_file_size, read_disk_info[index]->min_file_age, read_disk_info[index]->allow_compressed_files, read_disk_info[index]->allow_encrypted_files, read_disk_info[index]->allow_hidden_files, read_disk_info[index]->allow_offline_files, read_disk_info[index]->allow_temporary_files, excluded_paths.num_paths[index], excluded_paths.paths[index], read_parameters.base_regrovel_interval, read_parameters.max_regrovel_interval);
ASSERT(groveler_statuses[index] != Grovel_disable); }
if (groveler_statuses[index] == Grovel_ok) { log_drive->partition_initialized(index); eventlog.report_event(GROVMSG_GROVELER_STARTED, 1, sis_drives.partition_mount_name(index)); } else if (groveler_statuses[index] == Grovel_disable) { eventlog.report_event(GROVMSG_GROVELER_DISABLED, 1, sis_drives.partition_mount_name(index)); } else if (groveler_statuses[index] != Grovel_new) { ASSERT(groveler_statuses[index] == Grovel_error); eventlog.report_event(GROVMSG_GROVELER_NOSTART, 1, sis_drives.partition_mount_name(index)); } }
//
// We have to pass a lot of information to the central controller that
// it shouldn't really need. However, if a groveler fails, this
// information is needed to restart it. It would be better if the
// Groveler open() member function had a form that did not require
// arguments, but rather re-used the arguments that had been passed in
// previously. But this is not how it currently works.
//
controller = new CentralController( num_partitions, grovelers, groveler_statuses, &read_parameters, &write_parameters, read_disk_info, write_disk_info, excluded_paths.num_paths, excluded_paths.paths);
SERVICE_RECORD_PARTITION_INDICES();
ASSERT(read_parameters.grovel_duration > 0);
SERVICE_SET_MAX_RESPONSE_TIME(read_parameters.grovel_duration);
//
// If any grovelers are alive, tell the service control manager that
// we have concluded the initialization, then commence running.
//
if (controller->any_grovelers_alive()) { event_timer.run(); }
//
// If tracing is being performed in delayed mode, print the trace log
// now that the run has completed.
//
PRINT_TRACE_LOG(); } catch (Exception exception) { switch (exception) { case exception_memory_allocation: eventlog.report_event(GROVMSG_MEMALLOC_FAILURE, 0); break;
case exception_create_event: eventlog.report_event(GROVMSG_CREATE_EVENT_FAILURE, 0); break;
default: eventlog.report_event(GROVMSG_UNKNOWN_EXCEPTION, 0); break; } exit_code = ERROR_EXCEPTION_IN_SERVICE; }
//
// If program tracing is being performed, and if the traces are being sent
// to a file, the file is closed. This call is made through a macro
// so that no code will be generated for released builds. Since the trace
// file is being closed before the objects are deleted, it is important
// not to write trace information in the destructor of an object.
//
CLOSE_TRACE_FILE();
//
// Close each groveler object that was opened.
//
if (groveler_statuses != 0 && grovelers != 0) { for (int index = 0; index < num_partitions; index++) { if (groveler_statuses[index] != Grovel_disable) { grovelers[index].close(); } } }
//
// Delete all objects that were allocated.
//
if (groveler_statuses != 0) { delete[] groveler_statuses; groveler_statuses = 0; }
if (grovelers != 0) { delete[] grovelers; grovelers = 0; }
if (read_disk_info != 0) { for (int index = 0; index < num_partitions; index++) { if (read_disk_info[index] != 0) { delete read_disk_info[index]; read_disk_info[index] = 0; } } delete[] read_disk_info; read_disk_info = 0; }
if (write_disk_info != 0) { for (int index = 0; index < num_partitions; index++) { if (write_disk_info[index] != 0) { delete write_disk_info[index]; write_disk_info[index] = 0; } } delete[] write_disk_info; write_disk_info = 0; }
if (controller != 0) { delete controller; controller = 0; }
if (shared_data != 0) { delete shared_data; shared_data = 0; }
if (log_drive != 0) { delete log_drive; log_drive = 0; }
eventlog.report_event(GROVMSG_SERVICE_STOPPED, 0); return exit_code; }
|