/** @file * * tstVBoxAPILinux - sample program to illustrate the VirtualBox * XPCOM API for machine management on Linux. * It only uses standard C/C++ and XPCOM semantics, * no additional VBox classes/macros/helpers. */ /* * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ /* * PURPOSE OF THIS SAMPLE PROGRAM * ------------------------------ * * This sample program is intended to demonstrate the minimal code necessary * to use VirtualBox XPCOM API for learning puroses only. The program uses * pure XPCOM and doesn't have any extra dependencies to let you better * understand what is going on when a client talks to the VirtualBox core * using the XPCOM framework. * * However, if you want to write a real application, it is highly recommended * to use our MS COM XPCOM Glue library and helper C++ classes. This way, you * will get at least the following benefits: * * a) better portability: both the MS COM (used on Windows) and XPCOM (used * everywhere else) VirtualBox client application from the same source code * (including common smart C++ templates for automatic interface pointer * reference counter and string data management); * b) simpler XPCOM initialization and shutdown (only a signle method call * that does everything right). * * Currently, there is no separate sample program that uses the VirtualBox MS * COM XPCOM Glue library. Please refer to the sources of stock VirtualBox * applications such as the VirtualBox GUI frontend or the VBoxManage command * line frontend. * * * RUNNING THIS SAMPLE PROGRAM * --------------------------- * * This sample program needs to know where the VirtualBox core files reside * and where to search for VirtualBox shared libraries. Therefore, you need to * use the following (or similar) command to execute it: * * $ env VBOX_XPCOM_HOME=../../.. LD_LIBRARY_PATH=../../.. ./tstVBoxAPILinux * * The above command assumes that VBoxRT.so, VBoxXPCOM.so and others reside in * the directory ../../.. */ #include #include #include #include /* * Include the XPCOM headers */ #if defined(XPCOM_GLUE) #include #endif #include #include #include #include #include /* * VirtualBox XPCOM interface. This header is generated * from IDL which in turn is generated from a custom XML format. */ #include "VirtualBox_XPCOM.h" /* * Prototypes */ char *nsIDToString(nsID *guid); void printErrorInfo(); /** * Display all registered VMs on the screen with some information about each * * @param virtualBox VirtualBox instance object. */ void listVMs(IVirtualBox *virtualBox) { nsresult rc; printf("----------------------------------------------------\n"); printf("VM List:\n\n"); /* * Get the list of all registered VMs */ IMachine **machines = NULL; PRUint32 machineCnt = 0; rc = virtualBox->GetMachines(&machineCnt, &machines); if (NS_SUCCEEDED(rc)) { /* * Iterate through the collection */ for (PRUint32 i = 0; i < machineCnt; ++ i) { IMachine *machine = machines[i]; if (machine) { PRBool isAccessible = PR_FALSE; machine->GetAccessible(&isAccessible); if (isAccessible) { nsXPIDLString machineName; machine->GetName(getter_Copies(machineName)); char *machineNameAscii = ToNewCString(machineName); printf("\tName: %s\n", machineNameAscii); free(machineNameAscii); } else { printf("\tName: \n"); } nsXPIDLString iid; machine->GetId(getter_Copies(iid)); const char *uuidString = ToNewCString(iid); printf("\tUUID: %s\n", uuidString); free((void*)uuidString); if (isAccessible) { nsXPIDLString configFile; machine->GetSettingsFilePath(getter_Copies(configFile)); char *configFileAscii = ToNewCString(configFile); printf("\tConfig file: %s\n", configFileAscii); free(configFileAscii); PRUint32 memorySize; machine->GetMemorySize(&memorySize); printf("\tMemory size: %uMB\n", memorySize); nsXPIDLString typeId; machine->GetOSTypeId(getter_Copies(typeId)); IGuestOSType *osType = nsnull; virtualBox->GetGuestOSType (typeId.get(), &osType); nsXPIDLString osName; osType->GetDescription(getter_Copies(osName)); char *osNameAscii = ToNewCString(osName); printf("\tGuest OS: %s\n\n", osNameAscii); free(osNameAscii); osType->Release(); } /* don't forget to release the objects in the array... */ machine->Release(); } } } printf("----------------------------------------------------\n\n"); } /** * Create a sample VM * * @param virtualBox VirtualBox instance object. */ void createVM(IVirtualBox *virtualBox) { nsresult rc; /* * First create a unnamed new VM. It will be unconfigured and not be saved * in the configuration until we explicitely choose to do so. */ nsCOMPtr machine; rc = virtualBox->CreateMachine(NS_LITERAL_STRING("A brand new name").get(), nsnull, nsnull, nsnull, false, getter_AddRefs(machine)); if (NS_FAILED(rc)) { printf("Error: could not create machine! rc=%08X\n", rc); return; } /* * Set some properties */ /* alternative to illustrate the use of string classes */ rc = machine->SetName(NS_ConvertUTF8toUTF16("A new name").get()); rc = machine->SetMemorySize(128); /* * Now a more advanced property -- the guest OS type. This is * an object by itself which has to be found first. Note that we * use the ID of the guest OS type here which is an internal * representation (you can find that by configuring the OS type of * a machine in the GUI and then looking at the * setting in the XML file. It is also possible to get the OS type from * its description (win2k would be "Windows 2000") by getting the * guest OS type collection and enumerating it. */ nsCOMPtr osType; rc = virtualBox->GetGuestOSType(NS_LITERAL_STRING("win2k").get(), getter_AddRefs(osType)); if (NS_FAILED(rc)) { printf("Error: could not find guest OS type! rc=%08X\n", rc); } else { machine->SetOSTypeId (NS_LITERAL_STRING("win2k").get()); } /* * Register the VM. Note that this call also saves the VM config * to disk. It is also possible to save the VM settings but not * register the VM. * * Also note that due to current VirtualBox limitations, the machine * must be registered *before* we can attach hard disks to it. */ rc = virtualBox->RegisterMachine(machine); if (NS_FAILED(rc)) { printf("Error: could not register machine! rc=%08X\n", rc); printErrorInfo(); return; } /* * In order to manipulate the registered machine, we must open a session * for that machine. Do it now. */ nsCOMPtr session; { nsCOMPtr manager; rc = NS_GetComponentManager (getter_AddRefs (manager)); if (NS_FAILED(rc)) { printf("Error: could not get component manager! rc=%08X\n", rc); return; } rc = manager->CreateInstanceByContractID (NS_SESSION_CONTRACTID, nsnull, NS_GET_IID(ISession), getter_AddRefs(session)); if (NS_FAILED(rc)) { printf("Error, could not instantiate Session object! rc=0x%x\n", rc); return; } nsXPIDLString machineUUID; machine->GetId(getter_Copies(machineUUID)); rc = virtualBox->OpenSession(session, machineUUID); if (NS_FAILED(rc)) { printf("Error, could not open session! rc=0x%x\n", rc); return; } /* * After the machine is registered, the initial machine object becomes * immutable. In order to get a mutable machine object, we must query * it from the opened session object. */ rc = session->GetMachine(getter_AddRefs(machine)); if (NS_FAILED(rc)) { printf("Error, could not get sessioned machine! rc=0x%x\n", rc); return; } } /* * Create a virtual harddisk */ nsCOMPtr hardDisk = 0; rc = virtualBox->CreateHardDisk(NS_LITERAL_STRING("VDI").get(), NS_LITERAL_STRING("TestHardDisk.vdi").get(), getter_AddRefs(hardDisk)); if (NS_FAILED(rc)) { printf("Failed creating a hard disk object! rc=%08X\n", rc); } else { /* * We have only created an object so far. No on disk representation exists * because none of its properties has been set so far. Let's continue creating * a dynamically expanding image. */ nsCOMPtr progress; rc = hardDisk->CreateBaseStorage(100, // size in megabytes MediumVariant_Standard, getter_AddRefs(progress)); // optional progress object if (NS_FAILED(rc)) { printf("Failed creating hard disk image! rc=%08X\n", rc); } else { /* * Creating the image is done in the background because it can take quite * some time (at least fixed size images). We have to wait for its completion. * Here we wait forever (timeout -1) which is potentially dangerous. */ rc = progress->WaitForCompletion(-1); PRInt32 resultCode; progress->GetResultCode(&resultCode); if (NS_FAILED(rc) || NS_FAILED(resultCode)) { printf("Error: could not create hard disk! rc=%08X\n", NS_FAILED(rc) ? rc : resultCode); } else { /* * Now that it's created, we can assign it to the VM. This is done * by UUID, so query that one fist. The UUID has been assigned automatically * when we've created the image. */ nsXPIDLString vdiUUID; hardDisk->GetId(getter_Copies(vdiUUID)); rc = machine->AttachDevice(NS_LITERAL_STRING("IDE Controller").get(), // controller identifier 0, // channel number on the controller 0, // device number on the controller DeviceType_HardDisk, vdiUUID); if (NS_FAILED(rc)) { printf("Error: could not attach hard disk! rc=%08X\n", rc); } } } } /* * It's got a hard disk but that one is new and thus not bootable. Make it * boot from an ISO file. This requires some processing. First the ISO file * has to be registered and then mounted to the VM's DVD drive and selected * as the boot device. */ nsCOMPtr dvdImage; rc = virtualBox->OpenDVDImage(NS_LITERAL_STRING("/home/vbox/isos/winnt4ger.iso").get(), nsnull, /* NULL UUID, i.e. a new one will be created */ getter_AddRefs(dvdImage)); if (NS_FAILED(rc)) { printf("Error: could not open CD image! rc=%08X\n", rc); } else { /* * Now assign it to our VM */ nsXPIDLString isoUUID; dvdImage->GetId(getter_Copies(isoUUID)); rc = machine->MountMedium(NS_LITERAL_STRING("IDE Controller").get(), // controller identifier 2, // channel number on the controller 0, // device number on the controller isoUUID, PR_FALSE); // aForce if (NS_FAILED(rc)) { printf("Error: could not mount ISO image! rc=%08X\n", rc); } else { /* * Last step: tell the VM to boot from the CD. */ rc = machine->SetBootOrder (1, DeviceType::DVD); if (NS_FAILED(rc)) { printf("Could not set boot device! rc=%08X\n", rc); } } } /* * Save all changes we've just made. */ rc = machine->SaveSettings(); if (NS_FAILED(rc)) { printf("Could not save machine settings! rc=%08X\n", rc); } /* * It is always important to close the open session when it becomes not * necessary any more. */ session->Close(); } // main /////////////////////////////////////////////////////////////////////////////// int main(int argc, char *argv[]) { /* * Check that PRUnichar is equal in size to what compiler composes L"" * strings from; otherwise NS_LITERAL_STRING macros won't work correctly * and we will get a meaningless SIGSEGV. This, of course, must be checked * at compile time in xpcom/string/nsTDependentString.h, but XPCOM lacks * compile-time assert macros and I'm not going to add them now. */ if (sizeof(PRUnichar) != sizeof(wchar_t)) { printf("Error: sizeof(PRUnichar) {%lu} != sizeof(wchar_t) {%lu}!\n" "Probably, you forgot the -fshort-wchar compiler option.\n", (unsigned long) sizeof(PRUnichar), (unsigned long) sizeof(wchar_t)); return -1; } nsresult rc; /* * This is the standard XPCOM init procedure. * What we do is just follow the required steps to get an instance * of our main interface, which is IVirtualBox. */ #if defined(XPCOM_GLUE) XPCOMGlueStartup(nsnull); #endif /* * Note that we scope all nsCOMPtr variables in order to have all XPCOM * objects automatically released before we call NS_ShutdownXPCOM at the * end. This is an XPCOM requirement. */ { nsCOMPtr serviceManager; rc = NS_InitXPCOM2(getter_AddRefs(serviceManager), nsnull, nsnull); if (NS_FAILED(rc)) { printf("Error: XPCOM could not be initialized! rc=0x%x\n", rc); return -1; } #if 0 /* * Register our components. This step is only necessary if this executable * implements XPCOM components itself which is not the case for this * simple example. */ nsCOMPtr registrar = do_QueryInterface(serviceManager); if (!registrar) { printf("Error: could not query nsIComponentRegistrar interface!\n"); return -1; } registrar->AutoRegister(nsnull); #endif /* * Make sure the main event queue is created. This event queue is * responsible for dispatching incoming XPCOM IPC messages. The main * thread should run this event queue's loop during lengthy non-XPCOM * operations to ensure messages from the VirtualBox server and other * XPCOM IPC clients are processed. This use case doesn't perform such * operations so it doesn't run the event loop. */ nsCOMPtr eventQ; rc = NS_GetMainEventQ(getter_AddRefs (eventQ)); if (NS_FAILED(rc)) { printf("Error: could not get main event queue! rc=%08X\n", rc); return -1; } /* * Now XPCOM is ready and we can start to do real work. * IVirtualBox is the root interface of VirtualBox and will be * retrieved from the XPCOM component manager. We use the * XPCOM provided smart pointer nsCOMPtr for all objects because * that's very convenient and removes the need deal with reference * counting and freeing. */ nsCOMPtr manager; rc = NS_GetComponentManager (getter_AddRefs (manager)); if (NS_FAILED(rc)) { printf("Error: could not get component manager! rc=%08X\n", rc); return -1; } nsCOMPtr virtualBox; rc = manager->CreateInstanceByContractID (NS_VIRTUALBOX_CONTRACTID, nsnull, NS_GET_IID(IVirtualBox), getter_AddRefs(virtualBox)); if (NS_FAILED(rc)) { printf("Error, could not instantiate VirtualBox object! rc=0x%x\n", rc); return -1; } printf("VirtualBox object created\n"); //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// listVMs(virtualBox); createVM(virtualBox); //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// /* this is enough to free the IVirtualBox instance -- smart pointers rule! */ virtualBox = nsnull; /* * Process events that might have queued up in the XPCOM event * queue. If we don't process them, the server might hang. */ eventQ->ProcessPendingEvents(); } /* * Perform the standard XPCOM shutdown procedure. */ NS_ShutdownXPCOM(nsnull); #if defined(XPCOM_GLUE) XPCOMGlueShutdown(); #endif printf("Done!\n"); return 0; } ////////////////////////////////////////////////////////////////////////////////////////////////////// //// Helpers ////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Helper function to convert an nsID into a human readable string * * @returns result string, allocated. Has to be freed using free() * @param guid Pointer to nsID that will be converted. */ char *nsIDToString(nsID *guid) { char *res = (char*)malloc(39); if (res != NULL) { snprintf(res, 39, "{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}", guid->m0, (PRUint32)guid->m1, (PRUint32)guid->m2, (PRUint32)guid->m3[0], (PRUint32)guid->m3[1], (PRUint32)guid->m3[2], (PRUint32)guid->m3[3], (PRUint32)guid->m3[4], (PRUint32)guid->m3[5], (PRUint32)guid->m3[6], (PRUint32)guid->m3[7]); } return res; } /** * Helper function to print XPCOM exception information set on the current * thread after a failed XPCOM method call. This function will also print * extended VirtualBox error info if it is available. */ void printErrorInfo() { nsresult rc; nsCOMPtr es; es = do_GetService (NS_EXCEPTIONSERVICE_CONTRACTID, &rc); if (NS_SUCCEEDED(rc)) { nsCOMPtr em; rc = es->GetCurrentExceptionManager (getter_AddRefs (em)); if (NS_SUCCEEDED(rc)) { nsCOMPtr ex; rc = em->GetCurrentException (getter_AddRefs (ex)); if (NS_SUCCEEDED(rc) && ex) { nsCOMPtr info; info = do_QueryInterface(ex, &rc); if (NS_SUCCEEDED(rc) && info) { /* got extended error info */ printf ("Extended error info (IVirtualBoxErrorInfo):\n"); PRInt32 resultCode = NS_OK; info->GetResultCode (&resultCode); printf (" resultCode=%08X\n", resultCode); nsXPIDLString component; info->GetComponent (getter_Copies (component)); printf (" component=%s\n", NS_ConvertUTF16toUTF8(component).get()); nsXPIDLString text; info->GetText (getter_Copies (text)); printf (" text=%s\n", NS_ConvertUTF16toUTF8(text).get()); } else { /* got basic error info */ printf ("Basic error info (nsIException):\n"); nsresult resultCode = NS_OK; ex->GetResult (&resultCode); printf (" resultCode=%08X\n", resultCode); nsXPIDLCString message; ex->GetMessage (getter_Copies (message)); printf (" message=%s\n", message.get()); } /* reset the exception to NULL to indicate we've processed it */ em->SetCurrentException (NULL); rc = NS_OK; } } } }