Unable to backup a running VM on an Intel host using PrlBackup_Begin

MaximT1

Bit poster
The function fails with PRL_ERR_FAILURE.

Here's what I found in parallels.log:
11-01 08:05:13.597 F /prldirs:2592:b76e/ ParallelsDirs::Init( ) was called. Current app mode = 1 ( DESKTOP ) build version: 19.1.0 (54729) Sat, 14 Oct 2023 10:05:20
11-01 08:05:13.691 W /[QT]:2592:b774/ WARNING: QApplication was not created in the main() thread.
11-01 08:05:13.737 F /disp:755:b777/ Client [Client] state changed [Connected] uuid [{5d198400-fc27-4a43-a96b-09c8a7de39b6}] pid [2592] uid [501]
11-01 08:05:13.743 F /disp:755:b777/ Processing command 'DspCmdUserLoginLocal' 2040 (PJOC_SRV_LOGIN_LOCAL) from client [{5d198400-fc27-4a43-a96b-09c8a7de39b6}]
11-01 08:05:13.751 F /disp:755:b777/ Processing command 'DspCmdUserLoginLocalStage2' 2041 (PJOC_SRV_LOGIN_LOCAL) from client [{5d198400-fc27-4a43-a96b-09c8a7de39b6}]
11-01 08:05:13.751 F /disp:755:b777/ Parallels user [tester@.] successfully logged on( LOCAL ). [sessionId = {5d198400-fc27-4a43-a96b-09c8a7de39b6}, Interactive]
11-01 08:05:13.756 F /disp:755:b777/ Session with uuid[ {5d198400-fc27-4a43-a96b-09c8a7de39b6} ] was started.
11-01 08:05:13.763 F /disp:755:b777/ Processing command 'DspCmdGetBackupVmList' 2188 (PJOC_UNKNOWN) from client [{5d198400-fc27-4a43-a96b-09c8a7de39b6}]
11-01 08:05:13.772 F /disp:755:b777/ Processing command 'DspCmdBackupBegin' 2185 (PJOC_UNKNOWN) from client [{5d198400-fc27-4a43-a96b-09c8a7de39b6}]
11-01 08:05:13.772 W /[QT]:755:b777/ QObject::connect: No such slot Dsp::Backup::CBackupBeginCommand::cancel()
11-01 08:05:13.780 F /DispBackup:755:18c0/ [DspBackup] Can't prepare VM for backup: event source is not accessible
11-01 08:05:13.780 F /DispBackup:755:18c0/ [DspBackup] Unable to begin the backup session '{b9d7093f-e24d-44cd-96c3-c4711e649ef3}': 0x80000009 (PRL_ERR_FAILURE)
11-01 08:05:13.780 F /DispBackup:755:18c0/ Failed to begin the backup: 0x80000009 (PRL_ERR_FAILURE)
11-01 08:05:13.780 F /DispBackup:755:18c0/ Failed to begin the backup: 0x80000009 (PRL_ERR_FAILURE)
11-01 08:05:13.784 F /disp:755:b777/ Client [Client] state changed [Disconnected] uuid [{5d198400-fc27-4a43-a96b-09c8a7de39b6}] pid [2592] uid [501]

And this is the code that shows the problem. It looks for a running backupable machine and tries to back it up:
Code:
#import <ParallelsVirtualizationSDK/Parallels.h>
#import <ParallelsVirtualizationSDK/PrlApiMacro.h>
#import <ParallelsVirtualizationSDK/PrlApiCore.h>
#import <assert.h>
#import <iostream>
#import <libgen.h>

namespace
{
  void CheckPrlResult(PRL_RESULT result)
  {
    if (result == PRL_ERR_SUCCESS)
      return;

    std::cout << "Error: " << prl_result_to_string(result) << std::endl;
    assert(false);
  }

  PRL_RESULT WaitJobResult(PRL_HANDLE job, PRL_HANDLE_PTR jobResult)
  {
    CheckPrlResult(PrlJob_Wait(job, 60000));
    CheckPrlResult(PrlJob_GetResult(job, jobResult));
    PRL_RESULT jobResultCode;
    CheckPrlResult(PrlJob_GetRetCode(job, &jobResultCode));
    return jobResultCode;
  }

