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.
Parallels C API by Example
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 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; }
// 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.
31
Parallels C API by Example
err = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(err)) { fprintf(stderr, "PrlJob_Wait for PrlSrv_Login returned with error: %s\n", prl_result_to_string(err)); PrlHandle_Free(hJob); PrlHandle_Free(hServer); PrlApi_Deinit(); SdkWrap_Unload(); return -1; }
// Analyze the result of PrlSrv_Login. err = PrlJob_GetRetCode(hJob, &nJobReturnCode);
// First, check PrlJob_GetRetCode success/failure. if (PRL_FAILED(err)) { fprintf(stderr, "PrlJob_GetRetCode returned with error: %s\n", prl_result_to_string(err)); PrlHandle_Free(hJob); PrlHandle_Free(hServer); PrlApi_Deinit(); SdkWrap_Unload(); return -1; }
// Now check the Login operation success/failure. if (PRL_FAILED(nJobReturnCode)) { PrlHandle_Free(hJob); PrlHandle_Free(hServer); printf("Login job returned with error: %s\n", prl_result_to_string(nJobReturnCode)); PrlHandle_Free(hJob); PrlHandle_Free(hServer); PrlApi_Deinit(); SdkWrap_Unload(); return -1; } else { printf( "Login was successful.\n" ); }
return 0; }
// Log off the Parallels Service and // deinitializes the SDK library. // PRL_RESULT LogOff(PRL_HANDLE &hServer) { PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
PRL_RESULT err = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
32
Parallels C API by Example
// 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; } 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; }
33
C HAPTER 5

Host Operations

This chapter describes the common tasks that can be performed on the host.
In This Chapter
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

Retrieving Host Configuration Information

The Parallels C API provides a set of functions to retrieve detailed information about a host machine. This includes:
CPU(s) - number of, mode, model, speed.
Devices - disk drives, network interfaces, ports, sound.
Operating system - type, version, etc.
Memory (RAM) size.
This information can be used when modifying Parallels Service preferences, setting up devices inside virtual machines, or whenever you need to obtain information about the resources available on the physical host.
To retrieve this information, first obtain a handle of type PHT_SERVER_CONFIG and then use its functions to get information about a particular resource. The following sample function demonstrates how it is accomplished. The function accepts the hServer parameter which is a server handle. For the example on how to obtain a server handle, see Obtaining Server Handle and Logging In (p. 30).
PRL_RESULT GetHostConfig(PRL_HANDLE hServer) { PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; PRL_HANDLE hHostConfig = PRL_INVALID_HANDLE;
PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// An asynchronous call that obtains a handle // of type PHT_SERVER_CONFIG.
hJob = PrlSrv_GetSrvConfig(hServer);
// Wait for the job to complete. ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; }
// Analyze the result of PrlSrv_GetSrvConfig. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; } // Get the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... PrlHandle_Free(hJob); return -1; }
// Get job result. ret = PrlJob_GetResult(hJob, &hJobResult); PrlHandle_Free(hJob); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Get the PHT_SERVER_CONFIG handle. ret = PrlResult_GetParam(hJobResult, &hHostConfig); PrlHandle_Free(hJobResult); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Obtain the individual configuration setting. printf("\nHost Configuration Information: \n\n");
// Get CPU count. PRL_UINT32 nCPUcount = 0; ret = PrlSrvCfg_GetCpuCount(hHostConfig, &nCPUcount); if (PRL_FAILED(ret)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(ret)); PrlHandle_Free(hHostConfig); return -1; }
printf("CPUs: %d\n", nCPUcount);
Parallels C API by Example
35
Parallels C API by Example
// Get host OS type. PRL_HOST_OS_TYPE nHostOsType; ret = PrlSrvCfg_GetHostOsType(hHostConfig, &nHostOsType);
// if (PRL_FAILED(ret)) { handle the error... }
printf("OS Type: %d\n", nHostOsType);
// Get host RAM size. PRL_UINT32 nHostRamSize; ret = PrlSrvCfg_GetHostRamSize(hHostConfig, &nHostRamSize);
// if (PRL_FAILED(ret)) { handle the error... }
printf("RAM: %d MB\n", nHostRamSize);
// Get the network adapter info. // First get the net adapter count. PRL_UINT32 nNetAdaptersCount = 0; ret = PrlSrvCfg_GetNetAdaptersCount(hHostConfig, &nNetAdaptersCount); // if (PRL_FAILED(ret)) { handle the error... }
// Now iterate through the list and get the info // about each adapter. printf("\n"); for (PRL_UINT32 i = 0; i < nNetAdaptersCount; ++i) { printf("Net Adapter %d\n", i+1);
// Obtains a handle of type PHT_HW_NET_ADAPTER. PRL_HANDLE phDevice = PRL_INVALID_HANDLE; ret = PrlSrvCfg_GetNetAdapter(hHostConfig, i, &phDevice);
// Get adapter type (physical, virtual). PRL_HW_INFO_NET_ADAPTER_TYPE nNetAdapterType; ret = PrlSrvCfgNet_GetNetAdapterType(phDevice, &nNetAdapterType); printf("Type: %d\n", nNetAdapterType);
// Get system adapter index. PRL_UINT32 nIndex = 0; ret = PrlSrvCfgNet_GetSysIndex(phDevice, &nIndex); printf("Index: %d\n\n", nIndex); }
PrlHandle_Free(hHostConfig);
return 0; }

Managing Parallels Service Preferences

Parallels Service preferences is a set of parameters that control its default behaviour. Some of the important parameters are:
Memory limits for the Parallels Service itself.
Memory limits and recommended values for virtual machines.
36
Parallels C API by Example
Virtual network adapter information.
Default virtual machine directory (the directory where all new virtual machines are created by
default).
Communication security level. Parallels Service preferences are managed using the PHT_DISP_CONFIG handle which is obtained
using the PrlSrv_GetCommonPrefs function. For the complete list of functions provided by the PHT_DISP_CONFIG object, see the Parallels C API Reference guide.
The following sample function demonstrates how to obtain a handle of type PHT_DISP_CONFIG and how to use its functions to retrieve and modify some of the Parallels Service preferences. The function accepts the hServer parameter which is a server handle. For the example on how to obtain a server handle, see Obtaining Server Handle and Logging In (p. 30).
PRL_RESULT GetSetServicePrefs(PRL_HANDLE hServer) { // Variables for handles. PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; PRL_HANDLE hDispConfig = PRL_INVALID_HANDLE;
// Variables for return codes. PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// An asynchronous call that obtains a handle // of type PHT_DISP_CONFIG. hJob = PrlSrv_GetCommonPrefs(hServer);
// Wait for the job to complete. ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; }
// Analyze the result of PrlSrv_GetCommonPrefs. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; } // Get the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... PrlHandle_Free(hJob); return -1; }
// Get job result. ret = PrlJob_GetResult(hJob, &hJobResult); PrlHandle_Free(hJob);
37
Parallels C API by Example
if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Get the PHT_DISP_CONFIG handle. ret = PrlResult_GetParam(hJobResult, &hDispConfig); PrlHandle_Free(hJobResult); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Get the default virtual machine directory. char sDefaultDir[1024]; PRL_UINT32 nBufSize = sizeof(sDefaultDir); ret = PrlDispCfg_GetDefaultVmDir(hDispConfig, sDefaultDir, &nBufSize);
if (PRL_FAILED(ret)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(ret)); PrlHandle_Free(hDispConfig); return -1; }
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))
38
Parallels C API by Example
{ 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; }
PrlHandle_Free(hJob);
// Modify the recommended virtual machine memory size. nMemSize = 512; ret = PrlDispCfg_SetRecommendMaxVmMem(hDispConfig, nMemSize); if (PRL_FAILED(ret)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(ret)); PrlHandle_Free(hDispConfig); return -1; }
// Commit the changes. hJob = PrlSrv_CommonPrefsCommit(hServer, hDispConfig); ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(ret)); PrlHandle_Free(hJob); PrlHandle_Free(hDispConfig); return -1; }
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(ret)); PrlHandle_Free(hJob); PrlHandle_Free(hDispConfig); return -1; } if (PRL_FAILED(nJobReturnCode)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobReturnCode)); PrlHandle_Free(hJob); PrlHandle_Free(hDispConfig); return -1; }
printf("The recommended VM memory size changed to: %d\n", nMemSize);
39
Parallels C API by Example
PrlHandle_Free(hDispConfig);
return 0; }

Searching for Parallels Servers

