Parallels Virtualization SDK 15 Programmer's Guide

Parallels Virtualization SDK
Programmer's Guide
v15
Parallels International GmbH Vordergasse 59 8200 Schaffhausen Switzerland Tel: + 41 52 672 20 30 www.parallels.com
Copyright © 1999-2019 Parallels International GmbH. All rights reserved.
This product is protected by United States and international copyright laws. The product’s underlying technology, patents, and trademarks are listed at http://www.parallels.com/about/legal/.
Microsoft, Windows, Windows Server, Windows Vista are registered trademarks of Microsoft Corporation. Apple, Mac, the Mac logo, OS X, macOS, iPad, iPhone, iPod touch are trademarks of Apple Inc., registered in the US and other countries. Linux is a registered trademark of Linus Torvalds. All other marks and names mentioned herein may be trademarks of their respective owners.
Contents
Getting Started .......................................................................................................... 6
Overview ......................................................................................................................... 6
System Requirements ..................................................................................................... 6
Parallels C API Concepts .......................................................................................... 7
Compiling Client Applications .......................................................................................... 7
Compiling with SdkWrap ..........................................................................................................8
Compiling with Framework .................................................................................................... 13
Handles ........................................................................................................................ 17
Synchronous Functions ................................................................................................. 19
Asynchronous Functions ............................................................................................... 19
Strings as Return Values ............................................................................................... 24
Error Handling ............................................................................................................... 25
Parallels C API by Example ..................................................................................... 29
Obtaining Server Handle and Logging In ........................................................................ 30
Host Operations ............................................................................................................ 34
Retrieving Host Configuration Information .............................................................................. 34
Managing Parallels Service Preferences ................................................................................ 36
Searching for Parallels Servers .............................................................................................. 40
Managing Parallels Service Users .......................................................................................... 42
Managing Files In The Host OS ............................................................................................. 47
Managing Licenses ................................................................................................................ 50
Obtaining a Problem Report .................................................................................................. 52
Virtual Machine Operations ............................................................................................ 54
Obtaining the Virtual Machines List ........................................................................................ 54
Searching for Virtual Machine by Name ................................................................................. 56
Obtaining Virtual Machine Configuration Information .............................................................. 58
Determining Virtual Machine State ......................................................................................... 60
Starting, Stopping, Resetting a Virtual Machine ..................................................................... 62
Suspending and Pausing a Virtual Machine ........................................................................... 63
Creating a New Virtual Machine ............................................................................................. 65
Searching for Virtual Machines .............................................................................................. 68
Contents
Adding an Existing Virtual Machine ........................................................................................ 72
Cloning a Virtual Machine ...................................................................................................... 74
Deleting a Virtual Machine ..................................................................................................... 76
Modifying Virtual Machine Configuration ................................................................................ 78
Managing User Access Rights ............................................................................................... 90
Working with Virtual Machine Templates ............................................................................... 93
Events ........................................................................................................................ 101
Receiving and Handling Events ........................................................................................... 101
Responding to Parallels Service Questions .......................................................................... 103
Performance Statistics ................................................................................................ 111
Obtaining Performance Statistics ......................................................................................... 111
Performance Monitoring ...................................................................................................... 115
Encryption Plug-in ....................................................................................................... 120
Encryption Plug-in Basics .................................................................................................... 120
The Encryption API Reference ............................................................................................. 120
Implementing a Plug-in ........................................................................................................ 123
Building the Dynamic Library ............................................................................................... 123
Plug-in Installation and Usage ............................................................................................. 124
Parallels Python API Concepts ............................................................................. 125
Package and Modules ................................................................................................. 125
Classes ....................................................................................................................... 126
Class Methods ............................................................................................................ 126
Synchronous Methods ........................................................................................................ 127
Asynchronous Methods ....................................................................................................... 127
Error Handling ............................................................................................................. 130
Parallels Python API by Example .......................................................................... 131
Creating a Basic Application ........................................................................................ 131
Connecting to Parallels Service and Logging In ............................................................ 133
Host Operations .......................................................................................................... 135
Retrieving Host Configuration Info ....................................................................................... 135
Managing Parallels Service Preferences .............................................................................. 137
Virtual Machine Operations .......................................................................................... 138
Obtaining the Virtual Machine List ........................................................................................ 139
Searching for a Virtual Machine ........................................................................................... 140
Contents
Performing Power Operations ............................................................................................. 141
Creating a New Virtual Machine ........................................................................................... 142
Obtaining Virtual Machine Configuration Data ...................................................................... 144
Modifying Virtual Machine Configuration .............................................................................. 147
Adding an Existing Virtual Machine ...................................................................................... 152
Removing an Existing Virtual Machine ................................................................................. 153
Cloning a Virtual Machine .................................................................................................... 154
Executing a Program in a Virtual Machine ............................................................................ 154
Remote Desktop Access ............................................................................................. 156
Creating a Simple OS Installation Program .......................................................................... 157
Index ...................................................................................................................... 162
C HAPTER 1

Getting Started

In This Chapter
Overview ................................................................................................................ 6
System Requirements ............................................................................................ 6

Overview

