|
|
// Active Directory Display Specifier Upgrade Tool
//
// Copyright (c) 2001 Microsoft Corporation
//
// class Analyst: analyzes the display specifiers, logs the findings, and
// compiles a set of corrective actions.
//
// 9 Mar 2001 sburns
#include "headers.hxx"
#include "resource.h"
#include "AdsiHelpers.hpp"
#include "Analyst.hpp"
#include "Amanuensis.hpp"
#include "Repairer.hpp"
#include "ChangedObjectHandlerList.hpp"
#include "ChangedObjectHandler.hpp"
Analyst::Analyst( const String& targetDomainControllerName, Amanuensis& amanuensis_, Repairer& repairer_) : targetDcName(targetDomainControllerName), ldapPrefix(), rootDse(0), // alias the objects
amanuensis(amanuensis_), repairer(repairer_) { LOG_CTOR(Analyst); ASSERT(!targetDcName.empty());
}
// basic idea: if the error is critical and analysis should not continue, set
// hr to a failure value, and break out, propagating the error backward. If
// the error is non-critical and analysis should continue, log the error, skip
// the current operation, and set hr to S_FALSE.
HRESULT AssessErrorSeverity(HRESULT hrIn) { HRESULT hr = hrIn; if (SUCCEEDED(hr)) { return hr; } switch (hr) { case 0: { } // CODEWORK: we need to define what errors are critical...
default: { // do nothing
break; } }
return hr; }
HRESULT Analyst::AnalyzeDisplaySpecifiers() { LOG_FUNCTION(Analyst::AnalyzeDisplaySpecifiers);
HRESULT hr = S_OK;
do { Computer targetDc(targetDcName); hr = targetDc.Refresh();
if (FAILED(hr)) { amanuensis.AddErrorEntry( hr, String::format( IDS_CANT_TARGET_MACHINE, targetDcName.c_str())); break; }
if (!targetDc.IsDomainController()) { amanuensis.AddEntry( String::format( IDS_TARGET_IS_NOT_DC, targetDcName.c_str())); break; }
String dcName = targetDc.GetActivePhysicalFullDnsName(); ldapPrefix = L"LDAP://" + dcName + L"/"; //
// Find the DN of the configuration container.
//
// Bind to the rootDSE object. We will keep this binding handle
// open for the duration of the analysis and repair phases in order
// to keep a server session open. If we decide to pass creds to the
// AdsiOpenObject call in a later revision, then by keeping the
// session open we will not need to pass the password to subsequent
// AdsiOpenObject calls.
hr = AdsiOpenObject<IADs>(ldapPrefix + L"RootDSE", rootDse); if (FAILED(hr)) { amanuensis.AddErrorEntry( hr, String::format( IDS_UNABLE_TO_CONNECT_TO_DC, dcName.c_str())); break; } // read the configuration naming context.
_variant_t variant; hr = rootDse->Get( AutoBstr(LDAP_OPATT_CONFIG_NAMING_CONTEXT_W), &variant); if (FAILED(hr)) { LOG(L"can't read config NC"); amanuensis.AddErrorEntry( hr, IDS_UNABLE_TO_READ_DIRECTORY_INFO); break; }
String configNc = V_BSTR(&variant);
LOG(configNc); ASSERT(!configNc.empty());
//
// Here we go...
//
hr = AnalyzeDisplaySpecifierContainers(configNc); BREAK_ON_FAILED_HRESULT(hr); } while (0);
LOG_HRESULT(hr);
return hr; }
HRESULT Analyst::AnalyzeDisplaySpecifierContainers(const String& configurationDn) { LOG_FUNCTION2(Analyst::AnalyzeDisplaySpecifierContainers, configurationDn); ASSERT(!configurationDn.empty());
HRESULT hr = S_OK; static const int LOCALEIDS[] = { // a list of all the non-english locale IDs that we support
0x401, 0x404, 0x405, 0x406, 0x407, 0x408, 0x40b, 0x40c, 0x40d, 0x40e, 0x410, 0x411, 0x412, 0x413, 0x414, 0x415, 0x416, 0x419, 0x41d, 0x41f, 0x804, 0x816, 0xc0a, 0 };
// compose the LDAP path of the display specifiers container
String rootContainerDn = L"CN=DisplaySpecifiers," + configurationDn;
for ( int i = 0; (i < sizeof(LOCALEIDS) / sizeof(int)) && LOCALEIDS[i]; ++i) { hr = AnalyzeDisplaySpecifierContainer(LOCALEIDS[i], rootContainerDn); BREAK_ON_FAILED_HRESULT(hr); }
LOG_HRESULT(hr);
return hr; }
HRESULT Analyst::AnalyzeDisplaySpecifierContainer( int localeId, const String& rootContainerDn) { LOG_FUNCTION2( Analyst::AnalyzeDisplaySpecifierContainer, rootContainerDn); ASSERT(!rootContainerDn.empty()); ASSERT(localeId);
HRESULT hr = S_OK;
do { String childContainerDn = ldapPrefix + String::format(L"CN=%1!3x!,", localeId) + rootContainerDn;
// Attempt to bind to the container.
SmartInterface<IADs> iads(0); hr = AdsiOpenObject<IADs>(childContainerDn, iads); if (hr == E_ADS_UNKNOWN_OBJECT) { // The container object does not exist. This is possible because
// the user has manually removed the container, or because it
// was never created due to an aboted post-dcpromo import of the
// display specifiers when the forest root dc was first promoted.
repairer.AddCreateContainerWorkItem(localeId); hr = S_OK; break; }
BREAK_ON_FAILED_HRESULT(hr);
// At this point, the bind succeeded, so the child container exists.
// So now we want to examine objects in that container.
hr = AnalyzeDisplaySpecifierObjects( localeId, childContainerDn); } while (0);
LOG_HRESULT(hr);
hr = AssessErrorSeverity(hr); return hr; }
HRESULT Analyst::AnalyzeDisplaySpecifierObjects( int localeId, const String& containerDn) { LOG_FUNCTION2(Analyst::AnalyzeDisplaySpecifierObjects, containerDn); ASSERT(localeId); ASSERT(!containerDn.empty());
HRESULT hr = S_OK;
do { // Part 1: deal with new objects added in Whistler
hr = AnalyzeAddedObjects(localeId, containerDn); hr = AssessErrorSeverity(hr); BREAK_ON_FAILED_HRESULT(hr);
// Part 2: deal with objects that have changed from Win2k to Whistler
hr = AnalyzeChangedObjects(localeId, containerDn); hr = AssessErrorSeverity(hr); BREAK_ON_FAILED_HRESULT(hr); // Part 3: deal with objects that have been deleted in whistler
// This part is easy: there are no deletions.
} while (0);
LOG_HRESULT(hr);
return hr; }
bool RepairWasRunPreviously() { LOG_FUNCTION(RepairWasRunPreviously);
bool result = false; // CODEWORK: need to complete
LOG_BOOL(result); return result; }
HRESULT Analyst::AnalyzeAddedObjects( int localeId, const String& containerDn) { LOG_FUNCTION2(Analyst::AnalyzeAddedObjects, containerDn); ASSERT(localeId); ASSERT(!containerDn.empty());
HRESULT hr = S_OK;
do { static const String ADDED_OBJECTS[] = { L"msMQ-Custom-Recipient-Display", L"msMQ-Group-Display", L"msCOM-PartitionSet-Display", L"msCOM-Partition-Display", L"lostAndFound-Display", L"inetOrgPerson-Display", L"", };
for ( int i = 0; i < (sizeof(ADDED_OBJECTS) / sizeof(String)) && !ADDED_OBJECTS[i].empty(); ++i) { String objectName = ADDED_OBJECTS[i]; String objectPath = ldapPrefix + L"CN=" + objectName + L"," + containerDn;
SmartInterface<IADs> iads(0); hr = AdsiOpenObject<IADs>(objectPath, iads); if (hr == E_ADS_UNKNOWN_OBJECT) { // The object does not exist. This is what we expect. We want
// to add the object in the repair phase.
repairer.AddCreateObjectWorkItem(localeId, objectName); hr = S_OK; continue; } else if (SUCCEEDED(hr)) { // The object already exists. Well, that's not expected, unless
// we've already run the tool.
if (!RepairWasRunPreviously()) { // we didn't create the object. If the user did, they did
// it manually, and we don't support that.
// cause the existing object to be deleted
repairer.AddDeleteObjectWorkItem(localeId, objectName);
// cause a new, replacement object to be created.
repairer.AddCreateObjectWorkItem(localeId, objectName); hr = S_OK; continue; } } else { ASSERT(FAILED(hr));
LOG(L"Unexpected error attempting to bind to " + objectName);
amanuensis.AddErrorEntry( hr, String::format( IDS_ERROR_BINDING_TO_OBJECT, objectName.c_str(), objectPath.c_str())); // move on to the next object
hr = S_FALSE; continue; } } BREAK_ON_FAILED_HRESULT(hr); } while (0); LOG_HRESULT(hr);
return hr; }
HRESULT Analyst::AnalyzeChangedObjects( int localeId, const String& containerDn) { LOG_FUNCTION2(Analyst::AnalyzeChangedObjects, containerDn); ASSERT(localeId); ASSERT(!containerDn.empty());
HRESULT hr = S_OK;
static const ChangedObjectHandlerList handlers; for ( ChangedObjectHandlerList::iterator i = handlers.begin(); i != handlers.end(); ++i) { hr = AnalyzeChangedObject(localeId, containerDn, **i); hr = AssessErrorSeverity(hr); BREAK_ON_FAILED_HRESULT(hr); }
LOG_HRESULT(hr);
return hr; }
HRESULT Analyst::AnalyzeChangedObject( int localeId, const String& containerDn, const ChangedObjectHandler& changeHandler) { LOG_FUNCTION2(Analyst::AnalyzeChangedObject, changeHandler.GetObjectName()); ASSERT(localeId); ASSERT(!containerDn.empty());
HRESULT hr = S_OK;
do { String objectName = changeHandler.GetObjectName(); String objectPath = ldapPrefix + L"CN=" + objectName + L"," + containerDn;
SmartInterface<IADs> iads(0); hr = AdsiOpenObject<IADs>(objectPath, iads); if (hr == E_ADS_UNKNOWN_OBJECT) { // The object does not exist. This is possible because the user has
// manually removed the container, or because it was never created
// due to an aboted post-dcpromo import of the display specifiers
// when the forest root dc was first promoted.
// Add a work item to create the missing object
repairer.AddCreateObjectWorkItem(localeId, objectName); hr = S_OK; break; }
if (FAILED(hr)) { // any other error is quittin' time.
break; }
// At this point, the display specifier object exists. Determine if
// if has been touched since its creation.
// Compare usnCreated to usnChanged
_variant_t variant; hr = iads->Get(AutoBstr(L"usnCreated"), &variant); if (FAILED(hr)) { LOG(L"Error reading usnCreated"); break; }
// CODEWORK: need to complete this
hr = changeHandler.HandleChange( localeId, containerDn, iads, amanuensis, repairer); } while (0);
LOG_HRESULT(hr);
return hr; }
|