SRVINST.CPP

//************************************************************************** 
// Copyright (C) 1995 by Microsoft Corporation.
//
// This is a sample program for the SMS SDK. It illustrates how to create
// a server install job and monitor its status until the job completes.
//
// This sample program also illustrates how to create a run command on
// workstation job.
//
// The program will request the following input:
//
// Share package on workstation job:
// Package name = User defined name of the package
// Package comment = User defined text
// Package location (UNC path) = The pathname to the directory where the
// package files are located.
// Package share name = The name that the package will be shared as on the
// server.
// Program item description = User defined text
// Program item command line = The command to execute on the server.
// Program item registry name = User defined registry key for the package
//
// Run command on workstation job.
// Package name = User defined name of the package
// Package comment = User defined text
// Package location (share name) = The pathname to the directory where the
// client package is located.
// WCL Name = User defined name for the workstation command line
// WCL Command = The command line to execute.
//
// NOTE: If you choose to create a share package on server job, then you must
// first share the package directory. If you choose to create a run
// command on workstation job, then you must first share the directory
// where the package files are located.
//
//*****************************************************************************


#include <afx.h>
#include <smsapi.h>
#include <smsinfo.h>

// Include the GetStatusName function.
// -------------------------------------
#include "..\common\status.inc"


// ====================================================================
// Defines.
// ====================================================================

#define CCH_MAXSTRING 256
#define CCH_MAXINPUT 256
#define C_MAXSITES 256

#define SHARE_PERMISSIONS (PACKAGE_USER_READ | PACKAGE_USER_WRITE | PACKAGE_GUEST_READ)


extern const char *GetStatusName( SMS_STATUS stat );

enum {CMD_RUN_COMMAND, CMD_SHARE_PACKAGE, CMD_QUIT};




// ====================================================================
// TClientParams
//
// This typedef defines the parameters for the client setup
// job that are collected from the user.
// ====================================================================
typedef struct
{
char szPackageName[CCH_MAXSTRING];
char szPackageComment[CCH_MAXSTRING];
char szPackagePath[CCH_MAXSTRING];
char szWclName[CCH_MAXSTRING];
char szWclCommand[CCH_MAXSTRING];
char szSiteName[CCH_MAXSTRING];
DWORD dwPlatforms;
} TClientParams;


// ====================================================================
// TServerParams
//
// This typedef defines the parameters for the share package on
// workstation job that are collected from the user.
// ====================================================================
typedef struct
{
char szPackageName[CCH_MAXSTRING];
char szPackageComment[CCH_MAXSTRING];
char szPackagePath[CCH_MAXSTRING];
char szShareName[CCH_MAXSTRING];
char szDescription[CCH_MAXSTRING];
char szCmdLine[CCH_MAXSTRING];
char szRegistryName[CCH_MAXSTRING];
char szSiteName[CCH_MAXSTRING];
DWORD dwPlatforms;

} TServerParams;



// ====================================================================
//
// Local prototypes.
//
// ====================================================================

void DisplayHelp();

void DisplayGreeting();

BOOL DidRequestHelp( int argc, char** argv );

void DisplaySmsError( const char *pszMessage, SMS_STATUS stat );

const char *GetStatusName( SMS_STATUS stat );

BOOL DidRequestHelp( int argc, char** argv );

HANDLE ConnectToDatasource();

void InputString( const char *pszMessage, char *pszResult );

void GetSiteName( HANDLE hConnect, char *pszSiteName );

void ClientDoRunCommand( HANDLE hConnect, TClientParams *pcp );

DWORD GetPlatformSelection();

void ServerDoShare( HANDLE hConnect, TServerParams *psp );

int GetJobType();


SMS_STATUS ClientJobCreate( HANDLE hConnect,
TClientParams *pcp,
char *pszPackageID,
char *pszWCLID );


// ====================================================================
//
// The work starts here.
//
// ====================================================================
void main( int argc, char **argv )
{

// Check to see if this is a request to display the help
// screen. If so, display it. Otherwise, display a greeting
// banner.
//=========================================================
if (DidRequestHelp(argc, argv)) {
DisplayHelp();
return;
}
else {
DisplayGreeting();
}

HANDLE hConnect = ConnectToDatasource();
if (hConnect == NULL) {
return;
}


char szSiteName[CCH_MAXSTRING];
GetSiteName(hConnect, szSiteName);

DWORD dwPlatforms = 0;
while (dwPlatforms == 0) {
dwPlatforms = GetPlatformSelection();
if (dwPlatforms == 0) {
printf("Error: Invalid platform selection\n");
}
}




switch(GetJobType()) {
case CMD_SHARE_PACKAGE:

printf("***************************************\n");
printf("* Share package on server job. *\n");
printf("***************************************\n");
TServerParams sp;
strcpy(sp.szSiteName, szSiteName);
sp.dwPlatforms = dwPlatforms;
InputString("Package name", sp.szPackageName);
InputString("Package comment", sp.szPackageComment);
InputString("Package directory location (UNC path)", sp.szPackagePath);
InputString("Package share name", sp.szShareName);
InputString("Program item description", sp.szDescription);
InputString("Program item command line", sp.szCmdLine);
InputString("Program item registry name", sp.szRegistryName);
printf("\n");

// First share the package on the server
//=======================================
ServerDoShare(hConnect, &sp);

printf("**************************************************\n");
printf("* The server install job has completed. To *\n");
printf("* use the shared package please create a program *\n");
printf("* group in the SMS administration program and add*\n");
printf("* the package that was just shared to the group. *\n");
printf("* *\n");
printf("* For more information, please refer to chapter *\n");
printf("* nine of the SMS Administrator's guide. *\n");
printf("**************************************************\n");
printf("\n");
break;

case CMD_RUN_COMMAND:

printf("************************************************\n");
printf("* Run command on workstation job. *\n");
printf("************************************************\n");
TClientParams cp;
strcpy(cp.szSiteName, szSiteName);
cp.dwPlatforms = dwPlatforms;
InputString("Package name", cp.szPackageName);
InputString("Package directory location (UNC path)", cp.szPackagePath);
InputString("Package comment", cp.szPackageComment);
InputString("WCL Name", cp.szWclName);
InputString("WCL Command", cp.szWclCommand);
printf("\n");


// Now that we've shared the package on the server,
// submit a job to setup the client program.
//=================================================
ClientDoRunCommand(hConnect, &cp);
break;
default:
printf("Invalid job type. No job submitted.\n");
break;
}

if (hConnect != NULL) {
SmsDataSourceDisconnect( hConnect );
}
}






