849 lines
25 KiB
C++
849 lines
25 KiB
C++
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1998 Seagate Software, Inc. All rights reserved.
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
rslaunch.cpp
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
HSM Remote Storage Job Launch Program.
|
|||
|
|
|||
|
This program is used by the HSM Remote Storage system to submit
|
|||
|
user-requested jobs to the NT Task Scheduler. This standalone command
|
|||
|
line program has two primary functions: to start the HSM job specified
|
|||
|
and not return until the job has completed; and to call into the HSM
|
|||
|
Engine to either update secondary storage copy set media, or to
|
|||
|
re-create a master secondary storage media from its most recent copy.
|
|||
|
|
|||
|
NOTE: This program is linked as a windows program, but has no visible
|
|||
|
window. It creates an invisible window so it can get the WM_CLOSE
|
|||
|
message from the Task Scheduler if the user wants to cancel the job.
|
|||
|
|
|||
|
ALSO NOTE: This program has no correspoinding header file.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "stdafx.h"
|
|||
|
#include "windows.h"
|
|||
|
#include "stdio.h"
|
|||
|
|
|||
|
#include "wsb.h"
|
|||
|
#include "hsmeng.h"
|
|||
|
#include "fsa.h"
|
|||
|
#include "job.h"
|
|||
|
#include "rms.h"
|
|||
|
#include "hsmconn.h"
|
|||
|
|
|||
|
HINSTANCE g_hInstance;
|
|||
|
|
|||
|
//#define RSL_TRACE
|
|||
|
#if defined(RSL_TRACE)
|
|||
|
#define LTRACE(x) WsbTracef x
|
|||
|
#else
|
|||
|
#define LTRACE(x)
|
|||
|
#endif
|
|||
|
|
|||
|
#define TRACE_FILE L"RsLaunch.trc"
|
|||
|
#define WINDOW_CLASS L"RsLaunchWin"
|
|||
|
|
|||
|
// Typedefs
|
|||
|
typedef enum { // Type of work requested
|
|||
|
WORK_NONE,
|
|||
|
WORK_RUN,
|
|||
|
WORK_RECREATE,
|
|||
|
WORK_SYNCH
|
|||
|
} WORK_TYPE;
|
|||
|
|
|||
|
typedef struct { // For passing data to/from DoWork
|
|||
|
WCHAR * pCmdLine;
|
|||
|
WORK_TYPE wtype;
|
|||
|
HRESULT hr;
|
|||
|
IHsmJob * pJob;
|
|||
|
} DO_WORK_DATA;
|
|||
|
|
|||
|
// Global data
|
|||
|
CComModule _Module;
|
|||
|
|
|||
|
// Local data
|
|||
|
|
|||
|
// Local functions
|
|||
|
static HRESULT CancelWork(DO_WORK_DATA* pWork);
|
|||
|
static HRESULT ConnectToServer(IHsmServer** ppServer);
|
|||
|
static BOOL CreateOurWindow(HINSTANCE hInstance);
|
|||
|
static DWORD DoWork(void* pVoid);
|
|||
|
static HRESULT RecreateMaster(GUID oldMasterMediaId,
|
|||
|
OLECHAR* oldMasterMediaName, USHORT copySet);
|
|||
|
static void ReportError(HRESULT hr);
|
|||
|
static HRESULT RunJob(OLECHAR* jobName, IHsmJob** ppJob);
|
|||
|
static HRESULT SynchronizeMedia(OLECHAR* poolName, USHORT copySet);
|
|||
|
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
|
|||
|
LPARAM lParam);
|
|||
|
|
|||
|
|
|||
|
//****************************** Functions:
|
|||
|
|
|||
|
|
|||
|
static HRESULT
|
|||
|
CancelWork(
|
|||
|
IN DO_WORK_DATA* pWork
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Try to cancel the current work.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
S_OK - Success
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = E_FAIL;
|
|||
|
|
|||
|
LTRACE((L"CancelWork: entry\n"));
|
|||
|
|
|||
|
try {
|
|||
|
CComPtr<IHsmServer> pServer;
|
|||
|
|
|||
|
WsbAffirmHr(ConnectToServer(&pServer));
|
|||
|
|
|||
|
// Because of a possible timing problem, we may have to wait
|
|||
|
// for the operation to start before we can cancel it
|
|||
|
for (int i = 0; i < 60; i++) {
|
|||
|
if (WORK_RUN == pWork->wtype) {
|
|||
|
LTRACE((L"CancelWork: wtype = WORK_RUN, pJob = %p\n",
|
|||
|
pWork->pJob));
|
|||
|
if (pWork->pJob) {
|
|||
|
LTRACE((L"CancelWork: cancelling job\n"));
|
|||
|
hr = pWork->pJob->Cancel(HSM_JOB_PHASE_ALL);
|
|||
|
break;
|
|||
|
}
|
|||
|
} else {
|
|||
|
LTRACE((L"CancelWork: cancelling copy media operation\n"));
|
|||
|
if (S_OK == pServer->CancelCopyMedia()) {
|
|||
|
hr = S_OK;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Sleep(1000);
|
|||
|
}
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
|
|||
|
LTRACE((L"CancelWork: exit = %ls\n", WsbHrAsString(hr)));
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static HRESULT
|
|||
|
ConnectToServer(
|
|||
|
IN OUT IHsmServer** ppServer
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Connect to the server that will do the work.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ppServer - Pointer to pointer to server.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
S_OK - Success
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
|
|||
|
LTRACE((L"ConnectToServer: entry\n"));
|
|||
|
|
|||
|
try {
|
|||
|
CWsbStringPtr tmpString;
|
|||
|
|
|||
|
WsbAffirm(ppServer, E_POINTER);
|
|||
|
WsbAffirm(!(*ppServer), E_FAIL);
|
|||
|
|
|||
|
// Store of the name of the server.
|
|||
|
WsbAffirmHr( WsbGetComputerName( tmpString ) );
|
|||
|
|
|||
|
// Find the Hsm to get it's id.
|
|||
|
WsbAffirmHr(HsmConnectFromName(HSMCONN_TYPE_HSM, tmpString, IID_IHsmServer,
|
|||
|
(void**) ppServer));
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
|
|||
|
LTRACE((L"ConnectToServer: exit = %ls\n", WsbHrAsString(hr)));
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static BOOL
|
|||
|
CreateOurWindow(
|
|||
|
HINSTANCE hInstance
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Create our invisible window.
|
|||
|
|
|||
|
NOTE: If the Task Scheduler ever gets smarter and can send the WM_CLOSE
|
|||
|
message to an application without a window, the invisible window may
|
|||
|
not be needed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hInstance - Handle for this instance of the program.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE - Everything worked.
|
|||
|
|
|||
|
FALSE - Something went wrong.
|
|||
|
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
BOOL bRet = FALSE;
|
|||
|
WNDCLASS wc;
|
|||
|
|
|||
|
// Register our window type
|
|||
|
wc.style = 0;
|
|||
|
wc.lpfnWndProc = &WindowProc;
|
|||
|
wc.cbClsExtra = 0;
|
|||
|
wc.cbWndExtra = 0;
|
|||
|
wc.hInstance = hInstance;
|
|||
|
wc.hIcon = NULL;
|
|||
|
wc.hCursor = NULL;
|
|||
|
wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
|
|||
|
wc.lpszMenuName = NULL;
|
|||
|
wc.lpszClassName = WINDOW_CLASS;
|
|||
|
if (RegisterClass(&wc)) {
|
|||
|
|
|||
|
// Create the window (invisible by default)
|
|||
|
if (CreateWindowEx( 0,
|
|||
|
WINDOW_CLASS,
|
|||
|
L"RsLaunch",
|
|||
|
WS_OVERLAPPEDWINDOW,
|
|||
|
CW_USEDEFAULT,
|
|||
|
CW_USEDEFAULT,
|
|||
|
CW_USEDEFAULT,
|
|||
|
CW_USEDEFAULT,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
hInstance,
|
|||
|
NULL)) {
|
|||
|
bRet = TRUE;
|
|||
|
} else {
|
|||
|
LTRACE((L"CreateWindowEx failed\n"));
|
|||
|
}
|
|||
|
} else {
|
|||
|
LTRACE((L"RegisterClass failed\n"));
|
|||
|
}
|
|||
|
return(bRet);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static DWORD
|
|||
|
DoWork(
|
|||
|
IN void* pVoid
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Process the command line and start the processing.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
pVoid - A pointer (cast to void*) to a DO_WORK_DATA structure.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The return value from the job
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
DO_WORK_DATA * pWork;
|
|||
|
|
|||
|
LTRACE((L"DoWork: entry\n"));
|
|||
|
|
|||
|
pWork = static_cast<DO_WORK_DATA*>(pVoid);
|
|||
|
|
|||
|
try {
|
|||
|
WCHAR delims[] = L" \r\n\t\"";
|
|||
|
WCHAR delims2[] = L" \t";
|
|||
|
WCHAR delims3[] = L"\"";
|
|||
|
WCHAR * pToken;
|
|||
|
|
|||
|
WsbAssert(pWork, E_POINTER);
|
|||
|
WsbAssert(pWork->pCmdLine, E_POINTER);
|
|||
|
LTRACE((L"DoWork: CmdLine = %ls\n", pWork->pCmdLine));
|
|||
|
|
|||
|
// Validate we have a parameter
|
|||
|
pToken = wcstok(pWork->pCmdLine, delims);
|
|||
|
WsbAssert(pToken, E_INVALIDARG);
|
|||
|
|
|||
|
// What type of request is it?
|
|||
|
if (_wcsicmp(pToken, OLESTR("run")) == 0) {
|
|||
|
CWsbStringPtr jobName;
|
|||
|
|
|||
|
// 'run' option passed in
|
|||
|
pWork->wtype = WORK_RUN;
|
|||
|
|
|||
|
// The job name can have embedded spaces so it may be in quotes.
|
|||
|
// This means that using wcstok may not work correctly.
|
|||
|
pToken = pToken + wcslen(pToken) + 1; // Skip "run" & NULL
|
|||
|
pToken = pToken + wcsspn(pToken, delims2); // Skip spaces
|
|||
|
if (L'\"' == *pToken) {
|
|||
|
// Job name is in quotes
|
|||
|
jobName = wcstok(pToken, delims3);
|
|||
|
} else {
|
|||
|
jobName = wcstok(pToken, delims);
|
|||
|
}
|
|||
|
WsbAssert(jobName, E_INVALIDARG);
|
|||
|
LTRACE((L"DoWork: calling RunJob(%ls)\n", jobName));
|
|||
|
WsbAffirmHr(RunJob(jobName, &(pWork->pJob)));
|
|||
|
|
|||
|
} else if (_wcsicmp(pToken, OLESTR("sync")) == 0) {
|
|||
|
CWsbStringPtr poolName;
|
|||
|
USHORT copySet = 1;
|
|||
|
WCHAR * pTemp;
|
|||
|
|
|||
|
// 'sync' (update a copy set) option passed in
|
|||
|
pWork->wtype = WORK_SYNCH;
|
|||
|
pToken = wcstok(NULL, delims);
|
|||
|
WsbAssert(pToken, E_INVALIDARG);
|
|||
|
pTemp = wcstok(NULL, delims);
|
|||
|
if (!pTemp) {
|
|||
|
// will pass NULL for poolName if no pool name specified
|
|||
|
copySet = (USHORT) _wtoi(pToken);
|
|||
|
} else {
|
|||
|
poolName = pToken;
|
|||
|
copySet = (USHORT) _wtoi(pTemp);
|
|||
|
}
|
|||
|
|
|||
|
WsbAffirmHr(SynchronizeMedia(poolName, copySet));
|
|||
|
|
|||
|
} else if (_wcsicmp(pToken, OLESTR("recreate")) == 0) {
|
|||
|
USHORT copySet = 0;
|
|||
|
CWsbStringPtr mediaName;
|
|||
|
GUID mediaId = GUID_NULL;
|
|||
|
|
|||
|
// 'recreate' (re-create a master media) option passed in
|
|||
|
pWork->wtype = WORK_RECREATE;
|
|||
|
pToken = wcstok(NULL, delims);
|
|||
|
WsbAssert(pToken, E_INVALIDARG);
|
|||
|
if ( _wcsicmp(pToken, OLESTR("-i")) == 0 ) {
|
|||
|
|
|||
|
// media id was passed in, convert from string to GUID
|
|||
|
pToken = wcstok(NULL, delims);
|
|||
|
WsbAssert(pToken, E_INVALIDARG);
|
|||
|
WsbAffirmHr(WsbGuidFromString( pToken, &mediaId ));
|
|||
|
} else if ( _wcsicmp(pToken, OLESTR("-n")) == 0 ) {
|
|||
|
|
|||
|
// Media description (name) was passed in.
|
|||
|
// The function RecreateMaster() will look up its id (GUID).
|
|||
|
pToken = wcstok(NULL, delims);
|
|||
|
WsbAssert(pToken, E_INVALIDARG);
|
|||
|
mediaName = pToken;
|
|||
|
}
|
|||
|
|
|||
|
// Get copySet number
|
|||
|
pToken = wcstok(NULL, delims);
|
|||
|
if (pToken && _wcsicmp(pToken, OLESTR("-c")) == 0) {
|
|||
|
pToken = wcstok(NULL, delims);
|
|||
|
WsbAssert(pToken, E_INVALIDARG);
|
|||
|
copySet = (USHORT) _wtoi(pToken);
|
|||
|
}
|
|||
|
|
|||
|
WsbAffirmHr( RecreateMaster( mediaId, mediaName, copySet ));
|
|||
|
|
|||
|
} else {
|
|||
|
WsbThrow(E_INVALIDARG);
|
|||
|
}
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
if (pWork) {
|
|||
|
pWork->hr = hr;
|
|||
|
}
|
|||
|
|
|||
|
LTRACE((L"DoWork: exit = %ls\n", WsbHrAsString(hr)));
|
|||
|
return(static_cast<DWORD>(hr));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static HRESULT
|
|||
|
RecreateMaster(
|
|||
|
IN GUID oldMasterMediaId,
|
|||
|
IN OLECHAR* oldMasterMediaName,
|
|||
|
IN USHORT copySet
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine implements the method that will cause a Remote Storage master media
|
|||
|
to be re-created by calling the appropraite method on the Remote Storage engine.
|
|||
|
The master will be re-created from the specified copy or its most recent copy.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
oldMasterMediaId - The GUID of the current master media which is to be re-created.
|
|||
|
Normally passed, but an option exists where if the master's
|
|||
|
description is passed, the id (GUID) will be looked up by this
|
|||
|
method prior to invoking the engine. See below.
|
|||
|
|
|||
|
oldMasterMediaName - A wide character string representing the master media's
|
|||
|
description (display name). If this argument is passed with a valid
|
|||
|
string, the string is used to look up the oldMasterMediaId above.
|
|||
|
|
|||
|
copySet - The copyset number of the copy to use for the recreation or zero, which
|
|||
|
indicates that the Engine should just use the most recent copy
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
S_OK - The call succeeded (the specified master was re-created).
|
|||
|
|
|||
|
E_FAIL - Could not get host computer's name (highly unexpected error).
|
|||
|
|
|||
|
E_UNEXPECTED - The argument 'oldMasterMediaId' equaled GUID_NULL just prior to
|
|||
|
calling the HSM Engine to re-create a media master. This argument should
|
|||
|
either be received with a valid value (the norm), or it should be set by this
|
|||
|
method if a valid media description was passed in as 'oldMasterMediaName'.
|
|||
|
|
|||
|
Any other value - The call failed because one of the Remote Storage API calls
|
|||
|
contained internally in this method failed. The error value returned is
|
|||
|
specific to the API call which failed.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
|
|||
|
try {
|
|||
|
CComPtr<IHsmServer> pServer;
|
|||
|
|
|||
|
WsbAffirmHr(ConnectToServer(&pServer));
|
|||
|
|
|||
|
// If we were passed a media name, find its id. Since the name option is
|
|||
|
// presently only used internally and it bypasses the UI, also mark
|
|||
|
// the media record for re-creation (normally done by the UI) otherwise
|
|||
|
// the RecreateMaster() call below will fail.
|
|||
|
|
|||
|
// If the string is not null...
|
|||
|
if ( oldMasterMediaName != 0 ) {
|
|||
|
// and if the 1st character of the string is not the null terminator
|
|||
|
if ( *oldMasterMediaName != 0 ) {
|
|||
|
WsbAffirmHr(pServer->FindMediaIdByDescription(oldMasterMediaName,
|
|||
|
&oldMasterMediaId));
|
|||
|
WsbAffirmHr(pServer->MarkMediaForRecreation(oldMasterMediaId));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Ensure we have a non-null media id
|
|||
|
WsbAffirm( oldMasterMediaId != GUID_NULL, E_UNEXPECTED );
|
|||
|
|
|||
|
// Re-create the master media.
|
|||
|
WsbAffirmHr(pServer->RecreateMaster( oldMasterMediaId, copySet ));
|
|||
|
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static void
|
|||
|
ReportError(
|
|||
|
IN HRESULT hr
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Report errors.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
hr - The error.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
CWsbStringPtr BoxTitle;
|
|||
|
CWsbStringPtr BoxString;
|
|||
|
CWsbStringPtr BoxString1;
|
|||
|
CWsbStringPtr BoxString2;
|
|||
|
CWsbStringPtr BoxString3;
|
|||
|
CWsbStringPtr BoxString4;
|
|||
|
CWsbStringPtr BoxString5;
|
|||
|
CWsbStringPtr BoxString6;
|
|||
|
BOOL displayMsg = FALSE;
|
|||
|
UINT style = MB_OK;
|
|||
|
|
|||
|
#if DBG
|
|||
|
if (E_INVALIDARG == hr) {
|
|||
|
// If this is a Debug build then command line invocation is allowed.
|
|||
|
// (Debug build implies Development/Test usage.)
|
|||
|
// Tell them the valid command lines. Since this program, originally
|
|||
|
// written as a console app, is now linked as a Windows program, pop
|
|||
|
// this up as a message box.
|
|||
|
|
|||
|
// define the lines of text to appear in the message box
|
|||
|
BoxString = L"Remote Storage Launch Program\r\n";
|
|||
|
BoxString1 = L"allowable command line options:\r\n\n";
|
|||
|
BoxString2 = L" RSLAUNCH run <job name>\r\n";
|
|||
|
BoxString3 = L" RSLAUNCH sync <copyset number>\r\n";
|
|||
|
BoxString4 = L" RSLAUNCH sync <pool name> <copyset number>\r\n";
|
|||
|
BoxString5 = L" RSLAUNCH recreate -i <media id> [-c <copyset number>]\r\n";
|
|||
|
BoxString6 = L" RSLAUNCH recreate -n <media name> [-c <copyset number>]\r\n";
|
|||
|
|
|||
|
// display the Help message box
|
|||
|
style = MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND;
|
|||
|
displayMsg = TRUE;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
// message box text lines
|
|||
|
BoxString = L"An error occurred while Remote Storage Launch was launching a job.\n";
|
|||
|
BoxString1 = WsbHrAsString(hr);
|
|||
|
|
|||
|
// display the Error message box
|
|||
|
style = MB_OK | MB_ICONERROR | MB_TOPMOST;
|
|||
|
displayMsg = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
#else
|
|||
|
if (E_INVALIDARG == hr) {
|
|||
|
// error message box if the Release version:
|
|||
|
|
|||
|
// message box text lines
|
|||
|
BoxString.LoadFromRsc( g_hInstance, IDS_INVALID_PARAMETER );
|
|||
|
|
|||
|
// display the Error message box
|
|||
|
style = MB_OK | MB_ICONERROR | MB_SETFOREGROUND;
|
|||
|
displayMsg = TRUE;
|
|||
|
}
|
|||
|
#endif // DBG
|
|||
|
|
|||
|
if (displayMsg) {
|
|||
|
// concatenate all text lines
|
|||
|
BoxString.Append( BoxString1 );
|
|||
|
BoxString.Append( BoxString2 );
|
|||
|
BoxString.Append( BoxString3 );
|
|||
|
BoxString.Append( BoxString4 );
|
|||
|
BoxString.Append( BoxString5 );
|
|||
|
BoxString.Append( BoxString6 );
|
|||
|
WsbAffirm(0 != (WCHAR *)BoxString, E_OUTOFMEMORY);
|
|||
|
|
|||
|
// message box title line
|
|||
|
WsbAffirmHr(BoxTitle.LoadFromRsc( g_hInstance, IDS_APPLICATION_TITLE ));
|
|||
|
|
|||
|
// display the Help message box
|
|||
|
MessageBox( NULL, BoxString, BoxTitle, style);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
static HRESULT
|
|||
|
RunJob(
|
|||
|
IN OLECHAR* jobName,
|
|||
|
OUT IHsmJob** ppJob
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine implements the method for running a Remote Storage job.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
jobName - A wide character string containing the name of the job to run.
|
|||
|
|
|||
|
ppJob - Pointer to pointer to Job interface obtained from the server
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
S_OK - The call succeeded (the specified job ran successfully).
|
|||
|
|
|||
|
E_POINTER - Input argument 'jobName' is null.
|
|||
|
|
|||
|
E_FAIL - Used to indicate 2 error conditions:
|
|||
|
1. could not get host computer's name (highly unexpected error);
|
|||
|
2. the job run by this method returned an HRESULT other than S_OK.
|
|||
|
|
|||
|
Any other value - The call failed because one of the Remote Storage API calls
|
|||
|
contained internally in this method failed. The error value returned is
|
|||
|
specific to the API call which failed.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
|
|||
|
try {
|
|||
|
CComPtr<IHsmServer> pServer;
|
|||
|
|
|||
|
WsbAssert(0 != jobName, E_POINTER);
|
|||
|
WsbAssert(ppJob, E_POINTER);
|
|||
|
WsbAffirmHr(ConnectToServer(&pServer));
|
|||
|
|
|||
|
// Find the job, start the job, wait for the job to complete.
|
|||
|
WsbAffirmHr(pServer->FindJobByName(jobName, ppJob));
|
|||
|
WsbAffirmHr((*ppJob)->Start());
|
|||
|
WsbAffirmHr((*ppJob)->WaitUntilDone());
|
|||
|
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
static HRESULT
|
|||
|
SynchronizeMedia(
|
|||
|
IN OLECHAR* poolName,
|
|||
|
IN USHORT copySet
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine implements the method that will cause the updating (synchronizing) of
|
|||
|
an entire copy set by calling the appropriate method on the Remote Storage engine.
|
|||
|
Specifically, this method causes all copy media belonging to a specified copy set
|
|||
|
to be checked for synchronization (being up to date) with each of their respective
|
|||
|
master media. Those out of date will be brought up to date. Running this method
|
|||
|
assumes that Remote Storage has already been configured for a certain number of
|
|||
|
copy sets.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
poolName - A wide character string containing the name of a specific storage pool
|
|||
|
that the user wants the specified copy set synchronized for. If this
|
|||
|
argument is passed as NULL then all storage pools will have the specified
|
|||
|
copy set synchronized.
|
|||
|
|
|||
|
copySet - A number indicating which copy set is to be synchronized.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
S_OK - The call succeeded (the specified copy set of the specified storage pool was
|
|||
|
updated).
|
|||
|
|
|||
|
E_FAIL - Could not get host computer's name (highly unexpected error).
|
|||
|
|
|||
|
Any other value - The call failed because one of the Remote Storage API calls
|
|||
|
contained internally in this method failed. The error value returned is
|
|||
|
specific to the API call which failed.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
GUID poolId = GUID_NULL;
|
|||
|
CComPtr<IHsmStoragePool> pPool;
|
|||
|
|
|||
|
try {
|
|||
|
CComPtr<IHsmServer> pServer;
|
|||
|
|
|||
|
WsbAffirmHr(ConnectToServer(&pServer));
|
|||
|
|
|||
|
// If they specified a pool, then find it's id.
|
|||
|
if ( poolName != 0 ) {
|
|||
|
if ( *poolName != 0 ) {
|
|||
|
CWsbStringPtr tmpString;
|
|||
|
|
|||
|
WsbAffirmHr(pServer->FindStoragePoolByName(poolName, &pPool));
|
|||
|
WsbAffirmHr(pPool->GetMediaSet(&poolId, &tmpString));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Synchronize the media. Note that if no pool name was passed in, we pass
|
|||
|
// GUID_NULL as the pool id.
|
|||
|
WsbAffirmHr(pServer->SynchronizeMedia(poolId, copySet));
|
|||
|
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
// WindowProc - Needed for our invisible window
|
|||
|
static LRESULT CALLBACK
|
|||
|
WindowProc(
|
|||
|
HWND hwnd,
|
|||
|
UINT uMsg,
|
|||
|
WPARAM wParam,
|
|||
|
LPARAM lParam
|
|||
|
)
|
|||
|
{
|
|||
|
LTRACE((L"WindowProc: msg = %4.4x\n", uMsg));
|
|||
|
return(DefWindowProc(hwnd, uMsg, wParam, lParam));
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//****************************** MAIN *********************************
|
|||
|
|
|||
|
extern "C"
|
|||
|
int WINAPI wWinMain(HINSTANCE hInstance,
|
|||
|
HINSTANCE /*hPrevInstance*/,
|
|||
|
LPTSTR lpCmdLine,
|
|||
|
int /*nShowCmd*/
|
|||
|
)
|
|||
|
{
|
|||
|
HRESULT hr = S_OK;
|
|||
|
#if defined(RSL_TRACE)
|
|||
|
CComPtr<IWsbTrace> pTrace;
|
|||
|
#endif
|
|||
|
|
|||
|
// Store our instance handle so it can be used by called code.
|
|||
|
g_hInstance = hInstance;
|
|||
|
|
|||
|
try {
|
|||
|
HANDLE hJobThread[1] = { NULL };
|
|||
|
DO_WORK_DATA workData = { NULL, WORK_NONE, E_FAIL, NULL };
|
|||
|
|
|||
|
// Register & create our invisible window
|
|||
|
WsbAssert(CreateOurWindow(hInstance), E_FAIL);
|
|||
|
|
|||
|
// Initialize COM
|
|||
|
WsbAffirmHr(CoInitializeEx(NULL, COINIT_MULTITHREADED));
|
|||
|
|
|||
|
// This provides a NULL DACL which will allow access to everyone.
|
|||
|
CSecurityDescriptor sd;
|
|||
|
sd.InitializeFromThreadToken();
|
|||
|
WsbAffirmHr(CoInitializeSecurity(sd, -1, NULL, NULL,
|
|||
|
RPC_C_AUTHN_LEVEL_CONNECT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL,
|
|||
|
EOAC_NONE, NULL));
|
|||
|
|
|||
|
try {
|
|||
|
DWORD ThreadId = 0;
|
|||
|
|
|||
|
#if defined(RSL_TRACE)
|
|||
|
// Start tracing
|
|||
|
CoCreateInstance(CLSID_CWsbTrace, 0, CLSCTX_SERVER, IID_IWsbTrace,
|
|||
|
(void **) &pTrace);
|
|||
|
pTrace->DirectOutput(WSB_TRACE_OUT_DEBUG_SCREEN | WSB_TRACE_OUT_FILE);
|
|||
|
pTrace->SetTraceFileControls(TRACE_FILE, FALSE, 3000000, NULL);
|
|||
|
pTrace->SetOutputFormat(TRUE, TRUE, TRUE);
|
|||
|
pTrace->SetTraceSettings(0xffffffffffffffffL);
|
|||
|
pTrace->StartTrace();
|
|||
|
#endif
|
|||
|
|
|||
|
LTRACE((L"Main: lpCmdLine = %ls\n", lpCmdLine));
|
|||
|
workData.pCmdLine = lpCmdLine;
|
|||
|
|
|||
|
// Create a thread to start the work and wait for it
|
|||
|
// to finish
|
|||
|
LTRACE((L"Main: creating thread for DoWork\n"));
|
|||
|
hJobThread[0] = CreateThread(0, 0, DoWork,
|
|||
|
static_cast<void*>(&workData), 0, &ThreadId);
|
|||
|
if (!hJobThread[0]) {
|
|||
|
LTRACE((L"Main: CreateThread failed\n"));
|
|||
|
WsbThrow(HRESULT_FROM_WIN32(GetLastError()));
|
|||
|
}
|
|||
|
|
|||
|
// Don't exit if we're waiting for work to complete
|
|||
|
while (TRUE) {
|
|||
|
DWORD exitcode;
|
|||
|
DWORD waitStatus;
|
|||
|
|
|||
|
// Wait for a message or thread to end
|
|||
|
LTRACE((L"Main: waiting for multiple objects\n"));
|
|||
|
waitStatus = MsgWaitForMultipleObjects(1, hJobThread, FALSE,
|
|||
|
INFINITE, QS_ALLINPUT);
|
|||
|
|
|||
|
// Find out which event happened
|
|||
|
if (WAIT_OBJECT_0 == waitStatus) {
|
|||
|
|
|||
|
// The thread ended; get it's exit code
|
|||
|
LTRACE((L"Main: got event on thread\n"));
|
|||
|
if (GetExitCodeThread(hJobThread[0], &exitcode)) {
|
|||
|
if (STILL_ACTIVE == exitcode) {
|
|||
|
// This shouldn't happen; don't know what to do!
|
|||
|
} else {
|
|||
|
WsbThrow(static_cast<HRESULT>(exitcode));
|
|||
|
}
|
|||
|
} else {
|
|||
|
WsbThrow(HRESULT_FROM_WIN32(GetLastError()));
|
|||
|
}
|
|||
|
|
|||
|
} else if ((WAIT_OBJECT_0 + 1) == waitStatus) {
|
|||
|
|
|||
|
// Message in queue
|
|||
|
MSG msg;
|
|||
|
|
|||
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
|||
|
LTRACE((L"Main: message = %4.4x\n", msg.message));
|
|||
|
if (WM_CLOSE == msg.message) {
|
|||
|
|
|||
|
// Cancel the job since someone cancelled us.
|
|||
|
// (Should we kill the thread that is waiting?)
|
|||
|
LTRACE((L"Main: got WM_CLOSE\n"));
|
|||
|
WsbThrow(CancelWork(&workData));
|
|||
|
}
|
|||
|
DispatchMessage(&msg);
|
|||
|
}
|
|||
|
|
|||
|
} else if (0xFFFFFFFF == waitStatus) {
|
|||
|
|
|||
|
// Error in MsgWaitForMultipleObjects
|
|||
|
WsbThrow(HRESULT_FROM_WIN32(GetLastError()));
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
// This shouldn't happend; don't know what to do
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
if (hJobThread[0]) {
|
|||
|
CloseHandle(hJobThread[0]);
|
|||
|
}
|
|||
|
|
|||
|
if (workData.pJob) {
|
|||
|
workData.pJob->Release();
|
|||
|
}
|
|||
|
|
|||
|
// Cleanup COM
|
|||
|
CoUninitialize();
|
|||
|
|
|||
|
} WsbCatch(hr);
|
|||
|
|
|||
|
LTRACE((L"Main: exit hr = %ls\n", WsbHrAsString(hr)));
|
|||
|
if (FAILED(hr)) {
|
|||
|
ReportError(hr);
|
|||
|
}
|
|||
|
|
|||
|
return(hr);
|
|||
|
}
|
|||
|
|
|||
|
|