Parallels Virtualization SDK is a development kit that can be used to create and integrate custom software solutions with Parallels Desktop for Mac.
Parallels Virtualization SDK comprises the following components:
C header files and dynamic libraries.
Python package for developing client applications in Python.
Virtualization SDK Programmer's Guide (this document).
C API Reference Guide.
Python API Reference Guide.

System Requirements

To develop applications on Mac computers using the SDK, the following requirements must be met.
Hardware Requirements
Mac computer with Intel Core 2 Duo, Core i3, Core i5, Core i7, or Xeon processor
Minimum 2 GB RAM
Software Requirements
macOS 10.12 or newer
Python 2.7 or 3.0 to develop applications in Python
C HAPTER 2

Parallels C API Concepts

This chapter describes the basics of the Parallels C API. It contains information on how to compile client applications that use the API and explains basic API concepts.
In This Chapter
Compiling Client Applications .................................................................................. 7
Handles ................................................................................................................. 17
Synchronous Functions .......................................................................................... 19
Asynchronous Functions ........................................................................................ 19
Strings as Return Values ......................................................................................... 24
Error Handling ........................................................................................................ 25

Compiling Client Applications

Parallels Virtualization SDK for OS X is provided as a framework. The framework is installed in the following directory:
/Library/Frameworks/ParallelsVirtualizationSDK.framework
You can use the framework just like any other Apple framework when creating development projects and compiling applications. Alternately, you can compile and build your applications without using the framework. In such a case, you will have to specify all the necessary paths to the SDK source files manually.
When using the framework, the dynamic library, which is supplied with the SDK, will be directly linked to the application. If you would like to load the dynamic library at runtime, the Parallels Virtualization SDK includes a convenient dlopen wrapper for this purpose called SdkWrap. Using the wrapper, you can load and unload the library symbols at any time with one simple call. Please note that in order to use SdkWrap, you must compile your application without using the framework. The wrapper source files are located in the Helpers/SdkWrap directory, which is located in the main SDK installation directory.
The following subsections describe various compilation scenarios in detail and provide code samples.
Parallels C API Concepts

Compiling with SdkWrap