// ====================================================================
//
// SetScalarString
//
// This function sets the value specified scalar to the given string
// value.
//
// Parameters:
// HANDLE hFolder
// The handle of the folder containing the scalar to modify.
// char *pszName
// The name of the scalar.
// char *pszValue
// The string that will be assigned to the scalar's value.
//
// Returns:
// SMS_STATUS
// The SMS status code.
//
// Note that the scalar specified must be of type SCALAR_STRING.
//
// ====================================================================
SMS_STATUS SetScalarString( HANDLE hFolder, char *pszName, char *pszValue )
{
SCALAR sc;
sc.pszName = pszName;
sc.scType = SCALAR_STRING;
sc.pszValue = pszValue;

SMS_STATUS stat;
stat = SmsSetScalar(hFolder, &sc);
return stat;
}


// ====================================================================
//
// SetScalarBool
//
// This function sets the value specified scalar to the given boolean value
//
// Parameters:
// HANDLE hFolder
// The handle of the folder containing the scalar to modify.
// char *pszName
// The name of the scalar.
// BOOL bIsTrue
// The boolean value to set the scalar to.
//
// Returns:
// SMS_STATUS
// The SMS status code.
//
// Note that the scalar specified must be of type SCALAR_INT.
//
// ====================================================================
SMS_STATUS SetScalarBool( HANDLE hFolder, char *pszName, BOOL bIsTrue )
{
SCALAR sc;
sc.pszName = pszName;
sc.scType = SCALAR_INT;
sc.dwValue = bIsTrue ? 1 : 0;


SMS_STATUS stat;
stat = SmsSetScalar(hFolder, &sc);
return stat;
}


// ====================================================================
//
// SetScalarDword
//
// This function sets the value specified scalar to the given DWORD value.
//
// Parameters:
// HANDLE hFolder
// The handle of the folder containing the scalar to modify.
// char *pszName
// The name of the scalar.
// DWORD dwValue
// The DWORD value to set the scalar to.
//
// Returns:
// SMS_STATUS
// The SMS status code.
//
// Note that the scalar specified must be of type SCALAR_INT.
//
// ====================================================================
SMS_STATUS SetScalarDword( HANDLE hFolder, char *pszName, DWORD dwValue )
{
SCALAR sc;
sc.pszName = pszName;
sc.scType = SCALAR_INT;
sc.dwValue = dwValue;


SMS_STATUS stat;
stat = SmsSetScalar(hFolder, &sc);
return stat;
}


// ====================================================================
//
// SetScalarTime
//
// This function sets the value specified scalar to the given time
// value.
//
// Parameters:
// HANDLE hFolder
// The handle of the folder containing the scalar to modify.
// char *pszName
// The name of the scalar.
// time_t tValue
// The time value to assign to the scalar.
//
// Returns:
// SMS_STATUS
// The SMS status code.
//
// Note that the scalar specified must be of type SCALAR_TIME.
//
// ====================================================================
SMS_STATUS SetScalarTime( HANDLE hFolder, char *pszName, time_t tValue )
{
SCALAR sc;
sc.pszName = pszName;
sc.scType = SCALAR_TIME;
sc.tValue = tValue;

SMS_STATUS stat;
stat = SmsSetScalar(hFolder, &sc);
return stat;
}


// ====================================================================
//
// GetScalarString
//
// This function gets the string value of the specified scalar.
//
// Parameters:
// HANDLE hFolder
// The handle of the folder containing the scalar to modify.
// char *pszName
// The name of the scalar.
// char *pchBuffer
// Pointer to the buffer to store the scalar's string value.
// DWORD cchBuffer
// The size of the buffer.
//
// Returns:
// SMS_STATUS
// The SMS status code.
//
// Note that the scalar specified must be of type SCALAR_STRING.
//
// ====================================================================
SMS_STATUS GetScalarString( HANDLE hFolder,
char *pszName,
char *pchBuffer,
DWORD cchBuffer )
{
SCALAR sc;

SMS_STATUS stat;
*pchBuffer = '\0';

char szNameBuffer[CCH_MAXSTRING];
sc.pszName = szNameBuffer;
sc.pszValue = pchBuffer;
sc.dwLen = cchBuffer;
stat = SmsGetScalarByName(hFolder, pszName, &sc);

if (stat == SMS_OK) {
if (sc.scType == SCALAR_STRING) {
strcpy(pchBuffer, sc.pszValue);
}
else {
DisplaySmsError("GetScalarString: scalar is not SCALAR_STRING", SMS_OK);
stat = SMS_ERROR;
}
}
else {
DisplaySmsError("GetScalarString: SmsGetScalarByName failed", stat);
}
return(stat);
}


// ====================================================================
//
// GetScalarDword
//
// This function gets the DWORD value of the specified scalar.
//
// Parameters:
// HANDLE hFolder
// The handle of the folder containing the scalar to modify.
//
// char *pszName
// The name of the scalar.
//
// DWORD *pdwValue
// Pointer to the place to store the scalar's DWORD value.
//
// Returns:
// SMS_STATUS
// The SMS status code.
//
// Note that the scalar specified must be of type SCALAR_INT.
//
// ====================================================================
SMS_STATUS GetScalarDword( HANDLE hFolder, char *pszName, DWORD *pdwValue )
{
SCALAR sc;

SMS_STATUS stat;
char szNameBuffer[CCH_MAXSTRING];
sc.pszName = szNameBuffer;
stat = SmsGetScalarByName(hFolder, pszName, &sc);

if (stat == SMS_OK) {
if (sc.scType == SCALAR_INT) {
*pdwValue = sc.dwValue;
}
else {
DisplaySmsError("GetScalarDword: Scalar is not SCALAR_INT", SMS_OK);
stat = SMS_ERROR;
}
}
else {
DisplaySmsError("GetScalarDword: SmsGetScalarByName failed", stat);
}
return(stat);
}