This topic applies to Parallels Server only.
If you have multiple Parallels Servers running on your network and don't know their exact locations and/or connection parameters, you can search for them using the PrlSrv_LookupParallelsServers function. The function returns the information as a list of handles of type PHT_SERVER_INFO, each containing the information about an individual Parallels Server. The information includes host name, port number, version of the OS that a host is running, Parallels Server version number, and the global ID (UUID). This information can then be used to establish a connection with the Parallels Server of interest (you will have to know the correct user name and password in addition to the returned parameters).
The PrlSrv_LookupParallelsServers function can be executed asynchronously using the callback functionality or it can be used synchronously. To use the function asynchronously, you must implement a callback function first. The callback function pointer must then be passed to the PrlSrv_LookupParallelsServers as a parameter. During the search operation, the callback function will be called for every Parallels Server found and a handle of type PHT_SERVER_INFO containing the Parallels Server information will be passed to it. Searching an entire local area network can take a significant time, so using a callback is the recommended approach.
To use the PrlSrv_LookupParallelsServers function synchronously, pass a null pointer instead of the callback function pointer, and use PrlJob_Wait to wait for the job to complete. The returned job object will contain a list of PHT_SERVER_INFO objects.
Note: The PrlSrv_LookupParallelsServers function can be executed without being logged in to a Parallels Service. For example, if you are writing an application with a user interface, you can search the network for available Parallels Servers and present the list to the user so that he/she can select a server to connect to.
The following sample functions demonstrate how to search local network for Parallels Servers. The first sample function calls the PrlSrv_LookupParallelsServers function synchronously. The second function takes an asynchronous approach.
PRL_RESULT SearchServersSynch() { // Variables for handles. PRL_HANDLE hJob = PRL_INVALID_HANDLE; // job handle PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; // job result PRL_HANDLE hHostConfig = PRL_INVALID_HANDLE; // PHT_SERVER_CONFIG
// Variables for return codes. PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Search for Parallels servers.
40
Parallels C API by Example
hJob = PrlSrv_LookupParallelsServers( 1000, // timeout NULL, // callback function (not used) NULL // user object pointer (not used) );
// Wait for the job to complete. ret = PrlJob_Wait(hJob, 10000); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); fprintf(stderr, "Error: %s. \n", prl_result_to_string(ret)); return -1; }
// Analyze the result of PrlSrv_LookupParallelsServers. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; } // Get the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... PrlHandle_Free(hJob); return -1; }
// Get job result. ret = PrlJob_GetResult(hJob, &hJobResult); PrlHandle_Free(hJob); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Get the number of objects returned. PRL_UINT32 nCount = 0; PrlResult_GetParamsCount(hJobResult, &nCount);
// Iterate and a obtain handle to each object. for (PRL_UINT32 i = 0; i < nCount ; ++i) { PRL_HANDLE hParam = PRL_INVALID_HANDLE; PrlResult_GetParamByIndex(hJobResult, i, &hParam);
PRL_CHAR sBuf[1024]; PRL_UINT32 nBufSize = sizeof(sBuf);
// Get the host name. ret = PrlSrvInfo_GetHostName(hParam, sBuf, &nBufSize);
if (PRL_SUCCEEDED(ret)) { printf("Found Parallels Server: %s\n", sBuf);
41
Parallels C API by Example
} else { fprintf(stderr, "Error: %s \n", prl_result_to_string(ret)); }
PrlHandle_Free(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):
static PRL_RESULT ourCallback(PRL_HANDLE hEvent, PRL_VOID_PTR pUserData) { printf("%s: ", pUserData);
// Get the host name. PRL_UINT32 nBufSize = 1024; PRL_CHAR sBuf[nBufSize]; PrlSrvInfo_GetHostName(hEvent, sBuf, &nBufSize);
// Get the other server properties and process them here, if needed...
// The handle must be freed. PrlHandle_Free(hEvent); return rc; }
The PrlSrv_LookupParallelsServers function can now be called as follows:
hJob = PrlSrv_LookupParallelsServers( 1000, &ourCallback, (PRL_VOID_PTR)("callback"));

Managing Parallels Service Users

This topic applies to Parallels Server only.
Parallels Service doesn't have its own user database. It performs user authentication against the host operating system user database. However, it has a user registry where the user information that relates to Parallels Service operations is kept. The information includes user UUID (Universally Unique ID), user name, the name and path of the virtual machine directory for the user, and two flags indicating if a user is allowed to modify server preferences and use management console application. A new user record is created in the registry for every user as soon as he/she logs in to a Parallels Service for the very first time.
There are two API handles that are used to obtain information about Parallels Service users and to modify some of the user profile parameters. These handles are PHT_USER_INFO and PHT_USER_PROFILE. Both handles are containers that contain information about a user. The difference between the two is PHT_USER_PROFILE is used to obtain information about currently logged in user while PHT_USER_INFO is used to obtain information about a specified user. There are also some differences in the type of the information provided.
42
Parallels C API by Example
Getting the information about the currently logged in user
The information about the currently logged in user can be retrieved using functions of the PHT_USER_PROFILE handle. The following sample demonstrates how to obtain the handle and how to use its functions to retrieve user information. The sample also shows how to set up a default virtual machine directory for the user. Parallels Service automatically assigns a default virtual machine directory (the directory where new virtual machines are created) for every new user. If needed, a user can specify a different directory for his/her virtual machines. At the time of this writing, this is the only property of the Parallels Service user profile that can be modified. Every user profile modification must begin with the PrlSrv_UserProfileBeginEdit function call and end with the PrlSrv_UserProfileCommit call. These two functions are used to prevent collisions with other clients trying to modify the same user profile at the same time.
PRL_RESULT UserProfileSample(const PRL_HANDLE &hServer) { PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; PRL_HANDLE hUserProfile = PRL_INVALID_HANDLE;
PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Get user info from the server. hJob = PrlSrv_GetUserProfile(hServer);
// Wait for the job to complete. ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Analyze the result of PrlSrv_GetUserProfile. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; } // Check the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... PrlHandle_Free(hJob); return -1; }
// Get job result. ret = PrlJob_GetResult(hJob, &hJobResult); PrlHandle_Free(hJob); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
43
Parallels C API by Example
// Get the user profile handle (PHT_USER_PROFILE) from // the result. ret = PrlResult_GetParam(hJobResult, &hUserProfile); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJobResult); return -1; }
// Free job result handle. PrlHandle_Free(hJobResult);
// See if the user is allowed to modify // the Parallels server preferences. PRL_BOOL bCanChange = PRL_FALSE; ret = PrlUsrCfg_CanChangeSrvSets(hUserProfile, &bCanChange); printf("Can modify server preferences: %d\n", bCanChange);
// See if the user is allowed to use management // console application. ret = PrlUsrCfg_CanUseMngConsole(hUserProfile, &bCanChange); printf("Can use management console: %d\n", bCanChange);
// Get the default virtual machine folder // for the user. PRL_CHAR sBufFolder[1024]; PRL_UINT32 nBufSize = sizeof(sBufFolder); ret = PrlUsrCfg_GetDefaultVmFolder(hUserProfile, sBufFolder, &nBufSize);
// If sBufFolder contains an empty string then this user // does not have a default virtual machine folder and is // currently using the default virtual machine folder set // for this Parallels server. If this is the case, retrieve // that folder. if (sBufFolder == "") { ret = PrlUsrCfg_GetVmDirUuid(hUserProfile, sBufFolder, &nBufSize); }
printf("VM folder: %s\n", sBufFolder);
// Modify the name and location of the virtual // machine folder. // This operation must begin with the // PrlSrv_UserProfileBeginEdit that marks the // beginning of the operation. This is done to // prevent collisions with other sessions trying to // modify the same profile at the same time. hJob = PrlSrv_UserProfileBeginEdit(hServer); ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; } // Analyze the result of PrlSrv_UserProfileBeginEdit. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); PrlHandle_Free(hJob);
44
Parallels C API by Example
if (PRL_FAILED(ret)) { // Handle the error... return -1; } // Check the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... return -1; } // Set the new virtual machine folder. // The folder must already exist on the server. ret = PrlUsrCfg_SetDefaultVmFolder(hUserProfile, "/Users/Shared/Parallels/JDoe"); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hUserProfile); return -1; } // Finally, commit the changes to the server. hJob = PrlSrv_UserProfileCommit(hServer, hUserProfile); ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; } // Analyze the result of PrlSrv_UserProfileCommit. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); PrlHandle_Free(hJob); if (PRL_FAILED(ret)) { // Handle the error... return -1; } // Check the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... return -1; }
PrlHandle_Free(hUserProfile); }
Getting the information about a particular user
The information about a particular Parallels Service user can be obtained using the functions of the
PHT_USER_INFO handle. The handle can be obtain using one of the following functions: PrlSrv_GetUserInfo or PrlSrv_GetUserInfoList. The first function takes the user UUID
as an input parameter and returns a single handle of type PHT_USER_INFO containing the user information. The second function returns information about all users that exist in the Parallels Service user registry. The information is returned as a list of handles of type PHT_USER_INFO.
45
Parallels C API by Example
The following sample uses the PrlSrv_GetUserInfoList function to obtain information about all users in the Parallels Service user registry. It then iterates through the returned list of
PHT_USER_INFO handles and retrieves information about individual users.
PRL_RESULT UserInfoSample(const PRL_HANDLE &hServer) { PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; PRL_HANDLE hUserInfo = PRL_INVALID_HANDLE;
PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Get user info from the Parallels Service. hJob = PrlSrv_GetUserInfoList(hServer);
// Wait for the job to complete. ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Analyze the result of PrlSrv_GetUserInfoList. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; } // Check the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... PrlHandle_Free(hJob); return -1; }
// Get job result. ret = PrlJob_GetResult(hJob, &hJobResult); PrlHandle_Free(hJob); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Get parameter count (the number of PHT_USER_INFO // handles in the result set). PRL_UINT32 nParamCount = 0; ret = PrlResult_GetParamsCount(hJobResult, &nParamCount);
// Iterate through the list obtaining // a handle of type PHT_USER_INFO for // each user. for (PRL_UINT32 i = 0; i < nParamCount; ++i) { ret = PrlResult_GetParamByIndex(hJobResult, i, &hUserInfo);
46
Parallels C API by Example
if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Get user UUID. PRL_CHAR sBufID[1024]; PRL_UINT32 nBufSize = sizeof(sBufID); ret = PrlUsrInfo_GetUuid(hUserInfo, sBufID, &nBufSize); printf("UUID: %s\n", sBufID);
// Get user name. PRL_CHAR sBufName[1024]; nBufSize = sizeof(sBufName); PrlUsrInfo_GetName(hUserInfo, sBufName, &nBufSize); printf("Name: %s\n", sBufName);
// Get default virtual machine folder // for the user. PRL_CHAR sBufFolder[1024]; nBufSize = sizeof(sBufFolder); PrlUsrInfo_GetDefaultVmFolder(hUserInfo, sBufFolder, &nBufSize); printf("VM folder: %s\n", sBufFolder);
// See if the user is allowed to modify // the Parallels server preferences. PRL_BOOL bCanChange = PRL_FALSE; PrlUsrInfo_CanChangeSrvSets(hUserInfo, &bCanChange); printf("Can modify server preferences: %d\n\n", bCanChange);
PrlHandle_Free(hUserInfo); } PrlHandle_Free(hJobResult); }

Managing Files In The Host OS

The following file management operations can be performed using the Parallels C API on the host machine:
Obtaining a directory listing.
Creating directories.
Automatically generate unique names for new file system entries.
Rename file system entries.
Delete file system entries.
The file management functionality can be accessed through the PHT_SERVER handle. The file management functions are prefixed with "PrlSrv_Fs".
47
Parallels C API by Example
Obtaining the host OS directory listing
The directory listing is obtained using the PrlSrv_FsGetDirEntries function. The function returns a handle of type PHT_REMOTE_FILESYSTEM_INFO containing the information about the specified file system entry and its immediate child entries (if any). The child entries are returned as a list of handles of type PHT_REMOTE_FILESYSTEM_ENTRY which is included in the PHT_REMOTE_FILESYSTEM_INFO object. The sample function below demonstrates how to obtain a listing for the specified directory. On initial call, the function obtains a list of child entries (files and sub-directories) for the specified directory and is then called recursively for each file system entry returned. On completion, the entire directory tree will be displayed on the screen.
// Obtains the entire directory tree in the host OS // starting at the specified path. // The "levels" parameter specifies how many levels should the // function traverse down the directory tree. PRL_RESULT GetHostDirList(PRL_HANDLE hServer, PRL_CONST_STR path, int levels) { PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; PRL_HANDLE hParentDirectory = PRL_INVALID_HANDLE; PRL_HANDLE hChildElement = PRL_INVALID_HANDLE;
PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Get directory list from the host. // The second parameter specifies the absolute // path for which to get the directory listing. hJob = PrlSrv_FsGetDirEntries(hServer, path);
// Wait for the job to complete. ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Analyze the result of PrlSrv_FsGetDirEntries. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; } // Check the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... PrlHandle_Free(hJob); return -1; }
// Get job result. ret = PrlJob_GetResult(hJob, &hJobResult); PrlHandle_Free(hJob);
48
Parallels C API by Example
if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Get a handle to the parent directory. // This is the directory that we specified in the // PrlSrv_FsGetDirEntries call above. ret = PrlResult_GetParam(hJobResult, &hParentDirectory); PrlHandle_Free(hJobResult); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Get parameter count (the number of child entries). PRL_UINT32 nParamCount = 0; ret = PrlFsInfo_GetChildEntriesCount(hParentDirectory, &nParamCount); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; }
// Iterate through the list obtaining // a handle of type PHT_REMOTE_FILESYSTEM_ENTRY // for each child element of the parent directory. for (PRL_UINT32 i = 0; i < nParamCount; ++i) { // Get a handle to the child element. ret = PrlFsInfo_GetChildEntry(hParentDirectory, i, &hChildElement); if (PRL_FAILED(ret)) { // Handle the error... continue; }
// Get the filesystem element name. PRL_CHAR sBuf[1024]; PRL_UINT32 nBufSize = sizeof(sBuf); ret = PrlFsEntry_GetAbsolutePath(hChildElement, sBuf, &nBufSize); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hChildElement); continue; }
printf("%s\n", sBuf); PrlHandle_Free(hChildElement);
// 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); }
49
Parallels C API by Example
} PrlHandle_Free(hParentDirectory); PrlHandle_Free(hJob);
return PRL_ERR_SUCCESS; }

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 UpdateLicenseSample(PRL_HANDLE hServer) { PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; PRL_HANDLE hLicense = PRL_INVALID_HANDLE; PRL_HANDLE hUpdateLicense = PRL_INVALID_HANDLE;
PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Get the license info from the Parallels Service. hJob = PrlSrv_GetLicenseInfo(hServer); // Wait for the job to complete. ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Analyze the result of PrlSrv_GetUserInfoList. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; } // Check the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... PrlHandle_Free(hJob); return -1; }
// Get job result. ret = PrlJob_GetResult(hJob, &hJobResult); PrlHandle_Free(hJob); if (PRL_FAILED(ret)) {
50
Parallels C API by Example
// Handle the error... return -1; }
// Get parameter from job result. ret = PrlResult_GetParam(hJobResult, &hLicense); PrlHandle_Free(hJobResult); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Get company name. PRL_CHAR sCompany[1024]; PRL_UINT32 nCompanyBufSize = sizeof(sCompany); ret = PrlLic_GetCompanyName(hLicense, sCompany, &nCompanyBufSize); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hLicense); return -1; } printf("Company: %s\n", sCompany);
// Get user name. PRL_CHAR sUser[1024]; PRL_UINT32 nUserBufSize = sizeof(sUser); ret = PrlLic_GetUserName(hLicense, sUser, &nUserBufSize); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hLicense); return -1; } printf("User: %s\n", sUser);
// Get license key. PRL_CHAR sKey[1024]; PRL_UINT32 nKeyBufSize = sizeof(sKey); ret = PrlLic_GetLicenseKey(hLicense, sKey, &nKeyBufSize); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hLicense); return -1; } printf("Key: %s\n", sKey);
// See license type. PRL_BOOL isTrial = PRL_TRUE; ret = PrlLic_IsTrial(hLicense, &isTrial); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hLicense); return -1; } printf("Trial: %d\n", isTrial);
PrlHandle_Free(hLicense);
51
Parallels C API by Example
// Update the license info. // Here, we use the same license information that we // retrieved earlier. Normally, you would use the // information that you received from // your Parallels product distributor. hUpdateLicense = PrlSrv_UpdateLicense(hServer, sKey, sUser, sCompany); // Wait for the job to complete. ret = PrlJob_Wait(hUpdateLicense, 1000); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
PrlHandle_Free(hUpdateLicense); }

Obtaining a Problem Report

If you are experiencing a problem with a virtual machine, you can obtain a problem report from the Parallels Service. The report can then be sent to the Parallels technical support for evaluation. A problem report contains technical data about your Parallels product installation, log data, and other technical details that can be used to determine the source of the problem and to develop a solution. The following example demonstrates how to obtain the report.
PRL_RESULT GetProblemReport(PRL_HANDLE hServer) { PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Get problem report from the host. hJob = PrlSrv_GetProblemReport(hServer);
// Wait for the job to complete. ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Analyze the result of PrlSrv_GetProblemReport. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; } // Check the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... PrlHandle_Free(hJob);
52
Parallels C API by Example
return -1; }
// Get job result. ret = PrlJob_GetResult(hJob, &hJobResult); PrlHandle_Free(hJob); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Get the string containing the report data. // First, get the required buffer size. PRL_UINT32 nBufSize = 0; ret = PrlResult_GetParamAsString(hJobResult, NULL, &nBufSize); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJobResult); return -1; }
// Second, initialize the buffer and get the report. char* sReportData =(PRL_STR)malloc(nBufSize); ret = PrlResult_GetParamAsString(hJobResult, sReportData, &nBufSize);
if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJobResult); return ret; }
printf("%s", sReportData); PrlHandle_Free(hJobResult); free(sReportData);
return 0; }
53
C HAPTER 6