When using SdkWrap, your program must contain the following:
The #include "SdkWrap.h" directive. This header file defines the wrapper functions.
The #define SDK_LIB_NAME "libprl_sdk.dylib" directive. This is the name of the
dynamic library included in the SDK.
The SdkWrap_Load(SDK_LIB_NAME) function call that will load the dynamic library symbols.
The SdkWrap_Unload() function call that will unload the dynamic library when it is no longer
needed.
To compile a program, the following compiler options and instructions must be used:
The DYN_API_WRAP preprocessor macro must be defined.
Full paths to the Headers and the Helpers/SdkWrap directories must be specified. Both
directories are located in the main SDK installation directory.
The SdkWrap.cpp file must be included in the project and must be built together with the
main target.
The libdl library must be linked to the application. This is the standard dynamic linking
interface library needed to load the SDK library.
Using Makefile
The following is a sample Makefile that demonstrates the implementation of the requirements described above. To compile a program and to build an executable, type make in the Terminal window. To clean up the project, type make clean. Please note that the SOURCE variable must contain the name of your source file name.
# Source file name. # Substitute the file name with your own. SOURCE = HelloWorld
# Target executable file name. # Here we are using the same name as the source file name. TARGET = $(SOURCE)
# Path to the Parallels Virtualization SDK files. SDK_PATH = /Library/Frameworks/ParallelsVirtualizationSDK.framework
# Relative path to the SdkWrap directory containing # the SDK helper files. The files are used to load # the dynamic library. SDK_WRAP_PATH = Helpers/SdkWrap
OBJS = SdkWrap.o $(SOURCE).o CXX = g++ CXXFLAGS = -DDYN_API_WRAP -I$(SDK_PATH)/Headers -I$(SDK_PATH)/$(SDK_WRAP_PATH) LDFLAGS = -ldl
8
Parallels C API Concepts
all : $(TARGET)
$(TARGET) : $(OBJS) $(CXX) -o $@ $(LDFLAGS) $(OBJS)
$(SOURCE).o : $(SOURCE).cpp $(CXX) -c -o $@ $(CXXFLAGS) $(SOURCE).cpp
SdkWrap.o : $(SDK_PATH)/$(SDK_WRAP_PATH)/SdkWrap.cpp $(CXX) -c -o $@ $(CXXFLAGS) $(SDK_PATH)/$(SDK_WRAP_PATH)/SdkWrap.cpp
clean: @rm -f $(OBJS) $(TARGET)
.PHONY : all clean
Using Xcode IDE
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.
#include "SdkWrap.h" #include <stdio.h> #include <stdlib.h> #include <string.h>
#ifdef _WIN_ #include <windows.h> #else #include <unistd.h> #endif
PRL_RESULT LoginLocal(PRL_HANDLE &hServer); PRL_RESULT LogOff(PRL_HANDLE &hServer);
/////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) { // Variables for handles.
9
Parallels C API Concepts
PRL_HANDLE hJob = PRL_INVALID_HANDLE; // job handle PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; // job result PRL_HANDLE hServer = PRL_INVALID_HANDLE; // server handle
// Variables for return codes. PRL_RESULT err = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Log in to Parallels Service. err = LoginLocal(hServer);
// Log off. err = LogOff(hServer);
printf( "\nEnd of program.\n\n" ); printf("Press Enter to exit..."); getchar();
exit(0); }
// Initializes the SDK library and // logs in to the local Parallels Service. // PRL_RESULT LoginLocal(PRL_HANDLE &hServer) { // Variables for handles. PRL_HANDLE hJob = PRL_INVALID_HANDLE; // job handle PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; // job result
// Variables for return codes. PRL_RESULT err = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Use the correct dynamic library depending on the platform. #ifdef _WIN_ #define SDK_LIB_NAME "prl_sdk.dll" #elif defined(_LIN_) #define SDK_LIB_NAME "libprl_sdk.so" #elif defined(_MAC_) #define SDK_LIB_NAME "libprl_sdk.dylib" #endif
// Load SDK library. if (PRL_FAILED(SdkWrap_Load(SDK_LIB_NAME)) && PRL_FAILED(SdkWrap_Load("./" SDK_LIB_NAME))) { fprintf( stderr, "Failed to load " SDK_LIB_NAME "\n" ); return -1; }
// Initialize the API. In this example, we are initializing the // API for Parallels Desktop. // To initialize in the Parallels Workstation mode, pass PAM_WORKSTATION // as the second parameter. // To initialize for Parallels Server, pass PAM_SERVER. // See the PRL_APPLICATION_MODE enumeration for all possible options. err = PrlApi_InitEx(PARALLELS_API_VER, PAM_DESKTOP, 0, 0);
if (PRL_FAILED(err)) {
10
Parallels C API Concepts
fprintf(stderr, "PrlApi_InitEx returned with error: %s.\n", prl_result_to_string(err)); PrlApi_Deinit(); SdkWrap_Unload(); return -1; }
// Create a server handle (PHT_SERVER). err = PrlSrv_Create(&hServer); if (PRL_FAILED(err)) { fprintf(stderr, "PrlSvr_Create failed, error: %s", prl_result_to_string(err)); PrlApi_Deinit(); SdkWrap_Unload(); return -1; }
// Log in (asynchronous call). hJob = PrlSrv_LoginLocal(hServer, NULL, NULL, PSL_NORMAL_SECURITY);
// 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();
11
Parallels C API Concepts
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;
PRL_RESULT err = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Log off. hJob = PrlSrv_Logoff(hServer); err = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(err)) { fprintf(stderr, "PrlJob_Wait for PrlSrv_Logoff returned error: %s\n", prl_result_to_string(err)); PrlHandle_Free(hJob); PrlHandle_Free(hServer); PrlApi_Deinit(); SdkWrap_Unload(); return -1; }
// Get the Logoff operation return code. err = PrlJob_GetRetCode(hJob, &nJobReturnCode);
// Check the PrlJob_GetRetCode success/failure. if (PRL_FAILED(err)) { fprintf(stderr, "PrlJob_GetRetCode failed for PrlSrv_Logoff with error: %s\n", prl_result_to_string(err)); PrlHandle_Free(hJob); PrlHandle_Free(hServer); PrlApi_Deinit(); SdkWrap_Unload(); return -1; }
// Report success or failure of PrlSrv_Logoff. if (PRL_FAILED(nJobReturnCode)) { fprintf(stderr, "PrlSrv_Logoff failed with error: %s\n", prl_result_to_string(nJobReturnCode)); PrlHandle_Free(hJob); PrlHandle_Free(hServer); PrlApi_Deinit(); SdkWrap_Unload(); return -1;
12
Parallels C API Concepts
} else { printf( "Logoff was successful.\n" ); }
// Free handles that are no longer required. PrlHandle_Free(hJob); PrlHandle_Free(hServer);
// De-initialize the Parallels API, and unload the SDK. PrlApi_Deinit(); SdkWrap_Unload();
return 0; }

Compiling with Framework