// ====================================================================
//
// FindFolder
//
// This function searches a container for a folder with a scalar that
// matches the given scalar name and string scalar value.
//
// Parameters:
// HANDLE hContainer
// The handle of the containter to search.
//
// char *pszName
// The name of the scalar.
//
// char *pszScalarValue
// The "match" value to test for.
//
// HFOLDER* phFolder
// Pointer to the place to store the folder handle if a folder
// containing the specified scalar value is found.
//
//
// Returns;
// SMS_OK if the folder is found, SMS_NOT_FOUND otherwise.
// The folder handle is returned via a pointer.
//
// ====================================================================
SMS_STATUS FindFolder( HANDLE hContainer,
DWORD fType,
char *pszScalarName,
char *pszScalarValue,
HANDLE *phFolder )
{
*phFolder = NULL;

while(TRUE) {
SMS_STATUS stat;
HANDLE hFolder;
char szValue[CCH_MAXSTRING];

stat = SmsGetNextFolder(hContainer, fType, &hFolder);
if (stat == SMS_NO_MORE_DATA) {
return(SMS_NOT_FOUND);
}

if (stat != SMS_OK) {
DisplaySmsError("SmsGetNextFolder error", stat);
return(SMS_NOT_FOUND);
}

GetScalarString(hFolder, pszScalarName, szValue, sizeof(szValue));
if (!strcmp(szValue, pszScalarValue)) {
*phFolder = hFolder;
return(SMS_OK);
}

SmsCloseFolder(hFolder);
}
}


// ====================================================================
//
// InputString
//
// Prompt the user to input a string and return the string in the
// specified buffer.
//
// Parameters:
// const char *pszMessage
// The user prompt to display.
//
// char *pszResult
// Pointer to the buffer where the user's input will be returned.
//
// Returns;
// The user's input is returned via the given buffer.
//
// ====================================================================
void InputString( const char *pszMessage, char *pszResult )
{
printf("%s: ", pszMessage);
gets(pszResult);
}




// ====================================================================
//
// ConnectToDatasource
//
// Get the datasource connection information from the user and use it
// to connect to the datasource.
//
// Parameters: None.
//
// Returns:
// The connection handle or NULL if the connection failed.
//
// ====================================================================
HANDLE ConnectToDatasource()
{
// Get the information we need to connect to the
// data source from the user.
//==============================================
char szServer[CCH_MAXINPUT];
char szUser[CCH_MAXINPUT];
char szPasswd[CCH_MAXINPUT];
char szDatabase[CCH_MAXINPUT];

printf("\n");
printf("**************************\n");
printf("* Connect to data source *\n");
printf("**************************\n");
InputString("SQL server name", szServer);
InputString("SQL database name", szDatabase);
InputString("User name for SQL server", szUser);
InputString("Password for SQL server", szPasswd);
printf("\n");


// Connect to a data source. SQL in this case.
// ===========================================
DATASOURCE dsParams;

dsParams.sqlParams.ds = DB_SQL;
dsParams.sqlParams.pszServer = szServer;
dsParams.sqlParams.pszUserName = szUser;
dsParams.sqlParams.pszPasswd = szPasswd;
dsParams.sqlParams.pszDbName = szDatabase;
dsParams.sqlParams.pFunc = NULL; // No encryption.
dsParams.sqlParams.pszKey = "";

HANDLE hConnect;
SMS_STATUS stat;
stat = SmsDataSourceConnect( &dsParams, &hConnect);

if (stat != SMS_OK) {
hConnect = NULL;
DisplaySmsError("Connect to data source failed", stat);
}

return( hConnect );
}



// ====================================================================
//
// GetPlatformSelection
//
// Prompt the user to select a one or more platforms. The platforms
// are selected by entering a comma separated list of platform indexes.
// These platform indexes are converted to the platforms flags value
// for the SDK.
//
// Parameters: None.
//
// Returns:
// The SMS SDK platform flags value.
//
// ====================================================================
DWORD GetPlatformSelection()
{
printf("**********************************************\n");
printf("* Select a platform from the following list. *\n");
printf("**********************************************\n");
printf(" 1) MS-DOS \n");
printf(" 2) Windows 3.1\n");
printf(" 3) Windows95\n");
printf(" 4) Windows NT (X86)\n");
printf(" 5) Windows NT (Mips)\n");
printf(" 6) Windows NT (Alpha)\n");
printf(" 7) Macintosh\n");
printf("\n");

DWORD dwPlatforms = 0;
char szReply[CCH_MAXINPUT];
InputString("Enter a comma separated list of platform number(s)", szReply);
printf("\n");

const char *pszReply = szReply;
while (*pszReply) {


if (*pszReply < '1' || *pszReply > '6') {
// An invalid character is present in the
// reply. Flag this error by returning zero.
//==========================================
dwPlatforms = 0;
break;
}

// Interpret the platform selector character.
//===========================================
switch(*pszReply++) {
case '1':
dwPlatforms |= PLTFRM_MSDOS;
break;

case '2':
dwPlatforms |= PLTFRM_WIN16;
break;

case '3':
dwPlatforms |= PLTFRM_WIN95;
break;

case '4':
dwPlatforms |= PLTFRM_WIN32_X86;
break;

case '5':
dwPlatforms |= PLTFRM_WIN32_MIPS;
break;

case '6':
dwPlatforms |= PLTFRM_WIN32_ALPHA;
break;

case '7':
dwPlatforms |= PLTFRM_MACINTOSH;
break;
}

// Skip any trailing whitespace after the digit.
//==============================================
while(*pszReply == ' ' || *pszReply=='\t') {
++pszReply;
}

// Skip the comma.
//================
if (*pszReply == ',') {
++pszReply;
}

// Skip white space preceding the next digit.
//=========================
while (*pszReply == ' ' || *pszReply=='\t') {
++pszReply;
}

}

return( dwPlatforms );
}