Virtual Machine Operations

This chapter describes the common tasks that can be performed on virtual machines.
In This Chapter
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
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

Obtaining the Virtual Machines List

Any virtual machine operation begins with obtaining a handle of type PHT_VIRTUAL_MACHINE identifying the virtual machine. Once a handle identifying a virtual machine is obtained, its functions can be used to perform a full range of virtual machine operations. This sections describes how to obtain a list of handles, each identifying an individual virtual machines registered with a Parallels Service. If you would like to search for unregistered virtual machines on the host computer, please refer to the Searching for Virtual Machines section (p. 68).
The steps that must be performed to obtain the virtual machine list are:
1 Log in to the Parallels Service and obtain a handle of type PHT_SERVER. See Obtaining
Server Handle and Logging In (p. 30) for more info and code samples.
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 PrlHandle_Free as it is no longer needed. 5 Call PrlResult_GetParamsCount to determine the number of virtual machines contained in
the PHT_RESULT object.
Parallels C API by Example
6 Call the PrlResult_GetParamByIndex function in a loop passing an index from 0 to the
total virtual machine count. The function obtains a handle of type PHT_VIRTUAL_MACHINE containing information about an individual virtual machine.
7 Use functions of the PHT_VIRTUAL_MACHINE object to obtain the virtual machine information.
For example, use PrlVmCfg_GetName to obtain the virtual machine name.
8 Free the virtual machine handle using PrlHandle_Free. 9 Free the result handle using PrlHandle_Free.
The following sample function implements the steps described above.
PRL_RESULT GetVmList(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 ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Get the list of the available virtual machines. hJob = PrlSrv_GetVmList(hServer);
// Wait for a maximum of 10 seconds for PrlSrv_GetVmList. ret = PrlJob_Wait(hJob, 10000); if (PRL_FAILED(ret)) { fprintf(stderr, "PrlJob_Wait for PrlSrv_GetVmList returned with error: %s\n", prl_result_to_string(ret)); PrlHandle_Free(hJob); return ret; }
// Check the results of PrlSrv_GetVmList. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret)) { fprintf(stderr, "PrlJob_GetRetCode returned with error: %s\n", prl_result_to_string(ret)); PrlHandle_Free(hJob); return ret; }
if (PRL_FAILED(nJobReturnCode)) { fprintf(stderr, "PrlSrv_GetVmList returned with error: %s\n", prl_result_to_string(ret)); PrlHandle_Free(hJob); return ret; }
// Get the results of PrlSrv_GetVmList. ret = PrlJob_GetResult(hJob, &hJobResult); if (PRL_FAILED(ret)) { fprintf(stderr, "PrlJob_GetResult returned with error: %s\n",
55
Parallels C API by Example
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); }
// Free the virtual machine handle. PrlHandle_Free(hVm); }
return PRL_ERR_SUCCESS; }

Searching for Virtual Machine by Name

This section contains am example of how to obtain a handle of type PHT_VIRTUAL_MACHINE identifying the virtual machine using the virtual machine name as a search parameter. We will use the sample as a helper function in the later section of this guide that demonstrate how to perform operations on virtual machines. The sample is based on the code provided in the Obtaining the Virtual Machine List section (p. 54).
// Obtains a handle of type PHT_VIRTUAL_MACHINE using the // virtual machine name as a search parameter. // Parameters
56
Parallels C API by Example
// hServer: A handle of type PHT_SERVER. // sVmName: The name of the virtual machine. // hVm: [out] A handle of type PHT_VIRTUAL_MACHINE // identifying the virtual machine. PRL_RESULT GetVmByName(PRL_HANDLE hServer, PRL_STR sVmName, PRL_HANDLE &hVm) { PRL_HANDLE hResult = PRL_INVALID_HANDLE; PRL_RESULT nJobResult = PRL_INVALID_HANDLE;
// Get a list of available virtual machines. PRL_HANDLE hJob = PrlSrv_GetVmList(hServer);
PRL_RESULT ret = PrlJob_Wait(hJob, 10000); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); PrlHandle_Free(hServer); return ret; }
// Check the results of PrlSrv_GetVmList. ret = PrlJob_GetRetCode(hJob, &nJobResult); if (PRL_FAILED(nJobResult)) { // Handle the error... PrlHandle_Free(hJob); PrlHandle_Free(hServer); return ret; }
// Get the results of PrlSrv_GetVmList. ret = PrlJob_GetResult(hJob, &hResult); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); PrlHandle_Free(hServer); return ret; }
PrlHandle_Free(hJob);
// Iteratre through the results (list of virtual machines returned). PRL_UINT32 nParamsCount = 0; ret = PrlResult_GetParamsCount(hResult, &nParamsCount);
for (PRL_UINT32 i = 0; i < nParamsCount; ++i) { // Get a handle to result i. PrlResult_GetParamByIndex(hResult, i, &hVm);
// Get the name of the virtual machine for result i. char vm_name[1024]; PRL_UINT32 nBufSize = sizeof(vm_name);
ret = PrlVmCfg_GetName(hVm, vm_name, &nBufSize);
if (PRL_FAILED(ret)) { // Handle the error...
57
Parallels C API by Example
return PRL_ERR_FAILURE; }
// If the name of the virtual machine at this index is equal to sVmName, // then this is the handle we need. if (strcmp(sVmName, vm_name) == 0) { PrlHandle_Free(hResult); return PRL_ERR_SUCCESS; }
// It's not the virtual machine being searched for, so free the handle to it. PrlHandle_Free(hVm); }
// The specified virtual machine was not found. PrlHandle_Free(hResult);
return PRL_ERR_NO_DATA; }

Obtaining Virtual Machine Configuration Information

The virtual machine configuration information is obtained using functions of the PHT_VM_CONFIGURATION object. The functions are prefixed with PrlVmCfg_. To use the functions, a handle of type PHT_VM_CONFIGURATION must first be obtained from the virtual machine object (a handle of type PHT_VIRTUAL_MACHINE) using the PrlVm_GetConfig function. The following example shows how to obtain the virtual machine name, guest operating system type and version, RAM size, HDD size, and CPU count. To obtain the virtual machine handle (hVm input parameter), use the helper function described in the Searching for Virtual Machine by Name section (p. 56).
PRL_RESULT GetVmConfig(PRL_HANDLE hVm) { PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
// Obtain the PHT_VM_CONFIGURATION handle. PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE;
ret = PrlVm_GetConfig(hVm, &hVmCfg);
if (PRL_FAILED(ret)) { // Handle the error... return ret; }
// Get the virtual machine name. PRL_STR szVmName; PRL_UINT32 nVmNameSize = 0;
// Call once with NULL (PRL_INVALID_HANDLE) to get the size // of the buffer to allocate for the VM name. PrlVmCfg_GetName(hVmCfg, PRL_INVALID_HANDLE, &nVmNameSize);
// Allocate memory for the VM name. szVmName = (PRL_STR)malloc(nVmNameSize);
58
Parallels C API by Example
// Get the virtual machine name. PrlVmCfg_GetName(hVmCfg, szVmName, &nVmNameSize); printf("Virtual machine name: %s\n", szVmName);
free(szVmName);
// Get the OS type. PRL_UINT32 nOsType = 0; PrlVmCfg_GetOsType(hVmCfg, &nOsType);
char* sOsTypeName; switch (nOsType) { case PVS_GUEST_TYPE_WINDOWS: sOsTypeName = "Windows"; printf("OS Type: %s\n", PVS_GUEST_TYPE_NAME_WINDOWS); break; case PVS_GUEST_TYPE_LINUX: printf("OS Type: %s\n", PVS_GUEST_TYPE_NAME_LINUX); break; case PVS_GUEST_TYPE_MACOS: printf("OS Type: %s\n", PVS_GUEST_TYPE_NAME_MACOS); break; case PVS_GUEST_TYPE_FREEBSD: printf("OS Type: %s\n", PVS_GUEST_TYPE_NAME_FREEBSD); break; default: printf("OS Type: %s: %d\n", "Other OS Type: ", nOsType); }
// Get the OS version. PRL_UINT32 nOsVersion = 0; PrlVmCfg_GetOsVersion(hVmCfg, &nOsVersion); printf("OS Version: %s\n", PVS_GUEST_TO_STRING(nOsVersion));
// Get RAM size. PRL_UINT32 nRamSize = 0; PrlVmCfg_GetRamSize(hVmCfg, &nRamSize); printf("RAM size: %dMB\n", nRamSize);
// Get default HDD size. PRL_UINT32 nDefaultHddSize = 0; PrlVmCfg_GetDefaultHddSize(nOsVersion, &nDefaultHddSize); printf("Default HDD size: %dMB\n", nDefaultHddSize);
// Get CPU count. PRL_UINT32 nCpuCount = 0; PrlVmCfg_GetCpuCount(hVmCfg, &nCpuCount); printf("Number of CPUs: %d\n", nCpuCount);
return PRL_ERR_SUCCESS; }
59
Parallels C API by Example

Determining Virtual Machine State

To determine the current state of a virtual machine, first obtain a handle to the virtual machine as described in the Obtaining a List of Virtual Machines section (p. 54). Then use the
PrlVmCfg_GetState function to obtain a handle of type PHT_VM_INFO and call the PrlVmInfo_GetState function to obtain the state information. The function returns the virtual
machine state as an enumerator from the VIRTUAL_MACHINE_STATE enumeration that defines every possible state and transition applicable to a virtual machine. The following table lists the available states and transitions:
Enumerator State/Transition Description
VMS_UNKNOWN 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
VMS_MIGRATING VMS_DELETING_STATE VMS_RESUMING
State Unknown or unsupported state.
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.
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 GetVMstate(PRL_HANDLE hVm) { PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; PRL_HANDLE hVmInfo = PRL_INVALID_HANDLE;
60
PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Obtain the PHT_VM_CONFIGURATION handle. PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE; ret = PrlVm_GetConfig(hVm, &hVmCfg);
// Obtain a handle of type PHT_VM_INFO containing the // state information. The object will also contain the // virtual machine access rights info. We will discuss // this functionality later in this guide. hJob = PrlVm_GetState(hVmCfg);
// Wait for the job to complete. ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Analyze the result of PrlVm_GetState. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; } // Check the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... PrlHandle_Free(hJob); return -1; }
// Get job result. ret = PrlJob_GetResult(hJob, &hJobResult); PrlHandle_Free(hJob); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Get the PHT_VM_INFO handle. ret = PrlResult_GetParam(hJobResult, &hVmInfo); PrlHandle_Free(hJobResult); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Get the virtual machine state. VIRTUAL_MACHINE_STATE vm_state = VMS_UNKNOWN; ret = PrlVmInfo_GetState(hVmInfo, &vm_state); if (PRL_FAILED(ret)) { // Handle the error...
Parallels C API by Example
61
Parallels C API by Example
PrlHandle_Free(hVmInfo); return -1; } printf("Status: ");
switch (vm_state) { case VMS_UNKNOWN: printf("Unknown state\n"); break; case VMS_STOPPED: printf("Stopped\n"); break; case VMS_STARTING: printf("Starting...\n"); break; case VMS_RESTORING: printf("Restoring...\n"); break; case VMS_RUNNING: printf("Running\n"); break; case VMS_PAUSED: printf("Paused\n"); break; case VMS_SUSPENDING: printf("Suspending...\n"); break; case VMS_STOPPING: printf("Stopping...\n"); break; case VMS_COMPACTING: printf("Compacting...\n"); break; case VMS_SUSPENDED: printf("Suspended\n"); break; default: printf("Unknown state\n"); } printf("\n");
PrlHandle_Free(hVmCfg); PrlHandle_Free(hVmInfo);
return 0; }

Starting, Stopping, Resetting a Virtual Machine

