This product is protected by United States and international copyright laws. The product’s underlying technology,
patents, and trademarks are listed at http://www.parallels.com/about/legal/.
Microsoft, Windows, Windows Server, Windows Vista are registered trademarks of Microsoft Corporation.
Apple, Mac, the Mac logo, OS X, macOS, iPad, iPhone, iPod touch are trademarks of Apple Inc., registered in the US
and other countries.
Linux is a registered trademark of Linus Torvalds.
All other marks and names mentioned herein may be trademarks of their respective owners.
Contents
Getting Started .......................................................................................................... 6
To develop applications on Mac computers using the SDK, the following requirements must be
met.
Hardware Requirements
• Mac computer with Intel Core 2 Duo, Core i3, Core i5, Core i7, or Xeon processor
• Minimum 2 GB RAM
Software Requirements
• macOS 10.12 or newer
• Python 2.7 or 3.0 to develop applications in Python
C HAPTER 2
Parallels C API Concepts
This chapter describes the basics of the Parallels C API. It contains information on how to compile
client applications that use the API and explains basic API concepts.
You can use the framework just like any other Apple framework when creating development
projects and compiling applications. Alternately, you can compile and build your applications
without using the framework. In such a case, you will have to specify all the necessary paths to the
SDK source files manually.
When using the framework, the dynamic library, which is supplied with the SDK, will be directly
linked to the application. If you would like to load the dynamic library at runtime, the Parallels
Virtualization SDK includes a convenient dlopen wrapper for this purpose called SdkWrap. Using
the wrapper, you can load and unload the library symbols at any time with one simple call. Please
note that in order to use SdkWrap, you must compile your application without using the framework.
The wrapper source files are located in the Helpers/SdkWrap directory, which is located in the
main SDK installation directory.
The following subsections describe various compilation scenarios in detail and provide code
samples.
Parallels C API Concepts
Compiling with SdkWrap
When using SdkWrap, your program must contain the following:
• The #include "SdkWrap.h" directive. This header file defines the wrapper functions.
• The #define SDK_LIB_NAME "libprl_sdk.dylib" directive. This is the name of the
dynamic library included in the SDK.
• The SdkWrap_Load(SDK_LIB_NAME) function call that will load the dynamic library symbols.
• The SdkWrap_Unload() function call that will unload the dynamic library when it is no longer
needed.
To compile a program, the following compiler options and instructions must be used:
• The DYN_API_WRAP preprocessor macro must be defined.
• Full paths to the Headers and the Helpers/SdkWrap directories must be specified. Both
directories are located in the main SDK installation directory.
• The SdkWrap.cpp file must be included in the project and must be built together with the
main target.
• The libdl library must be linked to the application. This is the standard dynamic linking
interface library needed to load the SDK library.
Using Makefile
The following is a sample Makefile that demonstrates the implementation of the requirements
described above. To compile a program and to build an executable, type make in the Terminal
window. To clean up the project, type make clean. Please note that the SOURCE variable must
contain the name of your source file name.
# Source file name.
# Substitute the file name with your own.
SOURCE = HelloWorld
# Target executable file name.
# Here we are using the same name as the source file name.
TARGET = $(SOURCE)
# Path to the Parallels Virtualization SDK files.
SDK_PATH = /Library/Frameworks/ParallelsVirtualizationSDK.framework
# Relative path to the SdkWrap directory containing
# the SDK helper files. The files are used to load
# the dynamic library.
SDK_WRAP_PATH = Helpers/SdkWrap
If you are using the Xcode IDE, follow these steps to set up your project:
1 Add the SdkWrap.h and the SdkWrap.cpp files to your project.
2 In the Search Paths collection, specify:
• a full path to the Helpers/SdkWrap directory (contains the wrapper source files)
• a full path to the Headers directory (contains the SDK header files)
• a full path to the Libraries directory (contains the dynamic library)
3In the Preprocessor collection, add the DYN_API_WRAP preprocessor macro.
Example
The following is a complete sample program that demonstrates the usage of the SdkWrap
wrapper. The program loads the dynamic library, initializes the API, and then logs in to the local
Parallels Service. You can copy the entire program into a file on your Mac and try building and then
running it. The program uses a cross-platform approach, so it can also be compiled on Windows
and Linux machines.
// Log in to Parallels Service.
err = LoginLocal(hServer);
// Log off.
err = LogOff(hServer);
printf( "\nEnd of program.\n\n" );
printf("Press Enter to exit...");
getchar();
exit(0);
}
// Initializes the SDK library and
// logs in to the local Parallels Service.
//
PRL_RESULT LoginLocal(PRL_HANDLE &hServer)
{
// Variables for handles.
PRL_HANDLE hJob = PRL_INVALID_HANDLE; // job handle
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; // job result
// Initialize the API. In this example, we are initializing the
// API for Parallels Desktop.
// To initialize in the Parallels Workstation mode, pass PAM_WORKSTATION
// as the second parameter.
// To initialize for Parallels Server, pass PAM_SERVER.
// See the PRL_APPLICATION_MODE enumeration for all possible options.
err = PrlApi_InitEx(PARALLELS_API_VER, PAM_DESKTOP, 0, 0);
if (PRL_FAILED(err))
{
10
Parallels C API Concepts
fprintf(stderr, "PrlApi_InitEx returned with error: %s.\n",
prl_result_to_string(err));
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Create a server handle (PHT_SERVER).
err = PrlSrv_Create(&hServer);
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlSvr_Create failed, error: %s",
prl_result_to_string(err));
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Wait for a maximum of 10 seconds for
// the job to complete.
err = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(err))
{
fprintf(stderr,
"PrlJob_Wait for PrlSrv_Login returned with error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Analyze the result of PrlSrv_Login.
err = PrlJob_GetRetCode(hJob, &nJobReturnCode);
// First, check PrlJob_GetRetCode success/failure.
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlJob_GetRetCode returned with error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Now check the Login operation success/failure.
if (PRL_FAILED(nJobReturnCode))
{
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
printf("Login job returned with error: %s\n",
prl_result_to_string(nJobReturnCode));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
// Logs off the Parallels Service and
// deinitializes the SDK library.
//
PRL_RESULT LogOff(PRL_HANDLE &hServer)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
// Log off.
hJob = PrlSrv_Logoff(hServer);
err = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlJob_Wait for PrlSrv_Logoff returned error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Get the Logoff operation return code.
err = PrlJob_GetRetCode(hJob, &nJobReturnCode);
// Check the PrlJob_GetRetCode success/failure.
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlJob_GetRetCode failed for PrlSrv_Logoff with error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Report success or failure of PrlSrv_Logoff.
if (PRL_FAILED(nJobReturnCode))
{
fprintf(stderr, "PrlSrv_Logoff failed with error: %s\n",
prl_result_to_string(nJobReturnCode));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
12
Parallels C API Concepts
}
else
{
printf( "Logoff was successful.\n" );
}
// Free handles that are no longer required.
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
// De-initialize the Parallels API, and unload the SDK.
PrlApi_Deinit();
SdkWrap_Unload();
return 0;
}
Compiling with Framework
If you are using the ParallelsVirtualizationSDK framework, the program must contain the following
include directive:
#include "ParallelsVirtualizationSDK/Parallels.h"
Parallels.h is the main SDK header file. Please note the framework name in front of the SDK
header file name. This is a common requirement when using a framework.
Note: The difference between the SdkWrap scenario (described in the previous subsection) and the
framework scenario is that Parallels.h must be included when using the framework, while
SdkWrap.h must be included when using SdkWrap. The two files must never be included together.
Please also note that you don't have to load the dynamic library manually in your program when using the
framework.
The only compiler option that must be specified when using the framework is:
-framework ParallelsVirtualizationSDK
Using Makefile
The following sample Makefile can be used to compile a program using the
ParallelsVirtualizationSDK framework:
# Source file name.
# Substitute the file name with your own.
SOURCE = HelloWorld
# Target executable file name.
# Here we are using the same name as the source file name.
TARGET = $(SOURCE)
When setting up an Xcode project, the only thing that you have to do is add the
ParallelsVirtualizationSDK framework to the project. No other project modifications are necessary.
Sample
The following is a complete sample program that demonstrates the usage of the
ParallelsVirtualizationSDK framework.
int main(int argc, char* argv[])
{
// Variables for handles.
PRL_HANDLE hServer = PRL_INVALID_HANDLE; // server handle
// Variables for return codes.
PRL_RESULT err = PRL_ERR_UNINITIALIZED;
// Log in.
err = LoginLocal(hServer);
// Log off
err = LogOff(hServer);
printf( "\nEnd of program.\n\n" );
printf("Press Enter to exit...");
getchar();
exit(0);
}
14
Parallels C API Concepts
// Intializes the SDK library and
// logs in to the local Parallels Service.
//
PRL_RESULT LoginLocal(PRL_HANDLE &hServer)
{
// Variables for handles.
PRL_HANDLE hJob = PRL_INVALID_HANDLE; // job handle
// Initialize the API. In this example, we are initializing the
// API for Parallels Workstation.
// To initialize in the Parallels Desktop mode, pass PAM_DESKTOP
// as the second parameter.
// To initialize for Parallels Server, pass PAM_SERVER.
// See the PRL_APPLICATION_MODE enumeration for all possible options.
err = PrlApi_InitEx(PARALLELS_API_VER, PAM_DESKTOP, 0, 0);
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlApi_InitEx returned with error: %s.\n",
prl_result_to_string(err));
PrlApi_Deinit();
return -1;
}
// Create a server handle (PHT_SERVER).
err = PrlSrv_Create(&hServer);
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlSvr_Create failed, error: %s",
prl_result_to_string(err));
PrlApi_Deinit();
return -1;
}
// Wait for a maximum of 10 seconds for
// the job to complete.
err = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(err))
{
fprintf(stderr,
"PrlJob_Wait for PrlSrv_Login returned with error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
return -1;
}
// Analyze the result of PrlSrv_Login.
err = PrlJob_GetRetCode(hJob, &nJobReturnCode);
// First, check PrlJob_GetRetCode success/failure.
if (PRL_FAILED(err))
15
Parallels C API Concepts
{
fprintf(stderr, "PrlJob_GetRetCode returned with error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
return -1;
}
// Now check the Login operation success/failure.
if (PRL_FAILED(nJobReturnCode))
{
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
printf("Login job returned with error: %s\n",
prl_result_to_string(nJobReturnCode));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
return -1;
}
else
{
printf( "Login was successful.\n" );
}
return 0;
}
// Log off the Parallels Service and
// deinitializes the SDK library.
//
PRL_RESULT LogOff(PRL_HANDLE &hServer)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
// Log off.
hJob = PrlSrv_Logoff(hServer);
err = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlJob_Wait for PrlSrv_Logoff returned error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
return -1;
}
// Get the Logoff operation return code.
err = PrlJob_GetRetCode(hJob, &nJobReturnCode);
// Check the PrlJob_GetRetCode success/failure.
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlJob_GetRetCode failed for PrlSrv_Logoff with error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
// Report success or failure of PrlSrv_Logoff.
if (PRL_FAILED(nJobReturnCode))
{
fprintf(stderr, "PrlSrv_Logoff failed with error: %s\n",
prl_result_to_string(nJobReturnCode));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
return -1;
}
else
{
printf( "Logoff was successful.\n" );
}
// Free handles that are no longer required.
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
// De-initialize the Parallels API, and unload the SDK.
PrlApi_Deinit();
return 0;
}
Handles
The Parallels C API is a set of functions that operate on objects. Objects are not accessed directly.
Instead, references to these objects are used. These references are known as handles.
Handle Types
PRL_HANDLE is the only handle type used in the C API. It is a pointer to an integer and it is defined
in PrlTypes.h.
PRL_HANDLE can reference any type of object within the API. The type of object that
PRL_HANDLE references determines the PRL_HANDLE type. A list of handle types can be found
in the PRL_HANDLE_TYPE enumeration in PrlEnums.h.
A handles' type can be extracted using the PrlHandle_GetType function. A string
representation of the handle type can then be obtained using the handle_type_to_string
function.
17
Parallels C API Concepts
Obtaining a Handle
A handle is usually obtained by calling a function belonging to another handle, which we may call a
"parent". For example, a virtual machine handle is obtained by calling a function that operates on
the Server handle. A virtual device handle is obtained by calling a function that operates on the
virtual machine handle, and so forth. The Parallels C API Reference guide contains a description
of every available handle and explains how each particular handle type can be obtained. The
examples in this guide also demonstrate how to obtain handles of different types.
Freeing a Handle
Parallels API handles are reference counted. Each handle contains a count of the number of
references to it held by other objects. A handle stays in memory for as long as the reference count
is greater than zero. A program is responsible for freeing any handles that are no longer needed. A
handle can be freed using the PrlHandle_Free function. The function decreases the reference
count by one. When the count reaches zero, the object is destroyed. Failing to free a handle after it
has been used will result in a memory leak.
Multithreading
Parallels API handles are thread safe. They can be used in multiple threads at the same time. To
maintain the proper reference counting, the count should be increased each time a handle is
passed to another thread by calling the PrlHandle_AddRef function. If this is not done, freeing a
handle in one thread may destroy it while other threads are still using it.
Example
The following code snippet demonstrates how to obtain a handle, how to determine its type, and
how to free it when it's no longer needed. The code is a part of the bigger example that
demonstrates how to log in to a Parallels Service (the full example is provided later in this guide).
ret = PrlSrv_Create(&hServer);
if (PRL_FAILED(ret))
{
fprintf(stderr, "PrlSvr_Create failed, error: %s",
prl_result_to_string(ret));
return PRL_ERR_FAILURE;
}
// Determine the type of the hServer handle.
PRL_HANDLE_TYPE nHandleType;
PrlHandle_GetType(hServer, &nHandleType);
printf("Handle type: %s\n",
handle_type_to_string(nHandleType));
// Free the handle when it is no longer needed.
PrlHandle_Free(hServer);
18
Parallels C API Concepts
Synchronous Functions
The Parallels C API provides synchronous and asynchronous functions. Synchronous functions run
in the same thread as the caller. When a synchronous function is called it completes executing
before returning control to the caller. Synchronous functions return PRL_RESULT, which is a
integer indicating success or failure of the operation. Consider the PrlSrv_Create function. The
purpose of this function is to obtain a handle of type PHT_SERVER. The handle is required to
access most of the functionality within the Parallels C API. The syntax of PrlSrv_Create is as
follows:
The following is an example of the PrlSrv_Create function call:
// Declare a handle variable.
PRL_HANDLE hServer = PRL_INVALID_HANDLE;
// Call the PrlSrv_Create to obtain the handle.
PRL_RESULT res = PrlSrv_Create(&hServer);
// Examine the function return code.
// PRL_FAILED is a macro that evaluates a variable of type PRL_RESULT.
// A return value of True indicates success; False indicates failure.
if (PRL_FAILED(res))
{
printf("PrlSrv_Create returned error: %s\n",
prl_result_to_string(res));
exit(ret);
}
Asynchronous Functions
An asynchronous operation is executed in its own thread. An asynchronous function that started
the operation returns to the caller immediately without waiting for the operation to complete. The
results of the operation can be verified later when needed. Asynchronous functions return
PRL_HANDLE, which is a pointer to an integer and is a handle of type PHT_JOB. The handle is
used as a reference to the asynchronous job executed in the background. The general procedure
for calling an asynchronous function is as follows:
1 Register an event handler (callback function).
2 Call an asynchronous function.
3 Analyze the results of events (jobs) within the callback function.
4 Handle the appropriate event in the callback function.
5 Un-register the event handler when it is no longer needed.
19
Parallels C API Concepts
The Callback Function (Event Handler)
Asynchronous functions return data to the caller by means of an event handler (or callback
function). The callback function could be called at any time, depending on how long the
asynchronous function takes to complete. The callback function must have a specific signature.
The prototype can be found in PrlApi.h and is as follows:
typedef PRL_METHOD_PTR(PRL_EVENT_HANDLER_PTR) (
PRL_HANDLE hEvent,
PRL_VOID_PTR data
);
The following is an example of the callback function implementation:
// You must always release the handle before exiting.
PrlHandle_Free(handle);
}
A handle received by the callback function can be of type PHT_EVENT or PHT_JOB. The type can
be determined using the PrlHandle_GetType function. The PHT_EVENT type indicates that the
callback was called by a system event. If the type is PHT_JOB then the callback was called by an
asynchronous job started by the program.
To handle system events within a callback function:
1 Get the event type using PrlEvent_GetType.
2 Examine the event type. If it is relevant, a handle of type PHT_EVENT_PARAMETER can be
extracted using PrlEvent_GetParam.
3Convert the PHT_EVENT_PARAMETER handle to the appropriate handle type using
PrlEvtPrm_ToHandle.
To handle jobs within a callback function:
1 Get the job type using PrlJob_GetType. A job type can be used to identify the function that
started the job and to determine the type of the result it contains. For example, a job of type
PJOC_SRV_GET_VM_LIST is started by PrlSrv_GetVmList function call, which returns a
list of virtual machines.
2 Examine the job type. If it is relevant, proceed to the next step.
3 Get the job return code using PrlJob_GetRetCode. If it doesn't contain an error, proceed to
the next step.
4Get the result (a handle of type PHT_RESULT) from the job handle using
PrlJob_GetResult.
20
Parallels C API Concepts
5 Get a handle to the result using PrlResult_GetParam. Note that some functions return a list
(ie. there can be more than a single parameter in the result). For example,
PrlSrv_GetVmList returns a list of available virtual machines. In such cases, use
PrlResult_GetParamCount and PrlResult_GetParamByIndex.
6 Implement code to use the handle obtained in step 5.
Note: You must always free the handle that was passed to the callback function before exiting,
regardless of whether you actually used it or not. Failure to do so will result in a memory leak.
The following skeleton code demonstrates implementation of the above steps. In this example, the
objective is to handle events of type PET_DSP_EVT_HOST_STATISTICS_UPDATED that are
generated by a call to function PrlSrv_SubscribeToHostStatistics, and to obtain the
result from a job of type PJOC_SRV_GET_VM_LIST.
// Check if the event type is a statistics update.
if (EventType == PET_DSP_EVT_HOST_STATISTICS_UPDATED)
{
// Get handle to PHT_EVENT_PARAMETER.
PRL_HANDLE hEventParameters = PRL_INVALID_HANDLE;
PrlEvent_GetParam(hHandle, 0, &hEventParameters);
// Get handle to PHT_SYSTEM_STATISTICS.
PRL_HANDLE hServerStatistics = PRL_INVALID_HANDLE;
PrlEvtPrm_ToHandle(hEventParameters, &hServerStatistics);
// Code goes here to extract the statistics data
// using hServerStatistics.
PrlHandle_Free(hServerStatistics);
PrlHandle_Free(hEventParameters);
}
}
else if (nHandleType == PHT_JOB) // Job handle
{
// Get the job type.
PrlJob_GetOpCode(hHandle, &nJobType);
// Check if the job type is PJOC_SRV_GET_VM_LIST.
21
Parallels C API Concepts
if (nJobType == PJOC_SRV_GET_VM_LIST)
{
// Check the job return code.
PRL_RESULT nJobRetCode;
PrlJob_GetRetCode(hHandle, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "[B]%.8X: %s\n", nJobRetCode,
prl_result_to_string(nJobRetCode));
PrlHandle_Free(hHandle);
return nJobRetCode;
}
err = PrlJob_GetResult(hHandle, &hJobResult);
// if (err != PRL_ERR_SUCCESS), process the error here.
// Determine the number of parameters in the result.
PrlResult_GetParamsCount(hJobResult, &nParamsCount);
// Iterate through the parameter list.
for(nParamIndex = 0; nParamIndex < nParamsCount ; nParamIndex++)
{
// Obtain a virtual machine handle (PHT_VIRTUAL_MACHINE).
PrlResult_GetParamByIndex(hJobResult, nParamIndex, &hVm);
// Code goes here to obtain virtual machine info from hVm.
// Free the handle when done using it.
PrlHandle_Free(hVm);
}
PrlHandle_Free(hJobResult);
}
}
The PrlSrv_RegEventHandler function is used to register an event handler,
PrlSrv_UnregEventHandler is used to unregister an event handler.
Note: When an event handler is registered, it will receive all of the events/jobs regardless of their origin. It
is the responsibility of the program to identify the type of the event and to handle each one accordingly.
// Register an event handler.
ReturnDataClass rd; // some user-defined class.
PrlSrv_RegEventHandler(hServer, OurCallbackFunction, &rd);
// Make a call to an asynchronous function here.
// OurCallbackFunction will be called by the background thread
// as soon as the job is completed, and code within
// OurCallbackFunction can populate the ReturnDataClass instance.
// For example, we can make the following call here:
// Please note that we still have to obtain the
// job object (hJob above) and free it; otherwise
// we will have memory leaks.
// Unregister the event handler when it is no longer needed.
PrlSrv_UnregEventHandler(hServer, OurCallbackFunction, &rd);
Calling Asynchronous Functions Synchronously
It is possible to call an asynchronous function synchronously by using the PrlJob_Wait function.
The function takes two parameters: a PHT_JOB handle and a timeout value in milliseconds. Once
you call the function, the main thread will be suspended and the function will wait for the
asynchronous job to complete. The function will return when the job is completed or when timeout
value is reached, whichever comes first. The following code snippet illustrates how to call an
asynchronous function PrlServer_Login synchronously:
// Log in (PrlSrv_Login is asynchronous).
PRL_HANDLE hJob = PrlSrv_Login(
hServer,
szHostnameOrIpAddress,
szUsername,
szPassword,
0,
0,
0,
PSL_LOW_SECURITY);
// Wait for a maximum of 10 seconds for
// asynchronous function PrlSrv_Login to complete.
ret = PrlJob_Wait(hJob, 10000);
if (PRL_FAILED(ret))
{
fprintf(stderr, "PrlJob_Wait for PrlSrv_Login returned with error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
return -1;
}
// Analyse the result of the PrlServer_Login call.
PRL_RESULT nJobResult;
ret = PrlJob_GetRetCode(hJob, &nJobResult);
if (PRL_FAILED(nJobResult))
{
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
printf("Login job returned with error: %s\n",
prl_result_to_string(nJobResult));
return -1;
}
else
{
printf("login successfully performed\n");
}
23
Parallels C API Concepts
Strings as Return Values
Sting values in the Parallels C API are received by passing a char pointer to a function which
populates it with data. It is the responsibility of the caller to allocate the memory required to receive
the value, and to free it when it is no longer needed. Since in most cases we don't know the string
size in advance, we have to either allocate a chunk of memory large enough for any possible value
or to determine the exact required size. To determine the required buffer size, the following two
approaches can be used:
1 Calling the same function twice: first, to obtain the required buffer size, and second, to receive
the actual string value. To get the required buffer size, call the function passing a null pointer as
a value of the output parameter, and pass 0 (zero) as a value of the variable that is used to
specify the buffer size. The function will calculate the required size and will populate the variable
with the correct value, which you can use to initialize a variable that will receive the string. You
can then call the function again to get the actual string value.
2 It is also possible to use a static buffer. If the length of the buffer is large enough, you will simply
receive the result. If the length is too small, a function will fail with the
PRL_ERR_BUFFER_OVERRUN error but it will populate the "buffer_size" variable with the
required size value. You can then allocate the memory using the received value and call the
function again to get the results.
The PrlVmCfg_GetName function above is a typical Parallels API function that returns a string
value (in this case, the name of a virtual machine). The hVmCfg parameter is a handle to an object
containing the virtual machine configuration information. The sVmName parameter is a char
pointer. It is used as output that receives the virtual machine name. The variable must be initialized
on the program side with enough memory allocated for the expected string. The size of the buffer
must be specified using the pnVmNameBufLength variable.
The following example demonstrates how to call the function using the first approach:
PRL_RESULT ret;
PRL_UINT32 nBufSize = 0;
// Get the required buffer size.
ret = PrlVmCfg_GetName(hVmCfg, 0, &nBufSize);
// Allocate the memory.
PRL_STR pBuf = (PRL_STR)malloc(sizeof(PRL_CHAR) * nBufSize);
// Get the virtual machine name.
ret = PrlVmCfg_GetName(hVmCfg, pBuf, &nBufSize);
printf("VM name: %s\n", pBuf);
24
Parallels C API Concepts
// Deallocate the memory.
free(pBuf);
The following example uses the second approach. To test the buffer-overrun scenario, set the
// Get the virtual machine name.
ret = PrlVmCfg_GetName(hVmCfg, sVmName, &nBufSize);
// Check for errors.
if (PRL_SUCCEEDED(ret))
{
// Everything's OK, print the machine name.
printf("VM name: %s\n", sVmName);
}
else if (ret == PRL_ERR_BUFFER_OVERRUN)
{
// The sVmName array size is too small.
// Get the required size, allocate the memory,
// and try getting the VM name again.
PRL_UINT32 nSize = 0;
PRL_STR pBuf;
// Get the required buffer size.
ret = PrlVmCfg_GetName(hVmCfg, 0, &nSize);
// Allocate the memory.
pBuf = (PRL_STR)malloc(sizeof(PRL_CHAR) * nSize);
// Get the virtual machine name.
ret = PrlVmCfg_GetName(hVmCfg, pBuf, &nSize);
printf("VM name: %s\n", pBuf);
// Dallocate the memory.
free(pBuf);
}
Error Handling
Synchronous Functions
All synchronous Parallels C API functions return PRL_RESULT, which is an integer indicating
success or failure of the operation.
25
Parallels C API Concepts
Error Codes for Asynchronous Functions
All asynchronous functions return PRL_HANDLE. The error code (return value) in this case can be
extracted with PrlJob_GetRetCode after the asynchronous job has finished.
Analyzing Return Values
Parallels C API provides the following macros to work with error codes:
PRL_FAILED
PRL_SUCCEEDED
prl_result_to_string
Returns True if the return value indicates failure, or False if the
return value indicates success.
Returns True if the return value indicates success, or False if
the return value indicates failure.
Returns a string representation of the error code.
The following code snippet attempts to create a directory on the host and analyzes the return value
(error code) of asynchronous function PrlSrv_CreateDir.
// Attempt to create directory /tmp/TestDir on the host.
char *szRemoteDir = "/tmp/TestDir";
hJob = PrlSrv_FsCreateDir(hServer, szRemoteDir);
// Wait for a maximum of 5 seconds for asynchronous
// function PrlSrv_FsCreateDir to complete.
PRL_RESULT resWaitForCreateDir = PrlJob_Wait(hJob, 5000);
if (PRL_FAILED(resWaitForCreateDir))
{
fprintf(stderr, "PrlJob_Wait for PrlSvr_FsCreateDir failed with error: %s\n",
prl_result_to_string(resWaitForCreateDir));
PrlHandle_Free(hJob);
return -1;
}
// Extract the asynchronous function return code.
PrlJob_GetRetCode(hJob, &nJobResult);
if (PRL_FAILED(nJobResult))
{
fprintf(stderr, "Error creating directory %s. Error returned: %s\n",
szRemoteDir, prl_result_to_string(nJobResult));
PrlHandle_Free(hJob);
return -1;
}
Descriptive error messages can sometimes be obtained using the PrlJob_GetError function.
This function will return a handle to an object of type PHT_EVENT. In cases where PrlJob_GetError is unable to return error information, PrlApi_GetResultDescription
can be used. Although it is possible to avoid using PrlJob_GetError and use
PrlJob_GetResultDescription instead, it is recommended to first use PrlJob_GetError,
and if this doesn't return additional descriptive error information then use
PrlApi_GetResultDescription. The reason is that sometimes errors contain dynamic
parameters. The following example demonstrates how to obtain descriptive error information:
// Check if additional error information is available.
if (PRL_SUCCEEDED(ret)) // Additional error information is available.
{
// Additional error information is available.
ret = PrlEvent_GetErrString(hError, PRL_FALSE, PRL_FALSE, szErrBuff,
&nErrBuffSize);
if (PRL_FAILED(ret))
{
printf("PrlEvent_GetErrString returned error: %.8x %s\n",
ret, prl_result_to_string(ret));
}
else
{
// Extra error information is available, display it.
printf("Error returned: %.8x %s\n", nJobResult,
prl_result_to_string(nJobResult));
printf("Descriptive error: %s\n", szErrBuff);
}
}
else
{
// No additional error information available, so use
PrlApi_GetResultDescription.
ret = PrlApi_GetResultDescription(nJobResult, PRL_FALSE, PRL_FALSE, szErrBuff,
&nErrBuffSize);
if (PRL_FAILED(ret))
{
printf("PrlApi_GetResultDescription returned error: %s\n",
prl_result_to_string(ret));
}
else
{
printf("Error returned: %.8x %s\n", nJobResult,
prl_result_to_string(nJobResult));
printf("Descriptive error: %s\n", szErrBuff);
}
}
// Free handles, return the error code.
PrlHandle_Free(hJob);
27
Parallels C API Concepts
PrlHandle_Free(hError);
return nJobResult;
}
28
C HAPTER 3
Parallels C API by Example
This chapter contains examples of using the Parallels C API. The examples include tasks such as
performing general Parallels Desktop tasks, creating and managing virtual machines, handling
event, collecting performance data, and others.
In This Chapter
Obtaining Server Handle and Logging In ................................................................. 30
The following steps are required in any program using the Parallels C API:
1 Load the Parallels Virtualization SDK library using the SdkWrap_Load function.
2 Initialize the API using the PrlApi_InitEx function. The API must be initialized properly for
the given Parallels product, such as Parallels Server, Parallels Desktop. The initialization mode is
determined by the value of the nAppMode parameter passed to the PrlApi_InitEx
function. The value must be one of the enumerators from the PRL_APPLICATION_MODE
enumeration.
3 Create a server handle using the PrlSrv_Create function.
4 Call PrlSrv_LoginLocal or PrlSrv_Login to login to the Parallels Virtualization Service
(or simply Parallels Service). Parallels Service is a combination of processes running on the host
machine that comprise the Parallels virtualization product. The first function is used when the
program and the target Parallels Service are running on the same host. The second function
(PrlSrv_Login) is used to log in to a remote Parallels Service. Please note that remote login
is supported in Parallels Server-based virtualization products only.
If the above steps are executed without errors, the handle created in step 3 will reference a Server
object (a handle of type PHT_SERVER) identifying the Parallels Service. A handle to a valid Server
object is required to access most of the functionality within the Parallels C API. The
PrlSrv_LoginLocal function (step 4) establishes a connection with a specified Parallels Service
and performs a login operation using the specified credentials. The function operates on the Server
object created in step 3. Upon successful login, the object can be used to perform other
operations.
To end the session with the Parallels Service, the following steps must be performed before exiting
the application:
1 Call PrlSrv_Logoff to log off the Parallels Service.
2 Free the Server handle using PrlHandle_Free.
3 Call PrlApi_Deinit to de-initialize the library.
4 Call SdkWrap_Unload to unload the API.
Example
The following sample functions demonstrates how to perform the steps described above.
// Intializes the SDK library and
// logs in to the local Parallels Service.
// Obtains a handle of type PHT_SERVER identifying
// the Parallels Service.
// Initialize the API. In this example, we are initializing the
// API for Parallels Desktop.
// To initialize in the Parallels Desktop mode, pass PAM_DESKTOP
// as the second parameter.
// To initialize for Parallels Server, pass PAM_SERVER.
// See the PRL_APPLICATION_MODE enumeration for all possible options.
//
err = PrlApi_InitEx(PARALLELS_API_VER, PAM_WORKSTATION, 0, 0);
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlApi_InitEx returned with error: %s.\n",
prl_result_to_string(err));
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Create a server handle (PHT_SERVER).
err = PrlSrv_Create(&hServer);
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlSvr_Create failed, error: %s",
prl_result_to_string(err));
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Wait for a maximum of 10 seconds for
// the job to complete.
31
Parallels C API by Example
err = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(err))
{
fprintf(stderr,
"PrlJob_Wait for PrlSrv_Login returned with error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Analyze the result of PrlSrv_Login.
err = PrlJob_GetRetCode(hJob, &nJobReturnCode);
// First, check PrlJob_GetRetCode success/failure.
if (PRL_FAILED(err))
{
fprintf(stderr, "PrlJob_GetRetCode returned with error: %s\n",
prl_result_to_string(err));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Now check the Login operation success/failure.
if (PRL_FAILED(nJobReturnCode))
{
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
printf("Login job returned with error: %s\n",
prl_result_to_string(nJobReturnCode));
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
else
{
printf( "Login was successful.\n" );
}
return 0;
}
// Log off the Parallels Service and
// deinitializes the SDK library.
//
PRL_RESULT LogOff(PRL_HANDLE &hServer)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
Obtaining a Problem Report ................................................................................... 52
Retrieving Host Configuration Information
The Parallels C API provides a set of functions to retrieve detailed information about a host
machine. This includes:
• CPU(s) - number of, mode, model, speed.
• Devices - disk drives, network interfaces, ports, sound.
• Operating system - type, version, etc.
• Memory (RAM) size.
This information can be used when modifying Parallels Service preferences, setting up devices
inside virtual machines, or whenever you need to obtain information about the resources available
on the physical host.
To retrieve this information, first obtain a handle of type PHT_SERVER_CONFIG and then use its
functions to get information about a particular resource. The following sample function
demonstrates how it is accomplished. The function accepts the hServer parameter which is a
server handle. For the example on how to obtain a server handle, see Obtaining Server Handle and Logging In (p. 30).
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// An asynchronous call that obtains a handle
// of type PHT_SERVER_CONFIG.
hJob = PrlSrv_GetSrvConfig(hServer);
// Wait for the job to complete.
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Analyze the result of PrlSrv_GetSrvConfig.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Get the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Get job result.
ret = PrlJob_GetResult(hJob, &hJobResult);
PrlHandle_Free(hJob);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Get the PHT_SERVER_CONFIG handle.
ret = PrlResult_GetParam(hJobResult, &hHostConfig);
PrlHandle_Free(hJobResult);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Obtain the individual configuration setting.
printf("\nHost Configuration Information: \n\n");
// Get CPU count.
PRL_UINT32 nCPUcount = 0;
ret = PrlSrvCfg_GetCpuCount(hHostConfig, &nCPUcount);
if (PRL_FAILED(ret))
{
fprintf(stderr, "Error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hHostConfig);
return -1;
}
printf("CPUs: %d\n", nCPUcount);
Parallels C API by Example
35
Parallels C API by Example
// Get host OS type.
PRL_HOST_OS_TYPE nHostOsType;
ret = PrlSrvCfg_GetHostOsType(hHostConfig, &nHostOsType);
// if (PRL_FAILED(ret)) { handle the error... }
printf("OS Type: %d\n", nHostOsType);
// Get host RAM size.
PRL_UINT32 nHostRamSize;
ret = PrlSrvCfg_GetHostRamSize(hHostConfig, &nHostRamSize);
// if (PRL_FAILED(ret)) { handle the error... }
printf("RAM: %d MB\n", nHostRamSize);
// Get the network adapter info.
// First get the net adapter count.
PRL_UINT32 nNetAdaptersCount = 0;
ret = PrlSrvCfg_GetNetAdaptersCount(hHostConfig,
&nNetAdaptersCount);
// if (PRL_FAILED(ret)) { handle the error... }
// Now iterate through the list and get the info
// about each adapter.
printf("\n");
for (PRL_UINT32 i = 0; i < nNetAdaptersCount; ++i)
{
printf("Net Adapter %d\n", i+1);
// Obtains a handle of type PHT_HW_NET_ADAPTER.
PRL_HANDLE phDevice = PRL_INVALID_HANDLE;
ret = PrlSrvCfg_GetNetAdapter(hHostConfig, i, &phDevice);
// Get adapter type (physical, virtual).
PRL_HW_INFO_NET_ADAPTER_TYPE nNetAdapterType;
ret = PrlSrvCfgNet_GetNetAdapterType(phDevice,
&nNetAdapterType);
printf("Type: %d\n", nNetAdapterType);
// Get system adapter index.
PRL_UINT32 nIndex = 0;
ret = PrlSrvCfgNet_GetSysIndex(phDevice, &nIndex);
printf("Index: %d\n\n", nIndex);
}
PrlHandle_Free(hHostConfig);
return 0;
}
Managing Parallels Service Preferences
Parallels Service preferences is a set of parameters that control its default behaviour. Some of the
important parameters are:
• Memory limits for the Parallels Service itself.
• Memory limits and recommended values for virtual machines.
36
Parallels C API by Example
• Virtual network adapter information.
• Default virtual machine directory (the directory where all new virtual machines are created by
default).
• Communication security level.
Parallels Service preferences are managed using the PHT_DISP_CONFIG handle which is obtained
using the PrlSrv_GetCommonPrefs function. For the complete list of functions provided by the
PHT_DISP_CONFIG object, see the Parallels C API Reference guide.
The following sample function demonstrates how to obtain a handle of type PHT_DISP_CONFIG
and how to use its functions to retrieve and modify some of the Parallels Service preferences. The
function accepts the hServer parameter which is a server handle. For the example on how to
obtain a server handle, see Obtaining Server Handle and Logging In (p. 30).
// Variables for return codes.
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// An asynchronous call that obtains a handle
// of type PHT_DISP_CONFIG.
hJob = PrlSrv_GetCommonPrefs(hServer);
// Wait for the job to complete.
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Analyze the result of PrlSrv_GetCommonPrefs.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Get the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Get job result.
ret = PrlJob_GetResult(hJob, &hJobResult);
PrlHandle_Free(hJob);
37
Parallels C API by Example
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Get the PHT_DISP_CONFIG handle.
ret = PrlResult_GetParam(hJobResult, &hDispConfig);
PrlHandle_Free(hJobResult);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Get the default virtual machine directory.
char sDefaultDir[1024];
PRL_UINT32 nBufSize = sizeof(sDefaultDir);
ret = PrlDispCfg_GetDefaultVmDir(hDispConfig,
sDefaultDir, &nBufSize);
printf("Parallels Service Preferences \n\n");
printf("Default VM Directory: %s\n", sDefaultDir);
// Get the recommended virtual machine memory size.
PRL_UINT32 nMemSize = 0;
ret = PrlDispCfg_GetRecommendMaxVmMem(hDispConfig, &nMemSize);
if (PRL_FAILED(ret))
{
fprintf(stderr, "Error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hDispConfig);
return -1;
}
printf("Recommended VM memory size: %d\n", nMemSize);
// Modify some of the Parallels Service preferences.
// Begin edit.
hJob = PrlSrv_CommonPrefsBeginEdit(hServer);
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
fprintf(stderr, "Error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
PrlHandle_Free(hDispConfig);
return -1;
}
// Get the "begin edit" operation success code.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
// Modify the recommended virtual machine memory size.
nMemSize = 512;
ret = PrlDispCfg_SetRecommendMaxVmMem(hDispConfig, nMemSize);
if (PRL_FAILED(ret))
{
fprintf(stderr, "Error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hDispConfig);
return -1;
}
// Commit the changes.
hJob = PrlSrv_CommonPrefsCommit(hServer, hDispConfig);
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
fprintf(stderr, "Error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
PrlHandle_Free(hDispConfig);
return -1;
}
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
fprintf(stderr, "Error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
PrlHandle_Free(hDispConfig);
return -1;
}
if (PRL_FAILED(nJobReturnCode))
{
fprintf(stderr, "Error: %s\n",
prl_result_to_string(nJobReturnCode));
PrlHandle_Free(hJob);
PrlHandle_Free(hDispConfig);
return -1;
}
printf("The recommended VM memory size changed to: %d\n", nMemSize);
39
Parallels C API by Example
PrlHandle_Free(hDispConfig);
return 0;
}
Searching for Parallels Servers
This topic applies to Parallels Server only.
If you have multiple Parallels Servers running on your network and don't know their exact locations
and/or connection parameters, you can search for them using the
PrlSrv_LookupParallelsServers function. The function returns the information as a list of
handles of type PHT_SERVER_INFO, each containing the information about an individual Parallels
Server. The information includes host name, port number, version of the OS that a host is running,
Parallels Server version number, and the global ID (UUID). This information can then be used to
establish a connection with the Parallels Server of interest (you will have to know the correct user
name and password in addition to the returned parameters).
The PrlSrv_LookupParallelsServers function can be executed asynchronously using the
callback functionality or it can be used synchronously. To use the function asynchronously, you
must implement a callback function first. The callback function pointer must then be passed to the
PrlSrv_LookupParallelsServers as a parameter. During the search operation, the callback
function will be called for every Parallels Server found and a handle of type PHT_SERVER_INFO
containing the Parallels Server information will be passed to it. Searching an entire local area
network can take a significant time, so using a callback is the recommended approach.
To use the PrlSrv_LookupParallelsServers function synchronously, pass a null pointer
instead of the callback function pointer, and use PrlJob_Wait to wait for the job to complete.
The returned job object will contain a list of PHT_SERVER_INFO objects.
Note: The PrlSrv_LookupParallelsServers function can be executed without being logged in to
a Parallels Service. For example, if you are writing an application with a user interface, you can search the
network for available Parallels Servers and present the list to the user so that he/she can select a server
to connect to.
The following sample functions demonstrate how to search local network for Parallels Servers. The
first sample function calls the PrlSrv_LookupParallelsServers function synchronously. The
second function takes an asynchronous approach.
// Variables for return codes.
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Search for Parallels servers.
40
Parallels C API by Example
hJob = PrlSrv_LookupParallelsServers(
1000, // timeout
NULL, // callback function (not used)
NULL // user object pointer (not used)
);
// Wait for the job to complete.
ret = PrlJob_Wait(hJob, 10000);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
fprintf(stderr, "Error: %s. \n",
prl_result_to_string(ret));
return -1;
}
// Analyze the result of PrlSrv_LookupParallelsServers.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Get the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Get job result.
ret = PrlJob_GetResult(hJob, &hJobResult);
PrlHandle_Free(hJob);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Get the number of objects returned.
PRL_UINT32 nCount = 0;
PrlResult_GetParamsCount(hJobResult, &nCount);
// Iterate and a obtain handle to each object.
for (PRL_UINT32 i = 0; i < nCount ; ++i)
{
PRL_HANDLE hParam = PRL_INVALID_HANDLE;
PrlResult_GetParamByIndex(hJobResult, i, &hParam);
In the following example, the PrlSrv_LookupParallelsServers is called asynchronously. In
order to that, we first have to implement a callback function (we'll call it ourCallback):
Parallels Service doesn't have its own user database. It performs user authentication against the
host operating system user database. However, it has a user registry where the user information
that relates to Parallels Service operations is kept. The information includes user UUID (Universally
Unique ID), user name, the name and path of the virtual machine directory for the user, and two
flags indicating if a user is allowed to modify server preferences and use management console
application. A new user record is created in the registry for every user as soon as he/she logs in to
a Parallels Service for the very first time.
There are two API handles that are used to obtain information about Parallels Service users and to
modify some of the user profile parameters. These handles are PHT_USER_INFO and
PHT_USER_PROFILE. Both handles are containers that contain information about a user. The
difference between the two is PHT_USER_PROFILE is used to obtain information about currently
logged in user while PHT_USER_INFO is used to obtain information about a specified user. There
are also some differences in the type of the information provided.
42
Parallels C API by Example
Getting the information about the currently logged in user
The information about the currently logged in user can be retrieved using functions of the
PHT_USER_PROFILE handle. The following sample demonstrates how to obtain the handle and
how to use its functions to retrieve user information. The sample also shows how to set up a
default virtual machine directory for the user. Parallels Service automatically assigns a default virtual
machine directory (the directory where new virtual machines are created) for every new user. If
needed, a user can specify a different directory for his/her virtual machines. At the time of this
writing, this is the only property of the Parallels Service user profile that can be modified. Every user
profile modification must begin with the PrlSrv_UserProfileBeginEdit function call and end
with the PrlSrv_UserProfileCommit call. These two functions are used to prevent collisions
with other clients trying to modify the same user profile at the same time.
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Get user info from the server.
hJob = PrlSrv_GetUserProfile(hServer);
// Wait for the job to complete.
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Analyze the result of PrlSrv_GetUserProfile.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Get job result.
ret = PrlJob_GetResult(hJob, &hJobResult);
PrlHandle_Free(hJob);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
43
Parallels C API by Example
// Get the user profile handle (PHT_USER_PROFILE) from
// the result.
ret = PrlResult_GetParam(hJobResult, &hUserProfile);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJobResult);
return -1;
}
// Free job result handle.
PrlHandle_Free(hJobResult);
// See if the user is allowed to modify
// the Parallels server preferences.
PRL_BOOL bCanChange = PRL_FALSE;
ret = PrlUsrCfg_CanChangeSrvSets(hUserProfile, &bCanChange);
printf("Can modify server preferences: %d\n", bCanChange);
// See if the user is allowed to use management
// console application.
ret = PrlUsrCfg_CanUseMngConsole(hUserProfile, &bCanChange);
printf("Can use management console: %d\n", bCanChange);
// Get the default virtual machine folder
// for the user.
PRL_CHAR sBufFolder[1024];
PRL_UINT32 nBufSize = sizeof(sBufFolder);
ret = PrlUsrCfg_GetDefaultVmFolder(hUserProfile, sBufFolder, &nBufSize);
// If sBufFolder contains an empty string then this user
// does not have a default virtual machine folder and is
// currently using the default virtual machine folder set
// for this Parallels server. If this is the case, retrieve
// that folder.
if (sBufFolder == "")
{
ret = PrlUsrCfg_GetVmDirUuid(hUserProfile, sBufFolder, &nBufSize);
}
printf("VM folder: %s\n", sBufFolder);
// Modify the name and location of the virtual
// machine folder.
// This operation must begin with the
// PrlSrv_UserProfileBeginEdit that marks the
// beginning of the operation. This is done to
// prevent collisions with other sessions trying to
// modify the same profile at the same time.
hJob = PrlSrv_UserProfileBeginEdit(hServer);
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Analyze the result of PrlSrv_UserProfileBeginEdit.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
PrlHandle_Free(hJob);
44
Parallels C API by Example
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
return -1;
}
// Set the new virtual machine folder.
// The folder must already exist on the server.
ret = PrlUsrCfg_SetDefaultVmFolder(hUserProfile, "/Users/Shared/Parallels/JDoe");
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hUserProfile);
return -1;
}
// Finally, commit the changes to the server.
hJob = PrlSrv_UserProfileCommit(hServer, hUserProfile);
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Analyze the result of PrlSrv_UserProfileCommit.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
PrlHandle_Free(hJob);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
return -1;
}
PrlHandle_Free(hUserProfile);
}
Getting the information about a particular user
The information about a particular Parallels Service user can be obtained using the functions of the
PHT_USER_INFO handle. The handle can be obtain using one of the following functions:
PrlSrv_GetUserInfo or PrlSrv_GetUserInfoList. The first function takes the user UUID
as an input parameter and returns a single handle of type PHT_USER_INFO containing the user
information. The second function returns information about all users that exist in the Parallels
Service user registry. The information is returned as a list of handles of type PHT_USER_INFO.
45
Parallels C API by Example
The following sample uses the PrlSrv_GetUserInfoList function to obtain information about
all users in the Parallels Service user registry. It then iterates through the returned list of
PHT_USER_INFO handles and retrieves information about individual users.
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Get user info from the Parallels Service.
hJob = PrlSrv_GetUserInfoList(hServer);
// Wait for the job to complete.
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Analyze the result of PrlSrv_GetUserInfoList.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Get job result.
ret = PrlJob_GetResult(hJob, &hJobResult);
PrlHandle_Free(hJob);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Get parameter count (the number of PHT_USER_INFO
// handles in the result set).
PRL_UINT32 nParamCount = 0;
ret = PrlResult_GetParamsCount(hJobResult, &nParamCount);
// Iterate through the list obtaining
// a handle of type PHT_USER_INFO for
// each user.
for (PRL_UINT32 i = 0; i < nParamCount; ++i)
{
ret = PrlResult_GetParamByIndex(hJobResult, i, &hUserInfo);
46
Parallels C API by Example
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Get user UUID.
PRL_CHAR sBufID[1024];
PRL_UINT32 nBufSize = sizeof(sBufID);
ret = PrlUsrInfo_GetUuid(hUserInfo, sBufID, &nBufSize);
printf("UUID: %s\n", sBufID);
// Get user name.
PRL_CHAR sBufName[1024];
nBufSize = sizeof(sBufName);
PrlUsrInfo_GetName(hUserInfo, sBufName, &nBufSize);
printf("Name: %s\n", sBufName);
// Get default virtual machine folder
// for the user.
PRL_CHAR sBufFolder[1024];
nBufSize = sizeof(sBufFolder);
PrlUsrInfo_GetDefaultVmFolder(hUserInfo, sBufFolder, &nBufSize);
printf("VM folder: %s\n", sBufFolder);
// See if the user is allowed to modify
// the Parallels server preferences.
PRL_BOOL bCanChange = PRL_FALSE;
PrlUsrInfo_CanChangeSrvSets(hUserInfo, &bCanChange);
printf("Can modify server preferences: %d\n\n", bCanChange);
The following file management operations can be performed using the Parallels C API on the host
machine:
• Obtaining a directory listing.
• Creating directories.
• Automatically generate unique names for new file system entries.
• Rename file system entries.
• Delete file system entries.
The file management functionality can be accessed through the PHT_SERVER handle. The file
management functions are prefixed with "PrlSrv_Fs".
47
Parallels C API by Example
Obtaining the host OS directory listing
The directory listing is obtained using the PrlSrv_FsGetDirEntries function. The function
returns a handle of type PHT_REMOTE_FILESYSTEM_INFO containing the information about the
specified file system entry and its immediate child entries (if any). The child entries are returned as a
list of handles of type PHT_REMOTE_FILESYSTEM_ENTRY which is included in the
PHT_REMOTE_FILESYSTEM_INFO object. The sample function below demonstrates how to
obtain a listing for the specified directory. On initial call, the function obtains a list of child entries
(files and sub-directories) for the specified directory and is then called recursively for each file
system entry returned. On completion, the entire directory tree will be displayed on the screen.
// Obtains the entire directory tree in the host OS
// starting at the specified path.
// The "levels" parameter specifies how many levels should the
// function traverse down the directory tree.
PRL_RESULT GetHostDirList(PRL_HANDLE hServer, PRL_CONST_STR path, int levels)
{
PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
PRL_HANDLE hParentDirectory = PRL_INVALID_HANDLE;
PRL_HANDLE hChildElement = PRL_INVALID_HANDLE;
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Get directory list from the host.
// The second parameter specifies the absolute
// path for which to get the directory listing.
hJob = PrlSrv_FsGetDirEntries(hServer, path);
// Wait for the job to complete.
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Analyze the result of PrlSrv_FsGetDirEntries.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Get job result.
ret = PrlJob_GetResult(hJob, &hJobResult);
PrlHandle_Free(hJob);
48
Parallels C API by Example
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Get a handle to the parent directory.
// This is the directory that we specified in the
// PrlSrv_FsGetDirEntries call above.
ret = PrlResult_GetParam(hJobResult, &hParentDirectory);
PrlHandle_Free(hJobResult);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Get parameter count (the number of child entries).
PRL_UINT32 nParamCount = 0;
ret = PrlFsInfo_GetChildEntriesCount(hParentDirectory, &nParamCount);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Iterate through the list obtaining
// a handle of type PHT_REMOTE_FILESYSTEM_ENTRY
// for each child element of the parent directory.
for (PRL_UINT32 i = 0; i < nParamCount; ++i)
{
// Get a handle to the child element.
ret = PrlFsInfo_GetChildEntry(hParentDirectory, i, &hChildElement);
if (PRL_FAILED(ret))
{
// Handle the error...
continue;
}
// Get the filesystem element name.
PRL_CHAR sBuf[1024];
PRL_UINT32 nBufSize = sizeof(sBuf);
ret = PrlFsEntry_GetAbsolutePath(hChildElement, sBuf, &nBufSize);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hChildElement);
continue;
}
// Recursive call. Obtains directory listing for
// the entry returned in this iteration.
if (levels > 0 || levels <= -1)
{
int count = levels - 1;
GetHostDirList(hServer, sBuf, count);
}
The Parallels license information can be retrieved using the PrlSrv_GetLicenseInfo function.
The function returns a handle of type PHT_LICENSE containing the license details. The handle
provides a set of functions to retrieve the details. To install or update a license, use the
PrlSrv_UpdateLicense function.
The following sample function demonstrates how to obtain license information and how to install a
new license.
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Get the license info from the Parallels Service.
hJob = PrlSrv_GetLicenseInfo(hServer);
// Wait for the job to complete.
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Analyze the result of PrlSrv_GetUserInfoList.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Get job result.
ret = PrlJob_GetResult(hJob, &hJobResult);
PrlHandle_Free(hJob);
if (PRL_FAILED(ret))
{
50
Parallels C API by Example
// Handle the error...
return -1;
}
// Get parameter from job result.
ret = PrlResult_GetParam(hJobResult, &hLicense);
PrlHandle_Free(hJobResult);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Get company name.
PRL_CHAR sCompany[1024];
PRL_UINT32 nCompanyBufSize = sizeof(sCompany);
ret = PrlLic_GetCompanyName(hLicense, sCompany, &nCompanyBufSize);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hLicense);
return -1;
}
printf("Company: %s\n", sCompany);
// Get user name.
PRL_CHAR sUser[1024];
PRL_UINT32 nUserBufSize = sizeof(sUser);
ret = PrlLic_GetUserName(hLicense, sUser, &nUserBufSize);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hLicense);
return -1;
}
printf("User: %s\n", sUser);
// Get license key.
PRL_CHAR sKey[1024];
PRL_UINT32 nKeyBufSize = sizeof(sKey);
ret = PrlLic_GetLicenseKey(hLicense, sKey, &nKeyBufSize);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hLicense);
return -1;
}
printf("Key: %s\n", sKey);
// See license type.
PRL_BOOL isTrial = PRL_TRUE;
ret = PrlLic_IsTrial(hLicense, &isTrial);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hLicense);
return -1;
}
printf("Trial: %d\n", isTrial);
PrlHandle_Free(hLicense);
51
Parallels C API by Example
// Update the license info.
// Here, we use the same license information that we
// retrieved earlier. Normally, you would use the
// information that you received from
// your Parallels product distributor.
hUpdateLicense = PrlSrv_UpdateLicense(hServer, sKey,
sUser, sCompany);
// Wait for the job to complete.
ret = PrlJob_Wait(hUpdateLicense, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
PrlHandle_Free(hUpdateLicense);
}
Obtaining a Problem Report
If you are experiencing a problem with a virtual machine, you can obtain a problem report from the
Parallels Service. The report can then be sent to the Parallels technical support for evaluation. A
problem report contains technical data about your Parallels product installation, log data, and other
technical details that can be used to determine the source of the problem and to develop a
solution. The following example demonstrates how to obtain the report.
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Get problem report from the host.
hJob = PrlSrv_GetProblemReport(hServer);
// Wait for the job to complete.
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Analyze the result of PrlSrv_GetProblemReport.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
PrlHandle_Free(hJob);
52
Parallels C API by Example
return -1;
}
// Get job result.
ret = PrlJob_GetResult(hJob, &hJobResult);
PrlHandle_Free(hJob);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Get the string containing the report data.
// First, get the required buffer size.
PRL_UINT32 nBufSize = 0;
ret = PrlResult_GetParamAsString(hJobResult, NULL, &nBufSize);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJobResult);
return -1;
}
// Second, initialize the buffer and get the report.
char* sReportData =(PRL_STR)malloc(nBufSize);
ret = PrlResult_GetParamAsString(hJobResult, sReportData, &nBufSize);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJobResult);
return ret;
}
Managing User Access Rights ................................................................................ 90
Working with Virtual Machine Templates ................................................................. 93
Obtaining the Virtual Machines List
Any virtual machine operation begins with obtaining a handle of type PHT_VIRTUAL_MACHINE
identifying the virtual machine. Once a handle identifying a virtual machine is obtained, its functions
can be used to perform a full range of virtual machine operations. This sections describes how to
obtain a list of handles, each identifying an individual virtual machines registered with a Parallels
Service. If you would like to search for unregistered virtual machines on the host computer, please
refer to the Searching for Virtual Machines section (p. 68).
The steps that must be performed to obtain the virtual machine list are:
1Log in to the Parallels Service and obtain a handle of type PHT_SERVER. See Obtaining
Server Handle and Logging In (p. 30) for more info and code samples.
2Call PrlSrv_GetVmList. This is an asynchronous function that returns a handle of type
PHT_JOB.
3 Call PrlJob_GetResults passing the PHT_JOB object obtained in step 2. This function
returns a handle of type PHT_RESULT containing the virtual machine list.
4 Free the job handle using PrlHandle_Free as it is no longer needed.
5 Call PrlResult_GetParamsCount to determine the number of virtual machines contained in
the PHT_RESULT object.
Parallels C API by Example
6 Call the PrlResult_GetParamByIndex function in a loop passing an index from 0 to the
total virtual machine count. The function obtains a handle of type PHT_VIRTUAL_MACHINE
containing information about an individual virtual machine.
7 Use functions of the PHT_VIRTUAL_MACHINE object to obtain the virtual machine information.
For example, use PrlVmCfg_GetName to obtain the virtual machine name.
8 Free the virtual machine handle using PrlHandle_Free.
9 Free the result handle using PrlHandle_Free.
The following sample function implements the steps described above.
// Variables for return codes.
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Get the list of the available virtual machines.
hJob = PrlSrv_GetVmList(hServer);
// Wait for a maximum of 10 seconds for PrlSrv_GetVmList.
ret = PrlJob_Wait(hJob, 10000);
if (PRL_FAILED(ret))
{
fprintf(stderr,
"PrlJob_Wait for PrlSrv_GetVmList returned with error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
return ret;
}
// Check the results of PrlSrv_GetVmList.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
fprintf(stderr, "PrlJob_GetRetCode returned with error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
return ret;
}
if (PRL_FAILED(nJobReturnCode))
{
fprintf(stderr, "PrlSrv_GetVmList returned with error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
return ret;
}
// Get the results of PrlSrv_GetVmList.
ret = PrlJob_GetResult(hJob, &hJobResult);
if (PRL_FAILED(ret))
{
fprintf(stderr, "PrlJob_GetResult returned with error: %s\n",
// Handle to the result object is available,
// job handle is no longer needed, so free it.
PrlHandle_Free(hJob);
// Iterate through the results (list of virtual machines returned).
PRL_UINT32 nParamsCount = 0;
ret = PrlResult_GetParamsCount(hJobResult, &nParamsCount);
printf("\nVirtual Machines:\n");
for (PRL_UINT32 i = 0; i < nParamsCount; ++i)
{
PRL_HANDLE hVm = PRL_INVALID_HANDLE; // virtual machine handle
// Get a handle to the result at index i.
PrlResult_GetParamByIndex(hJobResult, i, &hVm);
// Now that we have a handle of type PHT_VIRTUAL_MACHINE,
// we can use its functions to retrieve or to modify the
// virtual machine information.
// As an example, we will get the virtual machine name.
char szVmNameReturned[1024];
PRL_UINT32 nBufSize = sizeof(szVmNameReturned);
ret = PrlVmCfg_GetName(hVm, szVmNameReturned, &nBufSize);
if (PRL_FAILED(ret))
{
printf("PrlVmCfg_GetName returned with error (%s)\n",
prl_result_to_string(ret));
}
else
{
printf(" (%d) %s\n\n", i+1, szVmNameReturned);
}
// Free the virtual machine handle.
PrlHandle_Free(hVm);
}
return PRL_ERR_SUCCESS;
}
Searching for Virtual Machine by Name
This section contains am example of how to obtain a handle of type PHT_VIRTUAL_MACHINE
identifying the virtual machine using the virtual machine name as a search parameter. We will use
the sample as a helper function in the later section of this guide that demonstrate how to perform
operations on virtual machines. The sample is based on the code provided in the Obtaining the Virtual Machine List section (p. 54).
// Obtains a handle of type PHT_VIRTUAL_MACHINE using the
// virtual machine name as a search parameter.
// Parameters
56
Parallels C API by Example
// hServer: A handle of type PHT_SERVER.
// sVmName: The name of the virtual machine.
// hVm: [out] A handle of type PHT_VIRTUAL_MACHINE
// identifying the virtual machine.
PRL_RESULT GetVmByName(PRL_HANDLE hServer, PRL_STR sVmName, PRL_HANDLE &hVm)
{
PRL_HANDLE hResult = PRL_INVALID_HANDLE;
PRL_RESULT nJobResult = PRL_INVALID_HANDLE;
// Get a list of available virtual machines.
PRL_HANDLE hJob = PrlSrv_GetVmList(hServer);
PRL_RESULT ret = PrlJob_Wait(hJob, 10000);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
return ret;
}
// Check the results of PrlSrv_GetVmList.
ret = PrlJob_GetRetCode(hJob, &nJobResult);
if (PRL_FAILED(nJobResult))
{
// Handle the error...
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
return ret;
}
// Get the results of PrlSrv_GetVmList.
ret = PrlJob_GetResult(hJob, &hResult);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
return ret;
}
PrlHandle_Free(hJob);
// Iteratre through the results (list of virtual machines returned).
PRL_UINT32 nParamsCount = 0;
ret = PrlResult_GetParamsCount(hResult, &nParamsCount);
for (PRL_UINT32 i = 0; i < nParamsCount; ++i)
{
// Get a handle to result i.
PrlResult_GetParamByIndex(hResult, i, &hVm);
// Get the name of the virtual machine for result i.
char vm_name[1024];
PRL_UINT32 nBufSize = sizeof(vm_name);
ret = PrlVmCfg_GetName(hVm, vm_name, &nBufSize);
if (PRL_FAILED(ret))
{
// Handle the error...
57
Parallels C API by Example
return PRL_ERR_FAILURE;
}
// If the name of the virtual machine at this index is equal to sVmName,
// then this is the handle we need.
if (strcmp(sVmName, vm_name) == 0)
{
PrlHandle_Free(hResult);
return PRL_ERR_SUCCESS;
}
// It's not the virtual machine being searched for, so free the handle to it.
PrlHandle_Free(hVm);
}
// The specified virtual machine was not found.
PrlHandle_Free(hResult);
return PRL_ERR_NO_DATA;
}
Obtaining Virtual Machine Configuration Information
The virtual machine configuration information is obtained using functions of the
PHT_VM_CONFIGURATION object. The functions are prefixed with PrlVmCfg_. To use the
functions, a handle of type PHT_VM_CONFIGURATION must first be obtained from the virtual
machine object (a handle of type PHT_VIRTUAL_MACHINE) using the PrlVm_GetConfig
function. The following example shows how to obtain the virtual machine name, guest operating
system type and version, RAM size, HDD size, and CPU count. To obtain the virtual machine
handle (hVm input parameter), use the helper function described in the Searching for Virtual Machine by Name section (p. 56).
PRL_RESULT GetVmConfig(PRL_HANDLE hVm)
{
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
// Obtain the PHT_VM_CONFIGURATION handle.
PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE;
ret = PrlVm_GetConfig(hVm, &hVmCfg);
if (PRL_FAILED(ret))
{
// Handle the error...
return ret;
}
// Get the virtual machine name.
PRL_STR szVmName;
PRL_UINT32 nVmNameSize = 0;
// Call once with NULL (PRL_INVALID_HANDLE) to get the size
// of the buffer to allocate for the VM name.
PrlVmCfg_GetName(hVmCfg, PRL_INVALID_HANDLE, &nVmNameSize);
// Allocate memory for the VM name.
szVmName = (PRL_STR)malloc(nVmNameSize);
58
Parallels C API by Example
// Get the virtual machine name.
PrlVmCfg_GetName(hVmCfg, szVmName, &nVmNameSize);
printf("Virtual machine name: %s\n", szVmName);
free(szVmName);
// Get the OS type.
PRL_UINT32 nOsType = 0;
PrlVmCfg_GetOsType(hVmCfg, &nOsType);
char* sOsTypeName;
switch (nOsType)
{
case PVS_GUEST_TYPE_WINDOWS:
sOsTypeName = "Windows";
printf("OS Type: %s\n", PVS_GUEST_TYPE_NAME_WINDOWS);
break;
case PVS_GUEST_TYPE_LINUX:
printf("OS Type: %s\n", PVS_GUEST_TYPE_NAME_LINUX);
break;
case PVS_GUEST_TYPE_MACOS:
printf("OS Type: %s\n", PVS_GUEST_TYPE_NAME_MACOS);
break;
case PVS_GUEST_TYPE_FREEBSD:
printf("OS Type: %s\n", PVS_GUEST_TYPE_NAME_FREEBSD);
break;
default:
printf("OS Type: %s: %d\n", "Other OS Type: ", nOsType);
}
// Get the OS version.
PRL_UINT32 nOsVersion = 0;
PrlVmCfg_GetOsVersion(hVmCfg, &nOsVersion);
printf("OS Version: %s\n", PVS_GUEST_TO_STRING(nOsVersion));
// Get CPU count.
PRL_UINT32 nCpuCount = 0;
PrlVmCfg_GetCpuCount(hVmCfg, &nCpuCount);
printf("Number of CPUs: %d\n", nCpuCount);
return PRL_ERR_SUCCESS;
}
59
Parallels C API by Example
Determining Virtual Machine State
To determine the current state of a virtual machine, first obtain a handle to the virtual machine as
described in the Obtaining a List of Virtual Machines section (p. 54). Then use the
PrlVmCfg_GetState function to obtain a handle of type PHT_VM_INFO and call the
PrlVmInfo_GetState function to obtain the state information. The function returns the virtual
machine state as an enumerator from the VIRTUAL_MACHINE_STATE enumeration that defines
every possible state and transition applicable to a virtual machine. The following table lists the
available states and transitions:
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Obtain the PHT_VM_CONFIGURATION handle.
PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE;
ret = PrlVm_GetConfig(hVm, &hVmCfg);
// Obtain a handle of type PHT_VM_INFO containing the
// state information. The object will also contain the
// virtual machine access rights info. We will discuss
// this functionality later in this guide.
hJob = PrlVm_GetState(hVmCfg);
// Wait for the job to complete.
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Analyze the result of PrlVm_GetState.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Get job result.
ret = PrlJob_GetResult(hJob, &hJobResult);
PrlHandle_Free(hJob);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Get the PHT_VM_INFO handle.
ret = PrlResult_GetParam(hJobResult, &hVmInfo);
PrlHandle_Free(hJobResult);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Get the virtual machine state.
VIRTUAL_MACHINE_STATE vm_state = VMS_UNKNOWN;
ret = PrlVmInfo_GetState(hVmInfo, &vm_state);
if (PRL_FAILED(ret))
{
// Handle the error...
switch (vm_state) {
case VMS_UNKNOWN:
printf("Unknown state\n");
break;
case VMS_STOPPED:
printf("Stopped\n");
break;
case VMS_STARTING:
printf("Starting...\n");
break;
case VMS_RESTORING:
printf("Restoring...\n");
break;
case VMS_RUNNING:
printf("Running\n");
break;
case VMS_PAUSED:
printf("Paused\n");
break;
case VMS_SUSPENDING:
printf("Suspending...\n");
break;
case VMS_STOPPING:
printf("Stopping...\n");
break;
case VMS_COMPACTING:
printf("Compacting...\n");
break;
case VMS_SUSPENDED:
printf("Suspended\n");
break;
default:
printf("Unknown state\n");
}
printf("\n");
PrlHandle_Free(hVmCfg);
PrlHandle_Free(hVmInfo);
return 0;
}
Starting, Stopping, Resetting a Virtual Machine
Note: When stopping or resetting a virtual machine, please be aware of the following important
information:
Stopping a virtual machine is not the same as performing a guest operating system shutdown operation.
When a virtual machine is stopped, it is a cold stop (i.e. it is the same as turning off the power to a
physical computer). Any unsaved data will be lost. However, if the OS in the virtual machine supports
ACPI (Advanced Configuration and Power Interface) then you can set the second parameter of the
PrlVm_Stop function to PRL_FALSE in which case, the ACPI will be used and the machine will be
properly shut down.
62
Parallels C API by Example
Resetting a virtual machine is not the same as performing a guest operating system restart operation. It
is the same as pressing the "Reset" button on a physical box. Any unsaved data will be lost.
The following sample function demonstrates how start, stop, and reset a virtual machine.
When a virtual machine is suspended, the information about its state is stored in non-volatile
memory. A suspended virtual machine can resume operating in the same state it was in at the point
it was placed into a suspended state. Resuming a virtual machine from a suspended state is
quicker than starting a virtual machine from a stopped state.
63
Parallels C API by Example
To suspend a virtual machine, obtain a handle to the virtual machine, then call PrlVm_Suspend.
The following example will suspend a virtual machine called Windows XP - 01.
const char *szVmName = "Windows XP - 01";
// Get a handle to virtual machine with name szVmName.
PRL_HANDLE hVm = GetVmByName((char*)szVmName, hServer);
if (hVm == PRL_INVALID_HANDLE)
{
fprintf(stderr, "Virtual machine \"%s\" was not found.\n", szVmName);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
exit(-1);
}
PRL_RESULT nJobResult;
PRL_HANDLE hJob = PrlVm_Suspend(hVm);
PRL_RESULT ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAIL(ret))
{
fprintf(stderr, "PrlJob_Wait for PrlVm_Suspend failed. Error: %s",
prl_result_to_string(ret));
PrlHandle_Free(hServer);
PrlHandle_Free(hJob);
PrlApi_Deinit();
SdkWrap_Unload();
exit(-1);
}
PrlJob_GetRetCode(hJob, &nJobResult);
if (PRL_FAILED(nJobResult))
{
fprintf(stderr, "PrlVm_Suspend failed with error: %s\n",
prl_result_to_string(nJobResult));
PrlHandle_Free(hVm);
PrlHandle_Free(hJob);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
A suspended virtual machine can be stopped completely (placed into a "stopped" state) using the
PrlVm_DropSuspendedState function.
Pausing a Virtual Machine
Pausing a virtual machine will pause execution of the virtual machine. This can be achieved using
PrlVm_Pause. PrlVm_Pause takes two parameters: a handle to the virtual machine, and a
boolean value indicating if ACPI should be used. The above example could be modified to pause a
virtual machine by replacing the line:
PRL_HANDLE hJob = PrlVm_Suspend(hVm);
with:
PRL_HANDLE hJob = PrlVm_Pause(hVm, PRL_FALSE);
64
Parallels C API by Example
It would also be necessary to change the error messages accordingly.
Resuming / Continuing a Virtual Machine
A suspended or paused virtual machine can be restarted using PrlVm_Start. Alternatively,
PrlVm_Resume can be used to resume execution of a suspended virtual machine.
Dropping Suspended State
A suspended virtual machine can be shut down using PrlVm_DropSuspendedState. If this is
used, any unsaved data will be lost.
Creating a New Virtual Machine
The first step in creating a new virtual machine is to create a blank virtual machine and register it
with the Parallels Service. A blank virtual machine is the equivalent of a hardware box with no
operating system installed on the hard drive. Once a blank virtual machine is created and
registered, it can be powered on and an operating system can be installed on it.
In this section, we will discuss how to create a typical virtual machine for a particular OS type using
a sample configuration. By using this approach, you can easily create a virtual machine without
knowing all of the little details about configuring a virtual machine for a particular operating system
type.
The steps involved in creating a typical virtual machine are:
1 Obtain a new handle of type PHT_VIRTUAL_MACHINE using the PrlSrv_CreateVm
function. The handle will identify our new virtual machine.
2 Obtain a handle of type PHT_VM_CONFIGURATION by calling the PrlVm_GetConfig
function. The handle is used for manipulating virtual machine configuration settings.
3 Set the default configuration parameters based on the version of the OS that you will later install
in the virtual machine. This step is performed using the PrlVmCfg_SetDefaultConfig
function. You supply the version of the target OS, and the function will generate the appropriate
configuration parameters automatically. The OS version parameter value is specified using
predefined macros. The names of the macros are prefixed with PVS_GUEST_VER_. You can
find the macro definitions in the C API Reference guide or in the PrlOses.h file. In addition to
the OS information, the PrlVmCfg_SetDefaultConfig function allows to specify the
physical host configuration which will be used to connect the virtual devices inside a virtual
machine to their physical counterparts. The devices include floppy disk drive, CD drive, serial
and parallel ports, sound card, etc. To connect the available host devices, obtain a handle of
type PHT_SERVER_CONFIG (physical host configuration) using the PrlSrv_GetSrvConfig
function. The handle should then be passed to PrlVmCfg_SetDefaultConfig together
with OS information and other parameters. If you don't want to connect the devices, set the
hSrvConfig parameter to PRL_INVALID_HANDLE.
65
Parallels C API by Example
4 Choose a name for the new virtual machine and set it using the PrlVmCfg_SetName function.
5 Modify some of the default configuration parameters if needed. For example, you may want to
modify the hard disk image type and size, the amount of memory available to the machine, and
the networking options. You will have to obtain an appropriate handle for the type of the
parameter that you would like to modify and call one of its functions to perform the modification.
The code sample below shows how to modify some of the default values.
6 Create and register the new machine using the PrlVm_Reg function. This step will create the
necessary virtual machine files on the host and register the machine with the Parallels Service.
The directory containing the virtual machine files will have the same name as the virtual machine
name. The directory will be created in the default location for this Parallels Service. If you would
like to create the virtual machine directory in a different location, you may specify the desired
parent directory name and path.
The following sample demonstrates how to create a new virtual machine. The sample assumes that
the client program has already obtained a server object handle (hServer) and performed the login
operation.
// Obtain a new virtual machine handle.
ret = PrlSrv_CreateVm(hServer, &hVm);
if (PRL_FAILED(ret))
{
// Error handling goes here...
return ret;
}
// Get the host config info.
hJob = PrlSrv_GetSrvConfig(hServer);
ret = PrlJob_Wait(hJob, 10000);
// Check the return code of PrlSrv_GetSrvConfig.
PrlJob_GetRetCode(hJob, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJob);
PrlHandle_Free(hVm);
return nJobRetCode;
}
// Get a handle to the object containing the result of PrlSrv_GetSrvConfig,
// and then get a hosts configuration handle from it.
ret = PrlJob_GetResult(hJob, &hResult);
PRL_HANDLE hSrvCfg = PRL_INVALID_HANDLE;
PrlResult_GetParam(hResult, &hSrvCfg);
// Free job and result handles.
PrlHandle_Free(hJob);
PrlHandle_Free(hResult);
// Now that we have the host configuration data,
66
Parallels C API by Example
// we can set the default configuration for the new virtual machine.
ret = PrlVm_GetConfig(hVm, &hVmCfg);
ret = PrlVmCfg_SetDefaultConfig(
hVmCfg, // VM config handle.
hSrvCfg, // Host config data.
PVS_GUEST_VER_WIN_2003, // Target OS version.
PRL_TRUE); // Create and connect devices.
// Set the virtual machine name.
ret = PrlVmCfg_SetName(hVmCfg, "My Windows Server 2003");
// The following two calls demonstrate how to modify
// some of the default values of the virtual machine configuration.
// These calls are optional. You may remove them to use the default values.
//
// Set RAM size for the machine to 256 MB.
ret = PrlVmCfg_SetRamSize(hVmCfg, 256);
// Set virtual hard disk size to 20 GB.
// First, get the handle to the hard disk object using the
// PrlVmCfg_GetHardDisk function. The index of 0 is used
// because the default configuration has just one virtual hard disk.
// After that, use the handle to set the disk size.
PRL_HANDLE hHDD = PRL_INVALID_HANDLE;
ret = PrlVmCfg_GetHardDisk(hVmCfg, 0, &hHDD);
ret = PrlVmDevHd_SetDiskSize(hHDD, 20000);
// Create and register the machine with the Parallels Service.
// This is an asynchronous call. Returns a job handle.
hJob = PrlVm_Reg(hVm, // VM handle.
"", // VM root directory (using default).
PRL_TRUE); // Using non-interactive mode.
// Wait for the operation to complete.
ret = PrlJob_Wait(hJob, 10000);
// Check the return code of PrlVm_Reg.
PrlJob_GetRetCode(hJob, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJob);
PrlHandle_Free(hVmCfg);
PrlHandle_Free(hVm);
return nJobRetCode;
}
// Delete handles.
PrlHandle_Free(hJob);
67
Parallels C API by Example
PrlHandle_Free(hVmCfg);
PrlHandle_Free(hVm);
Searching for Virtual Machines
A host computer may have virtual machines on its hard drive that are not currently registered with
the Parallels Service. This can happen when a virtual machine is removed from the Parallels Service
registry but its files are kept on the drive, or when a virtual machine files are manually copied to the
drive from another computer. Parallels C API provides the PrlSrv_StartSearchVms function
that can be used to find such virtual machines on the specified host at the specified location on the
hard drive. The function accepts a string containing a directory name and path and searches the
directory and all its subdirectories for unregistered virtual machines. It then returns a list of
PHT_FOUND_VM_INFO handles, each containing information about an individual virtual machine
that it finds. You can then decide whether you want to keep the machine as-is, register it, or
remove it from the hard drive.
Since the search operation may take a long time (depending on the size of the specified directory
tree), the PrlSrv_StartSearchVms function should be executed using the callback functionality
(p. 19). The callback function will be called for every virtual machine found and a single instance of
the PHT_FOUND_VM_INFO handle will be passed to it. As we discussed earlier in this guide (p. 19),
a callback function can receive two types of objects: jobs (PHT_JOB) and events (PHT_EVENT). In
this instance, the information is passed to the callback function as an event of type
PET_DSP_EVT_FOUND_LOST_VM_CONFIG. To following steps are involved in processing the
event inside the callback function:
1Determine the type of the event using the PrlHandle_GetType function. If it is
PET_DSP_EVT_FOUND_LOST_VM_CONFIG then the data passed to the callback function
contains information about an unregistered virtual machine. If not, then the event was generated
by some other function and contains the data relevant to that function.
2 Use the PrlEvent_GetParam function to obtain a handle of type PHT_EVENT_PARAMETER
(this is a standard event processing step).
3 Use the PrlEvtPrm_ToHandle function to obtain a handle of type PHT_FOUND_VM_INFO
containing the virtual machine information.
4 Use functions of the PHT_FOUND_VM_INFO object to determine the location of the virtual
machine files, the virtual machine name, guest OS version, and some other information.
The following is an implementation of the steps above:
// If this is a job, release the handle and exit.
// Normally, we would process this, if we were after
// a job.
if (nHandleType == PHT_JOB)
{
68
Parallels C API by Example
PrlHandle_Free(hEvent);
return 0;
}
// If it's not a job, then it is an event (PHT_EVENT).
// Get the type of the event received.
PRL_EVENT_TYPE eventType;
PrlEvent_GetType(hEvent, &eventType);
// Check the event type. If it's what we are looking for, process it.
if (eventType == PET_DSP_EVT_FOUND_LOST_VM_CONFIG)
{
PRL_UINT32 nParamsCount = 0;
// this will receive the event parameter handle.
PRL_HANDLE hParam = PRL_INVALID_HANDLE;
// The PrlEvent_GetParam function obtains a handle of type
// PHT_EVENT_PARAMETER.
ret = PrlEvent_GetParam(hEvent, 0, &hParam);
if (PRL_FAILED(ret))
{
fprintf(stderr, "[4]%.8X: %s\n", ret,
prl_result_to_string(ret));
PrlHandle_Free(hParam);
PrlHandle_Free(hEvent);
return ret;
}
// Get the virtual machine name.
PRL_CHAR sName[1024];
PRL_UINT32 nBufSize = sizeof(sName);
ret = PrlFoundVmInfo_GetName(hFoundVmInfo, sName, &nBufSize);
printf("VM name: %s\n", sName);
// Get the name and path of the virtual machine directory.
PRL_CHAR sPath[1024];
nBufSize = sizeof(sPath);
ret = PrlFoundVmInfo_GetConfigPath(hFoundVmInfo, sPath, &nBufSize);
printf("Path: %s\n\n", sPath);
PrlHandle_Free(hFoundVmInfo);
PrlHandle_Free(hEvent);
return 0;
}
// The received event handler MUST be freed.
PrlHandle_Free(hEvent);
}
To begin the search operation, place the following code into your main program:
// Register the event handler.
PrlSrv_RegEventHandler(hServer, &callback, NULL);
// Create a string list object and populate it
// with the name and path of the directory to search.
PRL_HANDLE hStringList = PRL_INVALID_HANDLE;
ret = PrlApi_CreateStringsList(&hStringList );
ret = PrlStrList_AddItem(hStringList, "/Users/Shared/Parallels/");
// Begin the search operation.
hJob = PrlSrv_StartSearchVms(hServer, hStringList);
PrlHandle_Free(hJob);
In order for the callback function to be called, your program should have a global loop (the program
never exits on its own). The callback function will be called as soon as the first virtual machine is
found. If there are no unregistered virtual machines in the specified directory tree, then the function
will never be called as a PET_DSP_EVT_FOUND_LOST_VM_CONFIG event (it will still be called at
least once as a result of the started job and will receive the job object but this and possibly other
callback invocations are irrelevant in the context of this example).
Receiving the search results synchronously
It is also possible to use this function synchronously using the PrlJob_Wait function (p. 19). In
this case, the information is returned as a list of PHT_FOUND_VM_INFO objects contained in the
job object returned by the PrlSrv_StartSearchVms function. The following example
demonstrates how to call the function and to process results synchronously.
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Create a string list object and populate it
// with the name and path of the directory to search.
PRL_HANDLE hStringList = PRL_INVALID_HANDLE;
PrlApi_CreateStringsList(&hStringList );
PrlStrList_AddItem(hStringList, "/Users/Shared/Parallels/");
// Begin the search operation.
hJob = PrlSrv_StartSearchVms(hServer, hStringList);
// Wait for the job to complete.
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Analyze the result of PrlSrv_StartSearchVms.
70
Parallels C API by Example
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Get job result.
ret = PrlJob_GetResult(hJob, &hJobResult);
PrlHandle_Free(hJob);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Iterate through the returned list obtaining a
// handle of type PHT_FOUND_VM_INFO in each iteration containing
// the information about an individual virtual machine.
PRL_UINT32 nIndex, nCount;
PrlResult_GetParamsCount(hJobResult, &nCount);
for(nIndex = 0; nIndex < nCount ; nIndex++)
{
PrlResult_GetParamByIndex(hJobResult, nIndex, &hFoundVmInfo);
// Get the virtual machine name.
PRL_CHAR sName[1024];
PRL_UINT32 nBufSize = sizeof(sName);
ret = PrlFoundVmInfo_GetName(hFoundVmInfo, sName, &nBufSize);
printf("VM name: %s\n", sName);
// Get the name and path of the virtual machine directory.
PRL_CHAR sPath[1024];
nBufSize = sizeof(sPath);
ret = PrlFoundVmInfo_GetConfigPath(hFoundVmInfo, sPath, &nBufSize);
printf("Path: %s\n\n", sPath);
A host may have virtual machines that are not registered with the Parallels Service. This can
happen if a virtual machine was previously removed from the Parallels Service registry or if the
virtual machine files were manually copied from a different location. If you know the location of such
a virtual machine, you can easily register it with the Parallels Service using the
PrlSrv_RegisterVm function. The function accepts a server handle, name and path of the
directory containing the virtual machine files, and registers the machine.
Note: The PrlSrv_RegisterVm function does NOT generate new MAC addresses for the virtual
network adapters that already exist in the virtual machine. If the machine is a copy of another virtual
machine then you should set new MAC addresses for its network adapters after you register it. The
example at the end of this section demonstrates how this can be accomplished. For more information on
modifying an existing virtual machine, please see the Modifying Virtual Machine Configuration section
(p. 78).
The following sample function demonstrates how to add an existing virtual machine to the Parallels
Service. The function takes a handle of type PHT_SERVER and a string specifying the name and
path of the virtual machine directory. It registers the virtual machine with the Service and then
modifies the MAC address of every virtual network adapter installed in it.
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobRetCode = PRL_ERR_UNINITIALIZED;
// Register the virtual machine.
hJob = PrlSrv_RegisterVm(
hServer,
sVmDirectory,
PRL_TRUE); // Using non-interactive mode.
ret = PrlJob_Wait(hJob, 10000);
// Check the return code of PrlSrv_RegisterVm.
PrlJob_GetRetCode(hJob, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
printf("PrlSrv_RegisterVm returned error: %s\n",
prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJob);
return -1;
}
// Obtain the virtual machine handle from the job object.
// We will use the handle later to modify the virtual machine
// configuration.
PRL_HANDLE hVm = PRL_INVALID_HANDLE;
ret = PrlJob_GetResult(hJob, &hJobResult);
if (PRL_FAILED(ret))
{
// Handle the error...
72
Parallels C API by Example
return ret;
}
ret = PrlResult_GetParam(hJobResult, &hVm);
if (PRL_FAILED(ret))
{
// Handle the error...
return ret;
}
PrlHandle_Free(hJob);
PrlHandle_Free(hJobResult);
printf("Virtual machine '%s' was successfully registered.",
sVmDirectory);
// The following code will generate and set a new MAC address
// for every virtual network adapter that exists in the virtual machine.
// This step is optional and should normally be performed when the virtual
// machine is a copy of another virtual machine.
// Begin the virtual machine editing operation.
hJobBeginEdit = PrlVm_BeginEdit(hVm);
ret = PrlJob_Wait(hJobBeginEdit, 10000);
PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJobBeginEdit);
return nJobRetCode;
}
// Obtain a handle of type PHT_VM_CONFIGURATION containing the
// virtual machine configuration data.
PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE;
ret = PrlVm_GetConfig(hVm, &hVmCfg);
if (PRL_FAILED(ret))
{
// Handle the error...
return ret;
}
// Determine the number of the network adapters
// installed in the machine.
PRL_UINT32 nCount = PRL_ERR_UNINITIALIZED;
ret = PrlVmCfg_GetNetAdaptersCount(hVmCfg, &nCount);
if (PRL_FAILED(ret))
{
// Handle the error...
return ret;
}
// Itereate through the adapter list.
PRL_HANDLE hNetAdapter = PRL_INVALID_HANDLE;
for (PRL_UINT32 i = 0; i < nCount; ++i)
{
ret = PrlVmCfg_GetNetAdapter(hVmCfg, i, &hNetAdapter);
if (PRL_FAILED(ret))
73
Parallels C API by Example
{
// Handle the error...
return ret;
}
// Automatically generate new MAC address for the current adapter.
// The address will be updated in the configuration object.
ret = PrlVmDevNet_GenerateMacAddr(hNetAdapter);
if (PRL_FAILED(ret))
{
// Handle the error...
return ret;
}
}
// Commit the changes to the virtual machine.
hJobCommit = PrlVm_Commit(hVm);
// Check the results of the commit operation.
ret = PrlJob_Wait(hJobCommit, 10000);
PrlJob_GetRetCode(hJobCommit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "Commit error: %s\n", prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJobCommit);
return nJobRetCode;
}
A new virtual machine can also be created by cloning an existing virtual machine. The machine will
be created as an exact copy of the source virtual machine and will be automatically registered with
the Parallels Service. The cloning operation is performed using the PrlVm_Clone function. The
following parameters must be specified when cloning a virtual machine:
1 A valid handle of type PHT_VIRTUAL_MACHINE containing information about the source virtual
machine.
2 A unique name for the new virtual machine (the name is NOT generated automatically).
3 The name of the directory where the virtual machine files should be created (or an empty string
to create the files in the default directory).
4 A boolean value specifying whether to create the new machine as a valid virtual machine or as a
template. PRL_TRUE indicates to create a template. PRL_FALSE indicates to create a virtual
machine. See the Working with Virtual Machine Templates (p. 93) section for more virtual
machine info and examples.
74
Parallels C API by Example
The source virtual machine must be registered with the Parallels Service before it can be cloned.
The following sample function demonstrates how to clone an existing virtual machine. When testing
a function, the hVm parameter must contain a valid handle of type PHT_VIRTUAL_MACHINE (the
source virtual machine to clone). On completion, the new virtual machine should appear in the list
of registered virtual machines.
// Declare and populate variables that
// will be used as input parameters
// in the function that clones a VM.
// Virtual machine name.
// Get the name of the original VM and use
// it in the new virtual machine name. You can
// use any name that you like of course.
char vm_name[1024];
PRL_UINT32 nBufSize = sizeof(vm_name);
PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE;
ret = PrlVm_GetConfig(hVm, &hVmCfg);
ret = PrlVmCfg_GetName(hVmCfg, vm_name, &nBufSize);
char new_vm_name[1024] = "Clone of ";
strcat(new_vm_name, vm_name);
// Name of the target directory on the
// host.
// Empty string indicates that the default
// directory should be used.
PRL_CHAR_PTR new_vm_root_path = "";
// Virtual machine or template?
// The cloning functionality allows to create
// a new virtual machine or a new template.
// True indicates to create a template.
// False indicates to create a virtual machine.
// We are creating a virtual machine.
PRL_BOOL bCreateTemplate = PRL_FALSE;
// Begin the cloning operation.
hJob = PrlVm_Clone(hVm, new_vm_name, new_vm_root_path, bCreateTemplate);
// Wait for the job to complete.
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
printf("Error: (%s)\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
PrlHandle_Free(hVmCfg);
return -1;
}
// Analyze the result of PrlVm_Clone.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
75
Parallels C API by Example
{
// Handle the error...
PrlHandle_Free(hJob);
PrlHandle_Free(hVmCfg);
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
printf("Error: (%s)\n",
prl_result_to_string(nJobReturnCode));
PrlHandle_Free(hJob);
PrlHandle_Free(hVmCfg);
return -1;
}
PrlHandle_Free(hJob);
PrlHandle_Free(hVmCfg);
return 0;
}
Deleting a Virtual Machine
If a virtual machine is no longer needed, it can be removed. There are two options for removing a
virtual machine:
1 Un-register the virtual machine using PrlVm_Unreg. This will remove the virtual machine from
the list of the virtual machines registered with the Service. Once a virtual machine has been
unregistered it is not possible to use it. The directory containing the virtual machine files will
remain on the hard drive of the host computer, and the virtual machine can later be reregistered and used.
2 Delete the virtual machine using PrlVm_Delete. The virtual machine will be unregistered, and
the directory (or just some of its files that you can specify) will be deleted.
The following example demonstrates un-registering a virtual machine. Note that this example
makes use of a function called GetVmByName that can be found in the Obtaining a List of Virtual Machines section.
const char *szVmName = "Windows XP - 02";
// Get a handle to virtual machine with name szVmName.
PRL_HANDLE hVm = GetVmByName((char*)szVmName, hServer);
if (hVm == PRL_INVALID_HANDLE)
{
fprintf(stderr, "VM \"%s\"was not found.\n", szVmName);
PrlHandle_Free(hServer);
PrlApi_Deinit();
SdkWrap_Unload();
return -1;
}
// Unregister a virtual machine.
PRL_HANDLE hJob = PrlVm_Unreg(hVm);
PRL_RESULT ret = PrlJob_Wait(hJob, 10000);
if (PRL_FAILED(ret))
{
printf("PrlJob_Wait failed for PrlVm_Unreg. Error returned: %s\n",
from the above example. Note that this operation is irreversible.
77
Parallels C API by Example
Modifying Virtual Machine Configuration
The Parallels C API provides a complete set of functions to modify the configuration parameters of
an existing virtual machine. You can find the list of the available functions in the Parallels C API Reference guide by looking at the PHT_VM_CONFIGURATION group. Most of the get/set
functions in the group allow to obtain and modify the virtual machine configuration parameters.
Some parameters are handled as objects and require extra steps in getting or setting them. The
following subsections describe how to modify the most common configuration parameters and
provide code samples. The samples assume that:
• you've already obtained a handle to the server object and logged on to the Parallels Service.
• you've already obtained a handle to the virtual machine that you would like to modify.
Note: All operations on virtual machine devices (adding, modifying, removing) must be performed on a
stopped virtual machine. An attempt to modify the device configuration on a running machine will result in
error.
PrlVm_BeginEdit and PrlVm_Commit Functions
All virtual machine configuration changes must begin with the PrlVm_BeginEdit and end with
the PrlVm_Commit call. These two functions are used to detect collisions with other clients trying
to modify the configuration settings of the same virtual machine.
When PrlVm_BeginEdit is called, the Parallels Service timestamps the beginning of a
configuration change(s) operation. It does not lock the machine, so other clients can make changes
to the same virtual machine at the same time. The function will also automatically update your local
virtual machine object with the current virtual machine configuration information. This is done in
order to ensure that your local object contains the changes that might have have happened since
you obtained the virtual machine handle.
When you are done making the changes, you must call the PrlVm_Commit function. The first
thing that the function will do is verify that the virtual machine configuration has not been modified
by other client(s) since you called the PrlVm_BeginEdit function. If it has been, your changes
will be rejected and PrlVm_Commit will return with error. In such a case, you will have to reapply
your changes. In order to do that, you will have to get the latest configuration using the
PrlVm_GetConfig function, compare your changes with the latest changes, and make a
decision about merging them. Please note that PrlVm_GetConfig function will update the
configuration data in your current virtual machine object and will overwrite all existing data,
including the changes that you've made to it. Furthermore, the PrlVm_BeginEdit function will
also overwrite all existing data (see above). If you don't want to loose your data, save it locally
before calling PrlVm_GetConfig or PrlVm_BeginEdit.
The following example demonstrates how to use the PrlVm_BeginEdit and PrlVm_Commit
functions:
// Timestamps the beginning of the "transaction".
// Updates the hVm object with current configuration data.
hJobBeginEdit = PrlVm_BeginEdit(hVm);
ret = PrlJob_Wait(hJobBeginEdit, 10000);
PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJobBeginEdit);
return nJobRetCode;
}
// The code modifying configuration parameters goes here...
// Commits the changes to the virtual machine.
hJobCommit = PrlVm_Commit(hVm);
// Check the results of the commit operation.
ret = PrlJob_Wait(hJobCommit, 10000);
PrlJob_GetRetCode(hJobCommit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "Commit error: %s\n", prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJobCommit);
return nJobRetCode;
}
Obtaining a PHT_VM_CONFIGURATION handle
Before you can use any of the virtual machine configuration management functions, you have to
obtain a handle of type PHT_VM_CONFIGURATION. The handle is obtained from the virtual
machine object as shown in the following example:
PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE;
ret = PrlVm_GetConfig(hVm, &hVmCfg);
Once you have the handle, you can use its functions to manipulate the virtual machine configuration
settings. As usual, don't forget to free the handle when it is no longer needed.
Name, Description, Boot Options
The virtual machine name and description modifications are simple. They are performed using a
single call for each parameter:
// Modify VM name.
ret = PrlVm_GetConfig(hVm, &hVmCfg);
ret = PrlVmCfg_SetName(hVmCfg, "New Name1");
// Modify VM description.
ret = PrlVmCfg_SetDescription(hVmCfg, "My updated VM");
To modify the boot options (boot device priority), first make the PrlVmCfg_GetBootDevCount
call to determine the number of the available devices. Then obtain a handle to each device by
making the PrlVmCfg_GetBootDev call in a loop. To place a device at the specified position in
the boot device priority list, use the PrlBootDev_SetSequenceIndex function passing the
device handle and the index (0 - first boot device, 1 - second boot device, and so forth).
79
Parallels C API by Example
The following sample illustrates how to make the above modifications.
// Timestamp the beginning of the transaction.
hJobBeginEdit = PrlVm_BeginEdit(hVm);
ret = PrlJob_Wait(hJobBeginEdit, 10000);
PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJobBeginEdit);
return nJobRetCode;
}
// Modify VM name.
ret = PrlVmCfg_SetName(hVmCfg, "New Name1");
// Modify VM description.
ret = PrlVmCfg_SetDescription(hVmCfg, "My updated VM");
// Modify boot options.
// Set boot device list as follows:
// 0. CD/DVD drive.
// 1. Hard disk.
// 2. Network adapter.
// 3. Floppy disk drive.
// Remove all other devices (if any) from the
// boot devices list for this VM.
// Device count.
PRL_UINT32 nDevCount;
// A handle identifying the device.
PRL_HANDLE hDevice = PRL_INVALID_HANDLE;
// Device type.
PRL_DEVICE_TYPE devType;
// Get the total number of devices.
ret = PrlVmCfg_GetBootDevCount(hVmCfg, &nDevCount);
// Iterate through the device list.
// Get a handle for each available device.
// Set an index for a device in the boot list.
for (int i = 0; i < nDevCount; ++i)
{
ret = PrlVmCfg_GetBootDev(hVmCfg, i, &hDevice);
ret = PrlBootDev_GetType(hDevice, &devType);
if (devType == PDE_OPTICAL_DISK)
{
PrlBootDev_SetSequenceIndex(hDevice, 0);
}
if (devType == PDE_HARD_DISK)
{
PrlBootDev_SetSequenceIndex(hDevice, 1);
}
else if (devType == PDE_GENERIC_NETWORK_ADAPTER)
// Commit the changes.
hJobCommit = PrlVm_Commit(hVm);
// Check the results of the commit operation.
ret = PrlJob_Wait(hJobCommit, 10000);
PrlJob_GetRetCode(hJobCommit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "Commit error: %s\n", prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJobCommit);
return nJobRetCode;
}
RAM Size
The size of the memory available to the virtual machine is performed using the
PrlVmCfg_SetRamSize function. The first parameter is the virtual machine handle and the
second parameter is the new RAM size in megabytes:
PrlVmCfg_SetRamSize(hVmCfg, 512);
Hard Disks
Modifying the size of the existing hard disk image
A virtual machine may have more than one virtual hard disk. To select a disk that you would like to
modify, first retrieve the list of the available disks, as shown in the following example:
// Get the number of disks available.
PrlVmCfg_GetHardDisksCount(hVmCfg, &nCount);
// Iterate through the list.
for (PRL_UINT32 i = 0; i < nCount; ++i)
{
// Obtain a handle to the hard disk object.
ret = PrlVmCfg_GetHardDisk(hVmCfg, i, &hHDD);
// The code selecting the desired HDD goes here...
// {
// Modify the disk size.
// The hard disk size is specified in megabytes.
ret = PrlVmDevHd_SetDiskSize(hHDD, 20000);
// }
81
Parallels C API by Example
}
Adding a new hard disk
In this example, we will add a hard disk to a virtual machine. The following options are available:
• You may create new or use an existing image file for your new disk.
• Creating a dynamically expanding or a fixed-size disk. The expanding drive image will be initially
created with a size of zero. The space for it will be allocated dynamically on as-needed basis.
The space for the fixed-size disk will be allocated fully at the time of creation.
• Choosing the maximum disk size.
Creating a new image file
In the first example, we will create a new disk image and will add it to a virtual machine.
// Timestamp the beginning of the configuration changes operation.
// The hVm specifies the virtual machine that we'll be editing.
//
hJobBeginEdit = PrlVm_BeginEdit(hVm);
ret = PrlJob_Wait(hJobBeginEdit, 10000);
PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJobBeginEdit);
return nJobRetCode;
}
// Create a new device handle.
// This will be our new virtual hard disk.
PRL_HANDLE hHDD = PRL_INVALID_HANDLE;
ret = PrlVmCfg_CreateVmDev(
hVmCfg, // The target virtual machine.
PHT_VIRTUAL_DEV_HARD_DISK, // Device type.
&hHDD); // Device handle.
// Set disk type to "expanding".
ret = PrlVmDevHd_SetDiskType(hHDD, PHD_EXPANDING_HARD_DISK);
// Set max disk size, in megabytes.
ret = PrlVmDevHd_SetDiskSize(hHDD, 32000);
// This option determines whether the image file will be splitted
// into chunks or created as a single file.
ret = PrlVmDevHd_SetSplitted(hHDD, PRL_FALSE);
// Choose and set the name for the new image file.
// We must set both the "friendly" name and the "system" name.
// For a virtual device, use the name of the new image file in both
// functions. By default, the file will be
// created in the virtual machine directory. You may specify a
// full path if you want to place the file in a different
// directory.
82
Parallels C API by Example
//
ret = PrlVmDev_SetFriendlyName(hHDD, "harddisk4.hdd");
ret = PrlVmDev_SetSysName(hHDD, "harddisk4.hdd");
// Set the emulation type.
ret = PrlVmDev_SetEmulatedType(hHDD, PDT_USE_IMAGE_FILE);
// Enable the new disk on successful creation.
ret = PrlVmDev_SetEnabled(hHDD, PRL_TRUE);
// Create the new image file.
hJob = PrlVmDev_CreateImage(hHDD,
PRL_TRUE, // Do not overwrite if the file exists.
PRL_TRUE); // Use non-interactive mode.
// Commit the changes.
hJobCommit = PrlVm_Commit(hVm);
// Check the results of the commit operation.
ret = PrlJob_Wait(hJobCommit, 10000);
PrlJob_GetRetCode(hJobCommit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "Commit error: %s\n", prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJobCommit);
return nJobRetCode;
}
Using an existing image file
In the next example, we will use an existing image file to add a virtual hard disk to a virtual machine.
The procedure is similar to the one described above, except that you don't have to specify the disk
parameters and you don't have to create an image file.
// Timestamp the beginning of the configuration changes operation.
// The hVm specifies the virtual machine that we'll be editing.
//
hJobBeginEdit = PrlVm_BeginEdit(hVm);
ret = PrlJob_Wait(hJobBeginEdit, 10000);
PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJobBeginEdit);
return nJobRetCode;
}
// In this example, these two functions are used
// to specify the name of the existing image file.
// By default, it will look for the file in the
// virtual machine directory. If the file is located
// anywhere else, you must specify the full path here.
//
ret = PrlVmDev_SetFriendlyName(hHDD, "harddisk4.hdd");
83
Parallels C API by Example
ret = PrlVmDev_SetSysName(hHDD, "harddisk4.hdd");
// Set the emulation type.
ret = PrlVmDev_SetEmulatedType(hHDD, PDT_USE_IMAGE_FILE);
// Enable the drive on completion.
ret = PrlVmDev_SetEnabled(hHDD, PRL_TRUE);
// Commit the changes.
hJobCommit = PrlVm_Commit(hVm);
If the commit operation is successful, a hard disk will be added to the virtual machine and will
appear in the list of the available devices.
Network Adapters
When adding a network adapter to a virtual machine, you first have to choose a networking mode
for it. The following options are available:
• Host-only networking. A virtual machine can communicate with the host and other virtual
machines, but not with external networks.
• Shared networking. Uses the NAT feature. A virtual machine shares the IP address with the
host.
• Bridged networking. A virtual adapter in the VM is bound to a network adapter on the host. The
virtual machine appears as a standalone computer on the network.
Host-only and Shared Networking
The following sample function illustrates how to add virtual network adapters using the host-only
and shared networking (both types are created similarly). The steps are:
1 Call the PrlVm_BeginEdit function to mark the beginning of the virtual machine editing
operation. This step is required when modifying any of the virtual machine configuration
parameters.
2 Obtain a handle of type PHT_VM_CONFIGURATION containing the virtual machine
configuration information.
3 Create a new virtual device handle of type PHT_VIRTUAL_DEV_NET_ADAPTER (virtual
network adapter) using the PrlVmCfg_CreateVmDev function.
4 Set the desired device emulation type (host or shared) using the
PrlVmDev_SetEmulatedType function. Virtual network adapter emulation types are defined
in the PRL_VM_DEV_EMULATION_TYPE enumeration.
5 The MAC address for the adapter will be generated automatically. If needed, you can set the
address manually using the PrlVmDevNet_SetMacAddress function.
6Call the PrlVm_Commit function to finalize the changes.
// Timestamp the beginning of the configuration changes operation.
// The hVm parameter specifies the target virtual machine.
hJobBeginEdit = PrlVm_BeginEdit(hVm);
ret = PrlJob_Wait(hJobBeginEdit, 10000);
PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJobBeginEdit);
return nJobRetCode;
}
// Obtain a handle of type PHT_VM_CONFIGURATION containing
// the virtual machine configuration information.
ret = PrlVm_GetConfig(hVm, &hVmCfg);
if (PRL_FAILED(ret))
{
// Handle the error.
}
// Create a virtual network adapter device handle.
PRL_HANDLE hNet = PRL_INVALID_HANDLE;
ret = PrlVmCfg_CreateVmDev(
hVmCfg, // The virtual machine configuration handle.
PDE_GENERIC_NETWORK_ADAPTER, // Device type.
&hNet); // Device handle.
if (PRL_FAILED(ret))
{
// Handle the error.
}
// For host-only networking, set the device emulation type
// to PDT_USE_HOST_ONLY_NETWORK, which is an enumerator from the
// PRL_VM_DEV_EMULATION_TYPE enumeration.
// For shared networking, set the device emulation type
// to PDT_USE_SHARED_NETWORK, which is also an enumerator from
// the same enumeration.
// Un-comment one of the following lines (and comment out the other)
// to set the the desired emulation type.
PRL_VM_DEV_EMULATION_TYPE pdtType = PDT_USE_HOST_ONLY_NETWORK;
//PRL_VM_DEV_EMULATION_TYPE pdtType = PDT_USE_SHARED_NETWORK;
ret = PrlVmDev_SetEmulatedType(hNet, pdtType);
if (PRL_FAILED(ret))
{
// Handle the error.
}
// By default, a new device is created disabled.
// You can set the "connected" and "enabled" properties
// as desired.
PrlVmDev_SetConnected(hNet, PRL_TRUE);
PrlVmDev_SetEnabled(hNet, PRL_TRUE);
// Commit the changes.
hJobCommit = PrlVm_Commit(hVm);
85
Parallels C API by Example
ret = PrlJob_Wait(hJobBeginEdit, 10000);
PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
// Handle the error.
}
// Release all handles.
PrlHandle_Free(hNet);
PrlHandle_Free(hVmCfg);
PrlHandle_Free(hJobBeginEdit);
PrlHandle_Free(hJobCommit);
return PRL_ERR_SUCCESS;
}
Bridged Networking
Compared to host-only and shared network adapters, adding an adapter using bridged networking
involves additional steps. In a bridged networking mode, you are binding the virtual adapter inside a
virtual machine to an adapter on the host machine. Therefore, you first have to retrieve the list of
adapters from the host and select the one you would like to use. The complete procedure of
creating an adapter using bridged networking is as follows:
1 Obtain a list of network adapters installed on the host. This steps is preformed using the
PrlSrvCfg_GetNetAdaptersCount, PrlSrvCfg_GetNetAdapter, and
PrlSrvCfgDev_GetName functions.
2 Begin the virtual machine editing operation and create a new network adapter handle as
described in the Host-only and Shared Networking section (p. 84).
3 Bind the new virtual network adapter to the desired host machine adapter using the
PrlVmDevNet_SetBoundAdapterName function.
4 Finalize the changes by calling the PrlVm_Commit function.
You can also bind a virtual network adapter to the default adapter on the host machine. In this
case, you don't have to obtain the list of adapters from the host, so you can skip step 1 (above). In
step 3, instead of setting the adapter name, set its index as -1 using the
PrlVmDevNet_SetBoundAdapterIndex function.
The following are two sample functions that show the implementation of the steps described
above. The two functions are similar except that the first one shows how to bind a virtual network
adapter to a specific adapter on the host, whereas the second function shows how to bind the
virtual adapter to the default host network adapter.
Example 1:
The function accepts a server handle and a virtual machine handle. The server handle will be used
to obtain the list of network adapters from the host.
// Obtain a list of the network adapters installed on
// the host.
// First, obtain a handle containing the
// host configuration info.
hJob = PrlSrv_GetSrvConfig(hServer);
ret = PrlJob_Wait(hJob, 10000);
PrlJob_GetRetCode(hJob, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
// Handle the error.
}
// Get job results.
ret = PrlJob_GetResult(hJob, &hJobResult);
if (PRL_FAILED(ret))
{
// Handle the error.
}
// server config handle.
PRL_HANDLE hSrvCfg = PRL_INVALID_HANDLE;
// Now obtain the actual handle containing the
// host configuration info.
PrlResult_GetParam(hJobResult, &hSrvCfg);
// Get the number of the available adapters from the
// host configuration object.
PrlSrvCfg_GetNetAdaptersCount(hSrvCfg, &nCount);
// Net adapter handle.
PRL_HANDLE hHostNetAdapter = PRL_INVALID_HANDLE;
PRL_CHAR chHostAdapterName[1024];
// Iterate through the list of the adapters.
for (PRL_UINT32 i = 0; i < nCount; ++i)
{
PrlSrvCfg_GetNetAdapter(hSrvCfg, i, &hHostNetAdapter);
// Get adapter name.
PRL_CHAR chName[1024];
PRL_UINT32 nBufSize = sizeof(chName);
ret = PrlSrvCfgDev_GetName(hHostNetAdapter, chName, &nBufSize);
// Normally, you would iterate through the entire list
// and select an adapter to bind the virtual network adapter to.
// For simplicity, we will simply pick the first one and use it.
strcpy(chHostAdapterName, chName);
break;
}
87
Parallels C API by Example
// Now that we have the name of the host network adapter,
// we can add a new virtual network adapter to the virtual machine.
// Timestamp the beginning of the configuration changes operation.
// The hVm parameter specifies the target virtual machine.
hJobBeginEdit = PrlVm_BeginEdit(hVm);
ret = PrlJob_Wait(hJobBeginEdit, 10000);
PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJobBeginEdit);
return nJobRetCode;
}
// Obtain a handle of type PHT_VM_CONFIGURATION containing
// the virtual machine configuration information.
ret = PrlVm_GetConfig(hVm, &hVmCfg);
if (PRL_FAILED(ret))
{
// Handle the error.
}
// Create a virtual network adapter device handle.
PRL_HANDLE hNet = PRL_INVALID_HANDLE;
ret = PrlVmCfg_CreateVmDev(
hVmCfg, // The virtual machine configuration handle.
PDE_GENERIC_NETWORK_ADAPTER, // Device type.
&hNet); // Device handle.
if (PRL_FAILED(ret))
{
// Handle the error.
}
// Set the virtual network adapter emulation type (networking type).
// Bridged networking is set using the PDT_USE_BRIDGE_ETHERNET enumerator
// from the PRL_VM_DEV_EMULATION_TYPE enumeration.
ret = PrlVmDev_SetEmulatedType(hNet, PDT_USE_BRIDGE_ETHERNET);
if (PRL_FAILED(ret))
{
// Handle the error.
}
// Set the host adapter to which this adapter should be bound.
PrlVmDevNet_SetBoundAdapterName(hNet, chHostAdapterName);
// By default, a new device is created disabled.
// You can set the "connected" and "enabled" properties
// as desired.
PrlVmDev_SetConnected(hNet, PRL_TRUE);
PrlVmDev_SetEnabled(hNet, PRL_TRUE);
// Commit the changes.
hJobCommit = PrlVm_Commit(hVm);
ret = PrlJob_Wait(hJobBeginEdit, 10000);
PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
88
Parallels C API by Example
// Handle the error.
}
// Release all handles.
PrlHandle_Free(hNet);
PrlHandle_Free(hVmCfg);
PrlHandle_Free(hJobBeginEdit);
PrlHandle_Free(hJobCommit);
return PRL_ERR_SUCCESS;
}
Example 2:
This function shows how to add a virtual network adapter to a virtual machine and how to bind it to
the default adapter on the host.
// Timestamp the beginning of the configuration changes operation.
// The hVm parameter specifies the target virtual machine.
hJobBeginEdit = PrlVm_BeginEdit(hVm);
ret = PrlJob_Wait(hJobBeginEdit, 10000);
PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode));
PrlHandle_Free(hJobBeginEdit);
return nJobRetCode;
}
// Obtain a handle of type PHT_VM_CONFIGURATION containing
// the virtual machine configuration information.
ret = PrlVm_GetConfig(hVm, &hVmCfg);
if (PRL_FAILED(ret))
{
// Handle the error.
}
// Create a virtual network adapter device handle.
PRL_HANDLE hNet = PRL_INVALID_HANDLE;
ret = PrlVmCfg_CreateVmDev(
hVmCfg, // The virtual machine configuration handle.
PDE_GENERIC_NETWORK_ADAPTER, // Device type.
&hNet); // Device handle.
if (PRL_FAILED(ret))
{
// Handle the error.
}
// Set the virtual network adapter emulation type (networking type).
// Bridged networking is set using the PDT_USE_BRIDGE_ETHERNET enumerator
// from the PRL_VM_DEV_EMULATION_TYPE enumeration.
ret = PrlVmDev_SetEmulatedType(hNet, PDT_USE_BRIDGE_ETHERNET );
89
Parallels C API by Example
if (PRL_FAILED(ret))
{
// Handle the error.
}
// Set the host adapter index to -1. This will
// bind the virtual adapter to the default adapter on the
// host.
PrlVmDevNet_SetBoundAdapterIndex(hNet, -1);
// By default, a new device is created disabled.
// You can set the "connected" and "enabled" properties
// as desired.
PrlVmDev_SetConnected(hNet, PRL_TRUE);
PrlVmDev_SetEnabled(hNet, PRL_TRUE);
// Commit the changes.
hJobCommit = PrlVm_Commit(hVm);
ret = PrlJob_Wait(hJobBeginEdit, 10000);
PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode);
if (PRL_FAILED(nJobRetCode))
{
// Handle the error.
}
// Release all handles.
PrlHandle_Free(hNet);
PrlHandle_Free(hVmCfg);
PrlHandle_Free(hJobBeginEdit);
PrlHandle_Free(hJobCommit);
return PRL_ERR_SUCCESS;
}
Managing User Access Rights
This topic applies to Parallels Server only,
User authorization (determining user access rights) is performed using OS-level file access
permissions. Essentially, a virtual machine is a set of files that a user can read, write, and execute.
When determining access rights of a user for a particular virtual machine, Parallels Service looks at
the rights the user has on the virtual machine files and uses this information to allow or deny
privileges. The Parallels Server Administration Guide has a section that describes the Parallels
Server tasks in relation to the file access rights. Using this information, you can determine the tasks
that a user is allowed to perform based on the file access rights the user has. The same goal can
also be accomplished programmatically through Parallels C API.
The Parallels C API contains a PHT_ACCESS_RIGHTS object that is used to manage user access
rights. A handle to it is obtained using the PrlVmCfg_GetAccessRights or the
PrlVmInfo_GetAccessRights function. The difference between the two function is that
PrlVmInfo_GetAccessRights takes an additional step: obtaining a handle of type
PHT_VM_INFO which will also contain the virtual machine state information. If user access rights is
all you need, you can use the PrlVmCfg_GetAccessRights function.
90
Parallels C API by Example
The PHT_ACCESS_RIGHTS object provides an easy way of determining access rights for the
currently logged in user with the PrlAcl_IsAllowed function. The function allows to specify one
of the available virtual machine tasks (defined in the PRL_ALLOWED_VM_COMMAND enumeration)
and returns a boolean value indicating whether the user is allowed to perform the task or not. The
security is enforced on the server side, so if a user tries to perform a tasks that he/she is not
authorized to perform, the access will be denied. You can still use the functionality described here
to determine user access rights in advance and use it in accordance with your client application
logic.
An administrator of the host has full access rights to all virtual machines. A non-administrative user
has full rights to the machines created by him/her and no rights to any other virtual machines by
default (these machines will not even be included in the result set when the user requests a list of
virtual machines from the host). The host administrator can grant virtual machine access privileges
to other users when needed. Currently, the privileges can be granted to all existing users only. It is
not possible to set access rights for an individual user through the API. The
PrlAcl_SetAccessForOthers function is used to set access rights. The function takes the
PHT_ACCESS_RIGHTS object identifying the virtual machine and one of the enumerators from the
PRL_VM_ACCESS_FOR_OTHERS enumerations identifying the access level, which includes view,
view and run, full access, and no access. Once again, the function sets access rights for all existing
users (the users currently present in the Parallels Service user registry (p. 42)). To determine the
current access level for other users on a particular virtual machine, use the
PrlAcl_GetAccessForOthers function. For the complete set of user access management
functions, see the PHT_ACCESS_RIGHTS object description in the Parallels C API Reference
guide.
The following sample function demonstrates how to set virtual machine access rights and how to
determine access rights on the specified virtual machine for the currently logged in user. The
function accepts a virtual machine handle and operates on the referenced virtual machine.
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Obtain a PHT_VM_CONFIGURATION handle.
PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE;
ret = PrlVm_GetConfig(hVm, &hVmCfg);
// Obtain the access rights handle (this will be a
// handle of type PHT_ACCESS_RIGHTS).
ret = PrlVmCfg_GetAccessRights(hVmCfg, &hAccessRights);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hVmCfg);
return -1;
}
PrlHandle_Free(hVmCfg);
91
Parallels C API by Example
// Get the VM owner name from the access rights handle.
PRL_CHAR sBuf[1024];
PRL_UINT32 nBufSize = sizeof(sBuf);
ret = PrlAcl_GetOwnerName(hAccessRights, sBuf, &nBufSize);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hAccessRights);
return -1;
}
printf("Owner: %s\n", sBuf);
// Change the virtual machine access rights for other users
// to PAO_VM_SHARED_ON_VIEW_AND_RUN, which means that the
// users will be able to see the machine in the list and to
// run it. When this operation completes, we will use
// PrlAcl_IsAllowed function to determine whether the user
// is allowed to perform a particular task on the virtual
// machine.
PRL_VM_ACCESS_FOR_OTHERS access = PAO_VM_SHARED_ON_VIEW_AND_RUN;
ret = PrlAcl_SetAccessForOthers(hAccessRights, access);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Commit the changes.
hJob = PrlVm_UpdateSecurity(hVm, hAccessRights);
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Analyze the result of PrlVm_UpdateSecurity.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
PrlHandle_Free(hJob);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
return -1;
}
// Determine if the current user has the right to
// start the virtual machine.
PRL_ALLOWED_VM_COMMAND access_level = PAR_VM_START_ACCESS;
PRL_BOOL isAllowed = PRL_FALSE;
ret = PrlAcl_IsAllowed(hAccessRights, access_level, &isAllowed);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
printf("Can start: %d\n", isAllowed);
92
Parallels C API by Example
// Determine if the current user has the right to
// delete the specified virtual machine.
access_level = PAR_VM_DELETE_ACCESS;
isAllowed = PRL_FALSE;
ret = PrlAcl_IsAllowed(hAccessRights, access_level, &isAllowed);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
printf("Can delete: %d\n", isAllowed);
PrlHandle_Free(hAccessRights);
return 0;
}
Working with Virtual Machine Templates
Templates are virtual machines that cannot be run but can be used as a patterns to create new
virtual machines. Virtual machine templates are not different from regular virtual machines except,
as was mentioned earlier, that they cannot be run. In fact, you can convert a template to a regular
virtual machine at any time, just as you can convert a regular virtual machine to a template.
The Parallels C API allows to perform the following template-related operations:
• Obtaining a list of the available virtual machine templates.
• Creating a virtual machine template from scratch.
• Converting a regular virtual machine to a template.
• Converting a template to a regular virtual machine.
• Creating a new virtual machine from a template.
The following subsections describes each operation in detail and provide code examples.
Obtaining a List of Templates
A list of virtual machines and virtual machine templates are obtained from the server using the same
function: PrlSrv_GetVmList (p. 54). A template is identified by calling the
PrlVmCfg_IsTemplate function which returns a boolean value indicating whether the specified
virtual machine handle contains information about a regular virtual or a handle. The value of
PRL_TRUE indicates that the machine is a template. The value of PRL_FALSE indicates that the
machine is a regular virtual machine. The following sample is identical to the sample provided in the
Obtaining a List of Virtual Machines section (p. 54) with the exception that it was modified to
display only the lists of templates on the screen:
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
93
Parallels C API by Example
// Get a list of the available virtual machines.
hJob = PrlSrv_GetVmList(hServer);
// Wait for a maximum of 10 seconds for PrlSrv_GetVmList.
ret = PrlJob_Wait(hJob, 10000);
if (PRL_FAILED(ret))
{
fprintf(stderr,
"PrlJob_Wait for PrlSrv_GetVmList returned with error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
return ret;
}
// Check the results of PrlSrv_GetVmList.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
fprintf(stderr, "PrlJob_GetRetCode returned with error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
return ret;
}
if (PRL_FAILED(nJobReturnCode))
{
fprintf(stderr, "PrlSrv_GetVmList returned with error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
return ret;
}
// Get the results of PrlSrv_GetVmList.
ret = PrlJob_GetResult(hJob, &hJobResult);
if (PRL_FAILED(ret))
{
fprintf(stderr, "PrlJob_GetResult returned with error: %s\n",
prl_result_to_string(ret));
PrlHandle_Free(hJob);
return ret;
}
// Handle to result object is available,
// job handle is no longer needed, so free it.
PrlHandle_Free(hJob);
// Iterate through the results (list of virtual machines returned).
PRL_UINT32 nParamsCount = 0;
ret = PrlResult_GetParamsCount(hJobResult, &nParamsCount);
for (PRL_UINT32 i = 0; i < nParamsCount; ++i)
{
// Virtual machine handle
PRL_HANDLE hVm = PRL_INVALID_HANDLE;
// Get a handle to result at index i.
PrlResult_GetParamByIndex(hJobResult, i, &hVm);
// Obtain the PHT_VM_CONFIGURATION object.
PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE;
ret = PrlVm_GetConfig(hVm, &hVmCfg);
94
Parallels C API by Example
// See if the handle contains information about a template.
PRL_BOOL isTemplate = PRL_FALSE;
PrlVmCfg_IsTemplate(hVmCfg, &isTemplate);
// If this is not a template, proceed to the next
// virtual machine in the list.
if (isTemplate == PRL_FALSE)
{
PrlHandle_Free(hVmCfg);
PrlHandle_Free(hVm);
continue;
}
// Get the name of the template for result i.
char szVmNameReturned[1024];
PRL_UINT32 nBufSize = sizeof(szVmNameReturned);
ret = PrlVmCfg_GetName(hVmCfg, szVmNameReturned, &nBufSize);
if (PRL_FAILED(ret))
{
printf("PrlVmCfg_GetName returned with error (%s)\n",
prl_result_to_string(ret));
}
else
{
printf("Template name: '%s'.\n",
szVmNameReturned);
}
PrlHandle_Free(hVm);
PrlHandle_Free(hVmCfg);
}
return PRL_ERR_SUCCESS;
}
Creating a Template From Scratch
The steps in creating a new template and the steps in creating a new virtual machine are exactly
the same, with one exception: before registering a template with the Parallels Service, a call to
PrlVmCfg_SetTemplateSign function must be made passing the PRL_TRUE in the
bVmIsTemplate parameter. This will set a flag in the configuration structure indicating that you
want to create a template, not a regular virtual machine. The rest of the configuration parameters
are set exactly as they are set for a regular virtual machine. See the Creating a New Virtual Machine section (p. 65) for the detailed information about creating a virtual machine.
The following example illustrates how to create a virtual machine template. For simplicity reasons,
we only set a template name in this example. The rest of the configuration parameters are omitted.
As a result, a blank template will be created. It still can be used to create new virtual machines from
it but you will not be able to run them until you configure them properly. Once again, the Creating a New Virtual Machine section (p. 65) provides all the necessary information and code samples
needed to properly configure a virtual machine or a template.
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Create a new virtual machine handle.
ret = PrlSrv_CreateVm(hServer, &hVm);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Set the name for the new template.
ret = PrlVmCfg_SetName(hVm, "A simple template");
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hVm);
return -1;
}
// Set a flag indicating to create a template.
PRL_BOOL isTemplate = PRL_TRUE;
ret = PrlVmCfg_SetTemplateSign(hVm, isTemplate);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hVm);
return -1;
}
// Create and register the new template.
// The empty string in the configuration path
// indicates to create a template in the default
// virtual machine directory.
// The bNonInteractiveMode parameter indicates not to
// use interactive mode (the Service will not send questions
// to the client and will make all decisions on its own).
PRL_CHAR_PTR sVmConfigPath = "";
PRL_BOOL bNonInteractiveMode = PRL_TRUE;
hJob = PrlVm_Reg(hVm, sVmConfigPath, bNonInteractiveMode);
// Wait for the job to complete.
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
PrlHandle_Free(hVm);
return -1;
}
// Analyze the result of PrlVm_Reg.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
PrlHandle_Free(hVm);
return -1;
}
// Check the job return code.
96
Parallels C API by Example
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
PrlHandle_Free(hJob);
PrlHandle_Free(hVm);
return -1;
}
PrlHandle_Free(hJob);
PrlHandle_Free(hVm);
return 0;
}
Converting a Regular Virtual Machine to a Template
Any registered virtual machine can be converted to a template. This task is accomplished by
modifying the virtual machine configuration. Only a single parameter must be modified: a flag
indicating whether the machine is a regular virtual machine or a template, the rest will be handled
automatically and transparently to you on the server side. The name of the function that allows to
modify this parameter is PrlVm_SetTemplateSign.
The following code example illustrates how to convert a regular virtual machine to a template. Note
that any of the virtual machine (or a template) configuration changes must begin with the
PrlVm_BeginEdit and end with the PrlVm_BeginCommit function call. You should already
know that these two functions are used to prevent collisions with other clients trying to modify the
configuration of the same virtual machine or template at the same time.
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Begin of the VM configuration changes operation.
hJobBeginEdit = PrlVm_BeginEdit(hVm);
ret = PrlJob_Wait(hJobBeginEdit, 10000);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJobBeginEdit);
return -1;
}
ret = PrlJob_GetRetCode(hJobBeginEdit, &nJobReturnCode);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJobBeginEdit);
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
PrlHandle_Free(hJobBeginEdit);
return -1;
}
PrlHandle_Free(hJobBeginEdit);
97
Parallels C API by Example
// Set a flag in the virtual machine configuration
// indicating that we want it to become a template.
PRL_BOOL isTemplate = PRL_TRUE;
ret = PrlVmCfg_SetTemplateSign(hVm, isTemplate);
if (PRL_FAILED(ret))
{
// Handle the error...
return -1;
}
// Commit the changes.
hJobCommit = PrlVm_Commit(hVm);
// Check the results of the commit operation.
ret = PrlJob_Wait(hJobCommit, 10000);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJobCommit);
return -1;
}
ret = PrlJob_GetRetCode(hJobCommit, &nJobReturnCode);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJobCommit);
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
PrlHandle_Free(hJobCommit);
return -1;
}
PrlHandle_Free(hJobCommit);
return 0;
}
Converting a Template to a Regular Virtual Machine
Converting a template to a regular virtual machine is no different than converting a virtual machine
to a template (see the previous section for the description and an example). Simply set the boolean
parameter in the PrlVmCfg_SetTemplateSign function to PRL_FALSE and leave the rest of
the sample code the same.
98
Parallels C API by Example
Creating a New Virtual Machine From a Template.
The primary purpose of templates is to be used as patterns to create new virtual machines. New
virtual machines are created from templates using the cloning functionality. We've already
discussed how to clone a virtual machine in the Cloning a Virtual Machine section (p. 74). The
truth is, creating a virtual machine from a template is at all different than creating a clone of a virtual
machine. The PrlVm_Clone function that clones a virtual machine can also be used to create
virtual machines from templates. The function has a boolean parameter that allows to specify
whether a virtual machine or a template should be created. The following is almost the same
example that we used in the Cloning a Virtual Machine section (p. 74) but this time we are setting
the bCreateTemplate parameter to PRL_TRUE, thus creating a template instead of a regular
virtual machine.
// Declare and populate variables that
// will be used as input parameters
// in the function that clones a VM.
// Virtual machine name.
// Get the name of the original VM (template) and use
// it in the new virtual machine name. You can
// use any name that you like of course.
char vm_name[1024];
PRL_UINT32 nBufSize = sizeof(vm_name);
ret = PrlVmCfg_GetName(hVm, vm_name, &nBufSize);
char new_vm_name[1024] = "Created from template ";
strcat(new_vm_name, vm_name);
// Name of the target directory on the
// host.
// Empty string indicates that the default
// directory should be used.
PRL_CHAR_PTR new_vm_root_path = "";
// Virtual machine or template?
// The cloning functionality allows to create
// a new virtual machine or a new template.
// True specifies to create a template.
// False indicates to create a virtual machine.
// We want to create a virtual machine here, so we
// set it to PRL_FALSE.
PRL_BOOL bCreateTemplate = PRL_FALSE;
// Begin the cloning operation.
hJob = PrlVm_Clone(hVm, new_vm_name, new_vm_root_path, bCreateTemplate);
// Wait for the job to complete.
ret = PrlJob_Wait(hJob, 1000);
if (PRL_FAILED(ret))
{
// Handle the error...
printf("Error: (%s)\n",
prl_result_to_string(ret));
99
Parallels C API by Example
PrlHandle_Free(hJob);
return -1;
}
// Analyze the result of PrlVm_Clone.
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(ret))
{
// Handle the error...
PrlHandle_Free(hJob);
return -1;
}
// Check the job return code.
if (PRL_FAILED(nJobReturnCode))
{
// Handle the error...
printf("Error: (%s)\n",
prl_result_to_string(nJobReturnCode));
PrlHandle_Free(hJob);
return -1;
}
PrlHandle_Free(hJob);
return 0;
}
100
Loading...
+ hidden pages
You need points to download manuals.
1 point = 1 manual.
You can buy points or you can get point for every manual you upload.