If you are using the ParallelsVirtualizationSDK framework, the program must contain the following include directive:
#include "ParallelsVirtualizationSDK/Parallels.h"
Parallels.h is the main SDK header file. Please note the framework name in front of the SDK
header file name. This is a common requirement when using a framework.
Note: The difference between the SdkWrap scenario (described in the previous subsection) and the framework scenario is that Parallels.h must be included when using the framework, while SdkWrap.h must be included when using SdkWrap. The two files must never be included together. Please also note that you don't have to load the dynamic library manually in your program when using the framework.
The only compiler option that must be specified when using the framework is:
-framework ParallelsVirtualizationSDK
Using Makefile
The following sample Makefile can be used to compile a program using the ParallelsVirtualizationSDK framework:
# Source file name. # Substitute the file name with your own. SOURCE = HelloWorld
# Target executable file name. # Here we are using the same name as the source file name. TARGET = $(SOURCE)
CXX = g++ LDFLAGS = -framework ParallelsVirtualizationSDK
all : $(TARGET)
13
Parallels C API Concepts
$(TARGET) : $(OBJS) $(CXX) -o $@ $(LDFLAGS) $(OBJS)
$(SOURCE).o : $(SOURCE).cpp $(CXX) -c -o $@ $(SOURCE).cpp
clean: @rm -f $(OBJS) $(TARGET)
.PHONY : all clean
Using Xcode IDE
When setting up an Xcode project, the only thing that you have to do is add the ParallelsVirtualizationSDK framework to the project. No other project modifications are necessary.
Sample
The following is a complete sample program that demonstrates the usage of the ParallelsVirtualizationSDK framework.
#include "ParallelsVirtualizationSDK/Parallels.h" #include <stdio.h> #include <stdlib.h> #include <string.h>
#ifdef _WIN_ #include <windows.h> #else #include <unistd.h> #endif
PRL_RESULT LoginLocal(PRL_HANDLE &hServer); PRL_RESULT LogOff(PRL_HANDLE &hServer);
/////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) { // Variables for handles. PRL_HANDLE hServer = PRL_INVALID_HANDLE; // server handle
// Variables for return codes. PRL_RESULT err = PRL_ERR_UNINITIALIZED;
// Log in. err = LoginLocal(hServer);
// Log off err = LogOff(hServer);
printf( "\nEnd of program.\n\n" ); printf("Press Enter to exit..."); getchar();
exit(0); }
14
Parallels C API Concepts
// Intializes the SDK library and // logs in to the local Parallels Service. // PRL_RESULT LoginLocal(PRL_HANDLE &hServer) { // Variables for handles. PRL_HANDLE hJob = PRL_INVALID_HANDLE; // job handle
// Variables for return codes. PRL_RESULT err = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Initialize the API. In this example, we are initializing the // API for Parallels Workstation. // To initialize in the Parallels Desktop mode, pass PAM_DESKTOP // as the second parameter. // To initialize for Parallels Server, pass PAM_SERVER. // See the PRL_APPLICATION_MODE enumeration for all possible options. err = PrlApi_InitEx(PARALLELS_API_VER, PAM_DESKTOP, 0, 0);
if (PRL_FAILED(err)) { fprintf(stderr, "PrlApi_InitEx returned with error: %s.\n", prl_result_to_string(err)); PrlApi_Deinit(); return -1; }
// Create a server handle (PHT_SERVER). err = PrlSrv_Create(&hServer); if (PRL_FAILED(err)) { fprintf(stderr, "PrlSvr_Create failed, error: %s", prl_result_to_string(err)); PrlApi_Deinit(); return -1; }
// Log in (asynchronous call). hJob = PrlSrv_LoginLocal(hServer, NULL, NULL, PSL_NORMAL_SECURITY);
// Wait for a maximum of 10 seconds for // the job to complete. err = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(err)) { fprintf(stderr, "PrlJob_Wait for PrlSrv_Login returned with error: %s\n", prl_result_to_string(err)); PrlHandle_Free(hJob); PrlHandle_Free(hServer); PrlApi_Deinit(); return -1; }
// Analyze the result of PrlSrv_Login. err = PrlJob_GetRetCode(hJob, &nJobReturnCode);
// First, check PrlJob_GetRetCode success/failure. if (PRL_FAILED(err))
15
Parallels C API Concepts
{ fprintf(stderr, "PrlJob_GetRetCode returned with error: %s\n", prl_result_to_string(err)); PrlHandle_Free(hJob); PrlHandle_Free(hServer); PrlApi_Deinit(); return -1; }
// Now check the Login operation success/failure. if (PRL_FAILED(nJobReturnCode)) { PrlHandle_Free(hJob); PrlHandle_Free(hServer); printf("Login job returned with error: %s\n", prl_result_to_string(nJobReturnCode)); PrlHandle_Free(hJob); PrlHandle_Free(hServer); PrlApi_Deinit(); return -1; } else { printf( "Login was successful.\n" ); }
return 0; }
// Log off the Parallels Service and // deinitializes the SDK library. // PRL_RESULT LogOff(PRL_HANDLE &hServer) { PRL_HANDLE hJob = PRL_INVALID_HANDLE;
PRL_RESULT err = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// 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);
16
Parallels C API Concepts
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; }

Handles

The Parallels C API is a set of functions that operate on objects. Objects are not accessed directly. Instead, references to these objects are used. These references are known as handles.
Handle Types
PRL_HANDLE is the only handle type used in the C API. It is a pointer to an integer and it is defined in PrlTypes.h.
PRL_HANDLE can reference any type of object within the API. The type of object that PRL_HANDLE references determines the PRL_HANDLE type. A list of handle types can be found
in the PRL_HANDLE_TYPE enumeration in PrlEnums.h.
A handles' type can be extracted using the PrlHandle_GetType function. A string representation of the handle type can then be obtained using the handle_type_to_string function.
17
Parallels C API Concepts
Obtaining a Handle
A handle is usually obtained by calling a function belonging to another handle, which we may call a "parent". For example, a virtual machine handle is obtained by calling a function that operates on the Server handle. A virtual device handle is obtained by calling a function that operates on the virtual machine handle, and so forth. The Parallels C API Reference guide contains a description of every available handle and explains how each particular handle type can be obtained. The examples in this guide also demonstrate how to obtain handles of different types.
Freeing a Handle
Parallels API handles are reference counted. Each handle contains a count of the number of references to it held by other objects. A handle stays in memory for as long as the reference count is greater than zero. A program is responsible for freeing any handles that are no longer needed. A handle can be freed using the PrlHandle_Free function. The function decreases the reference count by one. When the count reaches zero, the object is destroyed. Failing to free a handle after it has been used will result in a memory leak.
Multithreading
Parallels API handles are thread safe. They can be used in multiple threads at the same time. To maintain the proper reference counting, the count should be increased each time a handle is passed to another thread by calling the PrlHandle_AddRef function. If this is not done, freeing a handle in one thread may destroy it while other threads are still using it.
Example
The following code snippet demonstrates how to obtain a handle, how to determine its type, and how to free it when it's no longer needed. The code is a part of the bigger example that demonstrates how to log in to a Parallels Service (the full example is provided later in this guide).
PRL_HANDLE hServer = PRL_INVALID_HANDLE; PRL_RESULT ret;
ret = PrlSrv_Create(&hServer); if (PRL_FAILED(ret)) { fprintf(stderr, "PrlSvr_Create failed, error: %s", prl_result_to_string(ret)); return PRL_ERR_FAILURE; }
// Determine the type of the hServer handle. PRL_HANDLE_TYPE nHandleType; PrlHandle_GetType(hServer, &nHandleType); printf("Handle type: %s\n", handle_type_to_string(nHandleType));
// Free the handle when it is no longer needed. PrlHandle_Free(hServer);
18
Parallels C API Concepts