Note: When stopping or resetting a virtual machine, please be aware of the following important information:
Stopping a virtual machine is not the same as performing a guest operating system shutdown operation. When a virtual machine is stopped, it is a cold stop (i.e. it is the same as turning off the power to a physical computer). Any unsaved data will be lost. However, if the OS in the virtual machine supports ACPI (Advanced Configuration and Power Interface) then you can set the second parameter of the PrlVm_Stop function to PRL_FALSE in which case, the ACPI will be used and the machine will be properly shut down.
62
Parallels C API by Example
Resetting a virtual machine is not the same as performing a guest operating system restart operation. It is the same as pressing the "Reset" button on a physical box. Any unsaved data will be lost.
The following sample function demonstrates how start, stop, and reset a virtual machine.
PRL_RESULT StartStopResetVm(PRL_HANDLE hVm, VIRTUAL_MACHINE_STATE action) { PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
if (action == VMS_RUNNING) { // Start the virtual machine. hJob = PrlVm_Start(hVm); printf("Starting the virtual machine... \n"); } else if (action == VMS_STOPPED) { // Stop the virtual machine. hJob = PrlVm_Stop(hVm, PRL_TRUE); printf("Stopping the virtual machine... \n"); } else if (action == VMS_RESETTING) { // Reset the virtual machine. hJob = PrlVm_Reset(hVm); printf("Resetting the virtual machine... \n"); } else { printf ("Invalid action type specified \n"); return PRL_ERR_FAILURE; }
PrlJob_Wait(hJob, 10000); PrlJob_GetRetCode(hJob, &nJobReturnCode);
if (PRL_FAILED(nJobReturnCode)) { printf ("Error: %s\n", prl_result_to_string(nJobReturnCode)); PrlHandle_Free(hJob); return PRL_ERR_FAILURE; }
return PRL_ERR_SUCCESS; }

Suspending and Pausing a Virtual Machine

Suspending a Virtual Machine
When a virtual machine is suspended, the information about its state is stored in non-volatile memory. A suspended virtual machine can resume operating in the same state it was in at the point it was placed into a suspended state. Resuming a virtual machine from a suspended state is quicker than starting a virtual machine from a stopped state.
63
Parallels C API by Example
To suspend a virtual machine, obtain a handle to the virtual machine, then call PrlVm_Suspend.
The following example will suspend a virtual machine called Windows XP - 01.
const char *szVmName = "Windows XP - 01";
// Get a handle to virtual machine with name szVmName. PRL_HANDLE hVm = GetVmByName((char*)szVmName, hServer); if (hVm == PRL_INVALID_HANDLE) { fprintf(stderr, "Virtual machine \"%s\" was not found.\n", szVmName); PrlHandle_Free(hServer); PrlApi_Deinit(); SdkWrap_Unload(); exit(-1); }
PRL_RESULT nJobResult; PRL_HANDLE hJob = PrlVm_Suspend(hVm); PRL_RESULT ret = PrlJob_Wait(hJob, 1000); if (PRL_FAIL(ret)) { fprintf(stderr, "PrlJob_Wait for PrlVm_Suspend failed. Error: %s", prl_result_to_string(ret)); PrlHandle_Free(hServer); PrlHandle_Free(hJob); PrlApi_Deinit(); SdkWrap_Unload(); exit(-1); } PrlJob_GetRetCode(hJob, &nJobResult); if (PRL_FAILED(nJobResult)) { fprintf(stderr, "PrlVm_Suspend failed with error: %s\n", prl_result_to_string(nJobResult)); PrlHandle_Free(hVm); PrlHandle_Free(hJob); PrlHandle_Free(hServer); PrlApi_Deinit(); SdkWrap_Unload(); return -1; }
A suspended virtual machine can be stopped completely (placed into a "stopped" state) using the PrlVm_DropSuspendedState function.
Pausing a Virtual Machine
Pausing a virtual machine will pause execution of the virtual machine. This can be achieved using PrlVm_Pause. PrlVm_Pause takes two parameters: a handle to the virtual machine, and a boolean value indicating if ACPI should be used. The above example could be modified to pause a virtual machine by replacing the line:
PRL_HANDLE hJob = PrlVm_Suspend(hVm);
with:
PRL_HANDLE hJob = PrlVm_Pause(hVm, PRL_FALSE);
64
Parallels C API by Example
It would also be necessary to change the error messages accordingly.
Resuming / Continuing a Virtual Machine
A suspended or paused virtual machine can be restarted using PrlVm_Start. Alternatively, PrlVm_Resume can be used to resume execution of a suspended virtual machine.
Dropping Suspended State
A suspended virtual machine can be shut down using PrlVm_DropSuspendedState. If this is used, any unsaved data will be lost.

Creating a New Virtual Machine

The first step in creating a new virtual machine is to create a blank virtual machine and register it with the Parallels Service. A blank virtual machine is the equivalent of a hardware box with no operating system installed on the hard drive. Once a blank virtual machine is created and registered, it can be powered on and an operating system can be installed on it.
In this section, we will discuss how to create a typical virtual machine for a particular OS type using a sample configuration. By using this approach, you can easily create a virtual machine without knowing all of the little details about configuring a virtual machine for a particular operating system type.
The steps involved in creating a typical virtual machine are:
1 Obtain a new handle of type PHT_VIRTUAL_MACHINE using the PrlSrv_CreateVm
function. The handle will identify our new virtual machine.
2 Obtain a handle of type PHT_VM_CONFIGURATION by calling the PrlVm_GetConfig
function. The handle is used for manipulating virtual machine configuration settings.
3 Set the default configuration parameters based on the version of the OS that you will later install
in the virtual machine. This step is performed using the PrlVmCfg_SetDefaultConfig function. You supply the version of the target OS, and the function will generate the appropriate configuration parameters automatically. The OS version parameter value is specified using predefined macros. The names of the macros are prefixed with PVS_GUEST_VER_. You can find the macro definitions in the C API Reference guide or in the PrlOses.h file. In addition to the OS information, the PrlVmCfg_SetDefaultConfig function allows to specify the physical host configuration which will be used to connect the virtual devices inside a virtual machine to their physical counterparts. The devices include floppy disk drive, CD drive, serial and parallel ports, sound card, etc. To connect the available host devices, obtain a handle of type PHT_SERVER_CONFIG (physical host configuration) using the PrlSrv_GetSrvConfig function. The handle should then be passed to PrlVmCfg_SetDefaultConfig together with OS information and other parameters. If you don't want to connect the devices, set the hSrvConfig parameter to PRL_INVALID_HANDLE.
65
Parallels C API by Example
4 Choose a name for the new virtual machine and set it using the PrlVmCfg_SetName function. 5 Modify some of the default configuration parameters if needed. For example, you may want to
modify the hard disk image type and size, the amount of memory available to the machine, and the networking options. You will have to obtain an appropriate handle for the type of the parameter that you would like to modify and call one of its functions to perform the modification. The code sample below shows how to modify some of the default values.
6 Create and register the new machine using the PrlVm_Reg function. This step will create the
necessary virtual machine files on the host and register the machine with the Parallels Service. The directory containing the virtual machine files will have the same name as the virtual machine name. The directory will be created in the default location for this Parallels Service. If you would like to create the virtual machine directory in a different location, you may specify the desired parent directory name and path.
The following sample demonstrates how to create a new virtual machine. The sample assumes that the client program has already obtained a server object handle (hServer) and performed the login operation.
PRL_HANDLE hVm = PRL_INVALID_HANDLE; PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE; PRL_HANDLE hResult = PRL_INVALID_HANDLE; PRL_RESULT nJobRetCode; PRL_RESULT ret;
// Obtain a new virtual machine handle. ret = PrlSrv_CreateVm(hServer, &hVm); if (PRL_FAILED(ret)) { // Error handling goes here... return ret; }
// Get the host config info. hJob = PrlSrv_GetSrvConfig(hServer); ret = PrlJob_Wait(hJob, 10000);
// Check the return code of PrlSrv_GetSrvConfig. PrlJob_GetRetCode(hJob, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode)); PrlHandle_Free(hJob); PrlHandle_Free(hVm); return nJobRetCode; }
// Get a handle to the object containing the result of PrlSrv_GetSrvConfig, // and then get a hosts configuration handle from it. ret = PrlJob_GetResult(hJob, &hResult); PRL_HANDLE hSrvCfg = PRL_INVALID_HANDLE; PrlResult_GetParam(hResult, &hSrvCfg);
// Free job and result handles. PrlHandle_Free(hJob); PrlHandle_Free(hResult);
// Now that we have the host configuration data,
66
Parallels C API by Example
// we can set the default configuration for the new virtual machine. ret = PrlVm_GetConfig(hVm, &hVmCfg); ret = PrlVmCfg_SetDefaultConfig( hVmCfg, // VM config handle. hSrvCfg, // Host config data. PVS_GUEST_VER_WIN_2003, // Target OS version. PRL_TRUE); // Create and connect devices.
if (PRL_FAILED(ret)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(ret)); PrlHandle_Free(hSrvCfg); PrlHandle_Free(hVmCfg); PrlHandle_Free(hVm); return ret; }
PrlHandle_Free(hSrvCfg);
// Set the virtual machine name. ret = PrlVmCfg_SetName(hVmCfg, "My Windows Server 2003");
// The following two calls demonstrate how to modify // some of the default values of the virtual machine configuration. // These calls are optional. You may remove them to use the default values. //
// Set RAM size for the machine to 256 MB. ret = PrlVmCfg_SetRamSize(hVmCfg, 256);
// Set virtual hard disk size to 20 GB. // First, get the handle to the hard disk object using the // PrlVmCfg_GetHardDisk function. The index of 0 is used // because the default configuration has just one virtual hard disk. // After that, use the handle to set the disk size. PRL_HANDLE hHDD = PRL_INVALID_HANDLE; ret = PrlVmCfg_GetHardDisk(hVmCfg, 0, &hHDD); ret = PrlVmDevHd_SetDiskSize(hHDD, 20000);
// Create and register the machine with the Parallels Service. // This is an asynchronous call. Returns a job handle. hJob = PrlVm_Reg(hVm, // VM handle. "", // VM root directory (using default). PRL_TRUE); // Using non-interactive mode.
// Wait for the operation to complete. ret = PrlJob_Wait(hJob, 10000);
// Check the return code of PrlVm_Reg. PrlJob_GetRetCode(hJob, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode)); PrlHandle_Free(hJob); PrlHandle_Free(hVmCfg); PrlHandle_Free(hVm); return nJobRetCode; }
// Delete handles. PrlHandle_Free(hJob);
67
Parallels C API by Example
PrlHandle_Free(hVmCfg); PrlHandle_Free(hVm);

Searching for Virtual Machines

A host computer may have virtual machines on its hard drive that are not currently registered with the Parallels Service. This can happen when a virtual machine is removed from the Parallels Service registry but its files are kept on the drive, or when a virtual machine files are manually copied to the drive from another computer. Parallels C API provides the PrlSrv_StartSearchVms function that can be used to find such virtual machines on the specified host at the specified location on the hard drive. The function accepts a string containing a directory name and path and searches the directory and all its subdirectories for unregistered virtual machines. It then returns a list of PHT_FOUND_VM_INFO handles, each containing information about an individual virtual machine that it finds. You can then decide whether you want to keep the machine as-is, register it, or remove it from the hard drive.
Since the search operation may take a long time (depending on the size of the specified directory tree), the PrlSrv_StartSearchVms function should be executed using the callback functionality (p. 19). The callback function will be called for every virtual machine found and a single instance of the PHT_FOUND_VM_INFO handle will be passed to it. As we discussed earlier in this guide (p. 19), a callback function can receive two types of objects: jobs (PHT_JOB) and events (PHT_EVENT). In this instance, the information is passed to the callback function as an event of type PET_DSP_EVT_FOUND_LOST_VM_CONFIG. To following steps are involved in processing the event inside the callback function:
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:
static PRL_RESULT callback(PRL_HANDLE hEvent, PRL_VOID_PTR pUserData) { PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_HANDLE_TYPE nHandleType;
PrlHandle_GetType(hEvent, &nHandleType);
// If this is a job, release the handle and exit. // Normally, we would process this, if we were after // a job. if (nHandleType == PHT_JOB) {
68
Parallels C API by Example
PrlHandle_Free(hEvent); return 0; }
// If it's not a job, then it is an event (PHT_EVENT). // Get the type of the event received. PRL_EVENT_TYPE eventType; PrlEvent_GetType(hEvent, &eventType);
// Check the event type. If it's what we are looking for, process it. if (eventType == PET_DSP_EVT_FOUND_LOST_VM_CONFIG) { PRL_UINT32 nParamsCount = 0;
// this will receive the event parameter handle. PRL_HANDLE hParam = PRL_INVALID_HANDLE;
// The PrlEvent_GetParam function obtains a handle of type // PHT_EVENT_PARAMETER. ret = PrlEvent_GetParam(hEvent, 0, &hParam); if (PRL_FAILED(ret)) { fprintf(stderr, "[4]%.8X: %s\n", ret, prl_result_to_string(ret)); PrlHandle_Free(hParam); PrlHandle_Free(hEvent); return ret; }
PRL_HANDLE hFoundVmInfo = PRL_INVALID_HANDLE; ret = PrlEvtPrm_ToHandle(hParam, &hFoundVmInfo); if (PRL_FAILED(ret)) { fprintf(stderr, "[9]%.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:
69
Parallels C API by Example
PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Register the event handler. PrlSrv_RegEventHandler(hServer, &callback, NULL);
// Create a string list object and populate it // with the name and path of the directory to search. PRL_HANDLE hStringList = PRL_INVALID_HANDLE; ret = PrlApi_CreateStringsList(&hStringList ); ret = PrlStrList_AddItem(hStringList, "/Users/Shared/Parallels/");
// Begin the search operation. hJob = PrlSrv_StartSearchVms(hServer, hStringList); PrlHandle_Free(hJob);
In order for the callback function to be called, your program should have a global loop (the program never exits on its own). The callback function will be called as soon as the first virtual machine is found. If there are no unregistered virtual machines in the specified directory tree, then the function will never be called as a PET_DSP_EVT_FOUND_LOST_VM_CONFIG event (it will still be called at least once as a result of the started job and will receive the job object but this and possibly other callback invocations are irrelevant in the context of this example).
Receiving the search results synchronously
It is also possible to use this function synchronously using the PrlJob_Wait function (p. 19). In this case, the information is returned as a list of PHT_FOUND_VM_INFO objects contained in the job object returned by the PrlSrv_StartSearchVms function. The following example demonstrates how to call the function and to process results synchronously.
PRL_RESULT SearchVMsSample(PRL_HANDLE hServer) { PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_HANDLE hJobResult = PRL_INVALID_HANDLE; PRL_HANDLE hFoundVmInfo = PRL_INVALID_HANDLE;
PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Create a string list object and populate it // with the name and path of the directory to search. PRL_HANDLE hStringList = PRL_INVALID_HANDLE; PrlApi_CreateStringsList(&hStringList ); PrlStrList_AddItem(hStringList, "/Users/Shared/Parallels/");
// Begin the search operation. hJob = PrlSrv_StartSearchVms(hServer, hStringList);
// Wait for the job to complete. ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Analyze the result of PrlSrv_StartSearchVms.
70
Parallels C API by Example
ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; } // Check the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... PrlHandle_Free(hJob); return -1; }
// Get job result. ret = PrlJob_GetResult(hJob, &hJobResult); PrlHandle_Free(hJob); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Iterate through the returned list obtaining a // handle of type PHT_FOUND_VM_INFO in each iteration containing // the information about an individual virtual machine. PRL_UINT32 nIndex, nCount; PrlResult_GetParamsCount(hJobResult, &nCount); for(nIndex = 0; nIndex < nCount ; nIndex++) { PrlResult_GetParamByIndex(hJobResult, nIndex, &hFoundVmInfo);
// Get the virtual machine name. PRL_CHAR sName[1024]; PRL_UINT32 nBufSize = sizeof(sName); ret = PrlFoundVmInfo_GetName(hFoundVmInfo, sName, &nBufSize); printf("VM name: %s\n", sName);
// Get the name and path of the virtual machine directory. PRL_CHAR sPath[1024]; nBufSize = sizeof(sPath); ret = PrlFoundVmInfo_GetConfigPath(hFoundVmInfo, sPath, &nBufSize); printf("Path: %s\n\n", sPath);
PrlHandle_Free(hFoundVmInfo); } PrlHandle_Free(hJobResult); PrlHandle_Free(hStringList); }
71
Parallels C API by Example