// ====================================================================
//
// ServerWriteProgItem
//
// Set the value of the program item folder's scalars, link it into the
// folder hierarchy and finally insert it into the data source.
//
// Parameters:
// HANDLE hFolderProgItem
// The program item folder's handle.
//
// TServerParams *psp
// Pointer to the "share package on server" parameters
// that the user was asked to input at the beginning of the
// program.
//
// Returns:
// SMS_OK if no error occurred, otherwise the SDK status code.
//
// ====================================================================
SMS_STATUS ServerWriteProgItem( HANDLE hFolderProgItem, TServerParams *psp )
{

SetScalarString(hFolderProgItem, "Description", psp->szDescription);
SetScalarString(hFolderProgItem, "Command line", psp->szCmdLine);
SetScalarDword(hFolderProgItem, "Run minimized", 0);
SetScalarDword(hFolderProgItem, "Platforms", psp->dwPlatforms);
SetScalarString(hFolderProgItem, "Configure script file", "");
SetScalarDword(hFolderProgItem, "Search local flag", 1);
SetScalarDword(hFolderProgItem, "Drive mode", 1);
SetScalarString(hFolderProgItem, "Registry name", psp->szRegistryName);
SetScalarDword(hFolderProgItem, "Executable flag", 1);



SMS_STATUS stat;
stat = SmsLinkFolder(hFolderProgItem);
if (stat != SMS_OK) {
DisplaySmsError("Failed to link the server share program item folder into the hierarchy", stat);
return( stat );
}

stat = SmsCommitFolder(hFolderProgItem);
if ((stat != SMS_OK) && (stat != SMS_PARENT_NEEDS_COMMIT)) {
DisplaySmsError("Failed to insert the server share program item folder into the package", stat);
}
else {
// If the status code was SMS_PARENT_NEEDS_COMMIT, we convert the status to
// SMS_OK, since the commit was successful.
//=========================================================================
stat = SMS_OK;
}


// Note program item folders are not actually inserted into the
// datasource until a commit is also done on the parent
// package folder.
//=====================================================
return( stat );
}


// ====================================================================
//
// ServerWritePkg
//
// Set the value of the package folder's scalars, link it into the
// folder hierarchy and finally insert it into the data source.
//
// Parameters:
// HANDLE hFolderPkg
// The package folder's handle.
//
// TServerParams *psp
// Pointer to the "share package on server" parameters
// that the user was asked to input at the beginning of the
// program.
//
// Returns:
// SMS_OK if no error occurred, otherwise the SDK status code.
//
// ====================================================================
SMS_STATUS ServerWritePkg( HANDLE hFolderPkg, TServerParams *psp )
{
SetScalarString(hFolderPkg, "Name", psp->szPackageName);
SetScalarString(hFolderPkg, "Comment", psp->szPackageComment);
SetScalarString(hFolderPkg, "Server root", psp->szPackagePath);
SetScalarString(hFolderPkg, "Share name", psp->szShareName);
SetScalarDword(hFolderPkg, "Share permissions", (DWORD)SHARE_PERMISSIONS);


SMS_STATUS stat;
stat = SmsLinkFolder(hFolderPkg);
if (stat != SMS_OK) {
DisplaySmsError("Failed to link the server share package folder into the hierarchy", stat);
return( stat );
}


stat = SmsCommitFolder(hFolderPkg);
if ((stat != SMS_OK) && (stat != SMS_PARENT_NEEDS_COMMIT)) {
DisplaySmsError("Failed to insert the server share package folder into the datasource", stat);
}

return( stat );
}


// ====================================================================
//
// ServerCreatePkg
//
// This function create the server install job package.
//
// Parameters:
// HANDLE hConnect
// The datasource connection handle.
//
// TServerParams *psp
// Pointer to the "share package on server" parameters
// that the user was asked to input at the beginning of the
// program.
//
// char *pszPackageID
// Pointer to the buffer where the package ID will be
// returned.
//
// DWORD cchPackageID
// Size of the package ID buffer.
//
// char *pszProgItemID
// Pointer to the buffer where the program item ID will be
// returned.
//
// DWORD cchProgItemID
// Size of the program item ID buffer.
//
// Returns:
// SMS_OK if no error occurred, otherwise the SDK status code.
//
// ====================================================================
SMS_STATUS ServerCreatePkg( HANDLE hConnect,
TServerParams *psp,
char *pszPackageID,
DWORD cchPackageID,
char *pszProgItemID,
DWORD cchProgItemID)
{
SMS_STATUS stat;
HANDLE hContainer;
HANDLE hFolderPkg = NULL;
HANDLE hFolderProgItem = NULL;

// First open the package container. If we fail to open it,
// display an error message and give up.
//=========================================================
stat = SmsOpenContainer( C_PACKAGE, hConnect, &hContainer );
if (stat != SMS_OK) {
DisplaySmsError("Failed to open the package container", stat);
return( stat );
}

// Now create a new package folder.
//=================================
stat = SmsCreateFolder( hContainer, F_PACKAGE, "Dummy", &hFolderPkg );
if (stat != SMS_OK) {
DisplaySmsError("Failed to create server share package", stat);
goto CLEANUP_AND_EXIT;
}


stat = SmsCreateFolder(hFolderPkg, F_PROGITEM, "Dummy", &hFolderProgItem);
if (stat != SMS_OK) {
DisplaySmsError("Failed to create server share program item", stat);
goto CLEANUP_AND_EXIT;
}


stat = ServerWriteProgItem(hFolderProgItem, psp);
if (stat != SMS_OK) {
goto CLEANUP_AND_EXIT;
}

stat = ServerWritePkg(hFolderPkg, psp);
if (stat != SMS_OK) {
goto CLEANUP_AND_EXIT;
}


GetScalarString(hFolderPkg, "Key", pszPackageID, cchPackageID);
GetScalarString(hFolderProgItem, "ItemKey", pszProgItemID, cchProgItemID);
stat = SMS_OK;


CLEANUP_AND_EXIT:

if (hFolderProgItem) {
SmsCloseFolder(hFolderProgItem);
}

if (hFolderPkg) {
SmsCloseFolder(hFolderPkg);
}

SmsCloseContainer(hContainer);
return( stat );
}



// ====================================================================

