Changeset 4426 in vbox for trunk/src/VBox
- Timestamp:
- Aug 29, 2007 11:03:26 PM (17 years ago)
- Location:
- trunk/src/VBox/Main
- Files:
-
- 2 edited
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/ConsoleImpl.cpp
r4404 r4426 830 830 { 831 831 LogFlowFuncEnter(); 832 832 833 833 #ifdef VRDP_NO_COM 834 834 Console *console = this; … … 3094 3094 fCableConnected)); 3095 3095 vrc = pINetCfg->pfnSetLinkState (pINetCfg, 3096 fCableConnected ? PDMNETWORKLINKSTATE_UP 3096 fCableConnected ? PDMNETWORKLINKSTATE_UP 3097 3097 : PDMNETWORKLINKSTATE_DOWN); 3098 3098 ComAssertRC (vrc); … … 4941 4941 LogFlowFuncLeave(); 4942 4942 return vrc; 4943 }4944 4945 /**4946 * Construct the VM configuration tree (CFGM).4947 *4948 * This is a callback for VMR3Create() call. It is called from CFGMR3Init()4949 * in the emulation thread (EMT). Any per thread COM/XPCOM initialization4950 * is done here.4951 *4952 * @param pVM VM handle.4953 * @param pvTask Pointer to the VMPowerUpTask object.4954 * @return VBox status code.4955 *4956 * @note Locks the Console object for writing.4957 */4958 DECLCALLBACK(int) Console::configConstructor(PVM pVM, void *pvTask)4959 {4960 LogFlowFuncEnter();4961 4962 /* Note: the task pointer is owned by powerUpThread() */4963 VMPowerUpTask *task = static_cast <VMPowerUpTask *> (pvTask);4964 AssertReturn (task, VERR_GENERAL_FAILURE);4965 4966 #if defined(RT_OS_WINDOWS)4967 {4968 /* initialize COM */4969 HRESULT hrc = CoInitializeEx(NULL,4970 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE |4971 COINIT_SPEED_OVER_MEMORY);4972 LogFlow (("Console::configConstructor(): CoInitializeEx()=%08X\n", hrc));4973 AssertComRCReturn (hrc, VERR_GENERAL_FAILURE);4974 }4975 #endif4976 4977 ComObjPtr <Console> pConsole = task->mConsole;4978 4979 AutoCaller autoCaller (pConsole);4980 AssertComRCReturn (autoCaller.rc(), VERR_ACCESS_DENIED);4981 4982 /* lock the console because we widely use internal fields and methods */4983 AutoLock alock (pConsole);4984 4985 ComPtr <IMachine> pMachine = pConsole->machine();4986 4987 int rc;4988 HRESULT hrc;4989 char *psz = NULL;4990 BSTR str = NULL;4991 ULONG cRamMBs;4992 unsigned i;4993 4994 #define STR_CONV() do { rc = RTStrUcs2ToUtf8(&psz, str); RC_CHECK(); } while (0)4995 #define STR_FREE() do { if (str) { SysFreeString(str); str = NULL; } if (psz) { RTStrFree(psz); psz = NULL; } } while (0)4996 #define RC_CHECK() do { if (VBOX_FAILURE(rc)) { AssertMsgFailed(("rc=%Vrc\n", rc)); STR_FREE(); return rc; } } while (0)4997 #define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%#x\n", hrc)); STR_FREE(); return VERR_GENERAL_FAILURE; } } while (0)4998 4999 /* Get necessary objects */5000 5001 ComPtr<IVirtualBox> virtualBox;5002 hrc = pMachine->COMGETTER(Parent)(virtualBox.asOutParam()); H();5003 5004 ComPtr<IHost> host;5005 hrc = virtualBox->COMGETTER(Host)(host.asOutParam()); H();5006 5007 ComPtr <ISystemProperties> systemProperties;5008 hrc = virtualBox->COMGETTER(SystemProperties)(systemProperties.asOutParam()); H();5009 5010 ComPtr<IBIOSSettings> biosSettings;5011 hrc = pMachine->COMGETTER(BIOSSettings)(biosSettings.asOutParam()); H();5012 5013 5014 /*5015 * Get root node first.5016 * This is the only node in the tree.5017 */5018 PCFGMNODE pRoot = CFGMR3GetRoot(pVM);5019 Assert(pRoot);5020 5021 /*5022 * Set the root level values.5023 */5024 hrc = pMachine->COMGETTER(Name)(&str); H();5025 STR_CONV();5026 rc = CFGMR3InsertString(pRoot, "Name", psz); RC_CHECK();5027 STR_FREE();5028 hrc = pMachine->COMGETTER(MemorySize)(&cRamMBs); H();5029 rc = CFGMR3InsertInteger(pRoot, "RamSize", cRamMBs * _1M); RC_CHECK();5030 rc = CFGMR3InsertInteger(pRoot, "TimerMillies", 10); RC_CHECK();5031 rc = CFGMR3InsertInteger(pRoot, "RawR3Enabled", 1); /* boolean */ RC_CHECK();5032 rc = CFGMR3InsertInteger(pRoot, "RawR0Enabled", 1); /* boolean */ RC_CHECK();5033 /** @todo Config: RawR0, PATMEnabled and CASMEnabled needs attention later. */5034 rc = CFGMR3InsertInteger(pRoot, "PATMEnabled", 1); /* boolean */ RC_CHECK();5035 rc = CFGMR3InsertInteger(pRoot, "CSAMEnabled", 1); /* boolean */ RC_CHECK();5036 5037 /* hardware virtualization extensions */5038 TriStateBool_T hwVirtExEnabled;5039 BOOL fHWVirtExEnabled;5040 hrc = pMachine->COMGETTER(HWVirtExEnabled)(&hwVirtExEnabled); H();5041 if (hwVirtExEnabled == TriStateBool_Default)5042 {5043 /* check the default value */5044 hrc = systemProperties->COMGETTER(HWVirtExEnabled)(&fHWVirtExEnabled); H();5045 }5046 else5047 fHWVirtExEnabled = (hwVirtExEnabled == TriStateBool_True);5048 #ifndef RT_OS_DARWIN /** @todo Implement HWVirtExt on darwin. See #1865. */5049 if (fHWVirtExEnabled)5050 {5051 PCFGMNODE pHWVirtExt;5052 rc = CFGMR3InsertNode(pRoot, "HWVirtExt", &pHWVirtExt); RC_CHECK();5053 rc = CFGMR3InsertInteger(pHWVirtExt, "Enabled", 1); RC_CHECK();5054 }5055 #endif5056 5057 BOOL fIOAPIC;5058 hrc = biosSettings->COMGETTER(IOAPICEnabled)(&fIOAPIC); H();5059 5060 /*5061 * PDM config.5062 * Load drivers in VBoxC.[so|dll]5063 */5064 PCFGMNODE pPDM;5065 PCFGMNODE pDrivers;5066 PCFGMNODE pMod;5067 rc = CFGMR3InsertNode(pRoot, "PDM", &pPDM); RC_CHECK();5068 rc = CFGMR3InsertNode(pPDM, "Drivers", &pDrivers); RC_CHECK();5069 rc = CFGMR3InsertNode(pDrivers, "VBoxC", &pMod); RC_CHECK();5070 #ifdef VBOX_WITH_XPCOM5071 // VBoxC is located in the components subdirectory5072 char szPathVBoxC[RTPATH_MAX];5073 rc = RTPathAppPrivateArch(szPathVBoxC, RTPATH_MAX - sizeof("/components/VBoxC")); AssertRC(rc);5074 strcat(szPathVBoxC, "/components/VBoxC");5075 rc = CFGMR3InsertString(pMod, "Path", szPathVBoxC); RC_CHECK();5076 #else5077 rc = CFGMR3InsertString(pMod, "Path", "VBoxC"); RC_CHECK();5078 #endif5079 5080 /*5081 * Devices5082 */5083 PCFGMNODE pDevices = NULL; /* /Devices */5084 PCFGMNODE pDev = NULL; /* /Devices/Dev/ */5085 PCFGMNODE pInst = NULL; /* /Devices/Dev/0/ */5086 PCFGMNODE pCfg = NULL; /* /Devices/Dev/.../Config/ */5087 PCFGMNODE pLunL0 = NULL; /* /Devices/Dev/0/LUN#0/ */5088 PCFGMNODE pLunL1 = NULL; /* /Devices/Dev/0/LUN#0/AttachedDriver/ */5089 PCFGMNODE pLunL2 = NULL; /* /Devices/Dev/0/LUN#0/AttachedDriver/Config/ */5090 5091 rc = CFGMR3InsertNode(pRoot, "Devices", &pDevices); RC_CHECK();5092 5093 /*5094 * PC Arch.5095 */5096 rc = CFGMR3InsertNode(pDevices, "pcarch", &pDev); RC_CHECK();5097 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();5098 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();5099 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5100 5101 /*5102 * PC Bios.5103 */5104 rc = CFGMR3InsertNode(pDevices, "pcbios", &pDev); RC_CHECK();5105 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();5106 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();5107 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5108 rc = CFGMR3InsertInteger(pCfg, "RamSize", cRamMBs * _1M); RC_CHECK();5109 rc = CFGMR3InsertString(pCfg, "HardDiskDevice", "piix3ide"); RC_CHECK();5110 rc = CFGMR3InsertString(pCfg, "FloppyDevice", "i82078"); RC_CHECK();5111 rc = CFGMR3InsertInteger(pCfg, "IOAPIC", fIOAPIC); RC_CHECK();5112 5113 DeviceType_T bootDevice;5114 if (SchemaDefs::MaxBootPosition > 9)5115 {5116 AssertMsgFailed (("Too many boot devices %d\n",5117 SchemaDefs::MaxBootPosition));5118 return VERR_INVALID_PARAMETER;5119 }5120 5121 for (ULONG pos = 1; pos <= SchemaDefs::MaxBootPosition; pos ++)5122 {5123 hrc = pMachine->GetBootOrder(pos, &bootDevice); H();5124 5125 char szParamName[] = "BootDeviceX";5126 szParamName[sizeof (szParamName) - 2] = ((char (pos - 1)) + '0');5127 5128 const char *pszBootDevice;5129 switch (bootDevice)5130 {5131 case DeviceType_NoDevice:5132 pszBootDevice = "NONE";5133 break;5134 case DeviceType_HardDiskDevice:5135 pszBootDevice = "IDE";5136 break;5137 case DeviceType_DVDDevice:5138 pszBootDevice = "DVD";5139 break;5140 case DeviceType_FloppyDevice:5141 pszBootDevice = "FLOPPY";5142 break;5143 case DeviceType_NetworkDevice:5144 pszBootDevice = "LAN";5145 break;5146 default:5147 AssertMsgFailed(("Invalid bootDevice=%d\n", bootDevice));5148 return VERR_INVALID_PARAMETER;5149 }5150 rc = CFGMR3InsertString(pCfg, szParamName, pszBootDevice); RC_CHECK();5151 }5152 5153 /*5154 * BIOS logo5155 */5156 BOOL fFadeIn;5157 hrc = biosSettings->COMGETTER(LogoFadeIn)(&fFadeIn); H();5158 rc = CFGMR3InsertInteger(pCfg, "FadeIn", fFadeIn ? 1 : 0); RC_CHECK();5159 BOOL fFadeOut;5160 hrc = biosSettings->COMGETTER(LogoFadeOut)(&fFadeOut); H();5161 rc = CFGMR3InsertInteger(pCfg, "FadeOut", fFadeOut ? 1: 0); RC_CHECK();5162 ULONG logoDisplayTime;5163 hrc = biosSettings->COMGETTER(LogoDisplayTime)(&logoDisplayTime); H();5164 rc = CFGMR3InsertInteger(pCfg, "LogoTime", logoDisplayTime); RC_CHECK();5165 Bstr logoImagePath;5166 hrc = biosSettings->COMGETTER(LogoImagePath)(logoImagePath.asOutParam()); H();5167 rc = CFGMR3InsertString(pCfg, "LogoFile", logoImagePath ? Utf8Str(logoImagePath) : ""); RC_CHECK();5168 5169 /*5170 * Boot menu5171 */5172 BIOSBootMenuMode_T bootMenuMode;5173 int value;5174 biosSettings->COMGETTER(BootMenuMode)(&bootMenuMode);5175 switch (bootMenuMode)5176 {5177 case BIOSBootMenuMode_Disabled:5178 value = 0;5179 break;5180 case BIOSBootMenuMode_MenuOnly:5181 value = 1;5182 break;5183 default:5184 value = 2;5185 }5186 rc = CFGMR3InsertInteger(pCfg, "ShowBootMenu", value); RC_CHECK();5187 5188 /*5189 * The time offset5190 */5191 LONG64 timeOffset;5192 hrc = biosSettings->COMGETTER(TimeOffset)(&timeOffset); H();5193 PCFGMNODE pTMNode;5194 rc = CFGMR3InsertNode(pRoot, "TM", &pTMNode); RC_CHECK();5195 rc = CFGMR3InsertInteger(pTMNode, "UTCOffset", timeOffset * 1000000); RC_CHECK();5196 5197 /*5198 * ACPI5199 */5200 BOOL fACPI;5201 hrc = biosSettings->COMGETTER(ACPIEnabled)(&fACPI); H();5202 if (fACPI)5203 {5204 rc = CFGMR3InsertNode(pDevices, "acpi", &pDev); RC_CHECK();5205 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();5206 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();5207 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5208 rc = CFGMR3InsertInteger(pCfg, "RamSize", cRamMBs * _1M); RC_CHECK();5209 rc = CFGMR3InsertInteger(pCfg, "IOAPIC", fIOAPIC); RC_CHECK();5210 rc = CFGMR3InsertInteger(pInst, "PCIDeviceNo", 7); RC_CHECK();5211 rc = CFGMR3InsertInteger(pInst, "PCIFunctionNo", 0); RC_CHECK();5212 5213 rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); RC_CHECK();5214 rc = CFGMR3InsertString(pLunL0, "Driver", "ACPIHost"); RC_CHECK();5215 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5216 }5217 5218 /*5219 * DMA5220 */5221 rc = CFGMR3InsertNode(pDevices, "8237A", &pDev); RC_CHECK();5222 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();5223 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();5224 5225 /*5226 * PCI bus.5227 */5228 rc = CFGMR3InsertNode(pDevices, "pci", &pDev); /* piix3 */ RC_CHECK();5229 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();5230 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();5231 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5232 rc = CFGMR3InsertInteger(pCfg, "IOAPIC", fIOAPIC); RC_CHECK();5233 5234 /*5235 * PS/2 keyboard & mouse.5236 */5237 rc = CFGMR3InsertNode(pDevices, "pckbd", &pDev); RC_CHECK();5238 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();5239 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();5240 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5241 5242 rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); RC_CHECK();5243 rc = CFGMR3InsertString(pLunL0, "Driver", "KeyboardQueue"); RC_CHECK();5244 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5245 rc = CFGMR3InsertInteger(pCfg, "QueueSize", 64); RC_CHECK();5246 5247 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); RC_CHECK();5248 rc = CFGMR3InsertString(pLunL1, "Driver", "MainKeyboard"); RC_CHECK();5249 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK();5250 Keyboard *pKeyboard = pConsole->mKeyboard;5251 rc = CFGMR3InsertInteger(pCfg, "Object", (uintptr_t)pKeyboard); RC_CHECK();5252 5253 rc = CFGMR3InsertNode(pInst, "LUN#1", &pLunL0); RC_CHECK();5254 rc = CFGMR3InsertString(pLunL0, "Driver", "MouseQueue"); RC_CHECK();5255 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5256 rc = CFGMR3InsertInteger(pCfg, "QueueSize", 128); RC_CHECK();5257 5258 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); RC_CHECK();5259 rc = CFGMR3InsertString(pLunL1, "Driver", "MainMouse"); RC_CHECK();5260 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK();5261 Mouse *pMouse = pConsole->mMouse;5262 rc = CFGMR3InsertInteger(pCfg, "Object", (uintptr_t)pMouse); RC_CHECK();5263 5264 /*5265 * i82078 Floppy drive controller5266 */5267 ComPtr<IFloppyDrive> floppyDrive;5268 hrc = pMachine->COMGETTER(FloppyDrive)(floppyDrive.asOutParam()); H();5269 BOOL fFloppyEnabled;5270 hrc = floppyDrive->COMGETTER(Enabled)(&fFloppyEnabled); H();5271 if (fFloppyEnabled)5272 {5273 rc = CFGMR3InsertNode(pDevices, "i82078", &pDev); RC_CHECK();5274 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();5275 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); RC_CHECK();5276 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5277 rc = CFGMR3InsertInteger(pCfg, "IRQ", 6); RC_CHECK();5278 rc = CFGMR3InsertInteger(pCfg, "DMA", 2); RC_CHECK();5279 rc = CFGMR3InsertInteger(pCfg, "MemMapped", 0 ); RC_CHECK();5280 rc = CFGMR3InsertInteger(pCfg, "IOBase", 0x3f0); RC_CHECK();5281 5282 /* Attach the status driver */5283 rc = CFGMR3InsertNode(pInst, "LUN#999", &pLunL0); RC_CHECK();5284 rc = CFGMR3InsertString(pLunL0, "Driver", "MainStatus"); RC_CHECK();5285 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5286 rc = CFGMR3InsertInteger(pCfg, "papLeds", (uintptr_t)&pConsole->mapFDLeds[0]); RC_CHECK();5287 rc = CFGMR3InsertInteger(pCfg, "First", 0); RC_CHECK();5288 rc = CFGMR3InsertInteger(pCfg, "Last", 0); RC_CHECK();5289 5290 rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); RC_CHECK();5291 5292 ComPtr<IFloppyImage> floppyImage;5293 hrc = floppyDrive->GetImage(floppyImage.asOutParam()); H();5294 if (floppyImage)5295 {5296 pConsole->meFloppyState = DriveState_ImageMounted;5297 rc = CFGMR3InsertString(pLunL0, "Driver", "Block"); RC_CHECK();5298 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5299 rc = CFGMR3InsertString(pCfg, "Type", "Floppy 1.44"); RC_CHECK();5300 rc = CFGMR3InsertInteger(pCfg, "Mountable", 1); RC_CHECK();5301 5302 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); RC_CHECK();5303 rc = CFGMR3InsertString(pLunL1, "Driver", "RawImage"); RC_CHECK();5304 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK();5305 hrc = floppyImage->COMGETTER(FilePath)(&str); H();5306 STR_CONV();5307 rc = CFGMR3InsertString(pCfg, "Path", psz); RC_CHECK();5308 STR_FREE();5309 }5310 else5311 {5312 ComPtr<IHostFloppyDrive> hostFloppyDrive;5313 hrc = floppyDrive->GetHostDrive(hostFloppyDrive.asOutParam()); H();5314 if (hostFloppyDrive)5315 {5316 pConsole->meFloppyState = DriveState_HostDriveCaptured;5317 rc = CFGMR3InsertString(pLunL0, "Driver", "HostFloppy"); RC_CHECK();5318 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5319 hrc = hostFloppyDrive->COMGETTER(Name)(&str); H();5320 STR_CONV();5321 rc = CFGMR3InsertString(pCfg, "Path", psz); RC_CHECK();5322 STR_FREE();5323 }5324 else5325 {5326 pConsole->meFloppyState = DriveState_NotMounted;5327 rc = CFGMR3InsertString(pLunL0, "Driver", "Block"); RC_CHECK();5328 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5329 rc = CFGMR3InsertString(pCfg, "Type", "Floppy 1.44"); RC_CHECK();5330 rc = CFGMR3InsertInteger(pCfg, "Mountable", 1); RC_CHECK();5331 }5332 }5333 }5334 5335 /*5336 * i8254 Programmable Interval Timer And Dummy Speaker5337 */5338 rc = CFGMR3InsertNode(pDevices, "i8254", &pDev); RC_CHECK();5339 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();5340 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5341 #ifdef DEBUG5342 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();5343 #endif5344 5345 /*5346 * i8259 Programmable Interrupt Controller.5347 */5348 rc = CFGMR3InsertNode(pDevices, "i8259", &pDev); RC_CHECK();5349 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();5350 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();5351 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5352 5353 /*5354 * Advanced Programmable Interrupt Controller.5355 */5356 rc = CFGMR3InsertNode(pDevices, "apic", &pDev); RC_CHECK();5357 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();5358 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();5359 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5360 rc = CFGMR3InsertInteger(pCfg, "IOAPIC", fIOAPIC); RC_CHECK();5361 5362 if (fIOAPIC)5363 {5364 /*5365 * I/O Advanced Programmable Interrupt Controller.5366 */5367 rc = CFGMR3InsertNode(pDevices, "ioapic", &pDev); RC_CHECK();5368 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();5369 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();5370 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5371 }5372 5373 /*5374 * RTC MC146818.5375 */5376 rc = CFGMR3InsertNode(pDevices, "mc146818", &pDev); RC_CHECK();5377 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();5378 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5379 5380 /*5381 * VGA.5382 */5383 rc = CFGMR3InsertNode(pDevices, "vga", &pDev); RC_CHECK();5384 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();5385 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();5386 rc = CFGMR3InsertInteger(pInst, "PCIDeviceNo", 2); RC_CHECK();5387 rc = CFGMR3InsertInteger(pInst, "PCIFunctionNo", 0); RC_CHECK();5388 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5389 hrc = pMachine->COMGETTER(VRAMSize)(&cRamMBs); H();5390 rc = CFGMR3InsertInteger(pCfg, "VRamSize", cRamMBs * _1M); RC_CHECK();5391 5392 /* Custom VESA mode list */5393 unsigned cModes = 0;5394 for (unsigned iMode = 1; iMode <= 16; iMode++)5395 {5396 char szExtraDataKey[sizeof("CustomVideoModeXX")];5397 RTStrPrintf(szExtraDataKey, sizeof(szExtraDataKey), "CustomVideoMode%d", iMode);5398 hrc = pMachine->GetExtraData(Bstr(szExtraDataKey), &str); H();5399 if (!str || !*str)5400 break;5401 STR_CONV();5402 rc = CFGMR3InsertString(pCfg, szExtraDataKey, psz);5403 STR_FREE();5404 cModes++;5405 }5406 rc = CFGMR3InsertInteger(pCfg, "CustomVideoModes", cModes);5407 5408 /* VESA height reduction */5409 ULONG ulHeightReduction;5410 IFramebuffer *pFramebuffer = pConsole->getDisplay()->getFramebuffer();5411 if (pFramebuffer)5412 {5413 hrc = pFramebuffer->COMGETTER(HeightReduction)(&ulHeightReduction); H();5414 }5415 else5416 {5417 /* If framebuffer is not available, there is no height reduction. */5418 ulHeightReduction = 0;5419 }5420 rc = CFGMR3InsertInteger(pCfg, "HeightReduction", ulHeightReduction); RC_CHECK();5421 5422 /* Attach the display. */5423 rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); RC_CHECK();5424 rc = CFGMR3InsertString(pLunL0, "Driver", "MainDisplay"); RC_CHECK();5425 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5426 Display *pDisplay = pConsole->mDisplay;5427 rc = CFGMR3InsertInteger(pCfg, "Object", (uintptr_t)pDisplay); RC_CHECK();5428 5429 /*5430 * IDE (update this when the main interface changes)5431 */5432 rc = CFGMR3InsertNode(pDevices, "piix3ide", &pDev); /* piix3 */ RC_CHECK();5433 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();5434 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();5435 rc = CFGMR3InsertInteger(pInst, "PCIDeviceNo", 1); RC_CHECK();5436 rc = CFGMR3InsertInteger(pInst, "PCIFunctionNo", 1); RC_CHECK();5437 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5438 5439 /* Attach the status driver */5440 rc = CFGMR3InsertNode(pInst, "LUN#999", &pLunL0); RC_CHECK();5441 rc = CFGMR3InsertString(pLunL0, "Driver", "MainStatus"); RC_CHECK();5442 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5443 rc = CFGMR3InsertInteger(pCfg, "papLeds", (uintptr_t)&pConsole->mapIDELeds[0]);RC_CHECK();5444 rc = CFGMR3InsertInteger(pCfg, "First", 0); RC_CHECK();5445 rc = CFGMR3InsertInteger(pCfg, "Last", 3); RC_CHECK();5446 5447 /* Attach the harddisks */5448 ComPtr<IHardDiskAttachmentCollection> hdaColl;5449 hrc = pMachine->COMGETTER(HardDiskAttachments)(hdaColl.asOutParam()); H();5450 ComPtr<IHardDiskAttachmentEnumerator> hdaEnum;5451 hrc = hdaColl->Enumerate(hdaEnum.asOutParam()); H();5452 5453 BOOL fMore = FALSE;5454 while ( SUCCEEDED(hrc = hdaEnum->HasMore(&fMore))5455 && fMore)5456 {5457 ComPtr<IHardDiskAttachment> hda;5458 hrc = hdaEnum->GetNext(hda.asOutParam()); H();5459 ComPtr<IHardDisk> hardDisk;5460 hrc = hda->COMGETTER(HardDisk)(hardDisk.asOutParam()); H();5461 DiskControllerType_T enmCtl;5462 hrc = hda->COMGETTER(Controller)(&enmCtl); H();5463 LONG lDev;5464 hrc = hda->COMGETTER(DeviceNumber)(&lDev); H();5465 5466 switch (enmCtl)5467 {5468 case DiskControllerType_IDE0Controller:5469 i = 0;5470 break;5471 case DiskControllerType_IDE1Controller:5472 i = 2;5473 break;5474 default:5475 AssertMsgFailed(("invalid disk controller type: %d\n", enmCtl));5476 return VERR_GENERAL_FAILURE;5477 }5478 5479 if (lDev < 0 || lDev >= 2)5480 {5481 AssertMsgFailed(("invalid controller device number: %d\n", lDev));5482 return VERR_GENERAL_FAILURE;5483 }5484 5485 i = i + lDev;5486 5487 char szLUN[16];5488 RTStrPrintf(szLUN, sizeof(szLUN), "LUN#%d", i);5489 rc = CFGMR3InsertNode(pInst, szLUN, &pLunL0); RC_CHECK();5490 rc = CFGMR3InsertString(pLunL0, "Driver", "Block"); RC_CHECK();5491 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5492 rc = CFGMR3InsertString(pCfg, "Type", "HardDisk"); RC_CHECK();5493 rc = CFGMR3InsertInteger(pCfg, "Mountable", 0); RC_CHECK();5494 5495 HardDiskStorageType_T hddType;5496 hardDisk->COMGETTER(StorageType)(&hddType);5497 if (hddType == HardDiskStorageType_VirtualDiskImage)5498 {5499 ComPtr<IVirtualDiskImage> vdiDisk = hardDisk;5500 AssertBreak (!vdiDisk.isNull(), hrc = E_FAIL);5501 5502 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); RC_CHECK();5503 rc = CFGMR3InsertString(pLunL1, "Driver", "VBoxHDD"); RC_CHECK();5504 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK();5505 hrc = vdiDisk->COMGETTER(FilePath)(&str); H();5506 STR_CONV();5507 rc = CFGMR3InsertString(pCfg, "Path", psz); RC_CHECK();5508 STR_FREE();5509 5510 /* Create an inversed tree of parents. */5511 ComPtr<IHardDisk> parentHardDisk = hardDisk;5512 for (PCFGMNODE pParent = pCfg;;)5513 {5514 ComPtr<IHardDisk> curHardDisk;5515 hrc = parentHardDisk->COMGETTER(Parent)(curHardDisk.asOutParam()); H();5516 if (!curHardDisk)5517 break;5518 5519 vdiDisk = curHardDisk;5520 AssertBreak (!vdiDisk.isNull(), hrc = E_FAIL);5521 5522 PCFGMNODE pCur;5523 rc = CFGMR3InsertNode(pParent, "Parent", &pCur); RC_CHECK();5524 hrc = vdiDisk->COMGETTER(FilePath)(&str); H();5525 STR_CONV();5526 rc = CFGMR3InsertString(pCur, "Path", psz); RC_CHECK();5527 STR_FREE();5528 rc = CFGMR3InsertInteger(pCur, "ReadOnly", 1); RC_CHECK();5529 5530 /* next */5531 pParent = pCur;5532 parentHardDisk = curHardDisk;5533 }5534 }5535 else if (hddType == HardDiskStorageType_ISCSIHardDisk)5536 {5537 ComPtr<IISCSIHardDisk> iSCSIDisk = hardDisk;5538 AssertBreak (!iSCSIDisk.isNull(), hrc = E_FAIL);5539 5540 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); RC_CHECK();5541 rc = CFGMR3InsertString(pLunL1, "Driver", "iSCSI"); RC_CHECK();5542 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK();5543 5544 /* Set up the iSCSI initiator driver configuration. */5545 hrc = iSCSIDisk->COMGETTER(Target)(&str); H();5546 STR_CONV();5547 rc = CFGMR3InsertString(pCfg, "TargetName", psz); RC_CHECK();5548 STR_FREE();5549 5550 // @todo currently there is no Initiator name config.5551 rc = CFGMR3InsertString(pCfg, "InitiatorName", "iqn.2006-02.de.innotek.initiator"); RC_CHECK();5552 5553 ULONG64 lun;5554 hrc = iSCSIDisk->COMGETTER(Lun)(&lun); H();5555 rc = CFGMR3InsertInteger(pCfg, "LUN", lun); RC_CHECK();5556 5557 hrc = iSCSIDisk->COMGETTER(Server)(&str); H();5558 STR_CONV();5559 USHORT port;5560 hrc = iSCSIDisk->COMGETTER(Port)(&port); H();5561 if (port != 0)5562 {5563 char *pszTN;5564 RTStrAPrintf(&pszTN, "%s:%u", psz, port);5565 rc = CFGMR3InsertString(pCfg, "TargetAddress", pszTN); RC_CHECK();5566 RTStrFree(pszTN);5567 }5568 else5569 {5570 rc = CFGMR3InsertString(pCfg, "TargetAddress", psz); RC_CHECK();5571 }5572 STR_FREE();5573 5574 hrc = iSCSIDisk->COMGETTER(UserName)(&str); H();5575 if (str)5576 {5577 STR_CONV();5578 rc = CFGMR3InsertString(pCfg, "InitiatorUsername", psz); RC_CHECK();5579 STR_FREE();5580 }5581 5582 hrc = iSCSIDisk->COMGETTER(Password)(&str); H();5583 if (str)5584 {5585 STR_CONV();5586 rc = CFGMR3InsertString(pCfg, "InitiatorSecret", psz); RC_CHECK();5587 STR_FREE();5588 }5589 5590 // @todo currently there is no target username config.5591 //rc = CFGMR3InsertString(pCfg, "TargetUsername", ""); RC_CHECK();5592 5593 // @todo currently there is no target password config.5594 //rc = CFGMR3InsertString(pCfg, "TargetSecret", ""); RC_CHECK();5595 5596 /* The iSCSI initiator needs an attached iSCSI transport driver. */5597 rc = CFGMR3InsertNode(pLunL1, "AttachedDriver", &pLunL2); RC_CHECK();5598 rc = CFGMR3InsertString(pLunL2, "Driver", "iSCSITCP"); RC_CHECK();5599 /* Currently the transport driver has no config options. */5600 }5601 else if (hddType == HardDiskStorageType_VMDKImage)5602 {5603 ComPtr<IVMDKImage> vmdkDisk = hardDisk;5604 AssertBreak (!vmdkDisk.isNull(), hrc = E_FAIL);5605 5606 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); RC_CHECK();5607 #if 1 /* Enable new VD container code (and new VMDK), as the bugs are fixed. */5608 rc = CFGMR3InsertString(pLunL1, "Driver", "VD"); RC_CHECK();5609 #else5610 rc = CFGMR3InsertString(pLunL1, "Driver", "VmdkHDD"); RC_CHECK();5611 #endif5612 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK();5613 hrc = vmdkDisk->COMGETTER(FilePath)(&str); H();5614 STR_CONV();5615 rc = CFGMR3InsertString(pCfg, "Path", psz); RC_CHECK();5616 STR_FREE();5617 }5618 else5619 AssertFailed();5620 }5621 H();5622 5623 ComPtr<IDVDDrive> dvdDrive;5624 hrc = pMachine->COMGETTER(DVDDrive)(dvdDrive.asOutParam()); H();5625 if (dvdDrive)5626 {5627 // ASSUME: DVD drive is always attached to LUN#2 (i.e. secondary IDE master)5628 rc = CFGMR3InsertNode(pInst, "LUN#2", &pLunL0); RC_CHECK();5629 ComPtr<IHostDVDDrive> hostDvdDrive;5630 hrc = dvdDrive->GetHostDrive(hostDvdDrive.asOutParam()); H();5631 if (hostDvdDrive)5632 {5633 pConsole->meDVDState = DriveState_HostDriveCaptured;5634 rc = CFGMR3InsertString(pLunL0, "Driver", "HostDVD"); RC_CHECK();5635 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5636 hrc = hostDvdDrive->COMGETTER(Name)(&str); H();5637 STR_CONV();5638 rc = CFGMR3InsertString(pCfg, "Path", psz); RC_CHECK();5639 STR_FREE();5640 BOOL fPassthrough;5641 hrc = dvdDrive->COMGETTER(Passthrough)(&fPassthrough); H();5642 rc = CFGMR3InsertInteger(pCfg, "Passthrough", !!fPassthrough); RC_CHECK();5643 }5644 else5645 {5646 pConsole->meDVDState = DriveState_NotMounted;5647 rc = CFGMR3InsertString(pLunL0, "Driver", "Block"); RC_CHECK();5648 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5649 rc = CFGMR3InsertString(pCfg, "Type", "DVD"); RC_CHECK();5650 rc = CFGMR3InsertInteger(pCfg, "Mountable", 1); RC_CHECK();5651 5652 ComPtr<IDVDImage> dvdImage;5653 hrc = dvdDrive->GetImage(dvdImage.asOutParam()); H();5654 if (dvdImage)5655 {5656 pConsole->meDVDState = DriveState_ImageMounted;5657 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); RC_CHECK();5658 rc = CFGMR3InsertString(pLunL1, "Driver", "MediaISO"); RC_CHECK();5659 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK();5660 hrc = dvdImage->COMGETTER(FilePath)(&str); H();5661 STR_CONV();5662 rc = CFGMR3InsertString(pCfg, "Path", psz); RC_CHECK();5663 STR_FREE();5664 }5665 }5666 }5667 5668 /*5669 * Network adapters5670 */5671 rc = CFGMR3InsertNode(pDevices, "pcnet", &pDev); RC_CHECK();5672 //rc = CFGMR3InsertNode(pDevices, "ne2000", &pDev); RC_CHECK();5673 for (ULONG ulInstance = 0; ulInstance < SchemaDefs::NetworkAdapterCount; ulInstance++)5674 {5675 ComPtr<INetworkAdapter> networkAdapter;5676 hrc = pMachine->GetNetworkAdapter(ulInstance, networkAdapter.asOutParam()); H();5677 BOOL fEnabled = FALSE;5678 hrc = networkAdapter->COMGETTER(Enabled)(&fEnabled); H();5679 if (!fEnabled)5680 continue;5681 5682 char szInstance[4]; Assert(ulInstance <= 999);5683 RTStrPrintf(szInstance, sizeof(szInstance), "%lu", ulInstance);5684 rc = CFGMR3InsertNode(pDev, szInstance, &pInst); RC_CHECK();5685 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();5686 /* the first network card gets the PCI ID 3, the followings starting from 8 */5687 rc = CFGMR3InsertInteger(pInst, "PCIDeviceNo", !ulInstance ? 3 : ulInstance - 1 + 8); RC_CHECK();5688 rc = CFGMR3InsertInteger(pInst, "PCIFunctionNo", 0); RC_CHECK();5689 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5690 5691 /*5692 * The virtual hardware type.5693 */5694 NetworkAdapterType_T adapterType;5695 hrc = networkAdapter->COMGETTER(AdapterType)(&adapterType); H();5696 switch (adapterType)5697 {5698 case NetworkAdapterType_NetworkAdapterAm79C970A:5699 rc = CFGMR3InsertInteger(pCfg, "Am79C973", 0); RC_CHECK();5700 break;5701 case NetworkAdapterType_NetworkAdapterAm79C973:5702 rc = CFGMR3InsertInteger(pCfg, "Am79C973", 1); RC_CHECK();5703 break;5704 default:5705 AssertMsgFailed(("Invalid network adapter type '%d' for slot '%d'",5706 adapterType, ulInstance));5707 return VERR_GENERAL_FAILURE;5708 }5709 5710 /*5711 * Get the MAC address and convert it to binary representation5712 */5713 Bstr macAddr;5714 hrc = networkAdapter->COMGETTER(MACAddress)(macAddr.asOutParam()); H();5715 Assert(macAddr);5716 Utf8Str macAddrUtf8 = macAddr;5717 char *macStr = (char*)macAddrUtf8.raw();5718 Assert(strlen(macStr) == 12);5719 PDMMAC Mac;5720 memset(&Mac, 0, sizeof(Mac));5721 char *pMac = (char*)&Mac;5722 for (uint32_t i = 0; i < 6; i++)5723 {5724 char c1 = *macStr++ - '0';5725 if (c1 > 9)5726 c1 -= 7;5727 char c2 = *macStr++ - '0';5728 if (c2 > 9)5729 c2 -= 7;5730 *pMac++ = ((c1 & 0x0f) << 4) | (c2 & 0x0f);5731 }5732 rc = CFGMR3InsertBytes(pCfg, "MAC", &Mac, sizeof(Mac)); RC_CHECK();5733 5734 /*5735 * Check if the cable is supposed to be unplugged5736 */5737 BOOL fCableConnected;5738 hrc = networkAdapter->COMGETTER(CableConnected)(&fCableConnected); H();5739 rc = CFGMR3InsertInteger(pCfg, "CableConnected", fCableConnected ? 1 : 0); RC_CHECK();5740 5741 /*5742 * Attach the status driver.5743 */5744 rc = CFGMR3InsertNode(pInst, "LUN#999", &pLunL0); RC_CHECK();5745 rc = CFGMR3InsertString(pLunL0, "Driver", "MainStatus"); RC_CHECK();5746 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5747 rc = CFGMR3InsertInteger(pCfg, "papLeds", (uintptr_t)&pConsole->mapNetworkLeds[ulInstance]); RC_CHECK();5748 5749 /*5750 * Enable the packet sniffer if requested.5751 */5752 BOOL fSniffer;5753 hrc = networkAdapter->COMGETTER(TraceEnabled)(&fSniffer); H();5754 if (fSniffer)5755 {5756 /* insert the sniffer filter driver. */5757 rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); RC_CHECK();5758 rc = CFGMR3InsertString(pLunL0, "Driver", "NetSniffer"); RC_CHECK();5759 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5760 hrc = networkAdapter->COMGETTER(TraceFile)(&str); H();5761 if (str) /* check convention for indicating default file. */5762 {5763 STR_CONV();5764 rc = CFGMR3InsertString(pCfg, "File", psz); RC_CHECK();5765 STR_FREE();5766 }5767 }5768 5769 NetworkAttachmentType_T networkAttachment;5770 hrc = networkAdapter->COMGETTER(AttachmentType)(&networkAttachment); H();5771 switch (networkAttachment)5772 {5773 case NetworkAttachmentType_NoNetworkAttachment:5774 break;5775 5776 case NetworkAttachmentType_NATNetworkAttachment:5777 {5778 if (fSniffer)5779 {5780 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL0); RC_CHECK();5781 }5782 else5783 {5784 rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); RC_CHECK();5785 }5786 rc = CFGMR3InsertString(pLunL0, "Driver", "NAT"); RC_CHECK();5787 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5788 /* (Port forwarding goes here.) */5789 break;5790 }5791 5792 case NetworkAttachmentType_HostInterfaceNetworkAttachment:5793 {5794 /*5795 * Perform the attachment if required (don't return on error!)5796 */5797 hrc = pConsole->attachToHostInterface(networkAdapter);5798 if (SUCCEEDED(hrc))5799 {5800 #ifdef VBOX_WITH_UNIXY_TAP_NETWORKING5801 Assert (pConsole->maTapFD[ulInstance] >= 0);5802 if (pConsole->maTapFD[ulInstance] >= 0)5803 {5804 if (fSniffer)5805 {5806 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL0); RC_CHECK();5807 }5808 else5809 {5810 rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); RC_CHECK();5811 }5812 rc = CFGMR3InsertString(pLunL0, "Driver", "HostInterface"); RC_CHECK();5813 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5814 rc = CFGMR3InsertInteger(pCfg, "FileHandle", pConsole->maTapFD[ulInstance]); RC_CHECK();5815 }5816 #elif defined(RT_OS_WINDOWS)5817 if (fSniffer)5818 {5819 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL0); RC_CHECK();5820 }5821 else5822 {5823 rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); RC_CHECK();5824 }5825 Bstr hostInterfaceName;5826 hrc = networkAdapter->COMGETTER(HostInterface)(hostInterfaceName.asOutParam()); H();5827 ComPtr<IHostNetworkInterfaceCollection> coll;5828 hrc = host->COMGETTER(NetworkInterfaces)(coll.asOutParam()); H();5829 ComPtr<IHostNetworkInterface> hostInterface;5830 rc = coll->FindByName(hostInterfaceName, hostInterface.asOutParam());5831 if (!SUCCEEDED(rc))5832 {5833 AssertMsgFailed(("Cannot get GUID for host interface '%ls'\n", hostInterfaceName));5834 hrc = networkAdapter->Detach(); H();5835 }5836 else5837 {5838 rc = CFGMR3InsertString(pLunL0, "Driver", "HostInterface"); RC_CHECK();5839 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5840 rc = CFGMR3InsertString(pCfg, "HostInterfaceName", Utf8Str(hostInterfaceName)); RC_CHECK();5841 Guid hostIFGuid;5842 hrc = hostInterface->COMGETTER(Id)(hostIFGuid.asOutParam()); H();5843 char szDriverGUID[256] = {0};5844 /* add curly brackets */5845 szDriverGUID[0] = '{';5846 strcpy(szDriverGUID + 1, hostIFGuid.toString().raw());5847 strcat(szDriverGUID, "}");5848 rc = CFGMR3InsertBytes(pCfg, "GUID", szDriverGUID, sizeof(szDriverGUID)); RC_CHECK();5849 }5850 #else5851 # error "Port me"5852 #endif5853 }5854 else5855 {5856 switch (hrc)5857 {5858 #ifdef RT_OS_LINUX5859 case VERR_ACCESS_DENIED:5860 return VMSetError(pVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(5861 "Failed to open '/dev/net/tun' for read/write access. Please check the "5862 "permissions of that node. Either do 'chmod 0666 /dev/net/tun' or "5863 "change the group of that node and get member of that group. Make "5864 "sure that these changes are permanently in particular if you are "5865 "using udev"));5866 #endif /* RT_OS_LINUX */5867 default:5868 AssertMsgFailed(("Could not attach to host interface! Bad!\n"));5869 return VMSetError(pVM, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS, N_(5870 "Failed to initialize Host Interface Networking"));5871 }5872 }5873 break;5874 }5875 5876 case NetworkAttachmentType_InternalNetworkAttachment:5877 {5878 hrc = networkAdapter->COMGETTER(InternalNetwork)(&str); H();5879 STR_CONV();5880 if (psz && *psz)5881 {5882 if (fSniffer)5883 {5884 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL0); RC_CHECK();5885 }5886 else5887 {5888 rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); RC_CHECK();5889 }5890 rc = CFGMR3InsertString(pLunL0, "Driver", "IntNet"); RC_CHECK();5891 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();5892 rc = CFGMR3InsertString(pCfg, "Network", psz); RC_CHECK();5893 }5894 STR_FREE();5895 break;5896 }5897 5898 default:5899 AssertMsgFailed(("should not get here!\n"));5900 break;5901 }5902 }5903 5904 /*5905 * Serial (UART) Ports5906 */5907 rc = CFGMR3InsertNode(pDevices, "serial", &pDev); RC_CHECK();5908 for (ULONG ulInstance = 0; ulInstance < SchemaDefs::SerialPortCount; ulInstance++)5909 {5910 ComPtr<ISerialPort> serialPort;5911 hrc = pMachine->GetSerialPort (ulInstance, serialPort.asOutParam()); H();5912 BOOL fEnabled = FALSE;5913 if (serialPort)5914 hrc = serialPort->COMGETTER(Enabled)(&fEnabled); H();5915 if (!fEnabled)5916 continue;5917 5918 char szInstance[4]; Assert(ulInstance <= 999);5919 RTStrPrintf(szInstance, sizeof(szInstance), "%lu", ulInstance);5920 5921 rc = CFGMR3InsertNode(pDev, szInstance, &pInst); RC_CHECK();5922 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5923 5924 ULONG ulIRQ, ulIOBase;5925 PortMode_T HostMode;5926 Bstr path;5927 BOOL fServer;5928 hrc = serialPort->COMGETTER(HostMode)(&HostMode); H();5929 hrc = serialPort->COMGETTER(IRQ)(&ulIRQ); H();5930 hrc = serialPort->COMGETTER(IOBase)(&ulIOBase); H();5931 hrc = serialPort->COMGETTER(Path)(path.asOutParam()); H();5932 hrc = serialPort->COMGETTER(Server)(&fServer); H();5933 rc = CFGMR3InsertInteger(pCfg, "IRQ", ulIRQ); RC_CHECK();5934 rc = CFGMR3InsertInteger(pCfg, "IOBase", ulIOBase); RC_CHECK();5935 if (HostMode != PortMode_DisconnectedPort)5936 {5937 rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); RC_CHECK();5938 if (HostMode == PortMode_HostPipePort)5939 {5940 rc = CFGMR3InsertString(pLunL0, "Driver", "Char"); RC_CHECK();5941 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); RC_CHECK();5942 rc = CFGMR3InsertString(pLunL1, "Driver", "NamedPipe"); RC_CHECK();5943 rc = CFGMR3InsertNode(pLunL1, "Config", &pLunL2); RC_CHECK();5944 rc = CFGMR3InsertString(pLunL2, "Location", Utf8Str(path)); RC_CHECK();5945 rc = CFGMR3InsertInteger(pLunL2, "IsServer", fServer); RC_CHECK();5946 }5947 else if (HostMode == PortMode_HostDevicePort)5948 {5949 rc = CFGMR3InsertString(pLunL0, "Driver", "Host Serial"); RC_CHECK();5950 rc = CFGMR3InsertNode(pLunL0, "Config", &pLunL1); RC_CHECK();5951 rc = CFGMR3InsertString(pLunL1, "DevicePath", Utf8Str(path)); RC_CHECK();5952 }5953 }5954 }5955 5956 /*5957 * Parallel (LPT) Ports5958 */5959 rc = CFGMR3InsertNode(pDevices, "parallel", &pDev); RC_CHECK();5960 for (ULONG ulInstance = 0; ulInstance < SchemaDefs::ParallelPortCount; ulInstance++)5961 {5962 ComPtr<IParallelPort> parallelPort;5963 hrc = pMachine->GetParallelPort (ulInstance, parallelPort.asOutParam()); H();5964 BOOL fEnabled = FALSE;5965 if (parallelPort)5966 hrc = parallelPort->COMGETTER(Enabled)(&fEnabled); H();5967 if (!fEnabled)5968 continue;5969 5970 char szInstance[4]; Assert(ulInstance <= 999);5971 RTStrPrintf(szInstance, sizeof(szInstance), "%lu", ulInstance);5972 5973 rc = CFGMR3InsertNode(pDev, szInstance, &pInst); RC_CHECK();5974 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5975 5976 ULONG ulIRQ, ulIOBase;5977 Bstr DevicePath;5978 hrc = parallelPort->COMGETTER(IRQ)(&ulIRQ); H();5979 hrc = parallelPort->COMGETTER(IOBase)(&ulIOBase); H();5980 hrc = parallelPort->COMGETTER(Path)(DevicePath.asOutParam()); H();5981 rc = CFGMR3InsertInteger(pCfg, "IRQ", ulIRQ); RC_CHECK();5982 rc = CFGMR3InsertInteger(pCfg, "IOBase", ulIOBase); RC_CHECK();5983 rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); RC_CHECK();5984 rc = CFGMR3InsertString(pLunL0, "Driver", "HostParallel"); RC_CHECK();5985 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); RC_CHECK();5986 rc = CFGMR3InsertString(pLunL1, "DevicePath", Utf8Str(DevicePath)); RC_CHECK();5987 }5988 5989 /*5990 * VMM Device5991 */5992 rc = CFGMR3InsertNode(pDevices, "VMMDev", &pDev); RC_CHECK();5993 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();5994 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();5995 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();5996 rc = CFGMR3InsertInteger(pInst, "PCIDeviceNo", 4); RC_CHECK();5997 rc = CFGMR3InsertInteger(pInst, "PCIFunctionNo", 0); RC_CHECK();5998 5999 /* the VMM device's Main driver */6000 rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); RC_CHECK();6001 rc = CFGMR3InsertString(pLunL0, "Driver", "MainVMMDev"); RC_CHECK();6002 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();6003 VMMDev *pVMMDev = pConsole->mVMMDev;6004 rc = CFGMR3InsertInteger(pCfg, "Object", (uintptr_t)pVMMDev); RC_CHECK();6005 6006 /*6007 * Attach the status driver.6008 */6009 rc = CFGMR3InsertNode(pInst, "LUN#999", &pLunL0); RC_CHECK();6010 rc = CFGMR3InsertString(pLunL0, "Driver", "MainStatus"); RC_CHECK();6011 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();6012 rc = CFGMR3InsertInteger(pCfg, "papLeds", (uintptr_t)&pConsole->mapSharedFolderLed); RC_CHECK();6013 rc = CFGMR3InsertInteger(pCfg, "First", 0); RC_CHECK();6014 rc = CFGMR3InsertInteger(pCfg, "Last", 0); RC_CHECK();6015 6016 /*6017 * Audio Sniffer Device6018 */6019 rc = CFGMR3InsertNode(pDevices, "AudioSniffer", &pDev); RC_CHECK();6020 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();6021 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();6022 6023 /* the Audio Sniffer device's Main driver */6024 rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); RC_CHECK();6025 rc = CFGMR3InsertString(pLunL0, "Driver", "MainAudioSniffer"); RC_CHECK();6026 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();6027 AudioSniffer *pAudioSniffer = pConsole->mAudioSniffer;6028 rc = CFGMR3InsertInteger(pCfg, "Object", (uintptr_t)pAudioSniffer); RC_CHECK();6029 6030 /*6031 * AC'97 ICH audio6032 */6033 ComPtr<IAudioAdapter> audioAdapter;6034 hrc = pMachine->COMGETTER(AudioAdapter)(audioAdapter.asOutParam()); H();6035 BOOL enabled = FALSE;6036 if (audioAdapter)6037 {6038 hrc = audioAdapter->COMGETTER(Enabled)(&enabled); H();6039 }6040 if (enabled)6041 {6042 rc = CFGMR3InsertNode(pDevices, "ichac97", &pDev); /* ichac97 */6043 rc = CFGMR3InsertNode(pDev, "0", &pInst);6044 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();6045 rc = CFGMR3InsertInteger(pInst, "PCIDeviceNo", 5); RC_CHECK();6046 rc = CFGMR3InsertInteger(pInst, "PCIFunctionNo", 0); RC_CHECK();6047 rc = CFGMR3InsertNode(pInst, "Config", &pCfg);6048 6049 /* the Audio driver */6050 rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); RC_CHECK();6051 rc = CFGMR3InsertString(pLunL0, "Driver", "AUDIO"); RC_CHECK();6052 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();6053 AudioDriverType_T audioDriver;6054 hrc = audioAdapter->COMGETTER(AudioDriver)(&audioDriver); H();6055 switch (audioDriver)6056 {6057 case AudioDriverType_NullAudioDriver:6058 {6059 rc = CFGMR3InsertString(pCfg, "AudioDriver", "null"); RC_CHECK();6060 break;6061 }6062 #ifdef RT_OS_WINDOWS6063 #ifdef VBOX_WITH_WINMM6064 case AudioDriverType_WINMMAudioDriver:6065 {6066 rc = CFGMR3InsertString(pCfg, "AudioDriver", "winmm"); RC_CHECK();6067 break;6068 }6069 #endif6070 case AudioDriverType_DSOUNDAudioDriver:6071 {6072 rc = CFGMR3InsertString(pCfg, "AudioDriver", "dsound"); RC_CHECK();6073 break;6074 }6075 #endif /* RT_OS_WINDOWS */6076 #ifdef RT_OS_LINUX6077 case AudioDriverType_OSSAudioDriver:6078 {6079 rc = CFGMR3InsertString(pCfg, "AudioDriver", "oss"); RC_CHECK();6080 break;6081 }6082 # ifdef VBOX_WITH_ALSA6083 case AudioDriverType_ALSAAudioDriver:6084 {6085 rc = CFGMR3InsertString(pCfg, "AudioDriver", "alsa"); RC_CHECK();6086 break;6087 }6088 # endif6089 #endif /* RT_OS_LINUX */6090 #ifdef RT_OS_DARWIN6091 case AudioDriverType_CoreAudioDriver:6092 {6093 rc = CFGMR3InsertString(pCfg, "AudioDriver", "coreaudio"); RC_CHECK();6094 break;6095 }6096 #endif6097 }6098 }6099 6100 /*6101 * The USB Controller.6102 */6103 ComPtr<IUSBController> USBCtlPtr;6104 hrc = pMachine->COMGETTER(USBController)(USBCtlPtr.asOutParam());6105 if (USBCtlPtr)6106 {6107 BOOL fEnabled;6108 hrc = USBCtlPtr->COMGETTER(Enabled)(&fEnabled); H();6109 if (fEnabled)6110 {6111 rc = CFGMR3InsertNode(pDevices, "usb-ohci", &pDev); RC_CHECK();6112 rc = CFGMR3InsertNode(pDev, "0", &pInst); RC_CHECK();6113 rc = CFGMR3InsertNode(pInst, "Config", &pCfg); RC_CHECK();6114 rc = CFGMR3InsertInteger(pInst, "Trusted", 1); /* boolean */ RC_CHECK();6115 rc = CFGMR3InsertInteger(pInst, "PCIDeviceNo", 6); RC_CHECK();6116 rc = CFGMR3InsertInteger(pInst, "PCIFunctionNo", 0); RC_CHECK();6117 6118 rc = CFGMR3InsertNode(pInst, "LUN#0", &pLunL0); RC_CHECK();6119 rc = CFGMR3InsertString(pLunL0, "Driver", "VUSBRootHub"); RC_CHECK();6120 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();6121 6122 /*6123 * Attach the status driver.6124 */6125 rc = CFGMR3InsertNode(pInst, "LUN#999", &pLunL0); RC_CHECK();6126 rc = CFGMR3InsertString(pLunL0, "Driver", "MainStatus"); RC_CHECK();6127 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK();6128 rc = CFGMR3InsertInteger(pCfg, "papLeds", (uintptr_t)&pConsole->mapUSBLed);RC_CHECK();6129 rc = CFGMR3InsertInteger(pCfg, "First", 0); RC_CHECK();6130 rc = CFGMR3InsertInteger(pCfg, "Last", 0); RC_CHECK();6131 }6132 }6133 6134 /*6135 * Clipboard6136 */6137 {6138 ClipboardMode_T mode = ClipboardMode_ClipDisabled;6139 hrc = pMachine->COMGETTER(ClipboardMode) (&mode); H();6140 6141 if (mode != ClipboardMode_ClipDisabled)6142 {6143 /* Load the service */6144 rc = pConsole->mVMMDev->hgcmLoadService ("VBoxSharedClipboard", "VBoxSharedClipboard");6145 6146 if (VBOX_FAILURE (rc))6147 {6148 LogRel(("VBoxSharedClipboard is not available. rc = %Vrc\n", rc));6149 /* That is not a fatal failure. */6150 rc = VINF_SUCCESS;6151 }6152 else6153 {6154 /* Setup the service. */6155 VBOXHGCMSVCPARM parm;6156 6157 parm.type = VBOX_HGCM_SVC_PARM_32BIT;6158 6159 switch (mode)6160 {6161 default:6162 case ClipboardMode_ClipDisabled:6163 {6164 LogRel(("VBoxSharedClipboard mode: Off\n"));6165 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_OFF;6166 break;6167 }6168 case ClipboardMode_ClipGuestToHost:6169 {6170 LogRel(("VBoxSharedClipboard mode: Guest to Host\n"));6171 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_GUEST_TO_HOST;6172 break;6173 }6174 case ClipboardMode_ClipHostToGuest:6175 {6176 LogRel(("VBoxSharedClipboard mode: Host to Guest\n"));6177 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_HOST_TO_GUEST;6178 break;6179 }6180 case ClipboardMode_ClipBidirectional:6181 {6182 LogRel(("VBoxSharedClipboard mode: Bidirectional\n"));6183 parm.u.uint32 = VBOX_SHARED_CLIPBOARD_MODE_BIDIRECTIONAL;6184 break;6185 }6186 }6187 6188 pConsole->mVMMDev->hgcmHostCall ("VBoxSharedClipboard", VBOX_SHARED_CLIPBOARD_HOST_FN_SET_MODE, 1, &parm);6189 6190 Log(("Set VBoxSharedClipboard mode\n"));6191 }6192 }6193 }6194 6195 /*6196 * CFGM overlay handling.6197 *6198 * Here we check the extra data entries for CFGM values6199 * and create the nodes and insert the values on the fly. Existing6200 * values will be removed and reinserted. If a value is a valid number,6201 * it will be inserted as a number, otherwise as a string.6202 *6203 * We first perform a run on global extra data, then on the machine6204 * extra data to support global settings with local overrides.6205 *6206 */6207 Bstr strExtraDataKey;6208 bool fGlobalExtraData = true;6209 for (;;)6210 {6211 Bstr strNextExtraDataKey;6212 Bstr strExtraDataValue;6213 6214 /* get the next key */6215 if (fGlobalExtraData)6216 hrc = virtualBox->GetNextExtraDataKey(strExtraDataKey, strNextExtraDataKey.asOutParam(),6217 strExtraDataValue.asOutParam());6218 else6219 hrc = pMachine->GetNextExtraDataKey(strExtraDataKey, strNextExtraDataKey.asOutParam(),6220 strExtraDataValue.asOutParam());6221 6222 /* stop if for some reason there's nothing more to request */6223 if (FAILED(hrc) || !strNextExtraDataKey)6224 {6225 /* if we're out of global keys, continue with machine, otherwise we're done */6226 if (fGlobalExtraData)6227 {6228 fGlobalExtraData = false;6229 strExtraDataKey.setNull();6230 continue;6231 }6232 break;6233 }6234 6235 strExtraDataKey = strNextExtraDataKey;6236 Utf8Str strExtraDataKeyUtf8 = Utf8Str(strExtraDataKey);6237 6238 /* we only care about keys starting with "VBoxInternal/" */6239 if (strncmp(strExtraDataKeyUtf8.raw(), "VBoxInternal/", 13) != 0)6240 continue;6241 char *pszExtraDataKey = (char*)strExtraDataKeyUtf8.raw() + 13;6242 6243 /* the key will be in the format "Node1/Node2/Value" or simply "Value". */6244 PCFGMNODE pNode;6245 char *pszCFGMValueName = strrchr(pszExtraDataKey, '/');6246 if (pszCFGMValueName)6247 {6248 /* terminate the node and advance to the value */6249 *pszCFGMValueName = '\0';6250 pszCFGMValueName++;6251 6252 /* does the node already exist? */6253 pNode = CFGMR3GetChild(pRoot, pszExtraDataKey);6254 if (pNode)6255 {6256 /* the value might already exist, remove it to be safe */6257 CFGMR3RemoveValue(pNode, pszCFGMValueName);6258 }6259 else6260 {6261 /* create the node */6262 rc = CFGMR3InsertNode(pRoot, pszExtraDataKey, &pNode);6263 AssertMsgRC(rc, ("failed to insert node '%s'\n", pszExtraDataKey));6264 if (VBOX_FAILURE(rc) || !pNode)6265 continue;6266 }6267 }6268 else6269 {6270 pNode = pRoot;6271 pszCFGMValueName = pszExtraDataKey;6272 pszExtraDataKey--;6273 6274 /* the value might already exist, remove it to be safe */6275 CFGMR3RemoveValue(pNode, pszCFGMValueName);6276 }6277 6278 /* now let's have a look at the value */6279 Utf8Str strCFGMValueUtf8 = Utf8Str(strExtraDataValue);6280 const char *pszCFGMValue = strCFGMValueUtf8.raw();6281 /* empty value means remove value which we've already done */6282 if (pszCFGMValue && *pszCFGMValue)6283 {6284 /* if it's a valid number, we'll insert it as such, otherwise string */6285 uint64_t u64Value;6286 if (RTStrToUInt64Ex(pszCFGMValue, NULL, 0, &u64Value) == VINF_SUCCESS)6287 {6288 rc = CFGMR3InsertInteger(pNode, pszCFGMValueName, u64Value);6289 }6290 else6291 {6292 rc = CFGMR3InsertString(pNode, pszCFGMValueName, pszCFGMValue);6293 }6294 AssertMsgRC(rc, ("failed to insert CFGM value '%s' to key '%s'\n", pszCFGMValue, pszExtraDataKey));6295 }6296 }6297 6298 #undef H6299 #undef RC_CHECK6300 #undef STR_FREE6301 #undef STR_CONV6302 6303 /* Register VM state change handler */6304 int rc2 = VMR3AtStateRegister (pVM, Console::vmstateChangeCallback, pConsole);6305 AssertRC (rc2);6306 if (VBOX_SUCCESS (rc))6307 rc = rc2;6308 6309 /* Register VM runtime error handler */6310 rc2 = VMR3AtRuntimeErrorRegister (pVM, Console::setVMRuntimeErrorCallback, pConsole);6311 AssertRC (rc2);6312 if (VBOX_SUCCESS (rc))6313 rc = rc2;6314 6315 /* Save the VM pointer in the machine object */6316 pConsole->mpVM = pVM;6317 6318 LogFlowFunc (("vrc = %Vrc\n", rc));6319 LogFlowFuncLeave();6320 6321 return rc;6322 4943 } 6323 4944 … … 7234 5855 7235 5856 vrc = VMR3Create (task->mSetVMErrorCallback, task.get(), 7236 task->mConfigConstructor, task.get(),5857 task->mConfigConstructor, static_cast <Console *> (console), 7237 5858 &pVM); 7238 5859 -
trunk/src/VBox/Main/ConsoleImpl2.cpp
r4424 r4426 1 /** $Id$ */ 1 2 /** @file 3 * VBox Console COM Class implementation 2 4 * 3 * VBox Console COM Class implementation 5 * @remark We've split out the code that the 64-bit VC++ v8 compiler 6 * finds problematic to optimize so we can disable optimizations 7 * and later, perhaps, find a real solution for it. 4 8 */ 5 9 … … 16 20 */ 17 21 18 #if defined(RT_OS_WINDOWS) 19 #elif defined(RT_OS_LINUX) 20 # include <errno.h> 21 # include <sys/ioctl.h> 22 # include <sys/poll.h> 23 # include <sys/fcntl.h> 24 # include <sys/types.h> 25 # include <sys/wait.h> 26 # include <net/if.h> 27 # include <linux/if_tun.h> 28 # include <stdio.h> 29 # include <stdlib.h> 30 # include <string.h> 31 #endif 32 22 /******************************************************************************* 23 * Header Files * 24 *******************************************************************************/ 33 25 #include "ConsoleImpl.h" 34 #include "GuestImpl.h"35 #include "KeyboardImpl.h"36 #include "MouseImpl.h"37 26 #include "DisplayImpl.h" 38 #include "MachineDebuggerImpl.h"39 #include "USBDeviceImpl.h"40 #include "RemoteUSBDeviceImpl.h"41 #include "SharedFolderImpl.h"42 #include "AudioSnifferInterface.h"43 #include "ConsoleVRDPServer.h"44 27 #include "VMMDev.h" 45 28 … … 50 33 51 34 #include <iprt/string.h> 52 #include <iprt/asm.h>53 #include <iprt/file.h>54 35 #include <iprt/path.h> 55 36 #include <iprt/dir.h> 56 #include <iprt/process.h>57 #include <iprt/ldr.h>58 #include <iprt/cpputils.h>59 37 60 38 #include <VBox/vmapi.h> 61 39 #include <VBox/err.h> 62 #include <VBox/param.h>63 #include <VBox/vusb.h>64 #include <VBox/mm.h>65 #include <VBox/ssm.h>66 40 #include <VBox/version.h> 67 68 #include <VBox/VBoxDev.h>69 70 41 #include <VBox/HostServices/VBoxClipboardSvc.h> 71 42 72 #include <set> 73 #include <algorithm> 74 #include <memory> // for auto_ptr 75 76 77 // VMTask and friends 78 //////////////////////////////////////////////////////////////////////////////// 79 80 /** 81 * Task structure for asynchronous VM operations. 82 * 83 * Once created, the task structure adds itself as a Console caller. 84 * This means: 85 * 86 * 1. The user must check for #rc() before using the created structure 87 * (e.g. passing it as a thread function argument). If #rc() returns a 88 * failure, the Console object may not be used by the task (see 89 Console::addCaller() for more details). 90 * 2. On successful initialization, the structure keeps the Console caller 91 * until destruction (to ensure Console remains in the Ready state and won't 92 * be accidentially uninitialized). Forgetting to delete the created task 93 * will lead to Console::uninit() stuck waiting for releasing all added 94 * callers. 95 * 96 * If \a aUsesVMPtr parameter is true, the task structure will also add itself 97 * as a Console::mpVM caller with the same meaning as above. See 98 * Console::addVMCaller() for more info. 43 44 /* 45 * VC++ 8 / amd64 has some serious trouble with this function. 46 * As a temporary measure, we'll drop global optimizations. 99 47 */ 100 struct VMTask 101 { 102 VMTask (Console *aConsole, bool aUsesVMPtr) 103 : mConsole (aConsole), mCallerAdded (false), mVMCallerAdded (false) 104 { 105 AssertReturnVoid (aConsole); 106 mRC = aConsole->addCaller(); 107 if (SUCCEEDED (mRC)) 108 { 109 mCallerAdded = true; 110 if (aUsesVMPtr) 111 { 112 mRC = aConsole->addVMCaller(); 113 if (SUCCEEDED (mRC)) 114 mVMCallerAdded = true; 115 } 116 } 117 } 118 119 ~VMTask() 120 { 121 if (mVMCallerAdded) 122 mConsole->releaseVMCaller(); 123 if (mCallerAdded) 124 mConsole->releaseCaller(); 125 } 126 127 HRESULT rc() const { return mRC; } 128 bool isOk() const { return SUCCEEDED (rc()); } 129 130 /** Releases the Console caller before destruction. Not normally necessary. */ 131 void releaseCaller() 132 { 133 AssertReturnVoid (mCallerAdded); 134 mConsole->releaseCaller(); 135 mCallerAdded = false; 136 } 137 138 /** Releases the VM caller before destruction. Not normally necessary. */ 139 void releaseVMCaller() 140 { 141 AssertReturnVoid (mVMCallerAdded); 142 mConsole->releaseVMCaller(); 143 mVMCallerAdded = false; 144 } 145 146 const ComObjPtr <Console> mConsole; 147 148 private: 149 150 HRESULT mRC; 151 bool mCallerAdded : 1; 152 bool mVMCallerAdded : 1; 153 }; 154 155 struct VMProgressTask : public VMTask 156 { 157 VMProgressTask (Console *aConsole, Progress *aProgress, bool aUsesVMPtr) 158 : VMTask (aConsole, aUsesVMPtr), mProgress (aProgress) {} 159 160 const ComObjPtr <Progress> mProgress; 161 }; 162 163 struct VMPowerUpTask : public VMProgressTask 164 { 165 VMPowerUpTask (Console *aConsole, Progress *aProgress) 166 : VMProgressTask (aConsole, aProgress, false /* aUsesVMPtr */) 167 , mSetVMErrorCallback (NULL), mConfigConstructor (NULL) {} 168 169 PFNVMATERROR mSetVMErrorCallback; 170 PFNCFGMCONSTRUCTOR mConfigConstructor; 171 Utf8Str mSavedStateFile; 172 Console::SharedFolderDataMap mSharedFolders; 173 }; 174 175 struct VMSaveTask : public VMProgressTask 176 { 177 VMSaveTask (Console *aConsole, Progress *aProgress) 178 : VMProgressTask (aConsole, aProgress, true /* aUsesVMPtr */) 179 , mIsSnapshot (false) 180 , mLastMachineState (MachineState_InvalidMachineState) {} 181 182 bool mIsSnapshot; 183 Utf8Str mSavedStateFile; 184 MachineState_T mLastMachineState; 185 ComPtr <IProgress> mServerProgress; 186 }; 187 188 189 // constructor / desctructor 190 ///////////////////////////////////////////////////////////////////////////// 191 192 Console::Console() 193 : mSavedStateDataLoaded (false) 194 , mConsoleVRDPServer (NULL) 195 , mpVM (NULL) 196 , mVMCallers (0) 197 , mVMZeroCallersSem (NIL_RTSEMEVENT) 198 , mVMDestroying (false) 199 , meDVDState (DriveState_NotMounted) 200 , meFloppyState (DriveState_NotMounted) 201 , mVMMDev (NULL) 202 , mAudioSniffer (NULL) 203 , mVMStateChangeCallbackDisabled (false) 204 , mMachineState (MachineState_PoweredOff) 205 {} 206 207 Console::~Console() 208 {} 209 210 HRESULT Console::FinalConstruct() 211 { 212 LogFlowThisFunc (("\n")); 213 214 memset(mapFDLeds, 0, sizeof(mapFDLeds)); 215 memset(mapIDELeds, 0, sizeof(mapIDELeds)); 216 memset(mapNetworkLeds, 0, sizeof(mapNetworkLeds)); 217 memset(&mapUSBLed, 0, sizeof(mapUSBLed)); 218 memset(&mapSharedFolderLed, 0, sizeof(mapSharedFolderLed)); 219 220 #ifdef VBOX_WITH_UNIXY_TAP_NETWORKING 221 Assert(ELEMENTS(maTapFD) == ELEMENTS(maTAPDeviceName)); 222 Assert(ELEMENTS(maTapFD) >= SchemaDefs::NetworkAdapterCount); 223 for (unsigned i = 0; i < ELEMENTS(maTapFD); i++) 224 { 225 maTapFD[i] = NIL_RTFILE; 226 maTAPDeviceName[i] = ""; 227 } 48 #if defined(_MSC_VER) && defined(RT_ARCH_AMD64) 49 # pragma optimize("g", off) 228 50 #endif 229 230 return S_OK;231 }232 233 void Console::FinalRelease()234 {235 LogFlowThisFunc (("\n"));236 237 uninit();238 }239 240 // public initializer/uninitializer for internal purposes only241 /////////////////////////////////////////////////////////////////////////////242 243 HRESULT Console::init (IMachine *aMachine, IInternalMachineControl *aControl)244 {245 AssertReturn (aMachine && aControl, E_INVALIDARG);246 247 /* Enclose the state transition NotReady->InInit->Ready */248 AutoInitSpan autoInitSpan (this);249 AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);250 251 LogFlowThisFuncEnter();252 LogFlowThisFunc(("aMachine=%p, aControl=%p\n", aMachine, aControl));253 254 HRESULT rc = E_FAIL;255 256 unconst (mMachine) = aMachine;257 unconst (mControl) = aControl;258 259 memset (&mCallbackData, 0, sizeof (mCallbackData));260 261 /* Cache essential properties and objects */262 263 rc = mMachine->COMGETTER(State) (&mMachineState);264 AssertComRCReturnRC (rc);265 266 #ifdef VBOX_VRDP267 rc = mMachine->COMGETTER(VRDPServer) (unconst (mVRDPServer).asOutParam());268 AssertComRCReturnRC (rc);269 #endif270 271 rc = mMachine->COMGETTER(DVDDrive) (unconst (mDVDDrive).asOutParam());272 AssertComRCReturnRC (rc);273 274 rc = mMachine->COMGETTER(FloppyDrive) (unconst (mFloppyDrive).asOutParam());275 AssertComRCReturnRC (rc);276 277 /* Create associated child COM objects */278 279 unconst (mGuest).createObject();280 rc = mGuest->init (this);281 AssertComRCReturnRC (rc);282 283 unconst (mKeyboard).createObject();284 rc = mKeyboard->init (this);285 AssertComRCReturnRC (rc);286 287 unconst (mMouse).createObject();288 rc = mMouse->init (this);289 AssertComRCReturnRC (rc);290 291 unconst (mDisplay).createObject();292 rc = mDisplay->init (this);293 AssertComRCReturnRC (rc);294 295 unconst (mRemoteDisplayInfo).createObject();296 rc = mRemoteDisplayInfo->init (this);297 AssertComRCReturnRC (rc);298 299 /* Grab global and machine shared folder lists */300 301 rc = fetchSharedFolders (true /* aGlobal */);302 AssertComRCReturnRC (rc);303 rc = fetchSharedFolders (false /* aGlobal */);304 AssertComRCReturnRC (rc);305 306 /* Create other child objects */307 308 unconst (mConsoleVRDPServer) = new ConsoleVRDPServer (this);309 AssertReturn (mConsoleVRDPServer, E_FAIL);310 311 mcAudioRefs = 0;312 mcVRDPClients = 0;313 314 unconst (mVMMDev) = new VMMDev(this);315 AssertReturn (mVMMDev, E_FAIL);316 317 unconst (mAudioSniffer) = new AudioSniffer(this);318 AssertReturn (mAudioSniffer, E_FAIL);319 320 /* Confirm a successful initialization when it's the case */321 autoInitSpan.setSucceeded();322 323 LogFlowThisFuncLeave();324 325 return S_OK;326 }327 328 /**329 * Uninitializes the Console object.330 */331 void Console::uninit()332 {333 LogFlowThisFuncEnter();334 335 /* Enclose the state transition Ready->InUninit->NotReady */336 AutoUninitSpan autoUninitSpan (this);337 if (autoUninitSpan.uninitDone())338 {339 LogFlowThisFunc (("Already uninitialized.\n"));340 LogFlowThisFuncLeave();341 return;342 }343 344 LogFlowThisFunc (("initFailed()=%d\n", autoUninitSpan.initFailed()));345 346 /*347 * Uninit all children that ise addDependentChild()/removeDependentChild()348 * in their init()/uninit() methods.349 */350 uninitDependentChildren();351 352 /* This should be the first, since this may cause detaching remote USB devices. */353 if (mConsoleVRDPServer)354 {355 mConsoleVRDPServer->Stop ();356 /* Do not delete the mConsoleVRDPServer yet, it could be called357 * since the VM may be not powered done at the moment.358 */359 }360 361 /* power down the VM if necessary */362 if (mpVM)363 {364 powerDown();365 Assert (mpVM == NULL);366 }367 368 if (mVMZeroCallersSem != NIL_RTSEMEVENT)369 {370 RTSemEventDestroy (mVMZeroCallersSem);371 mVMZeroCallersSem = NIL_RTSEMEVENT;372 }373 374 if (mAudioSniffer)375 {376 delete mAudioSniffer;377 unconst (mAudioSniffer) = NULL;378 }379 380 if (mVMMDev)381 {382 delete mVMMDev;383 unconst (mVMMDev) = NULL;384 }385 386 mGlobalSharedFolders.clear();387 mMachineSharedFolders.clear();388 389 mSharedFolders.clear();390 mRemoteUSBDevices.clear();391 mUSBDevices.clear();392 393 if (mRemoteDisplayInfo)394 {395 mRemoteDisplayInfo->uninit();396 unconst (mRemoteDisplayInfo).setNull();;397 }398 399 if (mDebugger)400 {401 mDebugger->uninit();402 unconst (mDebugger).setNull();403 }404 405 if (mDisplay)406 {407 mDisplay->uninit();408 unconst (mDisplay).setNull();409 }410 411 if (mMouse)412 {413 mMouse->uninit();414 unconst (mMouse).setNull();415 }416 417 if (mKeyboard)418 {419 mKeyboard->uninit();420 unconst (mKeyboard).setNull();;421 }422 423 if (mGuest)424 {425 mGuest->uninit();426 unconst (mGuest).setNull();;427 }428 429 if (mConsoleVRDPServer)430 {431 delete mConsoleVRDPServer;432 unconst (mConsoleVRDPServer) = NULL;433 }434 435 unconst (mFloppyDrive).setNull();436 unconst (mDVDDrive).setNull();437 #ifdef VBOX_VRDP438 unconst (mVRDPServer).setNull();439 #endif440 441 unconst (mControl).setNull();442 unconst (mMachine).setNull();443 444 /* Release all callbacks. Do this after uninitializing the components,445 * as some of them are well-behaved and unregister their callbacks.446 * These would trigger error messages complaining about trying to447 * unregister a non-registered callback. */448 mCallbacks.clear();449 450 /* dynamically allocated members of mCallbackData are uninitialized451 * at the end of powerDown() */452 Assert (!mCallbackData.mpsc.valid && mCallbackData.mpsc.shape == NULL);453 Assert (!mCallbackData.mcc.valid);454 Assert (!mCallbackData.klc.valid);455 456 LogFlowThisFuncLeave();457 }458 459 #ifdef VRDP_NO_COM460 int Console::VRDPClientLogon (uint32_t u32ClientId, const char *pszUser, const char *pszPassword, const char *pszDomain)461 #else462 DECLCALLBACK(int) Console::vrdp_ClientLogon (void *pvUser,463 uint32_t u32ClientId,464 const char *pszUser,465 const char *pszPassword,466 const char *pszDomain)467 #endif /* VRDP_NO_COM */468 {469 LogFlowFuncEnter();470 LogFlowFunc (("%d, %s, %s, %s\n", u32ClientId, pszUser, pszPassword, pszDomain));471 472 #ifdef VRDP_NO_COM473 Console *console = this;474 #else475 Console *console = static_cast <Console *> (pvUser);476 #endif /* VRDP_NO_COM */477 AssertReturn (console, VERR_INVALID_POINTER);478 479 AutoCaller autoCaller (console);480 if (!autoCaller.isOk())481 {482 /* Console has been already uninitialized, deny request */483 LogRel(("VRDPAUTH: Access denied (Console uninitialized).\n"));484 LogFlowFuncLeave();485 return VERR_ACCESS_DENIED;486 }487 488 Guid uuid;489 HRESULT hrc = console->mMachine->COMGETTER (Id) (uuid.asOutParam());490 AssertComRCReturn (hrc, VERR_ACCESS_DENIED);491 492 VRDPAuthType_T authType = VRDPAuthType_VRDPAuthNull;493 hrc = console->mVRDPServer->COMGETTER(AuthType) (&authType);494 AssertComRCReturn (hrc, VERR_ACCESS_DENIED);495 496 ULONG authTimeout = 0;497 hrc = console->mVRDPServer->COMGETTER(AuthTimeout) (&authTimeout);498 AssertComRCReturn (hrc, VERR_ACCESS_DENIED);499 500 VRDPAuthResult result = VRDPAuthAccessDenied;501 VRDPAuthGuestJudgement guestJudgement = VRDPAuthGuestNotAsked;502 503 LogFlowFunc(("Auth type %d\n", authType));504 505 LogRel (("VRDPAUTH: User: [%s]. Domain: [%s]. Authentication type: [%s]\n",506 pszUser, pszDomain,507 authType == VRDPAuthType_VRDPAuthNull?508 "null":509 (authType == VRDPAuthType_VRDPAuthExternal?510 "external":511 (authType == VRDPAuthType_VRDPAuthGuest?512 "guest":513 "INVALID"514 )515 )516 ));517 518 /* Multiconnection check. */519 BOOL allowMultiConnection = FALSE;520 hrc = console->mVRDPServer->COMGETTER(AllowMultiConnection) (&allowMultiConnection);521 AssertComRCReturn (hrc, VERR_ACCESS_DENIED);522 523 LogFlowFunc(("allowMultiConnection %d, console->mcVRDPClients = %d\n", allowMultiConnection, console->mcVRDPClients));524 525 if (allowMultiConnection == FALSE)526 {527 /* Note: the variable is incremented in ClientConnect callback, which is called when the client528 * is successfully connected, that is after the ClientLogon callback. Therefore the mcVRDPClients529 * value is 0 for first client.530 */531 if (console->mcVRDPClients > 0)532 {533 /* Reject. */534 LogRel(("VRDPAUTH: Multiple connections are not enabled. Access denied.\n"));535 return VERR_ACCESS_DENIED;536 }537 }538 539 switch (authType)540 {541 case VRDPAuthType_VRDPAuthNull:542 {543 result = VRDPAuthAccessGranted;544 break;545 }546 547 case VRDPAuthType_VRDPAuthExternal:548 {549 /* Call the external library. */550 result = console->mConsoleVRDPServer->Authenticate (uuid, guestJudgement, pszUser, pszPassword, pszDomain, u32ClientId);551 552 if (result != VRDPAuthDelegateToGuest)553 {554 break;555 }556 557 LogRel(("VRDPAUTH: Delegated to guest.\n"));558 559 LogFlowFunc (("External auth asked for guest judgement\n"));560 } /* pass through */561 562 case VRDPAuthType_VRDPAuthGuest:563 {564 guestJudgement = VRDPAuthGuestNotReacted;565 566 if (console->mVMMDev)567 {568 /* Issue the request to guest. Assume that the call does not require EMT. It should not. */569 570 /* Ask the guest to judge these credentials. */571 uint32_t u32GuestFlags = VMMDEV_SETCREDENTIALS_JUDGE;572 573 int rc = console->mVMMDev->getVMMDevPort()->pfnSetCredentials (console->mVMMDev->getVMMDevPort(),574 pszUser, pszPassword, pszDomain, u32GuestFlags);575 576 if (VBOX_SUCCESS (rc))577 {578 /* Wait for guest. */579 rc = console->mVMMDev->WaitCredentialsJudgement (authTimeout, &u32GuestFlags);580 581 if (VBOX_SUCCESS (rc))582 {583 switch (u32GuestFlags & (VMMDEV_CREDENTIALS_JUDGE_OK | VMMDEV_CREDENTIALS_JUDGE_DENY | VMMDEV_CREDENTIALS_JUDGE_NOJUDGEMENT))584 {585 case VMMDEV_CREDENTIALS_JUDGE_DENY: guestJudgement = VRDPAuthGuestAccessDenied; break;586 case VMMDEV_CREDENTIALS_JUDGE_NOJUDGEMENT: guestJudgement = VRDPAuthGuestNoJudgement; break;587 case VMMDEV_CREDENTIALS_JUDGE_OK: guestJudgement = VRDPAuthGuestAccessGranted; break;588 default:589 LogFlowFunc (("Invalid guest flags %08X!!!\n", u32GuestFlags)); break;590 }591 }592 else593 {594 LogFlowFunc (("Wait for credentials judgement rc = %Vrc!!!\n", rc));595 }596 597 LogFlowFunc (("Guest judgement %d\n", guestJudgement));598 }599 else600 {601 LogFlowFunc (("Could not set credentials rc = %Vrc!!!\n", rc));602 }603 }604 605 if (authType == VRDPAuthType_VRDPAuthExternal)606 {607 LogRel(("VRDPAUTH: Guest judgement %d.\n", guestJudgement));608 LogFlowFunc (("External auth called again with guest judgement = %d\n", guestJudgement));609 result = console->mConsoleVRDPServer->Authenticate (uuid, guestJudgement, pszUser, pszPassword, pszDomain, u32ClientId);610 }611 else612 {613 switch (guestJudgement)614 {615 case VRDPAuthGuestAccessGranted:616 result = VRDPAuthAccessGranted;617 break;618 default:619 result = VRDPAuthAccessDenied;620 break;621 }622 }623 } break;624 625 default:626 AssertFailed();627 }628 629 LogFlowFunc (("Result = %d\n", result));630 LogFlowFuncLeave();631 632 if (result == VRDPAuthAccessGranted)633 {634 LogRel(("VRDPAUTH: Access granted.\n"));635 return VINF_SUCCESS;636 }637 638 /* Reject. */639 LogRel(("VRDPAUTH: Access denied.\n"));640 return VERR_ACCESS_DENIED;641 }642 643 #ifdef VRDP_NO_COM644 void Console::VRDPClientConnect (uint32_t u32ClientId)645 #else646 DECLCALLBACK(void) Console::vrdp_ClientConnect (void *pvUser,647 uint32_t u32ClientId)648 #endif /* VRDP_NO_COM */649 {650 LogFlowFuncEnter();651 652 #ifdef VRDP_NO_COM653 Console *console = this;654 #else655 Console *console = static_cast <Console *> (pvUser);656 #endif /* VRDP_NO_COM */657 AssertReturnVoid (console);658 659 AutoCaller autoCaller (console);660 AssertComRCReturnVoid (autoCaller.rc());661 662 #ifdef VBOX_VRDP663 ASMAtomicIncU32(&console->mcVRDPClients);664 665 NOREF(u32ClientId);666 console->mDisplay->VideoAccelVRDP (true);667 #endif /* VBOX_VRDP */668 669 LogFlowFuncLeave();670 return;671 }672 673 #ifdef VRDP_NO_COM674 void Console::VRDPClientDisconnect (uint32_t u32ClientId,675 uint32_t fu32Intercepted)676 #else677 DECLCALLBACK(void) Console::vrdp_ClientDisconnect (void *pvUser,678 uint32_t u32ClientId,679 uint32_t fu32Intercepted)680 #endif /* VRDP_NO_COM */681 {682 LogFlowFuncEnter();683 684 #ifdef VRDP_NO_COM685 Console *console = this;686 #else687 Console *console = static_cast <Console *> (pvUser);688 #endif /* VRDP_NO_COM */689 AssertReturnVoid (console);690 691 AutoCaller autoCaller (console);692 AssertComRCReturnVoid (autoCaller.rc());693 694 AssertReturnVoid (console->mConsoleVRDPServer);695 696 #ifdef VBOX_VRDP697 ASMAtomicDecU32(&console->mcVRDPClients);698 699 console->mDisplay->VideoAccelVRDP (false);700 #endif /* VBOX_VRDP */701 702 if (fu32Intercepted & VRDP_CLIENT_INTERCEPT_USB)703 {704 console->mConsoleVRDPServer->USBBackendDelete (u32ClientId);705 }706 707 #ifdef VBOX_VRDP708 if (fu32Intercepted & VRDP_CLIENT_INTERCEPT_CLIPBOARD)709 {710 console->mConsoleVRDPServer->ClipboardDelete (u32ClientId);711 }712 713 if (fu32Intercepted & VRDP_CLIENT_INTERCEPT_AUDIO)714 {715 console->mcAudioRefs--;716 717 if (console->mcAudioRefs <= 0)718 {719 if (console->mAudioSniffer)720 {721 PPDMIAUDIOSNIFFERPORT port = console->mAudioSniffer->getAudioSnifferPort();722 if (port)723 {724 port->pfnSetup (port, false, false);725 }726 }727 }728 }729 #endif /* VBOX_VRDP */730 731 Guid uuid;732 HRESULT hrc = console->mMachine->COMGETTER (Id) (uuid.asOutParam());733 AssertComRC (hrc);734 735 VRDPAuthType_T authType = VRDPAuthType_VRDPAuthNull;736 hrc = console->mVRDPServer->COMGETTER(AuthType) (&authType);737 AssertComRC (hrc);738 739 if (authType == VRDPAuthType_VRDPAuthExternal)740 console->mConsoleVRDPServer->AuthDisconnect (uuid, u32ClientId);741 742 LogFlowFuncLeave();743 return;744 }745 746 #ifdef VRDP_NO_COM747 void Console::VRDPInterceptAudio (uint32_t u32ClientId)748 #else749 DECLCALLBACK(void) Console::vrdp_InterceptAudio (void *pvUser,750 uint32_t u32ClientId)751 #endif /* VRDP_NO_COM */752 {753 LogFlowFuncEnter();754 755 #ifdef VRDP_NO_COM756 Console *console = this;757 #else758 Console *console = static_cast <Console *> (pvUser);759 #endif /* VRDP_NO_COM */760 AssertReturnVoid (console);761 762 AutoCaller autoCaller (console);763 AssertComRCReturnVoid (autoCaller.rc());764 765 LogFlowFunc (("mAudioSniffer %p, u32ClientId %d.\n",766 console->mAudioSniffer, u32ClientId));767 NOREF(u32ClientId);768 769 #ifdef VBOX_VRDP770 console->mcAudioRefs++;771 772 if (console->mcAudioRefs == 1)773 {774 if (console->mAudioSniffer)775 {776 PPDMIAUDIOSNIFFERPORT port = console->mAudioSniffer->getAudioSnifferPort();777 if (port)778 {779 port->pfnSetup (port, true, true);780 }781 }782 }783 #endif784 785 LogFlowFuncLeave();786 return;787 }788 789 #ifdef VRDP_NO_COM790 void Console::VRDPInterceptUSB (uint32_t u32ClientId, void **ppvIntercept)791 #else792 DECLCALLBACK(void) Console::vrdp_InterceptUSB (void *pvUser,793 uint32_t u32ClientId,794 PFNVRDPUSBCALLBACK *ppfn,795 void **ppv)796 #endif /* VRDP_NO_COM */797 {798 LogFlowFuncEnter();799 800 #ifdef VRDP_NO_COM801 Console *console = this;802 #else803 Console *console = static_cast <Console *> (pvUser);804 #endif /* VRDP_NO_COM */805 AssertReturnVoid (console);806 807 AutoCaller autoCaller (console);808 AssertComRCReturnVoid (autoCaller.rc());809 810 AssertReturnVoid (console->mConsoleVRDPServer);811 812 #ifdef VRDP_NO_COM813 mConsoleVRDPServer->USBBackendCreate (u32ClientId, ppvIntercept);814 #else815 console->mConsoleVRDPServer->USBBackendCreate (u32ClientId, ppfn, ppv);816 #endif /* VRDP_NO_COM */817 818 LogFlowFuncLeave();819 return;820 }821 822 #ifdef VRDP_NO_COM823 void Console::VRDPInterceptClipboard (uint32_t u32ClientId)824 #else825 DECLCALLBACK(void) Console::vrdp_InterceptClipboard (void *pvUser,826 uint32_t u32ClientId,827 PFNVRDPCLIPBOARDCALLBACK *ppfn,828 void **ppv)829 #endif /* VRDP_NO_COM */830 {831 LogFlowFuncEnter();832 833 #ifdef VRDP_NO_COM834 Console *console = this;835 #else836 Console *console = static_cast <Console *> (pvUser);837 #endif /* VRDP_NO_COM */838 AssertReturnVoid (console);839 840 AutoCaller autoCaller (console);841 AssertComRCReturnVoid (autoCaller.rc());842 843 AssertReturnVoid (console->mConsoleVRDPServer);844 845 #ifdef VBOX_VRDP846 #ifdef VRDP_NO_COM847 mConsoleVRDPServer->ClipboardCreate (u32ClientId);848 #else849 console->mConsoleVRDPServer->ClipboardCreate (u32ClientId, ppfn, ppv);850 #endif /* VRDP_NO_COM */851 #endif /* VBOX_VRDP */852 853 LogFlowFuncLeave();854 return;855 }856 857 858 #ifdef VRDP_NO_COM859 #else860 // static861 VRDPSERVERCALLBACK Console::sVrdpServerCallback =862 {863 vrdp_ClientLogon,864 vrdp_ClientConnect,865 vrdp_ClientDisconnect,866 vrdp_InterceptAudio,867 vrdp_InterceptUSB,868 vrdp_InterceptClipboard869 };870 #endif /* VRDP_NO_COM */871 872 //static873 const char *Console::sSSMConsoleUnit = "ConsoleData";874 //static875 uint32_t Console::sSSMConsoleVer = 0x00010000;876 877 /**878 * Loads various console data stored in the saved state file.879 * This method does validation of the state file and returns an error info880 * when appropriate.881 *882 * The method does nothing if the machine is not in the Saved file or if883 * console data from it has already been loaded.884 *885 * @note The caller must lock this object for writing.886 */887 HRESULT Console::loadDataFromSavedState()888 {889 if (mMachineState != MachineState_Saved || mSavedStateDataLoaded)890 return S_OK;891 892 Bstr savedStateFile;893 HRESULT rc = mMachine->COMGETTER(StateFilePath) (savedStateFile.asOutParam());894 if (FAILED (rc))895 return rc;896 897 PSSMHANDLE ssm;898 int vrc = SSMR3Open (Utf8Str(savedStateFile), 0, &ssm);899 if (VBOX_SUCCESS (vrc))900 {901 uint32_t version = 0;902 vrc = SSMR3Seek (ssm, sSSMConsoleUnit, 0 /* iInstance */, &version);903 if (version == sSSMConsoleVer)904 {905 if (VBOX_SUCCESS (vrc))906 vrc = loadStateFileExec (ssm, this, 0);907 else if (vrc == VERR_SSM_UNIT_NOT_FOUND)908 vrc = VINF_SUCCESS;909 }910 else911 vrc = VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;912 913 SSMR3Close (ssm);914 }915 916 if (VBOX_FAILURE (vrc))917 rc = setError (E_FAIL,918 tr ("The saved state file '%ls' is invalid (%Vrc). "919 "Discard the saved state and try again"),920 savedStateFile.raw(), vrc);921 922 mSavedStateDataLoaded = true;923 924 return rc;925 }926 927 /**928 * Callback handler to save various console data to the state file,929 * called when the user saves the VM state.930 *931 * @param pvUser pointer to Console932 *933 * @note Locks the Console object for reading.934 */935 //static936 DECLCALLBACK(void)937 Console::saveStateFileExec (PSSMHANDLE pSSM, void *pvUser)938 {939 LogFlowFunc (("\n"));940 941 Console *that = static_cast <Console *> (pvUser);942 AssertReturnVoid (that);943 944 AutoCaller autoCaller (that);945 AssertComRCReturnVoid (autoCaller.rc());946 947 AutoReaderLock alock (that);948 949 int vrc = SSMR3PutU32 (pSSM, that->mSharedFolders.size());950 AssertRC (vrc);951 952 for (SharedFolderMap::const_iterator it = that->mSharedFolders.begin();953 it != that->mSharedFolders.end();954 ++ it)955 {956 ComObjPtr <SharedFolder> folder = (*it).second;957 // don't lock the folder because methods we access are const958 959 Utf8Str name = folder->name();960 vrc = SSMR3PutU32 (pSSM, name.length() + 1 /* term. 0 */);961 AssertRC (vrc);962 vrc = SSMR3PutStrZ (pSSM, name);963 AssertRC (vrc);964 965 Utf8Str hostPath = folder->hostPath();966 vrc = SSMR3PutU32 (pSSM, hostPath.length() + 1 /* term. 0 */);967 AssertRC (vrc);968 vrc = SSMR3PutStrZ (pSSM, hostPath);969 AssertRC (vrc);970 }971 972 return;973 }974 975 /**976 * Callback handler to load various console data from the state file.977 * When \a u32Version is 0, this method is called from #loadDataFromSavedState,978 * otherwise it is called when the VM is being restored from the saved state.979 *980 * @param pvUser pointer to Console981 * @param u32Version Console unit version.982 * When not 0, should match sSSMConsoleVer.983 *984 * @note Locks the Console object for writing.985 */986 //static987 DECLCALLBACK(int)988 Console::loadStateFileExec (PSSMHANDLE pSSM, void *pvUser, uint32_t u32Version)989 {990 LogFlowFunc (("\n"));991 992 if (u32Version != 0 && u32Version != sSSMConsoleVer)993 return VERR_VERSION_MISMATCH;994 995 if (u32Version != 0)996 {997 /* currently, nothing to do when we've been called from VMR3Load */998 return VINF_SUCCESS;999 }1000 1001 Console *that = static_cast <Console *> (pvUser);1002 AssertReturn (that, VERR_INVALID_PARAMETER);1003 1004 AutoCaller autoCaller (that);1005 AssertComRCReturn (autoCaller.rc(), VERR_ACCESS_DENIED);1006 1007 AutoLock alock (that);1008 1009 AssertReturn (that->mSharedFolders.size() == 0, VERR_INTERNAL_ERROR);1010 1011 uint32_t size = 0;1012 int vrc = SSMR3GetU32 (pSSM, &size);1013 AssertRCReturn (vrc, vrc);1014 1015 for (uint32_t i = 0; i < size; ++ i)1016 {1017 Bstr name;1018 Bstr hostPath;1019 1020 uint32_t szBuf = 0;1021 char *buf = NULL;1022 1023 vrc = SSMR3GetU32 (pSSM, &szBuf);1024 AssertRCReturn (vrc, vrc);1025 buf = new char [szBuf];1026 vrc = SSMR3GetStrZ (pSSM, buf, szBuf);1027 AssertRC (vrc);1028 name = buf;1029 delete[] buf;1030 1031 vrc = SSMR3GetU32 (pSSM, &szBuf);1032 AssertRCReturn (vrc, vrc);1033 buf = new char [szBuf];1034 vrc = SSMR3GetStrZ (pSSM, buf, szBuf);1035 AssertRC (vrc);1036 hostPath = buf;1037 delete[] buf;1038 1039 ComObjPtr <SharedFolder> sharedFolder;1040 sharedFolder.createObject();1041 HRESULT rc = sharedFolder->init (that, name, hostPath);1042 AssertComRCReturn (rc, VERR_INTERNAL_ERROR);1043 1044 that->mSharedFolders.insert (std::make_pair (name, sharedFolder));1045 }1046 1047 return VINF_SUCCESS;1048 }1049 1050 // IConsole properties1051 /////////////////////////////////////////////////////////////////////////////1052 1053 STDMETHODIMP Console::COMGETTER(Machine) (IMachine **aMachine)1054 {1055 if (!aMachine)1056 return E_POINTER;1057 1058 AutoCaller autoCaller (this);1059 CheckComRCReturnRC (autoCaller.rc());1060 1061 /* mMachine is constant during life time, no need to lock */1062 mMachine.queryInterfaceTo (aMachine);1063 1064 return S_OK;1065 }1066 1067 STDMETHODIMP Console::COMGETTER(State) (MachineState_T *aMachineState)1068 {1069 if (!aMachineState)1070 return E_POINTER;1071 1072 AutoCaller autoCaller (this);1073 CheckComRCReturnRC (autoCaller.rc());1074 1075 AutoReaderLock alock (this);1076 1077 /* we return our local state (since it's always the same as on the server) */1078 *aMachineState = mMachineState;1079 1080 return S_OK;1081 }1082 1083 STDMETHODIMP Console::COMGETTER(Guest) (IGuest **aGuest)1084 {1085 if (!aGuest)1086 return E_POINTER;1087 1088 AutoCaller autoCaller (this);1089 CheckComRCReturnRC (autoCaller.rc());1090 1091 /* mGuest is constant during life time, no need to lock */1092 mGuest.queryInterfaceTo (aGuest);1093 1094 return S_OK;1095 }1096 1097 STDMETHODIMP Console::COMGETTER(Keyboard) (IKeyboard **aKeyboard)1098 {1099 if (!aKeyboard)1100 return E_POINTER;1101 1102 AutoCaller autoCaller (this);1103 CheckComRCReturnRC (autoCaller.rc());1104 1105 /* mKeyboard is constant during life time, no need to lock */1106 mKeyboard.queryInterfaceTo (aKeyboard);1107 1108 return S_OK;1109 }1110 1111 STDMETHODIMP Console::COMGETTER(Mouse) (IMouse **aMouse)1112 {1113 if (!aMouse)1114 return E_POINTER;1115 1116 AutoCaller autoCaller (this);1117 CheckComRCReturnRC (autoCaller.rc());1118 1119 /* mMouse is constant during life time, no need to lock */1120 mMouse.queryInterfaceTo (aMouse);1121 1122 return S_OK;1123 }1124 1125 STDMETHODIMP Console::COMGETTER(Display) (IDisplay **aDisplay)1126 {1127 if (!aDisplay)1128 return E_POINTER;1129 1130 AutoCaller autoCaller (this);1131 CheckComRCReturnRC (autoCaller.rc());1132 1133 /* mDisplay is constant during life time, no need to lock */1134 mDisplay.queryInterfaceTo (aDisplay);1135 1136 return S_OK;1137 }1138 1139 STDMETHODIMP Console::COMGETTER(Debugger) (IMachineDebugger **aDebugger)1140 {1141 if (!aDebugger)1142 return E_POINTER;1143 1144 AutoCaller autoCaller (this);1145 CheckComRCReturnRC (autoCaller.rc());1146 1147 /* we need a write lock because of the lazy mDebugger initialization*/1148 AutoLock alock (this);1149 1150 /* check if we have to create the debugger object */1151 if (!mDebugger)1152 {1153 unconst (mDebugger).createObject();1154 mDebugger->init (this);1155 }1156 1157 mDebugger.queryInterfaceTo (aDebugger);1158 1159 return S_OK;1160 }1161 1162 STDMETHODIMP Console::COMGETTER(USBDevices) (IUSBDeviceCollection **aUSBDevices)1163 {1164 if (!aUSBDevices)1165 return E_POINTER;1166 1167 AutoCaller autoCaller (this);1168 CheckComRCReturnRC (autoCaller.rc());1169 1170 AutoReaderLock alock (this);1171 1172 ComObjPtr <OUSBDeviceCollection> collection;1173 collection.createObject();1174 collection->init (mUSBDevices);1175 collection.queryInterfaceTo (aUSBDevices);1176 1177 return S_OK;1178 }1179 1180 STDMETHODIMP Console::COMGETTER(RemoteUSBDevices) (IHostUSBDeviceCollection **aRemoteUSBDevices)1181 {1182 if (!aRemoteUSBDevices)1183 return E_POINTER;1184 1185 AutoCaller autoCaller (this);1186 CheckComRCReturnRC (autoCaller.rc());1187 1188 AutoReaderLock alock (this);1189 1190 ComObjPtr <RemoteUSBDeviceCollection> collection;1191 collection.createObject();1192 collection->init (mRemoteUSBDevices);1193 collection.queryInterfaceTo (aRemoteUSBDevices);1194 1195 return S_OK;1196 }1197 1198 STDMETHODIMP Console::COMGETTER(RemoteDisplayInfo) (IRemoteDisplayInfo **aRemoteDisplayInfo)1199 {1200 if (!aRemoteDisplayInfo)1201 return E_POINTER;1202 1203 AutoCaller autoCaller (this);1204 CheckComRCReturnRC (autoCaller.rc());1205 1206 /* mDisplay is constant during life time, no need to lock */1207 mRemoteDisplayInfo.queryInterfaceTo (aRemoteDisplayInfo);1208 1209 return S_OK;1210 }1211 1212 STDMETHODIMP1213 Console::COMGETTER(SharedFolders) (ISharedFolderCollection **aSharedFolders)1214 {1215 if (!aSharedFolders)1216 return E_POINTER;1217 1218 AutoCaller autoCaller (this);1219 CheckComRCReturnRC (autoCaller.rc());1220 1221 /* loadDataFromSavedState() needs a write lock */1222 AutoLock alock (this);1223 1224 /* Read console data stored in the saved state file (if not yet done) */1225 HRESULT rc = loadDataFromSavedState();1226 CheckComRCReturnRC (rc);1227 1228 ComObjPtr <SharedFolderCollection> coll;1229 coll.createObject();1230 coll->init (mSharedFolders);1231 coll.queryInterfaceTo (aSharedFolders);1232 1233 return S_OK;1234 }1235 1236 // IConsole methods1237 /////////////////////////////////////////////////////////////////////////////1238 1239 STDMETHODIMP Console::PowerUp (IProgress **aProgress)1240 {1241 LogFlowThisFuncEnter();1242 LogFlowThisFunc (("mMachineState=%d\n", mMachineState));1243 1244 AutoCaller autoCaller (this);1245 CheckComRCReturnRC (autoCaller.rc());1246 1247 AutoLock alock (this);1248 1249 if (mMachineState >= MachineState_Running)1250 return setError(E_FAIL, tr ("Cannot power up the machine as it is "1251 "already running (machine state: %d)"),1252 mMachineState);1253 1254 /*1255 * First check whether all disks are accessible. This is not a 100%1256 * bulletproof approach (race condition, it might become inaccessible1257 * right after the check) but it's convenient as it will cover 99.9%1258 * of the cases and here, we're able to provide meaningful error1259 * information.1260 */1261 ComPtr<IHardDiskAttachmentCollection> coll;1262 mMachine->COMGETTER(HardDiskAttachments)(coll.asOutParam());1263 ComPtr<IHardDiskAttachmentEnumerator> enumerator;1264 coll->Enumerate(enumerator.asOutParam());1265 BOOL fHasMore;1266 while (SUCCEEDED(enumerator->HasMore(&fHasMore)) && fHasMore)1267 {1268 ComPtr<IHardDiskAttachment> attach;1269 enumerator->GetNext(attach.asOutParam());1270 ComPtr<IHardDisk> hdd;1271 attach->COMGETTER(HardDisk)(hdd.asOutParam());1272 Assert(hdd);1273 BOOL fAccessible;1274 HRESULT rc = hdd->COMGETTER(AllAccessible)(&fAccessible);1275 CheckComRCReturnRC (rc);1276 if (!fAccessible)1277 {1278 Bstr loc;1279 hdd->COMGETTER(Location) (loc.asOutParam());1280 Bstr errMsg;1281 hdd->COMGETTER(LastAccessError) (errMsg.asOutParam());1282 return setError (E_FAIL,1283 tr ("VM cannot start because the hard disk '%ls' is not accessible "1284 "(%ls)"),1285 loc.raw(), errMsg.raw());1286 }1287 }1288 1289 /* now perform the same check if a ISO is mounted */1290 ComPtr<IDVDDrive> dvdDrive;1291 mMachine->COMGETTER(DVDDrive)(dvdDrive.asOutParam());1292 ComPtr<IDVDImage> dvdImage;1293 dvdDrive->GetImage(dvdImage.asOutParam());1294 if (dvdImage)1295 {1296 BOOL fAccessible;1297 HRESULT rc = dvdImage->COMGETTER(Accessible)(&fAccessible);1298 CheckComRCReturnRC (rc);1299 if (!fAccessible)1300 {1301 Bstr filePath;1302 dvdImage->COMGETTER(FilePath)(filePath.asOutParam());1303 /// @todo (r=dmik) grab the last access error once1304 // IDVDImage::lastAccessError is there1305 return setError (E_FAIL,1306 tr ("VM cannot start because the DVD image '%ls' is not accessible"),1307 filePath.raw());1308 }1309 }1310 1311 /* now perform the same check if a floppy is mounted */1312 ComPtr<IFloppyDrive> floppyDrive;1313 mMachine->COMGETTER(FloppyDrive)(floppyDrive.asOutParam());1314 ComPtr<IFloppyImage> floppyImage;1315 floppyDrive->GetImage(floppyImage.asOutParam());1316 if (floppyImage)1317 {1318 BOOL fAccessible;1319 HRESULT rc = floppyImage->COMGETTER(Accessible)(&fAccessible);1320 CheckComRCReturnRC (rc);1321 if (!fAccessible)1322 {1323 Bstr filePath;1324 floppyImage->COMGETTER(FilePath)(filePath.asOutParam());1325 /// @todo (r=dmik) grab the last access error once1326 // IDVDImage::lastAccessError is there1327 return setError (E_FAIL,1328 tr ("VM cannot start because the floppy image '%ls' is not accessible"),1329 filePath.raw());1330 }1331 }1332 1333 /* now the network cards will undergo a quick consistency check */1334 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++)1335 {1336 ComPtr<INetworkAdapter> adapter;1337 mMachine->GetNetworkAdapter (slot, adapter.asOutParam());1338 BOOL enabled = FALSE;1339 adapter->COMGETTER(Enabled) (&enabled);1340 if (!enabled)1341 continue;1342 1343 NetworkAttachmentType_T netattach;1344 adapter->COMGETTER(AttachmentType)(&netattach);1345 switch (netattach)1346 {1347 case NetworkAttachmentType_HostInterfaceNetworkAttachment:1348 {1349 #ifdef RT_OS_WINDOWS1350 /* a valid host interface must have been set */1351 Bstr hostif;1352 adapter->COMGETTER(HostInterface)(hostif.asOutParam());1353 if (!hostif)1354 {1355 return setError (E_FAIL,1356 tr ("VM cannot start because host interface networking "1357 "requires a host interface name to be set"));1358 }1359 ComPtr<IVirtualBox> virtualBox;1360 mMachine->COMGETTER(Parent)(virtualBox.asOutParam());1361 ComPtr<IHost> host;1362 virtualBox->COMGETTER(Host)(host.asOutParam());1363 ComPtr<IHostNetworkInterfaceCollection> coll;1364 host->COMGETTER(NetworkInterfaces)(coll.asOutParam());1365 ComPtr<IHostNetworkInterface> hostInterface;1366 if (!SUCCEEDED(coll->FindByName(hostif, hostInterface.asOutParam())))1367 {1368 return setError (E_FAIL,1369 tr ("VM cannot start because the host interface '%ls' "1370 "does not exist"),1371 hostif.raw());1372 }1373 #endif /* RT_OS_WINDOWS */1374 break;1375 }1376 default:1377 break;1378 }1379 }1380 1381 /* Read console data stored in the saved state file (if not yet done) */1382 {1383 HRESULT rc = loadDataFromSavedState();1384 CheckComRCReturnRC (rc);1385 }1386 1387 /* Check all types of shared folders and compose a single list */1388 SharedFolderDataMap sharedFolders;1389 {1390 /* first, insert global folders */1391 for (SharedFolderDataMap::const_iterator it = mGlobalSharedFolders.begin();1392 it != mGlobalSharedFolders.end(); ++ it)1393 sharedFolders [it->first] = it->second;1394 1395 /* second, insert machine folders */1396 for (SharedFolderDataMap::const_iterator it = mMachineSharedFolders.begin();1397 it != mMachineSharedFolders.end(); ++ it)1398 sharedFolders [it->first] = it->second;1399 1400 /* third, insert console folders */1401 for (SharedFolderMap::const_iterator it = mSharedFolders.begin();1402 it != mSharedFolders.end(); ++ it)1403 sharedFolders [it->first] = it->second->hostPath();1404 }1405 1406 Bstr savedStateFile;1407 1408 /*1409 * Saved VMs will have to prove that their saved states are kosher.1410 */1411 if (mMachineState == MachineState_Saved)1412 {1413 HRESULT rc = mMachine->COMGETTER(StateFilePath) (savedStateFile.asOutParam());1414 CheckComRCReturnRC (rc);1415 ComAssertRet (!!savedStateFile, E_FAIL);1416 int vrc = SSMR3ValidateFile (Utf8Str (savedStateFile));1417 if (VBOX_FAILURE (vrc))1418 return setError (E_FAIL,1419 tr ("VM cannot start because the saved state file '%ls' is invalid (%Vrc). "1420 "Discard the saved state prior to starting the VM"),1421 savedStateFile.raw(), vrc);1422 }1423 1424 /* create an IProgress object to track progress of this operation */1425 ComObjPtr <Progress> progress;1426 progress.createObject();1427 Bstr progressDesc;1428 if (mMachineState == MachineState_Saved)1429 progressDesc = tr ("Restoring the virtual machine");1430 else1431 progressDesc = tr ("Starting the virtual machine");1432 progress->init ((IConsole *) this, progressDesc, FALSE /* aCancelable */);1433 1434 /* pass reference to caller if requested */1435 if (aProgress)1436 progress.queryInterfaceTo (aProgress);1437 1438 /* setup task object and thread to carry out the operation asynchronously */1439 std::auto_ptr <VMPowerUpTask> task (new VMPowerUpTask (this, progress));1440 ComAssertComRCRetRC (task->rc());1441 1442 task->mSetVMErrorCallback = setVMErrorCallback;1443 task->mConfigConstructor = configConstructor;1444 task->mSharedFolders = sharedFolders;1445 if (mMachineState == MachineState_Saved)1446 task->mSavedStateFile = savedStateFile;1447 1448 int vrc = RTThreadCreate (NULL, Console::powerUpThread, (void *) task.get(),1449 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMPowerUp");1450 1451 ComAssertMsgRCRet (vrc, ("Could not create VMPowerUp thread (%Vrc)\n", vrc),1452 E_FAIL);1453 1454 /* task is now owned by powerUpThread(), so release it */1455 task.release();1456 1457 if (mMachineState == MachineState_Saved)1458 setMachineState (MachineState_Restoring);1459 else1460 setMachineState (MachineState_Starting);1461 1462 LogFlowThisFunc (("mMachineState=%d\n", mMachineState));1463 LogFlowThisFuncLeave();1464 return S_OK;1465 }1466 1467 STDMETHODIMP Console::PowerDown()1468 {1469 LogFlowThisFuncEnter();1470 LogFlowThisFunc (("mMachineState=%d\n", mMachineState));1471 1472 AutoCaller autoCaller (this);1473 CheckComRCReturnRC (autoCaller.rc());1474 1475 AutoLock alock (this);1476 1477 if (mMachineState != MachineState_Running &&1478 mMachineState != MachineState_Paused &&1479 mMachineState != MachineState_Stuck)1480 {1481 /* extra nice error message for a common case */1482 if (mMachineState == MachineState_Saved)1483 return setError(E_FAIL, tr ("Cannot power off a saved machine"));1484 else1485 return setError(E_FAIL, tr ("Cannot power off the machine as it is "1486 "not running or paused (machine state: %d)"),1487 mMachineState);1488 }1489 1490 LogFlowThisFunc (("Sending SHUTDOWN request...\n"));1491 1492 HRESULT rc = powerDown();1493 1494 LogFlowThisFunc (("mMachineState=%d, rc=%08X\n", mMachineState, rc));1495 LogFlowThisFuncLeave();1496 return rc;1497 }1498 1499 STDMETHODIMP Console::Reset()1500 {1501 LogFlowThisFuncEnter();1502 LogFlowThisFunc (("mMachineState=%d\n", mMachineState));1503 1504 AutoCaller autoCaller (this);1505 CheckComRCReturnRC (autoCaller.rc());1506 1507 AutoLock alock (this);1508 1509 if (mMachineState != MachineState_Running)1510 return setError(E_FAIL, tr ("Cannot reset the machine as it is "1511 "not running (machine state: %d)"),1512 mMachineState);1513 1514 /* protect mpVM */1515 AutoVMCaller autoVMCaller (this);1516 CheckComRCReturnRC (autoVMCaller.rc());1517 1518 /* leave the lock before a VMR3* call (EMT will call us back)! */1519 alock.leave();1520 1521 int vrc = VMR3Reset (mpVM);1522 1523 HRESULT rc = VBOX_SUCCESS (vrc) ? S_OK :1524 setError (E_FAIL, tr ("Could not reset the machine (%Vrc)"), vrc);1525 1526 LogFlowThisFunc (("mMachineState=%d, rc=%08X\n", mMachineState, rc));1527 LogFlowThisFuncLeave();1528 return rc;1529 }1530 1531 STDMETHODIMP Console::Pause()1532 {1533 LogFlowThisFuncEnter();1534 1535 AutoCaller autoCaller (this);1536 CheckComRCReturnRC (autoCaller.rc());1537 1538 AutoLock alock (this);1539 1540 if (mMachineState != MachineState_Running)1541 return setError (E_FAIL, tr ("Cannot pause the machine as it is "1542 "not running (machine state: %d)"),1543 mMachineState);1544 1545 /* protect mpVM */1546 AutoVMCaller autoVMCaller (this);1547 CheckComRCReturnRC (autoVMCaller.rc());1548 1549 LogFlowThisFunc (("Sending PAUSE request...\n"));1550 1551 /* leave the lock before a VMR3* call (EMT will call us back)! */1552 alock.leave();1553 1554 int vrc = VMR3Suspend (mpVM);1555 1556 HRESULT rc = VBOX_SUCCESS (vrc) ? S_OK :1557 setError (E_FAIL,1558 tr ("Could not suspend the machine execution (%Vrc)"), vrc);1559 1560 LogFlowThisFunc (("rc=%08X\n", rc));1561 LogFlowThisFuncLeave();1562 return rc;1563 }1564 1565 STDMETHODIMP Console::Resume()1566 {1567 LogFlowThisFuncEnter();1568 1569 AutoCaller autoCaller (this);1570 CheckComRCReturnRC (autoCaller.rc());1571 1572 AutoLock alock (this);1573 1574 if (mMachineState != MachineState_Paused)1575 return setError (E_FAIL, tr ("Cannot resume the machine as it is "1576 "not paused (machine state: %d)"),1577 mMachineState);1578 1579 /* protect mpVM */1580 AutoVMCaller autoVMCaller (this);1581 CheckComRCReturnRC (autoVMCaller.rc());1582 1583 LogFlowThisFunc (("Sending RESUME request...\n"));1584 1585 /* leave the lock before a VMR3* call (EMT will call us back)! */1586 alock.leave();1587 1588 int vrc = VMR3Resume (mpVM);1589 1590 HRESULT rc = VBOX_SUCCESS (vrc) ? S_OK :1591 setError (E_FAIL,1592 tr ("Could not resume the machine execution (%Vrc)"), vrc);1593 1594 LogFlowThisFunc (("rc=%08X\n", rc));1595 LogFlowThisFuncLeave();1596 return rc;1597 }1598 1599 STDMETHODIMP Console::PowerButton()1600 {1601 LogFlowThisFuncEnter();1602 1603 AutoCaller autoCaller (this);1604 CheckComRCReturnRC (autoCaller.rc());1605 1606 AutoLock lock (this);1607 1608 if (mMachineState != MachineState_Running)1609 return setError (E_FAIL, tr ("Cannot power off the machine as it is "1610 "not running (machine state: %d)"),1611 mMachineState);1612 1613 /* protect mpVM */1614 AutoVMCaller autoVMCaller (this);1615 CheckComRCReturnRC (autoVMCaller.rc());1616 1617 PPDMIBASE pBase;1618 int vrc = PDMR3QueryDeviceLun (mpVM, "acpi", 0, 0, &pBase);1619 if (VBOX_SUCCESS (vrc))1620 {1621 Assert (pBase);1622 PPDMIACPIPORT pPort =1623 (PPDMIACPIPORT) pBase->pfnQueryInterface(pBase, PDMINTERFACE_ACPI_PORT);1624 vrc = pPort ? pPort->pfnPowerButtonPress(pPort) : VERR_INVALID_POINTER;1625 }1626 1627 HRESULT rc = VBOX_SUCCESS (vrc) ? S_OK :1628 setError (E_FAIL,1629 tr ("Controlled power off failed (%Vrc)"), vrc);1630 1631 LogFlowThisFunc (("rc=%08X\n", rc));1632 LogFlowThisFuncLeave();1633 return rc;1634 }1635 1636 STDMETHODIMP Console::SaveState (IProgress **aProgress)1637 {1638 LogFlowThisFuncEnter();1639 LogFlowThisFunc (("mMachineState=%d\n", mMachineState));1640 1641 if (!aProgress)1642 return E_POINTER;1643 1644 AutoCaller autoCaller (this);1645 CheckComRCReturnRC (autoCaller.rc());1646 1647 AutoLock alock (this);1648 1649 if (mMachineState != MachineState_Running &&1650 mMachineState != MachineState_Paused)1651 {1652 return setError (E_FAIL,1653 tr ("Cannot save the execution state as the machine "1654 "is not running (machine state: %d)"), mMachineState);1655 }1656 1657 /* memorize the current machine state */1658 MachineState_T lastMachineState = mMachineState;1659 1660 if (mMachineState == MachineState_Running)1661 {1662 HRESULT rc = Pause();1663 CheckComRCReturnRC (rc);1664 }1665 1666 HRESULT rc = S_OK;1667 1668 /* create a progress object to track operation completion */1669 ComObjPtr <Progress> progress;1670 progress.createObject();1671 progress->init ((IConsole *) this,1672 Bstr (tr ("Saving the execution state of the virtual machine")),1673 FALSE /* aCancelable */);1674 1675 bool beganSavingState = false;1676 bool taskCreationFailed = false;1677 1678 do1679 {1680 /* create a task object early to ensure mpVM protection is successful */1681 std::auto_ptr <VMSaveTask> task (new VMSaveTask (this, progress));1682 rc = task->rc();1683 /*1684 * If we fail here it means a PowerDown() call happened on another1685 * thread while we were doing Pause() (which leaves the Console lock).1686 * We assign PowerDown() a higher precendence than SaveState(),1687 * therefore just return the error to the caller.1688 */1689 if (FAILED (rc))1690 {1691 taskCreationFailed = true;1692 break;1693 }1694 1695 Bstr stateFilePath;1696 1697 /*1698 * request a saved state file path from the server1699 * (this will set the machine state to Saving on the server to block1700 * others from accessing this machine)1701 */1702 rc = mControl->BeginSavingState (progress, stateFilePath.asOutParam());1703 CheckComRCBreakRC (rc);1704 1705 beganSavingState = true;1706 1707 /* sync the state with the server */1708 setMachineStateLocally (MachineState_Saving);1709 1710 /* ensure the directory for the saved state file exists */1711 {1712 Utf8Str dir = stateFilePath;1713 RTPathStripFilename (dir.mutableRaw());1714 if (!RTDirExists (dir))1715 {1716 int vrc = RTDirCreateFullPath (dir, 0777);1717 if (VBOX_FAILURE (vrc))1718 {1719 rc = setError (E_FAIL,1720 tr ("Could not create a directory '%s' to save the state to (%Vrc)"),1721 dir.raw(), vrc);1722 break;1723 }1724 }1725 }1726 1727 /* setup task object and thread to carry out the operation asynchronously */1728 task->mIsSnapshot = false;1729 task->mSavedStateFile = stateFilePath;1730 /* set the state the operation thread will restore when it is finished */1731 task->mLastMachineState = lastMachineState;1732 1733 /* create a thread to wait until the VM state is saved */1734 int vrc = RTThreadCreate (NULL, Console::saveStateThread, (void *) task.get(),1735 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMSave");1736 1737 ComAssertMsgRCBreak (vrc, ("Could not create VMSave thread (%Vrc)\n", vrc),1738 rc = E_FAIL);1739 1740 /* task is now owned by saveStateThread(), so release it */1741 task.release();1742 1743 /* return the progress to the caller */1744 progress.queryInterfaceTo (aProgress);1745 }1746 while (0);1747 1748 if (FAILED (rc) && !taskCreationFailed)1749 {1750 /* preserve existing error info */1751 ErrorInfoKeeper eik;1752 1753 if (beganSavingState)1754 {1755 /*1756 * cancel the requested save state procedure.1757 * This will reset the machine state to the state it had right1758 * before calling mControl->BeginSavingState().1759 */1760 mControl->EndSavingState (FALSE);1761 }1762 1763 if (lastMachineState == MachineState_Running)1764 {1765 /* restore the paused state if appropriate */1766 setMachineStateLocally (MachineState_Paused);1767 /* restore the running state if appropriate */1768 Resume();1769 }1770 else1771 setMachineStateLocally (lastMachineState);1772 }1773 1774 LogFlowThisFunc (("rc=%08X\n", rc));1775 LogFlowThisFuncLeave();1776 return rc;1777 }1778 1779 STDMETHODIMP Console::DiscardSavedState()1780 {1781 AutoCaller autoCaller (this);1782 CheckComRCReturnRC (autoCaller.rc());1783 1784 AutoLock alock (this);1785 1786 if (mMachineState != MachineState_Saved)1787 return setError (E_FAIL,1788 tr ("Cannot discard the machine state as the machine is "1789 "not in the saved state (machine state: %d)"),1790 mMachineState);1791 1792 /*1793 * Saved -> PoweredOff transition will be detected in the SessionMachine1794 * and properly handled.1795 */1796 setMachineState (MachineState_PoweredOff);1797 1798 return S_OK;1799 }1800 1801 /** read the value of a LEd. */1802 inline uint32_t readAndClearLed(PPDMLED pLed)1803 {1804 if (!pLed)1805 return 0;1806 uint32_t u32 = pLed->Actual.u32 | pLed->Asserted.u32;1807 pLed->Asserted.u32 = 0;1808 return u32;1809 }1810 1811 STDMETHODIMP Console::GetDeviceActivity (DeviceType_T aDeviceType,1812 DeviceActivity_T *aDeviceActivity)1813 {1814 if (!aDeviceActivity)1815 return E_INVALIDARG;1816 1817 AutoCaller autoCaller (this);1818 CheckComRCReturnRC (autoCaller.rc());1819 1820 /*1821 * Note: we don't lock the console object here because1822 * readAndClearLed() should be thread safe.1823 */1824 1825 /* Get LED array to read */1826 PDMLEDCORE SumLed = {0};1827 switch (aDeviceType)1828 {1829 case DeviceType_FloppyDevice:1830 {1831 for (unsigned i = 0; i < ELEMENTS(mapFDLeds); i++)1832 SumLed.u32 |= readAndClearLed(mapFDLeds[i]);1833 break;1834 }1835 1836 case DeviceType_DVDDevice:1837 {1838 SumLed.u32 |= readAndClearLed(mapIDELeds[2]);1839 break;1840 }1841 1842 case DeviceType_HardDiskDevice:1843 {1844 SumLed.u32 |= readAndClearLed(mapIDELeds[0]);1845 SumLed.u32 |= readAndClearLed(mapIDELeds[1]);1846 SumLed.u32 |= readAndClearLed(mapIDELeds[3]);1847 break;1848 }1849 1850 case DeviceType_NetworkDevice:1851 {1852 for (unsigned i = 0; i < ELEMENTS(mapNetworkLeds); i++)1853 SumLed.u32 |= readAndClearLed(mapNetworkLeds[i]);1854 break;1855 }1856 1857 case DeviceType_USBDevice:1858 {1859 SumLed.u32 |= readAndClearLed(mapUSBLed);1860 break;1861 }1862 1863 case DeviceType_SharedFolderDevice:1864 {1865 SumLed.u32 |= readAndClearLed(mapSharedFolderLed);1866 break;1867 }1868 1869 default:1870 return setError (E_INVALIDARG,1871 tr ("Invalid device type: %d"), aDeviceType);1872 }1873 1874 /* Compose the result */1875 switch (SumLed.u32 & (PDMLED_READING | PDMLED_WRITING))1876 {1877 case 0:1878 *aDeviceActivity = DeviceActivity_DeviceIdle;1879 break;1880 case PDMLED_READING:1881 *aDeviceActivity = DeviceActivity_DeviceReading;1882 break;1883 case PDMLED_WRITING:1884 case PDMLED_READING | PDMLED_WRITING:1885 *aDeviceActivity = DeviceActivity_DeviceWriting;1886 break;1887 }1888 1889 return S_OK;1890 }1891 1892 STDMETHODIMP Console::AttachUSBDevice (INPTR GUIDPARAM aId)1893 {1894 AutoCaller autoCaller (this);1895 CheckComRCReturnRC (autoCaller.rc());1896 1897 AutoLock alock (this);1898 1899 /// @todo (r=dmik) is it legal to attach USB devices when the machine is1900 // Paused, Starting, Saving, Stopping, etc? if not, we should make a1901 // stricter check (mMachineState != MachineState_Running).1902 //1903 // I'm changing it to the semi-strict check for the time being. We'll1904 // consider the below later.1905 //1906 /* bird: It is not permitted to attach or detach while the VM is saving,1907 * is restoring or has stopped - definintly not.1908 *1909 * Attaching while starting, well, if you don't create any deadlock it1910 * should work... Paused should work I guess, but we shouldn't push our1911 * luck if we're pausing because an runtime error condition was raised1912 * (which is one of the reasons there better be a separate state for that1913 * in the VMM).1914 */1915 if (mMachineState != MachineState_Running &&1916 mMachineState != MachineState_Paused)1917 return setError (E_FAIL,1918 tr ("Cannot attach a USB device to the machine which is not running"1919 "(machine state: %d)"),1920 mMachineState);1921 1922 /* protect mpVM */1923 AutoVMCaller autoVMCaller (this);1924 CheckComRCReturnRC (autoVMCaller.rc());1925 1926 /* Don't proceed unless we've found the usb controller. */1927 PPDMIBASE pBase = NULL;1928 int vrc = PDMR3QueryLun (mpVM, "usb-ohci", 0, 0, &pBase);1929 if (VBOX_FAILURE (vrc))1930 return setError (E_FAIL,1931 tr ("The virtual machine does not have a USB controller"));1932 1933 /* leave the lock because the USB Proxy service may call us back1934 * (via onUSBDeviceAttach()) */1935 alock.leave();1936 1937 /* Request the device capture */1938 HRESULT rc = mControl->CaptureUSBDevice (aId);1939 CheckComRCReturnRC (rc);1940 1941 return rc;1942 }1943 1944 STDMETHODIMP Console::DetachUSBDevice (INPTR GUIDPARAM aId, IUSBDevice **aDevice)1945 {1946 if (!aDevice)1947 return E_POINTER;1948 1949 AutoCaller autoCaller (this);1950 CheckComRCReturnRC (autoCaller.rc());1951 1952 AutoLock alock (this);1953 1954 /* Find it. */1955 ComObjPtr <OUSBDevice> device;1956 USBDeviceList::iterator it = mUSBDevices.begin();1957 while (it != mUSBDevices.end())1958 {1959 if ((*it)->id() == aId)1960 {1961 device = *it;1962 break;1963 }1964 ++ it;1965 }1966 1967 if (!device)1968 return setError (E_INVALIDARG,1969 tr ("USB device with UUID {%Vuuid} is not attached to this machine"),1970 Guid (aId).raw());1971 1972 #ifdef RT_OS_DARWIN1973 /* Notify the USB Proxy that we're about to detach the device. Since1974 * we don't dare do IPC when holding the console lock, so we'll have1975 * to revalidate the device when we get back. */1976 alock.leave();1977 HRESULT rc2 = mControl->DetachUSBDevice (aId, false /* aDone */);1978 if (FAILED (rc2))1979 return rc2;1980 alock.enter();1981 1982 for (it = mUSBDevices.begin(); it != mUSBDevices.end(); ++ it)1983 if ((*it)->id() == aId)1984 break;1985 if (it == mUSBDevices.end())1986 return S_OK;1987 #endif1988 1989 /* First, request VMM to detach the device */1990 HRESULT rc = detachUSBDevice (it);1991 1992 if (SUCCEEDED (rc))1993 {1994 /* leave the lock since we don't need it any more (note though that1995 * the USB Proxy service must not call us back here) */1996 alock.leave();1997 1998 /* Request the device release. Even if it fails, the device will1999 * remain as held by proxy, which is OK for us (the VM process). */2000 rc = mControl->DetachUSBDevice (aId, true /* aDone */);2001 }2002 2003 return rc;2004 }2005 2006 STDMETHODIMP2007 Console::CreateSharedFolder (INPTR BSTR aName, INPTR BSTR aHostPath)2008 {2009 if (!aName || !aHostPath)2010 return E_INVALIDARG;2011 2012 AutoCaller autoCaller (this);2013 CheckComRCReturnRC (autoCaller.rc());2014 2015 AutoLock alock (this);2016 2017 /// @todo see @todo in AttachUSBDevice() about the Paused state2018 if (mMachineState == MachineState_Saved)2019 return setError (E_FAIL,2020 tr ("Cannot create a transient shared folder on the "2021 "machine in the saved state"));2022 if (mMachineState > MachineState_Paused)2023 return setError (E_FAIL,2024 tr ("Cannot create a transient shared folder on the "2025 "machine while it is changing the state (machine state: %d)"),2026 mMachineState);2027 2028 ComObjPtr <SharedFolder> sharedFolder;2029 HRESULT rc = findSharedFolder (aName, sharedFolder, false /* aSetError */);2030 if (SUCCEEDED (rc))2031 return setError (E_FAIL,2032 tr ("Shared folder named '%ls' already exists"), aName);2033 2034 sharedFolder.createObject();2035 rc = sharedFolder->init (this, aName, aHostPath);2036 CheckComRCReturnRC (rc);2037 2038 BOOL accessible = FALSE;2039 rc = sharedFolder->COMGETTER(Accessible) (&accessible);2040 CheckComRCReturnRC (rc);2041 2042 if (!accessible)2043 return setError (E_FAIL,2044 tr ("Shared folder host path '%ls' is not accessible"), aHostPath);2045 2046 /* protect mpVM (if not NULL) */2047 AutoVMCallerQuietWeak autoVMCaller (this);2048 2049 if (mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive())2050 {2051 /* If the VM is online and supports shared folders, share this folder2052 * under the specified name. */2053 2054 /* first, remove the machine or the global folder if there is any */2055 SharedFolderDataMap::const_iterator it;2056 if (findOtherSharedFolder (aName, it))2057 {2058 rc = removeSharedFolder (aName);2059 CheckComRCReturnRC (rc);2060 }2061 2062 /* second, create the given folder */2063 rc = createSharedFolder (aName, aHostPath);2064 CheckComRCReturnRC (rc);2065 }2066 2067 mSharedFolders.insert (std::make_pair (aName, sharedFolder));2068 2069 /* notify console callbacks after the folder is added to the list */2070 {2071 CallbackList::iterator it = mCallbacks.begin();2072 while (it != mCallbacks.end())2073 (*it++)->OnSharedFolderChange (Scope_SessionScope);2074 }2075 2076 return rc;2077 }2078 2079 STDMETHODIMP Console::RemoveSharedFolder (INPTR BSTR aName)2080 {2081 if (!aName)2082 return E_INVALIDARG;2083 2084 AutoCaller autoCaller (this);2085 CheckComRCReturnRC (autoCaller.rc());2086 2087 AutoLock alock (this);2088 2089 /// @todo see @todo in AttachUSBDevice() about the Paused state2090 if (mMachineState == MachineState_Saved)2091 return setError (E_FAIL,2092 tr ("Cannot remove a transient shared folder from the "2093 "machine in the saved state"));2094 if (mMachineState > MachineState_Paused)2095 return setError (E_FAIL,2096 tr ("Cannot remove a transient shared folder from the "2097 "machine while it is changing the state (machine state: %d)"),2098 mMachineState);2099 2100 ComObjPtr <SharedFolder> sharedFolder;2101 HRESULT rc = findSharedFolder (aName, sharedFolder, true /* aSetError */);2102 CheckComRCReturnRC (rc);2103 2104 /* protect mpVM (if not NULL) */2105 AutoVMCallerQuietWeak autoVMCaller (this);2106 2107 if (mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive())2108 {2109 /* if the VM is online and supports shared folders, UNshare this2110 * folder. */2111 2112 /* first, remove the given folder */2113 rc = removeSharedFolder (aName);2114 CheckComRCReturnRC (rc);2115 2116 /* first, remove the machine or the global folder if there is any */2117 SharedFolderDataMap::const_iterator it;2118 if (findOtherSharedFolder (aName, it))2119 {2120 rc = createSharedFolder (aName, it->second);2121 /* don't check rc here because we need to remove the console2122 * folder from the collection even on failure */2123 }2124 }2125 2126 mSharedFolders.erase (aName);2127 2128 /* notify console callbacks after the folder is removed to the list */2129 {2130 CallbackList::iterator it = mCallbacks.begin();2131 while (it != mCallbacks.end())2132 (*it++)->OnSharedFolderChange (Scope_SessionScope);2133 }2134 2135 return rc;2136 }2137 2138 STDMETHODIMP Console::TakeSnapshot (INPTR BSTR aName, INPTR BSTR aDescription,2139 IProgress **aProgress)2140 {2141 LogFlowThisFuncEnter();2142 LogFlowThisFunc (("aName='%ls' mMachineState=%08X\n", aName, mMachineState));2143 2144 if (!aName)2145 return E_INVALIDARG;2146 if (!aProgress)2147 return E_POINTER;2148 2149 AutoCaller autoCaller (this);2150 CheckComRCReturnRC (autoCaller.rc());2151 2152 AutoLock alock (this);2153 2154 if (mMachineState > MachineState_Paused)2155 {2156 return setError (E_FAIL,2157 tr ("Cannot take a snapshot of the machine "2158 "while it is changing the state (machine state: %d)"),2159 mMachineState);2160 }2161 2162 /* memorize the current machine state */2163 MachineState_T lastMachineState = mMachineState;2164 2165 if (mMachineState == MachineState_Running)2166 {2167 HRESULT rc = Pause();2168 CheckComRCReturnRC (rc);2169 }2170 2171 HRESULT rc = S_OK;2172 2173 bool takingSnapshotOnline = mMachineState == MachineState_Paused;2174 2175 /*2176 * create a descriptionless VM-side progress object2177 * (only when creating a snapshot online)2178 */2179 ComObjPtr <Progress> saveProgress;2180 if (takingSnapshotOnline)2181 {2182 saveProgress.createObject();2183 rc = saveProgress->init (FALSE, 1, Bstr (tr ("Saving the execution state")));2184 AssertComRCReturn (rc, rc);2185 }2186 2187 bool beganTakingSnapshot = false;2188 bool taskCreationFailed = false;2189 2190 do2191 {2192 /* create a task object early to ensure mpVM protection is successful */2193 std::auto_ptr <VMSaveTask> task;2194 if (takingSnapshotOnline)2195 {2196 task.reset (new VMSaveTask (this, saveProgress));2197 rc = task->rc();2198 /*2199 * If we fail here it means a PowerDown() call happened on another2200 * thread while we were doing Pause() (which leaves the Console lock).2201 * We assign PowerDown() a higher precendence than TakeSnapshot(),2202 * therefore just return the error to the caller.2203 */2204 if (FAILED (rc))2205 {2206 taskCreationFailed = true;2207 break;2208 }2209 }2210 2211 Bstr stateFilePath;2212 ComPtr <IProgress> serverProgress;2213 2214 /*2215 * request taking a new snapshot object on the server2216 * (this will set the machine state to Saving on the server to block2217 * others from accessing this machine)2218 */2219 rc = mControl->BeginTakingSnapshot (this, aName, aDescription,2220 saveProgress, stateFilePath.asOutParam(),2221 serverProgress.asOutParam());2222 if (FAILED (rc))2223 break;2224 2225 /*2226 * state file is non-null only when the VM is paused2227 * (i.e. createing a snapshot online)2228 */2229 ComAssertBreak (2230 (!stateFilePath.isNull() && takingSnapshotOnline) ||2231 (stateFilePath.isNull() && !takingSnapshotOnline),2232 rc = E_FAIL);2233 2234 beganTakingSnapshot = true;2235 2236 /* sync the state with the server */2237 setMachineStateLocally (MachineState_Saving);2238 2239 /*2240 * create a combined VM-side progress object and start the save task2241 * (only when creating a snapshot online)2242 */2243 ComObjPtr <CombinedProgress> combinedProgress;2244 if (takingSnapshotOnline)2245 {2246 combinedProgress.createObject();2247 rc = combinedProgress->init ((IConsole *) this,2248 Bstr (tr ("Taking snapshot of virtual machine")),2249 serverProgress, saveProgress);2250 AssertComRCBreakRC (rc);2251 2252 /* setup task object and thread to carry out the operation asynchronously */2253 task->mIsSnapshot = true;2254 task->mSavedStateFile = stateFilePath;2255 task->mServerProgress = serverProgress;2256 /* set the state the operation thread will restore when it is finished */2257 task->mLastMachineState = lastMachineState;2258 2259 /* create a thread to wait until the VM state is saved */2260 int vrc = RTThreadCreate (NULL, Console::saveStateThread, (void *) task.get(),2261 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMTakeSnap");2262 2263 ComAssertMsgRCBreak (vrc, ("Could not create VMTakeSnap thread (%Vrc)\n", vrc),2264 rc = E_FAIL);2265 2266 /* task is now owned by saveStateThread(), so release it */2267 task.release();2268 }2269 2270 if (SUCCEEDED (rc))2271 {2272 /* return the correct progress to the caller */2273 if (combinedProgress)2274 combinedProgress.queryInterfaceTo (aProgress);2275 else2276 serverProgress.queryInterfaceTo (aProgress);2277 }2278 }2279 while (0);2280 2281 if (FAILED (rc) && !taskCreationFailed)2282 {2283 /* preserve existing error info */2284 ErrorInfoKeeper eik;2285 2286 if (beganTakingSnapshot && takingSnapshotOnline)2287 {2288 /*2289 * cancel the requested snapshot (only when creating a snapshot2290 * online, otherwise the server will cancel the snapshot itself).2291 * This will reset the machine state to the state it had right2292 * before calling mControl->BeginTakingSnapshot().2293 */2294 mControl->EndTakingSnapshot (FALSE);2295 }2296 2297 if (lastMachineState == MachineState_Running)2298 {2299 /* restore the paused state if appropriate */2300 setMachineStateLocally (MachineState_Paused);2301 /* restore the running state if appropriate */2302 Resume();2303 }2304 else2305 setMachineStateLocally (lastMachineState);2306 }2307 2308 LogFlowThisFunc (("rc=%08X\n", rc));2309 LogFlowThisFuncLeave();2310 return rc;2311 }2312 2313 STDMETHODIMP Console::DiscardSnapshot (INPTR GUIDPARAM aId, IProgress **aProgress)2314 {2315 if (Guid (aId).isEmpty())2316 return E_INVALIDARG;2317 if (!aProgress)2318 return E_POINTER;2319 2320 AutoCaller autoCaller (this);2321 CheckComRCReturnRC (autoCaller.rc());2322 2323 AutoLock alock (this);2324 2325 if (mMachineState >= MachineState_Running)2326 return setError (E_FAIL,2327 tr ("Cannot discard a snapshot of the running machine "2328 "(machine state: %d)"),2329 mMachineState);2330 2331 MachineState_T machineState = MachineState_InvalidMachineState;2332 HRESULT rc = mControl->DiscardSnapshot (this, aId, &machineState, aProgress);2333 CheckComRCReturnRC (rc);2334 2335 setMachineStateLocally (machineState);2336 return S_OK;2337 }2338 2339 STDMETHODIMP Console::DiscardCurrentState (IProgress **aProgress)2340 {2341 AutoCaller autoCaller (this);2342 CheckComRCReturnRC (autoCaller.rc());2343 2344 AutoLock alock (this);2345 2346 if (mMachineState >= MachineState_Running)2347 return setError (E_FAIL,2348 tr ("Cannot discard the current state of the running machine "2349 "(nachine state: %d)"),2350 mMachineState);2351 2352 MachineState_T machineState = MachineState_InvalidMachineState;2353 HRESULT rc = mControl->DiscardCurrentState (this, &machineState, aProgress);2354 CheckComRCReturnRC (rc);2355 2356 setMachineStateLocally (machineState);2357 return S_OK;2358 }2359 2360 STDMETHODIMP Console::DiscardCurrentSnapshotAndState (IProgress **aProgress)2361 {2362 AutoCaller autoCaller (this);2363 CheckComRCReturnRC (autoCaller.rc());2364 2365 AutoLock alock (this);2366 2367 if (mMachineState >= MachineState_Running)2368 return setError (E_FAIL,2369 tr ("Cannot discard the current snapshot and state of the "2370 "running machine (machine state: %d)"),2371 mMachineState);2372 2373 MachineState_T machineState = MachineState_InvalidMachineState;2374 HRESULT rc =2375 mControl->DiscardCurrentSnapshotAndState (this, &machineState, aProgress);2376 CheckComRCReturnRC (rc);2377 2378 setMachineStateLocally (machineState);2379 return S_OK;2380 }2381 2382 STDMETHODIMP Console::RegisterCallback (IConsoleCallback *aCallback)2383 {2384 if (!aCallback)2385 return E_INVALIDARG;2386 2387 AutoCaller autoCaller (this);2388 CheckComRCReturnRC (autoCaller.rc());2389 2390 AutoLock alock (this);2391 2392 mCallbacks.push_back (CallbackList::value_type (aCallback));2393 2394 /* Inform the callback about the current status (for example, the new2395 * callback must know the current mouse capabilities and the pointer2396 * shape in order to properly integrate the mouse pointer). */2397 2398 if (mCallbackData.mpsc.valid)2399 aCallback->OnMousePointerShapeChange (mCallbackData.mpsc.visible,2400 mCallbackData.mpsc.alpha,2401 mCallbackData.mpsc.xHot,2402 mCallbackData.mpsc.yHot,2403 mCallbackData.mpsc.width,2404 mCallbackData.mpsc.height,2405 mCallbackData.mpsc.shape);2406 if (mCallbackData.mcc.valid)2407 aCallback->OnMouseCapabilityChange (mCallbackData.mcc.supportsAbsolute,2408 mCallbackData.mcc.needsHostCursor);2409 2410 aCallback->OnAdditionsStateChange();2411 2412 if (mCallbackData.klc.valid)2413 aCallback->OnKeyboardLedsChange (mCallbackData.klc.numLock,2414 mCallbackData.klc.capsLock,2415 mCallbackData.klc.scrollLock);2416 2417 /* Note: we don't call OnStateChange for new callbacks because the2418 * machine state is a) not actually changed on callback registration2419 * and b) can be always queried from Console. */2420 2421 return S_OK;2422 }2423 2424 STDMETHODIMP Console::UnregisterCallback (IConsoleCallback *aCallback)2425 {2426 if (!aCallback)2427 return E_INVALIDARG;2428 2429 AutoCaller autoCaller (this);2430 CheckComRCReturnRC (autoCaller.rc());2431 2432 AutoLock alock (this);2433 2434 CallbackList::iterator it;2435 it = std::find (mCallbacks.begin(),2436 mCallbacks.end(),2437 CallbackList::value_type (aCallback));2438 if (it == mCallbacks.end())2439 return setError (E_INVALIDARG,2440 tr ("The given callback handler is not registered"));2441 2442 mCallbacks.erase (it);2443 return S_OK;2444 }2445 2446 // Non-interface public methods2447 /////////////////////////////////////////////////////////////////////////////2448 2449 /**2450 * Called by IInternalSessionControl::OnDVDDriveChange().2451 *2452 * @note Locks this object for reading.2453 */2454 HRESULT Console::onDVDDriveChange()2455 {2456 LogFlowThisFunc (("\n"));2457 2458 AutoCaller autoCaller (this);2459 AssertComRCReturnRC (autoCaller.rc());2460 2461 AutoReaderLock alock (this);2462 2463 /* Ignore callbacks when there's no VM around */2464 if (!mpVM)2465 return S_OK;2466 2467 /* protect mpVM */2468 AutoVMCaller autoVMCaller (this);2469 CheckComRCReturnRC (autoVMCaller.rc());2470 2471 /* Get the current DVD state */2472 HRESULT rc;2473 DriveState_T eState;2474 2475 rc = mDVDDrive->COMGETTER (State) (&eState);2476 ComAssertComRCRetRC (rc);2477 2478 /* Paranoia */2479 if ( eState == DriveState_NotMounted2480 && meDVDState == DriveState_NotMounted)2481 {2482 LogFlowThisFunc (("Returns (NotMounted -> NotMounted)\n"));2483 return S_OK;2484 }2485 2486 /* Get the path string and other relevant properties */2487 Bstr Path;2488 bool fPassthrough = false;2489 switch (eState)2490 {2491 case DriveState_ImageMounted:2492 {2493 ComPtr <IDVDImage> ImagePtr;2494 rc = mDVDDrive->GetImage (ImagePtr.asOutParam());2495 if (SUCCEEDED (rc))2496 rc = ImagePtr->COMGETTER(FilePath) (Path.asOutParam());2497 break;2498 }2499 2500 case DriveState_HostDriveCaptured:2501 {2502 ComPtr <IHostDVDDrive> DrivePtr;2503 BOOL enabled;2504 rc = mDVDDrive->GetHostDrive (DrivePtr.asOutParam());2505 if (SUCCEEDED (rc))2506 rc = DrivePtr->COMGETTER (Name) (Path.asOutParam());2507 if (SUCCEEDED (rc))2508 rc = mDVDDrive->COMGETTER (Passthrough) (&enabled);2509 if (SUCCEEDED (rc))2510 fPassthrough = !!enabled;2511 break;2512 }2513 2514 case DriveState_NotMounted:2515 break;2516 2517 default:2518 AssertMsgFailed (("Invalid DriveState: %d\n", eState));2519 rc = E_FAIL;2520 break;2521 }2522 2523 AssertComRC (rc);2524 if (FAILED (rc))2525 {2526 LogFlowThisFunc (("Returns %#x\n", rc));2527 return rc;2528 }2529 2530 rc = doDriveChange ("piix3ide", 0, 2, eState, &meDVDState,2531 Utf8Str (Path).raw(), fPassthrough);2532 2533 /* notify console callbacks on success */2534 if (SUCCEEDED (rc))2535 {2536 CallbackList::iterator it = mCallbacks.begin();2537 while (it != mCallbacks.end())2538 (*it++)->OnDVDDriveChange();2539 }2540 2541 return rc;2542 }2543 2544 2545 /**2546 * Called by IInternalSessionControl::OnFloppyDriveChange().2547 *2548 * @note Locks this object for reading.2549 */2550 HRESULT Console::onFloppyDriveChange()2551 {2552 LogFlowThisFunc (("\n"));2553 2554 AutoCaller autoCaller (this);2555 AssertComRCReturnRC (autoCaller.rc());2556 2557 AutoReaderLock alock (this);2558 2559 /* Ignore callbacks when there's no VM around */2560 if (!mpVM)2561 return S_OK;2562 2563 /* protect mpVM */2564 AutoVMCaller autoVMCaller (this);2565 CheckComRCReturnRC (autoVMCaller.rc());2566 2567 /* Get the current floppy state */2568 HRESULT rc;2569 DriveState_T eState;2570 2571 /* If the floppy drive is disabled, we're not interested */2572 BOOL fEnabled;2573 rc = mFloppyDrive->COMGETTER (Enabled) (&fEnabled);2574 ComAssertComRCRetRC (rc);2575 2576 if (!fEnabled)2577 return S_OK;2578 2579 rc = mFloppyDrive->COMGETTER (State) (&eState);2580 ComAssertComRCRetRC (rc);2581 2582 Log2 (("onFloppyDriveChange: eState=%d meFloppyState=%d\n", eState, meFloppyState));2583 2584 2585 /* Paranoia */2586 if ( eState == DriveState_NotMounted2587 && meFloppyState == DriveState_NotMounted)2588 {2589 LogFlowThisFunc (("Returns (NotMounted -> NotMounted)\n"));2590 return S_OK;2591 }2592 2593 /* Get the path string and other relevant properties */2594 Bstr Path;2595 switch (eState)2596 {2597 case DriveState_ImageMounted:2598 {2599 ComPtr <IFloppyImage> ImagePtr;2600 rc = mFloppyDrive->GetImage (ImagePtr.asOutParam());2601 if (SUCCEEDED (rc))2602 rc = ImagePtr->COMGETTER(FilePath) (Path.asOutParam());2603 break;2604 }2605 2606 case DriveState_HostDriveCaptured:2607 {2608 ComPtr <IHostFloppyDrive> DrivePtr;2609 rc = mFloppyDrive->GetHostDrive (DrivePtr.asOutParam());2610 if (SUCCEEDED (rc))2611 rc = DrivePtr->COMGETTER (Name) (Path.asOutParam());2612 break;2613 }2614 2615 case DriveState_NotMounted:2616 break;2617 2618 default:2619 AssertMsgFailed (("Invalid DriveState: %d\n", eState));2620 rc = E_FAIL;2621 break;2622 }2623 2624 AssertComRC (rc);2625 if (FAILED (rc))2626 {2627 LogFlowThisFunc (("Returns %#x\n", rc));2628 return rc;2629 }2630 2631 rc = doDriveChange ("i82078", 0, 0, eState, &meFloppyState,2632 Utf8Str (Path).raw(), false);2633 2634 /* notify console callbacks on success */2635 if (SUCCEEDED (rc))2636 {2637 CallbackList::iterator it = mCallbacks.begin();2638 while (it != mCallbacks.end())2639 (*it++)->OnFloppyDriveChange();2640 }2641 2642 return rc;2643 }2644 2645 2646 /**2647 * Process a floppy or dvd change.2648 *2649 * @returns COM status code.2650 *2651 * @param pszDevice The PDM device name.2652 * @param uInstance The PDM device instance.2653 * @param uLun The PDM LUN number of the drive.2654 * @param eState The new state.2655 * @param peState Pointer to the variable keeping the actual state of the drive.2656 * This will be both read and updated to eState or other appropriate state.2657 * @param pszPath The path to the media / drive which is now being mounted / captured.2658 * If NULL no media or drive is attached and the lun will be configured with2659 * the default block driver with no media. This will also be the state if2660 * mounting / capturing the specified media / drive fails.2661 * @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.2662 *2663 * @note Locks this object for reading.2664 */2665 HRESULT Console::doDriveChange (const char *pszDevice, unsigned uInstance, unsigned uLun, DriveState_T eState,2666 DriveState_T *peState, const char *pszPath, bool fPassthrough)2667 {2668 LogFlowThisFunc (("pszDevice=%p:{%s} uInstance=%u uLun=%u eState=%d "2669 "peState=%p:{%d} pszPath=%p:{%s} fPassthrough=%d\n",2670 pszDevice, pszDevice, uInstance, uLun, eState,2671 peState, *peState, pszPath, pszPath, fPassthrough));2672 2673 AutoCaller autoCaller (this);2674 AssertComRCReturnRC (autoCaller.rc());2675 2676 AutoReaderLock alock (this);2677 2678 /* protect mpVM */2679 AutoVMCaller autoVMCaller (this);2680 CheckComRCReturnRC (autoVMCaller.rc());2681 2682 /*2683 * Call worker in EMT, that's faster and safer than doing everything2684 * using VM3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait2685 * here to make requests from under the lock in order to serialize them.2686 */2687 PVMREQ pReq;2688 int vrc = VMR3ReqCall (mpVM, &pReq, 0 /* no wait! */,2689 (PFNRT) Console::changeDrive, 8,2690 this, pszDevice, uInstance, uLun, eState, peState,2691 pszPath, fPassthrough);2692 /// @todo (r=dmik) bird, it would be nice to have a special VMR3Req method2693 // for that purpose, that doesn't return useless VERR_TIMEOUT2694 if (vrc == VERR_TIMEOUT)2695 vrc = VINF_SUCCESS;2696 2697 /* leave the lock before waiting for a result (EMT will call us back!) */2698 alock.leave();2699 2700 if (VBOX_SUCCESS (vrc))2701 {2702 vrc = VMR3ReqWait (pReq, RT_INDEFINITE_WAIT);2703 AssertRC (vrc);2704 if (VBOX_SUCCESS (vrc))2705 vrc = pReq->iStatus;2706 }2707 VMR3ReqFree (pReq);2708 2709 if (VBOX_SUCCESS (vrc))2710 {2711 LogFlowThisFunc (("Returns S_OK\n"));2712 return S_OK;2713 }2714 2715 if (pszPath)2716 return setError (E_FAIL,2717 tr ("Could not mount the media/drive '%s' (%Vrc)"), pszPath, vrc);2718 2719 return setError (E_FAIL,2720 tr ("Could not unmount the currently mounted media/drive (%Vrc)"), vrc);2721 }2722 2723 2724 /**2725 * Performs the Floppy/DVD change in EMT.2726 *2727 * @returns VBox status code.2728 *2729 * @param pThis Pointer to the Console object.2730 * @param pszDevice The PDM device name.2731 * @param uInstance The PDM device instance.2732 * @param uLun The PDM LUN number of the drive.2733 * @param eState The new state.2734 * @param peState Pointer to the variable keeping the actual state of the drive.2735 * This will be both read and updated to eState or other appropriate state.2736 * @param pszPath The path to the media / drive which is now being mounted / captured.2737 * If NULL no media or drive is attached and the lun will be configured with2738 * the default block driver with no media. This will also be the state if2739 * mounting / capturing the specified media / drive fails.2740 * @param fPassthrough Enables using passthrough mode of the host DVD drive if applicable.2741 *2742 * @thread EMT2743 * @note Locks the Console object for writing2744 */2745 DECLCALLBACK(int) Console::changeDrive (Console *pThis, const char *pszDevice, unsigned uInstance, unsigned uLun,2746 DriveState_T eState, DriveState_T *peState,2747 const char *pszPath, bool fPassthrough)2748 {2749 LogFlowFunc (("pThis=%p pszDevice=%p:{%s} uInstance=%u uLun=%u eState=%d "2750 "peState=%p:{%d} pszPath=%p:{%s} fPassthrough=%d\n",2751 pThis, pszDevice, pszDevice, uInstance, uLun, eState,2752 peState, *peState, pszPath, pszPath, fPassthrough));2753 2754 AssertReturn (pThis, VERR_INVALID_PARAMETER);2755 2756 AssertMsg ( (!strcmp (pszDevice, "i82078") && uLun == 0 && uInstance == 0)2757 || (!strcmp (pszDevice, "piix3ide") && uLun == 2 && uInstance == 0),2758 ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance));2759 2760 AutoCaller autoCaller (pThis);2761 AssertComRCReturn (autoCaller.rc(), VERR_ACCESS_DENIED);2762 2763 /*2764 * Locking the object before doing VMR3* calls is quite safe here,2765 * since we're on EMT. Write lock is necessary because we're indirectly2766 * modify the meDVDState/meFloppyState members (pointed to by peState).2767 */2768 AutoLock alock (pThis);2769 2770 /* protect mpVM */2771 AutoVMCaller autoVMCaller (pThis);2772 CheckComRCReturnRC (autoVMCaller.rc());2773 2774 PVM pVM = pThis->mpVM;2775 2776 /*2777 * Suspend the VM first.2778 *2779 * The VM must not be running since it might have pending I/O to2780 * the drive which is being changed.2781 */2782 bool fResume;2783 VMSTATE enmVMState = VMR3GetState (pVM);2784 switch (enmVMState)2785 {2786 case VMSTATE_RESETTING:2787 case VMSTATE_RUNNING:2788 {2789 LogFlowFunc (("Suspending the VM...\n"));2790 /* disable the callback to prevent Console-level state change */2791 pThis->mVMStateChangeCallbackDisabled = true;2792 int rc = VMR3Suspend (pVM);2793 pThis->mVMStateChangeCallbackDisabled = false;2794 AssertRCReturn (rc, rc);2795 fResume = true;2796 break;2797 }2798 2799 case VMSTATE_SUSPENDED:2800 case VMSTATE_CREATED:2801 case VMSTATE_OFF:2802 fResume = false;2803 break;2804 2805 default:2806 AssertMsgFailedReturn (("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED);2807 }2808 2809 int rc = VINF_SUCCESS;2810 int rcRet = VINF_SUCCESS;2811 2812 do2813 {2814 /*2815 * Unmount existing media / detach host drive.2816 */2817 PPDMIMOUNT pIMount = NULL;2818 switch (*peState)2819 {2820 2821 case DriveState_ImageMounted:2822 {2823 /*2824 * Resolve the interface.2825 */2826 PPDMIBASE pBase;2827 rc = PDMR3QueryLun (pVM, pszDevice, uInstance, uLun, &pBase);2828 if (VBOX_FAILURE (rc))2829 {2830 if (rc == VERR_PDM_LUN_NOT_FOUND)2831 rc = VINF_SUCCESS;2832 AssertRC (rc);2833 break;2834 }2835 2836 pIMount = (PPDMIMOUNT) pBase->pfnQueryInterface (pBase, PDMINTERFACE_MOUNT);2837 AssertBreak (pIMount, rc = VERR_INVALID_POINTER);2838 2839 /*2840 * Unmount the media.2841 */2842 rc = pIMount->pfnUnmount (pIMount, false);2843 if (rc == VERR_PDM_MEDIA_NOT_MOUNTED)2844 rc = VINF_SUCCESS;2845 break;2846 }2847 2848 case DriveState_HostDriveCaptured:2849 {2850 rc = PDMR3DeviceDetach (pVM, pszDevice, uInstance, uLun);2851 if (rc == VINF_PDM_NO_DRIVER_ATTACHED_TO_LUN)2852 rc = VINF_SUCCESS;2853 AssertRC (rc);2854 break;2855 }2856 2857 case DriveState_NotMounted:2858 break;2859 2860 default:2861 AssertMsgFailed (("Invalid *peState: %d\n", peState));2862 break;2863 }2864 2865 if (VBOX_FAILURE (rc))2866 {2867 rcRet = rc;2868 break;2869 }2870 2871 /*2872 * Nothing is currently mounted.2873 */2874 *peState = DriveState_NotMounted;2875 2876 2877 /*2878 * Process the HostDriveCaptured state first, as the fallback path2879 * means mounting the normal block driver without media.2880 */2881 if (eState == DriveState_HostDriveCaptured)2882 {2883 /*2884 * Detach existing driver chain (block).2885 */2886 int rc = PDMR3DeviceDetach (pVM, pszDevice, uInstance, uLun);2887 if (VBOX_FAILURE (rc))2888 {2889 if (rc == VERR_PDM_LUN_NOT_FOUND)2890 rc = VINF_SUCCESS;2891 AssertReleaseRC (rc);2892 break; /* we're toast */2893 }2894 pIMount = NULL;2895 2896 /*2897 * Construct a new driver configuration.2898 */2899 PCFGMNODE pInst = CFGMR3GetChildF (CFGMR3GetRoot (pVM), "Devices/%s/%d/", pszDevice, uInstance);2900 AssertRelease (pInst);2901 /* nuke anything which might have been left behind. */2902 CFGMR3RemoveNode (CFGMR3GetChildF (pInst, "LUN#%d", uLun));2903 2904 /* create a new block driver config */2905 PCFGMNODE pLunL0;2906 PCFGMNODE pCfg;2907 if ( VBOX_SUCCESS (rc = CFGMR3InsertNodeF (pInst, &pLunL0, "LUN#%u", uLun))2908 && VBOX_SUCCESS (rc = CFGMR3InsertString (pLunL0, "Driver", !strcmp (pszDevice, "i82078") ? "HostFloppy" : "HostDVD"))2909 && VBOX_SUCCESS (rc = CFGMR3InsertNode (pLunL0, "Config", &pCfg))2910 && VBOX_SUCCESS (rc = CFGMR3InsertString (pCfg, "Path", pszPath))2911 && VBOX_SUCCESS (rc = !strcmp (pszDevice, "i82078") ? VINF_SUCCESS : CFGMR3InsertInteger(pCfg, "Passthrough", fPassthrough)))2912 {2913 /*2914 * Attempt to attach the driver.2915 */2916 rc = PDMR3DeviceAttach (pVM, pszDevice, uInstance, uLun, NULL);2917 AssertRC (rc);2918 }2919 if (VBOX_FAILURE (rc))2920 rcRet = rc;2921 }2922 2923 /*2924 * Process the ImageMounted, NotMounted and failed HostDriveCapture cases.2925 */2926 rc = VINF_SUCCESS;2927 switch (eState)2928 {2929 #define RC_CHECK() do { if (VBOX_FAILURE (rc)) { AssertReleaseRC (rc); break; } } while (0)2930 2931 case DriveState_HostDriveCaptured:2932 if (VBOX_SUCCESS (rcRet))2933 break;2934 /* fallback: umounted block driver. */2935 pszPath = NULL;2936 eState = DriveState_NotMounted;2937 /* fallthru */2938 case DriveState_ImageMounted:2939 case DriveState_NotMounted:2940 {2941 /*2942 * Resolve the drive interface / create the driver.2943 */2944 if (!pIMount)2945 {2946 PPDMIBASE pBase;2947 rc = PDMR3QueryLun (pVM, pszDevice, uInstance, uLun, &pBase);2948 if (rc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN)2949 {2950 /*2951 * We have to create it, so we'll do the full config setup and everything.2952 */2953 PCFGMNODE pIdeInst = CFGMR3GetChildF (CFGMR3GetRoot (pVM), "Devices/%s/%d/", pszDevice, uInstance);2954 AssertRelease (pIdeInst);2955 2956 /* nuke anything which might have been left behind. */2957 CFGMR3RemoveNode (CFGMR3GetChildF (pIdeInst, "LUN#%d", uLun));2958 2959 /* create a new block driver config */2960 PCFGMNODE pLunL0;2961 rc = CFGMR3InsertNodeF (pIdeInst, &pLunL0, "LUN#%d", uLun); RC_CHECK();2962 rc = CFGMR3InsertString (pLunL0, "Driver", "Block"); RC_CHECK();2963 PCFGMNODE pCfg;2964 rc = CFGMR3InsertNode (pLunL0, "Config", &pCfg); RC_CHECK();2965 rc = CFGMR3InsertString (pCfg, "Type", !strcmp (pszDevice, "i82078") ? "Floppy 1.44" : "DVD");2966 RC_CHECK();2967 rc = CFGMR3InsertInteger (pCfg, "Mountable", 1); RC_CHECK();2968 2969 /*2970 * Attach the driver.2971 */2972 rc = PDMR3DeviceAttach (pVM, pszDevice, uInstance, uLun, &pBase);2973 RC_CHECK();2974 }2975 else if (VBOX_FAILURE(rc))2976 {2977 AssertRC (rc);2978 return rc;2979 }2980 2981 pIMount = (PPDMIMOUNT) pBase->pfnQueryInterface (pBase, PDMINTERFACE_MOUNT);2982 if (!pIMount)2983 {2984 AssertFailed();2985 return rc;2986 }2987 }2988 2989 /*2990 * If we've got an image, let's mount it.2991 */2992 if (pszPath && *pszPath)2993 {2994 rc = pIMount->pfnMount (pIMount, pszPath, strcmp (pszDevice, "i82078") ? "MediaISO" : "RawImage");2995 if (VBOX_FAILURE (rc))2996 eState = DriveState_NotMounted;2997 }2998 break;2999 }3000 3001 default:3002 AssertMsgFailed (("Invalid eState: %d\n", eState));3003 break;3004 3005 #undef RC_CHECK3006 }3007 3008 if (VBOX_FAILURE (rc) && VBOX_SUCCESS (rcRet))3009 rcRet = rc;3010 3011 *peState = eState;3012 }3013 while (0);3014 3015 /*3016 * Resume the VM if necessary.3017 */3018 if (fResume)3019 {3020 LogFlowFunc (("Resuming the VM...\n"));3021 /* disable the callback to prevent Console-level state change */3022 pThis->mVMStateChangeCallbackDisabled = true;3023 rc = VMR3Resume (pVM);3024 pThis->mVMStateChangeCallbackDisabled = false;3025 AssertRC (rc);3026 if (VBOX_FAILURE (rc))3027 {3028 /* too bad, we failed. try to sync the console state with the VMM state */3029 vmstateChangeCallback (pVM, VMSTATE_SUSPENDED, enmVMState, pThis);3030 }3031 /// @todo (r=dmik) if we failed with drive mount, then the VMR3Resume3032 // error (if any) will be hidden from the caller. For proper reporting3033 // of such multiple errors to the caller we need to enhance the3034 // IVurtualBoxError interface. For now, give the first error the higher3035 // priority.3036 if (VBOX_SUCCESS (rcRet))3037 rcRet = rc;3038 }3039 3040 LogFlowFunc (("Returning %Vrc\n", rcRet));3041 return rcRet;3042 }3043 3044 3045 /**3046 * Called by IInternalSessionControl::OnNetworkAdapterChange().3047 *3048 * @note Locks this object for writing.3049 */3050 HRESULT Console::onNetworkAdapterChange (INetworkAdapter *aNetworkAdapter)3051 {3052 LogFlowThisFunc (("\n"));3053 3054 AutoCaller autoCaller (this);3055 AssertComRCReturnRC (autoCaller.rc());3056 3057 AutoLock alock (this);3058 3059 /* Don't do anything if the VM isn't running */3060 if (!mpVM)3061 return S_OK;3062 3063 /* protect mpVM */3064 AutoVMCaller autoVMCaller (this);3065 CheckComRCReturnRC (autoVMCaller.rc());3066 3067 /* Get the properties we need from the adapter */3068 BOOL fCableConnected;3069 HRESULT rc = aNetworkAdapter->COMGETTER(CableConnected) (&fCableConnected);3070 AssertComRC(rc);3071 if (SUCCEEDED(rc))3072 {3073 ULONG ulInstance;3074 rc = aNetworkAdapter->COMGETTER(Slot) (&ulInstance);3075 AssertComRC (rc);3076 if (SUCCEEDED (rc))3077 {3078 /*3079 * Find the pcnet instance, get the config interface and update3080 * the link state.3081 */3082 PPDMIBASE pBase;3083 int vrc = PDMR3QueryDeviceLun (mpVM, "pcnet", (unsigned) ulInstance,3084 0, &pBase);3085 ComAssertRC (vrc);3086 if (VBOX_SUCCESS (vrc))3087 {3088 Assert(pBase);3089 PPDMINETWORKCONFIG pINetCfg = (PPDMINETWORKCONFIG) pBase->3090 pfnQueryInterface(pBase, PDMINTERFACE_NETWORK_CONFIG);3091 if (pINetCfg)3092 {3093 Log (("Console::onNetworkAdapterChange: setting link state to %d\n",3094 fCableConnected));3095 vrc = pINetCfg->pfnSetLinkState (pINetCfg,3096 fCableConnected ? PDMNETWORKLINKSTATE_UP3097 : PDMNETWORKLINKSTATE_DOWN);3098 ComAssertRC (vrc);3099 }3100 }3101 3102 if (VBOX_FAILURE (vrc))3103 rc = E_FAIL;3104 }3105 }3106 3107 /* notify console callbacks on success */3108 if (SUCCEEDED (rc))3109 {3110 CallbackList::iterator it = mCallbacks.begin();3111 while (it != mCallbacks.end())3112 (*it++)->OnNetworkAdapterChange (aNetworkAdapter);3113 }3114 3115 LogFlowThisFunc (("Leaving rc=%#x\n", rc));3116 return rc;3117 }3118 3119 /**3120 * Called by IInternalSessionControl::OnSerialPortChange().3121 *3122 * @note Locks this object for writing.3123 */3124 HRESULT Console::onSerialPortChange (ISerialPort *aSerialPort)3125 {3126 LogFlowThisFunc (("\n"));3127 3128 AutoCaller autoCaller (this);3129 AssertComRCReturnRC (autoCaller.rc());3130 3131 AutoLock alock (this);3132 3133 /* Don't do anything if the VM isn't running */3134 if (!mpVM)3135 return S_OK;3136 3137 HRESULT rc = S_OK;3138 3139 /* protect mpVM */3140 AutoVMCaller autoVMCaller (this);3141 CheckComRCReturnRC (autoVMCaller.rc());3142 3143 /* nothing to do so far */3144 3145 /* notify console callbacks on success */3146 if (SUCCEEDED (rc))3147 {3148 CallbackList::iterator it = mCallbacks.begin();3149 while (it != mCallbacks.end())3150 (*it++)->OnSerialPortChange (aSerialPort);3151 }3152 3153 LogFlowThisFunc (("Leaving rc=%#x\n", rc));3154 return rc;3155 }3156 3157 /**3158 * Called by IInternalSessionControl::OnParallelPortChange().3159 *3160 * @note Locks this object for writing.3161 */3162 HRESULT Console::onParallelPortChange (IParallelPort *aParallelPort)3163 {3164 LogFlowThisFunc (("\n"));3165 3166 AutoCaller autoCaller (this);3167 AssertComRCReturnRC (autoCaller.rc());3168 3169 AutoLock alock (this);3170 3171 /* Don't do anything if the VM isn't running */3172 if (!mpVM)3173 return S_OK;3174 3175 HRESULT rc = S_OK;3176 3177 /* protect mpVM */3178 AutoVMCaller autoVMCaller (this);3179 CheckComRCReturnRC (autoVMCaller.rc());3180 3181 /* nothing to do so far */3182 3183 /* notify console callbacks on success */3184 if (SUCCEEDED (rc))3185 {3186 CallbackList::iterator it = mCallbacks.begin();3187 while (it != mCallbacks.end())3188 (*it++)->OnParallelPortChange (aParallelPort);3189 }3190 3191 LogFlowThisFunc (("Leaving rc=%#x\n", rc));3192 return rc;3193 }3194 3195 /**3196 * Called by IInternalSessionControl::OnVRDPServerChange().3197 *3198 * @note Locks this object for writing.3199 */3200 HRESULT Console::onVRDPServerChange()3201 {3202 AutoCaller autoCaller (this);3203 AssertComRCReturnRC (autoCaller.rc());3204 3205 AutoLock alock (this);3206 3207 HRESULT rc = S_OK;3208 3209 if (mVRDPServer && mMachineState == MachineState_Running)3210 {3211 BOOL vrdpEnabled = FALSE;3212 3213 rc = mVRDPServer->COMGETTER(Enabled) (&vrdpEnabled);3214 ComAssertComRCRetRC (rc);3215 3216 if (vrdpEnabled)3217 {3218 // If there was no VRDP server started the 'stop' will do nothing.3219 // However if a server was started and this notification was called,3220 // we have to restart the server.3221 mConsoleVRDPServer->Stop ();3222 3223 if (VBOX_FAILURE(mConsoleVRDPServer->Launch ()))3224 {3225 rc = E_FAIL;3226 }3227 else3228 {3229 #ifdef VRDP_NO_COM3230 mConsoleVRDPServer->EnableConnections ();3231 #else3232 mConsoleVRDPServer->SetCallback ();3233 #endif /* VRDP_NO_COM */3234 }3235 }3236 else3237 {3238 mConsoleVRDPServer->Stop ();3239 }3240 }3241 3242 /* notify console callbacks on success */3243 if (SUCCEEDED (rc))3244 {3245 CallbackList::iterator it = mCallbacks.begin();3246 while (it != mCallbacks.end())3247 (*it++)->OnVRDPServerChange();3248 }3249 3250 return rc;3251 }3252 3253 /**3254 * Called by IInternalSessionControl::OnUSBControllerChange().3255 *3256 * @note Locks this object for writing.3257 */3258 HRESULT Console::onUSBControllerChange()3259 {3260 LogFlowThisFunc (("\n"));3261 3262 AutoCaller autoCaller (this);3263 AssertComRCReturnRC (autoCaller.rc());3264 3265 AutoLock alock (this);3266 3267 /* Ignore if no VM is running yet. */3268 if (!mpVM)3269 return S_OK;3270 3271 HRESULT rc = S_OK;3272 3273 /// @todo (dmik)3274 // check for the Enabled state and disable virtual USB controller??3275 // Anyway, if we want to query the machine's USB Controller we need to cache3276 // it to to mUSBController in #init() (as it is done with mDVDDrive).3277 //3278 // bird: While the VM supports hot-plugging, I doubt any guest can handle it at this time... :-)3279 //3280 // /* protect mpVM */3281 // AutoVMCaller autoVMCaller (this);3282 // CheckComRCReturnRC (autoVMCaller.rc());3283 3284 /* notify console callbacks on success */3285 if (SUCCEEDED (rc))3286 {3287 CallbackList::iterator it = mCallbacks.begin();3288 while (it != mCallbacks.end())3289 (*it++)->OnUSBControllerChange();3290 }3291 3292 return rc;3293 }3294 3295 /**3296 * Called by IInternalSessionControl::OnSharedFolderChange().3297 *3298 * @note Locks this object for writing.3299 */3300 HRESULT Console::onSharedFolderChange (BOOL aGlobal)3301 {3302 LogFlowThisFunc (("aGlobal=%RTbool\n", aGlobal));3303 3304 AutoCaller autoCaller (this);3305 AssertComRCReturnRC (autoCaller.rc());3306 3307 AutoLock alock (this);3308 3309 HRESULT rc = fetchSharedFolders (aGlobal);3310 3311 /* notify console callbacks on success */3312 if (SUCCEEDED (rc))3313 {3314 CallbackList::iterator it = mCallbacks.begin();3315 while (it != mCallbacks.end())3316 (*it++)->OnSharedFolderChange (aGlobal ? (Scope_T)Scope_GlobalScope3317 : (Scope_T)Scope_MachineScope);3318 }3319 3320 return rc;3321 }3322 3323 /**3324 * Called by IInternalSessionControl::OnUSBDeviceAttach() or locally by3325 * processRemoteUSBDevices() after IInternalMachineControl::RunUSBDeviceFilters()3326 * returns TRUE for a given remote USB device.3327 *3328 * @return S_OK if the device was attached to the VM.3329 * @return failure if not attached.3330 *3331 * @param aDevice3332 * The device in question.3333 *3334 * @note Locks this object for writing.3335 */3336 HRESULT Console::onUSBDeviceAttach (IUSBDevice *aDevice, IVirtualBoxErrorInfo *aError)3337 {3338 LogFlowThisFunc (("aDevice=%p aError=%p\n", aDevice, aError));3339 3340 AutoCaller autoCaller (this);3341 ComAssertComRCRetRC (autoCaller.rc());3342 3343 AutoLock alock (this);3344 3345 /* protect mpVM (we don't need error info, since it's a callback) */3346 AutoVMCallerQuiet autoVMCaller (this);3347 if (FAILED (autoVMCaller.rc()))3348 {3349 /* The VM may be no more operational when this message arrives3350 * (e.g. it may be Saving or Stopping or just PoweredOff) --3351 * autoVMCaller.rc() will return a failure in this case. */3352 LogFlowThisFunc (("Attach request ignored (mMachineState=%d).\n",3353 mMachineState));3354 return autoVMCaller.rc();3355 }3356 3357 if (aError != NULL)3358 {3359 /* notify callbacks about the error */3360 onUSBDeviceStateChange (aDevice, true /* aAttached */, aError);3361 return S_OK;3362 }3363 3364 #if 13365 /* Don't proceed unless we've found the usb controller. */3366 PPDMIBASE pBase = NULL;3367 int vrc = PDMR3QueryLun (mpVM, "usb-ohci", 0, 0, &pBase);3368 if (VBOX_FAILURE (vrc))3369 {3370 LogFlowThisFunc (("Attach request ignored (no USB controller).\n"));3371 return E_FAIL;3372 }3373 3374 PVUSBIRHCONFIG pRhConfig = (PVUSBIRHCONFIG) pBase->3375 pfnQueryInterface (pBase, PDMINTERFACE_VUSB_RH_CONFIG);3376 ComAssertRet (pRhConfig, E_FAIL);3377 3378 HRESULT rc = attachUSBDevice (aDevice, pRhConfig);3379 #else /* PDMUsb */3380 /* Don't proceed unless there's a USB hub. */3381 if (!PDMR3USBHasHub (m_VM))3382 {3383 LogFlowThisFunc (("Attach request ignored (no USB controller).\n"));3384 return E_FAIL;3385 }3386 3387 HRESULT rc = attachUSBDevice (aDevice);3388 #endif /* PDMUsb */3389 3390 if (FAILED (rc))3391 {3392 /* take the current error info */3393 com::ErrorInfoKeeper eik;3394 /* the error must be a VirtualBoxErrorInfo instance */3395 ComPtr <IVirtualBoxErrorInfo> error = eik.takeError();3396 Assert (!error.isNull());3397 if (!error.isNull())3398 {3399 /* notify callbacks about the error */3400 onUSBDeviceStateChange (aDevice, true /* aAttached */, error);3401 }3402 }3403 3404 return rc;3405 }3406 3407 /**3408 * Called by IInternalSessionControl::OnUSBDeviceDetach() and locally by3409 * processRemoteUSBDevices().3410 *3411 * @note Locks this object for writing.3412 */3413 HRESULT Console::onUSBDeviceDetach (INPTR GUIDPARAM aId,3414 IVirtualBoxErrorInfo *aError)3415 {3416 Guid Uuid (aId);3417 LogFlowThisFunc (("aId={%Vuuid} aError=%p\n", Uuid.raw(), aError));3418 3419 AutoCaller autoCaller (this);3420 AssertComRCReturnRC (autoCaller.rc());3421 3422 AutoLock alock (this);3423 3424 /* Find the device. */3425 ComObjPtr <OUSBDevice> device;3426 USBDeviceList::iterator it = mUSBDevices.begin();3427 while (it != mUSBDevices.end())3428 {3429 LogFlowThisFunc (("it={%Vuuid}\n", (*it)->id().raw()));3430 if ((*it)->id() == Uuid)3431 {3432 device = *it;3433 break;3434 }3435 ++ it;3436 }3437 3438 3439 if (device.isNull())3440 {3441 LogFlowThisFunc (("USB device not found.\n"));3442 3443 /* The VM may be no more operational when this message arrives3444 * (e.g. it may be Saving or Stopping or just PoweredOff). Use3445 * AutoVMCaller to detect it -- AutoVMCaller::rc() will return a3446 * failure in this case. */3447 3448 AutoVMCallerQuiet autoVMCaller (this);3449 if (FAILED (autoVMCaller.rc()))3450 {3451 LogFlowThisFunc (("Detach request ignored (mMachineState=%d).\n",3452 mMachineState));3453 return autoVMCaller.rc();3454 }3455 3456 /* the device must be in the list otherwise */3457 AssertFailedReturn (E_FAIL);3458 }3459 3460 if (aError != NULL)3461 {3462 /* notify callback about an error */3463 onUSBDeviceStateChange (device, false /* aAttached */, aError);3464 return S_OK;3465 }3466 3467 HRESULT rc = detachUSBDevice (it);3468 3469 if (FAILED (rc))3470 {3471 /* take the current error info */3472 com::ErrorInfoKeeper eik;3473 /* the error must be a VirtualBoxErrorInfo instance */3474 ComPtr <IVirtualBoxErrorInfo> error = eik.takeError();3475 Assert (!error.isNull());3476 if (!error.isNull())3477 {3478 /* notify callbacks about the error */3479 onUSBDeviceStateChange (device, false /* aAttached */, error);3480 }3481 }3482 3483 return rc;3484 }3485 3486 /**3487 * Gets called by Session::UpdateMachineState()3488 * (IInternalSessionControl::updateMachineState()).3489 *3490 * Must be called only in certain cases (see the implementation).3491 *3492 * @note Locks this object for writing.3493 */3494 HRESULT Console::updateMachineState (MachineState_T aMachineState)3495 {3496 AutoCaller autoCaller (this);3497 AssertComRCReturnRC (autoCaller.rc());3498 3499 AutoLock alock (this);3500 3501 AssertReturn (mMachineState == MachineState_Saving ||3502 mMachineState == MachineState_Discarding,3503 E_FAIL);3504 3505 return setMachineStateLocally (aMachineState);3506 }3507 3508 /**3509 * @note Locks this object for writing.3510 */3511 void Console::onMousePointerShapeChange(bool fVisible, bool fAlpha,3512 uint32_t xHot, uint32_t yHot,3513 uint32_t width, uint32_t height,3514 void *pShape)3515 {3516 #if 03517 LogFlowThisFuncEnter();3518 LogFlowThisFunc (("fVisible=%d, fAlpha=%d, xHot = %d, yHot = %d, width=%d, "3519 "height=%d, shape=%p\n",3520 fVisible, fAlpha, xHot, yHot, width, height, pShape));3521 #endif3522 3523 AutoCaller autoCaller (this);3524 AssertComRCReturnVoid (autoCaller.rc());3525 3526 /* We need a write lock because we alter the cached callback data */3527 AutoLock alock (this);3528 3529 /* Save the callback arguments */3530 mCallbackData.mpsc.visible = fVisible;3531 mCallbackData.mpsc.alpha = fAlpha;3532 mCallbackData.mpsc.xHot = xHot;3533 mCallbackData.mpsc.yHot = yHot;3534 mCallbackData.mpsc.width = width;3535 mCallbackData.mpsc.height = height;3536 3537 /* start with not valid */3538 bool wasValid = mCallbackData.mpsc.valid;3539 mCallbackData.mpsc.valid = false;3540 3541 if (pShape != NULL)3542 {3543 size_t cb = (width + 7) / 8 * height; /* size of the AND mask */3544 cb += ((cb + 3) & ~3) + width * 4 * height; /* + gap + size of the XOR mask */3545 /* try to reuse the old shape buffer if the size is the same */3546 if (!wasValid)3547 mCallbackData.mpsc.shape = NULL;3548 else3549 if (mCallbackData.mpsc.shape != NULL && mCallbackData.mpsc.shapeSize != cb)3550 {3551 RTMemFree (mCallbackData.mpsc.shape);3552 mCallbackData.mpsc.shape = NULL;3553 }3554 if (mCallbackData.mpsc.shape == NULL)3555 {3556 mCallbackData.mpsc.shape = (BYTE *) RTMemAllocZ (cb);3557 AssertReturnVoid (mCallbackData.mpsc.shape);3558 }3559 mCallbackData.mpsc.shapeSize = cb;3560 memcpy (mCallbackData.mpsc.shape, pShape, cb);3561 }3562 else3563 {3564 if (wasValid && mCallbackData.mpsc.shape != NULL)3565 RTMemFree (mCallbackData.mpsc.shape);3566 mCallbackData.mpsc.shape = NULL;3567 mCallbackData.mpsc.shapeSize = 0;3568 }3569 3570 mCallbackData.mpsc.valid = true;3571 3572 CallbackList::iterator it = mCallbacks.begin();3573 while (it != mCallbacks.end())3574 (*it++)->OnMousePointerShapeChange (fVisible, fAlpha, xHot, yHot,3575 width, height, (BYTE *) pShape);3576 3577 #if 03578 LogFlowThisFuncLeave();3579 #endif3580 }3581 3582 /**3583 * @note Locks this object for writing.3584 */3585 void Console::onMouseCapabilityChange (BOOL supportsAbsolute, BOOL needsHostCursor)3586 {3587 LogFlowThisFunc (("supportsAbsolute=%d needsHostCursor=%d\n",3588 supportsAbsolute, needsHostCursor));3589 3590 AutoCaller autoCaller (this);3591 AssertComRCReturnVoid (autoCaller.rc());3592 3593 /* We need a write lock because we alter the cached callback data */3594 AutoLock alock (this);3595 3596 /* save the callback arguments */3597 mCallbackData.mcc.supportsAbsolute = supportsAbsolute;3598 mCallbackData.mcc.needsHostCursor = needsHostCursor;3599 mCallbackData.mcc.valid = true;3600 3601 CallbackList::iterator it = mCallbacks.begin();3602 while (it != mCallbacks.end())3603 {3604 Log2(("Console::onMouseCapabilityChange: calling %p\n", (void*)*it));3605 (*it++)->OnMouseCapabilityChange (supportsAbsolute, needsHostCursor);3606 }3607 }3608 3609 /**3610 * @note Locks this object for reading.3611 */3612 void Console::onStateChange (MachineState_T machineState)3613 {3614 AutoCaller autoCaller (this);3615 AssertComRCReturnVoid (autoCaller.rc());3616 3617 AutoReaderLock alock (this);3618 3619 CallbackList::iterator it = mCallbacks.begin();3620 while (it != mCallbacks.end())3621 (*it++)->OnStateChange (machineState);3622 }3623 3624 /**3625 * @note Locks this object for reading.3626 */3627 void Console::onAdditionsStateChange()3628 {3629 AutoCaller autoCaller (this);3630 AssertComRCReturnVoid (autoCaller.rc());3631 3632 AutoReaderLock alock (this);3633 3634 CallbackList::iterator it = mCallbacks.begin();3635 while (it != mCallbacks.end())3636 (*it++)->OnAdditionsStateChange();3637 }3638 3639 /**3640 * @note Locks this object for reading.3641 */3642 void Console::onAdditionsOutdated()3643 {3644 AutoCaller autoCaller (this);3645 AssertComRCReturnVoid (autoCaller.rc());3646 3647 AutoReaderLock alock (this);3648 3649 /** @todo Use the On-Screen Display feature to report the fact.3650 * The user should be told to install additions that are3651 * provided with the current VBox build:3652 * VBOX_VERSION_MAJOR.VBOX_VERSION_MINOR.VBOX_VERSION_BUILD3653 */3654 }3655 3656 /**3657 * @note Locks this object for writing.3658 */3659 void Console::onKeyboardLedsChange(bool fNumLock, bool fCapsLock, bool fScrollLock)3660 {3661 AutoCaller autoCaller (this);3662 AssertComRCReturnVoid (autoCaller.rc());3663 3664 /* We need a write lock because we alter the cached callback data */3665 AutoLock alock (this);3666 3667 /* save the callback arguments */3668 mCallbackData.klc.numLock = fNumLock;3669 mCallbackData.klc.capsLock = fCapsLock;3670 mCallbackData.klc.scrollLock = fScrollLock;3671 mCallbackData.klc.valid = true;3672 3673 CallbackList::iterator it = mCallbacks.begin();3674 while (it != mCallbacks.end())3675 (*it++)->OnKeyboardLedsChange(fNumLock, fCapsLock, fScrollLock);3676 }3677 3678 /**3679 * @note Locks this object for reading.3680 */3681 void Console::onUSBDeviceStateChange (IUSBDevice *aDevice, bool aAttached,3682 IVirtualBoxErrorInfo *aError)3683 {3684 AutoCaller autoCaller (this);3685 AssertComRCReturnVoid (autoCaller.rc());3686 3687 AutoReaderLock alock (this);3688 3689 CallbackList::iterator it = mCallbacks.begin();3690 while (it != mCallbacks.end())3691 (*it++)->OnUSBDeviceStateChange (aDevice, aAttached, aError);3692 }3693 3694 /**3695 * @note Locks this object for reading.3696 */3697 void Console::onRuntimeError (BOOL aFatal, INPTR BSTR aErrorID, INPTR BSTR aMessage)3698 {3699 AutoCaller autoCaller (this);3700 AssertComRCReturnVoid (autoCaller.rc());3701 3702 AutoReaderLock alock (this);3703 3704 CallbackList::iterator it = mCallbacks.begin();3705 while (it != mCallbacks.end())3706 (*it++)->OnRuntimeError (aFatal, aErrorID, aMessage);3707 }3708 3709 /**3710 * @note Locks this object for reading.3711 */3712 HRESULT Console::onShowWindow (BOOL aCheck, BOOL *aCanShow, ULONG64 *aWinId)3713 {3714 AssertReturn (aCanShow, E_POINTER);3715 AssertReturn (aWinId, E_POINTER);3716 3717 *aCanShow = FALSE;3718 *aWinId = 0;3719 3720 AutoCaller autoCaller (this);3721 AssertComRCReturnRC (autoCaller.rc());3722 3723 AutoReaderLock alock (this);3724 3725 HRESULT rc = S_OK;3726 CallbackList::iterator it = mCallbacks.begin();3727 3728 if (aCheck)3729 {3730 while (it != mCallbacks.end())3731 {3732 BOOL canShow = FALSE;3733 rc = (*it++)->OnCanShowWindow (&canShow);3734 AssertComRC (rc);3735 if (FAILED (rc) || !canShow)3736 return rc;3737 }3738 *aCanShow = TRUE;3739 }3740 else3741 {3742 while (it != mCallbacks.end())3743 {3744 ULONG64 winId = 0;3745 rc = (*it++)->OnShowWindow (&winId);3746 AssertComRC (rc);3747 if (FAILED (rc))3748 return rc;3749 /* only one callback may return non-null winId */3750 Assert (*aWinId == 0 || winId == 0);3751 if (*aWinId == 0)3752 *aWinId = winId;3753 }3754 }3755 3756 return S_OK;3757 }3758 3759 // private mehtods3760 ////////////////////////////////////////////////////////////////////////////////3761 3762 /**3763 * Increases the usage counter of the mpVM pointer. Guarantees that3764 * VMR3Destroy() will not be called on it at least until releaseVMCaller()3765 * is called.3766 *3767 * If this method returns a failure, the caller is not allowed to use mpVM3768 * and may return the failed result code to the upper level. This method sets3769 * the extended error info on failure if \a aQuiet is false.3770 *3771 * Setting \a aQuiet to true is useful for methods that don't want to return3772 * the failed result code to the caller when this method fails (e.g. need to3773 * silently check for the mpVM avaliability).3774 *3775 * When mpVM is NULL but \a aAllowNullVM is true, a corresponding error will be3776 * returned instead of asserting. Having it false is intended as a sanity check3777 * for methods that have checked mMachineState and expect mpVM *NOT* to be NULL.3778 *3779 * @param aQuiet true to suppress setting error info3780 * @param aAllowNullVM true to accept mpVM being NULL and return a failure3781 * (otherwise this method will assert if mpVM is NULL)3782 *3783 * @note Locks this object for writing.3784 */3785 HRESULT Console::addVMCaller (bool aQuiet /* = false */,3786 bool aAllowNullVM /* = false */)3787 {3788 AutoCaller autoCaller (this);3789 AssertComRCReturnRC (autoCaller.rc());3790 3791 AutoLock alock (this);3792 3793 if (mVMDestroying)3794 {3795 /* powerDown() is waiting for all callers to finish */3796 return aQuiet ? E_ACCESSDENIED : setError (E_ACCESSDENIED,3797 tr ("Virtual machine is being powered down"));3798 }3799 3800 if (mpVM == NULL)3801 {3802 Assert (aAllowNullVM == true);3803 3804 /* The machine is not powered up */3805 return aQuiet ? E_ACCESSDENIED : setError (E_ACCESSDENIED,3806 tr ("Virtual machine is not powered up"));3807 }3808 3809 ++ mVMCallers;3810 3811 return S_OK;3812 }3813 3814 /**3815 * Decreases the usage counter of the mpVM pointer. Must always complete3816 * the addVMCaller() call after the mpVM pointer is no more necessary.3817 *3818 * @note Locks this object for writing.3819 */3820 void Console::releaseVMCaller()3821 {3822 AutoCaller autoCaller (this);3823 AssertComRCReturnVoid (autoCaller.rc());3824 3825 AutoLock alock (this);3826 3827 AssertReturnVoid (mpVM != NULL);3828 3829 Assert (mVMCallers > 0);3830 -- mVMCallers;3831 3832 if (mVMCallers == 0 && mVMDestroying)3833 {3834 /* inform powerDown() there are no more callers */3835 RTSemEventSignal (mVMZeroCallersSem);3836 }3837 }3838 3839 /**3840 * Internal power off worker routine.3841 *3842 * This method may be called only at certain places with the folliwing meaning3843 * as shown below:3844 *3845 * - if the machine state is either Running or Paused, a normal3846 * Console-initiated powerdown takes place (e.g. PowerDown());3847 * - if the machine state is Saving, saveStateThread() has successfully3848 * done its job;3849 * - if the machine state is Starting or Restoring, powerUpThread() has3850 * failed to start/load the VM;3851 * - if the machine state is Stopping, the VM has powered itself off3852 * (i.e. not as a result of the powerDown() call).3853 *3854 * Calling it in situations other than the above will cause unexpected3855 * behavior.3856 *3857 * Note that this method should be the only one that destroys mpVM and sets3858 * it to NULL.3859 *3860 * @note Locks this object for writing.3861 *3862 * @note Never call this method from a thread that called addVMCaller() or3863 * instantiated an AutoVMCaller object; first call releaseVMCaller() or3864 * release(). Otherwise it will deadlock.3865 */3866 HRESULT Console::powerDown()3867 {3868 LogFlowThisFuncEnter();3869 3870 AutoCaller autoCaller (this);3871 AssertComRCReturnRC (autoCaller.rc());3872 3873 AutoLock alock (this);3874 3875 /* sanity */3876 AssertReturn (mVMDestroying == false, E_FAIL);3877 3878 LogRel (("Console::powerDown(): a request to power off the VM has been issued "3879 "(mMachineState=%d, InUninit=%d)\n",3880 mMachineState, autoCaller.state() == InUninit));3881 3882 /*3883 * Stop the VRDP server to prevent new clients connection while VM is being powered off.3884 * (When called from uninit mConsoleVRDPServer is already destroyed.)3885 */3886 if (mConsoleVRDPServer)3887 {3888 LogFlowThisFunc (("Stopping VRDP server...\n"));3889 3890 /* Leave the lock since EMT will call us back as addVMCaller in updateDisplayData(). */3891 alock.leave();3892 3893 mConsoleVRDPServer->Stop();3894 3895 alock.enter();3896 }3897 3898 /* First, wait for all mpVM callers to finish their work if necessary */3899 if (mVMCallers > 0)3900 {3901 /* go to the destroying state to prevent from adding new callers */3902 mVMDestroying = true;3903 3904 /* lazy creation */3905 if (mVMZeroCallersSem == NIL_RTSEMEVENT)3906 RTSemEventCreate (&mVMZeroCallersSem);3907 3908 LogFlowThisFunc (("Waiting for mpVM callers (%d) to drop to zero...\n",3909 mVMCallers));3910 3911 alock.leave();3912 3913 RTSemEventWait (mVMZeroCallersSem, RT_INDEFINITE_WAIT);3914 3915 alock.enter();3916 }3917 3918 AssertReturn (mpVM, E_FAIL);3919 3920 AssertMsg (mMachineState == MachineState_Running ||3921 mMachineState == MachineState_Paused ||3922 mMachineState == MachineState_Stuck ||3923 mMachineState == MachineState_Saving ||3924 mMachineState == MachineState_Starting ||3925 mMachineState == MachineState_Restoring ||3926 mMachineState == MachineState_Stopping,3927 ("Invalid machine state: %d\n", mMachineState));3928 3929 HRESULT rc = S_OK;3930 int vrc = VINF_SUCCESS;3931 3932 /*3933 * Power off the VM if not already done that. In case of Stopping, the VM3934 * has powered itself off and notified Console in vmstateChangeCallback().3935 * In case of Starting or Restoring, powerUpThread() is calling us on3936 * failure, so the VM is already off at that point.3937 */3938 if (mMachineState != MachineState_Stopping &&3939 mMachineState != MachineState_Starting &&3940 mMachineState != MachineState_Restoring)3941 {3942 /*3943 * don't go from Saving to Stopping, vmstateChangeCallback needs it3944 * to set the state to Saved on VMSTATE_TERMINATED.3945 */3946 if (mMachineState != MachineState_Saving)3947 setMachineState (MachineState_Stopping);3948 3949 LogFlowThisFunc (("Powering off the VM...\n"));3950 3951 /* Leave the lock since EMT will call us back on VMR3PowerOff() */3952 alock.leave();3953 3954 vrc = VMR3PowerOff (mpVM);3955 /*3956 * Note that VMR3PowerOff() may fail here (invalid VMSTATE) if the3957 * VM-(guest-)initiated power off happened in parallel a ms before3958 * this call. So far, we let this error pop up on the user's side.3959 */3960 3961 alock.enter();3962 }3963 3964 LogFlowThisFunc (("Ready for VM destruction\n"));3965 3966 /*3967 * If we are called from Console::uninit(), then try to destroy the VM3968 * even on failure (this will most likely fail too, but what to do?..)3969 */3970 if (VBOX_SUCCESS (vrc) || autoCaller.state() == InUninit)3971 {3972 /* If the machine has an USB comtroller, release all USB devices3973 * (symmetric to the code in captureUSBDevices()) */3974 bool fHasUSBController = false;3975 {3976 PPDMIBASE pBase;3977 int vrc = PDMR3QueryLun (mpVM, "usb-ohci", 0, 0, &pBase);3978 if (VBOX_SUCCESS (vrc))3979 {3980 fHasUSBController = true;3981 detachAllUSBDevices (false /* aDone */);3982 }3983 }3984 3985 /*3986 * Now we've got to destroy the VM as well. (mpVM is not valid3987 * beyond this point). We leave the lock before calling VMR3Destroy()3988 * because it will result into calling destructors of drivers3989 * associated with Console children which may in turn try to lock3990 * Console (e.g. by instantiating SafeVMPtr to access mpVM). It's safe3991 * here because mVMDestroying is set which should prevent any activity.3992 */3993 3994 /*3995 * Set mpVM to NULL early just in case if some old code is not using3996 * addVMCaller()/releaseVMCaller().3997 */3998 PVM pVM = mpVM;3999 mpVM = NULL;4000 4001 LogFlowThisFunc (("Destroying the VM...\n"));4002 4003 alock.leave();4004 4005 vrc = VMR3Destroy (pVM);4006 4007 /* take the lock again */4008 alock.enter();4009 4010 if (VBOX_SUCCESS (vrc))4011 {4012 LogFlowThisFunc (("Machine has been destroyed (mMachineState=%d)\n",4013 mMachineState));4014 /*4015 * Note: the Console-level machine state change happens on the4016 * VMSTATE_TERMINATE state change in vmstateChangeCallback(). If4017 * powerDown() is called from EMT (i.e. from vmstateChangeCallback()4018 * on receiving VM-initiated VMSTATE_OFF), VMSTATE_TERMINATE hasn't4019 * occured yet. This is okay, because mMachineState is already4020 * Stopping in this case, so any other attempt to call PowerDown()4021 * will be rejected.4022 */4023 }4024 else4025 {4026 /* bad bad bad, but what to do? */4027 mpVM = pVM;4028 rc = setError (E_FAIL,4029 tr ("Could not destroy the machine. (Error: %Vrc)"), vrc);4030 }4031 4032 /*4033 * Complete the detaching of the USB devices.4034 */4035 if (fHasUSBController)4036 detachAllUSBDevices (true /* aDone */);4037 }4038 else4039 {4040 rc = setError (E_FAIL,4041 tr ("Could not power off the machine. (Error: %Vrc)"), vrc);4042 }4043 4044 /*4045 * Finished with destruction. Note that if something impossible happened4046 * and we've failed to destroy the VM, mVMDestroying will remain false and4047 * mMachineState will be something like Stopping, so most Console methods4048 * will return an error to the caller.4049 */4050 if (mpVM == NULL)4051 mVMDestroying = false;4052 4053 if (SUCCEEDED (rc))4054 {4055 /* uninit dynamically allocated members of mCallbackData */4056 if (mCallbackData.mpsc.valid)4057 {4058 if (mCallbackData.mpsc.shape != NULL)4059 RTMemFree (mCallbackData.mpsc.shape);4060 }4061 memset (&mCallbackData, 0, sizeof (mCallbackData));4062 }4063 4064 LogFlowThisFuncLeave();4065 return rc;4066 }4067 4068 /**4069 * @note Locks this object for writing.4070 */4071 HRESULT Console::setMachineState (MachineState_T aMachineState,4072 bool aUpdateServer /* = true */)4073 {4074 AutoCaller autoCaller (this);4075 AssertComRCReturnRC (autoCaller.rc());4076 4077 AutoLock alock (this);4078 4079 HRESULT rc = S_OK;4080 4081 if (mMachineState != aMachineState)4082 {4083 LogFlowThisFunc (("machineState=%d\n", aMachineState));4084 mMachineState = aMachineState;4085 4086 /// @todo (dmik)4087 // possibly, we need to redo onStateChange() using the dedicated4088 // Event thread, like it is done in VirtualBox. This will make it4089 // much safer (no deadlocks possible if someone tries to use the4090 // console from the callback), however, listeners will lose the4091 // ability to synchronously react to state changes (is it really4092 // necessary??)4093 LogFlowThisFunc (("Doing onStateChange()...\n"));4094 onStateChange (aMachineState);4095 LogFlowThisFunc (("Done onStateChange()\n"));4096 4097 if (aUpdateServer)4098 {4099 /*4100 * Server notification MUST be done from under the lock; otherwise4101 * the machine state here and on the server might go out of sync, that4102 * can lead to various unexpected results (like the machine state being4103 * >= MachineState_Running on the server, while the session state is4104 * already SessionState_SessionClosed at the same time there).4105 *4106 * Cross-lock conditions should be carefully watched out: calling4107 * UpdateState we will require Machine and SessionMachine locks4108 * (remember that here we're holding the Console lock here, and4109 * also all locks that have been entered by the thread before calling4110 * this method).4111 */4112 LogFlowThisFunc (("Doing mControl->UpdateState()...\n"));4113 rc = mControl->UpdateState (aMachineState);4114 LogFlowThisFunc (("mControl->UpdateState()=%08X\n", rc));4115 }4116 }4117 4118 return rc;4119 }4120 4121 /**4122 * Searches for a shared folder with the given logical name4123 * in the collection of shared folders.4124 *4125 * @param aName logical name of the shared folder4126 * @param aSharedFolder where to return the found object4127 * @param aSetError whether to set the error info if the folder is4128 * not found4129 * @return4130 * S_OK when found or E_INVALIDARG when not found4131 *4132 * @note The caller must lock this object for writing.4133 */4134 HRESULT Console::findSharedFolder (const BSTR aName,4135 ComObjPtr <SharedFolder> &aSharedFolder,4136 bool aSetError /* = false */)4137 {4138 /* sanity check */4139 AssertReturn (isLockedOnCurrentThread(), E_FAIL);4140 4141 SharedFolderMap::const_iterator it = mSharedFolders.find (aName);4142 if (it != mSharedFolders.end())4143 {4144 aSharedFolder = it->second;4145 return S_OK;4146 }4147 4148 if (aSetError)4149 setError (E_INVALIDARG,4150 tr ("Could not find a shared folder named '%ls'."), aName);4151 4152 return E_INVALIDARG;4153 }4154 4155 /**4156 * Fetches the list of global or machine shared folders from the server.4157 *4158 * @param aGlobal true to fetch global folders.4159 *4160 * @note The caller must lock this object for writing.4161 */4162 HRESULT Console::fetchSharedFolders (BOOL aGlobal)4163 {4164 /* sanity check */4165 AssertReturn (AutoCaller (this).state() == InInit ||4166 isLockedOnCurrentThread(), E_FAIL);4167 4168 /* protect mpVM (if not NULL) */4169 AutoVMCallerQuietWeak autoVMCaller (this);4170 4171 HRESULT rc = S_OK;4172 4173 bool online = mpVM && autoVMCaller.isOk() && mVMMDev->isShFlActive();4174 4175 if (aGlobal)4176 {4177 /// @todo grab & process global folders when they are done4178 }4179 else4180 {4181 SharedFolderDataMap oldFolders;4182 if (online)4183 oldFolders = mMachineSharedFolders;4184 4185 mMachineSharedFolders.clear();4186 4187 ComPtr <ISharedFolderCollection> coll;4188 rc = mMachine->COMGETTER(SharedFolders) (coll.asOutParam());4189 AssertComRCReturnRC (rc);4190 4191 ComPtr <ISharedFolderEnumerator> en;4192 rc = coll->Enumerate (en.asOutParam());4193 AssertComRCReturnRC (rc);4194 4195 BOOL hasMore = FALSE;4196 while (SUCCEEDED (rc = en->HasMore (&hasMore)) && hasMore)4197 {4198 ComPtr <ISharedFolder> folder;4199 rc = en->GetNext (folder.asOutParam());4200 CheckComRCBreakRC (rc);4201 4202 Bstr name;4203 Bstr hostPath;4204 4205 rc = folder->COMGETTER(Name) (name.asOutParam());4206 CheckComRCBreakRC (rc);4207 rc = folder->COMGETTER(HostPath) (hostPath.asOutParam());4208 CheckComRCBreakRC (rc);4209 4210 mMachineSharedFolders.insert (std::make_pair (name, hostPath));4211 4212 /* send changes to HGCM if the VM is running */4213 /// @todo report errors as runtime warnings through VMSetError4214 if (online)4215 {4216 SharedFolderDataMap::iterator it = oldFolders.find (name);4217 if (it == oldFolders.end() || it->second != hostPath)4218 {4219 /* a new machine folder is added or4220 * the existing machine folder is changed */4221 if (mSharedFolders.find (name) != mSharedFolders.end())4222 ; /* the console folder exists, nothing to do */4223 else4224 {4225 /* remove the old machhine folder (when changed)4226 * or the global folder if any (when new) */4227 if (it != oldFolders.end() ||4228 mGlobalSharedFolders.find (name) !=4229 mGlobalSharedFolders.end())4230 rc = removeSharedFolder (name);4231 /* create the new machine folder */4232 rc = createSharedFolder (name, hostPath);4233 }4234 }4235 /* forget the processed (or identical) folder */4236 if (it != oldFolders.end())4237 oldFolders.erase (it);4238 4239 rc = S_OK;4240 }4241 }4242 4243 AssertComRCReturnRC (rc);4244 4245 /* process outdated (removed) folders */4246 /// @todo report errors as runtime warnings through VMSetError4247 if (online)4248 {4249 for (SharedFolderDataMap::const_iterator it = oldFolders.begin();4250 it != oldFolders.end(); ++ it)4251 {4252 if (mSharedFolders.find (it->first) != mSharedFolders.end())4253 ; /* the console folder exists, nothing to do */4254 else4255 {4256 /* remove the outdated machine folder */4257 rc = removeSharedFolder (it->first);4258 /* create the global folder if there is any */4259 SharedFolderDataMap::const_iterator git =4260 mGlobalSharedFolders.find (it->first);4261 if (git != mGlobalSharedFolders.end())4262 rc = createSharedFolder (git->first, git->second);4263 }4264 }4265 4266 rc = S_OK;4267 }4268 }4269 4270 return rc;4271 }4272 4273 /**4274 * Searches for a shared folder with the given name in the list of machine4275 * shared folders and then in the list of the global shared folders.4276 *4277 * @param aName Name of the folder to search for.4278 * @param aIt Where to store the pointer to the found folder.4279 * @return @c true if the folder was found and @c false otherwise.4280 *4281 * @note The caller must lock this object for reading.4282 */4283 bool Console::findOtherSharedFolder (INPTR BSTR aName,4284 SharedFolderDataMap::const_iterator &aIt)4285 {4286 /* sanity check */4287 AssertReturn (isLockedOnCurrentThread(), false);4288 4289 /* first, search machine folders */4290 aIt = mMachineSharedFolders.find (aName);4291 if (aIt != mMachineSharedFolders.end())4292 return true;4293 4294 /* second, search machine folders */4295 aIt = mGlobalSharedFolders.find (aName);4296 if (aIt != mGlobalSharedFolders.end())4297 return true;4298 4299 return false;4300 }4301 4302 /**4303 * Calls the HGCM service to add a shared folder definition.4304 *4305 * @param aName Shared folder name.4306 * @param aHostPath Shared folder path.4307 *4308 * @note Must be called from under AutoVMCaller and when mpVM != NULL!4309 * @note Doesn't lock anything.4310 */4311 HRESULT Console::createSharedFolder (INPTR BSTR aName, INPTR BSTR aHostPath)4312 {4313 ComAssertRet (aName && *aName, E_FAIL);4314 ComAssertRet (aHostPath && *aHostPath, E_FAIL);4315 4316 /* sanity checks */4317 AssertReturn (mpVM, E_FAIL);4318 AssertReturn (mVMMDev->isShFlActive(), E_FAIL);4319 4320 VBOXHGCMSVCPARM parms[2];4321 SHFLSTRING *pFolderName, *pMapName;4322 int cbString;4323 4324 Log (("Adding shared folder '%ls' -> '%ls'\n", aName, aHostPath));4325 4326 cbString = (RTStrUcs2Len (aHostPath) + 1) * sizeof (RTUCS2);4327 pFolderName = (SHFLSTRING *) RTMemAllocZ (sizeof (SHFLSTRING) + cbString);4328 Assert (pFolderName);4329 memcpy (pFolderName->String.ucs2, aHostPath, cbString);4330 4331 pFolderName->u16Size = cbString;4332 pFolderName->u16Length = cbString - sizeof(RTUCS2);4333 4334 parms[0].type = VBOX_HGCM_SVC_PARM_PTR;4335 parms[0].u.pointer.addr = pFolderName;4336 parms[0].u.pointer.size = sizeof (SHFLSTRING) + cbString;4337 4338 cbString = (RTStrUcs2Len (aName) + 1) * sizeof (RTUCS2);4339 pMapName = (SHFLSTRING *) RTMemAllocZ (sizeof(SHFLSTRING) + cbString);4340 Assert (pMapName);4341 memcpy (pMapName->String.ucs2, aName, cbString);4342 4343 pMapName->u16Size = cbString;4344 pMapName->u16Length = cbString - sizeof (RTUCS2);4345 4346 parms[1].type = VBOX_HGCM_SVC_PARM_PTR;4347 parms[1].u.pointer.addr = pMapName;4348 parms[1].u.pointer.size = sizeof (SHFLSTRING) + cbString;4349 4350 int vrc = mVMMDev->hgcmHostCall ("VBoxSharedFolders",4351 SHFL_FN_ADD_MAPPING,4352 2, &parms[0]);4353 RTMemFree (pFolderName);4354 RTMemFree (pMapName);4355 4356 if (VBOX_FAILURE (vrc))4357 return setError (E_FAIL,4358 tr ("Could not create a shared folder '%ls' "4359 "mapped to '%ls' (%Vrc)"),4360 aName, aHostPath, vrc);4361 4362 return S_OK;4363 }4364 4365 /**4366 * Calls the HGCM service to remove the shared folder definition.4367 *4368 * @param aName Shared folder name.4369 *4370 * @note Must be called from under AutoVMCaller and when mpVM != NULL!4371 * @note Doesn't lock anything.4372 */4373 HRESULT Console::removeSharedFolder (INPTR BSTR aName)4374 {4375 ComAssertRet (aName && *aName, E_FAIL);4376 4377 /* sanity checks */4378 AssertReturn (mpVM, E_FAIL);4379 AssertReturn (mVMMDev->isShFlActive(), E_FAIL);4380 4381 VBOXHGCMSVCPARM parms;4382 SHFLSTRING *pMapName;4383 int cbString;4384 4385 Log (("Removing shared folder '%ls'\n", aName));4386 4387 cbString = (RTStrUcs2Len (aName) + 1) * sizeof (RTUCS2);4388 pMapName = (SHFLSTRING *) RTMemAllocZ (sizeof (SHFLSTRING) + cbString);4389 Assert (pMapName);4390 memcpy (pMapName->String.ucs2, aName, cbString);4391 4392 pMapName->u16Size = cbString;4393 pMapName->u16Length = cbString - sizeof (RTUCS2);4394 4395 parms.type = VBOX_HGCM_SVC_PARM_PTR;4396 parms.u.pointer.addr = pMapName;4397 parms.u.pointer.size = sizeof (SHFLSTRING) + cbString;4398 4399 int vrc = mVMMDev->hgcmHostCall ("VBoxSharedFolders",4400 SHFL_FN_REMOVE_MAPPING,4401 1, &parms);4402 RTMemFree(pMapName);4403 if (VBOX_FAILURE (vrc))4404 return setError (E_FAIL,4405 tr ("Could not remove the shared folder '%ls' (%Vrc)"),4406 aName, vrc);4407 4408 return S_OK;4409 }4410 4411 /**4412 * VM state callback function. Called by the VMM4413 * using its state machine states.4414 *4415 * Primarily used to handle VM initiated power off, suspend and state saving,4416 * but also for doing termination completed work (VMSTATE_TERMINATE).4417 *4418 * In general this function is called in the context of the EMT.4419 *4420 * @param aVM The VM handle.4421 * @param aState The new state.4422 * @param aOldState The old state.4423 * @param aUser The user argument (pointer to the Console object).4424 *4425 * @note Locks the Console object for writing.4426 */4427 DECLCALLBACK(void)4428 Console::vmstateChangeCallback (PVM aVM, VMSTATE aState, VMSTATE aOldState,4429 void *aUser)4430 {4431 LogFlowFunc (("Changing state from %d to %d (aVM=%p)\n",4432 aOldState, aState, aVM));4433 4434 Console *that = static_cast <Console *> (aUser);4435 AssertReturnVoid (that);4436 4437 AutoCaller autoCaller (that);4438 /*4439 * Note that we must let this method proceed even if Console::uninit() has4440 * been already called. In such case this VMSTATE change is a result of:4441 * 1) powerDown() called from uninit() itself, or4442 * 2) VM-(guest-)initiated power off.4443 */4444 AssertReturnVoid (autoCaller.isOk() ||4445 autoCaller.state() == InUninit);4446 4447 switch (aState)4448 {4449 /*4450 * The VM has terminated4451 */4452 case VMSTATE_OFF:4453 {4454 AutoLock alock (that);4455 4456 if (that->mVMStateChangeCallbackDisabled)4457 break;4458 4459 /*4460 * Do we still think that it is running? It may happen if this is4461 * a VM-(guest-)initiated shutdown/poweroff.4462 */4463 if (that->mMachineState != MachineState_Stopping &&4464 that->mMachineState != MachineState_Saving &&4465 that->mMachineState != MachineState_Restoring)4466 {4467 LogFlowFunc (("VM has powered itself off but Console still "4468 "thinks it is running. Notifying.\n"));4469 4470 /* prevent powerDown() from calling VMR3PowerOff() again */4471 that->setMachineState (MachineState_Stopping);4472 4473 /*4474 * Setup task object and thread to carry out the operation4475 * asynchronously (if we call powerDown() right here but there4476 * is one or more mpVM callers (added with addVMCaller()) we'll4477 * deadlock.4478 */4479 std::auto_ptr <VMTask> task (new VMTask (that, true /* aUsesVMPtr */));4480 /*4481 * If creating a task is falied, this can currently mean one4482 * of two: either Console::uninit() has been called just a ms4483 * before (so a powerDown() call is already on the way), or4484 * powerDown() itself is being already executed. Just do4485 * nothing .4486 */4487 if (!task->isOk())4488 {4489 LogFlowFunc (("Console is already being uninitialized.\n"));4490 break;4491 }4492 4493 int vrc = RTThreadCreate (NULL, Console::powerDownThread,4494 (void *) task.get(), 0,4495 RTTHREADTYPE_MAIN_WORKER, 0,4496 "VMPowerDowm");4497 4498 AssertMsgRC (vrc, ("Could not create VMPowerUp thread (%Vrc)\n", vrc));4499 if (VBOX_FAILURE (vrc))4500 break;4501 4502 /* task is now owned by powerDownThread(), so release it */4503 task.release();4504 }4505 break;4506 }4507 4508 /*4509 * The VM has been completely destroyed.4510 *4511 * Note: This state change can happen at two points:4512 * 1) At the end of VMR3Destroy() if it was not called from EMT.4513 * 2) At the end of vmR3EmulationThread if VMR3Destroy() was4514 * called by EMT.4515 */4516 case VMSTATE_TERMINATED:4517 {4518 AutoLock alock (that);4519 4520 if (that->mVMStateChangeCallbackDisabled)4521 break;4522 4523 /*4524 * Terminate host interface networking. If aVM is NULL, we've been4525 * manually called from powerUpThread() either before calling4526 * VMR3Create() or after VMR3Create() failed, so no need to touch4527 * networking.4528 */4529 if (aVM)4530 that->powerDownHostInterfaces();4531 4532 /*4533 * From now on the machine is officially powered down or4534 * remains in the Saved state.4535 */4536 switch (that->mMachineState)4537 {4538 default:4539 AssertFailed();4540 /* fall through */4541 case MachineState_Stopping:4542 /* successfully powered down */4543 that->setMachineState (MachineState_PoweredOff);4544 break;4545 case MachineState_Saving:4546 /*4547 * successfully saved (note that the machine is already4548 * in the Saved state on the server due to EndSavingState()4549 * called from saveStateThread(), so only change the local4550 * state)4551 */4552 that->setMachineStateLocally (MachineState_Saved);4553 break;4554 case MachineState_Starting:4555 /*4556 * failed to start, but be patient: set back to PoweredOff4557 * (for similarity with the below)4558 */4559 that->setMachineState (MachineState_PoweredOff);4560 break;4561 case MachineState_Restoring:4562 /*4563 * failed to load the saved state file, but be patient:4564 * set back to Saved (to preserve the saved state file)4565 */4566 that->setMachineState (MachineState_Saved);4567 break;4568 }4569 4570 break;4571 }4572 4573 case VMSTATE_SUSPENDED:4574 {4575 if (aOldState == VMSTATE_RUNNING)4576 {4577 AutoLock alock (that);4578 4579 if (that->mVMStateChangeCallbackDisabled)4580 break;4581 4582 /* Change the machine state from Running to Paused */4583 Assert (that->mMachineState == MachineState_Running);4584 that->setMachineState (MachineState_Paused);4585 }4586 4587 break;4588 }4589 4590 case VMSTATE_RUNNING:4591 {4592 if (aOldState == VMSTATE_CREATED ||4593 aOldState == VMSTATE_SUSPENDED)4594 {4595 AutoLock alock (that);4596 4597 if (that->mVMStateChangeCallbackDisabled)4598 break;4599 4600 /*4601 * Change the machine state from Starting, Restoring or Paused4602 * to Running4603 */4604 Assert ((that->mMachineState == MachineState_Starting &&4605 aOldState == VMSTATE_CREATED) ||4606 ((that->mMachineState == MachineState_Restoring ||4607 that->mMachineState == MachineState_Paused) &&4608 aOldState == VMSTATE_SUSPENDED));4609 4610 that->setMachineState (MachineState_Running);4611 }4612 4613 break;4614 }4615 4616 case VMSTATE_GURU_MEDITATION:4617 {4618 AutoLock alock (that);4619 4620 if (that->mVMStateChangeCallbackDisabled)4621 break;4622 4623 /* Guru respects only running VMs */4624 Assert ((that->mMachineState >= MachineState_Running));4625 4626 that->setMachineState (MachineState_Stuck);4627 4628 break;4629 }4630 4631 default: /* shut up gcc */4632 break;4633 }4634 }4635 4636 /**4637 * Sends a request to VMM to attach the given host device.4638 * After this method succeeds, the attached device will appear in the4639 * mUSBDevices collection.4640 *4641 * @param aHostDevice device to attach4642 *4643 * @note Synchronously calls EMT.4644 * @note Must be called from under this object's lock.4645 */4646 #if 14647 HRESULT Console::attachUSBDevice (IUSBDevice *aHostDevice, PVUSBIRHCONFIG aConfig)4648 {4649 AssertReturn (aHostDevice && aConfig, E_FAIL);4650 #else /* PDMUsb */4651 HRESULT Console::attachUSBDevice (IUSBDevice *aHostDevice)4652 {4653 AssertReturn (aHostDevice, E_FAIL);4654 #endif4655 4656 AssertReturn (isLockedOnCurrentThread(), E_FAIL);4657 4658 /* still want a lock object because we need to leave it */4659 AutoLock alock (this);4660 4661 HRESULT hrc;4662 4663 /*4664 * Get the address and the Uuid, and call the pfnCreateProxyDevice roothub4665 * method in EMT (using usbAttachCallback()).4666 */4667 Bstr BstrAddress;4668 hrc = aHostDevice->COMGETTER (Address) (BstrAddress.asOutParam());4669 ComAssertComRCRetRC (hrc);4670 4671 Utf8Str Address (BstrAddress);4672 4673 Guid Uuid;4674 hrc = aHostDevice->COMGETTER (Id) (Uuid.asOutParam());4675 ComAssertComRCRetRC (hrc);4676 4677 BOOL fRemote = FALSE;4678 void *pvRemote = NULL;4679 4680 hrc = aHostDevice->COMGETTER (Remote) (&fRemote);4681 ComAssertComRCRetRC (hrc);4682 4683 /* protect mpVM */4684 AutoVMCaller autoVMCaller (this);4685 CheckComRCReturnRC (autoVMCaller.rc());4686 4687 LogFlowThisFunc (("Proxying USB device '%s' {%Vuuid}...\n",4688 Address.raw(), Uuid.ptr()));4689 4690 /* leave the lock before a VMR3* call (EMT will call us back)! */4691 alock.leave();4692 4693 #if 14694 PVMREQ pReq = NULL;4695 int vrc = VMR3ReqCall (mpVM, &pReq, RT_INDEFINITE_WAIT,4696 (PFNRT) usbAttachCallback, 7,4697 this, aHostDevice,4698 aConfig, Uuid.ptr(), fRemote, Address.raw(), pvRemote);4699 #else /* PDMUsb */4700 PVMREQ pReq = NULL;4701 int vrc = VMR3ReqCall (mpVM, &pReq, RT_INDEFINITE_WAIT,4702 (PFNRT) usbAttachCallback, 5, this, aHostDevice, aConfig, Uuid.ptr(), fRemote, Address.raw());4703 #endif /* PDMUsb */4704 if (VBOX_SUCCESS (vrc))4705 vrc = pReq->iStatus;4706 VMR3ReqFree (pReq);4707 4708 /* restore the lock */4709 alock.enter();4710 4711 /* hrc is S_OK here */4712 4713 if (VBOX_FAILURE (vrc))4714 {4715 LogWarningThisFunc (("Failed to create proxy device for '%s' {%Vuuid} (%Vrc)\n",4716 Address.raw(), Uuid.ptr(), vrc));4717 4718 switch (vrc)4719 {4720 case VERR_VUSB_NO_PORTS:4721 hrc = setError (E_FAIL,4722 tr ("Failed to attach the USB device. (No available ports on the USB controller)."));4723 break;4724 case VERR_VUSB_USBFS_PERMISSION:4725 hrc = setError (E_FAIL,4726 tr ("Not permitted to open the USB device, check usbfs options"));4727 break;4728 default:4729 hrc = setError (E_FAIL,4730 tr ("Failed to create a proxy device for the USB device. (Error: %Vrc)"), vrc);4731 break;4732 }4733 }4734 4735 return hrc;4736 }4737 4738 /**4739 * USB device attach callback used by AttachUSBDevice().4740 * Note that AttachUSBDevice() doesn't return until this callback is executed,4741 * so we don't use AutoCaller and don't care about reference counters of4742 * interface pointers passed in.4743 *4744 * @thread EMT4745 * @note Locks the console object for writing.4746 */4747 #if 14748 DECLCALLBACK(int)4749 Console::usbAttachCallback (Console *that, IUSBDevice *aHostDevice,4750 PVUSBIRHCONFIG aConfig, PCRTUUID aUuid, bool aRemote,4751 const char *aAddress, void *aRemoteBackend)4752 {4753 LogFlowFuncEnter();4754 LogFlowFunc (("that={%p}\n", that));4755 4756 AssertReturn (that && aConfig && aUuid, VERR_INVALID_PARAMETER);4757 4758 if (aRemote)4759 {4760 /* @todo aRemoteBackend input parameter is not needed. */4761 Assert (aRemoteBackend == NULL);4762 4763 RemoteUSBDevice *pRemoteUSBDevice = static_cast <RemoteUSBDevice *> (aHostDevice);4764 4765 Guid guid (*aUuid);4766 4767 aRemoteBackend = that->consoleVRDPServer ()->USBBackendRequestPointer (pRemoteUSBDevice->clientId (), &guid);4768 4769 if (aRemoteBackend == NULL)4770 {4771 /* The clientId is invalid then. */4772 return VERR_INVALID_PARAMETER;4773 }4774 }4775 4776 int vrc = aConfig->pfnCreateProxyDevice (aConfig, aUuid, aRemote, aAddress,4777 aRemoteBackend);4778 4779 if (VBOX_SUCCESS (vrc))4780 {4781 /* Create a OUSBDevice and add it to the device list */4782 ComObjPtr <OUSBDevice> device;4783 device.createObject();4784 HRESULT hrc = device->init (aHostDevice);4785 AssertComRC (hrc);4786 4787 AutoLock alock (that);4788 that->mUSBDevices.push_back (device);4789 LogFlowFunc (("Attached device {%Vuuid}\n", device->id().raw()));4790 4791 /* notify callbacks */4792 that->onUSBDeviceStateChange (device, true /* aAttached */, NULL);4793 }4794 4795 LogFlowFunc (("vrc=%Vrc\n", vrc));4796 LogFlowFuncLeave();4797 return vrc;4798 }4799 #else /* PDMUsb */4800 //static4801 DECLCALLBACK(int)4802 Console::usbAttachCallback (Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress)4803 {4804 LogFlowFuncEnter();4805 LogFlowFunc (("that={%p}\n", that));4806 4807 AssertReturn (that && aConfig && aUuid, VERR_INVALID_PARAMETER);4808 4809 if (aRemote)4810 {4811 RemoteUSBDevice *pRemoteUSBDevice = static_cast <RemoteUSBDevice *> (aHostDevice);4812 Guid guid (*aUuid);4813 4814 aRemoteBackend = that->consoleVRDPServer ()->USBBackendRequestPointer (pRemoteUSBDevice->clientId (), &guid);4815 if (!aRemoteBackend)4816 return VERR_INVALID_PARAMETER; /* The clientId is invalid then. */4817 }4818 4819 int vrc = PDMR3USBCreateProxyDevice (mVM, aUuid, aRemote, aAddress, aRemoteBackend);4820 if (VBOX_SUCCESS (vrc))4821 {4822 /* Create a OUSBDevice and add it to the device list */4823 ComObjPtr <OUSBDevice> device;4824 device.createObject();4825 HRESULT hrc = device->init (aHostDevice);4826 AssertComRC (hrc);4827 4828 AutoLock alock (that);4829 that->mUSBDevices.push_back (device);4830 LogFlowFunc (("Attached device {%Vuuid}\n", device->id().raw()));4831 4832 /* notify callbacks */4833 that->onUSBDeviceStateChange (device, true /* aAttached */, NULL);4834 }4835 4836 LogFlowFunc (("vrc=%Vrc\n", vrc));4837 LogFlowFuncLeave();4838 return vrc;4839 }4840 #endif /* PDMUsb */4841 4842 /**4843 * Sends a request to VMM to detach the given host device. After this method4844 * succeeds, the detached device will disappear from the mUSBDevices4845 * collection.4846 *4847 * @param aIt Iterator pointing to the device to detach.4848 *4849 * @note Synchronously calls EMT.4850 * @note Must be called from under this object's lock.4851 */4852 HRESULT Console::detachUSBDevice (USBDeviceList::iterator &aIt)4853 {4854 AssertReturn (isLockedOnCurrentThread(), E_FAIL);4855 4856 /* still want a lock object because we need to leave it */4857 AutoLock alock (this);4858 4859 /* protect mpVM */4860 AutoVMCaller autoVMCaller (this);4861 CheckComRCReturnRC (autoVMCaller.rc());4862 4863 PPDMIBASE pBase = NULL;4864 int vrc = PDMR3QueryLun (mpVM, "usb-ohci", 0, 0, &pBase);4865 4866 /* if the device is attached, then there must be a USB controller */4867 AssertRCReturn (vrc, E_FAIL);4868 4869 PVUSBIRHCONFIG pRhConfig = (PVUSBIRHCONFIG) pBase->4870 pfnQueryInterface (pBase, PDMINTERFACE_VUSB_RH_CONFIG);4871 AssertReturn (pRhConfig, E_FAIL);4872 4873 LogFlowThisFunc (("Detaching USB proxy device {%Vuuid}...\n",4874 (*aIt)->id().raw()));4875 4876 /* leave the lock before a VMR3* call (EMT will call us back)! */4877 alock.leave();4878 4879 PVMREQ pReq;4880 vrc = VMR3ReqCall (mpVM, &pReq, RT_INDEFINITE_WAIT,4881 (PFNRT) usbDetachCallback, 5,4882 this, &aIt, pRhConfig, (*aIt)->id().raw());4883 if (VBOX_SUCCESS (vrc))4884 vrc = pReq->iStatus;4885 VMR3ReqFree (pReq);4886 4887 ComAssertRCRet (vrc, E_FAIL);4888 4889 return S_OK;4890 }4891 4892 /**4893 * USB device detach callback used by DetachUSBDevice().4894 * Note that DetachUSBDevice() doesn't return until this callback is executed,4895 * so we don't use AutoCaller and don't care about reference counters of4896 * interface pointers passed in.4897 *4898 * @thread EMT4899 * @note Locks the console object for writing.4900 */4901 //static4902 DECLCALLBACK(int)4903 Console::usbDetachCallback (Console *that, USBDeviceList::iterator *aIt,4904 PVUSBIRHCONFIG aConfig, PCRTUUID aUuid)4905 {4906 LogFlowFuncEnter();4907 LogFlowFunc (("that={%p}\n", that));4908 4909 AssertReturn (that && aConfig && aUuid, VERR_INVALID_PARAMETER);4910 4911 /*4912 * If that was a remote device, release the backend pointer.4913 * The pointer was requested in usbAttachCallback.4914 */4915 BOOL fRemote = FALSE;4916 4917 HRESULT hrc2 = (**aIt)->COMGETTER (Remote) (&fRemote);4918 ComAssertComRC (hrc2);4919 4920 if (fRemote)4921 {4922 Guid guid (*aUuid);4923 that->consoleVRDPServer ()->USBBackendReleasePointer (&guid);4924 }4925 4926 int vrc = aConfig->pfnDestroyProxyDevice (aConfig, aUuid);4927 4928 if (VBOX_SUCCESS (vrc))4929 {4930 AutoLock alock (that);4931 4932 /* Remove the device from the collection */4933 that->mUSBDevices.erase (*aIt);4934 LogFlowFunc (("Detached device {%Vuuid}\n", (**aIt)->id().raw()));4935 4936 /* notify callbacks */4937 that->onUSBDeviceStateChange (**aIt, false /* aAttached */, NULL);4938 }4939 4940 LogFlowFunc (("vrc=%Vrc\n", vrc));4941 LogFlowFuncLeave();4942 return vrc;4943 }4944 51 4945 52 /** … … 4951 58 * 4952 59 * @param pVM VM handle. 4953 * @param pv TaskPointer to the VMPowerUpTask object.60 * @param pvConsole Pointer to the VMPowerUpTask object. 4954 61 * @return VBox status code. 4955 62 * 4956 63 * @note Locks the Console object for writing. 4957 64 */ 4958 DECLCALLBACK(int) Console::configConstructor(PVM pVM, void *pv Task)65 DECLCALLBACK(int) Console::configConstructor(PVM pVM, void *pvConsole) 4959 66 { 4960 67 LogFlowFuncEnter(); 4961 4962 /* Note: the task pointer is owned by powerUpThread() */4963 VMPowerUpTask *task = static_cast <VMPowerUpTask *> (pvTask);4964 AssertReturn (task, VERR_GENERAL_FAILURE);4965 68 4966 69 #if defined(RT_OS_WINDOWS) … … 4975 78 #endif 4976 79 4977 ComObjPtr <Console> pConsole = task->mConsole; 80 AssertReturn (pvConsole, VERR_GENERAL_FAILURE); 81 ComObjPtr <Console> pConsole = static_cast <Console *> (pvConsole); 4978 82 4979 83 AutoCaller autoCaller (pConsole); … … 4995 99 #define STR_FREE() do { if (str) { SysFreeString(str); str = NULL; } if (psz) { RTStrFree(psz); psz = NULL; } } while (0) 4996 100 #define RC_CHECK() do { if (VBOX_FAILURE(rc)) { AssertMsgFailed(("rc=%Vrc\n", rc)); STR_FREE(); return rc; } } while (0) 4997 #define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%#x\n", hrc)); STR_FREE(); return VERR_GENERAL_FAILURE; } } while (0)101 #define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%#x\n", hrc)); STR_FREE(); return VERR_GENERAL_FAILURE; } } while (0) 4998 102 4999 103 /* Get necessary objects */ … … 6322 1426 } 6323 1427 6324 /** 6325 * Call the initialisation script for a dynamic TAP interface. 6326 * 6327 * The initialisation script should create a TAP interface, set it up and write its name to 6328 * standard output followed by a carriage return. Anything further written to standard 6329 * output will be ignored. If it returns a non-zero exit code, or does not write an 6330 * intelligable interface name to standard output, it will be treated as having failed. 6331 * For now, this method only works on Linux. 6332 * 6333 * @returns COM status code 6334 * @param tapDevice string to store the name of the tap device created to 6335 * @param tapSetupApplication the name of the setup script 6336 */ 6337 HRESULT Console::callTapSetupApplication(bool isStatic, RTFILE tapFD, Bstr &tapDevice, 6338 Bstr &tapSetupApplication) 6339 { 6340 LogFlowThisFunc(("\n")); 6341 #ifdef RT_OS_LINUX 6342 /* Command line to start the script with. */ 6343 char szCommand[4096]; 6344 /* Result code */ 6345 int rc; 6346 6347 /* Get the script name. */ 6348 Utf8Str tapSetupAppUtf8(tapSetupApplication), tapDeviceUtf8(tapDevice); 6349 RTStrPrintf(szCommand, sizeof(szCommand), "%s %d %s", tapSetupAppUtf8.raw(), 6350 isStatic ? tapFD : 0, isStatic ? tapDeviceUtf8.raw() : ""); 6351 /* 6352 * Create the process and read its output. 6353 */ 6354 Log2(("About to start the TAP setup script with the following command line: %s\n", 6355 szCommand)); 6356 FILE *pfScriptHandle = popen(szCommand, "r"); 6357 if (pfScriptHandle == 0) 6358 { 6359 int iErr = errno; 6360 LogRel(("Failed to start the TAP interface setup script %s, error text: %s\n", 6361 szCommand, strerror(iErr))); 6362 LogFlowThisFunc(("rc=E_FAIL\n")); 6363 return setError(E_FAIL, tr ("Failed to run the host networking set up command %s: %s"), 6364 szCommand, strerror(iErr)); 6365 } 6366 /* If we are using a dynamic TAP interface, we need to get the interface name. */ 6367 if (!isStatic) 6368 { 6369 /* Buffer to read the application output to. It doesn't have to be long, as we are only 6370 interested in the first few (normally 5 or 6) bytes. */ 6371 char acBuffer[64]; 6372 /* The length of the string returned by the application. We only accept strings of 63 6373 characters or less. */ 6374 size_t cBufSize; 6375 6376 /* Read the name of the device from the application. */ 6377 fgets(acBuffer, sizeof(acBuffer), pfScriptHandle); 6378 cBufSize = strlen(acBuffer); 6379 /* The script must return the name of the interface followed by a carriage return as the 6380 first line of its output. We need a null-terminated string. */ 6381 if ((cBufSize < 2) || (acBuffer[cBufSize - 1] != '\n')) 6382 { 6383 pclose(pfScriptHandle); 6384 LogRel(("The TAP interface setup script did not return the name of a TAP device.\n")); 6385 LogFlowThisFunc(("rc=E_FAIL\n")); 6386 return setError(E_FAIL, tr ("The host networking set up command did not supply an interface name")); 6387 } 6388 /* Overwrite the terminating newline character. */ 6389 acBuffer[cBufSize - 1] = 0; 6390 tapDevice = acBuffer; 6391 } 6392 rc = pclose(pfScriptHandle); 6393 if (!WIFEXITED(rc)) 6394 { 6395 LogRel(("The TAP interface setup script terminated abnormally.\n")); 6396 LogFlowThisFunc(("rc=E_FAIL\n")); 6397 return setError(E_FAIL, tr ("The host networking set up command did not run correctly")); 6398 } 6399 if (WEXITSTATUS(rc) != 0) 6400 { 6401 LogRel(("The TAP interface setup script returned a non-zero exit code.\n")); 6402 LogFlowThisFunc(("rc=E_FAIL\n")); 6403 return setError(E_FAIL, tr ("The host networking set up command returned a non-zero exit code")); 6404 } 6405 LogFlowThisFunc(("rc=S_OK\n")); 6406 return S_OK; 6407 #else /* RT_OS_LINUX not defined */ 6408 LogFlowThisFunc(("rc=E_NOTIMPL\n")); 6409 return E_NOTIMPL; /* not yet supported */ 6410 #endif 6411 } 6412 6413 /** 6414 * Helper function to handle host interface device creation and attachment. 6415 * 6416 * @param networkAdapter the network adapter which attachment should be reset 6417 * @return COM status code 6418 * 6419 * @note The caller must lock this object for writing. 6420 */ 6421 HRESULT Console::attachToHostInterface(INetworkAdapter *networkAdapter) 6422 { 6423 LogFlowThisFunc(("\n")); 6424 /* sanity check */ 6425 AssertReturn (isLockedOnCurrentThread(), E_FAIL); 6426 6427 #ifdef DEBUG 6428 /* paranoia */ 6429 NetworkAttachmentType_T attachment; 6430 networkAdapter->COMGETTER(AttachmentType)(&attachment); 6431 Assert(attachment == NetworkAttachmentType_HostInterfaceNetworkAttachment); 6432 #endif /* DEBUG */ 6433 6434 HRESULT rc = S_OK; 6435 6436 #ifdef VBOX_WITH_UNIXY_TAP_NETWORKING 6437 ULONG slot = 0; 6438 rc = networkAdapter->COMGETTER(Slot)(&slot); 6439 AssertComRC(rc); 6440 6441 /* 6442 * Try get the FD. 6443 */ 6444 LONG ltapFD; 6445 rc = networkAdapter->COMGETTER(TAPFileDescriptor)(<apFD); 6446 if (SUCCEEDED(rc)) 6447 maTapFD[slot] = (RTFILE)ltapFD; 6448 else 6449 maTapFD[slot] = NIL_RTFILE; 6450 6451 /* 6452 * Are we supposed to use an existing TAP interface? 6453 */ 6454 if (maTapFD[slot] != NIL_RTFILE) 6455 { 6456 /* nothing to do */ 6457 Assert(ltapFD >= 0); 6458 Assert((LONG)maTapFD[slot] == ltapFD); 6459 rc = S_OK; 6460 } 6461 else 6462 #endif /* VBOX_WITH_UNIXY_TAP_NETWORKING */ 6463 { 6464 /* 6465 * Allocate a host interface device 6466 */ 6467 #ifdef RT_OS_WINDOWS 6468 /* nothing to do */ 6469 int rcVBox = VINF_SUCCESS; 6470 #elif defined(RT_OS_LINUX) 6471 int rcVBox = RTFileOpen(&maTapFD[slot], "/dev/net/tun", 6472 RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE | RTFILE_O_INHERIT); 6473 if (VBOX_SUCCESS(rcVBox)) 6474 { 6475 /* 6476 * Set/obtain the tap interface. 6477 */ 6478 bool isStatic = false; 6479 struct ifreq IfReq; 6480 memset(&IfReq, 0, sizeof(IfReq)); 6481 /* The name of the TAP interface we are using and the TAP setup script resp. */ 6482 Bstr tapDeviceName, tapSetupApplication; 6483 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam()); 6484 if (FAILED(rc)) 6485 { 6486 tapDeviceName.setNull(); /* Is this necessary? */ 6487 } 6488 else if (!tapDeviceName.isEmpty()) 6489 { 6490 isStatic = true; 6491 /* If we are using a static TAP device then try to open it. */ 6492 Utf8Str str(tapDeviceName); 6493 if (str.length() <= sizeof(IfReq.ifr_name)) 6494 strcpy(IfReq.ifr_name, str.raw()); 6495 else 6496 memcpy(IfReq.ifr_name, str.raw(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */ 6497 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI; 6498 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq); 6499 if (rcVBox != 0) 6500 { 6501 LogRel(("Failed to open the host network interface %ls\n", tapDeviceName.raw())); 6502 rc = setError(E_FAIL, tr ("Failed to open the host network interface %ls"), 6503 tapDeviceName.raw()); 6504 } 6505 } 6506 if (SUCCEEDED(rc)) 6507 { 6508 networkAdapter->COMGETTER(TAPSetupApplication)(tapSetupApplication.asOutParam()); 6509 if (tapSetupApplication.isEmpty()) 6510 { 6511 if (tapDeviceName.isEmpty()) 6512 { 6513 LogRel(("No setup application was supplied for the TAP interface.\n")); 6514 rc = setError(E_FAIL, tr ("No setup application was supplied for the host networking interface")); 6515 } 6516 } 6517 else 6518 { 6519 rc = callTapSetupApplication(isStatic, maTapFD[slot], tapDeviceName, 6520 tapSetupApplication); 6521 } 6522 } 6523 if (SUCCEEDED(rc)) 6524 { 6525 if (!isStatic) 6526 { 6527 Utf8Str str(tapDeviceName); 6528 if (str.length() <= sizeof(IfReq.ifr_name)) 6529 strcpy(IfReq.ifr_name, str.raw()); 6530 else 6531 memcpy(IfReq.ifr_name, str.raw(), sizeof(IfReq.ifr_name) - 1); /** @todo bitch about names which are too long... */ 6532 IfReq.ifr_flags = IFF_TAP | IFF_NO_PI; 6533 rcVBox = ioctl(maTapFD[slot], TUNSETIFF, &IfReq); 6534 if (rcVBox != 0) 6535 { 6536 LogRel(("Failed to open the host network interface %ls returned by the setup script", tapDeviceName.raw())); 6537 rc = setError(E_FAIL, tr ("Failed to open the host network interface %ls returned by the setup script"), tapDeviceName.raw()); 6538 } 6539 } 6540 if (SUCCEEDED(rc)) 6541 { 6542 /* 6543 * Make it pollable. 6544 */ 6545 if (fcntl(maTapFD[slot], F_SETFL, O_NONBLOCK) != -1) 6546 { 6547 Log(("attachToHostInterface: %RTfile %ls\n", maTapFD[slot], tapDeviceName.raw())); 6548 6549 /* 6550 * Here is the right place to communicate the TAP file descriptor and 6551 * the host interface name to the server if/when it becomes really 6552 * necessary. 6553 */ 6554 maTAPDeviceName[slot] = tapDeviceName; 6555 rcVBox = VINF_SUCCESS; 6556 } 6557 else 6558 { 6559 int iErr = errno; 6560 6561 LogRel(("Configuration error: Failed to configure /dev/net/tun non blocking. Error: %s\n", strerror(iErr))); 6562 rcVBox = VERR_HOSTIF_BLOCKING; 6563 rc = setError(E_FAIL, tr ("could not set up the host networking device for non blocking access: %s"), 6564 strerror(errno)); 6565 } 6566 } 6567 } 6568 } 6569 else 6570 { 6571 LogRel(("Configuration error: Failed to open /dev/net/tun rc=%Vrc\n", rcVBox)); 6572 switch (rcVBox) 6573 { 6574 case VERR_ACCESS_DENIED: 6575 /* will be handled by our caller */ 6576 rc = rcVBox; 6577 break; 6578 default: 6579 rc = setError(E_FAIL, tr ("Could not set up the host networking device: %Vrc"), rcVBox); 6580 break; 6581 } 6582 } 6583 #elif defined(RT_OS_DARWIN) 6584 /** @todo Implement tap networking for Darwin. */ 6585 int rcVBox = VERR_NOT_IMPLEMENTED; 6586 #elif defined(RT_OS_OS2) 6587 /** @todo Implement tap networking for OS/2. */ 6588 int rcVBox = VERR_NOT_IMPLEMENTED; 6589 #elif defined(VBOX_WITH_UNIXY_TAP_NETWORKING) 6590 # error "PORTME: Implement OS specific TAP interface open/creation." 6591 #else 6592 # error "Unknown host OS" 6593 #endif 6594 /* in case of failure, cleanup. */ 6595 if (VBOX_FAILURE(rcVBox) && SUCCEEDED(rc)) 6596 { 6597 LogRel(("General failure attaching to host interface\n")); 6598 rc = setError(E_FAIL, tr ("General failure attaching to host interface")); 6599 } 6600 } 6601 LogFlowThisFunc(("rc=%d\n", rc)); 6602 return rc; 6603 } 6604 6605 /** 6606 * Helper function to handle detachment from a host interface 6607 * 6608 * @param networkAdapter the network adapter which attachment should be reset 6609 * @return COM status code 6610 * 6611 * @note The caller must lock this object for writing. 6612 */ 6613 HRESULT Console::detachFromHostInterface(INetworkAdapter *networkAdapter) 6614 { 6615 /* sanity check */ 6616 LogFlowThisFunc(("\n")); 6617 AssertReturn (isLockedOnCurrentThread(), E_FAIL); 6618 6619 HRESULT rc = S_OK; 6620 #ifdef DEBUG 6621 /* paranoia */ 6622 NetworkAttachmentType_T attachment; 6623 networkAdapter->COMGETTER(AttachmentType)(&attachment); 6624 Assert(attachment == NetworkAttachmentType_HostInterfaceNetworkAttachment); 6625 #endif /* DEBUG */ 6626 6627 #ifdef VBOX_WITH_UNIXY_TAP_NETWORKING 6628 6629 ULONG slot = 0; 6630 rc = networkAdapter->COMGETTER(Slot)(&slot); 6631 AssertComRC(rc); 6632 6633 /* is there an open TAP device? */ 6634 if (maTapFD[slot] != NIL_RTFILE) 6635 { 6636 /* 6637 * Close the file handle. 6638 */ 6639 Bstr tapDeviceName, tapTerminateApplication; 6640 bool isStatic = true; 6641 rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam()); 6642 if (FAILED(rc) || tapDeviceName.isEmpty()) 6643 { 6644 /* If the name is not empty, this is a dynamic TAP device, so close it now, 6645 so that the termination script can remove the interface. Otherwise we still 6646 need the FD to pass to the termination script. */ 6647 isStatic = false; 6648 int rcVBox = RTFileClose(maTapFD[slot]); 6649 AssertRC(rcVBox); 6650 maTapFD[slot] = NIL_RTFILE; 6651 } 6652 /* 6653 * Execute the termination command. 6654 */ 6655 networkAdapter->COMGETTER(TAPTerminateApplication)(tapTerminateApplication.asOutParam()); 6656 if (tapTerminateApplication) 6657 { 6658 /* Get the program name. */ 6659 Utf8Str tapTermAppUtf8(tapTerminateApplication); 6660 6661 /* Build the command line. */ 6662 char szCommand[4096]; 6663 RTStrPrintf(szCommand, sizeof(szCommand), "%s %d %s", tapTermAppUtf8.raw(), 6664 isStatic ? maTapFD[slot] : 0, maTAPDeviceName[slot].raw()); 6665 6666 /* 6667 * Create the process and wait for it to complete. 6668 */ 6669 Log(("Calling the termination command: %s\n", szCommand)); 6670 int rcCommand = system(szCommand); 6671 if (rcCommand == -1) 6672 { 6673 LogRel(("Failed to execute the clean up script for the TAP interface")); 6674 rc = setError(E_FAIL, tr ("Failed to execute the clean up script for the TAP interface")); 6675 } 6676 if (!WIFEXITED(rc)) 6677 { 6678 LogRel(("The TAP interface clean up script terminated abnormally.\n")); 6679 rc = setError(E_FAIL, tr ("The TAP interface clean up script terminated abnormally")); 6680 } 6681 if (WEXITSTATUS(rc) != 0) 6682 { 6683 LogRel(("The TAP interface clean up script returned a non-zero exit code.\n")); 6684 rc = setError(E_FAIL, tr ("The TAP interface clean up script returned a non-zero exit code")); 6685 } 6686 } 6687 6688 if (isStatic) 6689 { 6690 /* If we are using a static TAP device, we close it now, after having called the 6691 termination script. */ 6692 int rcVBox = RTFileClose(maTapFD[slot]); 6693 AssertRC(rcVBox); 6694 } 6695 /* the TAP device name and handle are no longer valid */ 6696 maTapFD[slot] = NIL_RTFILE; 6697 maTAPDeviceName[slot] = ""; 6698 } 6699 #endif 6700 LogFlowThisFunc(("returning %d\n", rc)); 6701 return rc; 6702 } 6703 6704 6705 /** 6706 * Called at power down to terminate host interface networking. 6707 * 6708 * @note The caller must lock this object for writing. 6709 */ 6710 HRESULT Console::powerDownHostInterfaces() 6711 { 6712 LogFlowThisFunc (("\n")); 6713 6714 /* sanity check */ 6715 AssertReturn (isLockedOnCurrentThread(), E_FAIL); 6716 6717 /* 6718 * host interface termination handling 6719 */ 6720 HRESULT rc; 6721 for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; slot ++) 6722 { 6723 ComPtr<INetworkAdapter> networkAdapter; 6724 rc = mMachine->GetNetworkAdapter(slot, networkAdapter.asOutParam()); 6725 CheckComRCBreakRC (rc); 6726 6727 BOOL enabled = FALSE; 6728 networkAdapter->COMGETTER(Enabled) (&enabled); 6729 if (!enabled) 6730 continue; 6731 6732 NetworkAttachmentType_T attachment; 6733 networkAdapter->COMGETTER(AttachmentType)(&attachment); 6734 if (attachment == NetworkAttachmentType_HostInterfaceNetworkAttachment) 6735 { 6736 HRESULT rc2 = detachFromHostInterface(networkAdapter); 6737 if (FAILED(rc2) && SUCCEEDED(rc)) 6738 rc = rc2; 6739 } 6740 } 6741 6742 return rc; 6743 } 6744 6745 6746 /** 6747 * Process callback handler for VMR3Load and VMR3Save. 6748 * 6749 * @param pVM The VM handle. 6750 * @param uPercent Completetion precentage (0-100). 6751 * @param pvUser Pointer to the VMProgressTask structure. 6752 * @return VINF_SUCCESS. 6753 */ 6754 /*static*/ DECLCALLBACK (int) 6755 Console::stateProgressCallback (PVM pVM, unsigned uPercent, void *pvUser) 6756 { 6757 VMProgressTask *task = static_cast <VMProgressTask *> (pvUser); 6758 AssertReturn (task, VERR_INVALID_PARAMETER); 6759 6760 /* update the progress object */ 6761 if (task->mProgress) 6762 task->mProgress->notifyProgress (uPercent); 6763 6764 return VINF_SUCCESS; 6765 } 6766 6767 /** 6768 * VM error callback function. Called by the various VM components. 6769 * 6770 * @param pVM The VM handle. Can be NULL if an error occurred before 6771 * successfully creating a VM. 6772 * @param pvUser Pointer to the VMProgressTask structure. 6773 * @param rc VBox status code. 6774 * @param pszFormat The error message. 6775 * @thread EMT. 6776 */ 6777 /* static */ DECLCALLBACK (void) 6778 Console::setVMErrorCallback (PVM pVM, void *pvUser, int rc, RT_SRC_POS_DECL, 6779 const char *pszFormat, va_list args) 6780 { 6781 VMProgressTask *task = static_cast <VMProgressTask *> (pvUser); 6782 AssertReturnVoid (task); 6783 6784 /* we ignore RT_SRC_POS_DECL arguments to avoid confusion of end-users */ 6785 HRESULT hrc = setError (E_FAIL, tr ("%N.\n" 6786 "VBox status code: %d (%Vrc)"), 6787 tr (pszFormat), &args, 6788 rc, rc); 6789 task->mProgress->notifyComplete (hrc); 6790 } 6791 6792 /** 6793 * VM runtime error callback function. 6794 * See VMSetRuntimeError for the detailed description of parameters. 6795 * 6796 * @param pVM The VM handle. 6797 * @param pvUser The user argument. 6798 * @param fFatal Whether it is a fatal error or not. 6799 * @param pszErrorID Error ID string. 6800 * @param pszFormat Error message format string. 6801 * @param args Error message arguments. 6802 * @thread EMT. 6803 */ 6804 /* static */ DECLCALLBACK(void) 6805 Console::setVMRuntimeErrorCallback (PVM pVM, void *pvUser, bool fFatal, 6806 const char *pszErrorID, 6807 const char *pszFormat, va_list args) 6808 { 6809 LogFlowFuncEnter(); 6810 6811 Console *that = static_cast <Console *> (pvUser); 6812 AssertReturnVoid (that); 6813 6814 Utf8Str message = Utf8StrFmtVA (pszFormat, args); 6815 6816 LogRel (("Console: VM runtime error: fatal=%RTbool, " 6817 "errorID=%s message=\"%s\"\n", 6818 fFatal, pszErrorID, message.raw())); 6819 6820 that->onRuntimeError (BOOL (fFatal), Bstr (pszErrorID), Bstr (message)); 6821 6822 LogFlowFuncLeave(); 6823 } 6824 6825 /** 6826 * Captures USB devices that match filters of the VM. 6827 * Called at VM startup. 6828 * 6829 * @param pVM The VM handle. 6830 * 6831 * @note The caller must lock this object for writing. 6832 */ 6833 HRESULT Console::captureUSBDevices (PVM pVM) 6834 { 6835 LogFlowThisFunc (("\n")); 6836 6837 /* sanity check */ 6838 ComAssertRet (isLockedOnCurrentThread(), E_FAIL); 6839 6840 /* If the machine has an USB controller, ask the USB proxy service to 6841 * capture devices */ 6842 PPDMIBASE pBase; 6843 int vrc = PDMR3QueryLun (pVM, "usb-ohci", 0, 0, &pBase); 6844 if (VBOX_SUCCESS (vrc)) 6845 { 6846 /* leave the lock before calling Host in VBoxSVC since Host may call 6847 * us back from under its lock (e.g. onUSBDeviceAttach()) which would 6848 * produce an inter-process dead-lock otherwise. */ 6849 AutoLock alock (this); 6850 alock.leave(); 6851 6852 HRESULT hrc = mControl->AutoCaptureUSBDevices(); 6853 ComAssertComRCRetRC (hrc); 6854 } 6855 else if ( vrc == VERR_PDM_DEVICE_NOT_FOUND 6856 || vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND) 6857 vrc = VINF_SUCCESS; 6858 else 6859 AssertRC (vrc); 6860 6861 return VBOX_SUCCESS (vrc) ? S_OK : E_FAIL; 6862 } 6863 6864 6865 /** 6866 * Detach all USB device which are attached to the VM for the 6867 * purpose of clean up and such like. 6868 * 6869 * @note The caller must lock this object for writing. 6870 */ 6871 void Console::detachAllUSBDevices (bool aDone) 6872 { 6873 LogFlowThisFunc (("\n")); 6874 6875 /* sanity check */ 6876 AssertReturnVoid (isLockedOnCurrentThread()); 6877 6878 mUSBDevices.clear(); 6879 6880 /* leave the lock before calling Host in VBoxSVC since Host may call 6881 * us back from under its lock (e.g. onUSBDeviceAttach()) which would 6882 * produce an inter-process dead-lock otherwise. */ 6883 AutoLock alock (this); 6884 alock.leave(); 6885 6886 mControl->DetachAllUSBDevices (aDone); 6887 } 6888 6889 /** 6890 * @note Locks this object for writing. 6891 */ 6892 void Console::processRemoteUSBDevices (uint32_t u32ClientId, VRDPUSBDEVICEDESC *pDevList, uint32_t cbDevList) 6893 { 6894 LogFlowThisFuncEnter(); 6895 LogFlowThisFunc (("u32ClientId = %d, pDevList=%p, cbDevList = %d\n", u32ClientId, pDevList, cbDevList)); 6896 6897 AutoCaller autoCaller (this); 6898 if (!autoCaller.isOk()) 6899 { 6900 /* Console has been already uninitialized, deny request */ 6901 AssertMsgFailed (("Temporary assertion to prove that it happens, " 6902 "please report to dmik\n")); 6903 LogFlowThisFunc (("Console is already uninitialized\n")); 6904 LogFlowThisFuncLeave(); 6905 return; 6906 } 6907 6908 AutoLock alock (this); 6909 6910 /* 6911 * Mark all existing remote USB devices as dirty. 6912 */ 6913 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin(); 6914 while (it != mRemoteUSBDevices.end()) 6915 { 6916 (*it)->dirty (true); 6917 ++ it; 6918 } 6919 6920 /* 6921 * Process the pDevList and add devices those are not already in the mRemoteUSBDevices list. 6922 */ 6923 /** @todo (sunlover) REMOTE_USB Strict validation of the pDevList. */ 6924 VRDPUSBDEVICEDESC *e = pDevList; 6925 6926 /* The cbDevList condition must be checked first, because the function can 6927 * receive pDevList = NULL and cbDevList = 0 on client disconnect. 6928 */ 6929 while (cbDevList >= 2 && e->oNext) 6930 { 6931 LogFlowThisFunc (("vendor %04X, product %04X, name = %s\n", 6932 e->idVendor, e->idProduct, 6933 e->oProduct? (char *)e + e->oProduct: "")); 6934 6935 bool fNewDevice = true; 6936 6937 it = mRemoteUSBDevices.begin(); 6938 while (it != mRemoteUSBDevices.end()) 6939 { 6940 if ((*it)->devId () == e->id 6941 && (*it)->clientId () == u32ClientId) 6942 { 6943 /* The device is already in the list. */ 6944 (*it)->dirty (false); 6945 fNewDevice = false; 6946 break; 6947 } 6948 6949 ++ it; 6950 } 6951 6952 if (fNewDevice) 6953 { 6954 LogRel(("Remote USB: ++++ Vendor %04X. Product %04X. Name = [%s].\n", 6955 e->idVendor, e->idProduct, e->oProduct? (char *)e + e->oProduct: "" 6956 )); 6957 6958 /* Create the device object and add the new device to list. */ 6959 ComObjPtr <RemoteUSBDevice> device; 6960 device.createObject(); 6961 device->init (u32ClientId, e); 6962 6963 mRemoteUSBDevices.push_back (device); 6964 6965 /* Check if the device is ok for current USB filters. */ 6966 BOOL fMatched = FALSE; 6967 6968 HRESULT hrc = mControl->RunUSBDeviceFilters(device, &fMatched); 6969 6970 AssertComRC (hrc); 6971 6972 LogFlowThisFunc (("USB filters return %d\n", fMatched)); 6973 6974 if (fMatched) 6975 { 6976 hrc = onUSBDeviceAttach (device, NULL); 6977 6978 /// @todo (r=dmik) warning reporting subsystem 6979 6980 if (hrc == S_OK) 6981 { 6982 LogFlowThisFunc (("Device attached\n")); 6983 device->captured (true); 6984 } 6985 } 6986 } 6987 6988 if (cbDevList < e->oNext) 6989 { 6990 LogWarningThisFunc (("cbDevList %d > oNext %d\n", 6991 cbDevList, e->oNext)); 6992 break; 6993 } 6994 6995 cbDevList -= e->oNext; 6996 6997 e = (VRDPUSBDEVICEDESC *)((uint8_t *)e + e->oNext); 6998 } 6999 7000 /* 7001 * Remove dirty devices, that is those which are not reported by the server anymore. 7002 */ 7003 for (;;) 7004 { 7005 ComObjPtr <RemoteUSBDevice> device; 7006 7007 RemoteUSBDeviceList::iterator it = mRemoteUSBDevices.begin(); 7008 while (it != mRemoteUSBDevices.end()) 7009 { 7010 if ((*it)->dirty ()) 7011 { 7012 device = *it; 7013 break; 7014 } 7015 7016 ++ it; 7017 } 7018 7019 if (!device) 7020 { 7021 break; 7022 } 7023 7024 USHORT vendorId = 0; 7025 device->COMGETTER(VendorId) (&vendorId); 7026 7027 USHORT productId = 0; 7028 device->COMGETTER(ProductId) (&productId); 7029 7030 Bstr product; 7031 device->COMGETTER(Product) (product.asOutParam()); 7032 7033 LogRel(("Remote USB: ---- Vendor %04X. Product %04X. Name = [%ls].\n", 7034 vendorId, productId, product.raw () 7035 )); 7036 7037 /* Detach the device from VM. */ 7038 if (device->captured ()) 7039 { 7040 Guid uuid; 7041 device->COMGETTER (Id) (uuid.asOutParam()); 7042 onUSBDeviceDetach (uuid, NULL); 7043 } 7044 7045 /* And remove it from the list. */ 7046 mRemoteUSBDevices.erase (it); 7047 } 7048 7049 LogFlowThisFuncLeave(); 7050 } 7051 7052 7053 7054 /** 7055 * Thread function which starts the VM (also from saved state) and 7056 * track progress. 7057 * 7058 * @param Thread The thread id. 7059 * @param pvUser Pointer to a VMPowerUpTask structure. 7060 * @return VINF_SUCCESS (ignored). 7061 * 7062 * @note Locks the Console object for writing. 7063 */ 7064 /*static*/ 7065 DECLCALLBACK (int) Console::powerUpThread (RTTHREAD Thread, void *pvUser) 7066 { 7067 LogFlowFuncEnter(); 7068 7069 std::auto_ptr <VMPowerUpTask> task (static_cast <VMPowerUpTask *> (pvUser)); 7070 AssertReturn (task.get(), VERR_INVALID_PARAMETER); 7071 7072 AssertReturn (!task->mConsole.isNull(), VERR_INVALID_PARAMETER); 7073 AssertReturn (!task->mProgress.isNull(), VERR_INVALID_PARAMETER); 7074 7075 #if defined(RT_OS_WINDOWS) 7076 { 7077 /* initialize COM */ 7078 HRESULT hrc = CoInitializeEx (NULL, 7079 COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE | 7080 COINIT_SPEED_OVER_MEMORY); 7081 LogFlowFunc (("CoInitializeEx()=%08X\n", hrc)); 7082 } 7083 #endif 7084 7085 HRESULT hrc = S_OK; 7086 int vrc = VINF_SUCCESS; 7087 7088 /* Set up a build identifier so that it can be seen from core dumps what 7089 * exact build was used to produce the core. */ 7090 static char saBuildID[40]; 7091 RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%d %s%s%s%s", 7092 "BU", "IL", "DI", "D", VBOX_VERSION_STRING, VBOX_SVN_REV, "BU", "IL", "DI", "D"); 7093 7094 ComObjPtr <Console> console = task->mConsole; 7095 7096 /* Note: no need to use addCaller() because VMPowerUpTask does that */ 7097 7098 AutoLock alock (console); 7099 7100 /* sanity */ 7101 Assert (console->mpVM == NULL); 7102 7103 do 7104 { 7105 /* 7106 * Initialize the release logging facility. In case something 7107 * goes wrong, there will be no release logging. Maybe in the future 7108 * we can add some logic to use different file names in this case. 7109 * Note that the logic must be in sync with Machine::DeleteSettings(). 7110 */ 7111 7112 Bstr logFolder; 7113 hrc = console->mMachine->COMGETTER(LogFolder) (logFolder.asOutParam()); 7114 CheckComRCBreakRC (hrc); 7115 7116 Utf8Str logDir = logFolder; 7117 7118 /* make sure the Logs folder exists */ 7119 Assert (!logDir.isEmpty()); 7120 if (!RTDirExists (logDir)) 7121 RTDirCreateFullPath (logDir, 0777); 7122 7123 Utf8Str logFile = Utf8StrFmt ("%s%cVBox.log", 7124 logDir.raw(), RTPATH_DELIMITER); 7125 Utf8Str pngFile = Utf8StrFmt ("%s%cVBox.png", 7126 logDir.raw(), RTPATH_DELIMITER); 7127 7128 /* 7129 * Age the old log files 7130 * Rename .2 to .3, .1 to .2 and the last log file to .1 7131 * Overwrite target files in case they exist; 7132 */ 7133 for (int i = 2; i >= 0; i--) 7134 { 7135 Utf8Str *files[] = { &logFile, &pngFile }; 7136 Utf8Str oldName, newName; 7137 7138 for (unsigned int j = 0; j < ELEMENTS (files); ++ j) 7139 { 7140 if (i > 0) 7141 oldName = Utf8StrFmt ("%s.%d", files [j]->raw(), i); 7142 else 7143 oldName = *files [j]; 7144 newName = Utf8StrFmt ("%s.%d", files [j]->raw(), i + 1); 7145 /* If the old file doesn't exist, delete the new file (if it 7146 * exists) to provide correct rotation even if the sequence is 7147 * broken */ 7148 if (RTFileRename (oldName, newName, RTFILEMOVE_FLAGS_REPLACE) == 7149 VERR_FILE_NOT_FOUND) 7150 RTFileDelete (newName); 7151 } 7152 } 7153 7154 PRTLOGGER loggerRelease; 7155 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES; 7156 RTUINT fFlags = RTLOGFLAGS_PREFIX_TIME_PROG; 7157 #if defined (RT_OS_WINDOWS) || defined (RT_OS_OS2) 7158 fFlags |= RTLOGFLAGS_USECRLF; 7159 #endif 7160 char szError[RTPATH_MAX + 128] = ""; 7161 vrc = RTLogCreateEx(&loggerRelease, fFlags, "all", 7162 "VBOX_RELEASE_LOG", ELEMENTS(s_apszGroups), s_apszGroups, 7163 RTLOGDEST_FILE, szError, sizeof(szError), logFile.raw()); 7164 if (VBOX_SUCCESS(vrc)) 7165 { 7166 /* some introductory information */ 7167 RTTIMESPEC timeSpec; 7168 char nowUct[64]; 7169 RTTimeSpecToString(RTTimeNow(&timeSpec), nowUct, sizeof(nowUct)); 7170 RTLogRelLogger(loggerRelease, 0, ~0U, 7171 "VirtualBox %s r%d (%s %s) release log\n" 7172 "Log opened %s\n", 7173 VBOX_VERSION_STRING, VBOX_SVN_REV, __DATE__, __TIME__, 7174 nowUct); 7175 7176 /* register this logger as the release logger */ 7177 RTLogRelSetDefaultInstance(loggerRelease); 7178 } 7179 else 7180 { 7181 hrc = setError (E_FAIL, 7182 tr ("Failed to open release log (%s, %Vrc)"), szError, vrc); 7183 break; 7184 } 7185 7186 #ifdef VBOX_VRDP 7187 if (VBOX_SUCCESS (vrc)) 7188 { 7189 /* Create the VRDP server. In case of headless operation, this will 7190 * also create the framebuffer, required at VM creation. 7191 */ 7192 ConsoleVRDPServer *server = console->consoleVRDPServer(); 7193 Assert (server); 7194 /// @todo (dmik) 7195 // does VRDP server call Console from the other thread? 7196 // Not sure, so leave the lock just in case 7197 alock.leave(); 7198 vrc = server->Launch(); 7199 alock.enter(); 7200 if (VBOX_FAILURE (vrc)) 7201 { 7202 Utf8Str errMsg; 7203 switch (vrc) 7204 { 7205 case VERR_NET_ADDRESS_IN_USE: 7206 { 7207 ULONG port = 0; 7208 console->mVRDPServer->COMGETTER(Port) (&port); 7209 errMsg = Utf8StrFmt (tr ("VRDP server port %d is already in use"), 7210 port); 7211 break; 7212 } 7213 default: 7214 errMsg = Utf8StrFmt (tr ("Failed to launch VRDP server (%Vrc)"), 7215 vrc); 7216 } 7217 LogRel (("Failed to launch VRDP server (%Vrc), error message: '%s'\n", 7218 vrc, errMsg.raw())); 7219 hrc = setError (E_FAIL, errMsg); 7220 break; 7221 } 7222 } 7223 #endif /* VBOX_VRDP */ 7224 7225 /* 7226 * Create the VM 7227 */ 7228 PVM pVM; 7229 /* 7230 * leave the lock since EMT will call Console. It's safe because 7231 * mMachineState is either Starting or Restoring state here. 7232 */ 7233 alock.leave(); 7234 7235 vrc = VMR3Create (task->mSetVMErrorCallback, task.get(), 7236 task->mConfigConstructor, task.get(), 7237 &pVM); 7238 7239 alock.enter(); 7240 7241 #ifdef VBOX_VRDP 7242 { 7243 /* Enable client connections to the server. */ 7244 ConsoleVRDPServer *server = console->consoleVRDPServer(); 7245 #ifdef VRDP_NO_COM 7246 server->EnableConnections (); 7247 #else 7248 server->SetCallback (); 7249 #endif /* VRDP_NO_COM */ 7250 } 7251 #endif /* VBOX_VRDP */ 7252 7253 if (VBOX_SUCCESS (vrc)) 7254 { 7255 do 7256 { 7257 /* 7258 * Register our load/save state file handlers 7259 */ 7260 vrc = SSMR3RegisterExternal (pVM, 7261 sSSMConsoleUnit, 0 /* iInstance */, sSSMConsoleVer, 7262 0 /* cbGuess */, 7263 NULL, saveStateFileExec, NULL, NULL, loadStateFileExec, NULL, 7264 static_cast <Console *> (console)); 7265 AssertRC (vrc); 7266 if (VBOX_FAILURE (vrc)) 7267 break; 7268 7269 /* 7270 * Synchronize debugger settings 7271 */ 7272 MachineDebugger *machineDebugger = console->getMachineDebugger(); 7273 if (machineDebugger) 7274 { 7275 machineDebugger->flushQueuedSettings(); 7276 } 7277 7278 /* 7279 * Shared Folders 7280 */ 7281 if (console->getVMMDev()->isShFlActive()) 7282 { 7283 /// @todo (dmik) 7284 // does the code below call Console from the other thread? 7285 // Not sure, so leave the lock just in case 7286 alock.leave(); 7287 7288 for (SharedFolderDataMap::const_iterator 7289 it = task->mSharedFolders.begin(); 7290 it != task->mSharedFolders.end(); 7291 ++ it) 7292 { 7293 hrc = console->createSharedFolder ((*it).first, (*it).second); 7294 CheckComRCBreakRC (hrc); 7295 } 7296 7297 /* enter the lock again */ 7298 alock.enter(); 7299 7300 CheckComRCBreakRC (hrc); 7301 } 7302 7303 /* 7304 * Capture USB devices. 7305 */ 7306 hrc = console->captureUSBDevices (pVM); 7307 CheckComRCBreakRC (hrc); 7308 7309 /* leave the lock before a lengthy operation */ 7310 alock.leave(); 7311 7312 /* Load saved state? */ 7313 if (!!task->mSavedStateFile) 7314 { 7315 LogFlowFunc (("Restoring saved state from '%s'...\n", 7316 task->mSavedStateFile.raw())); 7317 7318 vrc = VMR3Load (pVM, task->mSavedStateFile, 7319 Console::stateProgressCallback, 7320 static_cast <VMProgressTask *> (task.get())); 7321 7322 /* Start/Resume the VM execution */ 7323 if (VBOX_SUCCESS (vrc)) 7324 { 7325 vrc = VMR3Resume (pVM); 7326 AssertRC (vrc); 7327 } 7328 7329 /* Power off in case we failed loading or resuming the VM */ 7330 if (VBOX_FAILURE (vrc)) 7331 { 7332 int vrc2 = VMR3PowerOff (pVM); 7333 AssertRC (vrc2); 7334 } 7335 } 7336 else 7337 { 7338 /* Power on the VM (i.e. start executing) */ 7339 vrc = VMR3PowerOn(pVM); 7340 AssertRC (vrc); 7341 } 7342 7343 /* enter the lock again */ 7344 alock.enter(); 7345 } 7346 while (0); 7347 7348 /* On failure, destroy the VM */ 7349 if (FAILED (hrc) || VBOX_FAILURE (vrc)) 7350 { 7351 /* preserve existing error info */ 7352 ErrorInfoKeeper eik; 7353 7354 /* 7355 * powerDown() will call VMR3Destroy() and do all necessary 7356 * cleanup (VRDP, USB devices) 7357 */ 7358 HRESULT hrc2 = console->powerDown(); 7359 AssertComRC (hrc2); 7360 } 7361 } 7362 else 7363 { 7364 /* 7365 * If VMR3Create() failed it has released the VM memory. 7366 */ 7367 console->mpVM = NULL; 7368 } 7369 7370 if (SUCCEEDED (hrc) && VBOX_FAILURE (vrc)) 7371 { 7372 /* 7373 * If VMR3Create() or one of the other calls in this function fail, 7374 * an appropriate error message has been already set. However since 7375 * that happens via a callback, the status code in this function is 7376 * not updated. 7377 */ 7378 if (!task->mProgress->completed()) 7379 { 7380 /* 7381 * If the COM error info is not yet set but we've got a 7382 * failure, convert the VBox status code into a meaningful 7383 * error message. This becomes unused once all the sources of 7384 * errors set the appropriate error message themselves. 7385 * Note that we don't use VMSetError() below because pVM is 7386 * either invalid or NULL here. 7387 */ 7388 AssertMsgFailed (("Missing error message during powerup for " 7389 "status code %Vrc\n", vrc)); 7390 hrc = setError (E_FAIL, 7391 tr ("Failed to start VM execution (%Vrc)"), vrc); 7392 } 7393 else 7394 hrc = task->mProgress->resultCode(); 7395 7396 Assert (FAILED (hrc)); 7397 break; 7398 } 7399 } 7400 while (0); 7401 7402 if (console->mMachineState == MachineState_Starting || 7403 console->mMachineState == MachineState_Restoring) 7404 { 7405 /* 7406 * We are still in the Starting/Restoring state. This means one of: 7407 * 1) we failed before VMR3Create() was called; 7408 * 2) VMR3Create() failed. 7409 * In both cases, there is no need to call powerDown(), but we still 7410 * need to go back to the PoweredOff/Saved state. Reuse 7411 * vmstateChangeCallback() for that purpose. 7412 */ 7413 7414 /* preserve existing error info */ 7415 ErrorInfoKeeper eik; 7416 7417 Assert (console->mpVM == NULL); 7418 vmstateChangeCallback (NULL, VMSTATE_TERMINATED, VMSTATE_CREATING, 7419 console); 7420 } 7421 7422 /* 7423 * Evaluate the final result. 7424 * Note that the appropriate mMachineState value is already set by 7425 * vmstateChangeCallback() in all cases. 7426 */ 7427 7428 /* leave the lock, don't need it any more */ 7429 alock.leave(); 7430 7431 if (SUCCEEDED (hrc)) 7432 { 7433 /* Notify the progress object of the success */ 7434 task->mProgress->notifyComplete (S_OK); 7435 } 7436 else 7437 { 7438 if (!task->mProgress->completed()) 7439 { 7440 /* The progress object will fetch the current error info. This 7441 * gets the errors signalled by using setError(). The ones 7442 * signalled via VMSetError() immediately notify the progress 7443 * object that the operation is completed. */ 7444 task->mProgress->notifyComplete (hrc); 7445 } 7446 7447 LogRel (("Power up failed (vrc=%Vrc, hrc=0x%08X)\n", vrc, hrc)); 7448 } 7449 7450 #if defined(RT_OS_WINDOWS) 7451 /* uninitialize COM */ 7452 CoUninitialize(); 7453 #endif 7454 7455 LogFlowFuncLeave(); 7456 7457 return VINF_SUCCESS; 7458 } 7459 7460 7461 /** 7462 * Reconfigures a VDI. 7463 * 7464 * @param pVM The VM handle. 7465 * @param hda The harddisk attachment. 7466 * @param phrc Where to store com error - only valid if we return VERR_GENERAL_FAILURE. 7467 * @return VBox status code. 7468 */ 7469 static DECLCALLBACK(int) reconfigureVDI(PVM pVM, IHardDiskAttachment *hda, HRESULT *phrc) 7470 { 7471 LogFlowFunc (("pVM=%p hda=%p phrc=%p\n", pVM, hda, phrc)); 7472 7473 int rc; 7474 HRESULT hrc; 7475 char *psz = NULL; 7476 BSTR str = NULL; 7477 *phrc = S_OK; 7478 #define STR_CONV() do { rc = RTStrUcs2ToUtf8(&psz, str); RC_CHECK(); } while (0) 7479 #define STR_FREE() do { if (str) { SysFreeString(str); str = NULL; } if (psz) { RTStrFree(psz); psz = NULL; } } while (0) 7480 #define RC_CHECK() do { if (VBOX_FAILURE(rc)) { AssertMsgFailed(("rc=%Vrc\n", rc)); STR_FREE(); return rc; } } while (0) 7481 #define H() do { if (FAILED(hrc)) { AssertMsgFailed(("hrc=%#x\n", hrc)); STR_FREE(); *phrc = hrc; return VERR_GENERAL_FAILURE; } } while (0) 7482 7483 /* 7484 * Figure out which IDE device this is. 7485 */ 7486 ComPtr<IHardDisk> hardDisk; 7487 hrc = hda->COMGETTER(HardDisk)(hardDisk.asOutParam()); H(); 7488 DiskControllerType_T enmCtl; 7489 hrc = hda->COMGETTER(Controller)(&enmCtl); H(); 7490 LONG lDev; 7491 hrc = hda->COMGETTER(DeviceNumber)(&lDev); H(); 7492 7493 int i; 7494 switch (enmCtl) 7495 { 7496 case DiskControllerType_IDE0Controller: 7497 i = 0; 7498 break; 7499 case DiskControllerType_IDE1Controller: 7500 i = 2; 7501 break; 7502 default: 7503 AssertMsgFailed(("invalid disk controller type: %d\n", enmCtl)); 7504 return VERR_GENERAL_FAILURE; 7505 } 7506 7507 if (lDev < 0 || lDev >= 2) 7508 { 7509 AssertMsgFailed(("invalid controller device number: %d\n", lDev)); 7510 return VERR_GENERAL_FAILURE; 7511 } 7512 7513 i = i + lDev; 7514 7515 /* 7516 * Is there an existing LUN? If not create it. 7517 * We ASSUME that this will NEVER collide with the DVD. 7518 */ 7519 PCFGMNODE pCfg; 7520 PCFGMNODE pLunL1 = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/piix3ide/0/LUN#%d/AttachedDriver/", i); 7521 if (!pLunL1) 7522 { 7523 PCFGMNODE pInst = CFGMR3GetChild(CFGMR3GetRoot(pVM), "Devices/piix3ide/0/"); 7524 AssertReturn(pInst, VERR_INTERNAL_ERROR); 7525 7526 PCFGMNODE pLunL0; 7527 rc = CFGMR3InsertNodeF(pInst, &pLunL0, "LUN#%d", i); RC_CHECK(); 7528 rc = CFGMR3InsertString(pLunL0, "Driver", "Block"); RC_CHECK(); 7529 rc = CFGMR3InsertNode(pLunL0, "Config", &pCfg); RC_CHECK(); 7530 rc = CFGMR3InsertString(pCfg, "Type", "HardDisk"); RC_CHECK(); 7531 rc = CFGMR3InsertInteger(pCfg, "Mountable", 0); RC_CHECK(); 7532 7533 rc = CFGMR3InsertNode(pLunL0, "AttachedDriver", &pLunL1); RC_CHECK(); 7534 rc = CFGMR3InsertString(pLunL1, "Driver", "VBoxHDD"); RC_CHECK(); 7535 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK(); 7536 } 7537 else 7538 { 7539 #ifdef VBOX_STRICT 7540 char *pszDriver; 7541 rc = CFGMR3QueryStringAlloc(pLunL1, "Driver", &pszDriver); RC_CHECK(); 7542 Assert(!strcmp(pszDriver, "VBoxHDD")); 7543 MMR3HeapFree(pszDriver); 7544 #endif 7545 7546 /* 7547 * Check if things has changed. 7548 */ 7549 pCfg = CFGMR3GetChild(pLunL1, "Config"); 7550 AssertReturn(pCfg, VERR_INTERNAL_ERROR); 7551 7552 /* the image */ 7553 /// @todo (dmik) we temporarily use the location property to 7554 // determine the image file name. This is subject to change 7555 // when iSCSI disks are here (we should either query a 7556 // storage-specific interface from IHardDisk, or "standardize" 7557 // the location property) 7558 hrc = hardDisk->COMGETTER(Location)(&str); H(); 7559 STR_CONV(); 7560 char *pszPath; 7561 rc = CFGMR3QueryStringAlloc(pCfg, "Path", &pszPath); RC_CHECK(); 7562 if (!strcmp(psz, pszPath)) 7563 { 7564 /* parent images. */ 7565 ComPtr<IHardDisk> parentHardDisk = hardDisk; 7566 for (PCFGMNODE pParent = pCfg;;) 7567 { 7568 MMR3HeapFree(pszPath); 7569 pszPath = NULL; 7570 STR_FREE(); 7571 7572 /* get parent */ 7573 ComPtr<IHardDisk> curHardDisk; 7574 hrc = parentHardDisk->COMGETTER(Parent)(curHardDisk.asOutParam()); H(); 7575 PCFGMNODE pCur; 7576 pCur = CFGMR3GetChild(pParent, "Parent"); 7577 if (!pCur && !curHardDisk) 7578 { 7579 /* no change */ 7580 LogFlowFunc (("No change!\n")); 7581 return VINF_SUCCESS; 7582 } 7583 if (!pCur || !curHardDisk) 7584 break; 7585 7586 /* compare paths. */ 7587 /// @todo (dmik) we temporarily use the location property to 7588 // determine the image file name. This is subject to change 7589 // when iSCSI disks are here (we should either query a 7590 // storage-specific interface from IHardDisk, or "standardize" 7591 // the location property) 7592 hrc = curHardDisk->COMGETTER(Location)(&str); H(); 7593 STR_CONV(); 7594 rc = CFGMR3QueryStringAlloc(pCfg, "Path", &pszPath); RC_CHECK(); 7595 if (strcmp(psz, pszPath)) 7596 break; 7597 7598 /* next */ 7599 pParent = pCur; 7600 parentHardDisk = curHardDisk; 7601 } 7602 7603 } 7604 else 7605 LogFlowFunc (("LUN#%d: old leaf image '%s'\n", i, pszPath)); 7606 7607 MMR3HeapFree(pszPath); 7608 STR_FREE(); 7609 7610 /* 7611 * Detach the driver and replace the config node. 7612 */ 7613 rc = PDMR3DeviceDetach(pVM, "piix3ide", 0, i); RC_CHECK(); 7614 CFGMR3RemoveNode(pCfg); 7615 rc = CFGMR3InsertNode(pLunL1, "Config", &pCfg); RC_CHECK(); 7616 } 7617 7618 /* 7619 * Create the driver configuration. 7620 */ 7621 /// @todo (dmik) we temporarily use the location property to 7622 // determine the image file name. This is subject to change 7623 // when iSCSI disks are here (we should either query a 7624 // storage-specific interface from IHardDisk, or "standardize" 7625 // the location property) 7626 hrc = hardDisk->COMGETTER(Location)(&str); H(); 7627 STR_CONV(); 7628 LogFlowFunc (("LUN#%d: leaf image '%s'\n", i, psz)); 7629 rc = CFGMR3InsertString(pCfg, "Path", psz); RC_CHECK(); 7630 STR_FREE(); 7631 /* Create an inversed tree of parents. */ 7632 ComPtr<IHardDisk> parentHardDisk = hardDisk; 7633 for (PCFGMNODE pParent = pCfg;;) 7634 { 7635 ComPtr<IHardDisk> curHardDisk; 7636 hrc = parentHardDisk->COMGETTER(Parent)(curHardDisk.asOutParam()); H(); 7637 if (!curHardDisk) 7638 break; 7639 7640 PCFGMNODE pCur; 7641 rc = CFGMR3InsertNode(pParent, "Parent", &pCur); RC_CHECK(); 7642 /// @todo (dmik) we temporarily use the location property to 7643 // determine the image file name. This is subject to change 7644 // when iSCSI disks are here (we should either query a 7645 // storage-specific interface from IHardDisk, or "standardize" 7646 // the location property) 7647 hrc = curHardDisk->COMGETTER(Location)(&str); H(); 7648 STR_CONV(); 7649 rc = CFGMR3InsertString(pCur, "Path", psz); RC_CHECK(); 7650 STR_FREE(); 7651 7652 /* next */ 7653 pParent = pCur; 7654 parentHardDisk = curHardDisk; 7655 } 7656 7657 /* 7658 * Attach the new driver. 7659 */ 7660 rc = PDMR3DeviceAttach(pVM, "piix3ide", 0, i, NULL); RC_CHECK(); 7661 7662 LogFlowFunc (("Returns success\n")); 7663 return rc; 7664 } 7665 7666 7667 /** 7668 * Thread for executing the saved state operation. 7669 * 7670 * @param Thread The thread handle. 7671 * @param pvUser Pointer to a VMSaveTask structure. 7672 * @return VINF_SUCCESS (ignored). 7673 * 7674 * @note Locks the Console object for writing. 7675 */ 7676 /*static*/ 7677 DECLCALLBACK (int) Console::saveStateThread (RTTHREAD Thread, void *pvUser) 7678 { 7679 LogFlowFuncEnter(); 7680 7681 std::auto_ptr <VMSaveTask> task (static_cast <VMSaveTask *> (pvUser)); 7682 AssertReturn (task.get(), VERR_INVALID_PARAMETER); 7683 7684 Assert (!task->mSavedStateFile.isNull()); 7685 Assert (!task->mProgress.isNull()); 7686 7687 const ComObjPtr <Console> &that = task->mConsole; 7688 7689 /* 7690 * Note: no need to use addCaller() to protect Console or addVMCaller() to 7691 * protect mpVM because VMSaveTask does that 7692 */ 7693 7694 Utf8Str errMsg; 7695 HRESULT rc = S_OK; 7696 7697 if (task->mIsSnapshot) 7698 { 7699 Assert (!task->mServerProgress.isNull()); 7700 LogFlowFunc (("Waiting until the server creates differencing VDIs...\n")); 7701 7702 rc = task->mServerProgress->WaitForCompletion (-1); 7703 if (SUCCEEDED (rc)) 7704 { 7705 HRESULT result = S_OK; 7706 rc = task->mServerProgress->COMGETTER(ResultCode) (&result); 7707 if (SUCCEEDED (rc)) 7708 rc = result; 7709 } 7710 } 7711 7712 if (SUCCEEDED (rc)) 7713 { 7714 LogFlowFunc (("Saving the state to '%s'...\n", task->mSavedStateFile.raw())); 7715 7716 int vrc = VMR3Save (that->mpVM, task->mSavedStateFile, 7717 Console::stateProgressCallback, 7718 static_cast <VMProgressTask *> (task.get())); 7719 if (VBOX_FAILURE (vrc)) 7720 { 7721 errMsg = Utf8StrFmt ( 7722 Console::tr ("Failed to save the machine state to '%s' (%Vrc)"), 7723 task->mSavedStateFile.raw(), vrc); 7724 rc = E_FAIL; 7725 } 7726 } 7727 7728 /* lock the console sonce we're going to access it */ 7729 AutoLock thatLock (that); 7730 7731 if (SUCCEEDED (rc)) 7732 { 7733 if (task->mIsSnapshot) 7734 do 7735 { 7736 LogFlowFunc (("Reattaching new differencing VDIs...\n")); 7737 7738 ComPtr <IHardDiskAttachmentCollection> hdaColl; 7739 rc = that->mMachine->COMGETTER(HardDiskAttachments) (hdaColl.asOutParam()); 7740 if (FAILED (rc)) 7741 break; 7742 ComPtr <IHardDiskAttachmentEnumerator> hdaEn; 7743 rc = hdaColl->Enumerate (hdaEn.asOutParam()); 7744 if (FAILED (rc)) 7745 break; 7746 BOOL more = FALSE; 7747 while (SUCCEEDED (rc = hdaEn->HasMore (&more)) && more) 7748 { 7749 ComPtr <IHardDiskAttachment> hda; 7750 rc = hdaEn->GetNext (hda.asOutParam()); 7751 if (FAILED (rc)) 7752 break; 7753 7754 PVMREQ pReq; 7755 IHardDiskAttachment *pHda = hda; 7756 /* 7757 * don't leave the lock since reconfigureVDI isn't going to 7758 * access Console. 7759 */ 7760 int vrc = VMR3ReqCall (that->mpVM, &pReq, RT_INDEFINITE_WAIT, 7761 (PFNRT)reconfigureVDI, 3, that->mpVM, 7762 pHda, &rc); 7763 if (VBOX_SUCCESS (rc)) 7764 rc = pReq->iStatus; 7765 VMR3ReqFree (pReq); 7766 if (FAILED (rc)) 7767 break; 7768 if (VBOX_FAILURE (vrc)) 7769 { 7770 errMsg = Utf8StrFmt (Console::tr ("%Vrc"), vrc); 7771 rc = E_FAIL; 7772 break; 7773 } 7774 } 7775 } 7776 while (0); 7777 } 7778 7779 /* finalize the procedure regardless of the result */ 7780 if (task->mIsSnapshot) 7781 { 7782 /* 7783 * finalize the requested snapshot object. 7784 * This will reset the machine state to the state it had right 7785 * before calling mControl->BeginTakingSnapshot(). 7786 */ 7787 that->mControl->EndTakingSnapshot (SUCCEEDED (rc)); 7788 } 7789 else 7790 { 7791 /* 7792 * finalize the requested save state procedure. 7793 * In case of success, the server will set the machine state to Saved; 7794 * in case of failure it will reset the it to the state it had right 7795 * before calling mControl->BeginSavingState(). 7796 */ 7797 that->mControl->EndSavingState (SUCCEEDED (rc)); 7798 } 7799 7800 /* synchronize the state with the server */ 7801 if (task->mIsSnapshot || FAILED (rc)) 7802 { 7803 if (task->mLastMachineState == MachineState_Running) 7804 { 7805 /* restore the paused state if appropriate */ 7806 that->setMachineStateLocally (MachineState_Paused); 7807 /* restore the running state if appropriate */ 7808 that->Resume(); 7809 } 7810 else 7811 that->setMachineStateLocally (task->mLastMachineState); 7812 } 7813 else 7814 { 7815 /* 7816 * The machine has been successfully saved, so power it down 7817 * (vmstateChangeCallback() will set state to Saved on success). 7818 * Note: we release the task's VM caller, otherwise it will 7819 * deadlock. 7820 */ 7821 task->releaseVMCaller(); 7822 7823 rc = that->powerDown(); 7824 } 7825 7826 /* notify the progress object about operation completion */ 7827 if (SUCCEEDED (rc)) 7828 task->mProgress->notifyComplete (S_OK); 7829 else 7830 { 7831 if (!errMsg.isNull()) 7832 task->mProgress->notifyComplete (rc, 7833 COM_IIDOF(IConsole), Console::getComponentName(), errMsg); 7834 else 7835 task->mProgress->notifyComplete (rc); 7836 } 7837 7838 LogFlowFuncLeave(); 7839 return VINF_SUCCESS; 7840 } 7841 7842 /** 7843 * Thread for powering down the Console. 7844 * 7845 * @param Thread The thread handle. 7846 * @param pvUser Pointer to the VMTask structure. 7847 * @return VINF_SUCCESS (ignored). 7848 * 7849 * @note Locks the Console object for writing. 7850 */ 7851 /*static*/ 7852 DECLCALLBACK (int) Console::powerDownThread (RTTHREAD Thread, void *pvUser) 7853 { 7854 LogFlowFuncEnter(); 7855 7856 std::auto_ptr <VMTask> task (static_cast <VMTask *> (pvUser)); 7857 AssertReturn (task.get(), VERR_INVALID_PARAMETER); 7858 7859 AssertReturn (task->isOk(), VERR_GENERAL_FAILURE); 7860 7861 const ComObjPtr <Console> &that = task->mConsole; 7862 7863 /* 7864 * Note: no need to use addCaller() to protect Console 7865 * because VMTask does that 7866 */ 7867 7868 /* release VM caller to let powerDown() proceed */ 7869 task->releaseVMCaller(); 7870 7871 HRESULT rc = that->powerDown(); 7872 AssertComRC (rc); 7873 7874 LogFlowFuncLeave(); 7875 return VINF_SUCCESS; 7876 } 7877 7878 /** 7879 * The Main status driver instance data. 7880 */ 7881 typedef struct DRVMAINSTATUS 7882 { 7883 /** The LED connectors. */ 7884 PDMILEDCONNECTORS ILedConnectors; 7885 /** Pointer to the LED ports interface above us. */ 7886 PPDMILEDPORTS pLedPorts; 7887 /** Pointer to the array of LED pointers. */ 7888 PPDMLED *papLeds; 7889 /** The unit number corresponding to the first entry in the LED array. */ 7890 RTUINT iFirstLUN; 7891 /** The unit number corresponding to the last entry in the LED array. 7892 * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */ 7893 RTUINT iLastLUN; 7894 } DRVMAINSTATUS, *PDRVMAINSTATUS; 7895 7896 7897 /** 7898 * Notification about a unit which have been changed. 7899 * 7900 * The driver must discard any pointers to data owned by 7901 * the unit and requery it. 7902 * 7903 * @param pInterface Pointer to the interface structure containing the called function pointer. 7904 * @param iLUN The unit number. 7905 */ 7906 DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN) 7907 { 7908 PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface; 7909 if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN) 7910 { 7911 PPDMLED pLed; 7912 int rc = pData->pLedPorts->pfnQueryStatusLed(pData->pLedPorts, iLUN, &pLed); 7913 if (VBOX_FAILURE(rc)) 7914 pLed = NULL; 7915 ASMAtomicXchgPtr((void * volatile *)&pData->papLeds[iLUN - pData->iFirstLUN], pLed); 7916 Log(("drvStatus_UnitChanged: iLUN=%d pLed=%p\n", iLUN, pLed)); 7917 } 7918 } 7919 7920 7921 /** 7922 * Queries an interface to the driver. 7923 * 7924 * @returns Pointer to interface. 7925 * @returns NULL if the interface was not supported by the driver. 7926 * @param pInterface Pointer to this interface structure. 7927 * @param enmInterface The requested interface identification. 7928 */ 7929 DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, PDMINTERFACE enmInterface) 7930 { 7931 PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface); 7932 PDRVMAINSTATUS pDrv = PDMINS2DATA(pDrvIns, PDRVMAINSTATUS); 7933 switch (enmInterface) 7934 { 7935 case PDMINTERFACE_BASE: 7936 return &pDrvIns->IBase; 7937 case PDMINTERFACE_LED_CONNECTORS: 7938 return &pDrv->ILedConnectors; 7939 default: 7940 return NULL; 7941 } 7942 } 7943 7944 7945 /** 7946 * Destruct a status driver instance. 7947 * 7948 * @returns VBox status. 7949 * @param pDrvIns The driver instance data. 7950 */ 7951 DECLCALLBACK(void) Console::drvStatus_Destruct(PPDMDRVINS pDrvIns) 7952 { 7953 PDRVMAINSTATUS pData = PDMINS2DATA(pDrvIns, PDRVMAINSTATUS); 7954 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance)); 7955 if (pData->papLeds) 7956 { 7957 unsigned iLed = pData->iLastLUN - pData->iFirstLUN + 1; 7958 while (iLed-- > 0) 7959 ASMAtomicXchgPtr((void * volatile *)&pData->papLeds[iLed], NULL); 7960 } 7961 } 7962 7963 7964 /** 7965 * Construct a status driver instance. 7966 * 7967 * @returns VBox status. 7968 * @param pDrvIns The driver instance data. 7969 * If the registration structure is needed, pDrvIns->pDrvReg points to it. 7970 * @param pCfgHandle Configuration node handle for the driver. Use this to obtain the configuration 7971 * of the driver instance. It's also found in pDrvIns->pCfgHandle, but like 7972 * iInstance it's expected to be used a bit in this function. 7973 */ 7974 DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCfgHandle) 7975 { 7976 PDRVMAINSTATUS pData = PDMINS2DATA(pDrvIns, PDRVMAINSTATUS); 7977 LogFlowFunc(("iInstance=%d\n", pDrvIns->iInstance)); 7978 7979 /* 7980 * Validate configuration. 7981 */ 7982 if (!CFGMR3AreValuesValid(pCfgHandle, "papLeds\0First\0Last\0")) 7983 return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES; 7984 PPDMIBASE pBaseIgnore; 7985 int rc = pDrvIns->pDrvHlp->pfnAttach(pDrvIns, &pBaseIgnore); 7986 if (rc != VERR_PDM_NO_ATTACHED_DRIVER) 7987 { 7988 AssertMsgFailed(("Configuration error: Not possible to attach anything to this driver!\n")); 7989 return VERR_PDM_DRVINS_NO_ATTACH; 7990 } 7991 7992 /* 7993 * Data. 7994 */ 7995 pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface; 7996 pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged; 7997 7998 /* 7999 * Read config. 8000 */ 8001 rc = CFGMR3QueryPtr(pCfgHandle, "papLeds", (void **)&pData->papLeds); 8002 if (VBOX_FAILURE(rc)) 8003 { 8004 AssertMsgFailed(("Configuration error: Failed to query the \"papLeds\" value! rc=%Vrc\n", rc)); 8005 return rc; 8006 } 8007 8008 rc = CFGMR3QueryU32(pCfgHandle, "First", &pData->iFirstLUN); 8009 if (rc == VERR_CFGM_VALUE_NOT_FOUND) 8010 pData->iFirstLUN = 0; 8011 else if (VBOX_FAILURE(rc)) 8012 { 8013 AssertMsgFailed(("Configuration error: Failed to query the \"First\" value! rc=%Vrc\n", rc)); 8014 return rc; 8015 } 8016 8017 rc = CFGMR3QueryU32(pCfgHandle, "Last", &pData->iLastLUN); 8018 if (rc == VERR_CFGM_VALUE_NOT_FOUND) 8019 pData->iLastLUN = 0; 8020 else if (VBOX_FAILURE(rc)) 8021 { 8022 AssertMsgFailed(("Configuration error: Failed to query the \"Last\" value! rc=%Vrc\n", rc)); 8023 return rc; 8024 } 8025 if (pData->iFirstLUN > pData->iLastLUN) 8026 { 8027 AssertMsgFailed(("Configuration error: Invalid unit range %u-%u\n", pData->iFirstLUN, pData->iLastLUN)); 8028 return VERR_GENERAL_FAILURE; 8029 } 8030 8031 /* 8032 * Get the ILedPorts interface of the above driver/device and 8033 * query the LEDs we want. 8034 */ 8035 pData->pLedPorts = (PPDMILEDPORTS)pDrvIns->pUpBase->pfnQueryInterface(pDrvIns->pUpBase, PDMINTERFACE_LED_PORTS); 8036 if (!pData->pLedPorts) 8037 { 8038 AssertMsgFailed(("Configuration error: No led ports interface above!\n")); 8039 return VERR_PDM_MISSING_INTERFACE_ABOVE; 8040 } 8041 8042 for (unsigned i = pData->iFirstLUN; i <= pData->iLastLUN; i++) 8043 Console::drvStatus_UnitChanged(&pData->ILedConnectors, i); 8044 8045 return VINF_SUCCESS; 8046 } 8047 8048 8049 /** 8050 * Keyboard driver registration record. 8051 */ 8052 const PDMDRVREG Console::DrvStatusReg = 8053 { 8054 /* u32Version */ 8055 PDM_DRVREG_VERSION, 8056 /* szDriverName */ 8057 "MainStatus", 8058 /* pszDescription */ 8059 "Main status driver (Main as in the API).", 8060 /* fFlags */ 8061 PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT, 8062 /* fClass. */ 8063 PDM_DRVREG_CLASS_STATUS, 8064 /* cMaxInstances */ 8065 ~0, 8066 /* cbInstance */ 8067 sizeof(DRVMAINSTATUS), 8068 /* pfnConstruct */ 8069 Console::drvStatus_Construct, 8070 /* pfnDestruct */ 8071 Console::drvStatus_Destruct, 8072 /* pfnIOCtl */ 8073 NULL, 8074 /* pfnPowerOn */ 8075 NULL, 8076 /* pfnReset */ 8077 NULL, 8078 /* pfnSuspend */ 8079 NULL, 8080 /* pfnResume */ 8081 NULL, 8082 /* pfnDetach */ 8083 NULL 8084 }; 8085 1428 -
trunk/src/VBox/Main/Makefile.kmk
r4338 r4426 6 6 # 7 7 # Copyright (C) 2006-2007 innotek GmbH 8 # 8 # 9 9 # This file is part of VirtualBox Open Source Edition (OSE), as 10 10 # available from http://www.virtualbox.org. This file is free software; … … 314 314 SessionImpl.cpp \ 315 315 ConsoleImpl.cpp \ 316 ConsoleImpl2.cpp \ 316 317 ConsoleVRDPServer.cpp \ 317 318 GuestImpl.cpp \
Note:
See TracChangeset
for help on using the changeset viewer.