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/trademarks.
Microsoft, Windows, Windows Server, Windows NT, Windows Vista, and MS-DOS are registered trademarks of
Microsoft Corporation.
Linux is a registered trademark of Linus Torvalds.
Mac is a registered trademark of Apple, Inc.
All other marks and names mentioned herein may be trademarks of their respective owners.
System Requirements ...................................................................................................................................6
Mac OS X Clients..............................................................................................................................6
Windows Clients ...............................................................................................................................6
Linux Clients.....................................................................................................................................7
Common Network Requirements......................................................................................................7
Linux ...............................................................................................................................................19
Strings as Return Values.............................................................................................................................26
Class Methods...........................................................................................................................................144
System Requirements............................................................................................................ 6
Overview
Parallels Virtualization SDK is a development kit used to create and integrate custom software
solutions with Parallels virtualization products. The SDK provides cross-platform ANSI C and
Python APIs. The SDK can be used to develop software for any hypervisor-based Parallels
virtualization product such as Parallels Server, Parallels Workstation, and Parallels Desktop.
The SDK comprises the following components:
C header files.
Dynamic libraries.
Python package for developing client applications in Python.
Parallels command line tools (prlctl, prlsrvctl) -- a command line utility that can
be used to perform a full range of host and virtual machine operations.
Parallels Virtualization SDK Programmer's Guide (this document).
Parallels C API Reference Guide.
Parallels Python API Reference Guide.
Parallels Command Line Reference Guide.
Getting Started 6
System Requirements
Mac OS X Clients
Hardware Requirements
Intel-powered Core™ Duo or Core Solo Mac® Mini, iMac®, MacBook™, MacBook Pro,
MacBook Air, Mac Pro, or Xserve.
Ethernet or WiFi network adapter.
Software Requirements
Mac OS X Tiger 10.4.8 or later.
Mac OS X Leopard 10.5.2 or later.
Parallels Python API requires Python 2.5. Other versions of Python are not officially
supported.
Windows Clients
Hardware Requirements
Intel-compatible x86 (32-bit) or x64 (64-bit) processor.
Ethernet or WiFi network adapter.
Software Requirements
Windows 2000 or higher.
The Parallels Python API requires Python 2.5. Other versions of Python are not officially
supported.
Getting Started 7
Linux Clients
Hardware Requirements
Intel-compatible x86 (32-bit) or x64 (64-bit) processor.
Ethernet network adapter.
Software Requirements
Red Hat® Enterprise Linux WS4 (x32, x64).
Red Hat Enterprise Linux AS4 (x32, x64).
Red Hat Enterprise Linux ES4 (x32, x64).
Red Hat Enterprise Linux 5 (x32, x64)
CentOS 4.x (x32, x64).
CentOS 5.0 (x32, x64).
CentOS 5.1 (x32, x64).
Ubuntu Server 7.10 (x32, x64).
®
SUSE
Linux Enterprise Server 10 SP1 (x32, x64).
Common Network Requirements
Parallels Server
When creating client applications for Parallels Server, your client computer must be able to
establish a network connection with the host computer running the server. The client computer
can be connected to a local area network via a wired or a wireless interface. Clients
communicate with a server via TCP/IP. The server is listening on port 6400. Please make sure
that the port is not blocked by a firewall.
Parallels Desktop and Parallels Workstation
Remote connections to the Parallels Service are not allowed with Parallels Desktop or Parallels
Workstation. With these products, you can run your client applications on the host computer
only.
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 9
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)
3 In 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);
Parallels C API Concepts 11
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))
{
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();
SdkWrap_Unload();
return -1;
}
else
{
printf( "Login was successful.\n" );
}
return 0;
}
// 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;
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;
}
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;
}
Parallels C API Concepts 14
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
Parallels C API Concepts 15
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);
}
// 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();
Parallels C API Concepts 16
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))
{
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.
//
// 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);
PrlHandle_Free(hServer);
PrlApi_Deinit();
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();
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;
}
Parallels C API Concepts 18
Windows
The following steps describe how to set up a project in Microsoft Visual Studio:
1 Create a project of your choice in a usual way and open the project Property Pages
windows.
2 In the C/C++ -> General -> Additional Include Directories section, add the path to the
Include and the Helpers\SdkWrap sub-directories located in the main directory
where you have the SDK installed.
3 Add the following files from the Helpers\SdkWrap subdirectory to the project:
SdkWrap.h
SdkWrap.cpp
These are the helper files that provide a set of methods for loading and unloading dynamic
libraries. You can use the included source code file to customize this functionality if you
wish.
4 Add the following #include directive to your program:
#include "SdkWrap.h"
The standard libraries used by the samples provided in this guide are:
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 referen ces 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.
Obtaining a Handle
A handle is usually obtained by calling a function that operates on another (we may call it
"parent") handle. 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 client application 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
Parallels C API Concepts 21
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);
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);
}
Parallels C API Concepts 22
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.
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 client.
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.
3 Convert the PHT_EVENT_PARAMETER handle to the appropriate handle type using
PrlEvtPrm_ToHandle.
To handle jobs within a callback function:
Parallels C API Concepts 23
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.
4 Get the result (a handle of type PHT_RESULT) from the job handle using
PrlJob_GetResult.
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.
Parallels C API Concepts 24
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.
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 client application 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
Parallels C API Concepts 25
// 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");
}
Parallels C API Concepts 26
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 client 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);
// Deallocate the memory.
free(pBuf);
Parallels C API Concepts 27
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);
}
Parallels C API Concepts 28
Error Handling
Synchronous Functions
All synchronous Parallels C API functions return PRL_RESULT, which is an integer indicating
success or failure of the operation.
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
Returns True if the return value indicates failure, or False if
the return value indicates success.
PRL_SUCCEEDED
Returns True if the return value indicates success, or False
if the return value indicates failure.
prl_result_to_string
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:
The following steps are required in any client application 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 Workstation,
Parallels Desktop, etc. 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 client 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.
PRL_RESULT LoginLocal(PRL_HANDLE &hServer)
Parallels C API by Example 32
{
// 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 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_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.
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);
// 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..................................................................................................56
Parallels C API by Example 36
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. 31).
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);
Parallels C API by Example 37
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);
// 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).
Parallels C API by Example 38
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;
}
Parallels C API by Example 39
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.
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.
// 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);
Parallels C API by Example 40
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_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);
PrlHandle_Free(hDispConfig);
return 0;
}
Parallels C API by Example 42
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.
// 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.
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))
{
Parallels C API by Example 46
// 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 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
Parallels C API by Example 47
// 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);
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
Parallels C API by Example 48
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.
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
Parallels C API by Example 49
// a handle of type PHT_USER_INFO for
// each user.
for (PRL_UINT32 i = 0; i < nParamCount; ++i)
{
ret = PrlResult_GetParamByIndex(hJobResult, i, &hUserInfo);
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".
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...
Parallels C API by Example 51
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 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);
}
}
PrlHandle_Free(hParentDirectory);
PrlHandle_Free(hJob);
return PRL_ERR_SUCCESS;
}
Parallels C API by Example 53
Managing Licenses
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))
{
// 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;
}
Parallels C API by Example 54
// 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);
// 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);
}
Parallels C API by Example 55
Parallels C API by Example 56
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);
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;
}
Parallels C API by Example 57
// 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.............................................................................................. 99
Working with Virtual Machine Templates............................................................................ 101
Parallels C API by Example 59
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. 75).
The steps that must be performed to obtain the virtual machine list are:
1 Log in to the Parallels Service and obtain a handle of type PHT_SERVER. See Obtaining
Server Handle and Logging In (p. 31) for more info and code samples.
2 Call 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 PrlSrv_GetVmList as it is no longer needed.
5 Call PrlResult_GetParamsCount to determine the number of virtual machines
contained in the PHT_RESULT object.
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);
Parallels C API by Example 60
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 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);
}
Parallels C API by Example 61
// Free the virtual machine handle.
PrlHandle_Free(hVm);
}
return PRL_ERR_SUCCESS;
}
Parallels C API by Example 62
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. 59).
// Obtains a handle of type PHT_VIRTUAL_MACHINE using the
// virtual machine name as a search parameter.
// Parameters
// 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);
Parallels C API by Example 63
// 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...
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;
}
Parallels C API by Example 64
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. 62).
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);
// 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);
// 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;
}
Parallels C API by Example 66
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. 59). 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:
Enumerator State/Transition Description
VMS_UNKNOWN
State Unknown or unsupported state.
VMS_STOPPED
VMS_STARTING
VMS_RESTORING
VMS_RUNNING
VMS_PAUSED
VMS_SUSPENDING
VMS_STOPPING
VMS_COMPACTING
VMS_SUSPENDED
VMS_SNAPSHOTING
VMS_RESETTING
VMS_PAUSING
VMS_CONTINUING
State Virtual machine is stopped.
Transition Virtual machine is starting.
Transition Virtual machine is being restored from a
snapshot.
State Virtual machine is running.
State Virtual machine is paused.
Transition Virtual machine is going into "suspended"
mode.
Transition Virtual machine is stopping.
Transition The Compact operation is being performed
on a virtual machine.
State Virtual machine is suspended.
Transition A snapshot of the virtual machine is being
taken.
Transition Virtual machine is being reset.
Transition Virtual machine is going into the "paused"
mode.
Transition Virtual machine is being brought back up
from the "paused" mode.
VMS_MIGRATING
VMS_DELETING_STATE
VMS_RESUMING
Transition Virtual machine is being migrated.
Transition Virtual machine is being deleted.
Transition Virtual machine is being resumed from the
"suspended" mode.
The following example demonstrates how obtain state/transition information for the specified
virtual machine.
PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
Parallels C API by Example 67
// 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...
PrlHandle_Free(hVmInfo);
return -1;
}
printf("Status: ");
switch (vm_state) {
case VMS_UNKNOWN:
printf("Unknown state\n");
Parallels C API by Example 68
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;
}
Parallels C API by Example 69
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.
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.
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
Parallels C API by Example 71
Pausing a virtual machine will pause execution of the virtual machine. This can be achieved
using PrlVm_Pause. PrlVm_Pause takes two parameters: a h andle 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);
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.
Parallels C API by Example 72
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.
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.
Parallels C API by Example 73
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,
// 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
Parallels C API by Example 74
// 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;
}
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. 22). 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. 22), 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:
1 Determine 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)
{
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);
Parallels C API by Example 76
// 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/");
Parallels C API by Example 77
// 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. 22).
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.
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))
Parallels C API by Example 78
{
// 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. 85).
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...
return ret;
}
ret = PrlResult_GetParam(hJobResult, &hVm);
if (PRL_FAILED(ret))
Parallels C API by Example 80
{
// 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))
{
// 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...
Parallels C API by Example 81
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. 101) section for more
virtual machine info and examples.
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.
Parallels C API by Example 83
// 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))
{
// 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;
}
Parallels C API by Example 84
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 re-registered 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",
prl_result_to_string(ret));
PrlHandle_Free(hVm);
PrlHandle_Free(hJob);
return -1;
}
from the above example. Note that this operation is irreversible.
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.
Parallels C API by Example 86
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
// 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;
Parallels C API by Example 87
}
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.
Parallels C API by Example 88
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).
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.
Parallels C API by Example 89
// 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)
{
PrlBootDev_SetSequenceIndex(hDevice, 2);
}
else if (devType == PDE_FLOPPY_DISK)
{
PrlBootDev_SetSequenceIndex(hDevice, 3);
}
else
{
PrlBootDev_Remove(hDevice);
}
}
// 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);
Parallels C API by Example 90
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);
// }
}
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 asneeded 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.
// 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.
//
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));
// 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");
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.
Parallels C API by Example 93
Host-only and Shared Networking
The following sample function illustrates how to add virtual network adapters using the hostonly 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.
6 Call 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
Parallels C API by Example 94
// 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);
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;
}
Parallels C API by Example 95
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. 93).
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))
Parallels C API by Example 96
{
// 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;
}
// 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.
// 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))
{
// 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));
// 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 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;
}
Parallels C API by Example 99
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.
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. 45)). 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);
// 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...
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.