// 
// ServerWriteJob
//
// This function sets the values of the job folder's scalars, links the
// job folder into the folder hierachy, and then inserts the folder
// into the data source.
//
// Parameters:
// HANDLE hFolderJob
// The handle to the server install job folder.
//
// TServerParams *psp
// Pointer to the "share package on server" parameters
// that the user was asked to input at the beginning of the
// program.
//
// char *pszPackageID
// Pointer to the server install package ID.
//
// Returns:
// SMS_OK if no error occurred, otherwise the SDK status code.
//
// ====================================================================
BOOL ServerWriteJob( HANDLE hFolderJob,
TServerParams *psp,
char *pszPackageID )
{
// Submit the job immediately, so set the job submission time
// to the current time.
//===========================================================
time_t timeJob;
time( &timeJob );


SetScalarString(hFolderJob, "Job comment", "This is an SDK sample job");
SetScalarTime( hFolderJob, "Activate time", timeJob);
SetScalarDword( hFolderJob, "Priority", 3);
SetScalarDword( hFolderJob, "Repeat mode", 1);
SetScalarDword( hFolderJob, "Cancel mode", 0);
SetScalarString(hFolderJob, "Package ID", pszPackageID);
SetScalarDword( hFolderJob, "PackageOverwriteFlag", 0);
SetScalarDword( hFolderJob, "Job target", WKSTAJOB_TGT_MACHPATH);
SetScalarDword( hFolderJob, "Limit to sites", JOBTGT_SITE);
SetScalarDword( hFolderJob, "Include subsites", JOBTGT_INCLUDESUBSITES);
SetScalarString(hFolderJob, "Site limit name", psp->szSiteName);

// At this point we have a folder in memory, but it has not
// been linked into the folder hierarchy yet. We do so by
// calling SmsLinkFolder.
//=========================================================
SMS_STATUS stat;
stat = SmsLinkFolder(hFolderJob);
if (stat != SMS_OK) {
DisplaySmsError("SmsLinkFolder(hFolderJob) failed", stat);
return(stat);
}

// The folder is now linked into the folder hierachy, but it
// has not yet been inserted into the data source. We do so
// now by calling SmsCommitFolder.
//==========================================================
stat = SmsCommitFolder(hFolderJob);
if (stat != SMS_OK) {
DisplaySmsError("Failed to insert the job into the datasource", stat);
return(stat);
}

return( SMS_OK );
}




// ====================================================================
//
// ServerSubmitShareJob
//
// This function creates a share package on server job.
//
// Parameters:
// HANDLE hConnect
// The datasource connection handle.
//
// TServerParams *psp
// Pointer to the "share package on server" parameters
// that the user was asked to input at the beginning of the
// program.
//
// char *pszPackageID
// Pointer to the server install package ID.
//
// char *pszJobID
// Pointer to the buffer where the server install job ID will
// be returned.
//
// DWORD cchJobID
// The size of the job ID buffer.
//
// Returns;
// SMS_OK if everything was successful, otherwise the SMS SDK
// status code.
// The job ID is in the job name buffer.
//
// ====================================================================
SMS_STATUS ServerSubmitShareJob(HANDLE hConnect,
TServerParams *psp,
char *pszPackageID,
char *pszJobID,
DWORD cchJobID)
{
*pszJobID = 0;

SMS_STATUS stat;
HANDLE hContainerJob;
stat = SmsOpenContainer( C_JOB, hConnect, &hContainerJob );
if (stat != SMS_OK) {
DisplaySmsError("Failed to open job container", stat);
return( stat );
}

HANDLE hFolderJob;
stat = SmsCreateFolder( hContainerJob, F_SRVINSTALLJOB, "Dummy", &hFolderJob );
if (stat == SMS_OK) {
if (ServerWriteJob(hFolderJob, psp, pszPackageID) == SMS_OK) {

// We will need the Job ID later so that we can wait for
// the job to complete before submitting the client setup
// package. We get the Job ID below.
//==================================================
GetScalarString(hFolderJob, "Job ID", pszJobID, cchJobID);
}


// We close the folder to avoid a memory leak.
//============================================
stat = SmsCloseFolder(hFolderJob);
if (stat != SMS_OK) {
DisplaySmsError("Failed to close job folder", stat);
}

}
else {
DisplaySmsError("Failed to create the job folder", stat);
}



stat = SmsCloseContainer(hContainerJob);
if (stat != SMS_OK) {
DisplaySmsError("Failed to close the job container", stat);
}
return stat;
}


// ====================================================================
//
// JobStatusToString
//
// Convert the job status value to a human readable string.
//
// Parameters:
// DWORD dwJobStatus
// The status value from the job's "Job status" scalar.
//
// char *pszJobStatus
// Pointer to a buffer to recieve the human readable version
// of the job status.
// The size of the job ID buffer.
//
// Returns;
// SMS_OK if everything was successful, otherwise the SMS SDK
// status code.
//
// ====================================================================
void JobStatusToString( DWORD dwJobStatus, char *pszJobStatus )
{
char *psz;
switch(dwJobStatus) {
case JOBSTAT_PENDING:
psz = "pending";
break;
case JOBSTAT_ACTIVE:
psz = "active";
break;
case JOBSTAT_CANCELLED:
psz = "cancelled";
break;
case JOBSTAT_COMPLETE:
psz = "complete";
break;
case JOBSTAT_FAILED:
psz = "failed";
break;
case JOBSTAT_RETRYING:
psz = "retrying";
break;
default:
sprintf(pszJobStatus, "unkown status %ld", dwJobStatus);
return;
break;
}
strcpy(pszJobStatus, psz);
}



// ====================================================================
//
// ServerWaitJobComplete
//
// This function polls the status of the server install job and
// continues doing so until the job is complete. As this is done,
// status given to the user as to the progress of the job.
//
// Parameters:
// HANDLE hConnect
// The datasource connection handle.
//
// char *pszJobID
// Pointer to the ID string for the job to wait on.
//
// Returns:
// SMS_OK if everything went ok and the job has completed,
// otherwise the SDK status code.
//
// ====================================================================
SMS_STATUS ServerWaitJobComplete( HANDLE hConnect, char *pszJobID )
{
printf("***************************************\n");
printf("* Share package on server job. *\n");
printf("* Waiting for job to complete. *\n");
printf("***************************************\n");

while(TRUE) {
SMS_STATUS stat;
HANDLE hContainerJob;
stat = SmsOpenContainer( C_JOB, hConnect, &hContainerJob );
if (stat != SMS_OK) {
DisplaySmsError("Failed to open job container while waiting for server share job", stat);
return( stat );
}

stat = SmsPopulate(hContainerJob, POP_SYNC, NULL);
if (stat != SMS_OK) {
DisplaySmsError("Failed to populate job container while waiting for server share job", stat);
SmsCloseContainer(hContainerJob);
return(stat);
}

HANDLE hFolderJob;
stat = FindFolder(hContainerJob, F_SRVINSTALLJOB, "Job ID", pszJobID, &hFolderJob);
if (stat != SMS_OK) {
DisplaySmsError("Server share job not found while waiting", stat);
SmsCloseContainer(hContainerJob);
return(stat);
}

DWORD dwScalarValue;
GetScalarDword(hFolderJob, "Job status", &dwScalarValue);

SmsCloseFolder(hFolderJob);
SmsCloseContainer(hContainerJob);


char szJobStatus[CCH_MAXSTRING];
JobStatusToString(dwScalarValue, szJobStatus);

printf("Share package on server job, status = %s\n", szJobStatus);

if (dwScalarValue == 4) {
return(SMS_OK);
}



// Sleep for 10 seconds before checking the status of the server
// share job again.
//=============================================================
Sleep(10000);
}

// Control should never come here. So no return value is
// necessary.
//======================================================

}