Synchronous Functions

The Parallels C API provides synchronous and asynchronous functions. Synchronous functions run in the same thread as the caller. When a synchronous function is called it completes executing before returning control to the caller. Synchronous functions return PRL_RESULT, which is a integer indicating success or failure of the operation. Consider the PrlSrv_Create function. The purpose of this function is to obtain a handle of type PHT_SERVER. The handle is required to access most of the functionality within the Parallels C API. The syntax of PrlSrv_Create is as follows:
PRL_RESULT PrlSrv_Create( PRL_HANDLE_PTR handle );
The following is an example of the PrlSrv_Create function call:
// Declare a handle variable. PRL_HANDLE hServer = PRL_INVALID_HANDLE;
// Call the PrlSrv_Create to obtain the handle. PRL_RESULT res = PrlSrv_Create(&hServer);
// Examine the function return code. // PRL_FAILED is a macro that evaluates a variable of type PRL_RESULT. // A return value of True indicates success; False indicates failure. if (PRL_FAILED(res)) { printf("PrlSrv_Create returned error: %s\n", prl_result_to_string(res)); exit(ret); }

Asynchronous Functions

An asynchronous operation is executed in its own thread. An asynchronous function that started the operation returns to the caller immediately without waiting for the operation to complete. The results of the operation can be verified later when needed. Asynchronous functions return PRL_HANDLE, which is a pointer to an integer and is a handle of type PHT_JOB. The handle is used as a reference to the asynchronous job executed in the background. The general procedure for calling an asynchronous function is as follows:
1 Register an event handler (callback function). 2 Call an asynchronous function. 3 Analyze the results of events (jobs) within the callback function. 4 Handle the appropriate event in the callback function. 5 Un-register the event handler when it is no longer needed.
19
Parallels C API Concepts
The Callback Function (Event Handler)
Asynchronous functions return data to the caller by means of an event handler (or callback function). The callback function could be called at any time, depending on how long the
asynchronous function takes to complete. The callback function must have a specific signature. The prototype can be found in PrlApi.h and is as follows:
typedef PRL_METHOD_PTR(PRL_EVENT_HANDLER_PTR) ( PRL_HANDLE hEvent, PRL_VOID_PTR data );
The following is an example of the callback function implementation:
static PRL_RESULT OurCallback(PRL_HANDLE handle, void *pData) { // Event handler code...
// You must always release the handle before exiting. PrlHandle_Free(handle); }
A handle received by the callback function can be of type PHT_EVENT or PHT_JOB. The type can be determined using the PrlHandle_GetType function. The PHT_EVENT type indicates that the callback was called by a system event. If the type is PHT_JOB then the callback was called by an asynchronous job started by the program.
To handle system events within a callback function:
1 Get the event type using PrlEvent_GetType. 2 Examine the event type. If it is relevant, a handle of type PHT_EVENT_PARAMETER can be
extracted using PrlEvent_GetParam.
3 Convert the PHT_EVENT_PARAMETER handle to the appropriate handle type using
PrlEvtPrm_ToHandle.
To handle jobs within a callback function:
1 Get the job type using PrlJob_GetType. A job type can be used to identify the function that
started the job and to determine the type of the result it contains. For example, a job of type PJOC_SRV_GET_VM_LIST is started by PrlSrv_GetVmList function call, which returns a list of virtual machines.
2 Examine the job type. If it is relevant, proceed to the next step. 3 Get the job return code using PrlJob_GetRetCode. If it doesn't contain an error, proceed to
the next step.
4 Get the result (a handle of type PHT_RESULT) from the job handle using
PrlJob_GetResult.
20
Parallels C API Concepts
5 Get a handle to the result using PrlResult_GetParam. Note that some functions return a list
(ie. there can be more than a single parameter in the result). For example,
PrlSrv_GetVmList returns a list of available virtual machines. In such cases, use PrlResult_GetParamCount and PrlResult_GetParamByIndex.
6 Implement code to use the handle obtained in step 5.
Note: You must always free the handle that was passed to the callback function before exiting, regardless of whether you actually used it or not. Failure to do so will result in a memory leak.
The following skeleton code demonstrates implementation of the above steps. In this example, the objective is to handle events of type PET_DSP_EVT_HOST_STATISTICS_UPDATED that are generated by a call to function PrlSrv_SubscribeToHostStatistics, and to obtain the result from a job of type PJOC_SRV_GET_VM_LIST.
static PRL_RESULT OurCallbackFunction(PRL_HANDLE hHandle, PRL_VOID_PTR pUserData) { PRL_JOB_OPERATION_CODE nJobType = PJOC_UNKNOWN; // job type PRL_HANDLE_TYPE nHandleType = PHT_ERROR; // handle type PRL_HANDLE hVm = PRL_INVALID_HANDLE; // virtual machine handle PRL_HANDLE hParam = PRL_INVALID_HANDLE; // event parameter PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; // job result PRL_UINT32 nParamsCount = -1; // parameter count PRL_UINT32 nParamIndex = -1; // parameter index PRL_RESULT err = PRL_ERR_UNINITIALIZED; // error
// Check the type of the received handle. PrlHandle_GetType(hHandle, &nHandleType);
if (nHandleType == PHT_EVENT) // Event handle { PRL_EVENT_TYPE EventType; PrlEvent_GetType(hHandle, &EventType);
// Check if the event type is a statistics update. if (EventType == PET_DSP_EVT_HOST_STATISTICS_UPDATED) { // Get handle to PHT_EVENT_PARAMETER. PRL_HANDLE hEventParameters = PRL_INVALID_HANDLE; PrlEvent_GetParam(hHandle, 0, &hEventParameters);
// Get handle to PHT_SYSTEM_STATISTICS. PRL_HANDLE hServerStatistics = PRL_INVALID_HANDLE; PrlEvtPrm_ToHandle(hEventParameters, &hServerStatistics);
// Code goes here to extract the statistics data // using hServerStatistics.
PrlHandle_Free(hServerStatistics); PrlHandle_Free(hEventParameters); } } else if (nHandleType == PHT_JOB) // Job handle { // Get the job type. PrlJob_GetOpCode(hHandle, &nJobType);
// Check if the job type is PJOC_SRV_GET_VM_LIST.
21
Parallels C API Concepts
if (nJobType == PJOC_SRV_GET_VM_LIST) { // Check the job return code. PRL_RESULT nJobRetCode; PrlJob_GetRetCode(hHandle, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "[B]%.8X: %s\n", nJobRetCode, prl_result_to_string(nJobRetCode)); PrlHandle_Free(hHandle); return nJobRetCode; }
err = PrlJob_GetResult(hHandle, &hJobResult);
// if (err != PRL_ERR_SUCCESS), process the error here.
// Determine the number of parameters in the result. PrlResult_GetParamsCount(hJobResult, &nParamsCount);
// Iterate through the parameter list. for(nParamIndex = 0; nParamIndex < nParamsCount ; nParamIndex++) { // Obtain a virtual machine handle (PHT_VIRTUAL_MACHINE). PrlResult_GetParamByIndex(hJobResult, nParamIndex, &hVm);
// Code goes here to obtain virtual machine info from hVm.
// Free the handle when done using it. PrlHandle_Free(hVm); } PrlHandle_Free(hJobResult); } }
PrlHandle_Free(hHandle); return PRL_ERR_SUCCESS; }
Registering / Unregistering an Event Handler
The PrlSrv_RegEventHandler function is used to register an event handler, PrlSrv_UnregEventHandler is used to unregister an event handler.
Note: When an event handler is registered, it will receive all of the events/jobs regardless of their origin. It is the responsibility of the program to identify the type of the event and to handle each one accordingly.
// Register an event handler. ReturnDataClass rd; // some user-defined class. PrlSrv_RegEventHandler(hServer, OurCallbackFunction, &rd);
// Make a call to an asynchronous function here. // OurCallbackFunction will be called by the background thread // as soon as the job is completed, and code within // OurCallbackFunction can populate the ReturnDataClass instance. // For example, we can make the following call here:
hJob = PrlSrv_GetVmList(hServer); PrlHandle_Free(hJob);
22
Parallels C API Concepts
// Please note that we still have to obtain the // job object (hJob above) and free it; otherwise // we will have memory leaks.
// Unregister the event handler when it is no longer needed. PrlSrv_UnregEventHandler(hServer, OurCallbackFunction, &rd);
Calling Asynchronous Functions Synchronously
It is possible to call an asynchronous function synchronously by using the PrlJob_Wait function. The function takes two parameters: a PHT_JOB handle and a timeout value in milliseconds. Once you call the function, the main thread will be suspended and the function will wait for the asynchronous job to complete. The function will return when the job is completed or when timeout value is reached, whichever comes first. The following code snippet illustrates how to call an asynchronous function PrlServer_Login synchronously:
// Log in (PrlSrv_Login is asynchronous). PRL_HANDLE hJob = PrlSrv_Login( hServer, szHostnameOrIpAddress, szUsername, szPassword, 0, 0, 0, PSL_LOW_SECURITY);
// Wait for a maximum of 10 seconds for // asynchronous function PrlSrv_Login to complete. ret = PrlJob_Wait(hJob, 10000); if (PRL_FAILED(ret)) { fprintf(stderr, "PrlJob_Wait for PrlSrv_Login returned with error: %s\n", prl_result_to_string(ret)); PrlHandle_Free(hJob); PrlHandle_Free(hServer); return -1; }
// Analyse the result of the PrlServer_Login call. PRL_RESULT nJobResult; ret = PrlJob_GetRetCode(hJob, &nJobResult); if (PRL_FAILED(nJobResult)) { PrlHandle_Free(hJob); PrlHandle_Free(hServer); printf("Login job returned with error: %s\n", prl_result_to_string(nJobResult)); return -1; } else { printf("login successfully performed\n"); }
23
Parallels C API Concepts

