Page 1 of 1

SessionState never unlocked

Posted: 21. Apr 2022, 14:21
by mbono
Hello,

I have used the VirtualBox COM interface for a while with VirtualBox 6.0. Now I am having troubles with 6.1.

The following code works well on 6.0.

* I start a VM in "headless"
* When the state is Running I stop it
* Then I wait until the session state is unlocked again
* I redo the previous steps 10 times.
simple_vbox_test.cpp
(6.25 KiB) Downloaded 48 times

Code: Select all

#include "VirtualBox.h"

#include <chrono>
#include <string>
#include <thread>

#define SAFE_RELEASE(x)                                                                                                \
  if (x)                                                                                                               \
  {                                                                                                                    \
    x->Release();                                                                                                      \
    x = NULL;                                                                                                          \
  }

void printErrorInfo()
{
  IErrorInfo *errorInfo;
  HRESULT rc = GetErrorInfo(0, &errorInfo);
  if (FAILED(rc))
  {
    printf("Failed to get error info! rc = 0x%x\n", rc);
    return;
  }

  BSTR errorDescription = NULL;

  rc = errorInfo->GetDescription(&errorDescription);

  if (FAILED(rc) || !errorDescription)
  {
    printf("Error getting error description! rc = 0x%x\n", rc);
    return;
  }

  printf("Successfully retrieved error description: %S\n", errorDescription);
  SysFreeString(errorDescription);
  SAFE_RELEASE(errorInfo);
}

int testStartVM(IVirtualBox *virtualBox, const wchar_t *name, const wchar_t *sessionTypeName)
{
  HRESULT rc;

  /* Try to start a VM called "WinXP SP2". */
  IMachine *machine = NULL;
  BSTR machineName = SysAllocString(name);

  rc = virtualBox->FindMachine(machineName, &machine);

  if (FAILED(rc))
  {
    printErrorInfo();
  }
  else
  {
    ISession *session = NULL;
    IConsole *console = NULL;
    IProgress *progress = NULL;
    BSTR sessiontype = SysAllocString(sessionTypeName);
    BSTR guid;

    do
    {
      rc = machine->get_Id(&guid); /* Get the GUID of the machine. */
      if (!SUCCEEDED(rc))
      {
        printf("Error retrieving machine ID! rc = 0x%x\n", rc);
        printErrorInfo();
        break;
      }

      /* Create the session object. */
      rc = CoCreateInstance(CLSID_Session,        /* the VirtualBox base object */
                            NULL,                 /* no aggregation */
                            CLSCTX_INPROC_SERVER, /* the object lives in the current process */
                            IID_ISession,         /* IID of the interface */
                            (void **)&session);
      if (!SUCCEEDED(rc))
      {
        printf("Error creating Session instance! rc = 0x%x\n", rc);
        printErrorInfo();
        break;
      }

      /* Start a VM session using the delivered VBox GUI. */
      rc = machine->LaunchVMProcess(session, sessiontype, NULL, &progress);
      if (!SUCCEEDED(rc))
      {
        printf("Could not open remote session! rc = 0x%x\n", rc);
        printErrorInfo();
        break;
      }

      /* Wait until VM is running. */
      printf("Starting VM, please wait ...\n");
      rc = progress->WaitForCompletion(-1);
      if (!SUCCEEDED(rc))
      {
        printf("Progress wait for LaunchVMProcess completion fail! rc = 0x%x\n", rc);
        printErrorInfo();
        break;
      }

      /* Get console object. */
      session->get_Console(&console);

      /* Bring console window to front. */
      machine->ShowConsoleWindow(0);

      while (true)
      {
        MachineState state;
        rc = machine->get_State(&state);
        if (FAILED(rc))
        {
          printf("Faile to get machine state! rc = 0x%x\n", rc);
          printErrorInfo();
          break;
        }

        printf("Machine state == %d\n", state);
        if (state == MachineState_Running)
        {
          break;
        }
      }

      /* Power down the machine. */
      rc = console->PowerDown(&progress);
      if (!SUCCEEDED(rc))
      {
        printf("Console PowerDown fail! rc = 0x%x\n", rc);
        printErrorInfo();
        break;
      }

      /* Wait until VM is powered down. */
      printf("Powering off VM, please wait ...\n");
      rc = progress->WaitForCompletion(-1);
      if (!SUCCEEDED(rc))
      {
        printf("Progress wait for PowerDown fail! rc = 0x%x\n", rc);
        printErrorInfo();
        break;
      }

      /* Close the session. */
      rc = session->UnlockMachine();
      if (!SUCCEEDED(rc))
      {
        printf("Cannot unlock machine! rc = 0x%x\n", rc);
        printErrorInfo();
        break;
      }

    } while (0);

    SAFE_RELEASE(console);
    SAFE_RELEASE(progress);
    SAFE_RELEASE(session);
    SysFreeString(guid);
    SysFreeString(sessiontype);

    // wait for unlocked
    printf("Wait for machine unlocked ...\n");
    while (true)
    {
      SessionState sessionState;
      rc = machine->get_SessionState(&sessionState);
      if (FAILED(rc))
      {
        printf("Cannot get session state! rc = 0x%x\n", rc);
        printErrorInfo();
        break;
      }

      if (sessionState == SessionState_Unlocked)
      {
        break;
      }
    }
    SAFE_RELEASE(machine);
  }

  SysFreeString(machineName);

  return 0;
}