// ====================================================================
//
// ServerDoShare
//
// This function creates a server install package and then submits
// a share package on server job.
//
// Parameters:
// HANDLE hConnect
// The datasource connection handle.
//
// TServerParams *psp
// Pointer to the "share package on server" parameters
// that the user was asked to input at the beginning of the
// program.
//
// Returns:
// SMS_OK if everything went ok, otherwise the SDK status code.
//
// ====================================================================
void ServerDoShare( HANDLE hConnect, TServerParams *psp )
{
char szPackageID[CCH_MAXSTRING];
char szProgItemID[CCH_MAXSTRING];
char szJobID[CCH_MAXSTRING];
SMS_STATUS stat;

stat = ServerCreatePkg(hConnect,
psp,
szPackageID, sizeof(szPackageID),
szProgItemID, sizeof(szProgItemID));
if (stat != SMS_OK) {
DisplaySmsError("Failed to create server share package", stat);
return;
}


// Submit the share package on server job and get the
// job ID.
//====================================================
stat = ServerSubmitShareJob(hConnect,
psp,
szPackageID,
szJobID,
sizeof(szJobID));
if (stat != SMS_OK) {
DisplaySmsError("Failed to submit server share package", stat);
return;
}


ServerWaitJobComplete(hConnect, szJobID);
}





// ====================================================================
//
// ClientWritePkg
//
// This function defines the attributes (scalar values) for the client
// setup package.
//
// Parameters:
// HANDLE hFolderPkg
// The handle to the package folder for the client setup job.
//
// TClientParams *pcp
// Pointer to the "run setup command on client" parameters
// that the user was asked to input at the beginning of the
// program.
//
// Returns:
// SMS_OK if everything went ok, otherwise the SDK status code.
//
// ====================================================================
BOOL ClientWritePkg( HANDLE hFolderPkg, TClientParams *pcp )
{
SetScalarString(hFolderPkg, "Name", pcp->szPackageName);
SetScalarString(hFolderPkg, "Comment", pcp->szPackageComment);
SetScalarString(hFolderPkg, "Workstation root", pcp->szPackagePath);
SetScalarDword(hFolderPkg, "Share permissions", (DWORD) SHARE_PERMISSIONS);

SMS_STATUS stat;
stat = SmsCommitFolder(hFolderPkg);
if (stat != SMS_OK) {
DisplaySmsError("Failed to insert the package into the datasource", stat);
return(FALSE);
}

return(TRUE);
}


// ====================================================================
//
// ClientWriteWcl
//
// This function defines the attributes (scalar values) for the
// workstation command line folder that is part of the client setup
// package.
//
// Parameters:
// HANDLE hFolderWcl
// The handle to the workstation command line folder for the
// client setup job.
//
// TClientParams *pcp
// Pointer to the "run setup command on client" parameters
// that the user was asked to input at the beginning of the
// program.
//
// Returns:
// SMS_OK if everything went ok, otherwise the SDK status code.
//
// ====================================================================
BOOL ClientWriteWcl( HANDLE hFolderWCL, TClientParams *pcp )
{

SetScalarString(hFolderWCL, "Name", pcp->szWclName);
SetScalarString(hFolderWCL, "Command", pcp->szWclCommand);
SetScalarDword( hFolderWCL, "Flags", 0);
SetScalarDword( hFolderWCL, "Platforms", pcp->dwPlatforms);


SMS_STATUS stat;
stat = SmsLinkFolder(hFolderWCL);
if (stat != SMS_OK) {
DisplaySmsError("Failed to link the WCL folder into the hierarchy", stat);
return( FALSE );
}

stat = SmsCommitFolder(hFolderWCL);
if ((stat != SMS_OK) && (stat != SMS_PARENT_NEEDS_COMMIT)) {
DisplaySmsError("Failed to insert the WCL folder into the package", stat);
return( FALSE );
}



// Note WCL folders are not actually inserted into the
// datasource until a commit is also done on the parent
// package folder.
//=====================================================

return( TRUE );
}