Strings as Return Values

Sting values in the Parallels C API are received by passing a char pointer to a function which populates it with data. It is the responsibility of the caller to allocate the memory required to receive the value, and to free it when it is no longer needed. Since in most cases we don't know the string size in advance, we have to either allocate a chunk of memory large enough for any possible value or to determine the exact required size. To determine the required buffer size, the following two approaches can be used:
1 Calling the same function twice: first, to obtain the required buffer size, and second, to receive
the actual string value. To get the required buffer size, call the function passing a null pointer as a value of the output parameter, and pass 0 (zero) as a value of the variable that is used to specify the buffer size. The function will calculate the required size and will populate the variable with the correct value, which you can use to initialize a variable that will receive the string. You can then call the function again to get the actual string value.
2 It is also possible to use a static buffer. If the length of the buffer is large enough, you will simply
receive the result. If the length is too small, a function will fail with the PRL_ERR_BUFFER_OVERRUN error but it will populate the "buffer_size" variable with the required size value. You can then allocate the memory using the received value and call the function again to get the results.
Consider the following function:
PRL_RESULT PrlVmCfg_GetName( PRL_HANDLE hVmCfg, PRL_STR sVmName, PRL_UINT32_PTR pnVmNameBufLength );
The PrlVmCfg_GetName function above is a typical Parallels API function that returns a string value (in this case, the name of a virtual machine). The hVmCfg parameter is a handle to an object containing the virtual machine configuration information. The sVmName parameter is a char pointer. It is used as output that receives the virtual machine name. The variable must be initialized on the program side with enough memory allocated for the expected string. The size of the buffer must be specified using the pnVmNameBufLength variable.
The following example demonstrates how to call the function using the first approach:
PRL_RESULT ret; PRL_UINT32 nBufSize = 0;
// Get the required buffer size. ret = PrlVmCfg_GetName(hVmCfg, 0, &nBufSize);
// Allocate the memory. PRL_STR pBuf = (PRL_STR)malloc(sizeof(PRL_CHAR) * nBufSize);
// Get the virtual machine name. ret = PrlVmCfg_GetName(hVmCfg, pBuf, &nBufSize);
printf("VM name: %s\n", pBuf);
24
Parallels C API Concepts
// Deallocate the memory. free(pBuf);
The following example uses the second approach. To test the buffer-overrun scenario, set the
sVmName array size to some small number.
#define MY_STR_BUF_SIZE 1024
PRL_RESULT ret; char sVmName[MY_STR_BUF_SIZE]; PRL_UINT32 nBufSize = MY_STR_BUF_SIZE;
// Get the virtual machine name. ret = PrlVmCfg_GetName(hVmCfg, sVmName, &nBufSize);
// Check for errors. if (PRL_SUCCEEDED(ret)) { // Everything's OK, print the machine name. printf("VM name: %s\n", sVmName); } else if (ret == PRL_ERR_BUFFER_OVERRUN) { // The sVmName array size is too small. // Get the required size, allocate the memory, // and try getting the VM name again.
PRL_UINT32 nSize = 0; PRL_STR pBuf;
// Get the required buffer size. ret = PrlVmCfg_GetName(hVmCfg, 0, &nSize);
// Allocate the memory. pBuf = (PRL_STR)malloc(sizeof(PRL_CHAR) * nSize);
// Get the virtual machine name. ret = PrlVmCfg_GetName(hVmCfg, pBuf, &nSize);
printf("VM name: %s\n", pBuf);
// Dallocate the memory. free(pBuf); }