int main(int argc, char **argv)
{
  /* Initialize the COM subsystem. */
  CoInitialize(NULL);

  /* Instantiate the VirtualBox root object. */
  IVirtualBoxClient *virtualBoxClient;
  HRESULT rc = CoCreateInstance(CLSID_VirtualBoxClient, /* the VirtualBoxClient object */
                                NULL,                   /* no aggregation */
                                CLSCTX_INPROC_SERVER,   /* the object lives in the current process */
                                IID_IVirtualBoxClient,  /* IID of the interface */
                                (void **)&virtualBoxClient);
  if (SUCCEEDED(rc))
  {
    IVirtualBox *virtualBox;
    rc = virtualBoxClient->get_VirtualBox(&virtualBox);
    if (SUCCEEDED(rc))
    {
      for (int i = 0; i < 10; ++i)
      {
        printf("Test run %d\n", i);
        testStartVM(virtualBox, L"TESTING", L"headless");
      }

      /* Release the VirtualBox object. */
      virtualBox->Release();
      virtualBoxClient->Release();
    }
    else
    {
      printf("Error creating VirtualBox instance! rc = 0x%x\n", rc);
      printErrorInfo();
    }
  }

  CoUninitialize();
  return 0;
}
This simple test works fine on VirtualBox 6.0 but on 6.1 the machine state change correctly to PowerOff but the session is never unlocked and the VBoxHeadless.exe process is not killed and I need to kill it from task manager.
If I use a "gui" session type than all works as exepected also in 6.1.

I think that the problem is caused by the event loop of VBoxHeadless because I see that it is started after I triggrer PowerDown command from the console.
TESTING-2022-04-21-14-15-29.log
(107.99 KiB) Downloaded 53 times

Code: Select all

00:00:01.819978 Changing the VM state from 'DESTROYING' to 'TERMINATED'
00:00:01.824010 Console: Machine state changed to 'PoweredOff'
00:00:01.937393 VBoxHeadless: starting event loop
Question: do you think that I am doing some errors? Or this is a bug on VirtualBox 6.1.

Attached the code and the log.

Thanks.

Re: SessionState never unlocked

Posted: 21. Apr 2022, 16:23
by fth0
I have no practical experience with programmatically running a VM, but the following came to mind:

In the loop waiting for the Machine to reach the Running state, does it help to insert a pause (e.g. 100 ms or 1 s) between successive requests?

When trying the Headless mode in the past from within the VirtualBox Manager 6.1, I discovered that the Show action didn't seem to work for non-trivial VMs. Does it help not to call machine->ShowConsoleWindow(0)?

Re: SessionState never unlocked

Posted: 22. Apr 2022, 12:00
by mbono
In the real code (this is just a sample that reproduce the problem) I have a sleep between request and I do not use ShowConsoleWindow.

Re: SessionState never unlocked

Posted: 24. Apr 2022, 11:13
by noteirak
You need to wait indeed, any operation on VMs can take a "long" time.
Example of how to do it in Java, but the principle is the same: https://github.com/hyperbox/server/blob ... e.java#L53