windows-nt/Source/XPSP1/NT/base/fs/sis/groveler/grovel.cpp

507 lines
15 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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;
}