// ====================================================================
//
// ClientPkgCreate
//
// This function defines creates an SMS package for the client setup
// program.
//
// Parameters:
// HANDLE hConnect
// The datasource connection handle.
//
// TClientParams *pcp
// Pointer to the "run setup command on client" parameters
// that the user was asked to input at the beginning of the
// program.
//
// char *pszPackageID
// Pointer to the buffer where the package folder ID string
// will be returned.
//
// DWORD cchPackageID
// The size of the package ID buffer.
//
// char *pszWclID
// Pointer to the buffer where the workstation command line
// folder ID will be returned.
//
// DWORD cchWclID
// The size of the workstation command line ID buffer.
//
// Returns:
// SMS_OK if everything went ok, otherwise the SMS SDK status
// code.
// The package ID and the WCL IDs are returned in their respective
// buffers.
//
// ====================================================================
SMS_STATUS ClientPkgCreate( HANDLE hConnect,
TClientParams *pcp,
char *pszPackageID,
DWORD cchPackageID,
char *pszWclID,
DWORD cchWclID )
{
SMS_STATUS stat;
HANDLE hContainer;

// First open the package container. If we fail to open it,
// display an error message and give up.
//=========================================================
stat = SmsOpenContainer( C_PACKAGE, hConnect, &hContainer );
if (stat != SMS_OK) {
DisplaySmsError("Failed to open the package container", stat);
return( stat );
}

// Now create a new package folder.
//=================================
HANDLE hFolderPkg;
stat = SmsCreateFolder( hContainer, F_PACKAGE, "Dummy", &hFolderPkg );
if (stat != SMS_OK) {
DisplaySmsError("Failed to create package folder for setup command", stat);
SmsCloseContainer(hContainer);
return(stat);
}


HANDLE hFolderWCL;
stat = SmsCreateFolder(hFolderPkg, F_WCL, "Dummy", &hFolderWCL);
if (stat != SMS_OK) {
DisplaySmsError("Failed to create WCL folder for setup command", stat);
SmsCloseContainer(hContainer);
return(stat);
}


// At this point we have created both the WCL and the package folder
// and now we define their attributes. Note that the WCL attributes
// are defined first because the call to SmsCommitFolder for the WCL
// must precede the call to SmsCommitFolder for the package. This is
// because the WCL is part of the package and the package can't be
// modified after a commit is done on it.
//==================================================================
ClientWriteWcl(hFolderWCL, pcp);
ClientWritePkg(hFolderPkg, pcp);



// We now get the identification strings for the package and the WCL.
// This is done here because the WCL key is not defined until the
// package it is contained in has been committed.
//==================================================================
GetScalarString(hFolderPkg, "Key", pszPackageID, cchPackageID);
GetScalarString(hFolderWCL, "Name", pszWclID, cchWclID);

stat = SmsCloseFolder(hFolderWCL);
if (stat != SMS_OK) {
DisplaySmsError("Failed to close the WCL folder", stat);
}


stat = SmsCloseFolder(hFolderPkg);
if (stat != SMS_OK) {
DisplaySmsError("Failed to close the package folder", stat);
}

stat = SmsCloseContainer(hContainer);
if (stat != SMS_OK) {
DisplaySmsError("Failed to close the package container", stat);
}

return( stat );
}


// ====================================================================
//
// ClientWriteJob
//
// This function defines the values of the job folder, links the folder
// into the folder hierarchy, and finally inserts the folder into the
// datasource.
//
// Parameters:
// HANDLE hFolderJob
// The job folder's handle.
//
// TClientParams *pcp
// Pointer to the "run command on client" parameters
// that the user was asked to input at the beginning of the
// program.
//
// char *pszPackageID
// Pointer to the package folder ID string.
//
// char *pszWclID
// Pointer to the workstation command line folder ID.
//
// Returns:
// SMS_OK if everything went OK, otherwise the SMS SDK status code.
//
// ====================================================================
SMS_STATUS ClientWriteJob( HANDLE hFolderJob,
TClientParams *pcp,
char *pszPackageID,
char *pszWCLID)
{
// Submit the job immediately. To do this we set the job
// submission time to the current time.
//======================================================
time_t timeJob;
time( &timeJob );

SetScalarString(hFolderJob, "Job comment", "This is an SDK sample job");
SetScalarTime( hFolderJob, "Activate time", timeJob);
SetScalarDword( hFolderJob, "Priority", JOBPRI_LOW);
SetScalarDword( hFolderJob, "Repeat mode", JOBRPT_NEVER);
SetScalarDword( hFolderJob, "Cancel mode", JOBCANCEL_DONT_CANCEL);
SetScalarString(hFolderJob, "Package ID", pszPackageID);
SetScalarString(hFolderJob, "WCL name", pcp->szWclName);
SetScalarString(hFolderJob, "Machine path", "*|*|*");
SetScalarTime( hFolderJob, "Offer time", timeJob);
SetScalarTime( hFolderJob, "Mandatory time", timeJob);
SetScalarDword( hFolderJob, "Use mandatory time", 0);
SetScalarDword( hFolderJob, "Force over slow link", 0);
SetScalarDword( hFolderJob, "Use expire time", 0);
SetScalarDword( hFolderJob, "Job target", WKSTAJOB_TGT_MACHPATH);
SetScalarDword( hFolderJob, "Limit to sites", JOBTGT_SITE);
SetScalarDword( hFolderJob, "Include subsites", JOBTGT_INCLUDESUBSITES);
SetScalarString(hFolderJob, "Site limit name", pcp->szSiteName);
SetScalarDword( hFolderJob, "Run workstation command", TRUE);


// At this point, the job folder exists in memory. Now
// we must insert the folder into the data source. We do
// this by calling SmsCommitFolder.
//=========================================================
SMS_STATUS stat;
stat = SmsLinkFolder(hFolderJob);
if (stat != SMS_OK) {
DisplaySmsError("SmsLinkFolder(hFolderJob) failed", stat);
return(stat);
}

stat = SmsCommitFolder(hFolderJob);
if (stat != SMS_OK) {
DisplaySmsError("Failed to insert the job into the datasource", stat);
return(stat);
}

return( SMS_OK );
}




// ====================================================================
//
// ClientJobCreate
//
// This function creates an SMS job to run a command on the client
// workstation. The name of the network share path is passed
// as a parameter to the client setup program.
//
// Parameters:
// HANDLE hConnect
// The datasource connection handle.
//
// TClientParams *pcp
// Pointer to the "run command on client" parameters
// that the user was asked to input at the beginning of the
// program.
//
// char *pszPackageID
// Pointer to the package folder ID string.
//
// char *pszWclID
// Pointer to the workstation command line folder ID.
//
// Returns:
// SMS_OK if everything went OK, otherwise the SMS SDK status
// code.
//
// ====================================================================
SMS_STATUS ClientJobCreate( HANDLE hConnect,
TClientParams *pcp,
char *pszPackageID,
char *pszWCLID)
{

SMS_STATUS stat;

HANDLE hContainerJob;
stat = SmsOpenContainer( C_JOB, hConnect, &hContainerJob );
if (stat != SMS_OK) {
DisplaySmsError("Failed to open job container", stat);
return( stat );
}

HANDLE hFolderJob;
stat = SmsCreateFolder( hContainerJob, F_INSTALLJOB, "Dummy", &hFolderJob );
if (stat == SMS_OK) {
ClientWriteJob(hFolderJob, pcp, pszPackageID, pszWCLID);

stat = SmsCloseFolder(hFolderJob);
if (stat != SMS_OK) {
DisplaySmsError("Failed to close job folder", stat);
}

}
else {
DisplaySmsError("Failed to create the job folder", stat);
}

stat = SmsCloseContainer(hContainerJob);
if (stat != SMS_OK) {
DisplaySmsError("Failed to close the job container", stat);
}
return stat;
}