Error Handling

Synchronous Functions
All synchronous Parallels C API functions return PRL_RESULT, which is an integer indicating success or failure of the operation.
25
Parallels C API Concepts
Error Codes for Asynchronous Functions
All asynchronous functions return PRL_HANDLE. The error code (return value) in this case can be extracted with PrlJob_GetRetCode after the asynchronous job has finished.
Analyzing Return Values
Parallels C API provides the following macros to work with error codes:
PRL_FAILED
PRL_SUCCEEDED
prl_result_to_string
Returns True if the return value indicates failure, or False if the return value indicates success.
Returns True if the return value indicates success, or False if the return value indicates failure.
Returns a string representation of the error code.
The following code snippet attempts to create a directory on the host and analyzes the return value (error code) of asynchronous function PrlSrv_CreateDir.
// Attempt to create directory /tmp/TestDir on the host. char *szRemoteDir = "/tmp/TestDir"; hJob = PrlSrv_FsCreateDir(hServer, szRemoteDir);
// Wait for a maximum of 5 seconds for asynchronous // function PrlSrv_FsCreateDir to complete. PRL_RESULT resWaitForCreateDir = PrlJob_Wait(hJob, 5000); if (PRL_FAILED(resWaitForCreateDir)) { fprintf(stderr, "PrlJob_Wait for PrlSvr_FsCreateDir failed with error: %s\n", prl_result_to_string(resWaitForCreateDir)); PrlHandle_Free(hJob); return -1; }
// Extract the asynchronous function return code. PrlJob_GetRetCode(hJob, &nJobResult); if (PRL_FAILED(nJobResult)) { fprintf(stderr, "Error creating directory %s. Error returned: %s\n", szRemoteDir, prl_result_to_string(nJobResult)); PrlHandle_Free(hJob); return -1; }
PrlHandle_Free( hJob ); printf( "Remote directory %s was successfully created.\n", szRemoteDir );
26
Parallels C API Concepts
Descriptive Error Strings
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:
PrlJob_GetRetCode(hJob, &nJobResult);
PRL_CHAR szErrBuff[1024]; PRL_UINT32 nErrBuffSize = sizeof(szErrBuff); PRL_HANDLE hError = PRL_INVALID_HANDLE; PRL_RESULT ret = PrlJob_GetError(hJob, &hError);
// Check if additional error information is available. if (PRL_SUCCEEDED(ret)) // Additional error information is available. { // Additional error information is available. ret = PrlEvent_GetErrString(hError, PRL_FALSE, PRL_FALSE, szErrBuff, &nErrBuffSize); if (PRL_FAILED(ret)) { printf("PrlEvent_GetErrString returned error: %.8x %s\n", ret, prl_result_to_string(ret)); } else { // Extra error information is available, display it. printf("Error returned: %.8x %s\n", nJobResult, prl_result_to_string(nJobResult)); printf("Descriptive error: %s\n", szErrBuff); } } else { // No additional error information available, so use PrlApi_GetResultDescription. ret = PrlApi_GetResultDescription(nJobResult, PRL_FALSE, PRL_FALSE, szErrBuff, &nErrBuffSize); if (PRL_FAILED(ret)) { printf("PrlApi_GetResultDescription returned error: %s\n", prl_result_to_string(ret)); } else { printf("Error returned: %.8x %s\n", nJobResult, prl_result_to_string(nJobResult)); printf("Descriptive error: %s\n", szErrBuff); } } // Free handles, return the error code. PrlHandle_Free(hJob);
27
Parallels C API Concepts
PrlHandle_Free(hError); return nJobResult; }
28
C HAPTER 3