Adding an Existing Virtual Machine

A host may have virtual machines that are not registered with the Parallels Service. This can happen if a virtual machine was previously removed from the Parallels Service registry or if the virtual machine files were manually copied from a different location. If you know the location of such a virtual machine, you can easily register it with the Parallels Service using the PrlSrv_RegisterVm function. The function accepts a server handle, name and path of the directory containing the virtual machine files, and registers the machine.
Note: The PrlSrv_RegisterVm function does NOT generate new MAC addresses for the virtual network adapters that already exist in the virtual machine. If the machine is a copy of another virtual machine then you should set new MAC addresses for its network adapters after you register it. The example at the end of this section demonstrates how this can be accomplished. For more information on modifying an existing virtual machine, please see the Modifying Virtual Machine Configuration section (p. 78).
The following sample function demonstrates how to add an existing virtual machine to the Parallels Service. The function takes a handle of type PHT_SERVER and a string specifying the name and path of the virtual machine directory. It registers the virtual machine with the Service and then modifies the MAC address of every virtual network adapter installed in it.
PRL_RESULT RegisterExistingVM(PRL_HANDLE hServer, PRL_CONST_STR sVmDirectory) { PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobRetCode = PRL_ERR_UNINITIALIZED;
// Register the virtual machine. hJob = PrlSrv_RegisterVm( hServer, sVmDirectory, PRL_TRUE); // Using non-interactive mode.
ret = PrlJob_Wait(hJob, 10000);
// Check the return code of PrlSrv_RegisterVm. PrlJob_GetRetCode(hJob, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { printf("PrlSrv_RegisterVm returned error: %s\n", prl_result_to_string(nJobRetCode)); PrlHandle_Free(hJob); return -1; }
// Obtain the virtual machine handle from the job object. // We will use the handle later to modify the virtual machine // configuration. PRL_HANDLE hVm = PRL_INVALID_HANDLE; ret = PrlJob_GetResult(hJob, &hJobResult); if (PRL_FAILED(ret)) { // Handle the error...
72
Parallels C API by Example
return ret; }
ret = PrlResult_GetParam(hJobResult, &hVm); if (PRL_FAILED(ret)) { // Handle the error... return ret; }
PrlHandle_Free(hJob); PrlHandle_Free(hJobResult);
printf("Virtual machine '%s' was successfully registered.", sVmDirectory);
// The following code will generate and set a new MAC address // for every virtual network adapter that exists in the virtual machine. // This step is optional and should normally be performed when the virtual // machine is a copy of another virtual machine.
PRL_HANDLE hJobBeginEdit = PRL_INVALID_HANDLE; PRL_HANDLE hJobCommit = PRL_INVALID_HANDLE;
// Begin the virtual machine editing operation. hJobBeginEdit = PrlVm_BeginEdit(hVm); ret = PrlJob_Wait(hJobBeginEdit, 10000); PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode)); PrlHandle_Free(hJobBeginEdit); return nJobRetCode; }
// Obtain a handle of type PHT_VM_CONFIGURATION containing the // virtual machine configuration data. PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE; ret = PrlVm_GetConfig(hVm, &hVmCfg); if (PRL_FAILED(ret)) { // Handle the error... return ret; }
// Determine the number of the network adapters // installed in the machine. PRL_UINT32 nCount = PRL_ERR_UNINITIALIZED; ret = PrlVmCfg_GetNetAdaptersCount(hVmCfg, &nCount); if (PRL_FAILED(ret)) { // Handle the error... return ret; }
// Itereate through the adapter list. PRL_HANDLE hNetAdapter = PRL_INVALID_HANDLE; for (PRL_UINT32 i = 0; i < nCount; ++i) { ret = PrlVmCfg_GetNetAdapter(hVmCfg, i, &hNetAdapter); if (PRL_FAILED(ret))
73
Parallels C API by Example
{ // Handle the error... return ret; }
// Automatically generate new MAC address for the current adapter. // The address will be updated in the configuration object. ret = PrlVmDevNet_GenerateMacAddr(hNetAdapter); if (PRL_FAILED(ret)) { // Handle the error... return ret; } }
// Commit the changes to the virtual machine. hJobCommit = PrlVm_Commit(hVm);
// Check the results of the commit operation. ret = PrlJob_Wait(hJobCommit, 10000); PrlJob_GetRetCode(hJobCommit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "Commit error: %s\n", prl_result_to_string(nJobRetCode)); PrlHandle_Free(hJobCommit); return nJobRetCode; }
PrlHandle_Free(hNetAdapter); PrlHandle_Free(hVm); PrlHandle_Free(hVmCfg); PrlHandle_Free(hJobCommit); PrlHandle_Free(hJobBeginEdit);
return 0; }

Cloning a Virtual Machine

A new virtual machine can also be created by cloning an existing virtual machine. The machine will be created as an exact copy of the source virtual machine and will be automatically registered with the Parallels Service. The cloning operation is performed using the PrlVm_Clone function. The following parameters must be specified when cloning a virtual machine:
1 A valid handle of type PHT_VIRTUAL_MACHINE containing information about the source virtual
machine.
2 A unique name for the new virtual machine (the name is NOT generated automatically). 3 The name of the directory where the virtual machine files should be created (or an empty string
to create the files in the default directory).
4 A boolean value specifying whether to create the new machine as a valid virtual machine or as a
template. PRL_TRUE indicates to create a template. PRL_FALSE indicates to create a virtual machine. See the Working with Virtual Machine Templates (p. 93) section for more virtual machine info and examples.
74
Parallels C API by Example
The source virtual machine must be registered with the Parallels Service before it can be cloned.
The following sample function demonstrates how to clone an existing virtual machine. When testing a function, the hVm parameter must contain a valid handle of type PHT_VIRTUAL_MACHINE (the source virtual machine to clone). On completion, the new virtual machine should appear in the list of registered virtual machines.
PRL_RESULT CloneVmSample(PRL_HANDLE hVm) { PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED; PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
// Declare and populate variables that // will be used as input parameters // in the function that clones a VM.
// Virtual machine name. // Get the name of the original VM and use // it in the new virtual machine name. You can // use any name that you like of course. char vm_name[1024]; PRL_UINT32 nBufSize = sizeof(vm_name); PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE; ret = PrlVm_GetConfig(hVm, &hVmCfg); ret = PrlVmCfg_GetName(hVmCfg, vm_name, &nBufSize); char new_vm_name[1024] = "Clone of "; strcat(new_vm_name, vm_name);
// Name of the target directory on the // host. // Empty string indicates that the default // directory should be used. PRL_CHAR_PTR new_vm_root_path = "";
// Virtual machine or template? // The cloning functionality allows to create // a new virtual machine or a new template. // True indicates to create a template. // False indicates to create a virtual machine. // We are creating a virtual machine. PRL_BOOL bCreateTemplate = PRL_FALSE;
// Begin the cloning operation. hJob = PrlVm_Clone(hVm, new_vm_name, new_vm_root_path, bCreateTemplate); // Wait for the job to complete. ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { // Handle the error... printf("Error: (%s)\n", prl_result_to_string(ret)); PrlHandle_Free(hJob); PrlHandle_Free(hVmCfg); return -1; }
// Analyze the result of PrlVm_Clone. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret))
75
Parallels C API by Example
{ // Handle the error... PrlHandle_Free(hJob); PrlHandle_Free(hVmCfg); return -1; } // Check the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... printf("Error: (%s)\n", prl_result_to_string(nJobReturnCode)); PrlHandle_Free(hJob); PrlHandle_Free(hVmCfg); return -1; } PrlHandle_Free(hJob); PrlHandle_Free(hVmCfg); return 0; }

Deleting a Virtual Machine

If a virtual machine is no longer needed, it can be removed. There are two options for removing a virtual machine:
1 Un-register the virtual machine using PrlVm_Unreg. This will remove the virtual machine from
the list of the virtual machines registered with the Service. Once a virtual machine has been unregistered it is not possible to use it. The directory containing the virtual machine files will remain on the hard drive of the host computer, and the virtual machine can later be 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",
76
Parallels C API by Example
prl_result_to_string(ret)); PrlHandle_Free(hVm); PrlHandle_Free(hJob); return -1; }
PrlJob_GetRetCode(hJob, &nJobResult); if (PRL_FAILED(nJobResult)) { printf("PrlVm_Unreg failed. Error returned: %s\n", prl_result_to_string(nJobResult)); PrlHandle_Free(hVm); PrlHandle_Free(hJob); return -1; }
The following example demonstrates deleting a virtual machine and deleting config.pvs within the virtual machine directory:
// Delete a virtual machine and a specified file. PRL_HANDLE hDeviceList = PRL_INVALID_HANDLE; PrlApi_CreateStringsList(&hDeviceList); PrlStrList_AddItem(hDeviceList, "/Users/Shared/Parallels/WinXP02/config.pvs"); hJob = PrlVm_Delete(hVm, hDeviceList); PrlHandle_Free(hDeviceList); 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); PrlHandle_Free(hServer); PrlApi_Deinit(); SdkWrap_Unload(); return -1; }
PrlJob_GetRetCode(hJob, &nJobResult); if (PRL_FAILED(nJobResult)) { printf("PrlVm_Delete failed. Error returned: %s\n", prl_result_to_string(nJobResult)); PrlHandle_Free(hVm); PrlHandle_Free(hJob); PrlHandle_Free(hServer); PrlApi_Deinit(); SdkWrap_Unload(); return -1; }
To delete the virtual machine and the virtual machine directory (all files belonging to the virtual machine), omit the line:
PrlStrList_AddItem(hDeviceList, "/Users/Shared/Parallels/WinXP02/config.pvs");
from the above example. Note that this operation is irreversible.
77
Parallels C API by Example

Modifying Virtual Machine Configuration

The Parallels C API provides a complete set of functions to modify the configuration parameters of an existing virtual machine. You can find the list of the available functions in the Parallels C API Reference guide by looking at the PHT_VM_CONFIGURATION group. Most of the get/set functions in the group allow to obtain and modify the virtual machine configuration parameters. Some parameters are handled as objects and require extra steps in getting or setting them. The following subsections describe how to modify the most common configuration parameters and provide code samples. The samples assume that:
you've already obtained a handle to the server object and logged on to the Parallels Service.
you've already obtained a handle to the virtual machine that you would like to modify.
Note: All operations on virtual machine devices (adding, modifying, removing) must be performed on a stopped virtual machine. An attempt to modify the device configuration on a running machine will result in error.
PrlVm_BeginEdit and PrlVm_Commit Functions
All virtual machine configuration changes must begin with the PrlVm_BeginEdit and end with the PrlVm_Commit call. These two functions are used to detect collisions with other clients trying to modify the configuration settings of the same virtual machine.
When PrlVm_BeginEdit is called, the Parallels Service timestamps the beginning of a configuration change(s) operation. It does not lock the machine, so other clients can make changes to the same virtual machine at the same time. The function will also automatically update your local virtual machine object with the current virtual machine configuration information. This is done in order to ensure that your local object contains the changes that might have have happened since you obtained the virtual machine handle.
When you are done making the changes, you must call the PrlVm_Commit function. The first thing that the function will do is verify that the virtual machine configuration has not been modified by other client(s) since you called the PrlVm_BeginEdit function. If it has been, your changes will be rejected and PrlVm_Commit will return with error. In such a case, you will have to reapply your changes. In order to do that, you will have to get the latest configuration using the PrlVm_GetConfig function, compare your changes with the latest changes, and make a decision about merging them. Please note that PrlVm_GetConfig function will update the configuration data in your current virtual machine object and will overwrite all existing data, including the changes that you've made to it. Furthermore, the PrlVm_BeginEdit function will also overwrite all existing data (see above). If you don't want to loose your data, save it locally before calling PrlVm_GetConfig or PrlVm_BeginEdit.
The following example demonstrates how to use the PrlVm_BeginEdit and PrlVm_Commit functions:
PRL_HANDLE hJobBeginEdit = PRL_INVALID_HANDLE; PRL_HANDLE hJobCommit = PRL_INVALID_HANDLE;
78
Parallels C API by Example
PRL_RESULT nJobRetCode = PRL_INVALID_HANDLE;
// Timestamps the beginning of the "transaction". // Updates the hVm object with current configuration data. hJobBeginEdit = PrlVm_BeginEdit(hVm); ret = PrlJob_Wait(hJobBeginEdit, 10000); PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode)); PrlHandle_Free(hJobBeginEdit); return nJobRetCode; }
// The code modifying configuration parameters goes here...
// Commits the changes to the virtual machine. hJobCommit = PrlVm_Commit(hVm);
// Check the results of the commit operation. ret = PrlJob_Wait(hJobCommit, 10000); PrlJob_GetRetCode(hJobCommit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "Commit error: %s\n", prl_result_to_string(nJobRetCode)); PrlHandle_Free(hJobCommit); return nJobRetCode; }
Obtaining a PHT_VM_CONFIGURATION handle
Before you can use any of the virtual machine configuration management functions, you have to obtain a handle of type PHT_VM_CONFIGURATION. The handle is obtained from the virtual machine object as shown in the following example:
PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE; ret = PrlVm_GetConfig(hVm, &hVmCfg);
Once you have the handle, you can use its functions to manipulate the virtual machine configuration settings. As usual, don't forget to free the handle when it is no longer needed.
Name, Description, Boot Options
The virtual machine name and description modifications are simple. They are performed using a single call for each parameter:
// Modify VM name. ret = PrlVm_GetConfig(hVm, &hVmCfg); ret = PrlVmCfg_SetName(hVmCfg, "New Name1");
// Modify VM description. ret = PrlVmCfg_SetDescription(hVmCfg, "My updated VM");
To modify the boot options (boot device priority), first make the PrlVmCfg_GetBootDevCount call to determine the number of the available devices. Then obtain a handle to each device by making the PrlVmCfg_GetBootDev call in a loop. To place a device at the specified position in the boot device priority list, use the PrlBootDev_SetSequenceIndex function passing the device handle and the index (0 - first boot device, 1 - second boot device, and so forth).
79
Parallels C API by Example
The following sample illustrates how to make the above modifications.
PRL_HANDLE hJobBeginEdit = PRL_INVALID_HANDLE; PRL_HANDLE hJobCommit = PRL_INVALID_HANDLE; PRL_RESULT nJobRetCode = PRL_INVALID_HANDLE;
// Timestamp the beginning of the transaction. hJobBeginEdit = PrlVm_BeginEdit(hVm); ret = PrlJob_Wait(hJobBeginEdit, 10000); PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode)); PrlHandle_Free(hJobBeginEdit); return nJobRetCode; }
// Modify VM name. ret = PrlVmCfg_SetName(hVmCfg, "New Name1");
// Modify VM description. ret = PrlVmCfg_SetDescription(hVmCfg, "My updated VM");
// Modify boot options. // Set boot device list as follows: // 0. CD/DVD drive. // 1. Hard disk. // 2. Network adapter. // 3. Floppy disk drive. // Remove all other devices (if any) from the // boot devices list for this VM.
// Device count. PRL_UINT32 nDevCount;
// A handle identifying the device. PRL_HANDLE hDevice = PRL_INVALID_HANDLE;
// Device type. PRL_DEVICE_TYPE devType;
// Get the total number of devices. ret = PrlVmCfg_GetBootDevCount(hVmCfg, &nDevCount);
// Iterate through the device list. // Get a handle for each available device. // Set an index for a device in the boot list. for (int i = 0; i < nDevCount; ++i) { ret = PrlVmCfg_GetBootDev(hVmCfg, i, &hDevice); ret = PrlBootDev_GetType(hDevice, &devType);
if (devType == PDE_OPTICAL_DISK) { PrlBootDev_SetSequenceIndex(hDevice, 0); } if (devType == PDE_HARD_DISK) { PrlBootDev_SetSequenceIndex(hDevice, 1); } else if (devType == PDE_GENERIC_NETWORK_ADAPTER)
80
Parallels C API by Example
{ 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);
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:
PRL_HANDLE hHDD = PRL_INVALID_HANDLE; PRL_UINT32 nCount;
// Get the number of disks available. PrlVmCfg_GetHardDisksCount(hVmCfg, &nCount);
// Iterate through the list. for (PRL_UINT32 i = 0; i < nCount; ++i) { // Obtain a handle to the hard disk object. ret = PrlVmCfg_GetHardDisk(hVmCfg, i, &hHDD);
// The code selecting the desired HDD goes here... // { // Modify the disk size. // The hard disk size is specified in megabytes. ret = PrlVmDevHd_SetDiskSize(hHDD, 20000); // }
81
Parallels C API by Example
}
Adding a new hard disk
In this example, we will add a hard disk to a virtual machine. The following options are available:
You may create new or use an existing image file for your new disk.
Creating a dynamically expanding or a fixed-size disk. The expanding drive image will be initially
created with a size of zero. The space for it will be allocated dynamically on as-needed basis. The space for the fixed-size disk will be allocated fully at the time of creation.
Choosing the maximum disk size.
Creating a new image file
In the first example, we will create a new disk image and will add it to a virtual machine.
PRL_HANDLE hJobBeginEdit = PRL_INVALID_HANDLE; PRL_HANDLE hJobCommit = PRL_INVALID_HANDLE; PRL_RESULT nJobRetCode = PRL_INVALID_HANDLE;
// Timestamp the beginning of the configuration changes operation. // The hVm specifies the virtual machine that we'll be editing. // hJobBeginEdit = PrlVm_BeginEdit(hVm); ret = PrlJob_Wait(hJobBeginEdit, 10000); PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode)); PrlHandle_Free(hJobBeginEdit); return nJobRetCode; }
// Create a new device handle. // This will be our new virtual hard disk. PRL_HANDLE hHDD = PRL_INVALID_HANDLE; ret = PrlVmCfg_CreateVmDev( hVmCfg, // The target virtual machine. PHT_VIRTUAL_DEV_HARD_DISK, // Device type. &hHDD); // Device handle.
// Set disk type to "expanding". ret = PrlVmDevHd_SetDiskType(hHDD, PHD_EXPANDING_HARD_DISK);
// Set max disk size, in megabytes. ret = PrlVmDevHd_SetDiskSize(hHDD, 32000);
// This option determines whether the image file will be splitted // into chunks or created as a single file. ret = PrlVmDevHd_SetSplitted(hHDD, PRL_FALSE);
// Choose and set the name for the new image file. // We must set both the "friendly" name and the "system" name. // For a virtual device, use the name of the new image file in both // functions. By default, the file will be // created in the virtual machine directory. You may specify a // full path if you want to place the file in a different // directory.
82
Parallels C API by Example
// ret = PrlVmDev_SetFriendlyName(hHDD, "harddisk4.hdd"); ret = PrlVmDev_SetSysName(hHDD, "harddisk4.hdd");
// Set the emulation type. ret = PrlVmDev_SetEmulatedType(hHDD, PDT_USE_IMAGE_FILE);
// Enable the new disk on successful creation. ret = PrlVmDev_SetEnabled(hHDD, PRL_TRUE);
// Create the new image file. hJob = PrlVmDev_CreateImage(hHDD, PRL_TRUE, // Do not overwrite if the file exists. PRL_TRUE); // Use non-interactive mode.
// Commit the changes. hJobCommit = PrlVm_Commit(hVm);
// Check the results of the commit operation. ret = PrlJob_Wait(hJobCommit, 10000); PrlJob_GetRetCode(hJobCommit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "Commit error: %s\n", prl_result_to_string(nJobRetCode)); PrlHandle_Free(hJobCommit); return nJobRetCode; }
Using an existing image file
In the next example, we will use an existing image file to add a virtual hard disk to a virtual machine. The procedure is similar to the one described above, except that you don't have to specify the disk parameters and you don't have to create an image file.
// Timestamp the beginning of the configuration changes operation. // The hVm specifies the virtual machine that we'll be editing. // hJobBeginEdit = PrlVm_BeginEdit(hVm); ret = PrlJob_Wait(hJobBeginEdit, 10000); PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode)); PrlHandle_Free(hJobBeginEdit); return nJobRetCode; }
// Create a device handle. PRL_HANDLE hHDD = PRL_INVALID_HANDLE; ret = PrlVmCfg_CreateVmDev( hVmCfg, // Target virtual machine. PHT_VIRTUAL_DEV_HARD_DISK, // Device type. &hHDD); // Device handle.
// In this example, these two functions are used // to specify the name of the existing image file. // By default, it will look for the file in the // virtual machine directory. If the file is located // anywhere else, you must specify the full path here. // ret = PrlVmDev_SetFriendlyName(hHDD, "harddisk4.hdd");
83
Parallels C API by Example
ret = PrlVmDev_SetSysName(hHDD, "harddisk4.hdd");
// Set the emulation type. ret = PrlVmDev_SetEmulatedType(hHDD, PDT_USE_IMAGE_FILE);
// Enable the drive on completion. ret = PrlVmDev_SetEnabled(hHDD, PRL_TRUE);
// Commit the changes. hJobCommit = PrlVm_Commit(hVm);
If the commit operation is successful, a hard disk will be added to the virtual machine and will appear in the list of the available devices.
Network Adapters
When adding a network adapter to a virtual machine, you first have to choose a networking mode for it. The following options are available:
Host-only networking. A virtual machine can communicate with the host and other virtual
machines, but not with external networks.
Shared networking. Uses the NAT feature. A virtual machine shares the IP address with the
host.
Bridged networking. A virtual adapter in the VM is bound to a network adapter on the host. The
virtual machine appears as a standalone computer on the network.
Host-only and Shared Networking
The following sample function illustrates how to add virtual network adapters using the host-only and shared networking (both types are created similarly). The steps are:
1 Call the PrlVm_BeginEdit function to mark the beginning of the virtual machine editing
operation. This step is required when modifying any of the virtual machine configuration parameters.
2 Obtain a handle of type PHT_VM_CONFIGURATION containing the virtual machine
configuration information.
3 Create a new virtual device handle of type PHT_VIRTUAL_DEV_NET_ADAPTER (virtual
network adapter) using the PrlVmCfg_CreateVmDev function.
4 Set the desired device emulation type (host or shared) using the
PrlVmDev_SetEmulatedType function. Virtual network adapter emulation types are defined in the PRL_VM_DEV_EMULATION_TYPE enumeration.
5 The MAC address for the adapter will be generated automatically. If needed, you can set the
address manually using the PrlVmDevNet_SetMacAddress function.
6 Call the PrlVm_Commit function to finalize the changes.
PRL_RESULT AddNetAdapterHostOrShared(PRL_HANDLE hVm) { PRL_HANDLE hJobBeginEdit = PRL_INVALID_HANDLE; PRL_HANDLE hJobCommit = PRL_INVALID_HANDLE;
84
Parallels C API by Example
PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE; PRL_RESULT nJobRetCode = PRL_INVALID_HANDLE; PRL_UINT32 ret = PRL_ERR_UNINITIALIZED;
// Timestamp the beginning of the configuration changes operation. // The hVm parameter specifies the target virtual machine. hJobBeginEdit = PrlVm_BeginEdit(hVm); ret = PrlJob_Wait(hJobBeginEdit, 10000); PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode)); PrlHandle_Free(hJobBeginEdit); return nJobRetCode; }
// Obtain a handle of type PHT_VM_CONFIGURATION containing // the virtual machine configuration information. ret = PrlVm_GetConfig(hVm, &hVmCfg); if (PRL_FAILED(ret)) { // Handle the error. }
// Create a virtual network adapter device handle. PRL_HANDLE hNet = PRL_INVALID_HANDLE; ret = PrlVmCfg_CreateVmDev( hVmCfg, // The virtual machine configuration handle. PDE_GENERIC_NETWORK_ADAPTER, // Device type. &hNet); // Device handle.
if (PRL_FAILED(ret)) { // Handle the error. }
// For host-only networking, set the device emulation type // to PDT_USE_HOST_ONLY_NETWORK, which is an enumerator from the // PRL_VM_DEV_EMULATION_TYPE enumeration. // For shared networking, set the device emulation type // to PDT_USE_SHARED_NETWORK, which is also an enumerator from // the same enumeration. // Un-comment one of the following lines (and comment out the other) // to set the the desired emulation type. PRL_VM_DEV_EMULATION_TYPE pdtType = PDT_USE_HOST_ONLY_NETWORK; //PRL_VM_DEV_EMULATION_TYPE pdtType = PDT_USE_SHARED_NETWORK;
ret = PrlVmDev_SetEmulatedType(hNet, pdtType); if (PRL_FAILED(ret)) { // Handle the error. }
// By default, a new device is created disabled. // You can set the "connected" and "enabled" properties // as desired. PrlVmDev_SetConnected(hNet, PRL_TRUE); PrlVmDev_SetEnabled(hNet, PRL_TRUE);
// Commit the changes. hJobCommit = PrlVm_Commit(hVm);
85
Parallels C API by Example
ret = PrlJob_Wait(hJobBeginEdit, 10000); PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { // Handle the error. }
// Release all handles. PrlHandle_Free(hNet); PrlHandle_Free(hVmCfg); PrlHandle_Free(hJobBeginEdit); PrlHandle_Free(hJobCommit);
return PRL_ERR_SUCCESS; }
Bridged Networking
Compared to host-only and shared network adapters, adding an adapter using bridged networking involves additional steps. In a bridged networking mode, you are binding the virtual adapter inside a virtual machine to an adapter on the host machine. Therefore, you first have to retrieve the list of adapters from the host and select the one you would like to use. The complete procedure of creating an adapter using bridged networking is as follows:
1 Obtain a list of network adapters installed on the host. This steps is preformed using the
PrlSrvCfg_GetNetAdaptersCount, PrlSrvCfg_GetNetAdapter, and PrlSrvCfgDev_GetName functions.
2 Begin the virtual machine editing operation and create a new network adapter handle as
described in the Host-only and Shared Networking section (p. 84).
3 Bind the new virtual network adapter to the desired host machine adapter using the
PrlVmDevNet_SetBoundAdapterName function.
4 Finalize the changes by calling the PrlVm_Commit function.
You can also bind a virtual network adapter to the default adapter on the host machine. In this case, you don't have to obtain the list of adapters from the host, so you can skip step 1 (above). In step 3, instead of setting the adapter name, set its index as -1 using the PrlVmDevNet_SetBoundAdapterIndex function.
The following are two sample functions that show the implementation of the steps described above. The two functions are similar except that the first one shows how to bind a virtual network adapter to a specific adapter on the host, whereas the second function shows how to bind the virtual adapter to the default host network adapter.
Example 1:
The function accepts a server handle and a virtual machine handle. The server handle will be used to obtain the list of network adapters from the host.
PRL_RESULT AddNetAdapterBridged(PRL_HANDLE hServer, PRL_HANDLE hVm) { PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
86
Parallels C API by Example
PRL_HANDLE hJobBeginEdit = PRL_INVALID_HANDLE; PRL_HANDLE hJobCommit = PRL_INVALID_HANDLE; PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE; PRL_RESULT nJobRetCode = PRL_INVALID_HANDLE; PRL_UINT32 ret = PRL_ERR_UNINITIALIZED;
// Obtain a list of the network adapters installed on // the host. // First, obtain a handle containing the // host configuration info. hJob = PrlSrv_GetSrvConfig(hServer); ret = PrlJob_Wait(hJob, 10000);
PrlJob_GetRetCode(hJob, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { // Handle the error. }
// Get job results. ret = PrlJob_GetResult(hJob, &hJobResult); if (PRL_FAILED(ret)) { // Handle the error. }
// server config handle. PRL_HANDLE hSrvCfg = PRL_INVALID_HANDLE;
// counter. PRL_UINT32 nCount = PRL_INVALID_HANDLE;
// Now obtain the actual handle containing the // host configuration info. PrlResult_GetParam(hJobResult, &hSrvCfg);
// Get the number of the available adapters from the // host configuration object. PrlSrvCfg_GetNetAdaptersCount(hSrvCfg, &nCount);
// Net adapter handle. PRL_HANDLE hHostNetAdapter = PRL_INVALID_HANDLE; PRL_CHAR chHostAdapterName[1024];
// Iterate through the list of the adapters. for (PRL_UINT32 i = 0; i < nCount; ++i) { PrlSrvCfg_GetNetAdapter(hSrvCfg, i, &hHostNetAdapter);
// Get adapter name. PRL_CHAR chName[1024]; PRL_UINT32 nBufSize = sizeof(chName); ret = PrlSrvCfgDev_GetName(hHostNetAdapter, chName, &nBufSize);
// Normally, you would iterate through the entire list // and select an adapter to bind the virtual network adapter to. // For simplicity, we will simply pick the first one and use it. strcpy(chHostAdapterName, chName); break;
}
87
Parallels C API by Example
// Now that we have the name of the host network adapter, // we can add a new virtual network adapter to the virtual machine.
// Timestamp the beginning of the configuration changes operation. // The hVm parameter specifies the target virtual machine. hJobBeginEdit = PrlVm_BeginEdit(hVm); ret = PrlJob_Wait(hJobBeginEdit, 10000); PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode)); PrlHandle_Free(hJobBeginEdit); return nJobRetCode; }
// Obtain a handle of type PHT_VM_CONFIGURATION containing // the virtual machine configuration information. ret = PrlVm_GetConfig(hVm, &hVmCfg); if (PRL_FAILED(ret)) { // Handle the error. }
// Create a virtual network adapter device handle. PRL_HANDLE hNet = PRL_INVALID_HANDLE; ret = PrlVmCfg_CreateVmDev( hVmCfg, // The virtual machine configuration handle. PDE_GENERIC_NETWORK_ADAPTER, // Device type. &hNet); // Device handle.
if (PRL_FAILED(ret)) { // Handle the error. }
// Set the virtual network adapter emulation type (networking type). // Bridged networking is set using the PDT_USE_BRIDGE_ETHERNET enumerator // from the PRL_VM_DEV_EMULATION_TYPE enumeration. ret = PrlVmDev_SetEmulatedType(hNet, PDT_USE_BRIDGE_ETHERNET); if (PRL_FAILED(ret)) { // Handle the error. }
// Set the host adapter to which this adapter should be bound. PrlVmDevNet_SetBoundAdapterName(hNet, chHostAdapterName);
// By default, a new device is created disabled. // You can set the "connected" and "enabled" properties // as desired. PrlVmDev_SetConnected(hNet, PRL_TRUE); PrlVmDev_SetEnabled(hNet, PRL_TRUE);
// Commit the changes. hJobCommit = PrlVm_Commit(hVm); ret = PrlJob_Wait(hJobBeginEdit, 10000); PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) {
88
Parallels C API by Example
// Handle the error. }
// Release all handles. PrlHandle_Free(hNet); PrlHandle_Free(hVmCfg); PrlHandle_Free(hJobBeginEdit); PrlHandle_Free(hJobCommit);
return PRL_ERR_SUCCESS; }
Example 2:
This function shows how to add a virtual network adapter to a virtual machine and how to bind it to the default adapter on the host.
PRL_RESULT AddNetAdapterBridgedDefault(PRL_HANDLE hVm) { PRL_HANDLE hJobBeginEdit = PRL_INVALID_HANDLE; PRL_HANDLE hJobCommit = PRL_INVALID_HANDLE; PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE; PRL_RESULT nJobRetCode = PRL_INVALID_HANDLE; PRL_UINT32 ret = PRL_ERR_UNINITIALIZED;
// Timestamp the beginning of the configuration changes operation. // The hVm parameter specifies the target virtual machine. hJobBeginEdit = PrlVm_BeginEdit(hVm); ret = PrlJob_Wait(hJobBeginEdit, 10000); PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { fprintf(stderr, "Error: %s\n", prl_result_to_string(nJobRetCode)); PrlHandle_Free(hJobBeginEdit); return nJobRetCode; }
// Obtain a handle of type PHT_VM_CONFIGURATION containing // the virtual machine configuration information. ret = PrlVm_GetConfig(hVm, &hVmCfg); if (PRL_FAILED(ret)) { // Handle the error. }
// Create a virtual network adapter device handle. PRL_HANDLE hNet = PRL_INVALID_HANDLE; ret = PrlVmCfg_CreateVmDev( hVmCfg, // The virtual machine configuration handle. PDE_GENERIC_NETWORK_ADAPTER, // Device type. &hNet); // Device handle.
if (PRL_FAILED(ret)) { // Handle the error. }
// Set the virtual network adapter emulation type (networking type). // Bridged networking is set using the PDT_USE_BRIDGE_ETHERNET enumerator // from the PRL_VM_DEV_EMULATION_TYPE enumeration. ret = PrlVmDev_SetEmulatedType(hNet, PDT_USE_BRIDGE_ETHERNET );
89
Parallels C API by Example
if (PRL_FAILED(ret)) { // Handle the error. }
// Set the host adapter index to -1. This will // bind the virtual adapter to the default adapter on the // host. PrlVmDevNet_SetBoundAdapterIndex(hNet, -1);
// By default, a new device is created disabled. // You can set the "connected" and "enabled" properties // as desired. PrlVmDev_SetConnected(hNet, PRL_TRUE); PrlVmDev_SetEnabled(hNet, PRL_TRUE);
// Commit the changes. hJobCommit = PrlVm_Commit(hVm); ret = PrlJob_Wait(hJobBeginEdit, 10000); PrlJob_GetRetCode(hJobBeginEdit, &nJobRetCode); if (PRL_FAILED(nJobRetCode)) { // Handle the error. }
// Release all handles. PrlHandle_Free(hNet); PrlHandle_Free(hVmCfg); PrlHandle_Free(hJobBeginEdit); PrlHandle_Free(hJobCommit);
return PRL_ERR_SUCCESS; }

Managing User Access Rights

This topic applies to Parallels Server only,
User authorization (determining user access rights) is performed using OS-level file access permissions. Essentially, a virtual machine is a set of files that a user can read, write, and execute. When determining access rights of a user for a particular virtual machine, Parallels Service looks at the rights the user has on the virtual machine files and uses this information to allow or deny privileges. The Parallels Server Administration Guide has a section that describes the Parallels Server tasks in relation to the file access rights. Using this information, you can determine the tasks that a user is allowed to perform based on the file access rights the user has. The same goal can also be accomplished programmatically through Parallels C API.
The Parallels C API contains a PHT_ACCESS_RIGHTS object that is used to manage user access rights. A handle to it is obtained using the PrlVmCfg_GetAccessRights or the
PrlVmInfo_GetAccessRights function. The difference between the two function is that PrlVmInfo_GetAccessRights takes an additional step: obtaining a handle of type PHT_VM_INFO which will also contain the virtual machine state information. If user access rights is
all you need, you can use the PrlVmCfg_GetAccessRights function.
90
Parallels C API by Example
The PHT_ACCESS_RIGHTS object provides an easy way of determining access rights for the currently logged in user with the PrlAcl_IsAllowed function. The function allows to specify one of the available virtual machine tasks (defined in the PRL_ALLOWED_VM_COMMAND enumeration) and returns a boolean value indicating whether the user is allowed to perform the task or not. The security is enforced on the server side, so if a user tries to perform a tasks that he/she is not authorized to perform, the access will be denied. You can still use the functionality described here to determine user access rights in advance and use it in accordance with your client application logic.
An administrator of the host has full access rights to all virtual machines. A non-administrative user has full rights to the machines created by him/her and no rights to any other virtual machines by default (these machines will not even be included in the result set when the user requests a list of virtual machines from the host). The host administrator can grant virtual machine access privileges to other users when needed. Currently, the privileges can be granted to all existing users only. It is not possible to set access rights for an individual user through the API. The
PrlAcl_SetAccessForOthers function is used to set access rights. The function takes the PHT_ACCESS_RIGHTS object identifying the virtual machine and one of the enumerators from the PRL_VM_ACCESS_FOR_OTHERS enumerations identifying the access level, which includes view,
view and run, full access, and no access. Once again, the function sets access rights for all existing users (the users currently present in the Parallels Service user registry (p. 42)). To determine the current access level for other users on a particular virtual machine, use the PrlAcl_GetAccessForOthers function. For the complete set of user access management functions, see the PHT_ACCESS_RIGHTS object description in the Parallels C API Reference guide.
The following sample function demonstrates how to set virtual machine access rights and how to determine access rights on the specified virtual machine for the currently logged in user. The function accepts a virtual machine handle and operates on the referenced virtual machine.
PRL_RESULT AccessRightsSample(PRL_HANDLE hVm) { PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_HANDLE hAccessRights = PRL_INVALID_HANDLE;
PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Obtain a PHT_VM_CONFIGURATION handle. PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE; ret = PrlVm_GetConfig(hVm, &hVmCfg);
// Obtain the access rights handle (this will be a // handle of type PHT_ACCESS_RIGHTS). ret = PrlVmCfg_GetAccessRights(hVmCfg, &hAccessRights); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hVmCfg); return -1; }
PrlHandle_Free(hVmCfg);
91
Parallels C API by Example
// Get the VM owner name from the access rights handle. PRL_CHAR sBuf[1024]; PRL_UINT32 nBufSize = sizeof(sBuf); ret = PrlAcl_GetOwnerName(hAccessRights, sBuf, &nBufSize); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hAccessRights); return -1; } printf("Owner: %s\n", sBuf);
// Change the virtual machine access rights for other users // to PAO_VM_SHARED_ON_VIEW_AND_RUN, which means that the // users will be able to see the machine in the list and to // run it. When this operation completes, we will use // PrlAcl_IsAllowed function to determine whether the user // is allowed to perform a particular task on the virtual // machine. PRL_VM_ACCESS_FOR_OTHERS access = PAO_VM_SHARED_ON_VIEW_AND_RUN; ret = PrlAcl_SetAccessForOthers(hAccessRights, access); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Commit the changes. hJob = PrlVm_UpdateSecurity(hVm, hAccessRights); ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { // Handle the error... return -1; } // Analyze the result of PrlVm_UpdateSecurity. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); PrlHandle_Free(hJob); if (PRL_FAILED(ret)) { // Handle the error... return -1; } // Check the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... return -1; }
// Determine if the current user has the right to // start the virtual machine. PRL_ALLOWED_VM_COMMAND access_level = PAR_VM_START_ACCESS; PRL_BOOL isAllowed = PRL_FALSE; ret = PrlAcl_IsAllowed(hAccessRights, access_level, &isAllowed); if (PRL_FAILED(ret)) { // Handle the error... return -1; } printf("Can start: %d\n", isAllowed);
92
Parallels C API by Example
// Determine if the current user has the right to // delete the specified virtual machine. access_level = PAR_VM_DELETE_ACCESS; isAllowed = PRL_FALSE; ret = PrlAcl_IsAllowed(hAccessRights, access_level, &isAllowed); if (PRL_FAILED(ret)) { // Handle the error... return -1; } printf("Can delete: %d\n", isAllowed);
PrlHandle_Free(hAccessRights); return 0; }

Working with Virtual Machine Templates

Templates are virtual machines that cannot be run but can be used as a patterns to create new virtual machines. Virtual machine templates are not different from regular virtual machines except, as was mentioned earlier, that they cannot be run. In fact, you can convert a template to a regular virtual machine at any time, just as you can convert a regular virtual machine to a template.
The Parallels C API allows to perform the following template-related operations:
Obtaining a list of the available virtual machine templates.
Creating a virtual machine template from scratch.
Converting a regular virtual machine to a template.
Converting a template to a regular virtual machine.
Creating a new virtual machine from a template.
The following subsections describes each operation in detail and provide code examples.
Obtaining a List of Templates
A list of virtual machines and virtual machine templates are obtained from the server using the same function: PrlSrv_GetVmList (p. 54). A template is identified by calling the PrlVmCfg_IsTemplate function which returns a boolean value indicating whether the specified virtual machine handle contains information about a regular virtual or a handle. The value of PRL_TRUE indicates that the machine is a template. The value of PRL_FALSE indicates that the machine is a regular virtual machine. The following sample is identical to the sample provided in the Obtaining a List of Virtual Machines section (p. 54) with the exception that it was modified to display only the lists of templates on the screen:
PRL_RESULT GetTemplateList(const PRL_HANDLE &hServer) { PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_HANDLE hJobResult = PRL_INVALID_HANDLE;
PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
93
Parallels C API by Example
// Get a list of the available virtual machines. hJob = PrlSrv_GetVmList(hServer);
// Wait for a maximum of 10 seconds for PrlSrv_GetVmList. ret = PrlJob_Wait(hJob, 10000); if (PRL_FAILED(ret)) { fprintf(stderr, "PrlJob_Wait for PrlSrv_GetVmList returned with error: %s\n", prl_result_to_string(ret)); PrlHandle_Free(hJob); return ret; }
// Check the results of PrlSrv_GetVmList. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret)) { fprintf(stderr, "PrlJob_GetRetCode returned with error: %s\n", prl_result_to_string(ret)); PrlHandle_Free(hJob); return ret; }
if (PRL_FAILED(nJobReturnCode)) { fprintf(stderr, "PrlSrv_GetVmList returned with error: %s\n", prl_result_to_string(ret)); PrlHandle_Free(hJob); return ret; }
// Get the results of PrlSrv_GetVmList. ret = PrlJob_GetResult(hJob, &hJobResult); if (PRL_FAILED(ret)) { fprintf(stderr, "PrlJob_GetResult returned with error: %s\n", prl_result_to_string(ret)); PrlHandle_Free(hJob); return ret; }
// Handle to result object is available, // job handle is no longer needed, so free it. PrlHandle_Free(hJob);
// Iterate through the results (list of virtual machines returned). PRL_UINT32 nParamsCount = 0; ret = PrlResult_GetParamsCount(hJobResult, &nParamsCount); for (PRL_UINT32 i = 0; i < nParamsCount; ++i) { // Virtual machine handle PRL_HANDLE hVm = PRL_INVALID_HANDLE;
// Get a handle to result at index i. PrlResult_GetParamByIndex(hJobResult, i, &hVm);
// Obtain the PHT_VM_CONFIGURATION object. PRL_HANDLE hVmCfg = PRL_INVALID_HANDLE; ret = PrlVm_GetConfig(hVm, &hVmCfg);
94
Parallels C API by Example
// See if the handle contains information about a template. PRL_BOOL isTemplate = PRL_FALSE; PrlVmCfg_IsTemplate(hVmCfg, &isTemplate);
// If this is not a template, proceed to the next // virtual machine in the list. if (isTemplate == PRL_FALSE) { PrlHandle_Free(hVmCfg); PrlHandle_Free(hVm); continue; }
// Get the name of the template for result i. char szVmNameReturned[1024]; PRL_UINT32 nBufSize = sizeof(szVmNameReturned); ret = PrlVmCfg_GetName(hVmCfg, szVmNameReturned, &nBufSize); if (PRL_FAILED(ret)) { printf("PrlVmCfg_GetName returned with error (%s)\n", prl_result_to_string(ret)); } else { printf("Template name: '%s'.\n", szVmNameReturned); }
PrlHandle_Free(hVm); PrlHandle_Free(hVmCfg); }
return PRL_ERR_SUCCESS; }
Creating a Template From Scratch
The steps in creating a new template and the steps in creating a new virtual machine are exactly the same, with one exception: before registering a template with the Parallels Service, a call to
PrlVmCfg_SetTemplateSign function must be made passing the PRL_TRUE in the bVmIsTemplate parameter. This will set a flag in the configuration structure indicating that you
want to create a template, not a regular virtual machine. The rest of the configuration parameters are set exactly as they are set for a regular virtual machine. See the Creating a New Virtual Machine section (p. 65) for the detailed information about creating a virtual machine.
The following example illustrates how to create a virtual machine template. For simplicity reasons, we only set a template name in this example. The rest of the configuration parameters are omitted. As a result, a blank template will be created. It still can be used to create new virtual machines from it but you will not be able to run them until you configure them properly. Once again, the Creating a New Virtual Machine section (p. 65) provides all the necessary information and code samples needed to properly configure a virtual machine or a template.
PRL_RESULT CreateTemplateFromScratch(PRL_HANDLE hServer) { PRL_HANDLE hVm = PRL_INVALID_HANDLE; PRL_HANDLE hJob = PRL_INVALID_HANDLE;
95
Parallels C API by Example
PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Create a new virtual machine handle. ret = PrlSrv_CreateVm(hServer, &hVm); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Set the name for the new template. ret = PrlVmCfg_SetName(hVm, "A simple template"); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hVm); return -1; }
// Set a flag indicating to create a template. PRL_BOOL isTemplate = PRL_TRUE; ret = PrlVmCfg_SetTemplateSign(hVm, isTemplate); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hVm); return -1; }
// Create and register the new template. // The empty string in the configuration path // indicates to create a template in the default // virtual machine directory. // The bNonInteractiveMode parameter indicates not to // use interactive mode (the Service will not send questions // to the client and will make all decisions on its own). PRL_CHAR_PTR sVmConfigPath = ""; PRL_BOOL bNonInteractiveMode = PRL_TRUE; hJob = PrlVm_Reg(hVm, sVmConfigPath, bNonInteractiveMode); // Wait for the job to complete. ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); PrlHandle_Free(hVm); return -1; }
// Analyze the result of PrlVm_Reg. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); PrlHandle_Free(hVm); return -1; } // Check the job return code.
96
Parallels C API by Example
if (PRL_FAILED(nJobReturnCode)) { // Handle the error... PrlHandle_Free(hJob); PrlHandle_Free(hVm); return -1; } PrlHandle_Free(hJob); PrlHandle_Free(hVm); return 0; }
Converting a Regular Virtual Machine to a Template
Any registered virtual machine can be converted to a template. This task is accomplished by modifying the virtual machine configuration. Only a single parameter must be modified: a flag indicating whether the machine is a regular virtual machine or a template, the rest will be handled automatically and transparently to you on the server side. The name of the function that allows to modify this parameter is PrlVm_SetTemplateSign.
The following code example illustrates how to convert a regular virtual machine to a template. Note that any of the virtual machine (or a template) configuration changes must begin with the PrlVm_BeginEdit and end with the PrlVm_BeginCommit function call. You should already know that these two functions are used to prevent collisions with other clients trying to modify the configuration of the same virtual machine or template at the same time.
PRL_RESULT ConvertVMtoTemplate(PRL_HANDLE hVm) { PRL_HANDLE hJobBeginEdit = PRL_INVALID_HANDLE; PRL_HANDLE hJobCommit = PRL_INVALID_HANDLE;
PRL_RESULT ret = PRL_ERR_UNINITIALIZED; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED;
// Begin of the VM configuration changes operation. hJobBeginEdit = PrlVm_BeginEdit(hVm); ret = PrlJob_Wait(hJobBeginEdit, 10000); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJobBeginEdit); return -1; } ret = PrlJob_GetRetCode(hJobBeginEdit, &nJobReturnCode); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJobBeginEdit); return -1; } // Check the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... PrlHandle_Free(hJobBeginEdit); return -1; } PrlHandle_Free(hJobBeginEdit);
97
Parallels C API by Example
// Set a flag in the virtual machine configuration // indicating that we want it to become a template. PRL_BOOL isTemplate = PRL_TRUE; ret = PrlVmCfg_SetTemplateSign(hVm, isTemplate); if (PRL_FAILED(ret)) { // Handle the error... return -1; }
// Commit the changes. hJobCommit = PrlVm_Commit(hVm); // Check the results of the commit operation. ret = PrlJob_Wait(hJobCommit, 10000); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJobCommit); return -1; } ret = PrlJob_GetRetCode(hJobCommit, &nJobReturnCode); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJobCommit); return -1; } // Check the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... PrlHandle_Free(hJobCommit); return -1; } PrlHandle_Free(hJobCommit);
return 0; }
Converting a Template to a Regular Virtual Machine
Converting a template to a regular virtual machine is no different than converting a virtual machine to a template (see the previous section for the description and an example). Simply set the boolean parameter in the PrlVmCfg_SetTemplateSign function to PRL_FALSE and leave the rest of the sample code the same.
98
Parallels C API by Example
Creating a New Virtual Machine From a Template.
The primary purpose of templates is to be used as patterns to create new virtual machines. New virtual machines are created from templates using the cloning functionality. We've already discussed how to clone a virtual machine in the Cloning a Virtual Machine section (p. 74). The truth is, creating a virtual machine from a template is at all different than creating a clone of a virtual machine. The PrlVm_Clone function that clones a virtual machine can also be used to create virtual machines from templates. The function has a boolean parameter that allows to specify whether a virtual machine or a template should be created. The following is almost the same example that we used in the Cloning a Virtual Machine section (p. 74) but this time we are setting the bCreateTemplate parameter to PRL_TRUE, thus creating a template instead of a regular virtual machine.
PRL_RESULT CreateVmFromTemplate(PRL_HANDLE hVm) { PRL_HANDLE hJob = PRL_INVALID_HANDLE; PRL_RESULT nJobReturnCode = PRL_ERR_UNINITIALIZED; PRL_RESULT ret = PRL_ERR_UNINITIALIZED;
// Declare and populate variables that // will be used as input parameters // in the function that clones a VM.
// Virtual machine name. // Get the name of the original VM (template) and use // it in the new virtual machine name. You can // use any name that you like of course. char vm_name[1024]; PRL_UINT32 nBufSize = sizeof(vm_name); ret = PrlVmCfg_GetName(hVm, vm_name, &nBufSize); char new_vm_name[1024] = "Created from template "; strcat(new_vm_name, vm_name);
// Name of the target directory on the // host. // Empty string indicates that the default // directory should be used. PRL_CHAR_PTR new_vm_root_path = "";
// Virtual machine or template? // The cloning functionality allows to create // a new virtual machine or a new template. // True specifies to create a template. // False indicates to create a virtual machine. // We want to create a virtual machine here, so we // set it to PRL_FALSE. PRL_BOOL bCreateTemplate = PRL_FALSE;
// Begin the cloning operation. hJob = PrlVm_Clone(hVm, new_vm_name, new_vm_root_path, bCreateTemplate); // Wait for the job to complete. ret = PrlJob_Wait(hJob, 1000); if (PRL_FAILED(ret)) { // Handle the error... printf("Error: (%s)\n", prl_result_to_string(ret));
99
Parallels C API by Example
PrlHandle_Free(hJob); return -1; }
// Analyze the result of PrlVm_Clone. ret = PrlJob_GetRetCode(hJob, &nJobReturnCode); if (PRL_FAILED(ret)) { // Handle the error... PrlHandle_Free(hJob); return -1; } // Check the job return code. if (PRL_FAILED(nJobReturnCode)) { // Handle the error... printf("Error: (%s)\n", prl_result_to_string(nJobReturnCode)); PrlHandle_Free(hJob); return -1; } PrlHandle_Free(hJob); return 0; }
100
Loading...