  std::string MachineStateToString(VIRTUAL_MACHINE_STATE state)
  {
    switch (state)
    {
      case VMS_UNKNOWN: return "Unknown";
      case VMS_STOPPED: return "Stopped";
      case VMS_STARTING: return "Starting";
      case VMS_RESTORING: return "Restoring";
      case VMS_RUNNING: return "Running";
      case VMS_PAUSED: return "Paused";
      case VMS_SUSPENDING: return "Suspending";
      case VMS_STOPPING: return "Stopping";
      case VMS_COMPACTING: return "Compacting";
      case VMS_SUSPENDED: return "Suspended";
      case VMS_SNAPSHOTING: return "Snapshoting";
      case VMS_RESETTING: return "Resetting";
      case VMS_PAUSING: return "Pausing";
      case VMS_CONTINUING: return "Continuing";
      case VMS_MIGRATING_OBSOLETE: return "Migrating";
      case VMS_DELETING_STATE: return "Deleting";
      case VMS_RESUMING: return "Resuming";
      case VMS_SUSPENDING_SYNC: return "Suspending sync";
      case VMS_RECONNECTING_OBSOLETE: return "Reconnecting";
      case VMS_MOUNTED_OBSOLETE: return "Mounted";
    }
  }
}
int main(int argc, const char * argv[])
{
  CheckPrlResult(PrlApi_InitEx(PARALLELS_API_VER_5, PAM_DESKTOP, 0, 0));

  PRL_HANDLE server;
  CheckPrlResult(PrlSrv_Create(&server));

  auto loginJob = PrlSrv_LoginLocal(server, nullptr, 0, PSL_NORMAL_SECURITY);
  PRL_HANDLE loginResult;
  CheckPrlResult(WaitJobResult(loginJob, &loginResult));

  PRL_HANDLE loginResultParam;
  CheckPrlResult(PrlResult_GetParam(loginResult, &loginResultParam));

  PRL_UINT32 bufSize = 0;
  CheckPrlResult(PrlLoginResponse_GetProductVersion(loginResultParam, nullptr, &bufSize));
  char version[bufSize];
  CheckPrlResult(PrlLoginResponse_GetProductVersion(loginResultParam, version, &bufSize));
  std::cout << "Version: " << version << std::endl;

  PrlHandle_Free(loginResultParam);
  PrlHandle_Free(loginResult);
  PrlHandle_Free(loginJob);

  PRL_HANDLE listJob = PrlSrv_GetBackupVmList(server, 0);
  PRL_HANDLE listResult;
  CheckPrlResult(WaitJobResult(listJob, &listResult));

  PRL_UINT32 machineCount;
  CheckPrlResult(PrlResult_GetParamsCount(listResult, &machineCount));
  std::cout << "Machines: " << machineCount << std::endl;
  for (PRL_UINT32 i = 0; i < machineCount; i++)
  {
    std::cout << "Getting machine..." << std::endl;
    PRL_HANDLE machineHandle;
    CheckPrlResult(PrlResult_GetParamByIndex(listResult, i, &machineHandle));

    std::cout << "Getting backup info..." << std::endl;
    PRL_HANDLE backupInfo;
    CheckPrlResult(PrlBackup_GetInfo(machineHandle, &backupInfo));

    std::cout << "Getting machine state..." << std::endl;
    VIRTUAL_MACHINE_STATE machineState;
    CheckPrlResult(PrlBackupInfo_GetState(backupInfo, &machineState));
    std::cout << "Machine state: " << MachineStateToString(machineState) << std::endl;
    if (machineState == VMS_RUNNING)
    {
      std::cout << "Setting up backup..." << std::endl;
      PRL_HANDLE params;
      CheckPrlResult(PrlBackup_GetParams(machineHandle, &params));
      CheckPrlResult(PrlBackupParam_SetChangeMonitoring(params, PRL_TRUE));
      CheckPrlResult(PrlBackupParam_SetLevel(params, PBL_FULL));
      PRL_UINT32 options = PBOPT_DISABLE_GUEST_FS_SUSPEND;
      CheckPrlResult(PrlBackupParam_SetOptions(params, options));
      CheckPrlResult(PrlBackup_SetParams(machineHandle, params));

      std::cout << "Starting backup..." << std::endl;
      PRL_HANDLE backupJob = PrlBackup_Begin(machineHandle, 0);
      PRL_HANDLE backupJobResult;
      CheckPrlResult(WaitJobResult(backupJob, &backupJobResult));
      PrlHandle_Free(backupJobResult);
      PrlHandle_Free(backupJob);

      PRL_HANDLE fileList;
      CheckPrlResult(PrlBackup_GetFilesList(machineHandle, &fileList));

      PRL_UINT32 fileCount;
      CheckPrlResult(PrlHndlList_GetItemsCount(fileList, &fileCount));
      std::cout << "Files in backup: " << fileCount << std::endl;
      for (PRL_UINT32 j = 0; j < fileCount; j++)
      {
        PRL_HANDLE file;
        CheckPrlResult(PrlHndlList_GetItem(fileList, j, &file));
        PRL_UINT32 filePathSize = 0;
        CheckPrlResult(PrlBackupFile_GetPath(file, nullptr, &filePathSize));
        char filePath[filePathSize];
        CheckPrlResult(PrlBackupFile_GetPath(file, filePath, &filePathSize));
        std::cout << filePath << std::endl;
        PrlHandle_Free(file);
      }

      std::cout << "Rolling back..." << std::endl;
      PRL_HANDLE rollbackJob = PrlBackup_Rollback(machineHandle, 0);
      PRL_HANDLE rollbackJobResult;
      CheckPrlResult(WaitJobResult(rollbackJob, &rollbackJobResult));
      PrlHandle_Free(rollbackJobResult);
      PrlHandle_Free(rollbackJob);
    }
    else
    {
      std::cout << "Skipping..." << std::endl;
    }
    PrlHandle_Free(backupInfo);
    PrlHandle_Free(machineHandle);
  }

  PrlHandle_Free(listResult);
  PrlHandle_Free(listJob);

  PrlHandle_Free(server);
  return 0;
}
 
Back
Top