Parallels C API by Example

This chapter contains examples of using the Parallels C API. The examples include tasks such as performing general Parallels Desktop tasks, creating and managing virtual machines, handling event, collecting performance data, and others.
In This Chapter
Obtaining Server Handle and Logging In ................................................................. 30
Host Operations ..................................................................................................... 34
Virtual Machine Operations ..................................................................................... 54
Events .................................................................................................................... 101
Performance Statistics ............................................................................................ 111
Encryption Plug-in .................................................................................................. 120
C HAPTER 4

Obtaining Server Handle and Logging In

The following steps are required in any program using the Parallels C API:
1 Load the Parallels Virtualization SDK library using the SdkWrap_Load function. 2 Initialize the API using the PrlApi_InitEx function. The API must be initialized properly for
the given Parallels product, such as Parallels Server, Parallels Desktop. The initialization mode is determined by the value of the nAppMode parameter passed to the PrlApi_InitEx function. The value must be one of the enumerators from the PRL_APPLICATION_MODE enumeration.
3 Create a server handle using the PrlSrv_Create function. 4 Call PrlSrv_LoginLocal or PrlSrv_Login to login to the Parallels Virtualization Service
(or simply Parallels Service). Parallels Service is a combination of processes running on the host machine that comprise the Parallels virtualization product. The first function is used when the program and the target Parallels Service are running on the same host. The second function (PrlSrv_Login) is used to log in to a remote Parallels Service. Please note that remote login is supported in Parallels Server-based virtualization products only.
If the above steps are executed without errors, the handle created in step 3 will reference a Server object (a handle of type PHT_SERVER) identifying the Parallels Service. A handle to a valid Server object is required to access most of the functionality within the Parallels C API. The PrlSrv_LoginLocal function (step 4) establishes a connection with a specified Parallels Service and performs a login operation using the specified credentials. The function operates on the Server object created in step 3. Upon successful login, the object can be used to perform other operations.
To end the session with the Parallels Service, the following steps must be performed before exiting the application:
1 Call PrlSrv_Logoff to log off the Parallels Service. 2 Free the Server handle using PrlHandle_Free. 3 Call PrlApi_Deinit to de-initialize the library. 4 Call SdkWrap_Unload to unload the API.
Example
The following sample functions demonstrates how to perform the steps described above.
// Intializes the SDK library and // logs in to the local Parallels Service. // Obtains a handle of type PHT_SERVER identifying // the Parallels Service.
Loading...
+ 133 hidden pages