// ====================================================================
//
// ClientDoRunCommand
//
// This function submits a run command on workstation job.
//
//
// Parameters:
// HANDLE hConnect
// The datasource connection handle.
//
// TClientParams *pcp
// Pointer to the "client" parameters that the user was asked to
// input at the beginning of the program.
//
// Returns: Nothing.
//
// ====================================================================
void ClientDoRunCommand( HANDLE hConnect, TClientParams *pcp )
{
char szPackageID[CCH_MAXSTRING];
char szWclID[CCH_MAXSTRING];

SMS_STATUS stat;
stat = ClientPkgCreate(hConnect,
pcp,
szPackageID, sizeof(szPackageID),
szWclID, sizeof(szWclID));
if (stat != SMS_OK) {
DisplaySmsError("Could not create client package", stat);
return;
}

stat = ClientJobCreate(hConnect, pcp, szPackageID, szWclID);
if (stat != SMS_OK) {
DisplaySmsError("Could not create client job", stat);
}
}



// ====================================================================
//
// GetSiteName
//
// This function query's the user to select the SMS site that will be
// used in this sample
//
// Parameters:
// HANDLE hConnect
// The datasource connection handle.
//
// char *pszSiteName
// Pointer to the buffer where the site name will be returned.
//
// Returns: Nothing.
//
// ====================================================================
void GetSiteName( HANDLE hConnect, char *pszSiteName )
{
*pszSiteName = '\0';


HANDLE hContainerSite;
SMS_STATUS stat;

stat = SmsOpenContainer( C_SITE, hConnect, &hContainerSite );
if (stat != SMS_OK) {
DisplaySmsError("Failed to open site container", stat);
return;
}


stat = SmsPopulate(hContainerSite, POP_SYNC, NULL);
if (stat != SMS_OK) {
DisplaySmsError("Failed to populate site container", stat);
return;
}


HANDLE ahFolderSite[C_MAXSITES];
HANDLE hFolderSite;
int ctFolders = 0;
int iFolder;


// Get the folder handle for each folder in the site container. Also
// count the folders so that we know how many we have.
//===================================================================
while(SmsGetNextFolder(hContainerSite, F_SITE, &hFolderSite) == SMS_OK) {
ahFolderSite[ctFolders] = hFolderSite;
++ctFolders;
}



// Ask the user to select one of the sites. After the user
// select's a site, copy its name to pszSiteName.
//=========================================================
char szValue[CCH_MAXSTRING];
if (ctFolders > 0) {
printf("******************************************\n");
printf("* Select a site from the following list. *\n");
printf("******************************************\n");
printf("\n");


for(iFolder=0; iFolder < ctFolders; ++iFolder) {
GetScalarString(ahFolderSite[iFolder], "Site name", szValue, sizeof(szValue));
printf("[%d]. %s\n", iFolder + 1, szValue);
}
printf("\n");


InputString("Enter site number", szValue);
printf("\n");
sscanf(szValue, "%d", &iFolder);

if (iFolder > 0 && iFolder <= ctFolders) {
GetScalarString(ahFolderSite[iFolder-1], "Site name", szValue, sizeof(szValue));
strcpy(pszSiteName, szValue);
}
}

// Close all of the site folders that we opened via SmsGetNextFolder.
//===================================================================
for (iFolder = 0; iFolder < ctFolders; ++iFolder) {
SmsCloseFolder(ahFolderSite[iFolder]);
}

}


// ====================================================================
//
// GetJobType
//
// This function query's the user to select the type of SMS job he or
// she would like to create.
//
// Parameters: None.
//
// Returns. A status code indicating the job type.
// CMD_RUN_COMMAND = Run command on workstation job.
// CMD_SHARE_PACKAGE = Share package on server job.
// CMD_QUIT = Exit without creating a job.
//
// ====================================================================
int GetJobType()
{
printf("***************************************\n");
printf("* Select the job type to create. *\n");
printf("***************************************\n");
printf("1. Run command on workstation.\n");
printf("2. Share package on server.\n");
printf("3. Quit.\n");
printf("\n");

char szReply[CCH_MAXSTRING];
InputString("Enter the job type", szReply);
printf("\n");

int iType = 0;
sscanf(szReply, "%d", &iType);
switch(iType) {
case 1:
return(CMD_RUN_COMMAND);
case 2:
return(CMD_SHARE_PACKAGE);
default:
return(CMD_QUIT);
}
}




// ********************************************************************
// Helper functions.
// ********************************************************************




// ====================================================================
//
// DidRequestHelp
//
// Check the program's arguments to see if the user asked for
// the help screen to be displayed.
//
// Parameters:
// int argc
// The argc value from main(argc, argv)
//
// char** argv
// The argv value from main(argc, argv)
//
// Returns:
// TRUE if command line parameters included a request
// for help to be displayed.
//
// ====================================================================
BOOL DidRequestHelp(int argc, char** argv)
{
if (argc == 2 && (strcmp(argv[1], "-help") == 0)) {

return(TRUE); 
}
else {
return(FALSE);
}
}




// ====================================================================
//
// DisplayHelp
//
// This function displays the samples help screen.
//
// Parameters:
// None
//
// Returns:
// Nothing.
//
// ====================================================================
void DisplayHelp()
{
printf("\n\n");
printf("***********************************************************\n");
printf("* srvinst.exe: *\n");
printf("* *\n");
printf("* SMS API Example: Creation of jobs. *\n");
printf("* *\n");
printf("* This sample program demonstrates how the SMS SDK can *\n");
printf("* be used to create a \"share package on server\" job or a *\n");
printf("* \"run command on workstation\" job. *\n");
printf("* *\n");
printf("* Prior to running this program, you must first share the *\n");
printf("* directory where the source files are for the package. *\n");
printf("* *\n");
printf("* Syntax: *\n");
printf("* srvinst.exe [-help] *\n");
printf("* *\n");
printf("* Switches: *\n");
printf("* -help Display this help message. *\n");
printf("* *\n");
printf("***********************************************************\n");
printf("\n");
}



// ====================================================================
//
// DisplayGreeting
//
// Display the initial greeting banner.
//
// Parameters:
// None.
//
// Returns:
// Nothing.
//
// ====================================================================
void DisplayGreeting()
{
// For this sample, the greeting is identical to the help screen.
//===============================================================
DisplayHelp();
}



/* EOF: srvinst.cpp */