VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/MachineImpl.cpp@ 42129

Last change on this file since 42129 was 42129, checked in by vboxsync, 13 years ago

Main/VirtualBox+Machine: add support for VM groups (incomplete, saving of settings is not implemented), remaining code adjusted accordingly, use better parameter checking macros, make XSLT generators process setter attributes using safearrays correctly, various cleanups and warning fixes
Frontends/VBoxManage: first traces of groups support

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 449.9 KB
Line 
1/* $Id: MachineImpl.cpp 42129 2012-07-12 17:32:31Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPid = NIL_RTPROCESS;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mHWVirtExEnabled = true;
169 mHWVirtExNestedPagingEnabled = true;
170#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
171 mHWVirtExLargePagesEnabled = true;
172#else
173 /* Not supported on 32 bits hosts. */
174 mHWVirtExLargePagesEnabled = false;
175#endif
176 mHWVirtExVPIDEnabled = true;
177 mHWVirtExForceEnabled = false;
178#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
179 mHWVirtExExclusive = false;
180#else
181 mHWVirtExExclusive = true;
182#endif
183#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
184 mPAEEnabled = true;
185#else
186 mPAEEnabled = false;
187#endif
188 mSyntheticCpu = false;
189 mHpetEnabled = false;
190
191 /* default boot order: floppy - DVD - HDD */
192 mBootOrder[0] = DeviceType_Floppy;
193 mBootOrder[1] = DeviceType_DVD;
194 mBootOrder[2] = DeviceType_HardDisk;
195 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
196 mBootOrder[i] = DeviceType_Null;
197
198 mClipboardMode = ClipboardMode_Disabled;
199 mGuestPropertyNotificationPatterns = "";
200
201 mFirmwareType = FirmwareType_BIOS;
202 mKeyboardHidType = KeyboardHidType_PS2Keyboard;
203 mPointingHidType = PointingHidType_PS2Mouse;
204 mChipsetType = ChipsetType_PIIX3;
205 mEmulatedUSBCardReaderEnabled = FALSE;
206
207 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
208 mCPUAttached[i] = false;
209
210 mIoCacheEnabled = true;
211 mIoCacheSize = 5; /* 5MB */
212
213 /* Maximum CPU execution cap by default. */
214 mCpuExecutionCap = 100;
215}
216
217Machine::HWData::~HWData()
218{
219}
220
221/////////////////////////////////////////////////////////////////////////////
222// Machine::HDData structure
223/////////////////////////////////////////////////////////////////////////////
224
225Machine::MediaData::MediaData()
226{
227}
228
229Machine::MediaData::~MediaData()
230{
231}
232
233/////////////////////////////////////////////////////////////////////////////
234// Machine class
235/////////////////////////////////////////////////////////////////////////////
236
237// constructor / destructor
238/////////////////////////////////////////////////////////////////////////////
239
240Machine::Machine()
241 : mCollectorGuest(NULL),
242 mPeer(NULL),
243 mParent(NULL),
244 mSerialPorts(),
245 mParallelPorts(),
246 uRegistryNeedsSaving(0)
247{}
248
249Machine::~Machine()
250{}
251
252HRESULT Machine::FinalConstruct()
253{
254 LogFlowThisFunc(("\n"));
255 return BaseFinalConstruct();
256}
257
258void Machine::FinalRelease()
259{
260 LogFlowThisFunc(("\n"));
261 uninit();
262 BaseFinalRelease();
263}
264
265/**
266 * Initializes a new machine instance; this init() variant creates a new, empty machine.
267 * This gets called from VirtualBox::CreateMachine().
268 *
269 * @param aParent Associated parent object
270 * @param strConfigFile Local file system path to the VM settings file (can
271 * be relative to the VirtualBox config directory).
272 * @param strName name for the machine
273 * @param llGroups list of groups for the machine
274 * @param aOsType OS Type of this machine or NULL.
275 * @param aId UUID for the new machine.
276 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
277 *
278 * @return Success indicator. if not S_OK, the machine object is invalid
279 */
280HRESULT Machine::init(VirtualBox *aParent,
281 const Utf8Str &strConfigFile,
282 const Utf8Str &strName,
283 const StringsList &llGroups,
284 GuestOSType *aOsType,
285 const Guid &aId,
286 bool fForceOverwrite)
287{
288 LogFlowThisFuncEnter();
289 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
290
291 /* Enclose the state transition NotReady->InInit->Ready */
292 AutoInitSpan autoInitSpan(this);
293 AssertReturn(autoInitSpan.isOk(), E_FAIL);
294
295 HRESULT rc = initImpl(aParent, strConfigFile);
296 if (FAILED(rc)) return rc;
297
298 rc = tryCreateMachineConfigFile(fForceOverwrite);
299 if (FAILED(rc)) return rc;
300
301 if (SUCCEEDED(rc))
302 {
303 // create an empty machine config
304 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
305
306 rc = initDataAndChildObjects();
307 }
308
309 if (SUCCEEDED(rc))
310 {
311 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
312 mData->mAccessible = TRUE;
313
314 unconst(mData->mUuid) = aId;
315
316 mUserData->s.strName = strName;
317
318 mUserData->s.llGroups = llGroups;
319
320 // the "name sync" flag determines whether the machine directory gets renamed along
321 // with the machine file; say so if the settings file name is the same as the
322 // settings file parent directory (machine directory)
323 mUserData->s.fNameSync = isInOwnDir();
324
325 // initialize the default snapshots folder
326 rc = COMSETTER(SnapshotFolder)(NULL);
327 AssertComRC(rc);
328
329 if (aOsType)
330 {
331 /* Store OS type */
332 mUserData->s.strOsType = aOsType->id();
333
334 /* Apply BIOS defaults */
335 mBIOSSettings->applyDefaults(aOsType);
336
337 /* Apply network adapters defaults */
338 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
339 mNetworkAdapters[slot]->applyDefaults(aOsType);
340
341 /* Apply serial port defaults */
342 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
343 mSerialPorts[slot]->applyDefaults(aOsType);
344 }
345
346 /* At this point the changing of the current state modification
347 * flag is allowed. */
348 allowStateModification();
349
350 /* commit all changes made during the initialization */
351 commit();
352 }
353
354 /* Confirm a successful initialization when it's the case */
355 if (SUCCEEDED(rc))
356 {
357 if (mData->mAccessible)
358 autoInitSpan.setSucceeded();
359 else
360 autoInitSpan.setLimited();
361 }
362
363 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
364 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
365 mData->mRegistered,
366 mData->mAccessible,
367 rc));
368
369 LogFlowThisFuncLeave();
370
371 return rc;
372}
373
374/**
375 * Initializes a new instance with data from machine XML (formerly Init_Registered).
376 * Gets called in two modes:
377 *
378 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
379 * UUID is specified and we mark the machine as "registered";
380 *
381 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
382 * and the machine remains unregistered until RegisterMachine() is called.
383 *
384 * @param aParent Associated parent object
385 * @param aConfigFile Local file system path to the VM settings file (can
386 * be relative to the VirtualBox config directory).
387 * @param aId UUID of the machine or NULL (see above).
388 *
389 * @return Success indicator. if not S_OK, the machine object is invalid
390 */
391HRESULT Machine::init(VirtualBox *aParent,
392 const Utf8Str &strConfigFile,
393 const Guid *aId)
394{
395 LogFlowThisFuncEnter();
396 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
397
398 /* Enclose the state transition NotReady->InInit->Ready */
399 AutoInitSpan autoInitSpan(this);
400 AssertReturn(autoInitSpan.isOk(), E_FAIL);
401
402 HRESULT rc = initImpl(aParent, strConfigFile);
403 if (FAILED(rc)) return rc;
404
405 if (aId)
406 {
407 // loading a registered VM:
408 unconst(mData->mUuid) = *aId;
409 mData->mRegistered = TRUE;
410 // now load the settings from XML:
411 rc = registeredInit();
412 // this calls initDataAndChildObjects() and loadSettings()
413 }
414 else
415 {
416 // opening an unregistered VM (VirtualBox::OpenMachine()):
417 rc = initDataAndChildObjects();
418
419 if (SUCCEEDED(rc))
420 {
421 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
422 mData->mAccessible = TRUE;
423
424 try
425 {
426 // load and parse machine XML; this will throw on XML or logic errors
427 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
428
429 // reject VM UUID duplicates, they can happen if someone
430 // tries to register an already known VM config again
431 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
432 true /* fPermitInaccessible */,
433 false /* aDoSetError */,
434 NULL) != VBOX_E_OBJECT_NOT_FOUND)
435 {
436 throw setError(E_FAIL,
437 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
438 mData->m_strConfigFile.c_str());
439 }
440
441 // use UUID from machine config
442 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
443
444 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
445 NULL /* puuidRegistry */);
446 if (FAILED(rc)) throw rc;
447
448 /* At this point the changing of the current state modification
449 * flag is allowed. */
450 allowStateModification();
451
452 commit();
453 }
454 catch (HRESULT err)
455 {
456 /* we assume that error info is set by the thrower */
457 rc = err;
458 }
459 catch (...)
460 {
461 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
462 }
463 }
464 }
465
466 /* Confirm a successful initialization when it's the case */
467 if (SUCCEEDED(rc))
468 {
469 if (mData->mAccessible)
470 autoInitSpan.setSucceeded();
471 else
472 {
473 autoInitSpan.setLimited();
474
475 // uninit media from this machine's media registry, or else
476 // reloading the settings will fail
477 mParent->unregisterMachineMedia(getId());
478 }
479 }
480
481 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
482 "rc=%08X\n",
483 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
484 mData->mRegistered, mData->mAccessible, rc));
485
486 LogFlowThisFuncLeave();
487
488 return rc;
489}
490
491/**
492 * Initializes a new instance from a machine config that is already in memory
493 * (import OVF case). Since we are importing, the UUID in the machine
494 * config is ignored and we always generate a fresh one.
495 *
496 * @param strName Name for the new machine; this overrides what is specified in config and is used
497 * for the settings file as well.
498 * @param config Machine configuration loaded and parsed from XML.
499 *
500 * @return Success indicator. if not S_OK, the machine object is invalid
501 */
502HRESULT Machine::init(VirtualBox *aParent,
503 const Utf8Str &strName,
504 const settings::MachineConfigFile &config)
505{
506 LogFlowThisFuncEnter();
507
508 /* Enclose the state transition NotReady->InInit->Ready */
509 AutoInitSpan autoInitSpan(this);
510 AssertReturn(autoInitSpan.isOk(), E_FAIL);
511
512 Utf8Str strConfigFile;
513 aParent->getDefaultMachineFolder(strConfigFile);
514 strConfigFile.append(RTPATH_DELIMITER);
515 strConfigFile.append(strName);
516 strConfigFile.append(RTPATH_DELIMITER);
517 strConfigFile.append(strName);
518 strConfigFile.append(".vbox");
519
520 HRESULT rc = initImpl(aParent, strConfigFile);
521 if (FAILED(rc)) return rc;
522
523 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
524 if (FAILED(rc)) return rc;
525
526 rc = initDataAndChildObjects();
527
528 if (SUCCEEDED(rc))
529 {
530 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
531 mData->mAccessible = TRUE;
532
533 // create empty machine config for instance data
534 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
535
536 // generate fresh UUID, ignore machine config
537 unconst(mData->mUuid).create();
538
539 rc = loadMachineDataFromSettings(config,
540 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
541
542 // override VM name as well, it may be different
543 mUserData->s.strName = strName;
544
545 if (SUCCEEDED(rc))
546 {
547 /* At this point the changing of the current state modification
548 * flag is allowed. */
549 allowStateModification();
550
551 /* commit all changes made during the initialization */
552 commit();
553 }
554 }
555
556 /* Confirm a successful initialization when it's the case */
557 if (SUCCEEDED(rc))
558 {
559 if (mData->mAccessible)
560 autoInitSpan.setSucceeded();
561 else
562 {
563 autoInitSpan.setLimited();
564
565 // uninit media from this machine's media registry, or else
566 // reloading the settings will fail
567 mParent->unregisterMachineMedia(getId());
568 }
569 }
570
571 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
572 "rc=%08X\n",
573 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
574 mData->mRegistered, mData->mAccessible, rc));
575
576 LogFlowThisFuncLeave();
577
578 return rc;
579}
580
581/**
582 * Shared code between the various init() implementations.
583 * @param aParent
584 * @return
585 */
586HRESULT Machine::initImpl(VirtualBox *aParent,
587 const Utf8Str &strConfigFile)
588{
589 LogFlowThisFuncEnter();
590
591 AssertReturn(aParent, E_INVALIDARG);
592 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
593
594 HRESULT rc = S_OK;
595
596 /* share the parent weakly */
597 unconst(mParent) = aParent;
598
599 /* allocate the essential machine data structure (the rest will be
600 * allocated later by initDataAndChildObjects() */
601 mData.allocate();
602
603 /* memorize the config file name (as provided) */
604 mData->m_strConfigFile = strConfigFile;
605
606 /* get the full file name */
607 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
608 if (RT_FAILURE(vrc1))
609 return setError(VBOX_E_FILE_ERROR,
610 tr("Invalid machine settings file name '%s' (%Rrc)"),
611 strConfigFile.c_str(),
612 vrc1);
613
614 LogFlowThisFuncLeave();
615
616 return rc;
617}
618
619/**
620 * Tries to create a machine settings file in the path stored in the machine
621 * instance data. Used when a new machine is created to fail gracefully if
622 * the settings file could not be written (e.g. because machine dir is read-only).
623 * @return
624 */
625HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
626{
627 HRESULT rc = S_OK;
628
629 // when we create a new machine, we must be able to create the settings file
630 RTFILE f = NIL_RTFILE;
631 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
632 if ( RT_SUCCESS(vrc)
633 || vrc == VERR_SHARING_VIOLATION
634 )
635 {
636 if (RT_SUCCESS(vrc))
637 RTFileClose(f);
638 if (!fForceOverwrite)
639 rc = setError(VBOX_E_FILE_ERROR,
640 tr("Machine settings file '%s' already exists"),
641 mData->m_strConfigFileFull.c_str());
642 else
643 {
644 /* try to delete the config file, as otherwise the creation
645 * of a new settings file will fail. */
646 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
647 if (RT_FAILURE(vrc2))
648 rc = setError(VBOX_E_FILE_ERROR,
649 tr("Could not delete the existing settings file '%s' (%Rrc)"),
650 mData->m_strConfigFileFull.c_str(), vrc2);
651 }
652 }
653 else if ( vrc != VERR_FILE_NOT_FOUND
654 && vrc != VERR_PATH_NOT_FOUND
655 )
656 rc = setError(VBOX_E_FILE_ERROR,
657 tr("Invalid machine settings file name '%s' (%Rrc)"),
658 mData->m_strConfigFileFull.c_str(),
659 vrc);
660 return rc;
661}
662
663/**
664 * Initializes the registered machine by loading the settings file.
665 * This method is separated from #init() in order to make it possible to
666 * retry the operation after VirtualBox startup instead of refusing to
667 * startup the whole VirtualBox server in case if the settings file of some
668 * registered VM is invalid or inaccessible.
669 *
670 * @note Must be always called from this object's write lock
671 * (unless called from #init() that doesn't need any locking).
672 * @note Locks the mUSBController method for writing.
673 * @note Subclasses must not call this method.
674 */
675HRESULT Machine::registeredInit()
676{
677 AssertReturn(!isSessionMachine(), E_FAIL);
678 AssertReturn(!isSnapshotMachine(), E_FAIL);
679 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
680 AssertReturn(!mData->mAccessible, E_FAIL);
681
682 HRESULT rc = initDataAndChildObjects();
683
684 if (SUCCEEDED(rc))
685 {
686 /* Temporarily reset the registered flag in order to let setters
687 * potentially called from loadSettings() succeed (isMutable() used in
688 * all setters will return FALSE for a Machine instance if mRegistered
689 * is TRUE). */
690 mData->mRegistered = FALSE;
691
692 try
693 {
694 // load and parse machine XML; this will throw on XML or logic errors
695 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
696
697 if (mData->mUuid != mData->pMachineConfigFile->uuid)
698 throw setError(E_FAIL,
699 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
700 mData->pMachineConfigFile->uuid.raw(),
701 mData->m_strConfigFileFull.c_str(),
702 mData->mUuid.toString().c_str(),
703 mParent->settingsFilePath().c_str());
704
705 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
706 NULL /* const Guid *puuidRegistry */);
707 if (FAILED(rc)) throw rc;
708 }
709 catch (HRESULT err)
710 {
711 /* we assume that error info is set by the thrower */
712 rc = err;
713 }
714 catch (...)
715 {
716 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
717 }
718
719 /* Restore the registered flag (even on failure) */
720 mData->mRegistered = TRUE;
721 }
722
723 if (SUCCEEDED(rc))
724 {
725 /* Set mAccessible to TRUE only if we successfully locked and loaded
726 * the settings file */
727 mData->mAccessible = TRUE;
728
729 /* commit all changes made during loading the settings file */
730 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
731 /// @todo r=klaus for some reason the settings loading logic backs up
732 // the settings, and therefore a commit is needed. Should probably be changed.
733 }
734 else
735 {
736 /* If the machine is registered, then, instead of returning a
737 * failure, we mark it as inaccessible and set the result to
738 * success to give it a try later */
739
740 /* fetch the current error info */
741 mData->mAccessError = com::ErrorInfo();
742 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
743 mData->mUuid.raw(),
744 mData->mAccessError.getText().raw()));
745
746 /* rollback all changes */
747 rollback(false /* aNotify */);
748
749 // uninit media from this machine's media registry, or else
750 // reloading the settings will fail
751 mParent->unregisterMachineMedia(getId());
752
753 /* uninitialize the common part to make sure all data is reset to
754 * default (null) values */
755 uninitDataAndChildObjects();
756
757 rc = S_OK;
758 }
759
760 return rc;
761}
762
763/**
764 * Uninitializes the instance.
765 * Called either from FinalRelease() or by the parent when it gets destroyed.
766 *
767 * @note The caller of this method must make sure that this object
768 * a) doesn't have active callers on the current thread and b) is not locked
769 * by the current thread; otherwise uninit() will hang either a) due to
770 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
771 * a dead-lock caused by this thread waiting for all callers on the other
772 * threads are done but preventing them from doing so by holding a lock.
773 */
774void Machine::uninit()
775{
776 LogFlowThisFuncEnter();
777
778 Assert(!isWriteLockOnCurrentThread());
779
780 Assert(!uRegistryNeedsSaving);
781 if (uRegistryNeedsSaving)
782 {
783 AutoCaller autoCaller(this);
784 if (SUCCEEDED(autoCaller.rc()))
785 {
786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
787 saveSettings(NULL, Machine::SaveS_Force);
788 }
789 }
790
791 /* Enclose the state transition Ready->InUninit->NotReady */
792 AutoUninitSpan autoUninitSpan(this);
793 if (autoUninitSpan.uninitDone())
794 return;
795
796 Assert(!isSnapshotMachine());
797 Assert(!isSessionMachine());
798 Assert(!!mData);
799
800 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
801 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
802
803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
804
805 if (!mData->mSession.mMachine.isNull())
806 {
807 /* Theoretically, this can only happen if the VirtualBox server has been
808 * terminated while there were clients running that owned open direct
809 * sessions. Since in this case we are definitely called by
810 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
811 * won't happen on the client watcher thread (because it does
812 * VirtualBox::addCaller() for the duration of the
813 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
814 * cannot happen until the VirtualBox caller is released). This is
815 * important, because SessionMachine::uninit() cannot correctly operate
816 * after we return from this method (it expects the Machine instance is
817 * still valid). We'll call it ourselves below.
818 */
819 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
820 (SessionMachine*)mData->mSession.mMachine));
821
822 if (Global::IsOnlineOrTransient(mData->mMachineState))
823 {
824 LogWarningThisFunc(("Setting state to Aborted!\n"));
825 /* set machine state using SessionMachine reimplementation */
826 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
827 }
828
829 /*
830 * Uninitialize SessionMachine using public uninit() to indicate
831 * an unexpected uninitialization.
832 */
833 mData->mSession.mMachine->uninit();
834 /* SessionMachine::uninit() must set mSession.mMachine to null */
835 Assert(mData->mSession.mMachine.isNull());
836 }
837
838 // uninit media from this machine's media registry, if they're still there
839 Guid uuidMachine(getId());
840
841 /* XXX This will fail with
842 * "cannot be closed because it is still attached to 1 virtual machines"
843 * because at this point we did not call uninitDataAndChildObjects() yet
844 * and therefore also removeBackReference() for all these mediums was not called! */
845 if (!uuidMachine.isEmpty()) // can be empty if we're called from a failure of Machine::init
846 mParent->unregisterMachineMedia(uuidMachine);
847
848 /* the lock is no more necessary (SessionMachine is uninitialized) */
849 alock.release();
850
851 // has machine been modified?
852 if (mData->flModifications)
853 {
854 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
855 rollback(false /* aNotify */);
856 }
857
858 if (mData->mAccessible)
859 uninitDataAndChildObjects();
860
861 /* free the essential data structure last */
862 mData.free();
863
864 LogFlowThisFuncLeave();
865}
866
867// IMachine properties
868/////////////////////////////////////////////////////////////////////////////
869
870STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
871{
872 CheckComArgOutPointerValid(aParent);
873
874 AutoLimitedCaller autoCaller(this);
875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
876
877 /* mParent is constant during life time, no need to lock */
878 ComObjPtr<VirtualBox> pVirtualBox(mParent);
879 pVirtualBox.queryInterfaceTo(aParent);
880
881 return S_OK;
882}
883
884STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
885{
886 CheckComArgOutPointerValid(aAccessible);
887
888 AutoLimitedCaller autoCaller(this);
889 if (FAILED(autoCaller.rc())) return autoCaller.rc();
890
891 LogFlowThisFunc(("ENTER\n"));
892
893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
894
895 HRESULT rc = S_OK;
896
897 if (!mData->mAccessible)
898 {
899 /* try to initialize the VM once more if not accessible */
900
901 AutoReinitSpan autoReinitSpan(this);
902 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
903
904#ifdef DEBUG
905 LogFlowThisFunc(("Dumping media backreferences\n"));
906 mParent->dumpAllBackRefs();
907#endif
908
909 if (mData->pMachineConfigFile)
910 {
911 // reset the XML file to force loadSettings() (called from registeredInit())
912 // to parse it again; the file might have changed
913 delete mData->pMachineConfigFile;
914 mData->pMachineConfigFile = NULL;
915 }
916
917 rc = registeredInit();
918
919 if (SUCCEEDED(rc) && mData->mAccessible)
920 {
921 autoReinitSpan.setSucceeded();
922
923 /* make sure interesting parties will notice the accessibility
924 * state change */
925 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
926 mParent->onMachineDataChange(mData->mUuid);
927 }
928 }
929
930 if (SUCCEEDED(rc))
931 *aAccessible = mData->mAccessible;
932
933 LogFlowThisFuncLeave();
934
935 return rc;
936}
937
938STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
939{
940 CheckComArgOutPointerValid(aAccessError);
941
942 AutoLimitedCaller autoCaller(this);
943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
944
945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
946
947 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
948 {
949 /* return shortly */
950 aAccessError = NULL;
951 return S_OK;
952 }
953
954 HRESULT rc = S_OK;
955
956 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
957 rc = errorInfo.createObject();
958 if (SUCCEEDED(rc))
959 {
960 errorInfo->init(mData->mAccessError.getResultCode(),
961 mData->mAccessError.getInterfaceID().ref(),
962 Utf8Str(mData->mAccessError.getComponent()).c_str(),
963 Utf8Str(mData->mAccessError.getText()));
964 rc = errorInfo.queryInterfaceTo(aAccessError);
965 }
966
967 return rc;
968}
969
970STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
971{
972 CheckComArgOutPointerValid(aName);
973
974 AutoCaller autoCaller(this);
975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
976
977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
978
979 mUserData->s.strName.cloneTo(aName);
980
981 return S_OK;
982}
983
984STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
985{
986 CheckComArgStrNotEmptyOrNull(aName);
987
988 AutoCaller autoCaller(this);
989 if (FAILED(autoCaller.rc())) return autoCaller.rc();
990
991 // prohibit setting a UUID only as the machine name, or else it can
992 // never be found by findMachine()
993 Guid test(aName);
994 if (test.isNotEmpty())
995 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
996
997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
998
999 HRESULT rc = checkStateDependency(MutableStateDep);
1000 if (FAILED(rc)) return rc;
1001
1002 setModified(IsModified_MachineData);
1003 mUserData.backup();
1004 mUserData->s.strName = aName;
1005
1006 return S_OK;
1007}
1008
1009STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1010{
1011 CheckComArgOutPointerValid(aDescription);
1012
1013 AutoCaller autoCaller(this);
1014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1015
1016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1017
1018 mUserData->s.strDescription.cloneTo(aDescription);
1019
1020 return S_OK;
1021}
1022
1023STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1024{
1025 AutoCaller autoCaller(this);
1026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1027
1028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1029
1030 HRESULT rc = checkStateDependency(MutableStateDep);
1031 if (FAILED(rc)) return rc;
1032
1033 setModified(IsModified_MachineData);
1034 mUserData.backup();
1035 mUserData->s.strDescription = aDescription;
1036
1037 return S_OK;
1038}
1039
1040STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1041{
1042 CheckComArgOutPointerValid(aId);
1043
1044 AutoLimitedCaller autoCaller(this);
1045 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1046
1047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1048
1049 mData->mUuid.toUtf16().cloneTo(aId);
1050
1051 return S_OK;
1052}
1053
1054STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1055{
1056 CheckComArgOutSafeArrayPointerValid(aGroups);
1057
1058 AutoCaller autoCaller(this);
1059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1060
1061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1062 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1063 size_t i = 0;
1064 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1065 it != mUserData->s.llGroups.end();
1066 ++it, i++)
1067 {
1068 Bstr tmp = *it;
1069 tmp.cloneTo(&groups[i]);
1070 }
1071 groups.detachTo(ComSafeArrayOutArg(aGroups));
1072
1073 return S_OK;
1074}
1075
1076STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1077{
1078 AutoCaller autoCaller(this);
1079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1080
1081 StringsList llGroups;
1082 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1083 if (FAILED(rc))
1084 return rc;
1085
1086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1087
1088 rc = checkStateDependency(MutableStateDep);
1089 if (FAILED(rc)) return rc;
1090
1091 setModified(IsModified_MachineData);
1092 mUserData.backup();
1093 mUserData->s.llGroups = llGroups;
1094
1095 return S_OK;
1096}
1097
1098STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1099{
1100 CheckComArgOutPointerValid(aOSTypeId);
1101
1102 AutoCaller autoCaller(this);
1103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1104
1105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1106
1107 mUserData->s.strOsType.cloneTo(aOSTypeId);
1108
1109 return S_OK;
1110}
1111
1112STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1113{
1114 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1115
1116 AutoCaller autoCaller(this);
1117 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1118
1119 /* look up the object by Id to check it is valid */
1120 ComPtr<IGuestOSType> guestOSType;
1121 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1122 if (FAILED(rc)) return rc;
1123
1124 /* when setting, always use the "etalon" value for consistency -- lookup
1125 * by ID is case-insensitive and the input value may have different case */
1126 Bstr osTypeId;
1127 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1128 if (FAILED(rc)) return rc;
1129
1130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1131
1132 rc = checkStateDependency(MutableStateDep);
1133 if (FAILED(rc)) return rc;
1134
1135 setModified(IsModified_MachineData);
1136 mUserData.backup();
1137 mUserData->s.strOsType = osTypeId;
1138
1139 return S_OK;
1140}
1141
1142
1143STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1144{
1145 CheckComArgOutPointerValid(aFirmwareType);
1146
1147 AutoCaller autoCaller(this);
1148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1149
1150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1151
1152 *aFirmwareType = mHWData->mFirmwareType;
1153
1154 return S_OK;
1155}
1156
1157STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1158{
1159 AutoCaller autoCaller(this);
1160 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1162
1163 HRESULT rc = checkStateDependency(MutableStateDep);
1164 if (FAILED(rc)) return rc;
1165
1166 setModified(IsModified_MachineData);
1167 mHWData.backup();
1168 mHWData->mFirmwareType = aFirmwareType;
1169
1170 return S_OK;
1171}
1172
1173STDMETHODIMP Machine::COMGETTER(KeyboardHidType)(KeyboardHidType_T *aKeyboardHidType)
1174{
1175 CheckComArgOutPointerValid(aKeyboardHidType);
1176
1177 AutoCaller autoCaller(this);
1178 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1179
1180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1181
1182 *aKeyboardHidType = mHWData->mKeyboardHidType;
1183
1184 return S_OK;
1185}
1186
1187STDMETHODIMP Machine::COMSETTER(KeyboardHidType)(KeyboardHidType_T aKeyboardHidType)
1188{
1189 AutoCaller autoCaller(this);
1190 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1192
1193 HRESULT rc = checkStateDependency(MutableStateDep);
1194 if (FAILED(rc)) return rc;
1195
1196 setModified(IsModified_MachineData);
1197 mHWData.backup();
1198 mHWData->mKeyboardHidType = aKeyboardHidType;
1199
1200 return S_OK;
1201}
1202
1203STDMETHODIMP Machine::COMGETTER(PointingHidType)(PointingHidType_T *aPointingHidType)
1204{
1205 CheckComArgOutPointerValid(aPointingHidType);
1206
1207 AutoCaller autoCaller(this);
1208 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1209
1210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1211
1212 *aPointingHidType = mHWData->mPointingHidType;
1213
1214 return S_OK;
1215}
1216
1217STDMETHODIMP Machine::COMSETTER(PointingHidType)(PointingHidType_T aPointingHidType)
1218{
1219 AutoCaller autoCaller(this);
1220 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1222
1223 HRESULT rc = checkStateDependency(MutableStateDep);
1224 if (FAILED(rc)) return rc;
1225
1226 setModified(IsModified_MachineData);
1227 mHWData.backup();
1228 mHWData->mPointingHidType = aPointingHidType;
1229
1230 return S_OK;
1231}
1232
1233STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1234{
1235 CheckComArgOutPointerValid(aChipsetType);
1236
1237 AutoCaller autoCaller(this);
1238 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1239
1240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1241
1242 *aChipsetType = mHWData->mChipsetType;
1243
1244 return S_OK;
1245}
1246
1247STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1248{
1249 AutoCaller autoCaller(this);
1250 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1252
1253 HRESULT rc = checkStateDependency(MutableStateDep);
1254 if (FAILED(rc)) return rc;
1255
1256 if (aChipsetType != mHWData->mChipsetType)
1257 {
1258 setModified(IsModified_MachineData);
1259 mHWData.backup();
1260 mHWData->mChipsetType = aChipsetType;
1261
1262 // Resize network adapter array, to be finalized on commit/rollback.
1263 // We must not throw away entries yet, otherwise settings are lost
1264 // without a way to roll back.
1265 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1266 uint32_t oldCount = mNetworkAdapters.size();
1267 if (newCount > oldCount)
1268 {
1269 mNetworkAdapters.resize(newCount);
1270 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1271 {
1272 unconst(mNetworkAdapters[slot]).createObject();
1273 mNetworkAdapters[slot]->init(this, slot);
1274 }
1275 }
1276 }
1277
1278 return S_OK;
1279}
1280
1281STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1282{
1283 CheckComArgOutPointerValid(aHWVersion);
1284
1285 AutoCaller autoCaller(this);
1286 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1287
1288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1289
1290 mHWData->mHWVersion.cloneTo(aHWVersion);
1291
1292 return S_OK;
1293}
1294
1295STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1296{
1297 /* check known version */
1298 Utf8Str hwVersion = aHWVersion;
1299 if ( hwVersion.compare("1") != 0
1300 && hwVersion.compare("2") != 0)
1301 return setError(E_INVALIDARG,
1302 tr("Invalid hardware version: %ls\n"), aHWVersion);
1303
1304 AutoCaller autoCaller(this);
1305 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1306
1307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1308
1309 HRESULT rc = checkStateDependency(MutableStateDep);
1310 if (FAILED(rc)) return rc;
1311
1312 setModified(IsModified_MachineData);
1313 mHWData.backup();
1314 mHWData->mHWVersion = hwVersion;
1315
1316 return S_OK;
1317}
1318
1319STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1320{
1321 CheckComArgOutPointerValid(aUUID);
1322
1323 AutoCaller autoCaller(this);
1324 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1325
1326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1327
1328 if (!mHWData->mHardwareUUID.isEmpty())
1329 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1330 else
1331 mData->mUuid.toUtf16().cloneTo(aUUID);
1332
1333 return S_OK;
1334}
1335
1336STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1337{
1338 Guid hardwareUUID(aUUID);
1339 if (hardwareUUID.isEmpty())
1340 return E_INVALIDARG;
1341
1342 AutoCaller autoCaller(this);
1343 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1344
1345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1346
1347 HRESULT rc = checkStateDependency(MutableStateDep);
1348 if (FAILED(rc)) return rc;
1349
1350 setModified(IsModified_MachineData);
1351 mHWData.backup();
1352 if (hardwareUUID == mData->mUuid)
1353 mHWData->mHardwareUUID.clear();
1354 else
1355 mHWData->mHardwareUUID = hardwareUUID;
1356
1357 return S_OK;
1358}
1359
1360STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1361{
1362 CheckComArgOutPointerValid(memorySize);
1363
1364 AutoCaller autoCaller(this);
1365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1366
1367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1368
1369 *memorySize = mHWData->mMemorySize;
1370
1371 return S_OK;
1372}
1373
1374STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1375{
1376 /* check RAM limits */
1377 if ( memorySize < MM_RAM_MIN_IN_MB
1378 || memorySize > MM_RAM_MAX_IN_MB
1379 )
1380 return setError(E_INVALIDARG,
1381 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1382 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1383
1384 AutoCaller autoCaller(this);
1385 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1386
1387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1388
1389 HRESULT rc = checkStateDependency(MutableStateDep);
1390 if (FAILED(rc)) return rc;
1391
1392 setModified(IsModified_MachineData);
1393 mHWData.backup();
1394 mHWData->mMemorySize = memorySize;
1395
1396 return S_OK;
1397}
1398
1399STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1400{
1401 CheckComArgOutPointerValid(CPUCount);
1402
1403 AutoCaller autoCaller(this);
1404 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1405
1406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1407
1408 *CPUCount = mHWData->mCPUCount;
1409
1410 return S_OK;
1411}
1412
1413STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1414{
1415 /* check CPU limits */
1416 if ( CPUCount < SchemaDefs::MinCPUCount
1417 || CPUCount > SchemaDefs::MaxCPUCount
1418 )
1419 return setError(E_INVALIDARG,
1420 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1421 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1422
1423 AutoCaller autoCaller(this);
1424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1425
1426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1427
1428 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1429 if (mHWData->mCPUHotPlugEnabled)
1430 {
1431 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1432 {
1433 if (mHWData->mCPUAttached[idx])
1434 return setError(E_INVALIDARG,
1435 tr("There is still a CPU attached to socket %lu."
1436 "Detach the CPU before removing the socket"),
1437 CPUCount, idx+1);
1438 }
1439 }
1440
1441 HRESULT rc = checkStateDependency(MutableStateDep);
1442 if (FAILED(rc)) return rc;
1443
1444 setModified(IsModified_MachineData);
1445 mHWData.backup();
1446 mHWData->mCPUCount = CPUCount;
1447
1448 return S_OK;
1449}
1450
1451STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1452{
1453 CheckComArgOutPointerValid(aExecutionCap);
1454
1455 AutoCaller autoCaller(this);
1456 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1457
1458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1459
1460 *aExecutionCap = mHWData->mCpuExecutionCap;
1461
1462 return S_OK;
1463}
1464
1465STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1466{
1467 HRESULT rc = S_OK;
1468
1469 /* check throttle limits */
1470 if ( aExecutionCap < 1
1471 || aExecutionCap > 100
1472 )
1473 return setError(E_INVALIDARG,
1474 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1475 aExecutionCap, 1, 100);
1476
1477 AutoCaller autoCaller(this);
1478 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1479
1480 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1481
1482 alock.release();
1483 rc = onCPUExecutionCapChange(aExecutionCap);
1484 alock.acquire();
1485 if (FAILED(rc)) return rc;
1486
1487 setModified(IsModified_MachineData);
1488 mHWData.backup();
1489 mHWData->mCpuExecutionCap = aExecutionCap;
1490
1491 /* Save settings if online - todo why is this required?? */
1492 if (Global::IsOnline(mData->mMachineState))
1493 saveSettings(NULL);
1494
1495 return S_OK;
1496}
1497
1498
1499STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1500{
1501 CheckComArgOutPointerValid(enabled);
1502
1503 AutoCaller autoCaller(this);
1504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1505
1506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1507
1508 *enabled = mHWData->mCPUHotPlugEnabled;
1509
1510 return S_OK;
1511}
1512
1513STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1514{
1515 HRESULT rc = S_OK;
1516
1517 AutoCaller autoCaller(this);
1518 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1519
1520 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1521
1522 rc = checkStateDependency(MutableStateDep);
1523 if (FAILED(rc)) return rc;
1524
1525 if (mHWData->mCPUHotPlugEnabled != enabled)
1526 {
1527 if (enabled)
1528 {
1529 setModified(IsModified_MachineData);
1530 mHWData.backup();
1531
1532 /* Add the amount of CPUs currently attached */
1533 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1534 {
1535 mHWData->mCPUAttached[i] = true;
1536 }
1537 }
1538 else
1539 {
1540 /*
1541 * We can disable hotplug only if the amount of maximum CPUs is equal
1542 * to the amount of attached CPUs
1543 */
1544 unsigned cCpusAttached = 0;
1545 unsigned iHighestId = 0;
1546
1547 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1548 {
1549 if (mHWData->mCPUAttached[i])
1550 {
1551 cCpusAttached++;
1552 iHighestId = i;
1553 }
1554 }
1555
1556 if ( (cCpusAttached != mHWData->mCPUCount)
1557 || (iHighestId >= mHWData->mCPUCount))
1558 return setError(E_INVALIDARG,
1559 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1560
1561 setModified(IsModified_MachineData);
1562 mHWData.backup();
1563 }
1564 }
1565
1566 mHWData->mCPUHotPlugEnabled = enabled;
1567
1568 return rc;
1569}
1570
1571STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1572{
1573#ifdef VBOX_WITH_USB_CARDREADER
1574 CheckComArgOutPointerValid(enabled);
1575
1576 AutoCaller autoCaller(this);
1577 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1578
1579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1580
1581 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1582
1583 return S_OK;
1584#else
1585 NOREF(enabled);
1586 return E_NOTIMPL;
1587#endif
1588}
1589
1590STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1591{
1592#ifdef VBOX_WITH_USB_CARDREADER
1593 AutoCaller autoCaller(this);
1594 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1595 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1596
1597 HRESULT rc = checkStateDependency(MutableStateDep);
1598 if (FAILED(rc)) return rc;
1599
1600 setModified(IsModified_MachineData);
1601 mHWData.backup();
1602 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1603
1604 return S_OK;
1605#else
1606 NOREF(enabled);
1607 return E_NOTIMPL;
1608#endif
1609}
1610
1611STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1612{
1613 NOREF(enabled);
1614 return E_NOTIMPL;
1615}
1616
1617STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1618{
1619 NOREF(enabled);
1620 return E_NOTIMPL;
1621}
1622
1623STDMETHODIMP Machine::COMGETTER(HpetEnabled)(BOOL *enabled)
1624{
1625 CheckComArgOutPointerValid(enabled);
1626
1627 AutoCaller autoCaller(this);
1628 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1630
1631 *enabled = mHWData->mHpetEnabled;
1632
1633 return S_OK;
1634}
1635
1636STDMETHODIMP Machine::COMSETTER(HpetEnabled)(BOOL enabled)
1637{
1638 HRESULT rc = S_OK;
1639
1640 AutoCaller autoCaller(this);
1641 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1642 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1643
1644 rc = checkStateDependency(MutableStateDep);
1645 if (FAILED(rc)) return rc;
1646
1647 setModified(IsModified_MachineData);
1648 mHWData.backup();
1649
1650 mHWData->mHpetEnabled = enabled;
1651
1652 return rc;
1653}
1654
1655STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1656{
1657 CheckComArgOutPointerValid(memorySize);
1658
1659 AutoCaller autoCaller(this);
1660 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1661
1662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1663
1664 *memorySize = mHWData->mVRAMSize;
1665
1666 return S_OK;
1667}
1668
1669STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1670{
1671 /* check VRAM limits */
1672 if (memorySize < SchemaDefs::MinGuestVRAM ||
1673 memorySize > SchemaDefs::MaxGuestVRAM)
1674 return setError(E_INVALIDARG,
1675 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1676 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1677
1678 AutoCaller autoCaller(this);
1679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1680
1681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 HRESULT rc = checkStateDependency(MutableStateDep);
1684 if (FAILED(rc)) return rc;
1685
1686 setModified(IsModified_MachineData);
1687 mHWData.backup();
1688 mHWData->mVRAMSize = memorySize;
1689
1690 return S_OK;
1691}
1692
1693/** @todo this method should not be public */
1694STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1695{
1696 CheckComArgOutPointerValid(memoryBalloonSize);
1697
1698 AutoCaller autoCaller(this);
1699 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1700
1701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1702
1703 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1704
1705 return S_OK;
1706}
1707
1708/**
1709 * Set the memory balloon size.
1710 *
1711 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1712 * we have to make sure that we never call IGuest from here.
1713 */
1714STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1715{
1716 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1717#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1718 /* check limits */
1719 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1720 return setError(E_INVALIDARG,
1721 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1722 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1723
1724 AutoCaller autoCaller(this);
1725 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1726
1727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1728
1729 setModified(IsModified_MachineData);
1730 mHWData.backup();
1731 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1732
1733 return S_OK;
1734#else
1735 NOREF(memoryBalloonSize);
1736 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1737#endif
1738}
1739
1740STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1741{
1742 CheckComArgOutPointerValid(enabled);
1743
1744 AutoCaller autoCaller(this);
1745 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1746
1747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1748
1749 *enabled = mHWData->mPageFusionEnabled;
1750 return S_OK;
1751}
1752
1753STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1754{
1755#ifdef VBOX_WITH_PAGE_SHARING
1756 AutoCaller autoCaller(this);
1757 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1758
1759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1760
1761 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1762 setModified(IsModified_MachineData);
1763 mHWData.backup();
1764 mHWData->mPageFusionEnabled = enabled;
1765 return S_OK;
1766#else
1767 NOREF(enabled);
1768 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1769#endif
1770}
1771
1772STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1773{
1774 CheckComArgOutPointerValid(enabled);
1775
1776 AutoCaller autoCaller(this);
1777 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1778
1779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1780
1781 *enabled = mHWData->mAccelerate3DEnabled;
1782
1783 return S_OK;
1784}
1785
1786STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1787{
1788 AutoCaller autoCaller(this);
1789 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1790
1791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1792
1793 HRESULT rc = checkStateDependency(MutableStateDep);
1794 if (FAILED(rc)) return rc;
1795
1796 /** @todo check validity! */
1797
1798 setModified(IsModified_MachineData);
1799 mHWData.backup();
1800 mHWData->mAccelerate3DEnabled = enable;
1801
1802 return S_OK;
1803}
1804
1805
1806STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1807{
1808 CheckComArgOutPointerValid(enabled);
1809
1810 AutoCaller autoCaller(this);
1811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1812
1813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 *enabled = mHWData->mAccelerate2DVideoEnabled;
1816
1817 return S_OK;
1818}
1819
1820STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1821{
1822 AutoCaller autoCaller(this);
1823 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1824
1825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1826
1827 HRESULT rc = checkStateDependency(MutableStateDep);
1828 if (FAILED(rc)) return rc;
1829
1830 /** @todo check validity! */
1831
1832 setModified(IsModified_MachineData);
1833 mHWData.backup();
1834 mHWData->mAccelerate2DVideoEnabled = enable;
1835
1836 return S_OK;
1837}
1838
1839STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1840{
1841 CheckComArgOutPointerValid(monitorCount);
1842
1843 AutoCaller autoCaller(this);
1844 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1845
1846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1847
1848 *monitorCount = mHWData->mMonitorCount;
1849
1850 return S_OK;
1851}
1852
1853STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1854{
1855 /* make sure monitor count is a sensible number */
1856 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1857 return setError(E_INVALIDARG,
1858 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1859 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1860
1861 AutoCaller autoCaller(this);
1862 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1863
1864 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1865
1866 HRESULT rc = checkStateDependency(MutableStateDep);
1867 if (FAILED(rc)) return rc;
1868
1869 setModified(IsModified_MachineData);
1870 mHWData.backup();
1871 mHWData->mMonitorCount = monitorCount;
1872
1873 return S_OK;
1874}
1875
1876STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1877{
1878 CheckComArgOutPointerValid(biosSettings);
1879
1880 AutoCaller autoCaller(this);
1881 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1882
1883 /* mBIOSSettings is constant during life time, no need to lock */
1884 mBIOSSettings.queryInterfaceTo(biosSettings);
1885
1886 return S_OK;
1887}
1888
1889STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1890{
1891 CheckComArgOutPointerValid(aVal);
1892
1893 AutoCaller autoCaller(this);
1894 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1895
1896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1897
1898 switch(property)
1899 {
1900 case CPUPropertyType_PAE:
1901 *aVal = mHWData->mPAEEnabled;
1902 break;
1903
1904 case CPUPropertyType_Synthetic:
1905 *aVal = mHWData->mSyntheticCpu;
1906 break;
1907
1908 default:
1909 return E_INVALIDARG;
1910 }
1911 return S_OK;
1912}
1913
1914STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
1915{
1916 AutoCaller autoCaller(this);
1917 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1918
1919 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1920
1921 HRESULT rc = checkStateDependency(MutableStateDep);
1922 if (FAILED(rc)) return rc;
1923
1924 switch(property)
1925 {
1926 case CPUPropertyType_PAE:
1927 setModified(IsModified_MachineData);
1928 mHWData.backup();
1929 mHWData->mPAEEnabled = !!aVal;
1930 break;
1931
1932 case CPUPropertyType_Synthetic:
1933 setModified(IsModified_MachineData);
1934 mHWData.backup();
1935 mHWData->mSyntheticCpu = !!aVal;
1936 break;
1937
1938 default:
1939 return E_INVALIDARG;
1940 }
1941 return S_OK;
1942}
1943
1944STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
1945{
1946 CheckComArgOutPointerValid(aValEax);
1947 CheckComArgOutPointerValid(aValEbx);
1948 CheckComArgOutPointerValid(aValEcx);
1949 CheckComArgOutPointerValid(aValEdx);
1950
1951 AutoCaller autoCaller(this);
1952 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1953
1954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1955
1956 switch(aId)
1957 {
1958 case 0x0:
1959 case 0x1:
1960 case 0x2:
1961 case 0x3:
1962 case 0x4:
1963 case 0x5:
1964 case 0x6:
1965 case 0x7:
1966 case 0x8:
1967 case 0x9:
1968 case 0xA:
1969 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
1970 return E_INVALIDARG;
1971
1972 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
1973 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
1974 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
1975 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
1976 break;
1977
1978 case 0x80000000:
1979 case 0x80000001:
1980 case 0x80000002:
1981 case 0x80000003:
1982 case 0x80000004:
1983 case 0x80000005:
1984 case 0x80000006:
1985 case 0x80000007:
1986 case 0x80000008:
1987 case 0x80000009:
1988 case 0x8000000A:
1989 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
1990 return E_INVALIDARG;
1991
1992 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
1993 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
1994 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
1995 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
1996 break;
1997
1998 default:
1999 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2000 }
2001 return S_OK;
2002}
2003
2004STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2005{
2006 AutoCaller autoCaller(this);
2007 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2008
2009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2010
2011 HRESULT rc = checkStateDependency(MutableStateDep);
2012 if (FAILED(rc)) return rc;
2013
2014 switch(aId)
2015 {
2016 case 0x0:
2017 case 0x1:
2018 case 0x2:
2019 case 0x3:
2020 case 0x4:
2021 case 0x5:
2022 case 0x6:
2023 case 0x7:
2024 case 0x8:
2025 case 0x9:
2026 case 0xA:
2027 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2028 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2029 setModified(IsModified_MachineData);
2030 mHWData.backup();
2031 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2032 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2033 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2034 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2035 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2036 break;
2037
2038 case 0x80000000:
2039 case 0x80000001:
2040 case 0x80000002:
2041 case 0x80000003:
2042 case 0x80000004:
2043 case 0x80000005:
2044 case 0x80000006:
2045 case 0x80000007:
2046 case 0x80000008:
2047 case 0x80000009:
2048 case 0x8000000A:
2049 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2050 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2051 setModified(IsModified_MachineData);
2052 mHWData.backup();
2053 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2054 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2055 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2056 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2057 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2058 break;
2059
2060 default:
2061 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2062 }
2063 return S_OK;
2064}
2065
2066STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2067{
2068 AutoCaller autoCaller(this);
2069 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2070
2071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2072
2073 HRESULT rc = checkStateDependency(MutableStateDep);
2074 if (FAILED(rc)) return rc;
2075
2076 switch(aId)
2077 {
2078 case 0x0:
2079 case 0x1:
2080 case 0x2:
2081 case 0x3:
2082 case 0x4:
2083 case 0x5:
2084 case 0x6:
2085 case 0x7:
2086 case 0x8:
2087 case 0x9:
2088 case 0xA:
2089 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2090 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2091 setModified(IsModified_MachineData);
2092 mHWData.backup();
2093 /* Invalidate leaf. */
2094 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2095 break;
2096
2097 case 0x80000000:
2098 case 0x80000001:
2099 case 0x80000002:
2100 case 0x80000003:
2101 case 0x80000004:
2102 case 0x80000005:
2103 case 0x80000006:
2104 case 0x80000007:
2105 case 0x80000008:
2106 case 0x80000009:
2107 case 0x8000000A:
2108 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2109 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2110 setModified(IsModified_MachineData);
2111 mHWData.backup();
2112 /* Invalidate leaf. */
2113 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2114 break;
2115
2116 default:
2117 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2118 }
2119 return S_OK;
2120}
2121
2122STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2123{
2124 AutoCaller autoCaller(this);
2125 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2126
2127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2128
2129 HRESULT rc = checkStateDependency(MutableStateDep);
2130 if (FAILED(rc)) return rc;
2131
2132 setModified(IsModified_MachineData);
2133 mHWData.backup();
2134
2135 /* Invalidate all standard leafs. */
2136 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2137 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2138
2139 /* Invalidate all extended leafs. */
2140 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2141 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2142
2143 return S_OK;
2144}
2145
2146STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2147{
2148 CheckComArgOutPointerValid(aVal);
2149
2150 AutoCaller autoCaller(this);
2151 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2152
2153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2154
2155 switch(property)
2156 {
2157 case HWVirtExPropertyType_Enabled:
2158 *aVal = mHWData->mHWVirtExEnabled;
2159 break;
2160
2161 case HWVirtExPropertyType_Exclusive:
2162 *aVal = mHWData->mHWVirtExExclusive;
2163 break;
2164
2165 case HWVirtExPropertyType_VPID:
2166 *aVal = mHWData->mHWVirtExVPIDEnabled;
2167 break;
2168
2169 case HWVirtExPropertyType_NestedPaging:
2170 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2171 break;
2172
2173 case HWVirtExPropertyType_LargePages:
2174 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2175#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2176 *aVal = FALSE;
2177#endif
2178 break;
2179
2180 case HWVirtExPropertyType_Force:
2181 *aVal = mHWData->mHWVirtExForceEnabled;
2182 break;
2183
2184 default:
2185 return E_INVALIDARG;
2186 }
2187 return S_OK;
2188}
2189
2190STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2191{
2192 AutoCaller autoCaller(this);
2193 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2194
2195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2196
2197 HRESULT rc = checkStateDependency(MutableStateDep);
2198 if (FAILED(rc)) return rc;
2199
2200 switch(property)
2201 {
2202 case HWVirtExPropertyType_Enabled:
2203 setModified(IsModified_MachineData);
2204 mHWData.backup();
2205 mHWData->mHWVirtExEnabled = !!aVal;
2206 break;
2207
2208 case HWVirtExPropertyType_Exclusive:
2209 setModified(IsModified_MachineData);
2210 mHWData.backup();
2211 mHWData->mHWVirtExExclusive = !!aVal;
2212 break;
2213
2214 case HWVirtExPropertyType_VPID:
2215 setModified(IsModified_MachineData);
2216 mHWData.backup();
2217 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2218 break;
2219
2220 case HWVirtExPropertyType_NestedPaging:
2221 setModified(IsModified_MachineData);
2222 mHWData.backup();
2223 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2224 break;
2225
2226 case HWVirtExPropertyType_LargePages:
2227 setModified(IsModified_MachineData);
2228 mHWData.backup();
2229 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2230 break;
2231
2232 case HWVirtExPropertyType_Force:
2233 setModified(IsModified_MachineData);
2234 mHWData.backup();
2235 mHWData->mHWVirtExForceEnabled = !!aVal;
2236 break;
2237
2238 default:
2239 return E_INVALIDARG;
2240 }
2241
2242 return S_OK;
2243}
2244
2245STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2246{
2247 CheckComArgOutPointerValid(aSnapshotFolder);
2248
2249 AutoCaller autoCaller(this);
2250 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2251
2252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2253
2254 Utf8Str strFullSnapshotFolder;
2255 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2256 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2257
2258 return S_OK;
2259}
2260
2261STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2262{
2263 /* @todo (r=dmik):
2264 * 1. Allow to change the name of the snapshot folder containing snapshots
2265 * 2. Rename the folder on disk instead of just changing the property
2266 * value (to be smart and not to leave garbage). Note that it cannot be
2267 * done here because the change may be rolled back. Thus, the right
2268 * place is #saveSettings().
2269 */
2270
2271 AutoCaller autoCaller(this);
2272 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2273
2274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2275
2276 HRESULT rc = checkStateDependency(MutableStateDep);
2277 if (FAILED(rc)) return rc;
2278
2279 if (!mData->mCurrentSnapshot.isNull())
2280 return setError(E_FAIL,
2281 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2282
2283 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2284
2285 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2286 if (strSnapshotFolder.isEmpty())
2287 strSnapshotFolder = "Snapshots";
2288 int vrc = calculateFullPath(strSnapshotFolder,
2289 strSnapshotFolder);
2290 if (RT_FAILURE(vrc))
2291 return setError(E_FAIL,
2292 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2293 aSnapshotFolder, vrc);
2294
2295 setModified(IsModified_MachineData);
2296 mUserData.backup();
2297
2298 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2299
2300 return S_OK;
2301}
2302
2303STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2304{
2305 CheckComArgOutSafeArrayPointerValid(aAttachments);
2306
2307 AutoCaller autoCaller(this);
2308 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2309
2310 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2311
2312 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2313 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2314
2315 return S_OK;
2316}
2317
2318STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2319{
2320 CheckComArgOutPointerValid(vrdeServer);
2321
2322 AutoCaller autoCaller(this);
2323 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2324
2325 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2326
2327 Assert(!!mVRDEServer);
2328 mVRDEServer.queryInterfaceTo(vrdeServer);
2329
2330 return S_OK;
2331}
2332
2333STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2334{
2335 CheckComArgOutPointerValid(audioAdapter);
2336
2337 AutoCaller autoCaller(this);
2338 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2339
2340 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2341
2342 mAudioAdapter.queryInterfaceTo(audioAdapter);
2343 return S_OK;
2344}
2345
2346STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2347{
2348#ifdef VBOX_WITH_VUSB
2349 CheckComArgOutPointerValid(aUSBController);
2350
2351 AutoCaller autoCaller(this);
2352 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2353
2354 clearError();
2355 MultiResult rc(S_OK);
2356
2357# ifdef VBOX_WITH_USB
2358 rc = mParent->host()->checkUSBProxyService();
2359 if (FAILED(rc)) return rc;
2360# endif
2361
2362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2363
2364 return rc = mUSBController.queryInterfaceTo(aUSBController);
2365#else
2366 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2367 * extended error info to indicate that USB is simply not available
2368 * (w/o treating it as a failure), for example, as in OSE */
2369 NOREF(aUSBController);
2370 ReturnComNotImplemented();
2371#endif /* VBOX_WITH_VUSB */
2372}
2373
2374STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2375{
2376 CheckComArgOutPointerValid(aFilePath);
2377
2378 AutoLimitedCaller autoCaller(this);
2379 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2380
2381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2382
2383 mData->m_strConfigFileFull.cloneTo(aFilePath);
2384 return S_OK;
2385}
2386
2387STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2388{
2389 CheckComArgOutPointerValid(aModified);
2390
2391 AutoCaller autoCaller(this);
2392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2393
2394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2395
2396 HRESULT rc = checkStateDependency(MutableStateDep);
2397 if (FAILED(rc)) return rc;
2398
2399 if (!mData->pMachineConfigFile->fileExists())
2400 // this is a new machine, and no config file exists yet:
2401 *aModified = TRUE;
2402 else
2403 *aModified = (mData->flModifications != 0);
2404
2405 return S_OK;
2406}
2407
2408STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2409{
2410 CheckComArgOutPointerValid(aSessionState);
2411
2412 AutoCaller autoCaller(this);
2413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2414
2415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2416
2417 *aSessionState = mData->mSession.mState;
2418
2419 return S_OK;
2420}
2421
2422STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2423{
2424 CheckComArgOutPointerValid(aSessionType);
2425
2426 AutoCaller autoCaller(this);
2427 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2428
2429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2430
2431 mData->mSession.mType.cloneTo(aSessionType);
2432
2433 return S_OK;
2434}
2435
2436STDMETHODIMP Machine::COMGETTER(SessionPid)(ULONG *aSessionPid)
2437{
2438 CheckComArgOutPointerValid(aSessionPid);
2439
2440 AutoCaller autoCaller(this);
2441 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2442
2443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2444
2445 *aSessionPid = mData->mSession.mPid;
2446
2447 return S_OK;
2448}
2449
2450STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2451{
2452 CheckComArgOutPointerValid(machineState);
2453
2454 AutoCaller autoCaller(this);
2455 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2456
2457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2458
2459 *machineState = mData->mMachineState;
2460
2461 return S_OK;
2462}
2463
2464STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2465{
2466 CheckComArgOutPointerValid(aLastStateChange);
2467
2468 AutoCaller autoCaller(this);
2469 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2470
2471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2472
2473 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2474
2475 return S_OK;
2476}
2477
2478STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2479{
2480 CheckComArgOutPointerValid(aStateFilePath);
2481
2482 AutoCaller autoCaller(this);
2483 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2484
2485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2486
2487 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2488
2489 return S_OK;
2490}
2491
2492STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2493{
2494 CheckComArgOutPointerValid(aLogFolder);
2495
2496 AutoCaller autoCaller(this);
2497 AssertComRCReturnRC(autoCaller.rc());
2498
2499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2500
2501 Utf8Str logFolder;
2502 getLogFolder(logFolder);
2503 logFolder.cloneTo(aLogFolder);
2504
2505 return S_OK;
2506}
2507
2508STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2509{
2510 CheckComArgOutPointerValid(aCurrentSnapshot);
2511
2512 AutoCaller autoCaller(this);
2513 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2514
2515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2516
2517 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2518
2519 return S_OK;
2520}
2521
2522STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2523{
2524 CheckComArgOutPointerValid(aSnapshotCount);
2525
2526 AutoCaller autoCaller(this);
2527 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2528
2529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2530
2531 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2532 ? 0
2533 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2534
2535 return S_OK;
2536}
2537
2538STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2539{
2540 CheckComArgOutPointerValid(aCurrentStateModified);
2541
2542 AutoCaller autoCaller(this);
2543 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2544
2545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2546
2547 /* Note: for machines with no snapshots, we always return FALSE
2548 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2549 * reasons :) */
2550
2551 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2552 ? FALSE
2553 : mData->mCurrentStateModified;
2554
2555 return S_OK;
2556}
2557
2558STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2559{
2560 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2561
2562 AutoCaller autoCaller(this);
2563 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2564
2565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2566
2567 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2568 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2569
2570 return S_OK;
2571}
2572
2573STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2574{
2575 CheckComArgOutPointerValid(aClipboardMode);
2576
2577 AutoCaller autoCaller(this);
2578 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2579
2580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2581
2582 *aClipboardMode = mHWData->mClipboardMode;
2583
2584 return S_OK;
2585}
2586
2587STDMETHODIMP
2588Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2589{
2590 HRESULT rc = S_OK;
2591
2592 AutoCaller autoCaller(this);
2593 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2594
2595 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2596
2597 alock.release();
2598 rc = onClipboardModeChange(aClipboardMode);
2599 alock.acquire();
2600 if (FAILED(rc)) return rc;
2601
2602 setModified(IsModified_MachineData);
2603 mHWData.backup();
2604 mHWData->mClipboardMode = aClipboardMode;
2605
2606 /* Save settings if online - todo why is this required?? */
2607 if (Global::IsOnline(mData->mMachineState))
2608 saveSettings(NULL);
2609
2610 return S_OK;
2611}
2612
2613STDMETHODIMP
2614Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2615{
2616 CheckComArgOutPointerValid(aPatterns);
2617
2618 AutoCaller autoCaller(this);
2619 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2620
2621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2622
2623 try
2624 {
2625 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2626 }
2627 catch (...)
2628 {
2629 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2630 }
2631
2632 return S_OK;
2633}
2634
2635STDMETHODIMP
2636Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2637{
2638 AutoCaller autoCaller(this);
2639 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2640
2641 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2642
2643 HRESULT rc = checkStateDependency(MutableStateDep);
2644 if (FAILED(rc)) return rc;
2645
2646 setModified(IsModified_MachineData);
2647 mHWData.backup();
2648 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2649 return rc;
2650}
2651
2652STDMETHODIMP
2653Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2654{
2655 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2656
2657 AutoCaller autoCaller(this);
2658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2659
2660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2661
2662 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2663 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2664
2665 return S_OK;
2666}
2667
2668STDMETHODIMP
2669Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2670{
2671 CheckComArgOutPointerValid(aEnabled);
2672
2673 AutoCaller autoCaller(this);
2674 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2675
2676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2677
2678 *aEnabled = mUserData->s.fTeleporterEnabled;
2679
2680 return S_OK;
2681}
2682
2683STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2684{
2685 AutoCaller autoCaller(this);
2686 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2687
2688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2689
2690 /* Only allow it to be set to true when PoweredOff or Aborted.
2691 (Clearing it is always permitted.) */
2692 if ( aEnabled
2693 && mData->mRegistered
2694 && ( !isSessionMachine()
2695 || ( mData->mMachineState != MachineState_PoweredOff
2696 && mData->mMachineState != MachineState_Teleported
2697 && mData->mMachineState != MachineState_Aborted
2698 )
2699 )
2700 )
2701 return setError(VBOX_E_INVALID_VM_STATE,
2702 tr("The machine is not powered off (state is %s)"),
2703 Global::stringifyMachineState(mData->mMachineState));
2704
2705 setModified(IsModified_MachineData);
2706 mUserData.backup();
2707 mUserData->s.fTeleporterEnabled = !!aEnabled;
2708
2709 return S_OK;
2710}
2711
2712STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2713{
2714 CheckComArgOutPointerValid(aPort);
2715
2716 AutoCaller autoCaller(this);
2717 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2718
2719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2720
2721 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2722
2723 return S_OK;
2724}
2725
2726STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2727{
2728 if (aPort >= _64K)
2729 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2730
2731 AutoCaller autoCaller(this);
2732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2733
2734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2735
2736 HRESULT rc = checkStateDependency(MutableStateDep);
2737 if (FAILED(rc)) return rc;
2738
2739 setModified(IsModified_MachineData);
2740 mUserData.backup();
2741 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2742
2743 return S_OK;
2744}
2745
2746STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2747{
2748 CheckComArgOutPointerValid(aAddress);
2749
2750 AutoCaller autoCaller(this);
2751 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2752
2753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2754
2755 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2756
2757 return S_OK;
2758}
2759
2760STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2761{
2762 AutoCaller autoCaller(this);
2763 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2764
2765 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2766
2767 HRESULT rc = checkStateDependency(MutableStateDep);
2768 if (FAILED(rc)) return rc;
2769
2770 setModified(IsModified_MachineData);
2771 mUserData.backup();
2772 mUserData->s.strTeleporterAddress = aAddress;
2773
2774 return S_OK;
2775}
2776
2777STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2778{
2779 CheckComArgOutPointerValid(aPassword);
2780
2781 AutoCaller autoCaller(this);
2782 HRESULT hrc = autoCaller.rc();
2783 if (SUCCEEDED(hrc))
2784 {
2785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2786 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2787 }
2788
2789 return hrc;
2790}
2791
2792STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2793{
2794 /*
2795 * Hash the password first.
2796 */
2797 Utf8Str strPassword(aPassword);
2798 if (!strPassword.isEmpty())
2799 {
2800 if (VBoxIsPasswordHashed(&strPassword))
2801 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2802 VBoxHashPassword(&strPassword);
2803 }
2804
2805 /*
2806 * Do the update.
2807 */
2808 AutoCaller autoCaller(this);
2809 HRESULT hrc = autoCaller.rc();
2810 if (SUCCEEDED(hrc))
2811 {
2812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2813 hrc = checkStateDependency(MutableStateDep);
2814 if (SUCCEEDED(hrc))
2815 {
2816 setModified(IsModified_MachineData);
2817 mUserData.backup();
2818 mUserData->s.strTeleporterPassword = strPassword;
2819 }
2820 }
2821
2822 return hrc;
2823}
2824
2825STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2826{
2827 CheckComArgOutPointerValid(aState);
2828
2829 AutoCaller autoCaller(this);
2830 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2831
2832 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2833
2834 *aState = mUserData->s.enmFaultToleranceState;
2835 return S_OK;
2836}
2837
2838STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
2839{
2840 AutoCaller autoCaller(this);
2841 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2842
2843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2844
2845 /* @todo deal with running state change. */
2846 HRESULT rc = checkStateDependency(MutableStateDep);
2847 if (FAILED(rc)) return rc;
2848
2849 setModified(IsModified_MachineData);
2850 mUserData.backup();
2851 mUserData->s.enmFaultToleranceState = aState;
2852 return S_OK;
2853}
2854
2855STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
2856{
2857 CheckComArgOutPointerValid(aAddress);
2858
2859 AutoCaller autoCaller(this);
2860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2861
2862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2863
2864 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
2865 return S_OK;
2866}
2867
2868STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
2869{
2870 AutoCaller autoCaller(this);
2871 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2872
2873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2874
2875 /* @todo deal with running state change. */
2876 HRESULT rc = checkStateDependency(MutableStateDep);
2877 if (FAILED(rc)) return rc;
2878
2879 setModified(IsModified_MachineData);
2880 mUserData.backup();
2881 mUserData->s.strFaultToleranceAddress = aAddress;
2882 return S_OK;
2883}
2884
2885STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
2886{
2887 CheckComArgOutPointerValid(aPort);
2888
2889 AutoCaller autoCaller(this);
2890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2891
2892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2893
2894 *aPort = mUserData->s.uFaultTolerancePort;
2895 return S_OK;
2896}
2897
2898STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
2899{
2900 AutoCaller autoCaller(this);
2901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2902
2903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2904
2905 /* @todo deal with running state change. */
2906 HRESULT rc = checkStateDependency(MutableStateDep);
2907 if (FAILED(rc)) return rc;
2908
2909 setModified(IsModified_MachineData);
2910 mUserData.backup();
2911 mUserData->s.uFaultTolerancePort = aPort;
2912 return S_OK;
2913}
2914
2915STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
2916{
2917 CheckComArgOutPointerValid(aPassword);
2918
2919 AutoCaller autoCaller(this);
2920 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2921
2922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2923
2924 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
2925
2926 return S_OK;
2927}
2928
2929STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
2930{
2931 AutoCaller autoCaller(this);
2932 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2933
2934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2935
2936 /* @todo deal with running state change. */
2937 HRESULT rc = checkStateDependency(MutableStateDep);
2938 if (FAILED(rc)) return rc;
2939
2940 setModified(IsModified_MachineData);
2941 mUserData.backup();
2942 mUserData->s.strFaultTolerancePassword = aPassword;
2943
2944 return S_OK;
2945}
2946
2947STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
2948{
2949 CheckComArgOutPointerValid(aInterval);
2950
2951 AutoCaller autoCaller(this);
2952 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2953
2954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2955
2956 *aInterval = mUserData->s.uFaultToleranceInterval;
2957 return S_OK;
2958}
2959
2960STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
2961{
2962 AutoCaller autoCaller(this);
2963 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2964
2965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2966
2967 /* @todo deal with running state change. */
2968 HRESULT rc = checkStateDependency(MutableStateDep);
2969 if (FAILED(rc)) return rc;
2970
2971 setModified(IsModified_MachineData);
2972 mUserData.backup();
2973 mUserData->s.uFaultToleranceInterval = aInterval;
2974 return S_OK;
2975}
2976
2977STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
2978{
2979 CheckComArgOutPointerValid(aEnabled);
2980
2981 AutoCaller autoCaller(this);
2982 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2983
2984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2985
2986 *aEnabled = mUserData->s.fRTCUseUTC;
2987
2988 return S_OK;
2989}
2990
2991STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
2992{
2993 AutoCaller autoCaller(this);
2994 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2995
2996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2997
2998 /* Only allow it to be set to true when PoweredOff or Aborted.
2999 (Clearing it is always permitted.) */
3000 if ( aEnabled
3001 && mData->mRegistered
3002 && ( !isSessionMachine()
3003 || ( mData->mMachineState != MachineState_PoweredOff
3004 && mData->mMachineState != MachineState_Teleported
3005 && mData->mMachineState != MachineState_Aborted
3006 )
3007 )
3008 )
3009 return setError(VBOX_E_INVALID_VM_STATE,
3010 tr("The machine is not powered off (state is %s)"),
3011 Global::stringifyMachineState(mData->mMachineState));
3012
3013 setModified(IsModified_MachineData);
3014 mUserData.backup();
3015 mUserData->s.fRTCUseUTC = !!aEnabled;
3016
3017 return S_OK;
3018}
3019
3020STDMETHODIMP Machine::COMGETTER(IoCacheEnabled)(BOOL *aEnabled)
3021{
3022 CheckComArgOutPointerValid(aEnabled);
3023
3024 AutoCaller autoCaller(this);
3025 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3026
3027 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3028
3029 *aEnabled = mHWData->mIoCacheEnabled;
3030
3031 return S_OK;
3032}
3033
3034STDMETHODIMP Machine::COMSETTER(IoCacheEnabled)(BOOL aEnabled)
3035{
3036 AutoCaller autoCaller(this);
3037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3038
3039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3040
3041 HRESULT rc = checkStateDependency(MutableStateDep);
3042 if (FAILED(rc)) return rc;
3043
3044 setModified(IsModified_MachineData);
3045 mHWData.backup();
3046 mHWData->mIoCacheEnabled = aEnabled;
3047
3048 return S_OK;
3049}
3050
3051STDMETHODIMP Machine::COMGETTER(IoCacheSize)(ULONG *aIoCacheSize)
3052{
3053 CheckComArgOutPointerValid(aIoCacheSize);
3054
3055 AutoCaller autoCaller(this);
3056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3057
3058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3059
3060 *aIoCacheSize = mHWData->mIoCacheSize;
3061
3062 return S_OK;
3063}
3064
3065STDMETHODIMP Machine::COMSETTER(IoCacheSize)(ULONG aIoCacheSize)
3066{
3067 AutoCaller autoCaller(this);
3068 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3069
3070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3071
3072 HRESULT rc = checkStateDependency(MutableStateDep);
3073 if (FAILED(rc)) return rc;
3074
3075 setModified(IsModified_MachineData);
3076 mHWData.backup();
3077 mHWData->mIoCacheSize = aIoCacheSize;
3078
3079 return S_OK;
3080}
3081
3082
3083/**
3084 * @note Locks objects!
3085 */
3086STDMETHODIMP Machine::LockMachine(ISession *aSession,
3087 LockType_T lockType)
3088{
3089 CheckComArgNotNull(aSession);
3090
3091 AutoCaller autoCaller(this);
3092 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3093
3094 /* check the session state */
3095 SessionState_T state;
3096 HRESULT rc = aSession->COMGETTER(State)(&state);
3097 if (FAILED(rc)) return rc;
3098
3099 if (state != SessionState_Unlocked)
3100 return setError(VBOX_E_INVALID_OBJECT_STATE,
3101 tr("The given session is busy"));
3102
3103 // get the client's IInternalSessionControl interface
3104 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3105 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3106 E_INVALIDARG);
3107
3108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3109
3110 if (!mData->mRegistered)
3111 return setError(E_UNEXPECTED,
3112 tr("The machine '%s' is not registered"),
3113 mUserData->s.strName.c_str());
3114
3115 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3116
3117 SessionState_T oldState = mData->mSession.mState;
3118 /* Hack: in case the session is closing and there is a progress object
3119 * which allows waiting for the session to be closed, take the opportunity
3120 * and do a limited wait (max. 1 second). This helps a lot when the system
3121 * is busy and thus session closing can take a little while. */
3122 if ( mData->mSession.mState == SessionState_Unlocking
3123 && mData->mSession.mProgress)
3124 {
3125 alock.release();
3126 mData->mSession.mProgress->WaitForCompletion(1000);
3127 alock.acquire();
3128 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3129 }
3130
3131 // try again now
3132 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3133 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3134 )
3135 {
3136 // OK, share the session... we are now dealing with three processes:
3137 // 1) VBoxSVC (where this code runs);
3138 // 2) process C: the caller's client process (who wants a shared session);
3139 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3140
3141 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3142 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3143 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3144 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3145 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3146
3147 /*
3148 * Release the lock before calling the client process. It's safe here
3149 * since the only thing to do after we get the lock again is to add
3150 * the remote control to the list (which doesn't directly influence
3151 * anything).
3152 */
3153 alock.release();
3154
3155 // get the console of the session holding the write lock (this is a remote call)
3156 ComPtr<IConsole> pConsoleW;
3157 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3158 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3159 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3160 if (FAILED(rc))
3161 // the failure may occur w/o any error info (from RPC), so provide one
3162 return setError(VBOX_E_VM_ERROR,
3163 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3164
3165 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3166
3167 // share the session machine and W's console with the caller's session
3168 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3169 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3170 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3171
3172 if (FAILED(rc))
3173 // the failure may occur w/o any error info (from RPC), so provide one
3174 return setError(VBOX_E_VM_ERROR,
3175 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3176 alock.acquire();
3177
3178 // need to revalidate the state after acquiring the lock again
3179 if (mData->mSession.mState != SessionState_Locked)
3180 {
3181 pSessionControl->Uninitialize();
3182 return setError(VBOX_E_INVALID_SESSION_STATE,
3183 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3184 mUserData->s.strName.c_str());
3185 }
3186
3187 // add the caller's session to the list
3188 mData->mSession.mRemoteControls.push_back(pSessionControl);
3189 }
3190 else if ( mData->mSession.mState == SessionState_Locked
3191 || mData->mSession.mState == SessionState_Unlocking
3192 )
3193 {
3194 // sharing not permitted, or machine still unlocking:
3195 return setError(VBOX_E_INVALID_OBJECT_STATE,
3196 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3197 mUserData->s.strName.c_str());
3198 }
3199 else
3200 {
3201 // machine is not locked: then write-lock the machine (create the session machine)
3202
3203 // must not be busy
3204 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3205
3206 // get the caller's session PID
3207 RTPROCESS pid = NIL_RTPROCESS;
3208 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3209 pSessionControl->GetPID((ULONG*)&pid);
3210 Assert(pid != NIL_RTPROCESS);
3211
3212 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3213
3214 if (fLaunchingVMProcess)
3215 {
3216 // this machine is awaiting for a spawning session to be opened:
3217 // then the calling process must be the one that got started by
3218 // LaunchVMProcess()
3219
3220 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n", mData->mSession.mPid, mData->mSession.mPid));
3221 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3222
3223 if (mData->mSession.mPid != pid)
3224 return setError(E_ACCESSDENIED,
3225 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3226 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3227 pid, mUserData->s.strName.c_str(), mData->mSession.mPid);
3228 }
3229
3230 // create the mutable SessionMachine from the current machine
3231 ComObjPtr<SessionMachine> sessionMachine;
3232 sessionMachine.createObject();
3233 rc = sessionMachine->init(this);
3234 AssertComRC(rc);
3235
3236 /* NOTE: doing return from this function after this point but
3237 * before the end is forbidden since it may call SessionMachine::uninit()
3238 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3239 * lock while still holding the Machine lock in alock so that a deadlock
3240 * is possible due to the wrong lock order. */
3241
3242 if (SUCCEEDED(rc))
3243 {
3244 /*
3245 * Set the session state to Spawning to protect against subsequent
3246 * attempts to open a session and to unregister the machine after
3247 * we release the lock.
3248 */
3249 SessionState_T origState = mData->mSession.mState;
3250 mData->mSession.mState = SessionState_Spawning;
3251
3252 /*
3253 * Release the lock before calling the client process -- it will call
3254 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3255 * because the state is Spawning, so that LaunchVMProcess() and
3256 * LockMachine() calls will fail. This method, called before we
3257 * acquire the lock again, will fail because of the wrong PID.
3258 *
3259 * Note that mData->mSession.mRemoteControls accessed outside
3260 * the lock may not be modified when state is Spawning, so it's safe.
3261 */
3262 alock.release();
3263
3264 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3265 rc = pSessionControl->AssignMachine(sessionMachine);
3266 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3267
3268 /* The failure may occur w/o any error info (from RPC), so provide one */
3269 if (FAILED(rc))
3270 setError(VBOX_E_VM_ERROR,
3271 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3272
3273 if ( SUCCEEDED(rc)
3274 && fLaunchingVMProcess
3275 )
3276 {
3277 /* complete the remote session initialization */
3278
3279 /* get the console from the direct session */
3280 ComPtr<IConsole> console;
3281 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3282 ComAssertComRC(rc);
3283
3284 if (SUCCEEDED(rc) && !console)
3285 {
3286 ComAssert(!!console);
3287 rc = E_FAIL;
3288 }
3289
3290 /* assign machine & console to the remote session */
3291 if (SUCCEEDED(rc))
3292 {
3293 /*
3294 * after LaunchVMProcess(), the first and the only
3295 * entry in remoteControls is that remote session
3296 */
3297 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3298 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3299 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3300
3301 /* The failure may occur w/o any error info (from RPC), so provide one */
3302 if (FAILED(rc))
3303 setError(VBOX_E_VM_ERROR,
3304 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3305 }
3306
3307 if (FAILED(rc))
3308 pSessionControl->Uninitialize();
3309 }
3310
3311 /* acquire the lock again */
3312 alock.acquire();
3313
3314 /* Restore the session state */
3315 mData->mSession.mState = origState;
3316 }
3317
3318 // finalize spawning anyway (this is why we don't return on errors above)
3319 if (fLaunchingVMProcess)
3320 {
3321 /* Note that the progress object is finalized later */
3322 /** @todo Consider checking mData->mSession.mProgress for cancellation
3323 * around here. */
3324
3325 /* We don't reset mSession.mPid here because it is necessary for
3326 * SessionMachine::uninit() to reap the child process later. */
3327
3328 if (FAILED(rc))
3329 {
3330 /* Close the remote session, remove the remote control from the list
3331 * and reset session state to Closed (@note keep the code in sync
3332 * with the relevant part in openSession()). */
3333
3334 Assert(mData->mSession.mRemoteControls.size() == 1);
3335 if (mData->mSession.mRemoteControls.size() == 1)
3336 {
3337 ErrorInfoKeeper eik;
3338 mData->mSession.mRemoteControls.front()->Uninitialize();
3339 }
3340
3341 mData->mSession.mRemoteControls.clear();
3342 mData->mSession.mState = SessionState_Unlocked;
3343 }
3344 }
3345 else
3346 {
3347 /* memorize PID of the directly opened session */
3348 if (SUCCEEDED(rc))
3349 mData->mSession.mPid = pid;
3350 }
3351
3352 if (SUCCEEDED(rc))
3353 {
3354 /* memorize the direct session control and cache IUnknown for it */
3355 mData->mSession.mDirectControl = pSessionControl;
3356 mData->mSession.mState = SessionState_Locked;
3357 /* associate the SessionMachine with this Machine */
3358 mData->mSession.mMachine = sessionMachine;
3359
3360 /* request an IUnknown pointer early from the remote party for later
3361 * identity checks (it will be internally cached within mDirectControl
3362 * at least on XPCOM) */
3363 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3364 NOREF(unk);
3365 }
3366
3367 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3368 * would break the lock order */
3369 alock.release();
3370
3371 /* uninitialize the created session machine on failure */
3372 if (FAILED(rc))
3373 sessionMachine->uninit();
3374
3375 }
3376
3377 if (SUCCEEDED(rc))
3378 {
3379 /*
3380 * tell the client watcher thread to update the set of
3381 * machines that have open sessions
3382 */
3383 mParent->updateClientWatcher();
3384
3385 if (oldState != SessionState_Locked)
3386 /* fire an event */
3387 mParent->onSessionStateChange(getId(), SessionState_Locked);
3388 }
3389
3390 return rc;
3391}
3392
3393/**
3394 * @note Locks objects!
3395 */
3396STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3397 IN_BSTR aType,
3398 IN_BSTR aEnvironment,
3399 IProgress **aProgress)
3400{
3401 CheckComArgStrNotEmptyOrNull(aType);
3402 Utf8Str strType(aType);
3403 Utf8Str strEnvironment(aEnvironment);
3404 /* "emergencystop" doesn't need the session, so skip the checks/interface
3405 * retrieval. This code doesn't quite fit in here, but introducing a
3406 * special API method would be even more effort, and would require explicit
3407 * support by every API client. It's better to hide the feature a bit. */
3408 if (strType != "emergencystop")
3409 CheckComArgNotNull(aSession);
3410 CheckComArgOutPointerValid(aProgress);
3411
3412 AutoCaller autoCaller(this);
3413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3414
3415 ComPtr<IInternalSessionControl> control;
3416 HRESULT rc = S_OK;
3417
3418 if (strType != "emergencystop")
3419 {
3420 /* check the session state */
3421 SessionState_T state;
3422 rc = aSession->COMGETTER(State)(&state);
3423 if (FAILED(rc))
3424 return rc;
3425
3426 if (state != SessionState_Unlocked)
3427 return setError(VBOX_E_INVALID_OBJECT_STATE,
3428 tr("The given session is busy"));
3429
3430 /* get the IInternalSessionControl interface */
3431 control = aSession;
3432 ComAssertMsgRet(!control.isNull(),
3433 ("No IInternalSessionControl interface"),
3434 E_INVALIDARG);
3435 }
3436
3437 /* get the teleporter enable state for the progress object init. */
3438 BOOL fTeleporterEnabled;
3439 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3440 if (FAILED(rc))
3441 return rc;
3442
3443 /* create a progress object */
3444 if (strType != "emergencystop")
3445 {
3446 ComObjPtr<ProgressProxy> progress;
3447 progress.createObject();
3448 rc = progress->init(mParent,
3449 static_cast<IMachine*>(this),
3450 Bstr(tr("Starting VM")).raw(),
3451 TRUE /* aCancelable */,
3452 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3453 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strType.c_str()).raw(),
3454 2 /* uFirstOperationWeight */,
3455 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3456
3457 if (SUCCEEDED(rc))
3458 {
3459 rc = launchVMProcess(control, strType, strEnvironment, progress);
3460 if (SUCCEEDED(rc))
3461 {
3462 progress.queryInterfaceTo(aProgress);
3463
3464 /* signal the client watcher thread */
3465 mParent->updateClientWatcher();
3466
3467 /* fire an event */
3468 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3469 }
3470 }
3471 }
3472 else
3473 {
3474 /* no progress object - either instant success or failure */
3475 *aProgress = NULL;
3476
3477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3478
3479 if (mData->mSession.mState != SessionState_Locked)
3480 return setError(VBOX_E_INVALID_OBJECT_STATE,
3481 tr("The machine '%s' is not locked by a session"),
3482 mUserData->s.strName.c_str());
3483
3484 /* must have a VM process associated - do not kill normal API clients
3485 * with an open session */
3486 if (!Global::IsOnline(mData->mMachineState))
3487 return setError(VBOX_E_INVALID_OBJECT_STATE,
3488 tr("The machine '%s' does not have a VM process"),
3489 mUserData->s.strName.c_str());
3490
3491 /* forcibly terminate the VM process */
3492 if (mData->mSession.mPid != NIL_RTPROCESS)
3493 RTProcTerminate(mData->mSession.mPid);
3494
3495 /* signal the client watcher thread, as most likely the client has
3496 * been terminated */
3497 mParent->updateClientWatcher();
3498 }
3499
3500 return rc;
3501}
3502
3503STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3504{
3505 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3506 return setError(E_INVALIDARG,
3507 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3508 aPosition, SchemaDefs::MaxBootPosition);
3509
3510 if (aDevice == DeviceType_USB)
3511 return setError(E_NOTIMPL,
3512 tr("Booting from USB device is currently not supported"));
3513
3514 AutoCaller autoCaller(this);
3515 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3516
3517 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3518
3519 HRESULT rc = checkStateDependency(MutableStateDep);
3520 if (FAILED(rc)) return rc;
3521
3522 setModified(IsModified_MachineData);
3523 mHWData.backup();
3524 mHWData->mBootOrder[aPosition - 1] = aDevice;
3525
3526 return S_OK;
3527}
3528
3529STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3530{
3531 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3532 return setError(E_INVALIDARG,
3533 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3534 aPosition, SchemaDefs::MaxBootPosition);
3535
3536 AutoCaller autoCaller(this);
3537 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3538
3539 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3540
3541 *aDevice = mHWData->mBootOrder[aPosition - 1];
3542
3543 return S_OK;
3544}
3545
3546STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3547 LONG aControllerPort,
3548 LONG aDevice,
3549 DeviceType_T aType,
3550 IMedium *aMedium)
3551{
3552 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3553 aControllerName, aControllerPort, aDevice, aType, aMedium));
3554
3555 CheckComArgStrNotEmptyOrNull(aControllerName);
3556
3557 AutoCaller autoCaller(this);
3558 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3559
3560 // request the host lock first, since might be calling Host methods for getting host drives;
3561 // next, protect the media tree all the while we're in here, as well as our member variables
3562 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3563 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3564
3565 HRESULT rc = checkStateDependency(MutableStateDep);
3566 if (FAILED(rc)) return rc;
3567
3568 /// @todo NEWMEDIA implicit machine registration
3569 if (!mData->mRegistered)
3570 return setError(VBOX_E_INVALID_OBJECT_STATE,
3571 tr("Cannot attach storage devices to an unregistered machine"));
3572
3573 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3574
3575 /* Check for an existing controller. */
3576 ComObjPtr<StorageController> ctl;
3577 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3578 if (FAILED(rc)) return rc;
3579
3580 StorageControllerType_T ctrlType;
3581 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3582 if (FAILED(rc))
3583 return setError(E_FAIL,
3584 tr("Could not get type of controller '%ls'"),
3585 aControllerName);
3586
3587 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3588 bool fHotplug = false;
3589 if (Global::IsOnlineOrTransient(mData->mMachineState))
3590 fHotplug = true;
3591
3592 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3593 return setError(VBOX_E_INVALID_VM_STATE,
3594 tr("Controller '%ls' does not support hotplugging"),
3595 aControllerName);
3596
3597 // check that the port and device are not out of range
3598 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3599 if (FAILED(rc)) return rc;
3600
3601 /* check if the device slot is already busy */
3602 MediumAttachment *pAttachTemp;
3603 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3604 aControllerName,
3605 aControllerPort,
3606 aDevice)))
3607 {
3608 Medium *pMedium = pAttachTemp->getMedium();
3609 if (pMedium)
3610 {
3611 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3612 return setError(VBOX_E_OBJECT_IN_USE,
3613 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3614 pMedium->getLocationFull().c_str(),
3615 aControllerPort,
3616 aDevice,
3617 aControllerName);
3618 }
3619 else
3620 return setError(VBOX_E_OBJECT_IN_USE,
3621 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3622 aControllerPort, aDevice, aControllerName);
3623 }
3624
3625 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3626 if (aMedium && medium.isNull())
3627 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3628
3629 AutoCaller mediumCaller(medium);
3630 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3631
3632 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3633
3634 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3635 && !medium.isNull()
3636 )
3637 return setError(VBOX_E_OBJECT_IN_USE,
3638 tr("Medium '%s' is already attached to this virtual machine"),
3639 medium->getLocationFull().c_str());
3640
3641 if (!medium.isNull())
3642 {
3643 MediumType_T mtype = medium->getType();
3644 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3645 // For DVDs it's not written to the config file, so needs no global config
3646 // version bump. For floppies it's a new attribute "type", which is ignored
3647 // by older VirtualBox version, so needs no global config version bump either.
3648 // For hard disks this type is not accepted.
3649 if (mtype == MediumType_MultiAttach)
3650 {
3651 // This type is new with VirtualBox 4.0 and therefore requires settings
3652 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3653 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3654 // two reasons: The medium type is a property of the media registry tree, which
3655 // can reside in the global config file (for pre-4.0 media); we would therefore
3656 // possibly need to bump the global config version. We don't want to do that though
3657 // because that might make downgrading to pre-4.0 impossible.
3658 // As a result, we can only use these two new types if the medium is NOT in the
3659 // global registry:
3660 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3661 if ( medium->isInRegistry(uuidGlobalRegistry)
3662 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3663 )
3664 return setError(VBOX_E_INVALID_OBJECT_STATE,
3665 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3666 "to machines that were created with VirtualBox 4.0 or later"),
3667 medium->getLocationFull().c_str());
3668 }
3669 }
3670
3671 bool fIndirect = false;
3672 if (!medium.isNull())
3673 fIndirect = medium->isReadOnly();
3674 bool associate = true;
3675
3676 do
3677 {
3678 if ( aType == DeviceType_HardDisk
3679 && mMediaData.isBackedUp())
3680 {
3681 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3682
3683 /* check if the medium was attached to the VM before we started
3684 * changing attachments in which case the attachment just needs to
3685 * be restored */
3686 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3687 {
3688 AssertReturn(!fIndirect, E_FAIL);
3689
3690 /* see if it's the same bus/channel/device */
3691 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3692 {
3693 /* the simplest case: restore the whole attachment
3694 * and return, nothing else to do */
3695 mMediaData->mAttachments.push_back(pAttachTemp);
3696 return S_OK;
3697 }
3698
3699 /* bus/channel/device differ; we need a new attachment object,
3700 * but don't try to associate it again */
3701 associate = false;
3702 break;
3703 }
3704 }
3705
3706 /* go further only if the attachment is to be indirect */
3707 if (!fIndirect)
3708 break;
3709
3710 /* perform the so called smart attachment logic for indirect
3711 * attachments. Note that smart attachment is only applicable to base
3712 * hard disks. */
3713
3714 if (medium->getParent().isNull())
3715 {
3716 /* first, investigate the backup copy of the current hard disk
3717 * attachments to make it possible to re-attach existing diffs to
3718 * another device slot w/o losing their contents */
3719 if (mMediaData.isBackedUp())
3720 {
3721 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3722
3723 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3724 uint32_t foundLevel = 0;
3725
3726 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3727 it != oldAtts.end();
3728 ++it)
3729 {
3730 uint32_t level = 0;
3731 MediumAttachment *pAttach = *it;
3732 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3733 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3734 if (pMedium.isNull())
3735 continue;
3736
3737 if (pMedium->getBase(&level) == medium)
3738 {
3739 /* skip the hard disk if its currently attached (we
3740 * cannot attach the same hard disk twice) */
3741 if (findAttachment(mMediaData->mAttachments,
3742 pMedium))
3743 continue;
3744
3745 /* matched device, channel and bus (i.e. attached to the
3746 * same place) will win and immediately stop the search;
3747 * otherwise the attachment that has the youngest
3748 * descendant of medium will be used
3749 */
3750 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3751 {
3752 /* the simplest case: restore the whole attachment
3753 * and return, nothing else to do */
3754 mMediaData->mAttachments.push_back(*it);
3755 return S_OK;
3756 }
3757 else if ( foundIt == oldAtts.end()
3758 || level > foundLevel /* prefer younger */
3759 )
3760 {
3761 foundIt = it;
3762 foundLevel = level;
3763 }
3764 }
3765 }
3766
3767 if (foundIt != oldAtts.end())
3768 {
3769 /* use the previously attached hard disk */
3770 medium = (*foundIt)->getMedium();
3771 mediumCaller.attach(medium);
3772 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3773 mediumLock.attach(medium);
3774 /* not implicit, doesn't require association with this VM */
3775 fIndirect = false;
3776 associate = false;
3777 /* go right to the MediumAttachment creation */
3778 break;
3779 }
3780 }
3781
3782 /* must give up the medium lock and medium tree lock as below we
3783 * go over snapshots, which needs a lock with higher lock order. */
3784 mediumLock.release();
3785 treeLock.release();
3786
3787 /* then, search through snapshots for the best diff in the given
3788 * hard disk's chain to base the new diff on */
3789
3790 ComObjPtr<Medium> base;
3791 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3792 while (snap)
3793 {
3794 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3795
3796 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3797
3798 MediumAttachment *pAttachFound = NULL;
3799 uint32_t foundLevel = 0;
3800
3801 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3802 it != snapAtts.end();
3803 ++it)
3804 {
3805 MediumAttachment *pAttach = *it;
3806 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3807 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3808 if (pMedium.isNull())
3809 continue;
3810
3811 uint32_t level = 0;
3812 if (pMedium->getBase(&level) == medium)
3813 {
3814 /* matched device, channel and bus (i.e. attached to the
3815 * same place) will win and immediately stop the search;
3816 * otherwise the attachment that has the youngest
3817 * descendant of medium will be used
3818 */
3819 if ( pAttach->getDevice() == aDevice
3820 && pAttach->getPort() == aControllerPort
3821 && pAttach->getControllerName() == aControllerName
3822 )
3823 {
3824 pAttachFound = pAttach;
3825 break;
3826 }
3827 else if ( !pAttachFound
3828 || level > foundLevel /* prefer younger */
3829 )
3830 {
3831 pAttachFound = pAttach;
3832 foundLevel = level;
3833 }
3834 }
3835 }
3836
3837 if (pAttachFound)
3838 {
3839 base = pAttachFound->getMedium();
3840 break;
3841 }
3842
3843 snap = snap->getParent();
3844 }
3845
3846 /* re-lock medium tree and the medium, as we need it below */
3847 treeLock.acquire();
3848 mediumLock.acquire();
3849
3850 /* found a suitable diff, use it as a base */
3851 if (!base.isNull())
3852 {
3853 medium = base;
3854 mediumCaller.attach(medium);
3855 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3856 mediumLock.attach(medium);
3857 }
3858 }
3859
3860 Utf8Str strFullSnapshotFolder;
3861 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3862
3863 ComObjPtr<Medium> diff;
3864 diff.createObject();
3865 // store this diff in the same registry as the parent
3866 Guid uuidRegistryParent;
3867 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
3868 {
3869 // parent image has no registry: this can happen if we're attaching a new immutable
3870 // image that has not yet been attached (medium then points to the base and we're
3871 // creating the diff image for the immutable, and the parent is not yet registered);
3872 // put the parent in the machine registry then
3873 mediumLock.release();
3874 treeLock.release();
3875 alock.release();
3876 addMediumToRegistry(medium);
3877 alock.acquire();
3878 treeLock.acquire();
3879 mediumLock.acquire();
3880 medium->getFirstRegistryMachineId(uuidRegistryParent);
3881 }
3882 rc = diff->init(mParent,
3883 medium->getPreferredDiffFormat(),
3884 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3885 uuidRegistryParent);
3886 if (FAILED(rc)) return rc;
3887
3888 /* Apply the normal locking logic to the entire chain. */
3889 MediumLockList *pMediumLockList(new MediumLockList());
3890 mediumLock.release();
3891 treeLock.release();
3892 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
3893 true /* fMediumLockWrite */,
3894 medium,
3895 *pMediumLockList);
3896 treeLock.acquire();
3897 mediumLock.acquire();
3898 if (SUCCEEDED(rc))
3899 {
3900 mediumLock.release();
3901 treeLock.release();
3902 rc = pMediumLockList->Lock();
3903 treeLock.acquire();
3904 mediumLock.acquire();
3905 if (FAILED(rc))
3906 setError(rc,
3907 tr("Could not lock medium when creating diff '%s'"),
3908 diff->getLocationFull().c_str());
3909 else
3910 {
3911 /* will release the lock before the potentially lengthy
3912 * operation, so protect with the special state */
3913 MachineState_T oldState = mData->mMachineState;
3914 setMachineState(MachineState_SettingUp);
3915
3916 mediumLock.release();
3917 treeLock.release();
3918 alock.release();
3919
3920 rc = medium->createDiffStorage(diff,
3921 MediumVariant_Standard,
3922 pMediumLockList,
3923 NULL /* aProgress */,
3924 true /* aWait */);
3925
3926 alock.acquire();
3927 treeLock.acquire();
3928 mediumLock.acquire();
3929
3930 setMachineState(oldState);
3931 }
3932 }
3933
3934 /* Unlock the media and free the associated memory. */
3935 delete pMediumLockList;
3936
3937 if (FAILED(rc)) return rc;
3938
3939 /* use the created diff for the actual attachment */
3940 medium = diff;
3941 mediumCaller.attach(medium);
3942 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3943 mediumLock.attach(medium);
3944 }
3945 while (0);
3946
3947 ComObjPtr<MediumAttachment> attachment;
3948 attachment.createObject();
3949 rc = attachment->init(this,
3950 medium,
3951 aControllerName,
3952 aControllerPort,
3953 aDevice,
3954 aType,
3955 fIndirect,
3956 false /* fPassthrough */,
3957 false /* fTempEject */,
3958 false /* fNonRotational */,
3959 false /* fDiscard */,
3960 Utf8Str::Empty);
3961 if (FAILED(rc)) return rc;
3962
3963 if (associate && !medium.isNull())
3964 {
3965 // as the last step, associate the medium to the VM
3966 rc = medium->addBackReference(mData->mUuid);
3967 // here we can fail because of Deleting, or being in process of creating a Diff
3968 if (FAILED(rc)) return rc;
3969
3970 mediumLock.release();
3971 treeLock.release();
3972 alock.release();
3973 addMediumToRegistry(medium);
3974 alock.acquire();
3975 treeLock.acquire();
3976 mediumLock.acquire();
3977 }
3978
3979 /* success: finally remember the attachment */
3980 setModified(IsModified_Storage);
3981 mMediaData.backup();
3982 mMediaData->mAttachments.push_back(attachment);
3983
3984 mediumLock.release();
3985 treeLock.release();
3986 alock.release();
3987
3988 if (fHotplug)
3989 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */);
3990
3991 mParent->saveModifiedRegistries();
3992
3993 return rc;
3994}
3995
3996STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
3997 LONG aDevice)
3998{
3999 CheckComArgStrNotEmptyOrNull(aControllerName);
4000
4001 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4002 aControllerName, aControllerPort, aDevice));
4003
4004 AutoCaller autoCaller(this);
4005 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4006
4007 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4008
4009 HRESULT rc = checkStateDependency(MutableStateDep);
4010 if (FAILED(rc)) return rc;
4011
4012 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4013
4014 /* Check for an existing controller. */
4015 ComObjPtr<StorageController> ctl;
4016 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4017 if (FAILED(rc)) return rc;
4018
4019 StorageControllerType_T ctrlType;
4020 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4021 if (FAILED(rc))
4022 return setError(E_FAIL,
4023 tr("Could not get type of controller '%ls'"),
4024 aControllerName);
4025
4026 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4027 bool fHotplug = false;
4028 if (Global::IsOnlineOrTransient(mData->mMachineState))
4029 fHotplug = true;
4030
4031 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4032 return setError(VBOX_E_INVALID_VM_STATE,
4033 tr("Controller '%ls' does not support hotplugging"),
4034 aControllerName);
4035
4036 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4037 aControllerName,
4038 aControllerPort,
4039 aDevice);
4040 if (!pAttach)
4041 return setError(VBOX_E_OBJECT_NOT_FOUND,
4042 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4043 aDevice, aControllerPort, aControllerName);
4044
4045 /*
4046 * The VM has to detach the device before we delete any implicit diffs.
4047 * If this fails we can roll back without loosing data.
4048 */
4049 if (fHotplug)
4050 {
4051 alock.release();
4052 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */);
4053 alock.acquire();
4054 }
4055 if (FAILED(rc)) return rc;
4056
4057 /* If we are here everything went well and we can delete the implicit now. */
4058 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4059
4060 alock.release();
4061
4062 mParent->saveModifiedRegistries();
4063
4064 return rc;
4065}
4066
4067STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4068 LONG aDevice, BOOL aPassthrough)
4069{
4070 CheckComArgStrNotEmptyOrNull(aControllerName);
4071
4072 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4073 aControllerName, aControllerPort, aDevice, aPassthrough));
4074
4075 AutoCaller autoCaller(this);
4076 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4077
4078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4079
4080 HRESULT rc = checkStateDependency(MutableStateDep);
4081 if (FAILED(rc)) return rc;
4082
4083 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4084
4085 if (Global::IsOnlineOrTransient(mData->mMachineState))
4086 return setError(VBOX_E_INVALID_VM_STATE,
4087 tr("Invalid machine state: %s"),
4088 Global::stringifyMachineState(mData->mMachineState));
4089
4090 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4091 aControllerName,
4092 aControllerPort,
4093 aDevice);
4094 if (!pAttach)
4095 return setError(VBOX_E_OBJECT_NOT_FOUND,
4096 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4097 aDevice, aControllerPort, aControllerName);
4098
4099
4100 setModified(IsModified_Storage);
4101 mMediaData.backup();
4102
4103 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4104
4105 if (pAttach->getType() != DeviceType_DVD)
4106 return setError(E_INVALIDARG,
4107 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4108 aDevice, aControllerPort, aControllerName);
4109 pAttach->updatePassthrough(!!aPassthrough);
4110
4111 return S_OK;
4112}
4113
4114STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4115 LONG aDevice, BOOL aTemporaryEject)
4116{
4117 CheckComArgStrNotEmptyOrNull(aControllerName);
4118
4119 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4120 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4121
4122 AutoCaller autoCaller(this);
4123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4124
4125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4126
4127 HRESULT rc = checkStateDependency(MutableStateDep);
4128 if (FAILED(rc)) return rc;
4129
4130 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4131 aControllerName,
4132 aControllerPort,
4133 aDevice);
4134 if (!pAttach)
4135 return setError(VBOX_E_OBJECT_NOT_FOUND,
4136 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4137 aDevice, aControllerPort, aControllerName);
4138
4139
4140 setModified(IsModified_Storage);
4141 mMediaData.backup();
4142
4143 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4144
4145 if (pAttach->getType() != DeviceType_DVD)
4146 return setError(E_INVALIDARG,
4147 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4148 aDevice, aControllerPort, aControllerName);
4149 pAttach->updateTempEject(!!aTemporaryEject);
4150
4151 return S_OK;
4152}
4153
4154STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4155 LONG aDevice, BOOL aNonRotational)
4156{
4157 CheckComArgStrNotEmptyOrNull(aControllerName);
4158
4159 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4160 aControllerName, aControllerPort, aDevice, aNonRotational));
4161
4162 AutoCaller autoCaller(this);
4163 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4164
4165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4166
4167 HRESULT rc = checkStateDependency(MutableStateDep);
4168 if (FAILED(rc)) return rc;
4169
4170 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4171
4172 if (Global::IsOnlineOrTransient(mData->mMachineState))
4173 return setError(VBOX_E_INVALID_VM_STATE,
4174 tr("Invalid machine state: %s"),
4175 Global::stringifyMachineState(mData->mMachineState));
4176
4177 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4178 aControllerName,
4179 aControllerPort,
4180 aDevice);
4181 if (!pAttach)
4182 return setError(VBOX_E_OBJECT_NOT_FOUND,
4183 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4184 aDevice, aControllerPort, aControllerName);
4185
4186
4187 setModified(IsModified_Storage);
4188 mMediaData.backup();
4189
4190 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4191
4192 if (pAttach->getType() != DeviceType_HardDisk)
4193 return setError(E_INVALIDARG,
4194 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4195 aDevice, aControllerPort, aControllerName);
4196 pAttach->updateNonRotational(!!aNonRotational);
4197
4198 return S_OK;
4199}
4200
4201STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4202 LONG aDevice, BOOL aDiscard)
4203{
4204 CheckComArgStrNotEmptyOrNull(aControllerName);
4205
4206 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4207 aControllerName, aControllerPort, aDevice, aDiscard));
4208
4209 AutoCaller autoCaller(this);
4210 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4211
4212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4213
4214 HRESULT rc = checkStateDependency(MutableStateDep);
4215 if (FAILED(rc)) return rc;
4216
4217 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4218
4219 if (Global::IsOnlineOrTransient(mData->mMachineState))
4220 return setError(VBOX_E_INVALID_VM_STATE,
4221 tr("Invalid machine state: %s"),
4222 Global::stringifyMachineState(mData->mMachineState));
4223
4224 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4225 aControllerName,
4226 aControllerPort,
4227 aDevice);
4228 if (!pAttach)
4229 return setError(VBOX_E_OBJECT_NOT_FOUND,
4230 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4231 aDevice, aControllerPort, aControllerName);
4232
4233
4234 setModified(IsModified_Storage);
4235 mMediaData.backup();
4236
4237 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4238
4239 if (pAttach->getType() != DeviceType_HardDisk)
4240 return setError(E_INVALIDARG,
4241 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4242 aDevice, aControllerPort, aControllerName);
4243 pAttach->updateDiscard(!!aDiscard);
4244
4245 return S_OK;
4246}
4247
4248STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4249 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4250{
4251 CheckComArgStrNotEmptyOrNull(aControllerName);
4252
4253 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4254 aControllerName, aControllerPort, aDevice));
4255
4256 AutoCaller autoCaller(this);
4257 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4258
4259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4260
4261 HRESULT rc = checkStateDependency(MutableStateDep);
4262 if (FAILED(rc)) return rc;
4263
4264 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4265
4266 if (Global::IsOnlineOrTransient(mData->mMachineState))
4267 return setError(VBOX_E_INVALID_VM_STATE,
4268 tr("Invalid machine state: %s"),
4269 Global::stringifyMachineState(mData->mMachineState));
4270
4271 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4272 aControllerName,
4273 aControllerPort,
4274 aDevice);
4275 if (!pAttach)
4276 return setError(VBOX_E_OBJECT_NOT_FOUND,
4277 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4278 aDevice, aControllerPort, aControllerName);
4279
4280
4281 setModified(IsModified_Storage);
4282 mMediaData.backup();
4283
4284 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4285 if (aBandwidthGroup && group.isNull())
4286 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4287
4288 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4289
4290 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4291 if (strBandwidthGroupOld.isNotEmpty())
4292 {
4293 /* Get the bandwidth group object and release it - this must not fail. */
4294 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4295 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4296 Assert(SUCCEEDED(rc));
4297
4298 pBandwidthGroupOld->release();
4299 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4300 }
4301
4302 if (!group.isNull())
4303 {
4304 group->reference();
4305 pAttach->updateBandwidthGroup(group->getName());
4306 }
4307
4308 return S_OK;
4309}
4310
4311
4312STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4313 LONG aControllerPort,
4314 LONG aDevice,
4315 IMedium *aMedium,
4316 BOOL aForce)
4317{
4318 int rc = S_OK;
4319 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4320 aControllerName, aControllerPort, aDevice, aForce));
4321
4322 CheckComArgStrNotEmptyOrNull(aControllerName);
4323
4324 AutoCaller autoCaller(this);
4325 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4326
4327 // request the host lock first, since might be calling Host methods for getting host drives;
4328 // next, protect the media tree all the while we're in here, as well as our member variables
4329 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4330 this->lockHandle(),
4331 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4332
4333 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4334 aControllerName,
4335 aControllerPort,
4336 aDevice);
4337 if (pAttach.isNull())
4338 return setError(VBOX_E_OBJECT_NOT_FOUND,
4339 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4340 aDevice, aControllerPort, aControllerName);
4341
4342 /* Remember previously mounted medium. The medium before taking the
4343 * backup is not necessarily the same thing. */
4344 ComObjPtr<Medium> oldmedium;
4345 oldmedium = pAttach->getMedium();
4346
4347 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4348 if (aMedium && pMedium.isNull())
4349 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4350
4351 AutoCaller mediumCaller(pMedium);
4352 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4353
4354 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4355 if (pMedium)
4356 {
4357 DeviceType_T mediumType = pAttach->getType();
4358 switch (mediumType)
4359 {
4360 case DeviceType_DVD:
4361 case DeviceType_Floppy:
4362 break;
4363
4364 default:
4365 return setError(VBOX_E_INVALID_OBJECT_STATE,
4366 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4367 aControllerPort,
4368 aDevice,
4369 aControllerName);
4370 }
4371 }
4372
4373 setModified(IsModified_Storage);
4374 mMediaData.backup();
4375
4376 {
4377 // The backup operation makes the pAttach reference point to the
4378 // old settings. Re-get the correct reference.
4379 pAttach = findAttachment(mMediaData->mAttachments,
4380 aControllerName,
4381 aControllerPort,
4382 aDevice);
4383 if (!oldmedium.isNull())
4384 oldmedium->removeBackReference(mData->mUuid);
4385 if (!pMedium.isNull())
4386 {
4387 pMedium->addBackReference(mData->mUuid);
4388
4389 mediumLock.release();
4390 multiLock.release();
4391 addMediumToRegistry(pMedium);
4392 multiLock.acquire();
4393 mediumLock.acquire();
4394 }
4395
4396 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4397 pAttach->updateMedium(pMedium);
4398 }
4399
4400 setModified(IsModified_Storage);
4401
4402 mediumLock.release();
4403 multiLock.release();
4404 rc = onMediumChange(pAttach, aForce);
4405 multiLock.acquire();
4406 mediumLock.acquire();
4407
4408 /* On error roll back this change only. */
4409 if (FAILED(rc))
4410 {
4411 if (!pMedium.isNull())
4412 pMedium->removeBackReference(mData->mUuid);
4413 pAttach = findAttachment(mMediaData->mAttachments,
4414 aControllerName,
4415 aControllerPort,
4416 aDevice);
4417 /* If the attachment is gone in the meantime, bail out. */
4418 if (pAttach.isNull())
4419 return rc;
4420 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4421 if (!oldmedium.isNull())
4422 oldmedium->addBackReference(mData->mUuid);
4423 pAttach->updateMedium(oldmedium);
4424 }
4425
4426 mediumLock.release();
4427 multiLock.release();
4428
4429 mParent->saveModifiedRegistries();
4430
4431 return rc;
4432}
4433
4434STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4435 LONG aControllerPort,
4436 LONG aDevice,
4437 IMedium **aMedium)
4438{
4439 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4440 aControllerName, aControllerPort, aDevice));
4441
4442 CheckComArgStrNotEmptyOrNull(aControllerName);
4443 CheckComArgOutPointerValid(aMedium);
4444
4445 AutoCaller autoCaller(this);
4446 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4447
4448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4449
4450 *aMedium = NULL;
4451
4452 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4453 aControllerName,
4454 aControllerPort,
4455 aDevice);
4456 if (pAttach.isNull())
4457 return setError(VBOX_E_OBJECT_NOT_FOUND,
4458 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4459 aDevice, aControllerPort, aControllerName);
4460
4461 pAttach->getMedium().queryInterfaceTo(aMedium);
4462
4463 return S_OK;
4464}
4465
4466STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4467{
4468 CheckComArgOutPointerValid(port);
4469 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4470
4471 AutoCaller autoCaller(this);
4472 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4473
4474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4475
4476 mSerialPorts[slot].queryInterfaceTo(port);
4477
4478 return S_OK;
4479}
4480
4481STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4482{
4483 CheckComArgOutPointerValid(port);
4484 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4485
4486 AutoCaller autoCaller(this);
4487 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4488
4489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4490
4491 mParallelPorts[slot].queryInterfaceTo(port);
4492
4493 return S_OK;
4494}
4495
4496STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4497{
4498 CheckComArgOutPointerValid(adapter);
4499 CheckComArgExpr(slot, slot < mNetworkAdapters.size());
4500
4501 AutoCaller autoCaller(this);
4502 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4503
4504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4505
4506 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4507
4508 return S_OK;
4509}
4510
4511STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4512{
4513 CheckComArgOutSafeArrayPointerValid(aKeys);
4514
4515 AutoCaller autoCaller(this);
4516 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4517
4518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4519
4520 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4521 int i = 0;
4522 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4523 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4524 ++it, ++i)
4525 {
4526 const Utf8Str &strKey = it->first;
4527 strKey.cloneTo(&saKeys[i]);
4528 }
4529 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4530
4531 return S_OK;
4532 }
4533
4534 /**
4535 * @note Locks this object for reading.
4536 */
4537STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4538 BSTR *aValue)
4539{
4540 CheckComArgStrNotEmptyOrNull(aKey);
4541 CheckComArgOutPointerValid(aValue);
4542
4543 AutoCaller autoCaller(this);
4544 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4545
4546 /* start with nothing found */
4547 Bstr bstrResult("");
4548
4549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4550
4551 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4552 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4553 // found:
4554 bstrResult = it->second; // source is a Utf8Str
4555
4556 /* return the result to caller (may be empty) */
4557 bstrResult.cloneTo(aValue);
4558
4559 return S_OK;
4560}
4561
4562 /**
4563 * @note Locks mParent for writing + this object for writing.
4564 */
4565STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4566{
4567 CheckComArgStrNotEmptyOrNull(aKey);
4568
4569 AutoCaller autoCaller(this);
4570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4571
4572 Utf8Str strKey(aKey);
4573 Utf8Str strValue(aValue);
4574 Utf8Str strOldValue; // empty
4575
4576 // locking note: we only hold the read lock briefly to look up the old value,
4577 // then release it and call the onExtraCanChange callbacks. There is a small
4578 // chance of a race insofar as the callback might be called twice if two callers
4579 // change the same key at the same time, but that's a much better solution
4580 // than the deadlock we had here before. The actual changing of the extradata
4581 // is then performed under the write lock and race-free.
4582
4583 // look up the old value first; if nothing has changed then we need not do anything
4584 {
4585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4586 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4587 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4588 strOldValue = it->second;
4589 }
4590
4591 bool fChanged;
4592 if ((fChanged = (strOldValue != strValue)))
4593 {
4594 // ask for permission from all listeners outside the locks;
4595 // onExtraDataCanChange() only briefly requests the VirtualBox
4596 // lock to copy the list of callbacks to invoke
4597 Bstr error;
4598 Bstr bstrValue(aValue);
4599
4600 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4601 {
4602 const char *sep = error.isEmpty() ? "" : ": ";
4603 CBSTR err = error.raw();
4604 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4605 sep, err));
4606 return setError(E_ACCESSDENIED,
4607 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4608 aKey,
4609 bstrValue.raw(),
4610 sep,
4611 err);
4612 }
4613
4614 // data is changing and change not vetoed: then write it out under the lock
4615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4616
4617 if (isSnapshotMachine())
4618 {
4619 HRESULT rc = checkStateDependency(MutableStateDep);
4620 if (FAILED(rc)) return rc;
4621 }
4622
4623 if (strValue.isEmpty())
4624 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4625 else
4626 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4627 // creates a new key if needed
4628
4629 bool fNeedsGlobalSaveSettings = false;
4630 saveSettings(&fNeedsGlobalSaveSettings);
4631
4632 if (fNeedsGlobalSaveSettings)
4633 {
4634 // save the global settings; for that we should hold only the VirtualBox lock
4635 alock.release();
4636 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4637 mParent->saveSettings();
4638 }
4639 }
4640
4641 // fire notification outside the lock
4642 if (fChanged)
4643 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4644
4645 return S_OK;
4646}
4647
4648STDMETHODIMP Machine::SaveSettings()
4649{
4650 AutoCaller autoCaller(this);
4651 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4652
4653 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4654
4655 /* when there was auto-conversion, we want to save the file even if
4656 * the VM is saved */
4657 HRESULT rc = checkStateDependency(MutableStateDep);
4658 if (FAILED(rc)) return rc;
4659
4660 /* the settings file path may never be null */
4661 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4662
4663 /* save all VM data excluding snapshots */
4664 bool fNeedsGlobalSaveSettings = false;
4665 rc = saveSettings(&fNeedsGlobalSaveSettings);
4666 mlock.release();
4667
4668 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4669 {
4670 // save the global settings; for that we should hold only the VirtualBox lock
4671 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4672 rc = mParent->saveSettings();
4673 }
4674
4675 return rc;
4676}
4677
4678STDMETHODIMP Machine::DiscardSettings()
4679{
4680 AutoCaller autoCaller(this);
4681 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4682
4683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4684
4685 HRESULT rc = checkStateDependency(MutableStateDep);
4686 if (FAILED(rc)) return rc;
4687
4688 /*
4689 * during this rollback, the session will be notified if data has
4690 * been actually changed
4691 */
4692 rollback(true /* aNotify */);
4693
4694 return S_OK;
4695}
4696
4697/** @note Locks objects! */
4698STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4699 ComSafeArrayOut(IMedium*, aMedia))
4700{
4701 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4702 AutoLimitedCaller autoCaller(this);
4703 AssertComRCReturnRC(autoCaller.rc());
4704
4705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4706
4707 Guid id(getId());
4708
4709 if (mData->mSession.mState != SessionState_Unlocked)
4710 return setError(VBOX_E_INVALID_OBJECT_STATE,
4711 tr("Cannot unregister the machine '%s' while it is locked"),
4712 mUserData->s.strName.c_str());
4713
4714 // wait for state dependents to drop to zero
4715 ensureNoStateDependencies();
4716
4717 if (!mData->mAccessible)
4718 {
4719 // inaccessible maschines can only be unregistered; uninitialize ourselves
4720 // here because currently there may be no unregistered that are inaccessible
4721 // (this state combination is not supported). Note releasing the caller and
4722 // leaving the lock before calling uninit()
4723 alock.release();
4724 autoCaller.release();
4725
4726 uninit();
4727
4728 mParent->unregisterMachine(this, id);
4729 // calls VirtualBox::saveSettings()
4730
4731 return S_OK;
4732 }
4733
4734 HRESULT rc = S_OK;
4735
4736 // discard saved state
4737 if (mData->mMachineState == MachineState_Saved)
4738 {
4739 // add the saved state file to the list of files the caller should delete
4740 Assert(!mSSData->strStateFilePath.isEmpty());
4741 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4742
4743 mSSData->strStateFilePath.setNull();
4744
4745 // unconditionally set the machine state to powered off, we now
4746 // know no session has locked the machine
4747 mData->mMachineState = MachineState_PoweredOff;
4748 }
4749
4750 size_t cSnapshots = 0;
4751 if (mData->mFirstSnapshot)
4752 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4753 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4754 // fail now before we start detaching media
4755 return setError(VBOX_E_INVALID_OBJECT_STATE,
4756 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4757 mUserData->s.strName.c_str(), cSnapshots);
4758
4759 // This list collects the medium objects from all medium attachments
4760 // which we will detach from the machine and its snapshots, in a specific
4761 // order which allows for closing all media without getting "media in use"
4762 // errors, simply by going through the list from the front to the back:
4763 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4764 // and must be closed before the parent media from the snapshots, or closing the parents
4765 // will fail because they still have children);
4766 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4767 // the root ("first") snapshot of the machine.
4768 MediaList llMedia;
4769
4770 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4771 && mMediaData->mAttachments.size()
4772 )
4773 {
4774 // we have media attachments: detach them all and add the Medium objects to our list
4775 if (cleanupMode != CleanupMode_UnregisterOnly)
4776 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
4777 else
4778 return setError(VBOX_E_INVALID_OBJECT_STATE,
4779 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4780 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4781 }
4782
4783 if (cSnapshots)
4784 {
4785 // autoCleanup must be true here, or we would have failed above
4786
4787 // add the media from the medium attachments of the snapshots to llMedia
4788 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4789 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4790 // into the children first
4791
4792 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4793 MachineState_T oldState = mData->mMachineState;
4794 mData->mMachineState = MachineState_DeletingSnapshot;
4795
4796 // make a copy of the first snapshot so the refcount does not drop to 0
4797 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4798 // because of the AutoCaller voodoo)
4799 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4800
4801 // GO!
4802 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
4803
4804 mData->mMachineState = oldState;
4805 }
4806
4807 if (FAILED(rc))
4808 {
4809 rollbackMedia();
4810 return rc;
4811 }
4812
4813 // commit all the media changes made above
4814 commitMedia();
4815
4816 mData->mRegistered = false;
4817
4818 // machine lock no longer needed
4819 alock.release();
4820
4821 // return media to caller
4822 SafeIfaceArray<IMedium> sfaMedia(llMedia);
4823 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
4824
4825 mParent->unregisterMachine(this, id);
4826 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
4827
4828 return S_OK;
4829}
4830
4831struct Machine::DeleteTask
4832{
4833 ComObjPtr<Machine> pMachine;
4834 RTCList<ComPtr<IMedium> > llMediums;
4835 StringsList llFilesToDelete;
4836 ComObjPtr<Progress> pProgress;
4837};
4838
4839STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
4840{
4841 LogFlowFuncEnter();
4842
4843 AutoCaller autoCaller(this);
4844 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4845
4846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4847
4848 HRESULT rc = checkStateDependency(MutableStateDep);
4849 if (FAILED(rc)) return rc;
4850
4851 if (mData->mRegistered)
4852 return setError(VBOX_E_INVALID_VM_STATE,
4853 tr("Cannot delete settings of a registered machine"));
4854
4855 DeleteTask *pTask = new DeleteTask;
4856 pTask->pMachine = this;
4857 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
4858
4859 // collect files to delete
4860 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
4861
4862 for (size_t i = 0; i < sfaMedia.size(); ++i)
4863 {
4864 IMedium *pIMedium(sfaMedia[i]);
4865 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
4866 if (pMedium.isNull())
4867 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
4868 SafeArray<BSTR> ids;
4869 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
4870 if (FAILED(rc)) return rc;
4871 /* At this point the medium should not have any back references
4872 * anymore. If it has it is attached to another VM and *must* not
4873 * deleted. */
4874 if (ids.size() < 1)
4875 pTask->llMediums.append(pMedium);
4876 }
4877 if (mData->pMachineConfigFile->fileExists())
4878 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
4879
4880 pTask->pProgress.createObject();
4881 pTask->pProgress->init(getVirtualBox(),
4882 static_cast<IMachine*>(this) /* aInitiator */,
4883 Bstr(tr("Deleting files")).raw(),
4884 true /* fCancellable */,
4885 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
4886 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
4887
4888 int vrc = RTThreadCreate(NULL,
4889 Machine::deleteThread,
4890 (void*)pTask,
4891 0,
4892 RTTHREADTYPE_MAIN_WORKER,
4893 0,
4894 "MachineDelete");
4895
4896 pTask->pProgress.queryInterfaceTo(aProgress);
4897
4898 if (RT_FAILURE(vrc))
4899 {
4900 delete pTask;
4901 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
4902 }
4903
4904 LogFlowFuncLeave();
4905
4906 return S_OK;
4907}
4908
4909/**
4910 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
4911 * calls Machine::deleteTaskWorker() on the actual machine object.
4912 * @param Thread
4913 * @param pvUser
4914 * @return
4915 */
4916/*static*/
4917DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
4918{
4919 LogFlowFuncEnter();
4920
4921 DeleteTask *pTask = (DeleteTask*)pvUser;
4922 Assert(pTask);
4923 Assert(pTask->pMachine);
4924 Assert(pTask->pProgress);
4925
4926 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
4927 pTask->pProgress->notifyComplete(rc);
4928
4929 delete pTask;
4930
4931 LogFlowFuncLeave();
4932
4933 NOREF(Thread);
4934
4935 return VINF_SUCCESS;
4936}
4937
4938/**
4939 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
4940 * @param task
4941 * @return
4942 */
4943HRESULT Machine::deleteTaskWorker(DeleteTask &task)
4944{
4945 AutoCaller autoCaller(this);
4946 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4947
4948 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4949
4950 HRESULT rc = S_OK;
4951
4952 try
4953 {
4954 ULONG uLogHistoryCount = 3;
4955 ComPtr<ISystemProperties> systemProperties;
4956 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
4957 if (FAILED(rc)) throw rc;
4958
4959 if (!systemProperties.isNull())
4960 {
4961 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
4962 if (FAILED(rc)) throw rc;
4963 }
4964
4965 MachineState_T oldState = mData->mMachineState;
4966 setMachineState(MachineState_SettingUp);
4967 alock.release();
4968 for (size_t i = 0; i < task.llMediums.size(); ++i)
4969 {
4970 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
4971 {
4972 AutoCaller mac(pMedium);
4973 if (FAILED(mac.rc())) throw mac.rc();
4974 Utf8Str strLocation = pMedium->getLocationFull();
4975 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
4976 if (FAILED(rc)) throw rc;
4977 LogFunc(("Deleting file %s\n", strLocation.c_str()));
4978 }
4979 ComPtr<IProgress> pProgress2;
4980 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
4981 if (FAILED(rc)) throw rc;
4982 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
4983 if (FAILED(rc)) throw rc;
4984 /* Check the result of the asynchrony process. */
4985 LONG iRc;
4986 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
4987 if (FAILED(rc)) throw rc;
4988 /* If the thread of the progress object has an error, then
4989 * retrieve the error info from there, or it'll be lost. */
4990 if (FAILED(iRc))
4991 throw setError(ProgressErrorInfo(pProgress2));
4992 }
4993 setMachineState(oldState);
4994 alock.acquire();
4995
4996 // delete the files pushed on the task list by Machine::Delete()
4997 // (this includes saved states of the machine and snapshots and
4998 // medium storage files from the IMedium list passed in, and the
4999 // machine XML file)
5000 StringsList::const_iterator it = task.llFilesToDelete.begin();
5001 while (it != task.llFilesToDelete.end())
5002 {
5003 const Utf8Str &strFile = *it;
5004 LogFunc(("Deleting file %s\n", strFile.c_str()));
5005 int vrc = RTFileDelete(strFile.c_str());
5006 if (RT_FAILURE(vrc))
5007 throw setError(VBOX_E_IPRT_ERROR,
5008 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5009
5010 ++it;
5011 if (it == task.llFilesToDelete.end())
5012 {
5013 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5014 if (FAILED(rc)) throw rc;
5015 break;
5016 }
5017
5018 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5019 if (FAILED(rc)) throw rc;
5020 }
5021
5022 /* delete the settings only when the file actually exists */
5023 if (mData->pMachineConfigFile->fileExists())
5024 {
5025 /* Delete any backup or uncommitted XML files. Ignore failures.
5026 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5027 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5028 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5029 RTFileDelete(otherXml.c_str());
5030 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5031 RTFileDelete(otherXml.c_str());
5032
5033 /* delete the Logs folder, nothing important should be left
5034 * there (we don't check for errors because the user might have
5035 * some private files there that we don't want to delete) */
5036 Utf8Str logFolder;
5037 getLogFolder(logFolder);
5038 Assert(logFolder.length());
5039 if (RTDirExists(logFolder.c_str()))
5040 {
5041 /* Delete all VBox.log[.N] files from the Logs folder
5042 * (this must be in sync with the rotation logic in
5043 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5044 * files that may have been created by the GUI. */
5045 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5046 logFolder.c_str(), RTPATH_DELIMITER);
5047 RTFileDelete(log.c_str());
5048 log = Utf8StrFmt("%s%cVBox.png",
5049 logFolder.c_str(), RTPATH_DELIMITER);
5050 RTFileDelete(log.c_str());
5051 for (int i = uLogHistoryCount; i > 0; i--)
5052 {
5053 log = Utf8StrFmt("%s%cVBox.log.%d",
5054 logFolder.c_str(), RTPATH_DELIMITER, i);
5055 RTFileDelete(log.c_str());
5056 log = Utf8StrFmt("%s%cVBox.png.%d",
5057 logFolder.c_str(), RTPATH_DELIMITER, i);
5058 RTFileDelete(log.c_str());
5059 }
5060
5061 RTDirRemove(logFolder.c_str());
5062 }
5063
5064 /* delete the Snapshots folder, nothing important should be left
5065 * there (we don't check for errors because the user might have
5066 * some private files there that we don't want to delete) */
5067 Utf8Str strFullSnapshotFolder;
5068 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5069 Assert(!strFullSnapshotFolder.isEmpty());
5070 if (RTDirExists(strFullSnapshotFolder.c_str()))
5071 RTDirRemove(strFullSnapshotFolder.c_str());
5072
5073 // delete the directory that contains the settings file, but only
5074 // if it matches the VM name
5075 Utf8Str settingsDir;
5076 if (isInOwnDir(&settingsDir))
5077 RTDirRemove(settingsDir.c_str());
5078 }
5079
5080 alock.release();
5081
5082 mParent->saveModifiedRegistries();
5083 }
5084 catch (HRESULT aRC) { rc = aRC; }
5085
5086 return rc;
5087}
5088
5089STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5090{
5091 CheckComArgOutPointerValid(aSnapshot);
5092
5093 AutoCaller autoCaller(this);
5094 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5095
5096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5097
5098 ComObjPtr<Snapshot> pSnapshot;
5099 HRESULT rc;
5100
5101 if (!aNameOrId || !*aNameOrId)
5102 // null case (caller wants root snapshot): findSnapshotById() handles this
5103 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5104 else
5105 {
5106 Guid uuid(aNameOrId);
5107 if (!uuid.isEmpty())
5108 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5109 else
5110 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5111 }
5112 pSnapshot.queryInterfaceTo(aSnapshot);
5113
5114 return rc;
5115}
5116
5117STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5118{
5119 CheckComArgStrNotEmptyOrNull(aName);
5120 CheckComArgStrNotEmptyOrNull(aHostPath);
5121
5122 AutoCaller autoCaller(this);
5123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5124
5125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5126
5127 HRESULT rc = checkStateDependency(MutableStateDep);
5128 if (FAILED(rc)) return rc;
5129
5130 Utf8Str strName(aName);
5131
5132 ComObjPtr<SharedFolder> sharedFolder;
5133 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5134 if (SUCCEEDED(rc))
5135 return setError(VBOX_E_OBJECT_IN_USE,
5136 tr("Shared folder named '%s' already exists"),
5137 strName.c_str());
5138
5139 sharedFolder.createObject();
5140 rc = sharedFolder->init(getMachine(),
5141 strName,
5142 aHostPath,
5143 !!aWritable,
5144 !!aAutoMount,
5145 true /* fFailOnError */);
5146 if (FAILED(rc)) return rc;
5147
5148 setModified(IsModified_SharedFolders);
5149 mHWData.backup();
5150 mHWData->mSharedFolders.push_back(sharedFolder);
5151
5152 /* inform the direct session if any */
5153 alock.release();
5154 onSharedFolderChange();
5155
5156 return S_OK;
5157}
5158
5159STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5160{
5161 CheckComArgStrNotEmptyOrNull(aName);
5162
5163 AutoCaller autoCaller(this);
5164 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5165
5166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5167
5168 HRESULT rc = checkStateDependency(MutableStateDep);
5169 if (FAILED(rc)) return rc;
5170
5171 ComObjPtr<SharedFolder> sharedFolder;
5172 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5173 if (FAILED(rc)) return rc;
5174
5175 setModified(IsModified_SharedFolders);
5176 mHWData.backup();
5177 mHWData->mSharedFolders.remove(sharedFolder);
5178
5179 /* inform the direct session if any */
5180 alock.release();
5181 onSharedFolderChange();
5182
5183 return S_OK;
5184}
5185
5186STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5187{
5188 CheckComArgOutPointerValid(aCanShow);
5189
5190 /* start with No */
5191 *aCanShow = FALSE;
5192
5193 AutoCaller autoCaller(this);
5194 AssertComRCReturnRC(autoCaller.rc());
5195
5196 ComPtr<IInternalSessionControl> directControl;
5197 {
5198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5199
5200 if (mData->mSession.mState != SessionState_Locked)
5201 return setError(VBOX_E_INVALID_VM_STATE,
5202 tr("Machine is not locked for session (session state: %s)"),
5203 Global::stringifySessionState(mData->mSession.mState));
5204
5205 directControl = mData->mSession.mDirectControl;
5206 }
5207
5208 /* ignore calls made after #OnSessionEnd() is called */
5209 if (!directControl)
5210 return S_OK;
5211
5212 LONG64 dummy;
5213 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5214}
5215
5216STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5217{
5218 CheckComArgOutPointerValid(aWinId);
5219
5220 AutoCaller autoCaller(this);
5221 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5222
5223 ComPtr<IInternalSessionControl> directControl;
5224 {
5225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5226
5227 if (mData->mSession.mState != SessionState_Locked)
5228 return setError(E_FAIL,
5229 tr("Machine is not locked for session (session state: %s)"),
5230 Global::stringifySessionState(mData->mSession.mState));
5231
5232 directControl = mData->mSession.mDirectControl;
5233 }
5234
5235 /* ignore calls made after #OnSessionEnd() is called */
5236 if (!directControl)
5237 return S_OK;
5238
5239 BOOL dummy;
5240 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5241}
5242
5243#ifdef VBOX_WITH_GUEST_PROPS
5244/**
5245 * Look up a guest property in VBoxSVC's internal structures.
5246 */
5247HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5248 BSTR *aValue,
5249 LONG64 *aTimestamp,
5250 BSTR *aFlags) const
5251{
5252 using namespace guestProp;
5253
5254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5255 Utf8Str strName(aName);
5256 HWData::GuestPropertyList::const_iterator it;
5257
5258 for (it = mHWData->mGuestProperties.begin();
5259 it != mHWData->mGuestProperties.end(); ++it)
5260 {
5261 if (it->strName == strName)
5262 {
5263 char szFlags[MAX_FLAGS_LEN + 1];
5264 it->strValue.cloneTo(aValue);
5265 *aTimestamp = it->mTimestamp;
5266 writeFlags(it->mFlags, szFlags);
5267 Bstr(szFlags).cloneTo(aFlags);
5268 break;
5269 }
5270 }
5271 return S_OK;
5272}
5273
5274/**
5275 * Query the VM that a guest property belongs to for the property.
5276 * @returns E_ACCESSDENIED if the VM process is not available or not
5277 * currently handling queries and the lookup should then be done in
5278 * VBoxSVC.
5279 */
5280HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5281 BSTR *aValue,
5282 LONG64 *aTimestamp,
5283 BSTR *aFlags) const
5284{
5285 HRESULT rc;
5286 ComPtr<IInternalSessionControl> directControl;
5287 directControl = mData->mSession.mDirectControl;
5288
5289 /* fail if we were called after #OnSessionEnd() is called. This is a
5290 * silly race condition. */
5291
5292 if (!directControl)
5293 rc = E_ACCESSDENIED;
5294 else
5295 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5296 false /* isSetter */,
5297 aValue, aTimestamp, aFlags);
5298 return rc;
5299}
5300#endif // VBOX_WITH_GUEST_PROPS
5301
5302STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5303 BSTR *aValue,
5304 LONG64 *aTimestamp,
5305 BSTR *aFlags)
5306{
5307#ifndef VBOX_WITH_GUEST_PROPS
5308 ReturnComNotImplemented();
5309#else // VBOX_WITH_GUEST_PROPS
5310 CheckComArgStrNotEmptyOrNull(aName);
5311 CheckComArgOutPointerValid(aValue);
5312 CheckComArgOutPointerValid(aTimestamp);
5313 CheckComArgOutPointerValid(aFlags);
5314
5315 AutoCaller autoCaller(this);
5316 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5317
5318 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5319 if (rc == E_ACCESSDENIED)
5320 /* The VM is not running or the service is not (yet) accessible */
5321 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5322 return rc;
5323#endif // VBOX_WITH_GUEST_PROPS
5324}
5325
5326STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5327{
5328 LONG64 dummyTimestamp;
5329 Bstr dummyFlags;
5330 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5331}
5332
5333STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5334{
5335 Bstr dummyValue;
5336 Bstr dummyFlags;
5337 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5338}
5339
5340#ifdef VBOX_WITH_GUEST_PROPS
5341/**
5342 * Set a guest property in VBoxSVC's internal structures.
5343 */
5344HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5345 IN_BSTR aFlags)
5346{
5347 using namespace guestProp;
5348
5349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5350 HRESULT rc = S_OK;
5351 HWData::GuestProperty property;
5352 property.mFlags = NILFLAG;
5353 bool found = false;
5354
5355 rc = checkStateDependency(MutableStateDep);
5356 if (FAILED(rc)) return rc;
5357
5358 try
5359 {
5360 Utf8Str utf8Name(aName);
5361 Utf8Str utf8Flags(aFlags);
5362 uint32_t fFlags = NILFLAG;
5363 if ( (aFlags != NULL)
5364 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5365 )
5366 return setError(E_INVALIDARG,
5367 tr("Invalid flag values: '%ls'"),
5368 aFlags);
5369
5370 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
5371 * know, this is simple and do an OK job atm.) */
5372 HWData::GuestPropertyList::iterator it;
5373 for (it = mHWData->mGuestProperties.begin();
5374 it != mHWData->mGuestProperties.end(); ++it)
5375 if (it->strName == utf8Name)
5376 {
5377 property = *it;
5378 if (it->mFlags & (RDONLYHOST))
5379 rc = setError(E_ACCESSDENIED,
5380 tr("The property '%ls' cannot be changed by the host"),
5381 aName);
5382 else
5383 {
5384 setModified(IsModified_MachineData);
5385 mHWData.backup(); // @todo r=dj backup in a loop?!?
5386
5387 /* The backup() operation invalidates our iterator, so
5388 * get a new one. */
5389 for (it = mHWData->mGuestProperties.begin();
5390 it->strName != utf8Name;
5391 ++it)
5392 ;
5393 mHWData->mGuestProperties.erase(it);
5394 }
5395 found = true;
5396 break;
5397 }
5398 if (found && SUCCEEDED(rc))
5399 {
5400 if (aValue)
5401 {
5402 RTTIMESPEC time;
5403 property.strValue = aValue;
5404 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5405 if (aFlags != NULL)
5406 property.mFlags = fFlags;
5407 mHWData->mGuestProperties.push_back(property);
5408 }
5409 }
5410 else if (SUCCEEDED(rc) && aValue)
5411 {
5412 RTTIMESPEC time;
5413 setModified(IsModified_MachineData);
5414 mHWData.backup();
5415 property.strName = aName;
5416 property.strValue = aValue;
5417 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5418 property.mFlags = fFlags;
5419 mHWData->mGuestProperties.push_back(property);
5420 }
5421 if ( SUCCEEDED(rc)
5422 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5423 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5424 RTSTR_MAX,
5425 utf8Name.c_str(),
5426 RTSTR_MAX,
5427 NULL)
5428 )
5429 )
5430 {
5431 /** @todo r=bird: Why aren't we leaving the lock here? The
5432 * same code in PushGuestProperty does... */
5433 mParent->onGuestPropertyChange(mData->mUuid, aName,
5434 aValue ? aValue : Bstr("").raw(),
5435 aFlags ? aFlags : Bstr("").raw());
5436 }
5437 }
5438 catch (std::bad_alloc &)
5439 {
5440 rc = E_OUTOFMEMORY;
5441 }
5442
5443 return rc;
5444}
5445
5446/**
5447 * Set a property on the VM that that property belongs to.
5448 * @returns E_ACCESSDENIED if the VM process is not available or not
5449 * currently handling queries and the setting should then be done in
5450 * VBoxSVC.
5451 */
5452HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5453 IN_BSTR aFlags)
5454{
5455 HRESULT rc;
5456
5457 try
5458 {
5459 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5460
5461 BSTR dummy = NULL; /* will not be changed (setter) */
5462 LONG64 dummy64;
5463 if (!directControl)
5464 rc = E_ACCESSDENIED;
5465 else
5466 /** @todo Fix when adding DeleteGuestProperty(),
5467 see defect. */
5468 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5469 true /* isSetter */,
5470 &dummy, &dummy64, &dummy);
5471 }
5472 catch (std::bad_alloc &)
5473 {
5474 rc = E_OUTOFMEMORY;
5475 }
5476
5477 return rc;
5478}
5479#endif // VBOX_WITH_GUEST_PROPS
5480
5481STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5482 IN_BSTR aFlags)
5483{
5484#ifndef VBOX_WITH_GUEST_PROPS
5485 ReturnComNotImplemented();
5486#else // VBOX_WITH_GUEST_PROPS
5487 CheckComArgStrNotEmptyOrNull(aName);
5488 CheckComArgMaybeNull(aFlags);
5489 CheckComArgMaybeNull(aValue);
5490
5491 AutoCaller autoCaller(this);
5492 if (FAILED(autoCaller.rc()))
5493 return autoCaller.rc();
5494
5495 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5496 if (rc == E_ACCESSDENIED)
5497 /* The VM is not running or the service is not (yet) accessible */
5498 rc = setGuestPropertyToService(aName, aValue, aFlags);
5499 return rc;
5500#endif // VBOX_WITH_GUEST_PROPS
5501}
5502
5503STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5504{
5505 return SetGuestProperty(aName, aValue, NULL);
5506}
5507
5508STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5509{
5510 return SetGuestProperty(aName, NULL, NULL);
5511}
5512
5513#ifdef VBOX_WITH_GUEST_PROPS
5514/**
5515 * Enumerate the guest properties in VBoxSVC's internal structures.
5516 */
5517HRESULT Machine::enumerateGuestPropertiesInService
5518 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5519 ComSafeArrayOut(BSTR, aValues),
5520 ComSafeArrayOut(LONG64, aTimestamps),
5521 ComSafeArrayOut(BSTR, aFlags))
5522{
5523 using namespace guestProp;
5524
5525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5526 Utf8Str strPatterns(aPatterns);
5527
5528 /*
5529 * Look for matching patterns and build up a list.
5530 */
5531 HWData::GuestPropertyList propList;
5532 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
5533 it != mHWData->mGuestProperties.end();
5534 ++it)
5535 if ( strPatterns.isEmpty()
5536 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5537 RTSTR_MAX,
5538 it->strName.c_str(),
5539 RTSTR_MAX,
5540 NULL)
5541 )
5542 propList.push_back(*it);
5543
5544 /*
5545 * And build up the arrays for returning the property information.
5546 */
5547 size_t cEntries = propList.size();
5548 SafeArray<BSTR> names(cEntries);
5549 SafeArray<BSTR> values(cEntries);
5550 SafeArray<LONG64> timestamps(cEntries);
5551 SafeArray<BSTR> flags(cEntries);
5552 size_t iProp = 0;
5553 for (HWData::GuestPropertyList::iterator it = propList.begin();
5554 it != propList.end();
5555 ++it)
5556 {
5557 char szFlags[MAX_FLAGS_LEN + 1];
5558 it->strName.cloneTo(&names[iProp]);
5559 it->strValue.cloneTo(&values[iProp]);
5560 timestamps[iProp] = it->mTimestamp;
5561 writeFlags(it->mFlags, szFlags);
5562 Bstr(szFlags).cloneTo(&flags[iProp]);
5563 ++iProp;
5564 }
5565 names.detachTo(ComSafeArrayOutArg(aNames));
5566 values.detachTo(ComSafeArrayOutArg(aValues));
5567 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5568 flags.detachTo(ComSafeArrayOutArg(aFlags));
5569 return S_OK;
5570}
5571
5572/**
5573 * Enumerate the properties managed by a VM.
5574 * @returns E_ACCESSDENIED if the VM process is not available or not
5575 * currently handling queries and the setting should then be done in
5576 * VBoxSVC.
5577 */
5578HRESULT Machine::enumerateGuestPropertiesOnVM
5579 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5580 ComSafeArrayOut(BSTR, aValues),
5581 ComSafeArrayOut(LONG64, aTimestamps),
5582 ComSafeArrayOut(BSTR, aFlags))
5583{
5584 HRESULT rc;
5585 ComPtr<IInternalSessionControl> directControl;
5586 directControl = mData->mSession.mDirectControl;
5587
5588 if (!directControl)
5589 rc = E_ACCESSDENIED;
5590 else
5591 rc = directControl->EnumerateGuestProperties
5592 (aPatterns, ComSafeArrayOutArg(aNames),
5593 ComSafeArrayOutArg(aValues),
5594 ComSafeArrayOutArg(aTimestamps),
5595 ComSafeArrayOutArg(aFlags));
5596 return rc;
5597}
5598#endif // VBOX_WITH_GUEST_PROPS
5599
5600STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5601 ComSafeArrayOut(BSTR, aNames),
5602 ComSafeArrayOut(BSTR, aValues),
5603 ComSafeArrayOut(LONG64, aTimestamps),
5604 ComSafeArrayOut(BSTR, aFlags))
5605{
5606#ifndef VBOX_WITH_GUEST_PROPS
5607 ReturnComNotImplemented();
5608#else // VBOX_WITH_GUEST_PROPS
5609 CheckComArgMaybeNull(aPatterns);
5610 CheckComArgOutSafeArrayPointerValid(aNames);
5611 CheckComArgOutSafeArrayPointerValid(aValues);
5612 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5613 CheckComArgOutSafeArrayPointerValid(aFlags);
5614
5615 AutoCaller autoCaller(this);
5616 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5617
5618 HRESULT rc = enumerateGuestPropertiesOnVM
5619 (aPatterns, ComSafeArrayOutArg(aNames),
5620 ComSafeArrayOutArg(aValues),
5621 ComSafeArrayOutArg(aTimestamps),
5622 ComSafeArrayOutArg(aFlags));
5623 if (rc == E_ACCESSDENIED)
5624 /* The VM is not running or the service is not (yet) accessible */
5625 rc = enumerateGuestPropertiesInService
5626 (aPatterns, ComSafeArrayOutArg(aNames),
5627 ComSafeArrayOutArg(aValues),
5628 ComSafeArrayOutArg(aTimestamps),
5629 ComSafeArrayOutArg(aFlags));
5630 return rc;
5631#endif // VBOX_WITH_GUEST_PROPS
5632}
5633
5634STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5635 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5636{
5637 MediaData::AttachmentList atts;
5638
5639 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5640 if (FAILED(rc)) return rc;
5641
5642 SafeIfaceArray<IMediumAttachment> attachments(atts);
5643 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5644
5645 return S_OK;
5646}
5647
5648STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5649 LONG aControllerPort,
5650 LONG aDevice,
5651 IMediumAttachment **aAttachment)
5652{
5653 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5654 aControllerName, aControllerPort, aDevice));
5655
5656 CheckComArgStrNotEmptyOrNull(aControllerName);
5657 CheckComArgOutPointerValid(aAttachment);
5658
5659 AutoCaller autoCaller(this);
5660 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5661
5662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5663
5664 *aAttachment = NULL;
5665
5666 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5667 aControllerName,
5668 aControllerPort,
5669 aDevice);
5670 if (pAttach.isNull())
5671 return setError(VBOX_E_OBJECT_NOT_FOUND,
5672 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5673 aDevice, aControllerPort, aControllerName);
5674
5675 pAttach.queryInterfaceTo(aAttachment);
5676
5677 return S_OK;
5678}
5679
5680STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5681 StorageBus_T aConnectionType,
5682 IStorageController **controller)
5683{
5684 CheckComArgStrNotEmptyOrNull(aName);
5685
5686 if ( (aConnectionType <= StorageBus_Null)
5687 || (aConnectionType > StorageBus_SAS))
5688 return setError(E_INVALIDARG,
5689 tr("Invalid connection type: %d"),
5690 aConnectionType);
5691
5692 AutoCaller autoCaller(this);
5693 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5694
5695 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5696
5697 HRESULT rc = checkStateDependency(MutableStateDep);
5698 if (FAILED(rc)) return rc;
5699
5700 /* try to find one with the name first. */
5701 ComObjPtr<StorageController> ctrl;
5702
5703 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5704 if (SUCCEEDED(rc))
5705 return setError(VBOX_E_OBJECT_IN_USE,
5706 tr("Storage controller named '%ls' already exists"),
5707 aName);
5708
5709 ctrl.createObject();
5710
5711 /* get a new instance number for the storage controller */
5712 ULONG ulInstance = 0;
5713 bool fBootable = true;
5714 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5715 it != mStorageControllers->end();
5716 ++it)
5717 {
5718 if ((*it)->getStorageBus() == aConnectionType)
5719 {
5720 ULONG ulCurInst = (*it)->getInstance();
5721
5722 if (ulCurInst >= ulInstance)
5723 ulInstance = ulCurInst + 1;
5724
5725 /* Only one controller of each type can be marked as bootable. */
5726 if ((*it)->getBootable())
5727 fBootable = false;
5728 }
5729 }
5730
5731 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5732 if (FAILED(rc)) return rc;
5733
5734 setModified(IsModified_Storage);
5735 mStorageControllers.backup();
5736 mStorageControllers->push_back(ctrl);
5737
5738 ctrl.queryInterfaceTo(controller);
5739
5740 /* inform the direct session if any */
5741 alock.release();
5742 onStorageControllerChange();
5743
5744 return S_OK;
5745}
5746
5747STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5748 IStorageController **aStorageController)
5749{
5750 CheckComArgStrNotEmptyOrNull(aName);
5751
5752 AutoCaller autoCaller(this);
5753 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5754
5755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5756
5757 ComObjPtr<StorageController> ctrl;
5758
5759 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5760 if (SUCCEEDED(rc))
5761 ctrl.queryInterfaceTo(aStorageController);
5762
5763 return rc;
5764}
5765
5766STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5767 IStorageController **aStorageController)
5768{
5769 AutoCaller autoCaller(this);
5770 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5771
5772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5773
5774 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5775 it != mStorageControllers->end();
5776 ++it)
5777 {
5778 if ((*it)->getInstance() == aInstance)
5779 {
5780 (*it).queryInterfaceTo(aStorageController);
5781 return S_OK;
5782 }
5783 }
5784
5785 return setError(VBOX_E_OBJECT_NOT_FOUND,
5786 tr("Could not find a storage controller with instance number '%lu'"),
5787 aInstance);
5788}
5789
5790STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
5791{
5792 AutoCaller autoCaller(this);
5793 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5794
5795 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5796
5797 HRESULT rc = checkStateDependency(MutableStateDep);
5798 if (FAILED(rc)) return rc;
5799
5800 ComObjPtr<StorageController> ctrl;
5801
5802 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5803 if (SUCCEEDED(rc))
5804 {
5805 /* Ensure that only one controller of each type is marked as bootable. */
5806 if (fBootable == TRUE)
5807 {
5808 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5809 it != mStorageControllers->end();
5810 ++it)
5811 {
5812 ComObjPtr<StorageController> aCtrl = (*it);
5813
5814 if ( (aCtrl->getName() != Utf8Str(aName))
5815 && aCtrl->getBootable() == TRUE
5816 && aCtrl->getStorageBus() == ctrl->getStorageBus()
5817 && aCtrl->getControllerType() == ctrl->getControllerType())
5818 {
5819 aCtrl->setBootable(FALSE);
5820 break;
5821 }
5822 }
5823 }
5824
5825 if (SUCCEEDED(rc))
5826 {
5827 ctrl->setBootable(fBootable);
5828 setModified(IsModified_Storage);
5829 }
5830 }
5831
5832 if (SUCCEEDED(rc))
5833 {
5834 /* inform the direct session if any */
5835 alock.release();
5836 onStorageControllerChange();
5837 }
5838
5839 return rc;
5840}
5841
5842STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
5843{
5844 CheckComArgStrNotEmptyOrNull(aName);
5845
5846 AutoCaller autoCaller(this);
5847 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5848
5849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5850
5851 HRESULT rc = checkStateDependency(MutableStateDep);
5852 if (FAILED(rc)) return rc;
5853
5854 ComObjPtr<StorageController> ctrl;
5855 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5856 if (FAILED(rc)) return rc;
5857
5858 {
5859 /* find all attached devices to the appropriate storage controller and detach them all*/
5860 MediaData::AttachmentList::const_iterator endList = mMediaData->mAttachments.end();
5861 MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
5862 for (;it != endList; it++)
5863 {
5864 MediumAttachment *pAttachTemp = *it;
5865 AutoCaller localAutoCaller(pAttachTemp);
5866 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5867
5868 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5869
5870 if (pAttachTemp->getControllerName() == aName)
5871 {
5872 LONG port = pAttachTemp->getPort();
5873 LONG device = pAttachTemp->getDevice();
5874 rc = DetachDevice(aName, port, device);
5875 if (FAILED(rc)) return rc;
5876 }
5877 }
5878 }
5879
5880 /* We can remove it now. */
5881 setModified(IsModified_Storage);
5882 mStorageControllers.backup();
5883
5884 ctrl->unshare();
5885
5886 mStorageControllers->remove(ctrl);
5887
5888 /* inform the direct session if any */
5889 alock.release();
5890 onStorageControllerChange();
5891
5892 return S_OK;
5893}
5894
5895STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
5896 ULONG *puOriginX,
5897 ULONG *puOriginY,
5898 ULONG *puWidth,
5899 ULONG *puHeight,
5900 BOOL *pfEnabled)
5901{
5902 LogFlowThisFunc(("\n"));
5903
5904 CheckComArgNotNull(puOriginX);
5905 CheckComArgNotNull(puOriginY);
5906 CheckComArgNotNull(puWidth);
5907 CheckComArgNotNull(puHeight);
5908 CheckComArgNotNull(pfEnabled);
5909
5910 uint32_t u32OriginX= 0;
5911 uint32_t u32OriginY= 0;
5912 uint32_t u32Width = 0;
5913 uint32_t u32Height = 0;
5914 uint16_t u16Flags = 0;
5915
5916 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
5917 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
5918 if (RT_FAILURE(vrc))
5919 {
5920#ifdef RT_OS_WINDOWS
5921 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
5922 * This works with XPCOM. But Windows COM sets all output parameters to zero.
5923 * So just assign fEnable to TRUE again.
5924 * The right fix would be to change GUI API wrappers to make sure that parameters
5925 * are changed only if API succeeds.
5926 */
5927 *pfEnabled = TRUE;
5928#endif
5929 return setError(VBOX_E_IPRT_ERROR,
5930 tr("Saved guest size is not available (%Rrc)"),
5931 vrc);
5932 }
5933
5934 *puOriginX = u32OriginX;
5935 *puOriginY = u32OriginY;
5936 *puWidth = u32Width;
5937 *puHeight = u32Height;
5938 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
5939
5940 return S_OK;
5941}
5942
5943STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
5944{
5945 LogFlowThisFunc(("\n"));
5946
5947 CheckComArgNotNull(aSize);
5948 CheckComArgNotNull(aWidth);
5949 CheckComArgNotNull(aHeight);
5950
5951 if (aScreenId != 0)
5952 return E_NOTIMPL;
5953
5954 AutoCaller autoCaller(this);
5955 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5956
5957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5958
5959 uint8_t *pu8Data = NULL;
5960 uint32_t cbData = 0;
5961 uint32_t u32Width = 0;
5962 uint32_t u32Height = 0;
5963
5964 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
5965
5966 if (RT_FAILURE(vrc))
5967 return setError(VBOX_E_IPRT_ERROR,
5968 tr("Saved screenshot data is not available (%Rrc)"),
5969 vrc);
5970
5971 *aSize = cbData;
5972 *aWidth = u32Width;
5973 *aHeight = u32Height;
5974
5975 freeSavedDisplayScreenshot(pu8Data);
5976
5977 return S_OK;
5978}
5979
5980STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
5981{
5982 LogFlowThisFunc(("\n"));
5983
5984 CheckComArgNotNull(aWidth);
5985 CheckComArgNotNull(aHeight);
5986 CheckComArgOutSafeArrayPointerValid(aData);
5987
5988 if (aScreenId != 0)
5989 return E_NOTIMPL;
5990
5991 AutoCaller autoCaller(this);
5992 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5993
5994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5995
5996 uint8_t *pu8Data = NULL;
5997 uint32_t cbData = 0;
5998 uint32_t u32Width = 0;
5999 uint32_t u32Height = 0;
6000
6001 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6002
6003 if (RT_FAILURE(vrc))
6004 return setError(VBOX_E_IPRT_ERROR,
6005 tr("Saved screenshot data is not available (%Rrc)"),
6006 vrc);
6007
6008 *aWidth = u32Width;
6009 *aHeight = u32Height;
6010
6011 com::SafeArray<BYTE> bitmap(cbData);
6012 /* Convert pixels to format expected by the API caller. */
6013 if (aBGR)
6014 {
6015 /* [0] B, [1] G, [2] R, [3] A. */
6016 for (unsigned i = 0; i < cbData; i += 4)
6017 {
6018 bitmap[i] = pu8Data[i];
6019 bitmap[i + 1] = pu8Data[i + 1];
6020 bitmap[i + 2] = pu8Data[i + 2];
6021 bitmap[i + 3] = 0xff;
6022 }
6023 }
6024 else
6025 {
6026 /* [0] R, [1] G, [2] B, [3] A. */
6027 for (unsigned i = 0; i < cbData; i += 4)
6028 {
6029 bitmap[i] = pu8Data[i + 2];
6030 bitmap[i + 1] = pu8Data[i + 1];
6031 bitmap[i + 2] = pu8Data[i];
6032 bitmap[i + 3] = 0xff;
6033 }
6034 }
6035 bitmap.detachTo(ComSafeArrayOutArg(aData));
6036
6037 freeSavedDisplayScreenshot(pu8Data);
6038
6039 return S_OK;
6040}
6041
6042
6043STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6044{
6045 LogFlowThisFunc(("\n"));
6046
6047 CheckComArgNotNull(aWidth);
6048 CheckComArgNotNull(aHeight);
6049 CheckComArgOutSafeArrayPointerValid(aData);
6050
6051 if (aScreenId != 0)
6052 return E_NOTIMPL;
6053
6054 AutoCaller autoCaller(this);
6055 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6056
6057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6058
6059 uint8_t *pu8Data = NULL;
6060 uint32_t cbData = 0;
6061 uint32_t u32Width = 0;
6062 uint32_t u32Height = 0;
6063
6064 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6065
6066 if (RT_FAILURE(vrc))
6067 return setError(VBOX_E_IPRT_ERROR,
6068 tr("Saved screenshot data is not available (%Rrc)"),
6069 vrc);
6070
6071 *aWidth = u32Width;
6072 *aHeight = u32Height;
6073
6074 HRESULT rc = S_OK;
6075 uint8_t *pu8PNG = NULL;
6076 uint32_t cbPNG = 0;
6077 uint32_t cxPNG = 0;
6078 uint32_t cyPNG = 0;
6079
6080 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6081
6082 if (RT_SUCCESS(vrc))
6083 {
6084 com::SafeArray<BYTE> screenData(cbPNG);
6085 screenData.initFrom(pu8PNG, cbPNG);
6086 if (pu8PNG)
6087 RTMemFree(pu8PNG);
6088 screenData.detachTo(ComSafeArrayOutArg(aData));
6089 }
6090 else
6091 {
6092 if (pu8PNG)
6093 RTMemFree(pu8PNG);
6094 return setError(VBOX_E_IPRT_ERROR,
6095 tr("Could not convert screenshot to PNG (%Rrc)"),
6096 vrc);
6097 }
6098
6099 freeSavedDisplayScreenshot(pu8Data);
6100
6101 return rc;
6102}
6103
6104STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6105{
6106 LogFlowThisFunc(("\n"));
6107
6108 CheckComArgNotNull(aSize);
6109 CheckComArgNotNull(aWidth);
6110 CheckComArgNotNull(aHeight);
6111
6112 if (aScreenId != 0)
6113 return E_NOTIMPL;
6114
6115 AutoCaller autoCaller(this);
6116 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6117
6118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6119
6120 uint8_t *pu8Data = NULL;
6121 uint32_t cbData = 0;
6122 uint32_t u32Width = 0;
6123 uint32_t u32Height = 0;
6124
6125 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6126
6127 if (RT_FAILURE(vrc))
6128 return setError(VBOX_E_IPRT_ERROR,
6129 tr("Saved screenshot data is not available (%Rrc)"),
6130 vrc);
6131
6132 *aSize = cbData;
6133 *aWidth = u32Width;
6134 *aHeight = u32Height;
6135
6136 freeSavedDisplayScreenshot(pu8Data);
6137
6138 return S_OK;
6139}
6140
6141STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6142{
6143 LogFlowThisFunc(("\n"));
6144
6145 CheckComArgNotNull(aWidth);
6146 CheckComArgNotNull(aHeight);
6147 CheckComArgOutSafeArrayPointerValid(aData);
6148
6149 if (aScreenId != 0)
6150 return E_NOTIMPL;
6151
6152 AutoCaller autoCaller(this);
6153 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6154
6155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6156
6157 uint8_t *pu8Data = NULL;
6158 uint32_t cbData = 0;
6159 uint32_t u32Width = 0;
6160 uint32_t u32Height = 0;
6161
6162 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6163
6164 if (RT_FAILURE(vrc))
6165 return setError(VBOX_E_IPRT_ERROR,
6166 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6167 vrc);
6168
6169 *aWidth = u32Width;
6170 *aHeight = u32Height;
6171
6172 com::SafeArray<BYTE> png(cbData);
6173 png.initFrom(pu8Data, cbData);
6174 png.detachTo(ComSafeArrayOutArg(aData));
6175
6176 freeSavedDisplayScreenshot(pu8Data);
6177
6178 return S_OK;
6179}
6180
6181STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6182{
6183 HRESULT rc = S_OK;
6184 LogFlowThisFunc(("\n"));
6185
6186 AutoCaller autoCaller(this);
6187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6188
6189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6190
6191 if (!mHWData->mCPUHotPlugEnabled)
6192 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6193
6194 if (aCpu >= mHWData->mCPUCount)
6195 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6196
6197 if (mHWData->mCPUAttached[aCpu])
6198 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6199
6200 alock.release();
6201 rc = onCPUChange(aCpu, false);
6202 alock.acquire();
6203 if (FAILED(rc)) return rc;
6204
6205 setModified(IsModified_MachineData);
6206 mHWData.backup();
6207 mHWData->mCPUAttached[aCpu] = true;
6208
6209 /* Save settings if online */
6210 if (Global::IsOnline(mData->mMachineState))
6211 saveSettings(NULL);
6212
6213 return S_OK;
6214}
6215
6216STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6217{
6218 HRESULT rc = S_OK;
6219 LogFlowThisFunc(("\n"));
6220
6221 AutoCaller autoCaller(this);
6222 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6223
6224 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6225
6226 if (!mHWData->mCPUHotPlugEnabled)
6227 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6228
6229 if (aCpu >= SchemaDefs::MaxCPUCount)
6230 return setError(E_INVALIDARG,
6231 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6232 SchemaDefs::MaxCPUCount);
6233
6234 if (!mHWData->mCPUAttached[aCpu])
6235 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6236
6237 /* CPU 0 can't be detached */
6238 if (aCpu == 0)
6239 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6240
6241 alock.release();
6242 rc = onCPUChange(aCpu, true);
6243 alock.acquire();
6244 if (FAILED(rc)) return rc;
6245
6246 setModified(IsModified_MachineData);
6247 mHWData.backup();
6248 mHWData->mCPUAttached[aCpu] = false;
6249
6250 /* Save settings if online */
6251 if (Global::IsOnline(mData->mMachineState))
6252 saveSettings(NULL);
6253
6254 return S_OK;
6255}
6256
6257STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6258{
6259 LogFlowThisFunc(("\n"));
6260
6261 CheckComArgNotNull(aCpuAttached);
6262
6263 *aCpuAttached = false;
6264
6265 AutoCaller autoCaller(this);
6266 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6267
6268 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6269
6270 /* If hotplug is enabled the CPU is always enabled. */
6271 if (!mHWData->mCPUHotPlugEnabled)
6272 {
6273 if (aCpu < mHWData->mCPUCount)
6274 *aCpuAttached = true;
6275 }
6276 else
6277 {
6278 if (aCpu < SchemaDefs::MaxCPUCount)
6279 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6280 }
6281
6282 return S_OK;
6283}
6284
6285STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6286{
6287 CheckComArgOutPointerValid(aName);
6288
6289 AutoCaller autoCaller(this);
6290 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6291
6292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6293
6294 Utf8Str log = queryLogFilename(aIdx);
6295 if (!RTFileExists(log.c_str()))
6296 log.setNull();
6297 log.cloneTo(aName);
6298
6299 return S_OK;
6300}
6301
6302STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6303{
6304 LogFlowThisFunc(("\n"));
6305 CheckComArgOutSafeArrayPointerValid(aData);
6306 if (aSize < 0)
6307 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6308
6309 AutoCaller autoCaller(this);
6310 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6311
6312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6313
6314 HRESULT rc = S_OK;
6315 Utf8Str log = queryLogFilename(aIdx);
6316
6317 /* do not unnecessarily hold the lock while doing something which does
6318 * not need the lock and potentially takes a long time. */
6319 alock.release();
6320
6321 /* Limit the chunk size to 32K for now, as that gives better performance
6322 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6323 * One byte expands to approx. 25 bytes of breathtaking XML. */
6324 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6325 com::SafeArray<BYTE> logData(cbData);
6326
6327 RTFILE LogFile;
6328 int vrc = RTFileOpen(&LogFile, log.c_str(),
6329 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6330 if (RT_SUCCESS(vrc))
6331 {
6332 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6333 if (RT_SUCCESS(vrc))
6334 logData.resize(cbData);
6335 else
6336 rc = setError(VBOX_E_IPRT_ERROR,
6337 tr("Could not read log file '%s' (%Rrc)"),
6338 log.c_str(), vrc);
6339 RTFileClose(LogFile);
6340 }
6341 else
6342 rc = setError(VBOX_E_IPRT_ERROR,
6343 tr("Could not open log file '%s' (%Rrc)"),
6344 log.c_str(), vrc);
6345
6346 if (FAILED(rc))
6347 logData.resize(0);
6348 logData.detachTo(ComSafeArrayOutArg(aData));
6349
6350 return rc;
6351}
6352
6353
6354/**
6355 * Currently this method doesn't attach device to the running VM,
6356 * just makes sure it's plugged on next VM start.
6357 */
6358STDMETHODIMP Machine::AttachHostPciDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6359{
6360 AutoCaller autoCaller(this);
6361 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6362
6363 // lock scope
6364 {
6365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6366
6367 HRESULT rc = checkStateDependency(MutableStateDep);
6368 if (FAILED(rc)) return rc;
6369
6370 ChipsetType_T aChipset = ChipsetType_PIIX3;
6371 COMGETTER(ChipsetType)(&aChipset);
6372
6373 if (aChipset != ChipsetType_ICH9)
6374 {
6375 return setError(E_INVALIDARG,
6376 tr("Host PCI attachment only supported with ICH9 chipset"));
6377 }
6378
6379 // check if device with this host PCI address already attached
6380 for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin();
6381 it != mHWData->mPciDeviceAssignments.end();
6382 ++it)
6383 {
6384 LONG iHostAddress = -1;
6385 ComPtr<PciDeviceAttachment> pAttach;
6386 pAttach = *it;
6387 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6388 if (iHostAddress == hostAddress)
6389 return setError(E_INVALIDARG,
6390 tr("Device with host PCI address already attached to this VM"));
6391 }
6392
6393 ComObjPtr<PciDeviceAttachment> pda;
6394 char name[32];
6395
6396 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6397 Bstr bname(name);
6398 pda.createObject();
6399 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6400 setModified(IsModified_MachineData);
6401 mHWData.backup();
6402 mHWData->mPciDeviceAssignments.push_back(pda);
6403 }
6404
6405 return S_OK;
6406}
6407
6408/**
6409 * Currently this method doesn't detach device from the running VM,
6410 * just makes sure it's not plugged on next VM start.
6411 */
6412STDMETHODIMP Machine::DetachHostPciDevice(LONG hostAddress)
6413{
6414 AutoCaller autoCaller(this);
6415 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6416
6417 ComObjPtr<PciDeviceAttachment> pAttach;
6418 bool fRemoved = false;
6419 HRESULT rc;
6420
6421 // lock scope
6422 {
6423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6424
6425 rc = checkStateDependency(MutableStateDep);
6426 if (FAILED(rc)) return rc;
6427
6428 for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin();
6429 it != mHWData->mPciDeviceAssignments.end();
6430 ++it)
6431 {
6432 LONG iHostAddress = -1;
6433 pAttach = *it;
6434 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6435 if (iHostAddress != -1 && iHostAddress == hostAddress)
6436 {
6437 setModified(IsModified_MachineData);
6438 mHWData.backup();
6439 mHWData->mPciDeviceAssignments.remove(pAttach);
6440 fRemoved = true;
6441 break;
6442 }
6443 }
6444 }
6445
6446
6447 /* Fire event outside of the lock */
6448 if (fRemoved)
6449 {
6450 Assert(!pAttach.isNull());
6451 ComPtr<IEventSource> es;
6452 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6453 Assert(SUCCEEDED(rc));
6454 Bstr mid;
6455 rc = this->COMGETTER(Id)(mid.asOutParam());
6456 Assert(SUCCEEDED(rc));
6457 fireHostPciDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6458 }
6459
6460 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6461 tr("No host PCI device %08x attached"),
6462 hostAddress
6463 );
6464}
6465
6466STDMETHODIMP Machine::COMGETTER(PciDeviceAssignments)(ComSafeArrayOut(IPciDeviceAttachment *, aAssignments))
6467{
6468 CheckComArgOutSafeArrayPointerValid(aAssignments);
6469
6470 AutoCaller autoCaller(this);
6471 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6472
6473 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6474
6475 SafeIfaceArray<IPciDeviceAttachment> assignments(mHWData->mPciDeviceAssignments);
6476 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6477
6478 return S_OK;
6479}
6480
6481STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6482{
6483 CheckComArgOutPointerValid(aBandwidthControl);
6484
6485 AutoCaller autoCaller(this);
6486 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6487
6488 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6489
6490 return S_OK;
6491}
6492
6493STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6494{
6495 CheckComArgOutPointerValid(pfEnabled);
6496 AutoCaller autoCaller(this);
6497 HRESULT hrc = autoCaller.rc();
6498 if (SUCCEEDED(hrc))
6499 {
6500 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6501 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6502 }
6503 return hrc;
6504}
6505
6506STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6507{
6508 AutoCaller autoCaller(this);
6509 HRESULT hrc = autoCaller.rc();
6510 if (SUCCEEDED(hrc))
6511 {
6512 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6513 hrc = checkStateDependency(MutableStateDep);
6514 if (SUCCEEDED(hrc))
6515 {
6516 hrc = mHWData.backupEx();
6517 if (SUCCEEDED(hrc))
6518 {
6519 setModified(IsModified_MachineData);
6520 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6521 }
6522 }
6523 }
6524 return hrc;
6525}
6526
6527STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6528{
6529 CheckComArgOutPointerValid(pbstrConfig);
6530 AutoCaller autoCaller(this);
6531 HRESULT hrc = autoCaller.rc();
6532 if (SUCCEEDED(hrc))
6533 {
6534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6535 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6536 }
6537 return hrc;
6538}
6539
6540STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6541{
6542 CheckComArgStr(bstrConfig);
6543 AutoCaller autoCaller(this);
6544 HRESULT hrc = autoCaller.rc();
6545 if (SUCCEEDED(hrc))
6546 {
6547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6548 hrc = checkStateDependency(MutableStateDep);
6549 if (SUCCEEDED(hrc))
6550 {
6551 hrc = mHWData.backupEx();
6552 if (SUCCEEDED(hrc))
6553 {
6554 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6555 if (SUCCEEDED(hrc))
6556 setModified(IsModified_MachineData);
6557 }
6558 }
6559 }
6560 return hrc;
6561
6562}
6563
6564STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6565{
6566 CheckComArgOutPointerValid(pfAllow);
6567 AutoCaller autoCaller(this);
6568 HRESULT hrc = autoCaller.rc();
6569 if (SUCCEEDED(hrc))
6570 {
6571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6572 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6573 }
6574 return hrc;
6575}
6576
6577STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6578{
6579 AutoCaller autoCaller(this);
6580 HRESULT hrc = autoCaller.rc();
6581 if (SUCCEEDED(hrc))
6582 {
6583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6584 hrc = checkStateDependency(MutableStateDep);
6585 if (SUCCEEDED(hrc))
6586 {
6587 hrc = mHWData.backupEx();
6588 if (SUCCEEDED(hrc))
6589 {
6590 setModified(IsModified_MachineData);
6591 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6592 }
6593 }
6594 }
6595 return hrc;
6596}
6597
6598STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6599{
6600 CheckComArgOutPointerValid(pfEnabled);
6601 AutoCaller autoCaller(this);
6602 HRESULT hrc = autoCaller.rc();
6603 if (SUCCEEDED(hrc))
6604 {
6605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6606 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6607 }
6608 return hrc;
6609}
6610
6611STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6612{
6613 AutoCaller autoCaller(this);
6614 HRESULT hrc = autoCaller.rc();
6615 if (SUCCEEDED(hrc))
6616 {
6617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6618 hrc = checkStateDependency(MutableStateDep);
6619 if ( SUCCEEDED(hrc)
6620 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6621 {
6622 AutostartDb *autostartDb = mParent->getAutostartDb();
6623 int vrc;
6624
6625 if (fEnabled)
6626 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6627 else
6628 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6629
6630 if (RT_SUCCESS(vrc))
6631 {
6632 hrc = mHWData.backupEx();
6633 if (SUCCEEDED(hrc))
6634 {
6635 setModified(IsModified_MachineData);
6636 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6637 }
6638 }
6639 else if (vrc == VERR_NOT_SUPPORTED)
6640 hrc = setError(VBOX_E_NOT_SUPPORTED,
6641 tr("The VM autostart feature is not supported on this platform"));
6642 else
6643 hrc = setError(E_UNEXPECTED,
6644 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6645 fEnabled ? "Adding" : "Removing",
6646 mUserData->s.strName.c_str(), vrc);
6647 }
6648 }
6649 return hrc;
6650}
6651
6652STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6653{
6654 CheckComArgOutPointerValid(puDelay);
6655 AutoCaller autoCaller(this);
6656 HRESULT hrc = autoCaller.rc();
6657 if (SUCCEEDED(hrc))
6658 {
6659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6660 *puDelay = mHWData->mAutostart.uAutostartDelay;
6661 }
6662 return hrc;
6663}
6664
6665STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6666{
6667 AutoCaller autoCaller(this);
6668 HRESULT hrc = autoCaller.rc();
6669 if (SUCCEEDED(hrc))
6670 {
6671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6672 hrc = checkStateDependency(MutableStateDep);
6673 if (SUCCEEDED(hrc))
6674 {
6675 hrc = mHWData.backupEx();
6676 if (SUCCEEDED(hrc))
6677 {
6678 setModified(IsModified_MachineData);
6679 mHWData->mAutostart.uAutostartDelay = uDelay;
6680 }
6681 }
6682 }
6683 return hrc;
6684}
6685
6686STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6687{
6688 CheckComArgOutPointerValid(penmAutostopType);
6689 AutoCaller autoCaller(this);
6690 HRESULT hrc = autoCaller.rc();
6691 if (SUCCEEDED(hrc))
6692 {
6693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6694 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6695 }
6696 return hrc;
6697}
6698
6699STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6700{
6701 AutoCaller autoCaller(this);
6702 HRESULT hrc = autoCaller.rc();
6703 if (SUCCEEDED(hrc))
6704 {
6705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6706 hrc = checkStateDependency(MutableStateDep);
6707 if ( SUCCEEDED(hrc)
6708 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6709 {
6710 AutostartDb *autostartDb = mParent->getAutostartDb();
6711 int vrc;
6712
6713 if (enmAutostopType != AutostopType_Disabled)
6714 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6715 else
6716 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6717
6718 if (RT_SUCCESS(vrc))
6719 {
6720 hrc = mHWData.backupEx();
6721 if (SUCCEEDED(hrc))
6722 {
6723 setModified(IsModified_MachineData);
6724 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6725 }
6726 }
6727 else if (vrc == VERR_NOT_SUPPORTED)
6728 hrc = setError(VBOX_E_NOT_SUPPORTED,
6729 tr("The VM autostop feature is not supported on this platform"));
6730 else
6731 hrc = setError(E_UNEXPECTED,
6732 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6733 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6734 mUserData->s.strName.c_str(), vrc);
6735 }
6736 }
6737 return hrc;
6738}
6739
6740
6741STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6742{
6743 LogFlowFuncEnter();
6744
6745 CheckComArgNotNull(pTarget);
6746 CheckComArgOutPointerValid(pProgress);
6747
6748 /* Convert the options. */
6749 RTCList<CloneOptions_T> optList;
6750 if (options != NULL)
6751 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6752
6753 if (optList.contains(CloneOptions_Link))
6754 {
6755 if (!isSnapshotMachine())
6756 return setError(E_INVALIDARG,
6757 tr("Linked clone can only be created from a snapshot"));
6758 if (mode != CloneMode_MachineState)
6759 return setError(E_INVALIDARG,
6760 tr("Linked clone can only be created for a single machine state"));
6761 }
6762 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6763
6764 AutoCaller autoCaller(this);
6765 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6766
6767
6768 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6769
6770 HRESULT rc = pWorker->start(pProgress);
6771
6772 LogFlowFuncLeave();
6773
6774 return rc;
6775}
6776
6777// public methods for internal purposes
6778/////////////////////////////////////////////////////////////////////////////
6779
6780/**
6781 * Adds the given IsModified_* flag to the dirty flags of the machine.
6782 * This must be called either during loadSettings or under the machine write lock.
6783 * @param fl
6784 */
6785void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6786{
6787 mData->flModifications |= fl;
6788 if (fAllowStateModification && isStateModificationAllowed())
6789 mData->mCurrentStateModified = true;
6790}
6791
6792/**
6793 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6794 * care of the write locking.
6795 *
6796 * @param fModifications The flag to add.
6797 */
6798void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6799{
6800 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6801 setModified(fModification, fAllowStateModification);
6802}
6803
6804/**
6805 * Saves the registry entry of this machine to the given configuration node.
6806 *
6807 * @param aEntryNode Node to save the registry entry to.
6808 *
6809 * @note locks this object for reading.
6810 */
6811HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
6812{
6813 AutoLimitedCaller autoCaller(this);
6814 AssertComRCReturnRC(autoCaller.rc());
6815
6816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6817
6818 data.uuid = mData->mUuid;
6819 data.strSettingsFile = mData->m_strConfigFile;
6820
6821 return S_OK;
6822}
6823
6824/**
6825 * Calculates the absolute path of the given path taking the directory of the
6826 * machine settings file as the current directory.
6827 *
6828 * @param aPath Path to calculate the absolute path for.
6829 * @param aResult Where to put the result (used only on success, can be the
6830 * same Utf8Str instance as passed in @a aPath).
6831 * @return IPRT result.
6832 *
6833 * @note Locks this object for reading.
6834 */
6835int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6836{
6837 AutoCaller autoCaller(this);
6838 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6839
6840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6841
6842 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6843
6844 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6845
6846 strSettingsDir.stripFilename();
6847 char folder[RTPATH_MAX];
6848 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
6849 if (RT_SUCCESS(vrc))
6850 aResult = folder;
6851
6852 return vrc;
6853}
6854
6855/**
6856 * Copies strSource to strTarget, making it relative to the machine folder
6857 * if it is a subdirectory thereof, or simply copying it otherwise.
6858 *
6859 * @param strSource Path to evaluate and copy.
6860 * @param strTarget Buffer to receive target path.
6861 *
6862 * @note Locks this object for reading.
6863 */
6864void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
6865 Utf8Str &strTarget)
6866{
6867 AutoCaller autoCaller(this);
6868 AssertComRCReturn(autoCaller.rc(), (void)0);
6869
6870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6871
6872 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6873 // use strTarget as a temporary buffer to hold the machine settings dir
6874 strTarget = mData->m_strConfigFileFull;
6875 strTarget.stripFilename();
6876 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6877 {
6878 // is relative: then append what's left
6879 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6880 // for empty paths (only possible for subdirs) use "." to avoid
6881 // triggering default settings for not present config attributes.
6882 if (strTarget.isEmpty())
6883 strTarget = ".";
6884 }
6885 else
6886 // is not relative: then overwrite
6887 strTarget = strSource;
6888}
6889
6890/**
6891 * Returns the full path to the machine's log folder in the
6892 * \a aLogFolder argument.
6893 */
6894void Machine::getLogFolder(Utf8Str &aLogFolder)
6895{
6896 AutoCaller autoCaller(this);
6897 AssertComRCReturnVoid(autoCaller.rc());
6898
6899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6900
6901 char szTmp[RTPATH_MAX];
6902 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
6903 if (RT_SUCCESS(vrc))
6904 {
6905 if (szTmp[0] && !mUserData.isNull())
6906 {
6907 char szTmp2[RTPATH_MAX];
6908 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
6909 if (RT_SUCCESS(vrc))
6910 aLogFolder = BstrFmt("%s%c%s",
6911 szTmp2,
6912 RTPATH_DELIMITER,
6913 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
6914 }
6915 else
6916 vrc = VERR_PATH_IS_RELATIVE;
6917 }
6918
6919 if (RT_FAILURE(vrc))
6920 {
6921 // fallback if VBOX_USER_LOGHOME is not set or invalid
6922 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
6923 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
6924 aLogFolder.append(RTPATH_DELIMITER);
6925 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
6926 }
6927}
6928
6929/**
6930 * Returns the full path to the machine's log file for an given index.
6931 */
6932Utf8Str Machine::queryLogFilename(ULONG idx)
6933{
6934 Utf8Str logFolder;
6935 getLogFolder(logFolder);
6936 Assert(logFolder.length());
6937 Utf8Str log;
6938 if (idx == 0)
6939 log = Utf8StrFmt("%s%cVBox.log",
6940 logFolder.c_str(), RTPATH_DELIMITER);
6941 else
6942 log = Utf8StrFmt("%s%cVBox.log.%d",
6943 logFolder.c_str(), RTPATH_DELIMITER, idx);
6944 return log;
6945}
6946
6947/**
6948 * Composes a unique saved state filename based on the current system time. The filename is
6949 * granular to the second so this will work so long as no more than one snapshot is taken on
6950 * a machine per second.
6951 *
6952 * Before version 4.1, we used this formula for saved state files:
6953 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
6954 * which no longer works because saved state files can now be shared between the saved state of the
6955 * "saved" machine and an online snapshot, and the following would cause problems:
6956 * 1) save machine
6957 * 2) create online snapshot from that machine state --> reusing saved state file
6958 * 3) save machine again --> filename would be reused, breaking the online snapshot
6959 *
6960 * So instead we now use a timestamp.
6961 *
6962 * @param str
6963 */
6964void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
6965{
6966 AutoCaller autoCaller(this);
6967 AssertComRCReturnVoid(autoCaller.rc());
6968
6969 {
6970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6971 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
6972 }
6973
6974 RTTIMESPEC ts;
6975 RTTimeNow(&ts);
6976 RTTIME time;
6977 RTTimeExplode(&time, &ts);
6978
6979 strStateFilePath += RTPATH_DELIMITER;
6980 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
6981 time.i32Year, time.u8Month, time.u8MonthDay,
6982 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
6983}
6984
6985/**
6986 * @note Locks this object for writing, calls the client process
6987 * (inside the lock).
6988 */
6989HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
6990 const Utf8Str &strType,
6991 const Utf8Str &strEnvironment,
6992 ProgressProxy *aProgress)
6993{
6994 LogFlowThisFuncEnter();
6995
6996 AssertReturn(aControl, E_FAIL);
6997 AssertReturn(aProgress, E_FAIL);
6998
6999 AutoCaller autoCaller(this);
7000 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7001
7002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7003
7004 if (!mData->mRegistered)
7005 return setError(E_UNEXPECTED,
7006 tr("The machine '%s' is not registered"),
7007 mUserData->s.strName.c_str());
7008
7009 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7010
7011 if ( mData->mSession.mState == SessionState_Locked
7012 || mData->mSession.mState == SessionState_Spawning
7013 || mData->mSession.mState == SessionState_Unlocking)
7014 return setError(VBOX_E_INVALID_OBJECT_STATE,
7015 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7016 mUserData->s.strName.c_str());
7017
7018 /* may not be busy */
7019 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7020
7021 /* get the path to the executable */
7022 char szPath[RTPATH_MAX];
7023 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7024 size_t sz = strlen(szPath);
7025 szPath[sz++] = RTPATH_DELIMITER;
7026 szPath[sz] = 0;
7027 char *cmd = szPath + sz;
7028 sz = RTPATH_MAX - sz;
7029
7030 int vrc = VINF_SUCCESS;
7031 RTPROCESS pid = NIL_RTPROCESS;
7032
7033 RTENV env = RTENV_DEFAULT;
7034
7035 if (!strEnvironment.isEmpty())
7036 {
7037 char *newEnvStr = NULL;
7038
7039 do
7040 {
7041 /* clone the current environment */
7042 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7043 AssertRCBreakStmt(vrc2, vrc = vrc2);
7044
7045 newEnvStr = RTStrDup(strEnvironment.c_str());
7046 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7047
7048 /* put new variables to the environment
7049 * (ignore empty variable names here since RTEnv API
7050 * intentionally doesn't do that) */
7051 char *var = newEnvStr;
7052 for (char *p = newEnvStr; *p; ++p)
7053 {
7054 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7055 {
7056 *p = '\0';
7057 if (*var)
7058 {
7059 char *val = strchr(var, '=');
7060 if (val)
7061 {
7062 *val++ = '\0';
7063 vrc2 = RTEnvSetEx(env, var, val);
7064 }
7065 else
7066 vrc2 = RTEnvUnsetEx(env, var);
7067 if (RT_FAILURE(vrc2))
7068 break;
7069 }
7070 var = p + 1;
7071 }
7072 }
7073 if (RT_SUCCESS(vrc2) && *var)
7074 vrc2 = RTEnvPutEx(env, var);
7075
7076 AssertRCBreakStmt(vrc2, vrc = vrc2);
7077 }
7078 while (0);
7079
7080 if (newEnvStr != NULL)
7081 RTStrFree(newEnvStr);
7082 }
7083
7084 /* Qt is default */
7085#ifdef VBOX_WITH_QTGUI
7086 if (strType == "gui" || strType == "GUI/Qt")
7087 {
7088# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7089 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7090# else
7091 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7092# endif
7093 Assert(sz >= sizeof(VirtualBox_exe));
7094 strcpy(cmd, VirtualBox_exe);
7095
7096 Utf8Str idStr = mData->mUuid.toString();
7097 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7098 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7099 }
7100#else /* !VBOX_WITH_QTGUI */
7101 if (0)
7102 ;
7103#endif /* VBOX_WITH_QTGUI */
7104
7105 else
7106
7107#ifdef VBOX_WITH_VBOXSDL
7108 if (strType == "sdl" || strType == "GUI/SDL")
7109 {
7110 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7111 Assert(sz >= sizeof(VBoxSDL_exe));
7112 strcpy(cmd, VBoxSDL_exe);
7113
7114 Utf8Str idStr = mData->mUuid.toString();
7115 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7116 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7117 }
7118#else /* !VBOX_WITH_VBOXSDL */
7119 if (0)
7120 ;
7121#endif /* !VBOX_WITH_VBOXSDL */
7122
7123 else
7124
7125#ifdef VBOX_WITH_HEADLESS
7126 if ( strType == "headless"
7127 || strType == "capture"
7128 || strType == "vrdp" /* Deprecated. Same as headless. */
7129 )
7130 {
7131 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7132 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7133 * and a VM works even if the server has not been installed.
7134 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7135 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7136 * differently in 4.0 and 3.x.
7137 */
7138 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7139 Assert(sz >= sizeof(VBoxHeadless_exe));
7140 strcpy(cmd, VBoxHeadless_exe);
7141
7142 Utf8Str idStr = mData->mUuid.toString();
7143 /* Leave space for "--capture" arg. */
7144 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7145 "--startvm", idStr.c_str(),
7146 "--vrde", "config",
7147 0, /* For "--capture". */
7148 0 };
7149 if (strType == "capture")
7150 {
7151 unsigned pos = RT_ELEMENTS(args) - 2;
7152 args[pos] = "--capture";
7153 }
7154 vrc = RTProcCreate(szPath, args, env,
7155#ifdef RT_OS_WINDOWS
7156 RTPROC_FLAGS_NO_WINDOW
7157#else
7158 0
7159#endif
7160 , &pid);
7161 }
7162#else /* !VBOX_WITH_HEADLESS */
7163 if (0)
7164 ;
7165#endif /* !VBOX_WITH_HEADLESS */
7166 else
7167 {
7168 RTEnvDestroy(env);
7169 return setError(E_INVALIDARG,
7170 tr("Invalid session type: '%s'"),
7171 strType.c_str());
7172 }
7173
7174 RTEnvDestroy(env);
7175
7176 if (RT_FAILURE(vrc))
7177 return setError(VBOX_E_IPRT_ERROR,
7178 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7179 mUserData->s.strName.c_str(), vrc);
7180
7181 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7182
7183 /*
7184 * Note that we don't release the lock here before calling the client,
7185 * because it doesn't need to call us back if called with a NULL argument.
7186 * Releasing the lock here is dangerous because we didn't prepare the
7187 * launch data yet, but the client we've just started may happen to be
7188 * too fast and call openSession() that will fail (because of PID, etc.),
7189 * so that the Machine will never get out of the Spawning session state.
7190 */
7191
7192 /* inform the session that it will be a remote one */
7193 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7194 HRESULT rc = aControl->AssignMachine(NULL);
7195 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7196
7197 if (FAILED(rc))
7198 {
7199 /* restore the session state */
7200 mData->mSession.mState = SessionState_Unlocked;
7201 /* The failure may occur w/o any error info (from RPC), so provide one */
7202 return setError(VBOX_E_VM_ERROR,
7203 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7204 }
7205
7206 /* attach launch data to the machine */
7207 Assert(mData->mSession.mPid == NIL_RTPROCESS);
7208 mData->mSession.mRemoteControls.push_back(aControl);
7209 mData->mSession.mProgress = aProgress;
7210 mData->mSession.mPid = pid;
7211 mData->mSession.mState = SessionState_Spawning;
7212 mData->mSession.mType = strType;
7213
7214 LogFlowThisFuncLeave();
7215 return S_OK;
7216}
7217
7218/**
7219 * Returns @c true if the given machine has an open direct session and returns
7220 * the session machine instance and additional session data (on some platforms)
7221 * if so.
7222 *
7223 * Note that when the method returns @c false, the arguments remain unchanged.
7224 *
7225 * @param aMachine Session machine object.
7226 * @param aControl Direct session control object (optional).
7227 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7228 *
7229 * @note locks this object for reading.
7230 */
7231#if defined(RT_OS_WINDOWS)
7232bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7233 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7234 HANDLE *aIPCSem /*= NULL*/,
7235 bool aAllowClosing /*= false*/)
7236#elif defined(RT_OS_OS2)
7237bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7238 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7239 HMTX *aIPCSem /*= NULL*/,
7240 bool aAllowClosing /*= false*/)
7241#else
7242bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7243 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7244 bool aAllowClosing /*= false*/)
7245#endif
7246{
7247 AutoLimitedCaller autoCaller(this);
7248 AssertComRCReturn(autoCaller.rc(), false);
7249
7250 /* just return false for inaccessible machines */
7251 if (autoCaller.state() != Ready)
7252 return false;
7253
7254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7255
7256 if ( mData->mSession.mState == SessionState_Locked
7257 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7258 )
7259 {
7260 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7261
7262 aMachine = mData->mSession.mMachine;
7263
7264 if (aControl != NULL)
7265 *aControl = mData->mSession.mDirectControl;
7266
7267#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7268 /* Additional session data */
7269 if (aIPCSem != NULL)
7270 *aIPCSem = aMachine->mIPCSem;
7271#endif
7272 return true;
7273 }
7274
7275 return false;
7276}
7277
7278/**
7279 * Returns @c true if the given machine has an spawning direct session and
7280 * returns and additional session data (on some platforms) if so.
7281 *
7282 * Note that when the method returns @c false, the arguments remain unchanged.
7283 *
7284 * @param aPID PID of the spawned direct session process.
7285 *
7286 * @note locks this object for reading.
7287 */
7288#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7289bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7290#else
7291bool Machine::isSessionSpawning()
7292#endif
7293{
7294 AutoLimitedCaller autoCaller(this);
7295 AssertComRCReturn(autoCaller.rc(), false);
7296
7297 /* just return false for inaccessible machines */
7298 if (autoCaller.state() != Ready)
7299 return false;
7300
7301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7302
7303 if (mData->mSession.mState == SessionState_Spawning)
7304 {
7305#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7306 /* Additional session data */
7307 if (aPID != NULL)
7308 {
7309 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
7310 *aPID = mData->mSession.mPid;
7311 }
7312#endif
7313 return true;
7314 }
7315
7316 return false;
7317}
7318
7319/**
7320 * Called from the client watcher thread to check for unexpected client process
7321 * death during Session_Spawning state (e.g. before it successfully opened a
7322 * direct session).
7323 *
7324 * On Win32 and on OS/2, this method is called only when we've got the
7325 * direct client's process termination notification, so it always returns @c
7326 * true.
7327 *
7328 * On other platforms, this method returns @c true if the client process is
7329 * terminated and @c false if it's still alive.
7330 *
7331 * @note Locks this object for writing.
7332 */
7333bool Machine::checkForSpawnFailure()
7334{
7335 AutoCaller autoCaller(this);
7336 if (!autoCaller.isOk())
7337 {
7338 /* nothing to do */
7339 LogFlowThisFunc(("Already uninitialized!\n"));
7340 return true;
7341 }
7342
7343 /* VirtualBox::addProcessToReap() needs a write lock */
7344 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7345
7346 if (mData->mSession.mState != SessionState_Spawning)
7347 {
7348 /* nothing to do */
7349 LogFlowThisFunc(("Not spawning any more!\n"));
7350 return true;
7351 }
7352
7353 HRESULT rc = S_OK;
7354
7355#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7356
7357 /* the process was already unexpectedly terminated, we just need to set an
7358 * error and finalize session spawning */
7359 rc = setError(E_FAIL,
7360 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7361 getName().c_str());
7362#else
7363
7364 /* PID not yet initialized, skip check. */
7365 if (mData->mSession.mPid == NIL_RTPROCESS)
7366 return false;
7367
7368 RTPROCSTATUS status;
7369 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
7370 &status);
7371
7372 if (vrc != VERR_PROCESS_RUNNING)
7373 {
7374 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7375 rc = setError(E_FAIL,
7376 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7377 getName().c_str(), status.iStatus);
7378 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7379 rc = setError(E_FAIL,
7380 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7381 getName().c_str(), status.iStatus);
7382 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7383 rc = setError(E_FAIL,
7384 tr("The virtual machine '%s' has terminated abnormally"),
7385 getName().c_str(), status.iStatus);
7386 else
7387 rc = setError(E_FAIL,
7388 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7389 getName().c_str(), rc);
7390 }
7391
7392#endif
7393
7394 if (FAILED(rc))
7395 {
7396 /* Close the remote session, remove the remote control from the list
7397 * and reset session state to Closed (@note keep the code in sync with
7398 * the relevant part in checkForSpawnFailure()). */
7399
7400 Assert(mData->mSession.mRemoteControls.size() == 1);
7401 if (mData->mSession.mRemoteControls.size() == 1)
7402 {
7403 ErrorInfoKeeper eik;
7404 mData->mSession.mRemoteControls.front()->Uninitialize();
7405 }
7406
7407 mData->mSession.mRemoteControls.clear();
7408 mData->mSession.mState = SessionState_Unlocked;
7409
7410 /* finalize the progress after setting the state */
7411 if (!mData->mSession.mProgress.isNull())
7412 {
7413 mData->mSession.mProgress->notifyComplete(rc);
7414 mData->mSession.mProgress.setNull();
7415 }
7416
7417 mParent->addProcessToReap(mData->mSession.mPid);
7418 mData->mSession.mPid = NIL_RTPROCESS;
7419
7420 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7421 return true;
7422 }
7423
7424 return false;
7425}
7426
7427/**
7428 * Checks whether the machine can be registered. If so, commits and saves
7429 * all settings.
7430 *
7431 * @note Must be called from mParent's write lock. Locks this object and
7432 * children for writing.
7433 */
7434HRESULT Machine::prepareRegister()
7435{
7436 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7437
7438 AutoLimitedCaller autoCaller(this);
7439 AssertComRCReturnRC(autoCaller.rc());
7440
7441 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7442
7443 /* wait for state dependents to drop to zero */
7444 ensureNoStateDependencies();
7445
7446 if (!mData->mAccessible)
7447 return setError(VBOX_E_INVALID_OBJECT_STATE,
7448 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7449 mUserData->s.strName.c_str(),
7450 mData->mUuid.toString().c_str());
7451
7452 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7453
7454 if (mData->mRegistered)
7455 return setError(VBOX_E_INVALID_OBJECT_STATE,
7456 tr("The machine '%s' with UUID {%s} is already registered"),
7457 mUserData->s.strName.c_str(),
7458 mData->mUuid.toString().c_str());
7459
7460 HRESULT rc = S_OK;
7461
7462 // Ensure the settings are saved. If we are going to be registered and
7463 // no config file exists yet, create it by calling saveSettings() too.
7464 if ( (mData->flModifications)
7465 || (!mData->pMachineConfigFile->fileExists())
7466 )
7467 {
7468 rc = saveSettings(NULL);
7469 // no need to check whether VirtualBox.xml needs saving too since
7470 // we can't have a machine XML file rename pending
7471 if (FAILED(rc)) return rc;
7472 }
7473
7474 /* more config checking goes here */
7475
7476 if (SUCCEEDED(rc))
7477 {
7478 /* we may have had implicit modifications we want to fix on success */
7479 commit();
7480
7481 mData->mRegistered = true;
7482 }
7483 else
7484 {
7485 /* we may have had implicit modifications we want to cancel on failure*/
7486 rollback(false /* aNotify */);
7487 }
7488
7489 return rc;
7490}
7491
7492/**
7493 * Increases the number of objects dependent on the machine state or on the
7494 * registered state. Guarantees that these two states will not change at least
7495 * until #releaseStateDependency() is called.
7496 *
7497 * Depending on the @a aDepType value, additional state checks may be made.
7498 * These checks will set extended error info on failure. See
7499 * #checkStateDependency() for more info.
7500 *
7501 * If this method returns a failure, the dependency is not added and the caller
7502 * is not allowed to rely on any particular machine state or registration state
7503 * value and may return the failed result code to the upper level.
7504 *
7505 * @param aDepType Dependency type to add.
7506 * @param aState Current machine state (NULL if not interested).
7507 * @param aRegistered Current registered state (NULL if not interested).
7508 *
7509 * @note Locks this object for writing.
7510 */
7511HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7512 MachineState_T *aState /* = NULL */,
7513 BOOL *aRegistered /* = NULL */)
7514{
7515 AutoCaller autoCaller(this);
7516 AssertComRCReturnRC(autoCaller.rc());
7517
7518 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7519
7520 HRESULT rc = checkStateDependency(aDepType);
7521 if (FAILED(rc)) return rc;
7522
7523 {
7524 if (mData->mMachineStateChangePending != 0)
7525 {
7526 /* ensureNoStateDependencies() is waiting for state dependencies to
7527 * drop to zero so don't add more. It may make sense to wait a bit
7528 * and retry before reporting an error (since the pending state
7529 * transition should be really quick) but let's just assert for
7530 * now to see if it ever happens on practice. */
7531
7532 AssertFailed();
7533
7534 return setError(E_ACCESSDENIED,
7535 tr("Machine state change is in progress. Please retry the operation later."));
7536 }
7537
7538 ++mData->mMachineStateDeps;
7539 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7540 }
7541
7542 if (aState)
7543 *aState = mData->mMachineState;
7544 if (aRegistered)
7545 *aRegistered = mData->mRegistered;
7546
7547 return S_OK;
7548}
7549
7550/**
7551 * Decreases the number of objects dependent on the machine state.
7552 * Must always complete the #addStateDependency() call after the state
7553 * dependency is no more necessary.
7554 */
7555void Machine::releaseStateDependency()
7556{
7557 AutoCaller autoCaller(this);
7558 AssertComRCReturnVoid(autoCaller.rc());
7559
7560 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7561
7562 /* releaseStateDependency() w/o addStateDependency()? */
7563 AssertReturnVoid(mData->mMachineStateDeps != 0);
7564 -- mData->mMachineStateDeps;
7565
7566 if (mData->mMachineStateDeps == 0)
7567 {
7568 /* inform ensureNoStateDependencies() that there are no more deps */
7569 if (mData->mMachineStateChangePending != 0)
7570 {
7571 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7572 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7573 }
7574 }
7575}
7576
7577// protected methods
7578/////////////////////////////////////////////////////////////////////////////
7579
7580/**
7581 * Performs machine state checks based on the @a aDepType value. If a check
7582 * fails, this method will set extended error info, otherwise it will return
7583 * S_OK. It is supposed, that on failure, the caller will immediately return
7584 * the return value of this method to the upper level.
7585 *
7586 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7587 *
7588 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7589 * current state of this machine object allows to change settings of the
7590 * machine (i.e. the machine is not registered, or registered but not running
7591 * and not saved). It is useful to call this method from Machine setters
7592 * before performing any change.
7593 *
7594 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7595 * as for MutableStateDep except that if the machine is saved, S_OK is also
7596 * returned. This is useful in setters which allow changing machine
7597 * properties when it is in the saved state.
7598 *
7599 * @param aDepType Dependency type to check.
7600 *
7601 * @note Non Machine based classes should use #addStateDependency() and
7602 * #releaseStateDependency() methods or the smart AutoStateDependency
7603 * template.
7604 *
7605 * @note This method must be called from under this object's read or write
7606 * lock.
7607 */
7608HRESULT Machine::checkStateDependency(StateDependency aDepType)
7609{
7610 switch (aDepType)
7611 {
7612 case AnyStateDep:
7613 {
7614 break;
7615 }
7616 case MutableStateDep:
7617 {
7618 if ( mData->mRegistered
7619 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7620 || ( mData->mMachineState != MachineState_Paused
7621 && mData->mMachineState != MachineState_Running
7622 && mData->mMachineState != MachineState_Aborted
7623 && mData->mMachineState != MachineState_Teleported
7624 && mData->mMachineState != MachineState_PoweredOff
7625 )
7626 )
7627 )
7628 return setError(VBOX_E_INVALID_VM_STATE,
7629 tr("The machine is not mutable (state is %s)"),
7630 Global::stringifyMachineState(mData->mMachineState));
7631 break;
7632 }
7633 case MutableOrSavedStateDep:
7634 {
7635 if ( mData->mRegistered
7636 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7637 || ( mData->mMachineState != MachineState_Paused
7638 && mData->mMachineState != MachineState_Running
7639 && mData->mMachineState != MachineState_Aborted
7640 && mData->mMachineState != MachineState_Teleported
7641 && mData->mMachineState != MachineState_Saved
7642 && mData->mMachineState != MachineState_PoweredOff
7643 )
7644 )
7645 )
7646 return setError(VBOX_E_INVALID_VM_STATE,
7647 tr("The machine is not mutable (state is %s)"),
7648 Global::stringifyMachineState(mData->mMachineState));
7649 break;
7650 }
7651 }
7652
7653 return S_OK;
7654}
7655
7656/**
7657 * Helper to initialize all associated child objects and allocate data
7658 * structures.
7659 *
7660 * This method must be called as a part of the object's initialization procedure
7661 * (usually done in the #init() method).
7662 *
7663 * @note Must be called only from #init() or from #registeredInit().
7664 */
7665HRESULT Machine::initDataAndChildObjects()
7666{
7667 AutoCaller autoCaller(this);
7668 AssertComRCReturnRC(autoCaller.rc());
7669 AssertComRCReturn(autoCaller.state() == InInit ||
7670 autoCaller.state() == Limited, E_FAIL);
7671
7672 AssertReturn(!mData->mAccessible, E_FAIL);
7673
7674 /* allocate data structures */
7675 mSSData.allocate();
7676 mUserData.allocate();
7677 mHWData.allocate();
7678 mMediaData.allocate();
7679 mStorageControllers.allocate();
7680
7681 /* initialize mOSTypeId */
7682 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7683
7684 /* create associated BIOS settings object */
7685 unconst(mBIOSSettings).createObject();
7686 mBIOSSettings->init(this);
7687
7688 /* create an associated VRDE object (default is disabled) */
7689 unconst(mVRDEServer).createObject();
7690 mVRDEServer->init(this);
7691
7692 /* create associated serial port objects */
7693 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7694 {
7695 unconst(mSerialPorts[slot]).createObject();
7696 mSerialPorts[slot]->init(this, slot);
7697 }
7698
7699 /* create associated parallel port objects */
7700 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7701 {
7702 unconst(mParallelPorts[slot]).createObject();
7703 mParallelPorts[slot]->init(this, slot);
7704 }
7705
7706 /* create the audio adapter object (always present, default is disabled) */
7707 unconst(mAudioAdapter).createObject();
7708 mAudioAdapter->init(this);
7709
7710 /* create the USB controller object (always present, default is disabled) */
7711 unconst(mUSBController).createObject();
7712 mUSBController->init(this);
7713
7714 /* create associated network adapter objects */
7715 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7716 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7717 {
7718 unconst(mNetworkAdapters[slot]).createObject();
7719 mNetworkAdapters[slot]->init(this, slot);
7720 }
7721
7722 /* create the bandwidth control */
7723 unconst(mBandwidthControl).createObject();
7724 mBandwidthControl->init(this);
7725
7726 return S_OK;
7727}
7728
7729/**
7730 * Helper to uninitialize all associated child objects and to free all data
7731 * structures.
7732 *
7733 * This method must be called as a part of the object's uninitialization
7734 * procedure (usually done in the #uninit() method).
7735 *
7736 * @note Must be called only from #uninit() or from #registeredInit().
7737 */
7738void Machine::uninitDataAndChildObjects()
7739{
7740 AutoCaller autoCaller(this);
7741 AssertComRCReturnVoid(autoCaller.rc());
7742 AssertComRCReturnVoid( autoCaller.state() == InUninit
7743 || autoCaller.state() == Limited);
7744
7745 /* tell all our other child objects we've been uninitialized */
7746 if (mBandwidthControl)
7747 {
7748 mBandwidthControl->uninit();
7749 unconst(mBandwidthControl).setNull();
7750 }
7751
7752 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7753 {
7754 if (mNetworkAdapters[slot])
7755 {
7756 mNetworkAdapters[slot]->uninit();
7757 unconst(mNetworkAdapters[slot]).setNull();
7758 }
7759 }
7760
7761 if (mUSBController)
7762 {
7763 mUSBController->uninit();
7764 unconst(mUSBController).setNull();
7765 }
7766
7767 if (mAudioAdapter)
7768 {
7769 mAudioAdapter->uninit();
7770 unconst(mAudioAdapter).setNull();
7771 }
7772
7773 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7774 {
7775 if (mParallelPorts[slot])
7776 {
7777 mParallelPorts[slot]->uninit();
7778 unconst(mParallelPorts[slot]).setNull();
7779 }
7780 }
7781
7782 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7783 {
7784 if (mSerialPorts[slot])
7785 {
7786 mSerialPorts[slot]->uninit();
7787 unconst(mSerialPorts[slot]).setNull();
7788 }
7789 }
7790
7791 if (mVRDEServer)
7792 {
7793 mVRDEServer->uninit();
7794 unconst(mVRDEServer).setNull();
7795 }
7796
7797 if (mBIOSSettings)
7798 {
7799 mBIOSSettings->uninit();
7800 unconst(mBIOSSettings).setNull();
7801 }
7802
7803 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
7804 * instance is uninitialized; SessionMachine instances refer to real
7805 * Machine hard disks). This is necessary for a clean re-initialization of
7806 * the VM after successfully re-checking the accessibility state. Note
7807 * that in case of normal Machine or SnapshotMachine uninitialization (as
7808 * a result of unregistering or deleting the snapshot), outdated hard
7809 * disk attachments will already be uninitialized and deleted, so this
7810 * code will not affect them. */
7811 if ( !!mMediaData
7812 && (!isSessionMachine())
7813 )
7814 {
7815 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7816 it != mMediaData->mAttachments.end();
7817 ++it)
7818 {
7819 ComObjPtr<Medium> hd = (*it)->getMedium();
7820 if (hd.isNull())
7821 continue;
7822 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
7823 AssertComRC(rc);
7824 }
7825 }
7826
7827 if (!isSessionMachine() && !isSnapshotMachine())
7828 {
7829 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
7830 if (mData->mFirstSnapshot)
7831 {
7832 // snapshots tree is protected by media write lock; strictly
7833 // this isn't necessary here since we're deleting the entire
7834 // machine, but otherwise we assert in Snapshot::uninit()
7835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7836 mData->mFirstSnapshot->uninit();
7837 mData->mFirstSnapshot.setNull();
7838 }
7839
7840 mData->mCurrentSnapshot.setNull();
7841 }
7842
7843 /* free data structures (the essential mData structure is not freed here
7844 * since it may be still in use) */
7845 mMediaData.free();
7846 mStorageControllers.free();
7847 mHWData.free();
7848 mUserData.free();
7849 mSSData.free();
7850}
7851
7852/**
7853 * Returns a pointer to the Machine object for this machine that acts like a
7854 * parent for complex machine data objects such as shared folders, etc.
7855 *
7856 * For primary Machine objects and for SnapshotMachine objects, returns this
7857 * object's pointer itself. For SessionMachine objects, returns the peer
7858 * (primary) machine pointer.
7859 */
7860Machine* Machine::getMachine()
7861{
7862 if (isSessionMachine())
7863 return (Machine*)mPeer;
7864 return this;
7865}
7866
7867/**
7868 * Makes sure that there are no machine state dependents. If necessary, waits
7869 * for the number of dependents to drop to zero.
7870 *
7871 * Make sure this method is called from under this object's write lock to
7872 * guarantee that no new dependents may be added when this method returns
7873 * control to the caller.
7874 *
7875 * @note Locks this object for writing. The lock will be released while waiting
7876 * (if necessary).
7877 *
7878 * @warning To be used only in methods that change the machine state!
7879 */
7880void Machine::ensureNoStateDependencies()
7881{
7882 AssertReturnVoid(isWriteLockOnCurrentThread());
7883
7884 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7885
7886 /* Wait for all state dependents if necessary */
7887 if (mData->mMachineStateDeps != 0)
7888 {
7889 /* lazy semaphore creation */
7890 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
7891 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
7892
7893 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
7894 mData->mMachineStateDeps));
7895
7896 ++mData->mMachineStateChangePending;
7897
7898 /* reset the semaphore before waiting, the last dependent will signal
7899 * it */
7900 RTSemEventMultiReset(mData->mMachineStateDepsSem);
7901
7902 alock.release();
7903
7904 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
7905
7906 alock.acquire();
7907
7908 -- mData->mMachineStateChangePending;
7909 }
7910}
7911
7912/**
7913 * Changes the machine state and informs callbacks.
7914 *
7915 * This method is not intended to fail so it either returns S_OK or asserts (and
7916 * returns a failure).
7917 *
7918 * @note Locks this object for writing.
7919 */
7920HRESULT Machine::setMachineState(MachineState_T aMachineState)
7921{
7922 LogFlowThisFuncEnter();
7923 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
7924
7925 AutoCaller autoCaller(this);
7926 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7927
7928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7929
7930 /* wait for state dependents to drop to zero */
7931 ensureNoStateDependencies();
7932
7933 if (mData->mMachineState != aMachineState)
7934 {
7935 mData->mMachineState = aMachineState;
7936
7937 RTTimeNow(&mData->mLastStateChange);
7938
7939 mParent->onMachineStateChange(mData->mUuid, aMachineState);
7940 }
7941
7942 LogFlowThisFuncLeave();
7943 return S_OK;
7944}
7945
7946/**
7947 * Searches for a shared folder with the given logical name
7948 * in the collection of shared folders.
7949 *
7950 * @param aName logical name of the shared folder
7951 * @param aSharedFolder where to return the found object
7952 * @param aSetError whether to set the error info if the folder is
7953 * not found
7954 * @return
7955 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
7956 *
7957 * @note
7958 * must be called from under the object's lock!
7959 */
7960HRESULT Machine::findSharedFolder(const Utf8Str &aName,
7961 ComObjPtr<SharedFolder> &aSharedFolder,
7962 bool aSetError /* = false */)
7963{
7964 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
7965 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
7966 it != mHWData->mSharedFolders.end();
7967 ++it)
7968 {
7969 SharedFolder *pSF = *it;
7970 AutoCaller autoCaller(pSF);
7971 if (pSF->getName() == aName)
7972 {
7973 aSharedFolder = pSF;
7974 rc = S_OK;
7975 break;
7976 }
7977 }
7978
7979 if (aSetError && FAILED(rc))
7980 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
7981
7982 return rc;
7983}
7984
7985/**
7986 * Initializes all machine instance data from the given settings structures
7987 * from XML. The exception is the machine UUID which needs special handling
7988 * depending on the caller's use case, so the caller needs to set that herself.
7989 *
7990 * This gets called in several contexts during machine initialization:
7991 *
7992 * -- When machine XML exists on disk already and needs to be loaded into memory,
7993 * for example, from registeredInit() to load all registered machines on
7994 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
7995 * attached to the machine should be part of some media registry already.
7996 *
7997 * -- During OVF import, when a machine config has been constructed from an
7998 * OVF file. In this case, puuidRegistry is set to the machine UUID to
7999 * ensure that the media listed as attachments in the config (which have
8000 * been imported from the OVF) receive the correct registry ID.
8001 *
8002 * -- During VM cloning.
8003 *
8004 * @param config Machine settings from XML.
8005 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8006 * @return
8007 */
8008HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8009 const Guid *puuidRegistry)
8010{
8011 // copy name, description, OS type, teleporter, UTC etc.
8012 mUserData->s = config.machineUserData;
8013
8014 // look up the object by Id to check it is valid
8015 ComPtr<IGuestOSType> guestOSType;
8016 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8017 guestOSType.asOutParam());
8018 if (FAILED(rc)) return rc;
8019
8020 // stateFile (optional)
8021 if (config.strStateFile.isEmpty())
8022 mSSData->strStateFilePath.setNull();
8023 else
8024 {
8025 Utf8Str stateFilePathFull(config.strStateFile);
8026 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8027 if (RT_FAILURE(vrc))
8028 return setError(E_FAIL,
8029 tr("Invalid saved state file path '%s' (%Rrc)"),
8030 config.strStateFile.c_str(),
8031 vrc);
8032 mSSData->strStateFilePath = stateFilePathFull;
8033 }
8034
8035 // snapshot folder needs special processing so set it again
8036 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8037 if (FAILED(rc)) return rc;
8038
8039 /* Copy the extra data items (Not in any case config is already the same as
8040 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8041 * make sure the extra data map is copied). */
8042 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8043
8044 /* currentStateModified (optional, default is true) */
8045 mData->mCurrentStateModified = config.fCurrentStateModified;
8046
8047 mData->mLastStateChange = config.timeLastStateChange;
8048
8049 /*
8050 * note: all mUserData members must be assigned prior this point because
8051 * we need to commit changes in order to let mUserData be shared by all
8052 * snapshot machine instances.
8053 */
8054 mUserData.commitCopy();
8055
8056 // machine registry, if present (must be loaded before snapshots)
8057 if (config.canHaveOwnMediaRegistry())
8058 {
8059 // determine machine folder
8060 Utf8Str strMachineFolder = getSettingsFileFull();
8061 strMachineFolder.stripFilename();
8062 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8063 config.mediaRegistry,
8064 strMachineFolder);
8065 if (FAILED(rc)) return rc;
8066 }
8067
8068 /* Snapshot node (optional) */
8069 size_t cRootSnapshots;
8070 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8071 {
8072 // there must be only one root snapshot
8073 Assert(cRootSnapshots == 1);
8074
8075 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8076
8077 rc = loadSnapshot(snap,
8078 config.uuidCurrentSnapshot,
8079 NULL); // no parent == first snapshot
8080 if (FAILED(rc)) return rc;
8081 }
8082
8083 // hardware data
8084 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8085 if (FAILED(rc)) return rc;
8086
8087 // load storage controllers
8088 rc = loadStorageControllers(config.storageMachine,
8089 puuidRegistry,
8090 NULL /* puuidSnapshot */);
8091 if (FAILED(rc)) return rc;
8092
8093 /*
8094 * NOTE: the assignment below must be the last thing to do,
8095 * otherwise it will be not possible to change the settings
8096 * somewhere in the code above because all setters will be
8097 * blocked by checkStateDependency(MutableStateDep).
8098 */
8099
8100 /* set the machine state to Aborted or Saved when appropriate */
8101 if (config.fAborted)
8102 {
8103 mSSData->strStateFilePath.setNull();
8104
8105 /* no need to use setMachineState() during init() */
8106 mData->mMachineState = MachineState_Aborted;
8107 }
8108 else if (!mSSData->strStateFilePath.isEmpty())
8109 {
8110 /* no need to use setMachineState() during init() */
8111 mData->mMachineState = MachineState_Saved;
8112 }
8113
8114 // after loading settings, we are no longer different from the XML on disk
8115 mData->flModifications = 0;
8116
8117 return S_OK;
8118}
8119
8120/**
8121 * Recursively loads all snapshots starting from the given.
8122 *
8123 * @param aNode <Snapshot> node.
8124 * @param aCurSnapshotId Current snapshot ID from the settings file.
8125 * @param aParentSnapshot Parent snapshot.
8126 */
8127HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8128 const Guid &aCurSnapshotId,
8129 Snapshot *aParentSnapshot)
8130{
8131 AssertReturn(!isSnapshotMachine(), E_FAIL);
8132 AssertReturn(!isSessionMachine(), E_FAIL);
8133
8134 HRESULT rc = S_OK;
8135
8136 Utf8Str strStateFile;
8137 if (!data.strStateFile.isEmpty())
8138 {
8139 /* optional */
8140 strStateFile = data.strStateFile;
8141 int vrc = calculateFullPath(strStateFile, strStateFile);
8142 if (RT_FAILURE(vrc))
8143 return setError(E_FAIL,
8144 tr("Invalid saved state file path '%s' (%Rrc)"),
8145 strStateFile.c_str(),
8146 vrc);
8147 }
8148
8149 /* create a snapshot machine object */
8150 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8151 pSnapshotMachine.createObject();
8152 rc = pSnapshotMachine->initFromSettings(this,
8153 data.hardware,
8154 &data.debugging,
8155 &data.autostart,
8156 data.storage,
8157 data.uuid.ref(),
8158 strStateFile);
8159 if (FAILED(rc)) return rc;
8160
8161 /* create a snapshot object */
8162 ComObjPtr<Snapshot> pSnapshot;
8163 pSnapshot.createObject();
8164 /* initialize the snapshot */
8165 rc = pSnapshot->init(mParent, // VirtualBox object
8166 data.uuid,
8167 data.strName,
8168 data.strDescription,
8169 data.timestamp,
8170 pSnapshotMachine,
8171 aParentSnapshot);
8172 if (FAILED(rc)) return rc;
8173
8174 /* memorize the first snapshot if necessary */
8175 if (!mData->mFirstSnapshot)
8176 mData->mFirstSnapshot = pSnapshot;
8177
8178 /* memorize the current snapshot when appropriate */
8179 if ( !mData->mCurrentSnapshot
8180 && pSnapshot->getId() == aCurSnapshotId
8181 )
8182 mData->mCurrentSnapshot = pSnapshot;
8183
8184 // now create the children
8185 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8186 it != data.llChildSnapshots.end();
8187 ++it)
8188 {
8189 const settings::Snapshot &childData = *it;
8190 // recurse
8191 rc = loadSnapshot(childData,
8192 aCurSnapshotId,
8193 pSnapshot); // parent = the one we created above
8194 if (FAILED(rc)) return rc;
8195 }
8196
8197 return rc;
8198}
8199
8200/**
8201 * Loads settings into mHWData.
8202 *
8203 * @param data Reference to the hardware settings.
8204 * @param pDbg Pointer to the debugging settings.
8205 * @param pAutostart Pointer to the autostart settings.
8206 */
8207HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8208 const settings::Autostart *pAutostart)
8209{
8210 AssertReturn(!isSessionMachine(), E_FAIL);
8211
8212 HRESULT rc = S_OK;
8213
8214 try
8215 {
8216 /* The hardware version attribute (optional). */
8217 mHWData->mHWVersion = data.strVersion;
8218 mHWData->mHardwareUUID = data.uuid;
8219
8220 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8221 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8222 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8223 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8224 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8225 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8226 mHWData->mPAEEnabled = data.fPAE;
8227 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8228
8229 mHWData->mCPUCount = data.cCPUs;
8230 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8231 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8232
8233 // cpu
8234 if (mHWData->mCPUHotPlugEnabled)
8235 {
8236 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8237 it != data.llCpus.end();
8238 ++it)
8239 {
8240 const settings::Cpu &cpu = *it;
8241
8242 mHWData->mCPUAttached[cpu.ulId] = true;
8243 }
8244 }
8245
8246 // cpuid leafs
8247 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8248 it != data.llCpuIdLeafs.end();
8249 ++it)
8250 {
8251 const settings::CpuIdLeaf &leaf = *it;
8252
8253 switch (leaf.ulId)
8254 {
8255 case 0x0:
8256 case 0x1:
8257 case 0x2:
8258 case 0x3:
8259 case 0x4:
8260 case 0x5:
8261 case 0x6:
8262 case 0x7:
8263 case 0x8:
8264 case 0x9:
8265 case 0xA:
8266 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8267 break;
8268
8269 case 0x80000000:
8270 case 0x80000001:
8271 case 0x80000002:
8272 case 0x80000003:
8273 case 0x80000004:
8274 case 0x80000005:
8275 case 0x80000006:
8276 case 0x80000007:
8277 case 0x80000008:
8278 case 0x80000009:
8279 case 0x8000000A:
8280 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8281 break;
8282
8283 default:
8284 /* just ignore */
8285 break;
8286 }
8287 }
8288
8289 mHWData->mMemorySize = data.ulMemorySizeMB;
8290 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8291
8292 // boot order
8293 for (size_t i = 0;
8294 i < RT_ELEMENTS(mHWData->mBootOrder);
8295 i++)
8296 {
8297 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8298 if (it == data.mapBootOrder.end())
8299 mHWData->mBootOrder[i] = DeviceType_Null;
8300 else
8301 mHWData->mBootOrder[i] = it->second;
8302 }
8303
8304 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8305 mHWData->mMonitorCount = data.cMonitors;
8306 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8307 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8308 mHWData->mFirmwareType = data.firmwareType;
8309 mHWData->mPointingHidType = data.pointingHidType;
8310 mHWData->mKeyboardHidType = data.keyboardHidType;
8311 mHWData->mChipsetType = data.chipsetType;
8312 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8313 mHWData->mHpetEnabled = data.fHpetEnabled;
8314
8315 /* VRDEServer */
8316 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8317 if (FAILED(rc)) return rc;
8318
8319 /* BIOS */
8320 rc = mBIOSSettings->loadSettings(data.biosSettings);
8321 if (FAILED(rc)) return rc;
8322
8323 // Bandwidth control (must come before network adapters)
8324 rc = mBandwidthControl->loadSettings(data.ioSettings);
8325 if (FAILED(rc)) return rc;
8326
8327 /* USB Controller */
8328 rc = mUSBController->loadSettings(data.usbController);
8329 if (FAILED(rc)) return rc;
8330
8331 // network adapters
8332 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8333 uint32_t oldCount = mNetworkAdapters.size();
8334 if (newCount > oldCount)
8335 {
8336 mNetworkAdapters.resize(newCount);
8337 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8338 {
8339 unconst(mNetworkAdapters[slot]).createObject();
8340 mNetworkAdapters[slot]->init(this, slot);
8341 }
8342 }
8343 else if (newCount < oldCount)
8344 mNetworkAdapters.resize(newCount);
8345 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8346 it != data.llNetworkAdapters.end();
8347 ++it)
8348 {
8349 const settings::NetworkAdapter &nic = *it;
8350
8351 /* slot unicity is guaranteed by XML Schema */
8352 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8353 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8354 if (FAILED(rc)) return rc;
8355 }
8356
8357 // serial ports
8358 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8359 it != data.llSerialPorts.end();
8360 ++it)
8361 {
8362 const settings::SerialPort &s = *it;
8363
8364 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8365 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8366 if (FAILED(rc)) return rc;
8367 }
8368
8369 // parallel ports (optional)
8370 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8371 it != data.llParallelPorts.end();
8372 ++it)
8373 {
8374 const settings::ParallelPort &p = *it;
8375
8376 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8377 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8378 if (FAILED(rc)) return rc;
8379 }
8380
8381 /* AudioAdapter */
8382 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8383 if (FAILED(rc)) return rc;
8384
8385 /* Shared folders */
8386 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8387 it != data.llSharedFolders.end();
8388 ++it)
8389 {
8390 const settings::SharedFolder &sf = *it;
8391
8392 ComObjPtr<SharedFolder> sharedFolder;
8393 /* Check for double entries. Not allowed! */
8394 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8395 if (SUCCEEDED(rc))
8396 return setError(VBOX_E_OBJECT_IN_USE,
8397 tr("Shared folder named '%s' already exists"),
8398 sf.strName.c_str());
8399
8400 /* Create the new shared folder. Don't break on error. This will be
8401 * reported when the machine starts. */
8402 sharedFolder.createObject();
8403 rc = sharedFolder->init(getMachine(),
8404 sf.strName,
8405 sf.strHostPath,
8406 RT_BOOL(sf.fWritable),
8407 RT_BOOL(sf.fAutoMount),
8408 false /* fFailOnError */);
8409 if (FAILED(rc)) return rc;
8410 mHWData->mSharedFolders.push_back(sharedFolder);
8411 }
8412
8413 // Clipboard
8414 mHWData->mClipboardMode = data.clipboardMode;
8415
8416 // guest settings
8417 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8418
8419 // IO settings
8420 mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled;
8421 mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize;
8422
8423 // Host PCI devices
8424 for (settings::HostPciDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8425 it != data.pciAttachments.end();
8426 ++it)
8427 {
8428 const settings::HostPciDeviceAttachment &hpda = *it;
8429 ComObjPtr<PciDeviceAttachment> pda;
8430
8431 pda.createObject();
8432 pda->loadSettings(this, hpda);
8433 mHWData->mPciDeviceAssignments.push_back(pda);
8434 }
8435
8436 /*
8437 * (The following isn't really real hardware, but it lives in HWData
8438 * for reasons of convenience.)
8439 */
8440
8441#ifdef VBOX_WITH_GUEST_PROPS
8442 /* Guest properties (optional) */
8443 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8444 it != data.llGuestProperties.end();
8445 ++it)
8446 {
8447 const settings::GuestProperty &prop = *it;
8448 uint32_t fFlags = guestProp::NILFLAG;
8449 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8450 HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags };
8451 mHWData->mGuestProperties.push_back(property);
8452 }
8453
8454 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8455#endif /* VBOX_WITH_GUEST_PROPS defined */
8456
8457 rc = loadDebugging(pDbg);
8458 if (FAILED(rc))
8459 return rc;
8460
8461 mHWData->mAutostart = *pAutostart;
8462 }
8463 catch(std::bad_alloc &)
8464 {
8465 return E_OUTOFMEMORY;
8466 }
8467
8468 AssertComRC(rc);
8469 return rc;
8470}
8471
8472/**
8473 * Called from Machine::loadHardware() to load the debugging settings of the
8474 * machine.
8475 *
8476 * @param pDbg Pointer to the settings.
8477 */
8478HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8479{
8480 mHWData->mDebugging = *pDbg;
8481 /* no more processing currently required, this will probably change. */
8482 return S_OK;
8483}
8484
8485/**
8486 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8487 *
8488 * @param data
8489 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8490 * @param puuidSnapshot
8491 * @return
8492 */
8493HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8494 const Guid *puuidRegistry,
8495 const Guid *puuidSnapshot)
8496{
8497 AssertReturn(!isSessionMachine(), E_FAIL);
8498
8499 HRESULT rc = S_OK;
8500
8501 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8502 it != data.llStorageControllers.end();
8503 ++it)
8504 {
8505 const settings::StorageController &ctlData = *it;
8506
8507 ComObjPtr<StorageController> pCtl;
8508 /* Try to find one with the name first. */
8509 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8510 if (SUCCEEDED(rc))
8511 return setError(VBOX_E_OBJECT_IN_USE,
8512 tr("Storage controller named '%s' already exists"),
8513 ctlData.strName.c_str());
8514
8515 pCtl.createObject();
8516 rc = pCtl->init(this,
8517 ctlData.strName,
8518 ctlData.storageBus,
8519 ctlData.ulInstance,
8520 ctlData.fBootable);
8521 if (FAILED(rc)) return rc;
8522
8523 mStorageControllers->push_back(pCtl);
8524
8525 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8526 if (FAILED(rc)) return rc;
8527
8528 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8529 if (FAILED(rc)) return rc;
8530
8531 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8532 if (FAILED(rc)) return rc;
8533
8534 /* Set IDE emulation settings (only for AHCI controller). */
8535 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8536 {
8537 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8538 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8539 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8540 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8541 )
8542 return rc;
8543 }
8544
8545 /* Load the attached devices now. */
8546 rc = loadStorageDevices(pCtl,
8547 ctlData,
8548 puuidRegistry,
8549 puuidSnapshot);
8550 if (FAILED(rc)) return rc;
8551 }
8552
8553 return S_OK;
8554}
8555
8556/**
8557 * Called from loadStorageControllers for a controller's devices.
8558 *
8559 * @param aStorageController
8560 * @param data
8561 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8562 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8563 * @return
8564 */
8565HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8566 const settings::StorageController &data,
8567 const Guid *puuidRegistry,
8568 const Guid *puuidSnapshot)
8569{
8570 HRESULT rc = S_OK;
8571
8572 /* paranoia: detect duplicate attachments */
8573 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8574 it != data.llAttachedDevices.end();
8575 ++it)
8576 {
8577 const settings::AttachedDevice &ad = *it;
8578
8579 for (settings::AttachedDevicesList::const_iterator it2 = it;
8580 it2 != data.llAttachedDevices.end();
8581 ++it2)
8582 {
8583 if (it == it2)
8584 continue;
8585
8586 const settings::AttachedDevice &ad2 = *it2;
8587
8588 if ( ad.lPort == ad2.lPort
8589 && ad.lDevice == ad2.lDevice)
8590 {
8591 return setError(E_FAIL,
8592 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8593 aStorageController->getName().c_str(),
8594 ad.lPort,
8595 ad.lDevice,
8596 mUserData->s.strName.c_str());
8597 }
8598 }
8599 }
8600
8601 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8602 it != data.llAttachedDevices.end();
8603 ++it)
8604 {
8605 const settings::AttachedDevice &dev = *it;
8606 ComObjPtr<Medium> medium;
8607
8608 switch (dev.deviceType)
8609 {
8610 case DeviceType_Floppy:
8611 case DeviceType_DVD:
8612 if (dev.strHostDriveSrc.isNotEmpty())
8613 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8614 else
8615 rc = mParent->findRemoveableMedium(dev.deviceType,
8616 dev.uuid,
8617 false /* fRefresh */,
8618 false /* aSetError */,
8619 medium);
8620 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8621 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8622 rc = S_OK;
8623 break;
8624
8625 case DeviceType_HardDisk:
8626 {
8627 /* find a hard disk by UUID */
8628 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8629 if (FAILED(rc))
8630 {
8631 if (isSnapshotMachine())
8632 {
8633 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8634 // so the user knows that the bad disk is in a snapshot somewhere
8635 com::ErrorInfo info;
8636 return setError(E_FAIL,
8637 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8638 puuidSnapshot->raw(),
8639 info.getText().raw());
8640 }
8641 else
8642 return rc;
8643 }
8644
8645 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8646
8647 if (medium->getType() == MediumType_Immutable)
8648 {
8649 if (isSnapshotMachine())
8650 return setError(E_FAIL,
8651 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8652 "of the virtual machine '%s' ('%s')"),
8653 medium->getLocationFull().c_str(),
8654 dev.uuid.raw(),
8655 puuidSnapshot->raw(),
8656 mUserData->s.strName.c_str(),
8657 mData->m_strConfigFileFull.c_str());
8658
8659 return setError(E_FAIL,
8660 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8661 medium->getLocationFull().c_str(),
8662 dev.uuid.raw(),
8663 mUserData->s.strName.c_str(),
8664 mData->m_strConfigFileFull.c_str());
8665 }
8666
8667 if (medium->getType() == MediumType_MultiAttach)
8668 {
8669 if (isSnapshotMachine())
8670 return setError(E_FAIL,
8671 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8672 "of the virtual machine '%s' ('%s')"),
8673 medium->getLocationFull().c_str(),
8674 dev.uuid.raw(),
8675 puuidSnapshot->raw(),
8676 mUserData->s.strName.c_str(),
8677 mData->m_strConfigFileFull.c_str());
8678
8679 return setError(E_FAIL,
8680 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8681 medium->getLocationFull().c_str(),
8682 dev.uuid.raw(),
8683 mUserData->s.strName.c_str(),
8684 mData->m_strConfigFileFull.c_str());
8685 }
8686
8687 if ( !isSnapshotMachine()
8688 && medium->getChildren().size() != 0
8689 )
8690 return setError(E_FAIL,
8691 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8692 "because it has %d differencing child hard disks"),
8693 medium->getLocationFull().c_str(),
8694 dev.uuid.raw(),
8695 mUserData->s.strName.c_str(),
8696 mData->m_strConfigFileFull.c_str(),
8697 medium->getChildren().size());
8698
8699 if (findAttachment(mMediaData->mAttachments,
8700 medium))
8701 return setError(E_FAIL,
8702 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8703 medium->getLocationFull().c_str(),
8704 dev.uuid.raw(),
8705 mUserData->s.strName.c_str(),
8706 mData->m_strConfigFileFull.c_str());
8707
8708 break;
8709 }
8710
8711 default:
8712 return setError(E_FAIL,
8713 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8714 medium->getLocationFull().c_str(),
8715 mUserData->s.strName.c_str(),
8716 mData->m_strConfigFileFull.c_str());
8717 }
8718
8719 if (FAILED(rc))
8720 break;
8721
8722 /* Bandwidth groups are loaded at this point. */
8723 ComObjPtr<BandwidthGroup> pBwGroup;
8724
8725 if (!dev.strBwGroup.isEmpty())
8726 {
8727 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8728 if (FAILED(rc))
8729 return setError(E_FAIL,
8730 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8731 medium->getLocationFull().c_str(),
8732 dev.strBwGroup.c_str(),
8733 mUserData->s.strName.c_str(),
8734 mData->m_strConfigFileFull.c_str());
8735 pBwGroup->reference();
8736 }
8737
8738 const Bstr controllerName = aStorageController->getName();
8739 ComObjPtr<MediumAttachment> pAttachment;
8740 pAttachment.createObject();
8741 rc = pAttachment->init(this,
8742 medium,
8743 controllerName,
8744 dev.lPort,
8745 dev.lDevice,
8746 dev.deviceType,
8747 false,
8748 dev.fPassThrough,
8749 dev.fTempEject,
8750 dev.fNonRotational,
8751 dev.fDiscard,
8752 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8753 if (FAILED(rc)) break;
8754
8755 /* associate the medium with this machine and snapshot */
8756 if (!medium.isNull())
8757 {
8758 AutoCaller medCaller(medium);
8759 if (FAILED(medCaller.rc())) return medCaller.rc();
8760 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8761
8762 if (isSnapshotMachine())
8763 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8764 else
8765 rc = medium->addBackReference(mData->mUuid);
8766 /* If the medium->addBackReference fails it sets an appropriate
8767 * error message, so no need to do any guesswork here. */
8768
8769 if (puuidRegistry)
8770 // caller wants registry ID to be set on all attached media (OVF import case)
8771 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
8772 }
8773
8774 if (FAILED(rc))
8775 break;
8776
8777 /* back up mMediaData to let registeredInit() properly rollback on failure
8778 * (= limited accessibility) */
8779 setModified(IsModified_Storage);
8780 mMediaData.backup();
8781 mMediaData->mAttachments.push_back(pAttachment);
8782 }
8783
8784 return rc;
8785}
8786
8787/**
8788 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
8789 *
8790 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
8791 * @param aSnapshot where to return the found snapshot
8792 * @param aSetError true to set extended error info on failure
8793 */
8794HRESULT Machine::findSnapshotById(const Guid &aId,
8795 ComObjPtr<Snapshot> &aSnapshot,
8796 bool aSetError /* = false */)
8797{
8798 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8799
8800 if (!mData->mFirstSnapshot)
8801 {
8802 if (aSetError)
8803 return setError(E_FAIL, tr("This machine does not have any snapshots"));
8804 return E_FAIL;
8805 }
8806
8807 if (aId.isEmpty())
8808 aSnapshot = mData->mFirstSnapshot;
8809 else
8810 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
8811
8812 if (!aSnapshot)
8813 {
8814 if (aSetError)
8815 return setError(E_FAIL,
8816 tr("Could not find a snapshot with UUID {%s}"),
8817 aId.toString().c_str());
8818 return E_FAIL;
8819 }
8820
8821 return S_OK;
8822}
8823
8824/**
8825 * Returns the snapshot with the given name or fails of no such snapshot.
8826 *
8827 * @param aName snapshot name to find
8828 * @param aSnapshot where to return the found snapshot
8829 * @param aSetError true to set extended error info on failure
8830 */
8831HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
8832 ComObjPtr<Snapshot> &aSnapshot,
8833 bool aSetError /* = false */)
8834{
8835 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
8836
8837 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8838
8839 if (!mData->mFirstSnapshot)
8840 {
8841 if (aSetError)
8842 return setError(VBOX_E_OBJECT_NOT_FOUND,
8843 tr("This machine does not have any snapshots"));
8844 return VBOX_E_OBJECT_NOT_FOUND;
8845 }
8846
8847 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
8848
8849 if (!aSnapshot)
8850 {
8851 if (aSetError)
8852 return setError(VBOX_E_OBJECT_NOT_FOUND,
8853 tr("Could not find a snapshot named '%s'"), strName.c_str());
8854 return VBOX_E_OBJECT_NOT_FOUND;
8855 }
8856
8857 return S_OK;
8858}
8859
8860/**
8861 * Returns a storage controller object with the given name.
8862 *
8863 * @param aName storage controller name to find
8864 * @param aStorageController where to return the found storage controller
8865 * @param aSetError true to set extended error info on failure
8866 */
8867HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
8868 ComObjPtr<StorageController> &aStorageController,
8869 bool aSetError /* = false */)
8870{
8871 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
8872
8873 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
8874 it != mStorageControllers->end();
8875 ++it)
8876 {
8877 if ((*it)->getName() == aName)
8878 {
8879 aStorageController = (*it);
8880 return S_OK;
8881 }
8882 }
8883
8884 if (aSetError)
8885 return setError(VBOX_E_OBJECT_NOT_FOUND,
8886 tr("Could not find a storage controller named '%s'"),
8887 aName.c_str());
8888 return VBOX_E_OBJECT_NOT_FOUND;
8889}
8890
8891HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
8892 MediaData::AttachmentList &atts)
8893{
8894 AutoCaller autoCaller(this);
8895 if (FAILED(autoCaller.rc())) return autoCaller.rc();
8896
8897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8898
8899 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
8900 it != mMediaData->mAttachments.end();
8901 ++it)
8902 {
8903 const ComObjPtr<MediumAttachment> &pAtt = *it;
8904
8905 // should never happen, but deal with NULL pointers in the list.
8906 AssertStmt(!pAtt.isNull(), continue);
8907
8908 // getControllerName() needs caller+read lock
8909 AutoCaller autoAttCaller(pAtt);
8910 if (FAILED(autoAttCaller.rc()))
8911 {
8912 atts.clear();
8913 return autoAttCaller.rc();
8914 }
8915 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
8916
8917 if (pAtt->getControllerName() == aName)
8918 atts.push_back(pAtt);
8919 }
8920
8921 return S_OK;
8922}
8923
8924/**
8925 * Helper for #saveSettings. Cares about renaming the settings directory and
8926 * file if the machine name was changed and about creating a new settings file
8927 * if this is a new machine.
8928 *
8929 * @note Must be never called directly but only from #saveSettings().
8930 */
8931HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
8932{
8933 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8934
8935 HRESULT rc = S_OK;
8936
8937 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
8938
8939 /// @todo need to handle primary group change, too
8940
8941 /* attempt to rename the settings file if machine name is changed */
8942 if ( mUserData->s.fNameSync
8943 && mUserData.isBackedUp()
8944 && mUserData.backedUpData()->s.strName != mUserData->s.strName
8945 )
8946 {
8947 bool dirRenamed = false;
8948 bool fileRenamed = false;
8949
8950 Utf8Str configFile, newConfigFile;
8951 Utf8Str configFilePrev, newConfigFilePrev;
8952 Utf8Str configDir, newConfigDir;
8953
8954 do
8955 {
8956 int vrc = VINF_SUCCESS;
8957
8958 Utf8Str name = mUserData.backedUpData()->s.strName;
8959 Utf8Str newName = mUserData->s.strName;
8960
8961 configFile = mData->m_strConfigFileFull;
8962
8963 /* first, rename the directory if it matches the machine name */
8964 configDir = configFile;
8965 configDir.stripFilename();
8966 newConfigDir = configDir;
8967 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str()))
8968 {
8969 newConfigDir.stripFilename();
8970 newConfigDir.append(RTPATH_DELIMITER);
8971 newConfigDir.append(newName);
8972 /* new dir and old dir cannot be equal here because of 'if'
8973 * above and because name != newName */
8974 Assert(configDir != newConfigDir);
8975 if (!fSettingsFileIsNew)
8976 {
8977 /* perform real rename only if the machine is not new */
8978 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
8979 if (RT_FAILURE(vrc))
8980 {
8981 rc = setError(E_FAIL,
8982 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
8983 configDir.c_str(),
8984 newConfigDir.c_str(),
8985 vrc);
8986 break;
8987 }
8988 dirRenamed = true;
8989 }
8990 }
8991
8992 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
8993 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
8994
8995 /* then try to rename the settings file itself */
8996 if (newConfigFile != configFile)
8997 {
8998 /* get the path to old settings file in renamed directory */
8999 configFile = Utf8StrFmt("%s%c%s",
9000 newConfigDir.c_str(),
9001 RTPATH_DELIMITER,
9002 RTPathFilename(configFile.c_str()));
9003 if (!fSettingsFileIsNew)
9004 {
9005 /* perform real rename only if the machine is not new */
9006 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9007 if (RT_FAILURE(vrc))
9008 {
9009 rc = setError(E_FAIL,
9010 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9011 configFile.c_str(),
9012 newConfigFile.c_str(),
9013 vrc);
9014 break;
9015 }
9016 fileRenamed = true;
9017 configFilePrev = configFile;
9018 configFilePrev += "-prev";
9019 newConfigFilePrev = newConfigFile;
9020 newConfigFilePrev += "-prev";
9021 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9022 }
9023 }
9024
9025 // update m_strConfigFileFull amd mConfigFile
9026 mData->m_strConfigFileFull = newConfigFile;
9027 // compute the relative path too
9028 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9029
9030 // store the old and new so that VirtualBox::saveSettings() can update
9031 // the media registry
9032 if ( mData->mRegistered
9033 && configDir != newConfigDir)
9034 {
9035 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9036
9037 if (pfNeedsGlobalSaveSettings)
9038 *pfNeedsGlobalSaveSettings = true;
9039 }
9040
9041 // in the saved state file path, replace the old directory with the new directory
9042 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9043 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9044
9045 // and do the same thing for the saved state file paths of all the online snapshots
9046 if (mData->mFirstSnapshot)
9047 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9048 newConfigDir.c_str());
9049 }
9050 while (0);
9051
9052 if (FAILED(rc))
9053 {
9054 /* silently try to rename everything back */
9055 if (fileRenamed)
9056 {
9057 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9058 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9059 }
9060 if (dirRenamed)
9061 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9062 }
9063
9064 if (FAILED(rc)) return rc;
9065 }
9066
9067 if (fSettingsFileIsNew)
9068 {
9069 /* create a virgin config file */
9070 int vrc = VINF_SUCCESS;
9071
9072 /* ensure the settings directory exists */
9073 Utf8Str path(mData->m_strConfigFileFull);
9074 path.stripFilename();
9075 if (!RTDirExists(path.c_str()))
9076 {
9077 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9078 if (RT_FAILURE(vrc))
9079 {
9080 return setError(E_FAIL,
9081 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9082 path.c_str(),
9083 vrc);
9084 }
9085 }
9086
9087 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9088 path = Utf8Str(mData->m_strConfigFileFull);
9089 RTFILE f = NIL_RTFILE;
9090 vrc = RTFileOpen(&f, path.c_str(),
9091 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9092 if (RT_FAILURE(vrc))
9093 return setError(E_FAIL,
9094 tr("Could not create the settings file '%s' (%Rrc)"),
9095 path.c_str(),
9096 vrc);
9097 RTFileClose(f);
9098 }
9099
9100 return rc;
9101}
9102
9103/**
9104 * Saves and commits machine data, user data and hardware data.
9105 *
9106 * Note that on failure, the data remains uncommitted.
9107 *
9108 * @a aFlags may combine the following flags:
9109 *
9110 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9111 * Used when saving settings after an operation that makes them 100%
9112 * correspond to the settings from the current snapshot.
9113 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9114 * #isReallyModified() returns false. This is necessary for cases when we
9115 * change machine data directly, not through the backup()/commit() mechanism.
9116 * - SaveS_Force: settings will be saved without doing a deep compare of the
9117 * settings structures. This is used when this is called because snapshots
9118 * have changed to avoid the overhead of the deep compare.
9119 *
9120 * @note Must be called from under this object's write lock. Locks children for
9121 * writing.
9122 *
9123 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9124 * initialized to false and that will be set to true by this function if
9125 * the caller must invoke VirtualBox::saveSettings() because the global
9126 * settings have changed. This will happen if a machine rename has been
9127 * saved and the global machine and media registries will therefore need
9128 * updating.
9129 */
9130HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9131 int aFlags /*= 0*/)
9132{
9133 LogFlowThisFuncEnter();
9134
9135 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9136
9137 /* make sure child objects are unable to modify the settings while we are
9138 * saving them */
9139 ensureNoStateDependencies();
9140
9141 AssertReturn(!isSnapshotMachine(),
9142 E_FAIL);
9143
9144 HRESULT rc = S_OK;
9145 bool fNeedsWrite = false;
9146
9147 /* First, prepare to save settings. It will care about renaming the
9148 * settings directory and file if the machine name was changed and about
9149 * creating a new settings file if this is a new machine. */
9150 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9151 if (FAILED(rc)) return rc;
9152
9153 // keep a pointer to the current settings structures
9154 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9155 settings::MachineConfigFile *pNewConfig = NULL;
9156
9157 try
9158 {
9159 // make a fresh one to have everyone write stuff into
9160 pNewConfig = new settings::MachineConfigFile(NULL);
9161 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9162
9163 // now go and copy all the settings data from COM to the settings structures
9164 // (this calles saveSettings() on all the COM objects in the machine)
9165 copyMachineDataToSettings(*pNewConfig);
9166
9167 if (aFlags & SaveS_ResetCurStateModified)
9168 {
9169 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9170 mData->mCurrentStateModified = FALSE;
9171 fNeedsWrite = true; // always, no need to compare
9172 }
9173 else if (aFlags & SaveS_Force)
9174 {
9175 fNeedsWrite = true; // always, no need to compare
9176 }
9177 else
9178 {
9179 if (!mData->mCurrentStateModified)
9180 {
9181 // do a deep compare of the settings that we just saved with the settings
9182 // previously stored in the config file; this invokes MachineConfigFile::operator==
9183 // which does a deep compare of all the settings, which is expensive but less expensive
9184 // than writing out XML in vain
9185 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9186
9187 // could still be modified if any settings changed
9188 mData->mCurrentStateModified = fAnySettingsChanged;
9189
9190 fNeedsWrite = fAnySettingsChanged;
9191 }
9192 else
9193 fNeedsWrite = true;
9194 }
9195
9196 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9197
9198 if (fNeedsWrite)
9199 // now spit it all out!
9200 pNewConfig->write(mData->m_strConfigFileFull);
9201
9202 mData->pMachineConfigFile = pNewConfig;
9203 delete pOldConfig;
9204 commit();
9205
9206 // after saving settings, we are no longer different from the XML on disk
9207 mData->flModifications = 0;
9208 }
9209 catch (HRESULT err)
9210 {
9211 // we assume that error info is set by the thrower
9212 rc = err;
9213
9214 // restore old config
9215 delete pNewConfig;
9216 mData->pMachineConfigFile = pOldConfig;
9217 }
9218 catch (...)
9219 {
9220 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9221 }
9222
9223 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9224 {
9225 /* Fire the data change event, even on failure (since we've already
9226 * committed all data). This is done only for SessionMachines because
9227 * mutable Machine instances are always not registered (i.e. private
9228 * to the client process that creates them) and thus don't need to
9229 * inform callbacks. */
9230 if (isSessionMachine())
9231 mParent->onMachineDataChange(mData->mUuid);
9232 }
9233
9234 LogFlowThisFunc(("rc=%08X\n", rc));
9235 LogFlowThisFuncLeave();
9236 return rc;
9237}
9238
9239/**
9240 * Implementation for saving the machine settings into the given
9241 * settings::MachineConfigFile instance. This copies machine extradata
9242 * from the previous machine config file in the instance data, if any.
9243 *
9244 * This gets called from two locations:
9245 *
9246 * -- Machine::saveSettings(), during the regular XML writing;
9247 *
9248 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9249 * exported to OVF and we write the VirtualBox proprietary XML
9250 * into a <vbox:Machine> tag.
9251 *
9252 * This routine fills all the fields in there, including snapshots, *except*
9253 * for the following:
9254 *
9255 * -- fCurrentStateModified. There is some special logic associated with that.
9256 *
9257 * The caller can then call MachineConfigFile::write() or do something else
9258 * with it.
9259 *
9260 * Caller must hold the machine lock!
9261 *
9262 * This throws XML errors and HRESULT, so the caller must have a catch block!
9263 */
9264void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9265{
9266 // deep copy extradata
9267 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9268
9269 config.uuid = mData->mUuid;
9270
9271 // copy name, description, OS type, teleport, UTC etc.
9272 config.machineUserData = mUserData->s;
9273
9274 if ( mData->mMachineState == MachineState_Saved
9275 || mData->mMachineState == MachineState_Restoring
9276 // when deleting a snapshot we may or may not have a saved state in the current state,
9277 // so let's not assert here please
9278 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9279 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9280 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9281 && (!mSSData->strStateFilePath.isEmpty())
9282 )
9283 )
9284 {
9285 Assert(!mSSData->strStateFilePath.isEmpty());
9286 /* try to make the file name relative to the settings file dir */
9287 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9288 }
9289 else
9290 {
9291 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9292 config.strStateFile.setNull();
9293 }
9294
9295 if (mData->mCurrentSnapshot)
9296 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9297 else
9298 config.uuidCurrentSnapshot.clear();
9299
9300 config.timeLastStateChange = mData->mLastStateChange;
9301 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9302 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9303
9304 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9305 if (FAILED(rc)) throw rc;
9306
9307 rc = saveStorageControllers(config.storageMachine);
9308 if (FAILED(rc)) throw rc;
9309
9310 // save machine's media registry if this is VirtualBox 4.0 or later
9311 if (config.canHaveOwnMediaRegistry())
9312 {
9313 // determine machine folder
9314 Utf8Str strMachineFolder = getSettingsFileFull();
9315 strMachineFolder.stripFilename();
9316 mParent->saveMediaRegistry(config.mediaRegistry,
9317 getId(), // only media with registry ID == machine UUID
9318 strMachineFolder);
9319 // this throws HRESULT
9320 }
9321
9322 // save snapshots
9323 rc = saveAllSnapshots(config);
9324 if (FAILED(rc)) throw rc;
9325}
9326
9327/**
9328 * Saves all snapshots of the machine into the given machine config file. Called
9329 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9330 * @param config
9331 * @return
9332 */
9333HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9334{
9335 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9336
9337 HRESULT rc = S_OK;
9338
9339 try
9340 {
9341 config.llFirstSnapshot.clear();
9342
9343 if (mData->mFirstSnapshot)
9344 {
9345 settings::Snapshot snapNew;
9346 config.llFirstSnapshot.push_back(snapNew);
9347
9348 // get reference to the fresh copy of the snapshot on the list and
9349 // work on that copy directly to avoid excessive copying later
9350 settings::Snapshot &snap = config.llFirstSnapshot.front();
9351
9352 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9353 if (FAILED(rc)) throw rc;
9354 }
9355
9356// if (mType == IsSessionMachine)
9357// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9358
9359 }
9360 catch (HRESULT err)
9361 {
9362 /* we assume that error info is set by the thrower */
9363 rc = err;
9364 }
9365 catch (...)
9366 {
9367 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9368 }
9369
9370 return rc;
9371}
9372
9373/**
9374 * Saves the VM hardware configuration. It is assumed that the
9375 * given node is empty.
9376 *
9377 * @param data Reference to the settings object for the hardware config.
9378 * @param pDbg Pointer to the settings object for the debugging config
9379 * which happens to live in mHWData.
9380 * @param pAutostart Pointer to the settings object for the autostart config
9381 * which happens to live in mHWData.
9382 */
9383HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9384 settings::Autostart *pAutostart)
9385{
9386 HRESULT rc = S_OK;
9387
9388 try
9389 {
9390 /* The hardware version attribute (optional).
9391 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9392 if ( mHWData->mHWVersion == "1"
9393 && mSSData->strStateFilePath.isEmpty()
9394 )
9395 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some other point needs to be found where this can be done. */
9396
9397 data.strVersion = mHWData->mHWVersion;
9398 data.uuid = mHWData->mHardwareUUID;
9399
9400 // CPU
9401 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9402 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9403 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9404 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9405 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9406 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9407 data.fPAE = !!mHWData->mPAEEnabled;
9408 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9409
9410 /* Standard and Extended CPUID leafs. */
9411 data.llCpuIdLeafs.clear();
9412 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9413 {
9414 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9415 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9416 }
9417 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9418 {
9419 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9420 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9421 }
9422
9423 data.cCPUs = mHWData->mCPUCount;
9424 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9425 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9426
9427 data.llCpus.clear();
9428 if (data.fCpuHotPlug)
9429 {
9430 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9431 {
9432 if (mHWData->mCPUAttached[idx])
9433 {
9434 settings::Cpu cpu;
9435 cpu.ulId = idx;
9436 data.llCpus.push_back(cpu);
9437 }
9438 }
9439 }
9440
9441 // memory
9442 data.ulMemorySizeMB = mHWData->mMemorySize;
9443 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9444
9445 // firmware
9446 data.firmwareType = mHWData->mFirmwareType;
9447
9448 // HID
9449 data.pointingHidType = mHWData->mPointingHidType;
9450 data.keyboardHidType = mHWData->mKeyboardHidType;
9451
9452 // chipset
9453 data.chipsetType = mHWData->mChipsetType;
9454
9455 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9456
9457 // HPET
9458 data.fHpetEnabled = !!mHWData->mHpetEnabled;
9459
9460 // boot order
9461 data.mapBootOrder.clear();
9462 for (size_t i = 0;
9463 i < RT_ELEMENTS(mHWData->mBootOrder);
9464 ++i)
9465 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9466
9467 // display
9468 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9469 data.cMonitors = mHWData->mMonitorCount;
9470 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9471 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9472
9473 /* VRDEServer settings (optional) */
9474 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9475 if (FAILED(rc)) throw rc;
9476
9477 /* BIOS (required) */
9478 rc = mBIOSSettings->saveSettings(data.biosSettings);
9479 if (FAILED(rc)) throw rc;
9480
9481 /* USB Controller (required) */
9482 rc = mUSBController->saveSettings(data.usbController);
9483 if (FAILED(rc)) throw rc;
9484
9485 /* Network adapters (required) */
9486 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9487 data.llNetworkAdapters.clear();
9488 /* Write out only the nominal number of network adapters for this
9489 * chipset type. Since Machine::commit() hasn't been called there
9490 * may be extra NIC settings in the vector. */
9491 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9492 {
9493 settings::NetworkAdapter nic;
9494 nic.ulSlot = slot;
9495 /* paranoia check... must not be NULL, but must not crash either. */
9496 if (mNetworkAdapters[slot])
9497 {
9498 rc = mNetworkAdapters[slot]->saveSettings(nic);
9499 if (FAILED(rc)) throw rc;
9500
9501 data.llNetworkAdapters.push_back(nic);
9502 }
9503 }
9504
9505 /* Serial ports */
9506 data.llSerialPorts.clear();
9507 for (ULONG slot = 0;
9508 slot < RT_ELEMENTS(mSerialPorts);
9509 ++slot)
9510 {
9511 settings::SerialPort s;
9512 s.ulSlot = slot;
9513 rc = mSerialPorts[slot]->saveSettings(s);
9514 if (FAILED(rc)) return rc;
9515
9516 data.llSerialPorts.push_back(s);
9517 }
9518
9519 /* Parallel ports */
9520 data.llParallelPorts.clear();
9521 for (ULONG slot = 0;
9522 slot < RT_ELEMENTS(mParallelPorts);
9523 ++slot)
9524 {
9525 settings::ParallelPort p;
9526 p.ulSlot = slot;
9527 rc = mParallelPorts[slot]->saveSettings(p);
9528 if (FAILED(rc)) return rc;
9529
9530 data.llParallelPorts.push_back(p);
9531 }
9532
9533 /* Audio adapter */
9534 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9535 if (FAILED(rc)) return rc;
9536
9537 /* Shared folders */
9538 data.llSharedFolders.clear();
9539 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9540 it != mHWData->mSharedFolders.end();
9541 ++it)
9542 {
9543 SharedFolder *pSF = *it;
9544 AutoCaller sfCaller(pSF);
9545 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9546 settings::SharedFolder sf;
9547 sf.strName = pSF->getName();
9548 sf.strHostPath = pSF->getHostPath();
9549 sf.fWritable = !!pSF->isWritable();
9550 sf.fAutoMount = !!pSF->isAutoMounted();
9551
9552 data.llSharedFolders.push_back(sf);
9553 }
9554
9555 // clipboard
9556 data.clipboardMode = mHWData->mClipboardMode;
9557
9558 /* Guest */
9559 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9560
9561 // IO settings
9562 data.ioSettings.fIoCacheEnabled = !!mHWData->mIoCacheEnabled;
9563 data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize;
9564
9565 /* BandwidthControl (required) */
9566 rc = mBandwidthControl->saveSettings(data.ioSettings);
9567 if (FAILED(rc)) throw rc;
9568
9569 /* Host PCI devices */
9570 for (HWData::PciDeviceAssignmentList::const_iterator it = mHWData->mPciDeviceAssignments.begin();
9571 it != mHWData->mPciDeviceAssignments.end();
9572 ++it)
9573 {
9574 ComObjPtr<PciDeviceAttachment> pda = *it;
9575 settings::HostPciDeviceAttachment hpda;
9576
9577 rc = pda->saveSettings(hpda);
9578 if (FAILED(rc)) throw rc;
9579
9580 data.pciAttachments.push_back(hpda);
9581 }
9582
9583
9584 // guest properties
9585 data.llGuestProperties.clear();
9586#ifdef VBOX_WITH_GUEST_PROPS
9587 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
9588 it != mHWData->mGuestProperties.end();
9589 ++it)
9590 {
9591 HWData::GuestProperty property = *it;
9592
9593 /* Remove transient guest properties at shutdown unless we
9594 * are saving state */
9595 if ( ( mData->mMachineState == MachineState_PoweredOff
9596 || mData->mMachineState == MachineState_Aborted
9597 || mData->mMachineState == MachineState_Teleported)
9598 && ( property.mFlags & guestProp::TRANSIENT
9599 || property.mFlags & guestProp::TRANSRESET))
9600 continue;
9601 settings::GuestProperty prop;
9602 prop.strName = property.strName;
9603 prop.strValue = property.strValue;
9604 prop.timestamp = property.mTimestamp;
9605 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9606 guestProp::writeFlags(property.mFlags, szFlags);
9607 prop.strFlags = szFlags;
9608
9609 data.llGuestProperties.push_back(prop);
9610 }
9611
9612 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9613 /* I presume this doesn't require a backup(). */
9614 mData->mGuestPropertiesModified = FALSE;
9615#endif /* VBOX_WITH_GUEST_PROPS defined */
9616
9617 *pDbg = mHWData->mDebugging;
9618 *pAutostart = mHWData->mAutostart;
9619 }
9620 catch(std::bad_alloc &)
9621 {
9622 return E_OUTOFMEMORY;
9623 }
9624
9625 AssertComRC(rc);
9626 return rc;
9627}
9628
9629/**
9630 * Saves the storage controller configuration.
9631 *
9632 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9633 */
9634HRESULT Machine::saveStorageControllers(settings::Storage &data)
9635{
9636 data.llStorageControllers.clear();
9637
9638 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9639 it != mStorageControllers->end();
9640 ++it)
9641 {
9642 HRESULT rc;
9643 ComObjPtr<StorageController> pCtl = *it;
9644
9645 settings::StorageController ctl;
9646 ctl.strName = pCtl->getName();
9647 ctl.controllerType = pCtl->getControllerType();
9648 ctl.storageBus = pCtl->getStorageBus();
9649 ctl.ulInstance = pCtl->getInstance();
9650 ctl.fBootable = pCtl->getBootable();
9651
9652 /* Save the port count. */
9653 ULONG portCount;
9654 rc = pCtl->COMGETTER(PortCount)(&portCount);
9655 ComAssertComRCRet(rc, rc);
9656 ctl.ulPortCount = portCount;
9657
9658 /* Save fUseHostIOCache */
9659 BOOL fUseHostIOCache;
9660 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9661 ComAssertComRCRet(rc, rc);
9662 ctl.fUseHostIOCache = !!fUseHostIOCache;
9663
9664 /* Save IDE emulation settings. */
9665 if (ctl.controllerType == StorageControllerType_IntelAhci)
9666 {
9667 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9668 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9669 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9670 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9671 )
9672 ComAssertComRCRet(rc, rc);
9673 }
9674
9675 /* save the devices now. */
9676 rc = saveStorageDevices(pCtl, ctl);
9677 ComAssertComRCRet(rc, rc);
9678
9679 data.llStorageControllers.push_back(ctl);
9680 }
9681
9682 return S_OK;
9683}
9684
9685/**
9686 * Saves the hard disk configuration.
9687 */
9688HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9689 settings::StorageController &data)
9690{
9691 MediaData::AttachmentList atts;
9692
9693 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9694 if (FAILED(rc)) return rc;
9695
9696 data.llAttachedDevices.clear();
9697 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9698 it != atts.end();
9699 ++it)
9700 {
9701 settings::AttachedDevice dev;
9702
9703 MediumAttachment *pAttach = *it;
9704 Medium *pMedium = pAttach->getMedium();
9705
9706 dev.deviceType = pAttach->getType();
9707 dev.lPort = pAttach->getPort();
9708 dev.lDevice = pAttach->getDevice();
9709 if (pMedium)
9710 {
9711 if (pMedium->isHostDrive())
9712 dev.strHostDriveSrc = pMedium->getLocationFull();
9713 else
9714 dev.uuid = pMedium->getId();
9715 dev.fPassThrough = pAttach->getPassthrough();
9716 dev.fTempEject = pAttach->getTempEject();
9717 dev.fDiscard = pAttach->getDiscard();
9718 }
9719
9720 dev.strBwGroup = pAttach->getBandwidthGroup();
9721
9722 data.llAttachedDevices.push_back(dev);
9723 }
9724
9725 return S_OK;
9726}
9727
9728/**
9729 * Saves machine state settings as defined by aFlags
9730 * (SaveSTS_* values).
9731 *
9732 * @param aFlags Combination of SaveSTS_* flags.
9733 *
9734 * @note Locks objects for writing.
9735 */
9736HRESULT Machine::saveStateSettings(int aFlags)
9737{
9738 if (aFlags == 0)
9739 return S_OK;
9740
9741 AutoCaller autoCaller(this);
9742 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9743
9744 /* This object's write lock is also necessary to serialize file access
9745 * (prevent concurrent reads and writes) */
9746 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9747
9748 HRESULT rc = S_OK;
9749
9750 Assert(mData->pMachineConfigFile);
9751
9752 try
9753 {
9754 if (aFlags & SaveSTS_CurStateModified)
9755 mData->pMachineConfigFile->fCurrentStateModified = true;
9756
9757 if (aFlags & SaveSTS_StateFilePath)
9758 {
9759 if (!mSSData->strStateFilePath.isEmpty())
9760 /* try to make the file name relative to the settings file dir */
9761 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
9762 else
9763 mData->pMachineConfigFile->strStateFile.setNull();
9764 }
9765
9766 if (aFlags & SaveSTS_StateTimeStamp)
9767 {
9768 Assert( mData->mMachineState != MachineState_Aborted
9769 || mSSData->strStateFilePath.isEmpty());
9770
9771 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
9772
9773 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
9774//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
9775 }
9776
9777 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
9778 }
9779 catch (...)
9780 {
9781 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9782 }
9783
9784 return rc;
9785}
9786
9787/**
9788 * Ensures that the given medium is added to a media registry. If this machine
9789 * was created with 4.0 or later, then the machine registry is used. Otherwise
9790 * the global VirtualBox media registry is used.
9791 *
9792 * Caller must NOT hold machine lock, media tree or any medium locks!
9793 *
9794 * @param pMedium
9795 */
9796void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
9797{
9798 /* Paranoia checks: do not hold machine or media tree locks. */
9799 AssertReturnVoid(!isWriteLockOnCurrentThread());
9800 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
9801
9802 ComObjPtr<Medium> pBase;
9803 {
9804 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9805 pBase = pMedium->getBase();
9806 }
9807
9808 /* Paranoia checks: do not hold medium locks. */
9809 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
9810 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
9811
9812 // decide which medium registry to use now that the medium is attached:
9813 Guid uuid;
9814 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
9815 // machine XML is VirtualBox 4.0 or higher:
9816 uuid = getId(); // machine UUID
9817 else
9818 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
9819
9820 if (pMedium->addRegistry(uuid, false /* fRecurse */))
9821 mParent->markRegistryModified(uuid);
9822
9823 /* For more complex hard disk structures it can happen that the base
9824 * medium isn't yet associated with any medium registry. Do that now. */
9825 if (pMedium != pBase)
9826 {
9827 if (pBase->addRegistry(uuid, true /* fRecurse */))
9828 mParent->markRegistryModified(uuid);
9829 }
9830}
9831
9832/**
9833 * Creates differencing hard disks for all normal hard disks attached to this
9834 * machine and a new set of attachments to refer to created disks.
9835 *
9836 * Used when taking a snapshot or when deleting the current state. Gets called
9837 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
9838 *
9839 * This method assumes that mMediaData contains the original hard disk attachments
9840 * it needs to create diffs for. On success, these attachments will be replaced
9841 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
9842 * called to delete created diffs which will also rollback mMediaData and restore
9843 * whatever was backed up before calling this method.
9844 *
9845 * Attachments with non-normal hard disks are left as is.
9846 *
9847 * If @a aOnline is @c false then the original hard disks that require implicit
9848 * diffs will be locked for reading. Otherwise it is assumed that they are
9849 * already locked for writing (when the VM was started). Note that in the latter
9850 * case it is responsibility of the caller to lock the newly created diffs for
9851 * writing if this method succeeds.
9852 *
9853 * @param aProgress Progress object to run (must contain at least as
9854 * many operations left as the number of hard disks
9855 * attached).
9856 * @param aOnline Whether the VM was online prior to this operation.
9857 *
9858 * @note The progress object is not marked as completed, neither on success nor
9859 * on failure. This is a responsibility of the caller.
9860 *
9861 * @note Locks this object for writing.
9862 */
9863HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
9864 ULONG aWeight,
9865 bool aOnline)
9866{
9867 LogFlowThisFunc(("aOnline=%d\n", aOnline));
9868
9869 AutoCaller autoCaller(this);
9870 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9871
9872 AutoMultiWriteLock2 alock(this->lockHandle(),
9873 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9874
9875 /* must be in a protective state because we release the lock below */
9876 AssertReturn( mData->mMachineState == MachineState_Saving
9877 || mData->mMachineState == MachineState_LiveSnapshotting
9878 || mData->mMachineState == MachineState_RestoringSnapshot
9879 || mData->mMachineState == MachineState_DeletingSnapshot
9880 , E_FAIL);
9881
9882 HRESULT rc = S_OK;
9883
9884 MediumLockListMap lockedMediaOffline;
9885 MediumLockListMap *lockedMediaMap;
9886 if (aOnline)
9887 lockedMediaMap = &mData->mSession.mLockedMedia;
9888 else
9889 lockedMediaMap = &lockedMediaOffline;
9890
9891 try
9892 {
9893 if (!aOnline)
9894 {
9895 /* lock all attached hard disks early to detect "in use"
9896 * situations before creating actual diffs */
9897 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
9898 it != mMediaData->mAttachments.end();
9899 ++it)
9900 {
9901 MediumAttachment* pAtt = *it;
9902 if (pAtt->getType() == DeviceType_HardDisk)
9903 {
9904 Medium* pMedium = pAtt->getMedium();
9905 Assert(pMedium);
9906
9907 MediumLockList *pMediumLockList(new MediumLockList());
9908 alock.release();
9909 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
9910 false /* fMediumLockWrite */,
9911 NULL,
9912 *pMediumLockList);
9913 alock.acquire();
9914 if (FAILED(rc))
9915 {
9916 delete pMediumLockList;
9917 throw rc;
9918 }
9919 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
9920 if (FAILED(rc))
9921 {
9922 throw setError(rc,
9923 tr("Collecting locking information for all attached media failed"));
9924 }
9925 }
9926 }
9927
9928 /* Now lock all media. If this fails, nothing is locked. */
9929 alock.release();
9930 rc = lockedMediaMap->Lock();
9931 alock.acquire();
9932 if (FAILED(rc))
9933 {
9934 throw setError(rc,
9935 tr("Locking of attached media failed"));
9936 }
9937 }
9938
9939 /* remember the current list (note that we don't use backup() since
9940 * mMediaData may be already backed up) */
9941 MediaData::AttachmentList atts = mMediaData->mAttachments;
9942
9943 /* start from scratch */
9944 mMediaData->mAttachments.clear();
9945
9946 /* go through remembered attachments and create diffs for normal hard
9947 * disks and attach them */
9948 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9949 it != atts.end();
9950 ++it)
9951 {
9952 MediumAttachment* pAtt = *it;
9953
9954 DeviceType_T devType = pAtt->getType();
9955 Medium* pMedium = pAtt->getMedium();
9956
9957 if ( devType != DeviceType_HardDisk
9958 || pMedium == NULL
9959 || pMedium->getType() != MediumType_Normal)
9960 {
9961 /* copy the attachment as is */
9962
9963 /** @todo the progress object created in Console::TakeSnaphot
9964 * only expects operations for hard disks. Later other
9965 * device types need to show up in the progress as well. */
9966 if (devType == DeviceType_HardDisk)
9967 {
9968 if (pMedium == NULL)
9969 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
9970 aWeight); // weight
9971 else
9972 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
9973 pMedium->getBase()->getName().c_str()).raw(),
9974 aWeight); // weight
9975 }
9976
9977 mMediaData->mAttachments.push_back(pAtt);
9978 continue;
9979 }
9980
9981 /* need a diff */
9982 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
9983 pMedium->getBase()->getName().c_str()).raw(),
9984 aWeight); // weight
9985
9986 Utf8Str strFullSnapshotFolder;
9987 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
9988
9989 ComObjPtr<Medium> diff;
9990 diff.createObject();
9991 // store the diff in the same registry as the parent
9992 // (this cannot fail here because we can't create implicit diffs for
9993 // unregistered images)
9994 Guid uuidRegistryParent;
9995 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
9996 Assert(fInRegistry); NOREF(fInRegistry);
9997 rc = diff->init(mParent,
9998 pMedium->getPreferredDiffFormat(),
9999 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10000 uuidRegistryParent);
10001 if (FAILED(rc)) throw rc;
10002
10003 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10004 * the push_back? Looks like we're going to release medium with the
10005 * wrong kind of lock (general issue with if we fail anywhere at all)
10006 * and an orphaned VDI in the snapshots folder. */
10007
10008 /* update the appropriate lock list */
10009 MediumLockList *pMediumLockList;
10010 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10011 AssertComRCThrowRC(rc);
10012 if (aOnline)
10013 {
10014 alock.release();
10015 rc = pMediumLockList->Update(pMedium, false);
10016 alock.acquire();
10017 AssertComRCThrowRC(rc);
10018 }
10019
10020 /* release the locks before the potentially lengthy operation */
10021 alock.release();
10022 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10023 pMediumLockList,
10024 NULL /* aProgress */,
10025 true /* aWait */);
10026 alock.acquire();
10027 if (FAILED(rc)) throw rc;
10028
10029 rc = lockedMediaMap->Unlock();
10030 AssertComRCThrowRC(rc);
10031 alock.release();
10032 rc = pMediumLockList->Append(diff, true);
10033 alock.acquire();
10034 AssertComRCThrowRC(rc);
10035 alock.release();
10036 rc = lockedMediaMap->Lock();
10037 alock.acquire();
10038 AssertComRCThrowRC(rc);
10039
10040 rc = diff->addBackReference(mData->mUuid);
10041 AssertComRCThrowRC(rc);
10042
10043 /* add a new attachment */
10044 ComObjPtr<MediumAttachment> attachment;
10045 attachment.createObject();
10046 rc = attachment->init(this,
10047 diff,
10048 pAtt->getControllerName(),
10049 pAtt->getPort(),
10050 pAtt->getDevice(),
10051 DeviceType_HardDisk,
10052 true /* aImplicit */,
10053 false /* aPassthrough */,
10054 false /* aTempEject */,
10055 pAtt->getNonRotational(),
10056 pAtt->getDiscard(),
10057 pAtt->getBandwidthGroup());
10058 if (FAILED(rc)) throw rc;
10059
10060 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10061 AssertComRCThrowRC(rc);
10062 mMediaData->mAttachments.push_back(attachment);
10063 }
10064 }
10065 catch (HRESULT aRC) { rc = aRC; }
10066
10067 /* unlock all hard disks we locked */
10068 if (!aOnline)
10069 {
10070 ErrorInfoKeeper eik;
10071
10072 HRESULT rc1 = lockedMediaMap->Clear();
10073 AssertComRC(rc1);
10074 }
10075
10076 if (FAILED(rc))
10077 {
10078 MultiResult mrc = rc;
10079
10080 alock.release();
10081 mrc = deleteImplicitDiffs();
10082 }
10083
10084 return rc;
10085}
10086
10087/**
10088 * Deletes implicit differencing hard disks created either by
10089 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10090 *
10091 * Note that to delete hard disks created by #AttachDevice() this method is
10092 * called from #fixupMedia() when the changes are rolled back.
10093 *
10094 * @note Locks this object for writing.
10095 */
10096HRESULT Machine::deleteImplicitDiffs()
10097{
10098 AutoCaller autoCaller(this);
10099 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10100
10101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10102 LogFlowThisFuncEnter();
10103
10104 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10105
10106 HRESULT rc = S_OK;
10107
10108 MediaData::AttachmentList implicitAtts;
10109
10110 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10111
10112 /* enumerate new attachments */
10113 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10114 it != mMediaData->mAttachments.end();
10115 ++it)
10116 {
10117 ComObjPtr<Medium> hd = (*it)->getMedium();
10118 if (hd.isNull())
10119 continue;
10120
10121 if ((*it)->isImplicit())
10122 {
10123 /* deassociate and mark for deletion */
10124 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
10125 rc = hd->removeBackReference(mData->mUuid);
10126 AssertComRC(rc);
10127 implicitAtts.push_back(*it);
10128 continue;
10129 }
10130
10131 /* was this hard disk attached before? */
10132 if (!findAttachment(oldAtts, hd))
10133 {
10134 /* no: de-associate */
10135 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
10136 rc = hd->removeBackReference(mData->mUuid);
10137 AssertComRC(rc);
10138 continue;
10139 }
10140 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
10141 }
10142
10143 /* rollback hard disk changes */
10144 mMediaData.rollback();
10145
10146 MultiResult mrc(S_OK);
10147
10148 /* delete unused implicit diffs */
10149 if (implicitAtts.size() != 0)
10150 {
10151 /* will release the lock before the potentially lengthy
10152 * operation, so protect with the special state (unless already
10153 * protected) */
10154 MachineState_T oldState = mData->mMachineState;
10155 if ( oldState != MachineState_Saving
10156 && oldState != MachineState_LiveSnapshotting
10157 && oldState != MachineState_RestoringSnapshot
10158 && oldState != MachineState_DeletingSnapshot
10159 && oldState != MachineState_DeletingSnapshotOnline
10160 && oldState != MachineState_DeletingSnapshotPaused
10161 )
10162 setMachineState(MachineState_SettingUp);
10163
10164 alock.release();
10165
10166 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10167 it != implicitAtts.end();
10168 ++it)
10169 {
10170 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
10171 ComObjPtr<Medium> hd = (*it)->getMedium();
10172
10173 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10174 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
10175 mrc = rc;
10176 }
10177
10178 alock.acquire();
10179
10180 if (mData->mMachineState == MachineState_SettingUp)
10181 setMachineState(oldState);
10182 }
10183
10184 return mrc;
10185}
10186
10187/**
10188 * Looks through the given list of media attachments for one with the given parameters
10189 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10190 * can be searched as well if needed.
10191 *
10192 * @param list
10193 * @param aControllerName
10194 * @param aControllerPort
10195 * @param aDevice
10196 * @return
10197 */
10198MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10199 IN_BSTR aControllerName,
10200 LONG aControllerPort,
10201 LONG aDevice)
10202{
10203 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10204 it != ll.end();
10205 ++it)
10206 {
10207 MediumAttachment *pAttach = *it;
10208 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10209 return pAttach;
10210 }
10211
10212 return NULL;
10213}
10214
10215/**
10216 * Looks through the given list of media attachments for one with the given parameters
10217 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10218 * can be searched as well if needed.
10219 *
10220 * @param list
10221 * @param aControllerName
10222 * @param aControllerPort
10223 * @param aDevice
10224 * @return
10225 */
10226MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10227 ComObjPtr<Medium> pMedium)
10228{
10229 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10230 it != ll.end();
10231 ++it)
10232 {
10233 MediumAttachment *pAttach = *it;
10234 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10235 if (pMediumThis == pMedium)
10236 return pAttach;
10237 }
10238
10239 return NULL;
10240}
10241
10242/**
10243 * Looks through the given list of media attachments for one with the given parameters
10244 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10245 * can be searched as well if needed.
10246 *
10247 * @param list
10248 * @param aControllerName
10249 * @param aControllerPort
10250 * @param aDevice
10251 * @return
10252 */
10253MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10254 Guid &id)
10255{
10256 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10257 it != ll.end();
10258 ++it)
10259 {
10260 MediumAttachment *pAttach = *it;
10261 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10262 if (pMediumThis->getId() == id)
10263 return pAttach;
10264 }
10265
10266 return NULL;
10267}
10268
10269/**
10270 * Main implementation for Machine::DetachDevice. This also gets called
10271 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10272 *
10273 * @param pAttach Medium attachment to detach.
10274 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10275 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10276 * @return
10277 */
10278HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10279 AutoWriteLock &writeLock,
10280 Snapshot *pSnapshot)
10281{
10282 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10283 DeviceType_T mediumType = pAttach->getType();
10284
10285 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10286
10287 if (pAttach->isImplicit())
10288 {
10289 /* attempt to implicitly delete the implicitly created diff */
10290
10291 /// @todo move the implicit flag from MediumAttachment to Medium
10292 /// and forbid any hard disk operation when it is implicit. Or maybe
10293 /// a special media state for it to make it even more simple.
10294
10295 Assert(mMediaData.isBackedUp());
10296
10297 /* will release the lock before the potentially lengthy operation, so
10298 * protect with the special state */
10299 MachineState_T oldState = mData->mMachineState;
10300 setMachineState(MachineState_SettingUp);
10301
10302 writeLock.release();
10303
10304 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10305 true /*aWait*/);
10306
10307 writeLock.acquire();
10308
10309 setMachineState(oldState);
10310
10311 if (FAILED(rc)) return rc;
10312 }
10313
10314 setModified(IsModified_Storage);
10315 mMediaData.backup();
10316 mMediaData->mAttachments.remove(pAttach);
10317
10318 if (!oldmedium.isNull())
10319 {
10320 // if this is from a snapshot, do not defer detachment to commitMedia()
10321 if (pSnapshot)
10322 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10323 // else if non-hard disk media, do not defer detachment to commitMedia() either
10324 else if (mediumType != DeviceType_HardDisk)
10325 oldmedium->removeBackReference(mData->mUuid);
10326 }
10327
10328 return S_OK;
10329}
10330
10331/**
10332 * Goes thru all media of the given list and
10333 *
10334 * 1) calls detachDevice() on each of them for this machine and
10335 * 2) adds all Medium objects found in the process to the given list,
10336 * depending on cleanupMode.
10337 *
10338 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10339 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10340 * media to the list.
10341 *
10342 * This gets called from Machine::Unregister, both for the actual Machine and
10343 * the SnapshotMachine objects that might be found in the snapshots.
10344 *
10345 * Requires caller and locking. The machine lock must be passed in because it
10346 * will be passed on to detachDevice which needs it for temporary unlocking.
10347 *
10348 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10349 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10350 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10351 * otherwise no media get added.
10352 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10353 * @return
10354 */
10355HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10356 Snapshot *pSnapshot,
10357 CleanupMode_T cleanupMode,
10358 MediaList &llMedia)
10359{
10360 Assert(isWriteLockOnCurrentThread());
10361
10362 HRESULT rc;
10363
10364 // make a temporary list because detachDevice invalidates iterators into
10365 // mMediaData->mAttachments
10366 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10367
10368 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10369 it != llAttachments2.end();
10370 ++it)
10371 {
10372 ComObjPtr<MediumAttachment> &pAttach = *it;
10373 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10374
10375 if (!pMedium.isNull())
10376 {
10377 AutoCaller mac(pMedium);
10378 if (FAILED(mac.rc())) return mac.rc();
10379 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10380 DeviceType_T devType = pMedium->getDeviceType();
10381 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10382 && devType == DeviceType_HardDisk)
10383 || (cleanupMode == CleanupMode_Full)
10384 )
10385 {
10386 llMedia.push_back(pMedium);
10387 ComObjPtr<Medium> pParent = pMedium->getParent();
10388 /*
10389 * Search for medias which are not attached to any machine, but
10390 * in the chain to an attached disk. Mediums are only consided
10391 * if they are:
10392 * - have only one child
10393 * - no references to any machines
10394 * - are of normal medium type
10395 */
10396 while (!pParent.isNull())
10397 {
10398 AutoCaller mac1(pParent);
10399 if (FAILED(mac1.rc())) return mac1.rc();
10400 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10401 if (pParent->getChildren().size() == 1)
10402 {
10403 if ( pParent->getMachineBackRefCount() == 0
10404 && pParent->getType() == MediumType_Normal
10405 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10406 llMedia.push_back(pParent);
10407 }else
10408 break;
10409 pParent = pParent->getParent();
10410 }
10411 }
10412 }
10413
10414 // real machine: then we need to use the proper method
10415 rc = detachDevice(pAttach, writeLock, pSnapshot);
10416
10417 if (FAILED(rc))
10418 return rc;
10419 }
10420
10421 return S_OK;
10422}
10423
10424/**
10425 * Perform deferred hard disk detachments.
10426 *
10427 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10428 * backed up).
10429 *
10430 * If @a aOnline is @c true then this method will also unlock the old hard disks
10431 * for which the new implicit diffs were created and will lock these new diffs for
10432 * writing.
10433 *
10434 * @param aOnline Whether the VM was online prior to this operation.
10435 *
10436 * @note Locks this object for writing!
10437 */
10438void Machine::commitMedia(bool aOnline /*= false*/)
10439{
10440 AutoCaller autoCaller(this);
10441 AssertComRCReturnVoid(autoCaller.rc());
10442
10443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10444
10445 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10446
10447 HRESULT rc = S_OK;
10448
10449 /* no attach/detach operations -- nothing to do */
10450 if (!mMediaData.isBackedUp())
10451 return;
10452
10453 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10454 bool fMediaNeedsLocking = false;
10455
10456 /* enumerate new attachments */
10457 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10458 it != mMediaData->mAttachments.end();
10459 ++it)
10460 {
10461 MediumAttachment *pAttach = *it;
10462
10463 pAttach->commit();
10464
10465 Medium* pMedium = pAttach->getMedium();
10466 bool fImplicit = pAttach->isImplicit();
10467
10468 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10469 (pMedium) ? pMedium->getName().c_str() : "NULL",
10470 fImplicit));
10471
10472 /** @todo convert all this Machine-based voodoo to MediumAttachment
10473 * based commit logic. */
10474 if (fImplicit)
10475 {
10476 /* convert implicit attachment to normal */
10477 pAttach->setImplicit(false);
10478
10479 if ( aOnline
10480 && pMedium
10481 && pAttach->getType() == DeviceType_HardDisk
10482 )
10483 {
10484 ComObjPtr<Medium> parent = pMedium->getParent();
10485 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10486
10487 /* update the appropriate lock list */
10488 MediumLockList *pMediumLockList;
10489 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10490 AssertComRC(rc);
10491 if (pMediumLockList)
10492 {
10493 /* unlock if there's a need to change the locking */
10494 if (!fMediaNeedsLocking)
10495 {
10496 rc = mData->mSession.mLockedMedia.Unlock();
10497 AssertComRC(rc);
10498 fMediaNeedsLocking = true;
10499 }
10500 rc = pMediumLockList->Update(parent, false);
10501 AssertComRC(rc);
10502 rc = pMediumLockList->Append(pMedium, true);
10503 AssertComRC(rc);
10504 }
10505 }
10506
10507 continue;
10508 }
10509
10510 if (pMedium)
10511 {
10512 /* was this medium attached before? */
10513 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10514 oldIt != oldAtts.end();
10515 ++oldIt)
10516 {
10517 MediumAttachment *pOldAttach = *oldIt;
10518 if (pOldAttach->getMedium() == pMedium)
10519 {
10520 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10521
10522 /* yes: remove from old to avoid de-association */
10523 oldAtts.erase(oldIt);
10524 break;
10525 }
10526 }
10527 }
10528 }
10529
10530 /* enumerate remaining old attachments and de-associate from the
10531 * current machine state */
10532 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10533 it != oldAtts.end();
10534 ++it)
10535 {
10536 MediumAttachment *pAttach = *it;
10537 Medium* pMedium = pAttach->getMedium();
10538
10539 /* Detach only hard disks, since DVD/floppy media is detached
10540 * instantly in MountMedium. */
10541 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10542 {
10543 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10544
10545 /* now de-associate from the current machine state */
10546 rc = pMedium->removeBackReference(mData->mUuid);
10547 AssertComRC(rc);
10548
10549 if (aOnline)
10550 {
10551 /* unlock since medium is not used anymore */
10552 MediumLockList *pMediumLockList;
10553 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10554 AssertComRC(rc);
10555 if (pMediumLockList)
10556 {
10557 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10558 AssertComRC(rc);
10559 }
10560 }
10561 }
10562 }
10563
10564 /* take media locks again so that the locking state is consistent */
10565 if (fMediaNeedsLocking)
10566 {
10567 Assert(aOnline);
10568 rc = mData->mSession.mLockedMedia.Lock();
10569 AssertComRC(rc);
10570 }
10571
10572 /* commit the hard disk changes */
10573 mMediaData.commit();
10574
10575 if (isSessionMachine())
10576 {
10577 /*
10578 * Update the parent machine to point to the new owner.
10579 * This is necessary because the stored parent will point to the
10580 * session machine otherwise and cause crashes or errors later
10581 * when the session machine gets invalid.
10582 */
10583 /** @todo Change the MediumAttachment class to behave like any other
10584 * class in this regard by creating peer MediumAttachment
10585 * objects for session machines and share the data with the peer
10586 * machine.
10587 */
10588 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10589 it != mMediaData->mAttachments.end();
10590 ++it)
10591 {
10592 (*it)->updateParentMachine(mPeer);
10593 }
10594
10595 /* attach new data to the primary machine and reshare it */
10596 mPeer->mMediaData.attach(mMediaData);
10597 }
10598
10599 return;
10600}
10601
10602/**
10603 * Perform deferred deletion of implicitly created diffs.
10604 *
10605 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10606 * backed up).
10607 *
10608 * @note Locks this object for writing!
10609 */
10610void Machine::rollbackMedia()
10611{
10612 AutoCaller autoCaller(this);
10613 AssertComRCReturnVoid (autoCaller.rc());
10614
10615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10616
10617 LogFlowThisFunc(("Entering\n"));
10618
10619 HRESULT rc = S_OK;
10620
10621 /* no attach/detach operations -- nothing to do */
10622 if (!mMediaData.isBackedUp())
10623 return;
10624
10625 /* enumerate new attachments */
10626 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10627 it != mMediaData->mAttachments.end();
10628 ++it)
10629 {
10630 MediumAttachment *pAttach = *it;
10631 /* Fix up the backrefs for DVD/floppy media. */
10632 if (pAttach->getType() != DeviceType_HardDisk)
10633 {
10634 Medium* pMedium = pAttach->getMedium();
10635 if (pMedium)
10636 {
10637 rc = pMedium->removeBackReference(mData->mUuid);
10638 AssertComRC(rc);
10639 }
10640 }
10641
10642 (*it)->rollback();
10643
10644 pAttach = *it;
10645 /* Fix up the backrefs for DVD/floppy media. */
10646 if (pAttach->getType() != DeviceType_HardDisk)
10647 {
10648 Medium* pMedium = pAttach->getMedium();
10649 if (pMedium)
10650 {
10651 rc = pMedium->addBackReference(mData->mUuid);
10652 AssertComRC(rc);
10653 }
10654 }
10655 }
10656
10657 /** @todo convert all this Machine-based voodoo to MediumAttachment
10658 * based rollback logic. */
10659 deleteImplicitDiffs();
10660
10661 return;
10662}
10663
10664/**
10665 * Returns true if the settings file is located in the directory named exactly
10666 * as the machine; this means, among other things, that the machine directory
10667 * should be auto-renamed.
10668 *
10669 * @param aSettingsDir if not NULL, the full machine settings file directory
10670 * name will be assigned there.
10671 *
10672 * @note Doesn't lock anything.
10673 * @note Not thread safe (must be called from this object's lock).
10674 */
10675bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
10676{
10677 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10678 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
10679 if (aSettingsDir)
10680 *aSettingsDir = strMachineDirName;
10681 strMachineDirName.stripPath(); // vmname
10682 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10683 strConfigFileOnly.stripPath() // vmname.vbox
10684 .stripExt(); // vmname
10685
10686 AssertReturn(!strMachineDirName.isEmpty(), false);
10687 AssertReturn(!strConfigFileOnly.isEmpty(), false);
10688
10689 return strMachineDirName == strConfigFileOnly;
10690}
10691
10692/**
10693 * Discards all changes to machine settings.
10694 *
10695 * @param aNotify Whether to notify the direct session about changes or not.
10696 *
10697 * @note Locks objects for writing!
10698 */
10699void Machine::rollback(bool aNotify)
10700{
10701 AutoCaller autoCaller(this);
10702 AssertComRCReturn(autoCaller.rc(), (void)0);
10703
10704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10705
10706 if (!mStorageControllers.isNull())
10707 {
10708 if (mStorageControllers.isBackedUp())
10709 {
10710 /* unitialize all new devices (absent in the backed up list). */
10711 StorageControllerList::const_iterator it = mStorageControllers->begin();
10712 StorageControllerList *backedList = mStorageControllers.backedUpData();
10713 while (it != mStorageControllers->end())
10714 {
10715 if ( std::find(backedList->begin(), backedList->end(), *it)
10716 == backedList->end()
10717 )
10718 {
10719 (*it)->uninit();
10720 }
10721 ++it;
10722 }
10723
10724 /* restore the list */
10725 mStorageControllers.rollback();
10726 }
10727
10728 /* rollback any changes to devices after restoring the list */
10729 if (mData->flModifications & IsModified_Storage)
10730 {
10731 StorageControllerList::const_iterator it = mStorageControllers->begin();
10732 while (it != mStorageControllers->end())
10733 {
10734 (*it)->rollback();
10735 ++it;
10736 }
10737 }
10738 }
10739
10740 mUserData.rollback();
10741
10742 mHWData.rollback();
10743
10744 if (mData->flModifications & IsModified_Storage)
10745 rollbackMedia();
10746
10747 if (mBIOSSettings)
10748 mBIOSSettings->rollback();
10749
10750 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
10751 mVRDEServer->rollback();
10752
10753 if (mAudioAdapter)
10754 mAudioAdapter->rollback();
10755
10756 if (mUSBController && (mData->flModifications & IsModified_USB))
10757 mUSBController->rollback();
10758
10759 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
10760 mBandwidthControl->rollback();
10761
10762 if (!mHWData.isNull())
10763 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10764 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
10765 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
10766 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
10767
10768 if (mData->flModifications & IsModified_NetworkAdapters)
10769 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10770 if ( mNetworkAdapters[slot]
10771 && mNetworkAdapters[slot]->isModified())
10772 {
10773 mNetworkAdapters[slot]->rollback();
10774 networkAdapters[slot] = mNetworkAdapters[slot];
10775 }
10776
10777 if (mData->flModifications & IsModified_SerialPorts)
10778 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10779 if ( mSerialPorts[slot]
10780 && mSerialPorts[slot]->isModified())
10781 {
10782 mSerialPorts[slot]->rollback();
10783 serialPorts[slot] = mSerialPorts[slot];
10784 }
10785
10786 if (mData->flModifications & IsModified_ParallelPorts)
10787 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10788 if ( mParallelPorts[slot]
10789 && mParallelPorts[slot]->isModified())
10790 {
10791 mParallelPorts[slot]->rollback();
10792 parallelPorts[slot] = mParallelPorts[slot];
10793 }
10794
10795 if (aNotify)
10796 {
10797 /* inform the direct session about changes */
10798
10799 ComObjPtr<Machine> that = this;
10800 uint32_t flModifications = mData->flModifications;
10801 alock.release();
10802
10803 if (flModifications & IsModified_SharedFolders)
10804 that->onSharedFolderChange();
10805
10806 if (flModifications & IsModified_VRDEServer)
10807 that->onVRDEServerChange(/* aRestart */ TRUE);
10808 if (flModifications & IsModified_USB)
10809 that->onUSBControllerChange();
10810
10811 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
10812 if (networkAdapters[slot])
10813 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
10814 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
10815 if (serialPorts[slot])
10816 that->onSerialPortChange(serialPorts[slot]);
10817 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
10818 if (parallelPorts[slot])
10819 that->onParallelPortChange(parallelPorts[slot]);
10820
10821 if (flModifications & IsModified_Storage)
10822 that->onStorageControllerChange();
10823
10824#if 0
10825 if (flModifications & IsModified_BandwidthControl)
10826 that->onBandwidthControlChange();
10827#endif
10828 }
10829}
10830
10831/**
10832 * Commits all the changes to machine settings.
10833 *
10834 * Note that this operation is supposed to never fail.
10835 *
10836 * @note Locks this object and children for writing.
10837 */
10838void Machine::commit()
10839{
10840 AutoCaller autoCaller(this);
10841 AssertComRCReturnVoid(autoCaller.rc());
10842
10843 AutoCaller peerCaller(mPeer);
10844 AssertComRCReturnVoid(peerCaller.rc());
10845
10846 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
10847
10848 /*
10849 * use safe commit to ensure Snapshot machines (that share mUserData)
10850 * will still refer to a valid memory location
10851 */
10852 mUserData.commitCopy();
10853
10854 mHWData.commit();
10855
10856 if (mMediaData.isBackedUp())
10857 commitMedia();
10858
10859 mBIOSSettings->commit();
10860 mVRDEServer->commit();
10861 mAudioAdapter->commit();
10862 mUSBController->commit();
10863 mBandwidthControl->commit();
10864
10865 /* Keep the original network adapter count until this point, so that
10866 * discarding a chipset type change will not lose settings. */
10867 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10868 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10869 mNetworkAdapters[slot]->commit();
10870 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10871 mSerialPorts[slot]->commit();
10872 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10873 mParallelPorts[slot]->commit();
10874
10875 bool commitStorageControllers = false;
10876
10877 if (mStorageControllers.isBackedUp())
10878 {
10879 mStorageControllers.commit();
10880
10881 if (mPeer)
10882 {
10883 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
10884
10885 /* Commit all changes to new controllers (this will reshare data with
10886 * peers for those who have peers) */
10887 StorageControllerList *newList = new StorageControllerList();
10888 StorageControllerList::const_iterator it = mStorageControllers->begin();
10889 while (it != mStorageControllers->end())
10890 {
10891 (*it)->commit();
10892
10893 /* look if this controller has a peer device */
10894 ComObjPtr<StorageController> peer = (*it)->getPeer();
10895 if (!peer)
10896 {
10897 /* no peer means the device is a newly created one;
10898 * create a peer owning data this device share it with */
10899 peer.createObject();
10900 peer->init(mPeer, *it, true /* aReshare */);
10901 }
10902 else
10903 {
10904 /* remove peer from the old list */
10905 mPeer->mStorageControllers->remove(peer);
10906 }
10907 /* and add it to the new list */
10908 newList->push_back(peer);
10909
10910 ++it;
10911 }
10912
10913 /* uninit old peer's controllers that are left */
10914 it = mPeer->mStorageControllers->begin();
10915 while (it != mPeer->mStorageControllers->end())
10916 {
10917 (*it)->uninit();
10918 ++it;
10919 }
10920
10921 /* attach new list of controllers to our peer */
10922 mPeer->mStorageControllers.attach(newList);
10923 }
10924 else
10925 {
10926 /* we have no peer (our parent is the newly created machine);
10927 * just commit changes to devices */
10928 commitStorageControllers = true;
10929 }
10930 }
10931 else
10932 {
10933 /* the list of controllers itself is not changed,
10934 * just commit changes to controllers themselves */
10935 commitStorageControllers = true;
10936 }
10937
10938 if (commitStorageControllers)
10939 {
10940 StorageControllerList::const_iterator it = mStorageControllers->begin();
10941 while (it != mStorageControllers->end())
10942 {
10943 (*it)->commit();
10944 ++it;
10945 }
10946 }
10947
10948 if (isSessionMachine())
10949 {
10950 /* attach new data to the primary machine and reshare it */
10951 mPeer->mUserData.attach(mUserData);
10952 mPeer->mHWData.attach(mHWData);
10953 /* mMediaData is reshared by fixupMedia */
10954 // mPeer->mMediaData.attach(mMediaData);
10955 Assert(mPeer->mMediaData.data() == mMediaData.data());
10956 }
10957}
10958
10959/**
10960 * Copies all the hardware data from the given machine.
10961 *
10962 * Currently, only called when the VM is being restored from a snapshot. In
10963 * particular, this implies that the VM is not running during this method's
10964 * call.
10965 *
10966 * @note This method must be called from under this object's lock.
10967 *
10968 * @note This method doesn't call #commit(), so all data remains backed up and
10969 * unsaved.
10970 */
10971void Machine::copyFrom(Machine *aThat)
10972{
10973 AssertReturnVoid(!isSnapshotMachine());
10974 AssertReturnVoid(aThat->isSnapshotMachine());
10975
10976 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
10977
10978 mHWData.assignCopy(aThat->mHWData);
10979
10980 // create copies of all shared folders (mHWData after attaching a copy
10981 // contains just references to original objects)
10982 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
10983 it != mHWData->mSharedFolders.end();
10984 ++it)
10985 {
10986 ComObjPtr<SharedFolder> folder;
10987 folder.createObject();
10988 HRESULT rc = folder->initCopy(getMachine(), *it);
10989 AssertComRC(rc);
10990 *it = folder;
10991 }
10992
10993 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
10994 mVRDEServer->copyFrom(aThat->mVRDEServer);
10995 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
10996 mUSBController->copyFrom(aThat->mUSBController);
10997 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
10998
10999 /* create private copies of all controllers */
11000 mStorageControllers.backup();
11001 mStorageControllers->clear();
11002 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11003 it != aThat->mStorageControllers->end();
11004 ++it)
11005 {
11006 ComObjPtr<StorageController> ctrl;
11007 ctrl.createObject();
11008 ctrl->initCopy(this, *it);
11009 mStorageControllers->push_back(ctrl);
11010 }
11011
11012 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11013 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11014 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11015 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11016 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11017 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11018}
11019
11020/**
11021 * Returns whether the given storage controller is hotplug capable.
11022 *
11023 * @returns true if the controller supports hotplugging
11024 * false otherwise.
11025 * @param enmCtrlType The controller type to check for.
11026 */
11027bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11028{
11029 switch (enmCtrlType)
11030 {
11031 case StorageControllerType_IntelAhci:
11032 return true;
11033 case StorageControllerType_LsiLogic:
11034 case StorageControllerType_LsiLogicSas:
11035 case StorageControllerType_BusLogic:
11036 case StorageControllerType_PIIX3:
11037 case StorageControllerType_PIIX4:
11038 case StorageControllerType_ICH6:
11039 case StorageControllerType_I82078:
11040 default:
11041 return false;
11042 }
11043}
11044
11045#ifdef VBOX_WITH_RESOURCE_USAGE_API
11046
11047void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11048{
11049 AssertReturnVoid(isWriteLockOnCurrentThread());
11050 AssertPtrReturnVoid(aCollector);
11051
11052 pm::CollectorHAL *hal = aCollector->getHAL();
11053 /* Create sub metrics */
11054 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11055 "Percentage of processor time spent in user mode by the VM process.");
11056 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11057 "Percentage of processor time spent in kernel mode by the VM process.");
11058 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11059 "Size of resident portion of VM process in memory.");
11060 /* Create and register base metrics */
11061 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11062 cpuLoadUser, cpuLoadKernel);
11063 aCollector->registerBaseMetric(cpuLoad);
11064 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11065 ramUsageUsed);
11066 aCollector->registerBaseMetric(ramUsage);
11067
11068 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11069 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11070 new pm::AggregateAvg()));
11071 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11072 new pm::AggregateMin()));
11073 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11074 new pm::AggregateMax()));
11075 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11076 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11077 new pm::AggregateAvg()));
11078 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11079 new pm::AggregateMin()));
11080 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11081 new pm::AggregateMax()));
11082
11083 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11084 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11085 new pm::AggregateAvg()));
11086 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11087 new pm::AggregateMin()));
11088 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11089 new pm::AggregateMax()));
11090
11091
11092 /* Guest metrics collector */
11093 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11094 aCollector->registerGuest(mCollectorGuest);
11095 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11096 this, __PRETTY_FUNCTION__, mCollectorGuest));
11097
11098 /* Create sub metrics */
11099 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11100 "Percentage of processor time spent in user mode as seen by the guest.");
11101 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11102 "Percentage of processor time spent in kernel mode as seen by the guest.");
11103 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11104 "Percentage of processor time spent idling as seen by the guest.");
11105
11106 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11107 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11108 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11109 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11110 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11111 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11112
11113 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11114
11115 /* Create and register base metrics */
11116 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11117 guestLoadUser, guestLoadKernel, guestLoadIdle);
11118 aCollector->registerBaseMetric(guestCpuLoad);
11119
11120 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11121 guestMemTotal, guestMemFree,
11122 guestMemBalloon, guestMemShared,
11123 guestMemCache, guestPagedTotal);
11124 aCollector->registerBaseMetric(guestCpuMem);
11125
11126 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11127 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11128 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11129 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11130
11131 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11132 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11133 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11134 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11135
11136 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11137 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11138 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11139 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11140
11141 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11142 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11143 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11144 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11145
11146 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11147 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11148 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11149 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11150
11151 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11152 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11153 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11154 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11155
11156 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11157 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11158 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11159 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11160
11161 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11162 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11163 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11164 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11165
11166 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11167 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11168 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11169 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11170}
11171
11172void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11173{
11174 AssertReturnVoid(isWriteLockOnCurrentThread());
11175
11176 if (aCollector)
11177 {
11178 aCollector->unregisterMetricsFor(aMachine);
11179 aCollector->unregisterBaseMetricsFor(aMachine);
11180 }
11181}
11182
11183#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11184
11185
11186////////////////////////////////////////////////////////////////////////////////
11187
11188DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11189
11190HRESULT SessionMachine::FinalConstruct()
11191{
11192 LogFlowThisFunc(("\n"));
11193
11194#if defined(RT_OS_WINDOWS)
11195 mIPCSem = NULL;
11196#elif defined(RT_OS_OS2)
11197 mIPCSem = NULLHANDLE;
11198#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11199 mIPCSem = -1;
11200#else
11201# error "Port me!"
11202#endif
11203
11204 return BaseFinalConstruct();
11205}
11206
11207void SessionMachine::FinalRelease()
11208{
11209 LogFlowThisFunc(("\n"));
11210
11211 uninit(Uninit::Unexpected);
11212
11213 BaseFinalRelease();
11214}
11215
11216/**
11217 * @note Must be called only by Machine::openSession() from its own write lock.
11218 */
11219HRESULT SessionMachine::init(Machine *aMachine)
11220{
11221 LogFlowThisFuncEnter();
11222 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11223
11224 AssertReturn(aMachine, E_INVALIDARG);
11225
11226 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11227
11228 /* Enclose the state transition NotReady->InInit->Ready */
11229 AutoInitSpan autoInitSpan(this);
11230 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11231
11232 /* create the interprocess semaphore */
11233#if defined(RT_OS_WINDOWS)
11234 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11235 for (size_t i = 0; i < mIPCSemName.length(); i++)
11236 if (mIPCSemName.raw()[i] == '\\')
11237 mIPCSemName.raw()[i] = '/';
11238 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11239 ComAssertMsgRet(mIPCSem,
11240 ("Cannot create IPC mutex '%ls', err=%d",
11241 mIPCSemName.raw(), ::GetLastError()),
11242 E_FAIL);
11243#elif defined(RT_OS_OS2)
11244 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11245 aMachine->mData->mUuid.raw());
11246 mIPCSemName = ipcSem;
11247 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11248 ComAssertMsgRet(arc == NO_ERROR,
11249 ("Cannot create IPC mutex '%s', arc=%ld",
11250 ipcSem.c_str(), arc),
11251 E_FAIL);
11252#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11253# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11254# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11255 /** @todo Check that this still works correctly. */
11256 AssertCompileSize(key_t, 8);
11257# else
11258 AssertCompileSize(key_t, 4);
11259# endif
11260 key_t key;
11261 mIPCSem = -1;
11262 mIPCKey = "0";
11263 for (uint32_t i = 0; i < 1 << 24; i++)
11264 {
11265 key = ((uint32_t)'V' << 24) | i;
11266 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11267 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11268 {
11269 mIPCSem = sem;
11270 if (sem >= 0)
11271 mIPCKey = BstrFmt("%u", key);
11272 break;
11273 }
11274 }
11275# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11276 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11277 char *pszSemName = NULL;
11278 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11279 key_t key = ::ftok(pszSemName, 'V');
11280 RTStrFree(pszSemName);
11281
11282 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11283# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11284
11285 int errnoSave = errno;
11286 if (mIPCSem < 0 && errnoSave == ENOSYS)
11287 {
11288 setError(E_FAIL,
11289 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11290 "support for SysV IPC. Check the host kernel configuration for "
11291 "CONFIG_SYSVIPC=y"));
11292 return E_FAIL;
11293 }
11294 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11295 * the IPC semaphores */
11296 if (mIPCSem < 0 && errnoSave == ENOSPC)
11297 {
11298#ifdef RT_OS_LINUX
11299 setError(E_FAIL,
11300 tr("Cannot create IPC semaphore because the system limit for the "
11301 "maximum number of semaphore sets (SEMMNI), or the system wide "
11302 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11303 "current set of SysV IPC semaphores can be determined from "
11304 "the file /proc/sysvipc/sem"));
11305#else
11306 setError(E_FAIL,
11307 tr("Cannot create IPC semaphore because the system-imposed limit "
11308 "on the maximum number of allowed semaphores or semaphore "
11309 "identifiers system-wide would be exceeded"));
11310#endif
11311 return E_FAIL;
11312 }
11313 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11314 E_FAIL);
11315 /* set the initial value to 1 */
11316 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11317 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11318 E_FAIL);
11319#else
11320# error "Port me!"
11321#endif
11322
11323 /* memorize the peer Machine */
11324 unconst(mPeer) = aMachine;
11325 /* share the parent pointer */
11326 unconst(mParent) = aMachine->mParent;
11327
11328 /* take the pointers to data to share */
11329 mData.share(aMachine->mData);
11330 mSSData.share(aMachine->mSSData);
11331
11332 mUserData.share(aMachine->mUserData);
11333 mHWData.share(aMachine->mHWData);
11334 mMediaData.share(aMachine->mMediaData);
11335
11336 mStorageControllers.allocate();
11337 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11338 it != aMachine->mStorageControllers->end();
11339 ++it)
11340 {
11341 ComObjPtr<StorageController> ctl;
11342 ctl.createObject();
11343 ctl->init(this, *it);
11344 mStorageControllers->push_back(ctl);
11345 }
11346
11347 unconst(mBIOSSettings).createObject();
11348 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11349 /* create another VRDEServer object that will be mutable */
11350 unconst(mVRDEServer).createObject();
11351 mVRDEServer->init(this, aMachine->mVRDEServer);
11352 /* create another audio adapter object that will be mutable */
11353 unconst(mAudioAdapter).createObject();
11354 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11355 /* create a list of serial ports that will be mutable */
11356 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11357 {
11358 unconst(mSerialPorts[slot]).createObject();
11359 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11360 }
11361 /* create a list of parallel ports that will be mutable */
11362 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11363 {
11364 unconst(mParallelPorts[slot]).createObject();
11365 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11366 }
11367 /* create another USB controller object that will be mutable */
11368 unconst(mUSBController).createObject();
11369 mUSBController->init(this, aMachine->mUSBController);
11370
11371 /* create a list of network adapters that will be mutable */
11372 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11373 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11374 {
11375 unconst(mNetworkAdapters[slot]).createObject();
11376 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11377 }
11378
11379 /* create another bandwidth control object that will be mutable */
11380 unconst(mBandwidthControl).createObject();
11381 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11382
11383 /* default is to delete saved state on Saved -> PoweredOff transition */
11384 mRemoveSavedState = true;
11385
11386 /* Confirm a successful initialization when it's the case */
11387 autoInitSpan.setSucceeded();
11388
11389 LogFlowThisFuncLeave();
11390 return S_OK;
11391}
11392
11393/**
11394 * Uninitializes this session object. If the reason is other than
11395 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11396 *
11397 * @param aReason uninitialization reason
11398 *
11399 * @note Locks mParent + this object for writing.
11400 */
11401void SessionMachine::uninit(Uninit::Reason aReason)
11402{
11403 LogFlowThisFuncEnter();
11404 LogFlowThisFunc(("reason=%d\n", aReason));
11405
11406 /*
11407 * Strongly reference ourselves to prevent this object deletion after
11408 * mData->mSession.mMachine.setNull() below (which can release the last
11409 * reference and call the destructor). Important: this must be done before
11410 * accessing any members (and before AutoUninitSpan that does it as well).
11411 * This self reference will be released as the very last step on return.
11412 */
11413 ComObjPtr<SessionMachine> selfRef = this;
11414
11415 /* Enclose the state transition Ready->InUninit->NotReady */
11416 AutoUninitSpan autoUninitSpan(this);
11417 if (autoUninitSpan.uninitDone())
11418 {
11419 LogFlowThisFunc(("Already uninitialized\n"));
11420 LogFlowThisFuncLeave();
11421 return;
11422 }
11423
11424 if (autoUninitSpan.initFailed())
11425 {
11426 /* We've been called by init() because it's failed. It's not really
11427 * necessary (nor it's safe) to perform the regular uninit sequence
11428 * below, the following is enough.
11429 */
11430 LogFlowThisFunc(("Initialization failed.\n"));
11431#if defined(RT_OS_WINDOWS)
11432 if (mIPCSem)
11433 ::CloseHandle(mIPCSem);
11434 mIPCSem = NULL;
11435#elif defined(RT_OS_OS2)
11436 if (mIPCSem != NULLHANDLE)
11437 ::DosCloseMutexSem(mIPCSem);
11438 mIPCSem = NULLHANDLE;
11439#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11440 if (mIPCSem >= 0)
11441 ::semctl(mIPCSem, 0, IPC_RMID);
11442 mIPCSem = -1;
11443# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11444 mIPCKey = "0";
11445# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11446#else
11447# error "Port me!"
11448#endif
11449 uninitDataAndChildObjects();
11450 mData.free();
11451 unconst(mParent) = NULL;
11452 unconst(mPeer) = NULL;
11453 LogFlowThisFuncLeave();
11454 return;
11455 }
11456
11457 MachineState_T lastState;
11458 {
11459 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11460 lastState = mData->mMachineState;
11461 }
11462 NOREF(lastState);
11463
11464#ifdef VBOX_WITH_USB
11465 // release all captured USB devices, but do this before requesting the locks below
11466 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11467 {
11468 /* Console::captureUSBDevices() is called in the VM process only after
11469 * setting the machine state to Starting or Restoring.
11470 * Console::detachAllUSBDevices() will be called upon successful
11471 * termination. So, we need to release USB devices only if there was
11472 * an abnormal termination of a running VM.
11473 *
11474 * This is identical to SessionMachine::DetachAllUSBDevices except
11475 * for the aAbnormal argument. */
11476 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11477 AssertComRC(rc);
11478 NOREF(rc);
11479
11480 USBProxyService *service = mParent->host()->usbProxyService();
11481 if (service)
11482 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11483 }
11484#endif /* VBOX_WITH_USB */
11485
11486 // we need to lock this object in uninit() because the lock is shared
11487 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11488 // and others need mParent lock, and USB needs host lock.
11489 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11490
11491#if 0
11492 // Trigger async cleanup tasks, avoid doing things here which are not
11493 // vital to be done immediately and maybe need more locks. This calls
11494 // Machine::unregisterMetrics().
11495 mParent->onMachineUninit(mPeer);
11496#else
11497 /*
11498 * It is safe to call Machine::unregisterMetrics() here because
11499 * PerformanceCollector::samplerCallback no longer accesses guest methods
11500 * holding the lock.
11501 */
11502 unregisterMetrics(mParent->performanceCollector(), mPeer);
11503#endif
11504 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11505 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11506 this, __PRETTY_FUNCTION__, mCollectorGuest));
11507 if (mCollectorGuest)
11508 {
11509 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11510 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11511 mCollectorGuest = NULL;
11512 }
11513
11514 if (aReason == Uninit::Abnormal)
11515 {
11516 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11517 Global::IsOnlineOrTransient(lastState)));
11518
11519 /* reset the state to Aborted */
11520 if (mData->mMachineState != MachineState_Aborted)
11521 setMachineState(MachineState_Aborted);
11522 }
11523
11524 // any machine settings modified?
11525 if (mData->flModifications)
11526 {
11527 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
11528 rollback(false /* aNotify */);
11529 }
11530
11531 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
11532 || !mConsoleTaskData.mSnapshot);
11533 if (!mConsoleTaskData.strStateFilePath.isEmpty())
11534 {
11535 LogWarningThisFunc(("canceling failed save state request!\n"));
11536 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
11537 }
11538 else if (!mConsoleTaskData.mSnapshot.isNull())
11539 {
11540 LogWarningThisFunc(("canceling untaken snapshot!\n"));
11541
11542 /* delete all differencing hard disks created (this will also attach
11543 * their parents back by rolling back mMediaData) */
11544 rollbackMedia();
11545
11546 // delete the saved state file (it might have been already created)
11547 // AFTER killing the snapshot so that releaseSavedStateFile() won't
11548 // think it's still in use
11549 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
11550 mConsoleTaskData.mSnapshot->uninit();
11551 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
11552 }
11553
11554 if (!mData->mSession.mType.isEmpty())
11555 {
11556 /* mType is not null when this machine's process has been started by
11557 * Machine::LaunchVMProcess(), therefore it is our child. We
11558 * need to queue the PID to reap the process (and avoid zombies on
11559 * Linux). */
11560 Assert(mData->mSession.mPid != NIL_RTPROCESS);
11561 mParent->addProcessToReap(mData->mSession.mPid);
11562 }
11563
11564 mData->mSession.mPid = NIL_RTPROCESS;
11565
11566 if (aReason == Uninit::Unexpected)
11567 {
11568 /* Uninitialization didn't come from #checkForDeath(), so tell the
11569 * client watcher thread to update the set of machines that have open
11570 * sessions. */
11571 mParent->updateClientWatcher();
11572 }
11573
11574 /* uninitialize all remote controls */
11575 if (mData->mSession.mRemoteControls.size())
11576 {
11577 LogFlowThisFunc(("Closing remote sessions (%d):\n",
11578 mData->mSession.mRemoteControls.size()));
11579
11580 Data::Session::RemoteControlList::iterator it =
11581 mData->mSession.mRemoteControls.begin();
11582 while (it != mData->mSession.mRemoteControls.end())
11583 {
11584 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
11585 HRESULT rc = (*it)->Uninitialize();
11586 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
11587 if (FAILED(rc))
11588 LogWarningThisFunc(("Forgot to close the remote session?\n"));
11589 ++it;
11590 }
11591 mData->mSession.mRemoteControls.clear();
11592 }
11593
11594 /*
11595 * An expected uninitialization can come only from #checkForDeath().
11596 * Otherwise it means that something's gone really wrong (for example,
11597 * the Session implementation has released the VirtualBox reference
11598 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
11599 * etc). However, it's also possible, that the client releases the IPC
11600 * semaphore correctly (i.e. before it releases the VirtualBox reference),
11601 * but the VirtualBox release event comes first to the server process.
11602 * This case is practically possible, so we should not assert on an
11603 * unexpected uninit, just log a warning.
11604 */
11605
11606 if ((aReason == Uninit::Unexpected))
11607 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
11608
11609 if (aReason != Uninit::Normal)
11610 {
11611 mData->mSession.mDirectControl.setNull();
11612 }
11613 else
11614 {
11615 /* this must be null here (see #OnSessionEnd()) */
11616 Assert(mData->mSession.mDirectControl.isNull());
11617 Assert(mData->mSession.mState == SessionState_Unlocking);
11618 Assert(!mData->mSession.mProgress.isNull());
11619 }
11620 if (mData->mSession.mProgress)
11621 {
11622 if (aReason == Uninit::Normal)
11623 mData->mSession.mProgress->notifyComplete(S_OK);
11624 else
11625 mData->mSession.mProgress->notifyComplete(E_FAIL,
11626 COM_IIDOF(ISession),
11627 getComponentName(),
11628 tr("The VM session was aborted"));
11629 mData->mSession.mProgress.setNull();
11630 }
11631
11632 /* remove the association between the peer machine and this session machine */
11633 Assert( (SessionMachine*)mData->mSession.mMachine == this
11634 || aReason == Uninit::Unexpected);
11635
11636 /* reset the rest of session data */
11637 mData->mSession.mMachine.setNull();
11638 mData->mSession.mState = SessionState_Unlocked;
11639 mData->mSession.mType.setNull();
11640
11641 /* close the interprocess semaphore before leaving the exclusive lock */
11642#if defined(RT_OS_WINDOWS)
11643 if (mIPCSem)
11644 ::CloseHandle(mIPCSem);
11645 mIPCSem = NULL;
11646#elif defined(RT_OS_OS2)
11647 if (mIPCSem != NULLHANDLE)
11648 ::DosCloseMutexSem(mIPCSem);
11649 mIPCSem = NULLHANDLE;
11650#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11651 if (mIPCSem >= 0)
11652 ::semctl(mIPCSem, 0, IPC_RMID);
11653 mIPCSem = -1;
11654# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11655 mIPCKey = "0";
11656# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11657#else
11658# error "Port me!"
11659#endif
11660
11661 /* fire an event */
11662 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
11663
11664 uninitDataAndChildObjects();
11665
11666 /* free the essential data structure last */
11667 mData.free();
11668
11669 /* release the exclusive lock before setting the below two to NULL */
11670 multilock.release();
11671
11672 unconst(mParent) = NULL;
11673 unconst(mPeer) = NULL;
11674
11675 LogFlowThisFuncLeave();
11676}
11677
11678// util::Lockable interface
11679////////////////////////////////////////////////////////////////////////////////
11680
11681/**
11682 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
11683 * with the primary Machine instance (mPeer).
11684 */
11685RWLockHandle *SessionMachine::lockHandle() const
11686{
11687 AssertReturn(mPeer != NULL, NULL);
11688 return mPeer->lockHandle();
11689}
11690
11691// IInternalMachineControl methods
11692////////////////////////////////////////////////////////////////////////////////
11693
11694/**
11695 * Passes collected guest statistics to performance collector object
11696 */
11697STDMETHODIMP SessionMachine::ReportGuestStatistics(ULONG aValidStats, ULONG aCpuUser,
11698 ULONG aCpuKernel, ULONG aCpuIdle,
11699 ULONG aMemTotal, ULONG aMemFree,
11700 ULONG aMemBalloon, ULONG aMemShared,
11701 ULONG aMemCache, ULONG aPageTotal,
11702 ULONG aAllocVMM, ULONG aFreeVMM,
11703 ULONG aBalloonedVMM, ULONG aSharedVMM)
11704{
11705 if (mCollectorGuest)
11706 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
11707 aMemTotal, aMemFree, aMemBalloon, aMemShared,
11708 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
11709 aBalloonedVMM, aSharedVMM);
11710
11711 return S_OK;
11712}
11713
11714/**
11715 * @note Locks this object for writing.
11716 */
11717STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
11718{
11719 AutoCaller autoCaller(this);
11720 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11721
11722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11723
11724 mRemoveSavedState = aRemove;
11725
11726 return S_OK;
11727}
11728
11729/**
11730 * @note Locks the same as #setMachineState() does.
11731 */
11732STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
11733{
11734 return setMachineState(aMachineState);
11735}
11736
11737/**
11738 * @note Locks this object for reading.
11739 */
11740STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
11741{
11742 AutoCaller autoCaller(this);
11743 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11744
11745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11746
11747#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
11748 mIPCSemName.cloneTo(aId);
11749 return S_OK;
11750#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11751# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11752 mIPCKey.cloneTo(aId);
11753# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11754 mData->m_strConfigFileFull.cloneTo(aId);
11755# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11756 return S_OK;
11757#else
11758# error "Port me!"
11759#endif
11760}
11761
11762/**
11763 * @note Locks this object for writing.
11764 */
11765STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
11766{
11767 LogFlowThisFunc(("aProgress=%p\n", aProgress));
11768 AutoCaller autoCaller(this);
11769 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11770
11771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11772
11773 if (mData->mSession.mState != SessionState_Locked)
11774 return VBOX_E_INVALID_OBJECT_STATE;
11775
11776 if (!mData->mSession.mProgress.isNull())
11777 mData->mSession.mProgress->setOtherProgressObject(aProgress);
11778
11779 LogFlowThisFunc(("returns S_OK.\n"));
11780 return S_OK;
11781}
11782
11783/**
11784 * @note Locks this object for writing.
11785 */
11786STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
11787{
11788 AutoCaller autoCaller(this);
11789 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11790
11791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11792
11793 if (mData->mSession.mState != SessionState_Locked)
11794 return VBOX_E_INVALID_OBJECT_STATE;
11795
11796 /* Finalize the LaunchVMProcess progress object. */
11797 if (mData->mSession.mProgress)
11798 {
11799 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
11800 mData->mSession.mProgress.setNull();
11801 }
11802
11803 if (SUCCEEDED((HRESULT)iResult))
11804 {
11805#ifdef VBOX_WITH_RESOURCE_USAGE_API
11806 /* The VM has been powered up successfully, so it makes sense
11807 * now to offer the performance metrics for a running machine
11808 * object. Doing it earlier wouldn't be safe. */
11809 registerMetrics(mParent->performanceCollector(), mPeer,
11810 mData->mSession.mPid);
11811#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11812 }
11813
11814 return S_OK;
11815}
11816
11817/**
11818 * @note Locks this object for writing.
11819 */
11820STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
11821{
11822 LogFlowThisFuncEnter();
11823
11824 CheckComArgOutPointerValid(aProgress);
11825
11826 AutoCaller autoCaller(this);
11827 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11828
11829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11830
11831 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
11832 E_FAIL);
11833
11834 /* create a progress object to track operation completion */
11835 ComObjPtr<Progress> pProgress;
11836 pProgress.createObject();
11837 pProgress->init(getVirtualBox(),
11838 static_cast<IMachine *>(this) /* aInitiator */,
11839 Bstr(tr("Stopping the virtual machine")).raw(),
11840 FALSE /* aCancelable */);
11841
11842 /* fill in the console task data */
11843 mConsoleTaskData.mLastState = mData->mMachineState;
11844 mConsoleTaskData.mProgress = pProgress;
11845
11846 /* set the state to Stopping (this is expected by Console::PowerDown()) */
11847 setMachineState(MachineState_Stopping);
11848
11849 pProgress.queryInterfaceTo(aProgress);
11850
11851 return S_OK;
11852}
11853
11854/**
11855 * @note Locks this object for writing.
11856 */
11857STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
11858{
11859 LogFlowThisFuncEnter();
11860
11861 AutoCaller autoCaller(this);
11862 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11863
11864 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11865
11866 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
11867 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
11868 && mConsoleTaskData.mLastState != MachineState_Null,
11869 E_FAIL);
11870
11871 /*
11872 * On failure, set the state to the state we had when BeginPoweringDown()
11873 * was called (this is expected by Console::PowerDown() and the associated
11874 * task). On success the VM process already changed the state to
11875 * MachineState_PoweredOff, so no need to do anything.
11876 */
11877 if (FAILED(iResult))
11878 setMachineState(mConsoleTaskData.mLastState);
11879
11880 /* notify the progress object about operation completion */
11881 Assert(mConsoleTaskData.mProgress);
11882 if (SUCCEEDED(iResult))
11883 mConsoleTaskData.mProgress->notifyComplete(S_OK);
11884 else
11885 {
11886 Utf8Str strErrMsg(aErrMsg);
11887 if (strErrMsg.length())
11888 mConsoleTaskData.mProgress->notifyComplete(iResult,
11889 COM_IIDOF(ISession),
11890 getComponentName(),
11891 strErrMsg.c_str());
11892 else
11893 mConsoleTaskData.mProgress->notifyComplete(iResult);
11894 }
11895
11896 /* clear out the temporary saved state data */
11897 mConsoleTaskData.mLastState = MachineState_Null;
11898 mConsoleTaskData.mProgress.setNull();
11899
11900 LogFlowThisFuncLeave();
11901 return S_OK;
11902}
11903
11904
11905/**
11906 * Goes through the USB filters of the given machine to see if the given
11907 * device matches any filter or not.
11908 *
11909 * @note Locks the same as USBController::hasMatchingFilter() does.
11910 */
11911STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
11912 BOOL *aMatched,
11913 ULONG *aMaskedIfs)
11914{
11915 LogFlowThisFunc(("\n"));
11916
11917 CheckComArgNotNull(aUSBDevice);
11918 CheckComArgOutPointerValid(aMatched);
11919
11920 AutoCaller autoCaller(this);
11921 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11922
11923#ifdef VBOX_WITH_USB
11924 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
11925#else
11926 NOREF(aUSBDevice);
11927 NOREF(aMaskedIfs);
11928 *aMatched = FALSE;
11929#endif
11930
11931 return S_OK;
11932}
11933
11934/**
11935 * @note Locks the same as Host::captureUSBDevice() does.
11936 */
11937STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
11938{
11939 LogFlowThisFunc(("\n"));
11940
11941 AutoCaller autoCaller(this);
11942 AssertComRCReturnRC(autoCaller.rc());
11943
11944#ifdef VBOX_WITH_USB
11945 /* if captureDeviceForVM() fails, it must have set extended error info */
11946 clearError();
11947 MultiResult rc = mParent->host()->checkUSBProxyService();
11948 if (FAILED(rc)) return rc;
11949
11950 USBProxyService *service = mParent->host()->usbProxyService();
11951 AssertReturn(service, E_FAIL);
11952 return service->captureDeviceForVM(this, Guid(aId).ref());
11953#else
11954 NOREF(aId);
11955 return E_NOTIMPL;
11956#endif
11957}
11958
11959/**
11960 * @note Locks the same as Host::detachUSBDevice() does.
11961 */
11962STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
11963{
11964 LogFlowThisFunc(("\n"));
11965
11966 AutoCaller autoCaller(this);
11967 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11968
11969#ifdef VBOX_WITH_USB
11970 USBProxyService *service = mParent->host()->usbProxyService();
11971 AssertReturn(service, E_FAIL);
11972 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
11973#else
11974 NOREF(aId);
11975 NOREF(aDone);
11976 return E_NOTIMPL;
11977#endif
11978}
11979
11980/**
11981 * Inserts all machine filters to the USB proxy service and then calls
11982 * Host::autoCaptureUSBDevices().
11983 *
11984 * Called by Console from the VM process upon VM startup.
11985 *
11986 * @note Locks what called methods lock.
11987 */
11988STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
11989{
11990 LogFlowThisFunc(("\n"));
11991
11992 AutoCaller autoCaller(this);
11993 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11994
11995#ifdef VBOX_WITH_USB
11996 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
11997 AssertComRC(rc);
11998 NOREF(rc);
11999
12000 USBProxyService *service = mParent->host()->usbProxyService();
12001 AssertReturn(service, E_FAIL);
12002 return service->autoCaptureDevicesForVM(this);
12003#else
12004 return S_OK;
12005#endif
12006}
12007
12008/**
12009 * Removes all machine filters from the USB proxy service and then calls
12010 * Host::detachAllUSBDevices().
12011 *
12012 * Called by Console from the VM process upon normal VM termination or by
12013 * SessionMachine::uninit() upon abnormal VM termination (from under the
12014 * Machine/SessionMachine lock).
12015 *
12016 * @note Locks what called methods lock.
12017 */
12018STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12019{
12020 LogFlowThisFunc(("\n"));
12021
12022 AutoCaller autoCaller(this);
12023 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12024
12025#ifdef VBOX_WITH_USB
12026 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12027 AssertComRC(rc);
12028 NOREF(rc);
12029
12030 USBProxyService *service = mParent->host()->usbProxyService();
12031 AssertReturn(service, E_FAIL);
12032 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12033#else
12034 NOREF(aDone);
12035 return S_OK;
12036#endif
12037}
12038
12039/**
12040 * @note Locks this object for writing.
12041 */
12042STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12043 IProgress **aProgress)
12044{
12045 LogFlowThisFuncEnter();
12046
12047 AssertReturn(aSession, E_INVALIDARG);
12048 AssertReturn(aProgress, E_INVALIDARG);
12049
12050 AutoCaller autoCaller(this);
12051
12052 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12053 /*
12054 * We don't assert below because it might happen that a non-direct session
12055 * informs us it is closed right after we've been uninitialized -- it's ok.
12056 */
12057 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12058
12059 /* get IInternalSessionControl interface */
12060 ComPtr<IInternalSessionControl> control(aSession);
12061
12062 ComAssertRet(!control.isNull(), E_INVALIDARG);
12063
12064 /* Creating a Progress object requires the VirtualBox lock, and
12065 * thus locking it here is required by the lock order rules. */
12066 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12067
12068 if (control == mData->mSession.mDirectControl)
12069 {
12070 ComAssertRet(aProgress, E_POINTER);
12071
12072 /* The direct session is being normally closed by the client process
12073 * ----------------------------------------------------------------- */
12074
12075 /* go to the closing state (essential for all open*Session() calls and
12076 * for #checkForDeath()) */
12077 Assert(mData->mSession.mState == SessionState_Locked);
12078 mData->mSession.mState = SessionState_Unlocking;
12079
12080 /* set direct control to NULL to release the remote instance */
12081 mData->mSession.mDirectControl.setNull();
12082 LogFlowThisFunc(("Direct control is set to NULL\n"));
12083
12084 if (mData->mSession.mProgress)
12085 {
12086 /* finalize the progress, someone might wait if a frontend
12087 * closes the session before powering on the VM. */
12088 mData->mSession.mProgress->notifyComplete(E_FAIL,
12089 COM_IIDOF(ISession),
12090 getComponentName(),
12091 tr("The VM session was closed before any attempt to power it on"));
12092 mData->mSession.mProgress.setNull();
12093 }
12094
12095 /* Create the progress object the client will use to wait until
12096 * #checkForDeath() is called to uninitialize this session object after
12097 * it releases the IPC semaphore.
12098 * Note! Because we're "reusing" mProgress here, this must be a proxy
12099 * object just like for LaunchVMProcess. */
12100 Assert(mData->mSession.mProgress.isNull());
12101 ComObjPtr<ProgressProxy> progress;
12102 progress.createObject();
12103 ComPtr<IUnknown> pPeer(mPeer);
12104 progress->init(mParent, pPeer,
12105 Bstr(tr("Closing session")).raw(),
12106 FALSE /* aCancelable */);
12107 progress.queryInterfaceTo(aProgress);
12108 mData->mSession.mProgress = progress;
12109 }
12110 else
12111 {
12112 /* the remote session is being normally closed */
12113 Data::Session::RemoteControlList::iterator it =
12114 mData->mSession.mRemoteControls.begin();
12115 while (it != mData->mSession.mRemoteControls.end())
12116 {
12117 if (control == *it)
12118 break;
12119 ++it;
12120 }
12121 BOOL found = it != mData->mSession.mRemoteControls.end();
12122 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12123 E_INVALIDARG);
12124 // This MUST be erase(it), not remove(*it) as the latter triggers a
12125 // very nasty use after free due to the place where the value "lives".
12126 mData->mSession.mRemoteControls.erase(it);
12127 }
12128
12129 LogFlowThisFuncLeave();
12130 return S_OK;
12131}
12132
12133/**
12134 * @note Locks this object for writing.
12135 */
12136STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12137{
12138 LogFlowThisFuncEnter();
12139
12140 CheckComArgOutPointerValid(aProgress);
12141 CheckComArgOutPointerValid(aStateFilePath);
12142
12143 AutoCaller autoCaller(this);
12144 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12145
12146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12147
12148 AssertReturn( mData->mMachineState == MachineState_Paused
12149 && mConsoleTaskData.mLastState == MachineState_Null
12150 && mConsoleTaskData.strStateFilePath.isEmpty(),
12151 E_FAIL);
12152
12153 /* create a progress object to track operation completion */
12154 ComObjPtr<Progress> pProgress;
12155 pProgress.createObject();
12156 pProgress->init(getVirtualBox(),
12157 static_cast<IMachine *>(this) /* aInitiator */,
12158 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12159 FALSE /* aCancelable */);
12160
12161 Utf8Str strStateFilePath;
12162 /* stateFilePath is null when the machine is not running */
12163 if (mData->mMachineState == MachineState_Paused)
12164 composeSavedStateFilename(strStateFilePath);
12165
12166 /* fill in the console task data */
12167 mConsoleTaskData.mLastState = mData->mMachineState;
12168 mConsoleTaskData.strStateFilePath = strStateFilePath;
12169 mConsoleTaskData.mProgress = pProgress;
12170
12171 /* set the state to Saving (this is expected by Console::SaveState()) */
12172 setMachineState(MachineState_Saving);
12173
12174 strStateFilePath.cloneTo(aStateFilePath);
12175 pProgress.queryInterfaceTo(aProgress);
12176
12177 return S_OK;
12178}
12179
12180/**
12181 * @note Locks mParent + this object for writing.
12182 */
12183STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12184{
12185 LogFlowThisFunc(("\n"));
12186
12187 AutoCaller autoCaller(this);
12188 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12189
12190 /* endSavingState() need mParent lock */
12191 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12192
12193 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12194 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12195 && mConsoleTaskData.mLastState != MachineState_Null
12196 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12197 E_FAIL);
12198
12199 /*
12200 * On failure, set the state to the state we had when BeginSavingState()
12201 * was called (this is expected by Console::SaveState() and the associated
12202 * task). On success the VM process already changed the state to
12203 * MachineState_Saved, so no need to do anything.
12204 */
12205 if (FAILED(iResult))
12206 setMachineState(mConsoleTaskData.mLastState);
12207
12208 return endSavingState(iResult, aErrMsg);
12209}
12210
12211/**
12212 * @note Locks this object for writing.
12213 */
12214STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12215{
12216 LogFlowThisFunc(("\n"));
12217
12218 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12219
12220 AutoCaller autoCaller(this);
12221 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12222
12223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12224
12225 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12226 || mData->mMachineState == MachineState_Teleported
12227 || mData->mMachineState == MachineState_Aborted
12228 , E_FAIL); /** @todo setError. */
12229
12230 Utf8Str stateFilePathFull = aSavedStateFile;
12231 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12232 if (RT_FAILURE(vrc))
12233 return setError(VBOX_E_FILE_ERROR,
12234 tr("Invalid saved state file path '%ls' (%Rrc)"),
12235 aSavedStateFile,
12236 vrc);
12237
12238 mSSData->strStateFilePath = stateFilePathFull;
12239
12240 /* The below setMachineState() will detect the state transition and will
12241 * update the settings file */
12242
12243 return setMachineState(MachineState_Saved);
12244}
12245
12246STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12247 ComSafeArrayOut(BSTR, aValues),
12248 ComSafeArrayOut(LONG64, aTimestamps),
12249 ComSafeArrayOut(BSTR, aFlags))
12250{
12251 LogFlowThisFunc(("\n"));
12252
12253#ifdef VBOX_WITH_GUEST_PROPS
12254 using namespace guestProp;
12255
12256 AutoCaller autoCaller(this);
12257 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12258
12259 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12260
12261 CheckComArgOutSafeArrayPointerValid(aNames);
12262 CheckComArgOutSafeArrayPointerValid(aValues);
12263 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12264 CheckComArgOutSafeArrayPointerValid(aFlags);
12265
12266 size_t cEntries = mHWData->mGuestProperties.size();
12267 com::SafeArray<BSTR> names(cEntries);
12268 com::SafeArray<BSTR> values(cEntries);
12269 com::SafeArray<LONG64> timestamps(cEntries);
12270 com::SafeArray<BSTR> flags(cEntries);
12271 unsigned i = 0;
12272 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
12273 it != mHWData->mGuestProperties.end();
12274 ++it)
12275 {
12276 char szFlags[MAX_FLAGS_LEN + 1];
12277 it->strName.cloneTo(&names[i]);
12278 it->strValue.cloneTo(&values[i]);
12279 timestamps[i] = it->mTimestamp;
12280 /* If it is NULL, keep it NULL. */
12281 if (it->mFlags)
12282 {
12283 writeFlags(it->mFlags, szFlags);
12284 Bstr(szFlags).cloneTo(&flags[i]);
12285 }
12286 else
12287 flags[i] = NULL;
12288 ++i;
12289 }
12290 names.detachTo(ComSafeArrayOutArg(aNames));
12291 values.detachTo(ComSafeArrayOutArg(aValues));
12292 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12293 flags.detachTo(ComSafeArrayOutArg(aFlags));
12294 return S_OK;
12295#else
12296 ReturnComNotImplemented();
12297#endif
12298}
12299
12300STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12301 IN_BSTR aValue,
12302 LONG64 aTimestamp,
12303 IN_BSTR aFlags)
12304{
12305 LogFlowThisFunc(("\n"));
12306
12307#ifdef VBOX_WITH_GUEST_PROPS
12308 using namespace guestProp;
12309
12310 CheckComArgStrNotEmptyOrNull(aName);
12311 CheckComArgNotNull(aValue);
12312 CheckComArgNotNull(aFlags);
12313
12314 try
12315 {
12316 /*
12317 * Convert input up front.
12318 */
12319 Utf8Str utf8Name(aName);
12320 uint32_t fFlags = NILFLAG;
12321 if (aFlags)
12322 {
12323 Utf8Str utf8Flags(aFlags);
12324 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12325 AssertRCReturn(vrc, E_INVALIDARG);
12326 }
12327
12328 /*
12329 * Now grab the object lock, validate the state and do the update.
12330 */
12331 AutoCaller autoCaller(this);
12332 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12333
12334 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12335
12336 switch (mData->mMachineState)
12337 {
12338 case MachineState_Paused:
12339 case MachineState_Running:
12340 case MachineState_Teleporting:
12341 case MachineState_TeleportingPausedVM:
12342 case MachineState_LiveSnapshotting:
12343 case MachineState_DeletingSnapshotOnline:
12344 case MachineState_DeletingSnapshotPaused:
12345 case MachineState_Saving:
12346 break;
12347
12348 default:
12349#ifndef DEBUG_sunlover
12350 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12351 VBOX_E_INVALID_VM_STATE);
12352#else
12353 return VBOX_E_INVALID_VM_STATE;
12354#endif
12355 }
12356
12357 setModified(IsModified_MachineData);
12358 mHWData.backup();
12359
12360 /** @todo r=bird: The careful memory handling doesn't work out here because
12361 * the catch block won't undo any damage we've done. So, if push_back throws
12362 * bad_alloc then you've lost the value.
12363 *
12364 * Another thing. Doing a linear search here isn't extremely efficient, esp.
12365 * since values that changes actually bubbles to the end of the list. Using
12366 * something that has an efficient lookup and can tolerate a bit of updates
12367 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
12368 * combination of RTStrCache (for sharing names and getting uniqueness into
12369 * the bargain) and hash/tree is another. */
12370 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
12371 iter != mHWData->mGuestProperties.end();
12372 ++iter)
12373 if (utf8Name == iter->strName)
12374 {
12375 mHWData->mGuestProperties.erase(iter);
12376 mData->mGuestPropertiesModified = TRUE;
12377 break;
12378 }
12379 if (aValue != NULL)
12380 {
12381 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
12382 mHWData->mGuestProperties.push_back(property);
12383 mData->mGuestPropertiesModified = TRUE;
12384 }
12385
12386 /*
12387 * Send a callback notification if appropriate
12388 */
12389 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12390 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12391 RTSTR_MAX,
12392 utf8Name.c_str(),
12393 RTSTR_MAX, NULL)
12394 )
12395 {
12396 alock.release();
12397
12398 mParent->onGuestPropertyChange(mData->mUuid,
12399 aName,
12400 aValue,
12401 aFlags);
12402 }
12403 }
12404 catch (...)
12405 {
12406 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12407 }
12408 return S_OK;
12409#else
12410 ReturnComNotImplemented();
12411#endif
12412}
12413
12414STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12415 IMediumAttachment **aNewAttachment)
12416{
12417 CheckComArgNotNull(aAttachment);
12418 CheckComArgOutPointerValid(aNewAttachment);
12419
12420 AutoCaller autoCaller(this);
12421 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12422
12423 // request the host lock first, since might be calling Host methods for getting host drives;
12424 // next, protect the media tree all the while we're in here, as well as our member variables
12425 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12426 this->lockHandle(),
12427 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12428
12429 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12430
12431 Bstr ctrlName;
12432 LONG lPort;
12433 LONG lDevice;
12434 bool fTempEject;
12435 {
12436 AutoCaller autoAttachCaller(this);
12437 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12438
12439 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12440
12441 /* Need to query the details first, as the IMediumAttachment reference
12442 * might be to the original settings, which we are going to change. */
12443 ctrlName = pAttach->getControllerName();
12444 lPort = pAttach->getPort();
12445 lDevice = pAttach->getDevice();
12446 fTempEject = pAttach->getTempEject();
12447 }
12448
12449 if (!fTempEject)
12450 {
12451 /* Remember previously mounted medium. The medium before taking the
12452 * backup is not necessarily the same thing. */
12453 ComObjPtr<Medium> oldmedium;
12454 oldmedium = pAttach->getMedium();
12455
12456 setModified(IsModified_Storage);
12457 mMediaData.backup();
12458
12459 // The backup operation makes the pAttach reference point to the
12460 // old settings. Re-get the correct reference.
12461 pAttach = findAttachment(mMediaData->mAttachments,
12462 ctrlName.raw(),
12463 lPort,
12464 lDevice);
12465
12466 {
12467 AutoCaller autoAttachCaller(this);
12468 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12469
12470 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12471 if (!oldmedium.isNull())
12472 oldmedium->removeBackReference(mData->mUuid);
12473
12474 pAttach->updateMedium(NULL);
12475 pAttach->updateEjected();
12476 }
12477
12478 setModified(IsModified_Storage);
12479 }
12480 else
12481 {
12482 {
12483 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12484 pAttach->updateEjected();
12485 }
12486 }
12487
12488 pAttach.queryInterfaceTo(aNewAttachment);
12489
12490 return S_OK;
12491}
12492
12493// public methods only for internal purposes
12494/////////////////////////////////////////////////////////////////////////////
12495
12496/**
12497 * Called from the client watcher thread to check for expected or unexpected
12498 * death of the client process that has a direct session to this machine.
12499 *
12500 * On Win32 and on OS/2, this method is called only when we've got the
12501 * mutex (i.e. the client has either died or terminated normally) so it always
12502 * returns @c true (the client is terminated, the session machine is
12503 * uninitialized).
12504 *
12505 * On other platforms, the method returns @c true if the client process has
12506 * terminated normally or abnormally and the session machine was uninitialized,
12507 * and @c false if the client process is still alive.
12508 *
12509 * @note Locks this object for writing.
12510 */
12511bool SessionMachine::checkForDeath()
12512{
12513 Uninit::Reason reason;
12514 bool terminated = false;
12515
12516 /* Enclose autoCaller with a block because calling uninit() from under it
12517 * will deadlock. */
12518 {
12519 AutoCaller autoCaller(this);
12520 if (!autoCaller.isOk())
12521 {
12522 /* return true if not ready, to cause the client watcher to exclude
12523 * the corresponding session from watching */
12524 LogFlowThisFunc(("Already uninitialized!\n"));
12525 return true;
12526 }
12527
12528 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12529
12530 /* Determine the reason of death: if the session state is Closing here,
12531 * everything is fine. Otherwise it means that the client did not call
12532 * OnSessionEnd() before it released the IPC semaphore. This may happen
12533 * either because the client process has abnormally terminated, or
12534 * because it simply forgot to call ISession::Close() before exiting. We
12535 * threat the latter also as an abnormal termination (see
12536 * Session::uninit() for details). */
12537 reason = mData->mSession.mState == SessionState_Unlocking ?
12538 Uninit::Normal :
12539 Uninit::Abnormal;
12540
12541#if defined(RT_OS_WINDOWS)
12542
12543 AssertMsg(mIPCSem, ("semaphore must be created"));
12544
12545 /* release the IPC mutex */
12546 ::ReleaseMutex(mIPCSem);
12547
12548 terminated = true;
12549
12550#elif defined(RT_OS_OS2)
12551
12552 AssertMsg(mIPCSem, ("semaphore must be created"));
12553
12554 /* release the IPC mutex */
12555 ::DosReleaseMutexSem(mIPCSem);
12556
12557 terminated = true;
12558
12559#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12560
12561 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
12562
12563 int val = ::semctl(mIPCSem, 0, GETVAL);
12564 if (val > 0)
12565 {
12566 /* the semaphore is signaled, meaning the session is terminated */
12567 terminated = true;
12568 }
12569
12570#else
12571# error "Port me!"
12572#endif
12573
12574 } /* AutoCaller block */
12575
12576 if (terminated)
12577 uninit(reason);
12578
12579 return terminated;
12580}
12581
12582/**
12583 * @note Locks this object for reading.
12584 */
12585HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
12586{
12587 LogFlowThisFunc(("\n"));
12588
12589 AutoCaller autoCaller(this);
12590 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12591
12592 ComPtr<IInternalSessionControl> directControl;
12593 {
12594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12595 directControl = mData->mSession.mDirectControl;
12596 }
12597
12598 /* ignore notifications sent after #OnSessionEnd() is called */
12599 if (!directControl)
12600 return S_OK;
12601
12602 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
12603}
12604
12605/**
12606 * @note Locks this object for reading.
12607 */
12608HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
12609 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
12610{
12611 LogFlowThisFunc(("\n"));
12612
12613 AutoCaller autoCaller(this);
12614 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12615
12616 ComPtr<IInternalSessionControl> directControl;
12617 {
12618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12619 directControl = mData->mSession.mDirectControl;
12620 }
12621
12622 /* ignore notifications sent after #OnSessionEnd() is called */
12623 if (!directControl)
12624 return S_OK;
12625 /*
12626 * instead acting like callback we ask IVirtualBox deliver corresponding event
12627 */
12628
12629 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
12630 return S_OK;
12631}
12632
12633/**
12634 * @note Locks this object for reading.
12635 */
12636HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
12637{
12638 LogFlowThisFunc(("\n"));
12639
12640 AutoCaller autoCaller(this);
12641 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12642
12643 ComPtr<IInternalSessionControl> directControl;
12644 {
12645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12646 directControl = mData->mSession.mDirectControl;
12647 }
12648
12649 /* ignore notifications sent after #OnSessionEnd() is called */
12650 if (!directControl)
12651 return S_OK;
12652
12653 return directControl->OnSerialPortChange(serialPort);
12654}
12655
12656/**
12657 * @note Locks this object for reading.
12658 */
12659HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
12660{
12661 LogFlowThisFunc(("\n"));
12662
12663 AutoCaller autoCaller(this);
12664 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12665
12666 ComPtr<IInternalSessionControl> directControl;
12667 {
12668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12669 directControl = mData->mSession.mDirectControl;
12670 }
12671
12672 /* ignore notifications sent after #OnSessionEnd() is called */
12673 if (!directControl)
12674 return S_OK;
12675
12676 return directControl->OnParallelPortChange(parallelPort);
12677}
12678
12679/**
12680 * @note Locks this object for reading.
12681 */
12682HRESULT SessionMachine::onStorageControllerChange()
12683{
12684 LogFlowThisFunc(("\n"));
12685
12686 AutoCaller autoCaller(this);
12687 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12688
12689 ComPtr<IInternalSessionControl> directControl;
12690 {
12691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12692 directControl = mData->mSession.mDirectControl;
12693 }
12694
12695 /* ignore notifications sent after #OnSessionEnd() is called */
12696 if (!directControl)
12697 return S_OK;
12698
12699 return directControl->OnStorageControllerChange();
12700}
12701
12702/**
12703 * @note Locks this object for reading.
12704 */
12705HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
12706{
12707 LogFlowThisFunc(("\n"));
12708
12709 AutoCaller autoCaller(this);
12710 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12711
12712 ComPtr<IInternalSessionControl> directControl;
12713 {
12714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12715 directControl = mData->mSession.mDirectControl;
12716 }
12717
12718 /* ignore notifications sent after #OnSessionEnd() is called */
12719 if (!directControl)
12720 return S_OK;
12721
12722 return directControl->OnMediumChange(aAttachment, aForce);
12723}
12724
12725/**
12726 * @note Locks this object for reading.
12727 */
12728HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
12729{
12730 LogFlowThisFunc(("\n"));
12731
12732 AutoCaller autoCaller(this);
12733 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12734
12735 ComPtr<IInternalSessionControl> directControl;
12736 {
12737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12738 directControl = mData->mSession.mDirectControl;
12739 }
12740
12741 /* ignore notifications sent after #OnSessionEnd() is called */
12742 if (!directControl)
12743 return S_OK;
12744
12745 return directControl->OnCPUChange(aCPU, aRemove);
12746}
12747
12748HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
12749{
12750 LogFlowThisFunc(("\n"));
12751
12752 AutoCaller autoCaller(this);
12753 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12754
12755 ComPtr<IInternalSessionControl> directControl;
12756 {
12757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12758 directControl = mData->mSession.mDirectControl;
12759 }
12760
12761 /* ignore notifications sent after #OnSessionEnd() is called */
12762 if (!directControl)
12763 return S_OK;
12764
12765 return directControl->OnCPUExecutionCapChange(aExecutionCap);
12766}
12767
12768/**
12769 * @note Locks this object for reading.
12770 */
12771HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
12772{
12773 LogFlowThisFunc(("\n"));
12774
12775 AutoCaller autoCaller(this);
12776 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12777
12778 ComPtr<IInternalSessionControl> directControl;
12779 {
12780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12781 directControl = mData->mSession.mDirectControl;
12782 }
12783
12784 /* ignore notifications sent after #OnSessionEnd() is called */
12785 if (!directControl)
12786 return S_OK;
12787
12788 return directControl->OnVRDEServerChange(aRestart);
12789}
12790
12791/**
12792 * @note Locks this object for reading.
12793 */
12794HRESULT SessionMachine::onUSBControllerChange()
12795{
12796 LogFlowThisFunc(("\n"));
12797
12798 AutoCaller autoCaller(this);
12799 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12800
12801 ComPtr<IInternalSessionControl> directControl;
12802 {
12803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12804 directControl = mData->mSession.mDirectControl;
12805 }
12806
12807 /* ignore notifications sent after #OnSessionEnd() is called */
12808 if (!directControl)
12809 return S_OK;
12810
12811 return directControl->OnUSBControllerChange();
12812}
12813
12814/**
12815 * @note Locks this object for reading.
12816 */
12817HRESULT SessionMachine::onSharedFolderChange()
12818{
12819 LogFlowThisFunc(("\n"));
12820
12821 AutoCaller autoCaller(this);
12822 AssertComRCReturnRC(autoCaller.rc());
12823
12824 ComPtr<IInternalSessionControl> directControl;
12825 {
12826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12827 directControl = mData->mSession.mDirectControl;
12828 }
12829
12830 /* ignore notifications sent after #OnSessionEnd() is called */
12831 if (!directControl)
12832 return S_OK;
12833
12834 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
12835}
12836
12837/**
12838 * @note Locks this object for reading.
12839 */
12840HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
12841{
12842 LogFlowThisFunc(("\n"));
12843
12844 AutoCaller autoCaller(this);
12845 AssertComRCReturnRC(autoCaller.rc());
12846
12847 ComPtr<IInternalSessionControl> directControl;
12848 {
12849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12850 directControl = mData->mSession.mDirectControl;
12851 }
12852
12853 /* ignore notifications sent after #OnSessionEnd() is called */
12854 if (!directControl)
12855 return S_OK;
12856
12857 return directControl->OnClipboardModeChange(aClipboardMode);
12858}
12859
12860/**
12861 * @note Locks this object for reading.
12862 */
12863HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
12864{
12865 LogFlowThisFunc(("\n"));
12866
12867 AutoCaller autoCaller(this);
12868 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12869
12870 ComPtr<IInternalSessionControl> directControl;
12871 {
12872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12873 directControl = mData->mSession.mDirectControl;
12874 }
12875
12876 /* ignore notifications sent after #OnSessionEnd() is called */
12877 if (!directControl)
12878 return S_OK;
12879
12880 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
12881}
12882
12883/**
12884 * @note Locks this object for reading.
12885 */
12886HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
12887{
12888 LogFlowThisFunc(("\n"));
12889
12890 AutoCaller autoCaller(this);
12891 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12892
12893 ComPtr<IInternalSessionControl> directControl;
12894 {
12895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12896 directControl = mData->mSession.mDirectControl;
12897 }
12898
12899 /* ignore notifications sent after #OnSessionEnd() is called */
12900 if (!directControl)
12901 return S_OK;
12902
12903 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
12904}
12905
12906/**
12907 * Returns @c true if this machine's USB controller reports it has a matching
12908 * filter for the given USB device and @c false otherwise.
12909 *
12910 * @note locks this object for reading.
12911 */
12912bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
12913{
12914 AutoCaller autoCaller(this);
12915 /* silently return if not ready -- this method may be called after the
12916 * direct machine session has been called */
12917 if (!autoCaller.isOk())
12918 return false;
12919
12920#ifdef VBOX_WITH_USB
12921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12922
12923 switch (mData->mMachineState)
12924 {
12925 case MachineState_Starting:
12926 case MachineState_Restoring:
12927 case MachineState_TeleportingIn:
12928 case MachineState_Paused:
12929 case MachineState_Running:
12930 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
12931 * elsewhere... */
12932 alock.release();
12933 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
12934 default: break;
12935 }
12936#else
12937 NOREF(aDevice);
12938 NOREF(aMaskedIfs);
12939#endif
12940 return false;
12941}
12942
12943/**
12944 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
12945 */
12946HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
12947 IVirtualBoxErrorInfo *aError,
12948 ULONG aMaskedIfs)
12949{
12950 LogFlowThisFunc(("\n"));
12951
12952 AutoCaller autoCaller(this);
12953
12954 /* This notification may happen after the machine object has been
12955 * uninitialized (the session was closed), so don't assert. */
12956 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12957
12958 ComPtr<IInternalSessionControl> directControl;
12959 {
12960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12961 directControl = mData->mSession.mDirectControl;
12962 }
12963
12964 /* fail on notifications sent after #OnSessionEnd() is called, it is
12965 * expected by the caller */
12966 if (!directControl)
12967 return E_FAIL;
12968
12969 /* No locks should be held at this point. */
12970 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
12971 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
12972
12973 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
12974}
12975
12976/**
12977 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
12978 */
12979HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
12980 IVirtualBoxErrorInfo *aError)
12981{
12982 LogFlowThisFunc(("\n"));
12983
12984 AutoCaller autoCaller(this);
12985
12986 /* This notification may happen after the machine object has been
12987 * uninitialized (the session was closed), so don't assert. */
12988 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12989
12990 ComPtr<IInternalSessionControl> directControl;
12991 {
12992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12993 directControl = mData->mSession.mDirectControl;
12994 }
12995
12996 /* fail on notifications sent after #OnSessionEnd() is called, it is
12997 * expected by the caller */
12998 if (!directControl)
12999 return E_FAIL;
13000
13001 /* No locks should be held at this point. */
13002 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13003 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13004
13005 return directControl->OnUSBDeviceDetach(aId, aError);
13006}
13007
13008// protected methods
13009/////////////////////////////////////////////////////////////////////////////
13010
13011/**
13012 * Helper method to finalize saving the state.
13013 *
13014 * @note Must be called from under this object's lock.
13015 *
13016 * @param aRc S_OK if the snapshot has been taken successfully
13017 * @param aErrMsg human readable error message for failure
13018 *
13019 * @note Locks mParent + this objects for writing.
13020 */
13021HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13022{
13023 LogFlowThisFuncEnter();
13024
13025 AutoCaller autoCaller(this);
13026 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13027
13028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13029
13030 HRESULT rc = S_OK;
13031
13032 if (SUCCEEDED(aRc))
13033 {
13034 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13035
13036 /* save all VM settings */
13037 rc = saveSettings(NULL);
13038 // no need to check whether VirtualBox.xml needs saving also since
13039 // we can't have a name change pending at this point
13040 }
13041 else
13042 {
13043 // delete the saved state file (it might have been already created);
13044 // we need not check whether this is shared with a snapshot here because
13045 // we certainly created this saved state file here anew
13046 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13047 }
13048
13049 /* notify the progress object about operation completion */
13050 Assert(mConsoleTaskData.mProgress);
13051 if (SUCCEEDED(aRc))
13052 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13053 else
13054 {
13055 if (aErrMsg.length())
13056 mConsoleTaskData.mProgress->notifyComplete(aRc,
13057 COM_IIDOF(ISession),
13058 getComponentName(),
13059 aErrMsg.c_str());
13060 else
13061 mConsoleTaskData.mProgress->notifyComplete(aRc);
13062 }
13063
13064 /* clear out the temporary saved state data */
13065 mConsoleTaskData.mLastState = MachineState_Null;
13066 mConsoleTaskData.strStateFilePath.setNull();
13067 mConsoleTaskData.mProgress.setNull();
13068
13069 LogFlowThisFuncLeave();
13070 return rc;
13071}
13072
13073/**
13074 * Deletes the given file if it is no longer in use by either the current machine state
13075 * (if the machine is "saved") or any of the machine's snapshots.
13076 *
13077 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13078 * but is different for each SnapshotMachine. When calling this, the order of calling this
13079 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13080 * is therefore critical. I know, it's all rather messy.
13081 *
13082 * @param strStateFile
13083 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13084 */
13085void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13086 Snapshot *pSnapshotToIgnore)
13087{
13088 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13089 if ( (strStateFile.isNotEmpty())
13090 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13091 )
13092 // ... and it must also not be shared with other snapshots
13093 if ( !mData->mFirstSnapshot
13094 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13095 // this checks the SnapshotMachine's state file paths
13096 )
13097 RTFileDelete(strStateFile.c_str());
13098}
13099
13100/**
13101 * Locks the attached media.
13102 *
13103 * All attached hard disks are locked for writing and DVD/floppy are locked for
13104 * reading. Parents of attached hard disks (if any) are locked for reading.
13105 *
13106 * This method also performs accessibility check of all media it locks: if some
13107 * media is inaccessible, the method will return a failure and a bunch of
13108 * extended error info objects per each inaccessible medium.
13109 *
13110 * Note that this method is atomic: if it returns a success, all media are
13111 * locked as described above; on failure no media is locked at all (all
13112 * succeeded individual locks will be undone).
13113 *
13114 * This method is intended to be called when the machine is in Starting or
13115 * Restoring state and asserts otherwise.
13116 *
13117 * The locks made by this method must be undone by calling #unlockMedia() when
13118 * no more needed.
13119 */
13120HRESULT SessionMachine::lockMedia()
13121{
13122 AutoCaller autoCaller(this);
13123 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13124
13125 AutoMultiWriteLock2 alock(this->lockHandle(),
13126 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13127
13128 AssertReturn( mData->mMachineState == MachineState_Starting
13129 || mData->mMachineState == MachineState_Restoring
13130 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13131 /* bail out if trying to lock things with already set up locking */
13132 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13133
13134 clearError();
13135 MultiResult mrc(S_OK);
13136
13137 /* Collect locking information for all medium objects attached to the VM. */
13138 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13139 it != mMediaData->mAttachments.end();
13140 ++it)
13141 {
13142 MediumAttachment* pAtt = *it;
13143 DeviceType_T devType = pAtt->getType();
13144 Medium *pMedium = pAtt->getMedium();
13145
13146 MediumLockList *pMediumLockList(new MediumLockList());
13147 // There can be attachments without a medium (floppy/dvd), and thus
13148 // it's impossible to create a medium lock list. It still makes sense
13149 // to have the empty medium lock list in the map in case a medium is
13150 // attached later.
13151 if (pMedium != NULL)
13152 {
13153 MediumType_T mediumType = pMedium->getType();
13154 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13155 || mediumType == MediumType_Shareable;
13156 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13157
13158 alock.release();
13159 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13160 !fIsReadOnlyLock /* fMediumLockWrite */,
13161 NULL,
13162 *pMediumLockList);
13163 alock.acquire();
13164 if (FAILED(mrc))
13165 {
13166 delete pMediumLockList;
13167 mData->mSession.mLockedMedia.Clear();
13168 break;
13169 }
13170 }
13171
13172 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13173 if (FAILED(rc))
13174 {
13175 mData->mSession.mLockedMedia.Clear();
13176 mrc = setError(rc,
13177 tr("Collecting locking information for all attached media failed"));
13178 break;
13179 }
13180 }
13181
13182 if (SUCCEEDED(mrc))
13183 {
13184 /* Now lock all media. If this fails, nothing is locked. */
13185 alock.release();
13186 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13187 alock.acquire();
13188 if (FAILED(rc))
13189 {
13190 mrc = setError(rc,
13191 tr("Locking of attached media failed"));
13192 }
13193 }
13194
13195 return mrc;
13196}
13197
13198/**
13199 * Undoes the locks made by by #lockMedia().
13200 */
13201void SessionMachine::unlockMedia()
13202{
13203 AutoCaller autoCaller(this);
13204 AssertComRCReturnVoid(autoCaller.rc());
13205
13206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13207
13208 /* we may be holding important error info on the current thread;
13209 * preserve it */
13210 ErrorInfoKeeper eik;
13211
13212 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13213 AssertComRC(rc);
13214}
13215
13216/**
13217 * Helper to change the machine state (reimplementation).
13218 *
13219 * @note Locks this object for writing.
13220 * @note This method must not call saveSettings or SaveSettings, otherwise
13221 * it can cause crashes in random places due to unexpectedly committing
13222 * the current settings. The caller is responsible for that. The call
13223 * to saveStateSettings is fine, because this method does not commit.
13224 */
13225HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13226{
13227 LogFlowThisFuncEnter();
13228 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13229
13230 AutoCaller autoCaller(this);
13231 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13232
13233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13234
13235 MachineState_T oldMachineState = mData->mMachineState;
13236
13237 AssertMsgReturn(oldMachineState != aMachineState,
13238 ("oldMachineState=%s, aMachineState=%s\n",
13239 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13240 E_FAIL);
13241
13242 HRESULT rc = S_OK;
13243
13244 int stsFlags = 0;
13245 bool deleteSavedState = false;
13246
13247 /* detect some state transitions */
13248
13249 if ( ( oldMachineState == MachineState_Saved
13250 && aMachineState == MachineState_Restoring)
13251 || ( ( oldMachineState == MachineState_PoweredOff
13252 || oldMachineState == MachineState_Teleported
13253 || oldMachineState == MachineState_Aborted
13254 )
13255 && ( aMachineState == MachineState_TeleportingIn
13256 || aMachineState == MachineState_Starting
13257 )
13258 )
13259 )
13260 {
13261 /* The EMT thread is about to start */
13262
13263 /* Nothing to do here for now... */
13264
13265 /// @todo NEWMEDIA don't let mDVDDrive and other children
13266 /// change anything when in the Starting/Restoring state
13267 }
13268 else if ( ( oldMachineState == MachineState_Running
13269 || oldMachineState == MachineState_Paused
13270 || oldMachineState == MachineState_Teleporting
13271 || oldMachineState == MachineState_LiveSnapshotting
13272 || oldMachineState == MachineState_Stuck
13273 || oldMachineState == MachineState_Starting
13274 || oldMachineState == MachineState_Stopping
13275 || oldMachineState == MachineState_Saving
13276 || oldMachineState == MachineState_Restoring
13277 || oldMachineState == MachineState_TeleportingPausedVM
13278 || oldMachineState == MachineState_TeleportingIn
13279 )
13280 && ( aMachineState == MachineState_PoweredOff
13281 || aMachineState == MachineState_Saved
13282 || aMachineState == MachineState_Teleported
13283 || aMachineState == MachineState_Aborted
13284 )
13285 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13286 * snapshot */
13287 && ( mConsoleTaskData.mSnapshot.isNull()
13288 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13289 )
13290 )
13291 {
13292 /* The EMT thread has just stopped, unlock attached media. Note that as
13293 * opposed to locking that is done from Console, we do unlocking here
13294 * because the VM process may have aborted before having a chance to
13295 * properly unlock all media it locked. */
13296
13297 unlockMedia();
13298 }
13299
13300 if (oldMachineState == MachineState_Restoring)
13301 {
13302 if (aMachineState != MachineState_Saved)
13303 {
13304 /*
13305 * delete the saved state file once the machine has finished
13306 * restoring from it (note that Console sets the state from
13307 * Restoring to Saved if the VM couldn't restore successfully,
13308 * to give the user an ability to fix an error and retry --
13309 * we keep the saved state file in this case)
13310 */
13311 deleteSavedState = true;
13312 }
13313 }
13314 else if ( oldMachineState == MachineState_Saved
13315 && ( aMachineState == MachineState_PoweredOff
13316 || aMachineState == MachineState_Aborted
13317 || aMachineState == MachineState_Teleported
13318 )
13319 )
13320 {
13321 /*
13322 * delete the saved state after Console::ForgetSavedState() is called
13323 * or if the VM process (owning a direct VM session) crashed while the
13324 * VM was Saved
13325 */
13326
13327 /// @todo (dmik)
13328 // Not sure that deleting the saved state file just because of the
13329 // client death before it attempted to restore the VM is a good
13330 // thing. But when it crashes we need to go to the Aborted state
13331 // which cannot have the saved state file associated... The only
13332 // way to fix this is to make the Aborted condition not a VM state
13333 // but a bool flag: i.e., when a crash occurs, set it to true and
13334 // change the state to PoweredOff or Saved depending on the
13335 // saved state presence.
13336
13337 deleteSavedState = true;
13338 mData->mCurrentStateModified = TRUE;
13339 stsFlags |= SaveSTS_CurStateModified;
13340 }
13341
13342 if ( aMachineState == MachineState_Starting
13343 || aMachineState == MachineState_Restoring
13344 || aMachineState == MachineState_TeleportingIn
13345 )
13346 {
13347 /* set the current state modified flag to indicate that the current
13348 * state is no more identical to the state in the
13349 * current snapshot */
13350 if (!mData->mCurrentSnapshot.isNull())
13351 {
13352 mData->mCurrentStateModified = TRUE;
13353 stsFlags |= SaveSTS_CurStateModified;
13354 }
13355 }
13356
13357 if (deleteSavedState)
13358 {
13359 if (mRemoveSavedState)
13360 {
13361 Assert(!mSSData->strStateFilePath.isEmpty());
13362
13363 // it is safe to delete the saved state file if ...
13364 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13365 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13366 // ... none of the snapshots share the saved state file
13367 )
13368 RTFileDelete(mSSData->strStateFilePath.c_str());
13369 }
13370
13371 mSSData->strStateFilePath.setNull();
13372 stsFlags |= SaveSTS_StateFilePath;
13373 }
13374
13375 /* redirect to the underlying peer machine */
13376 mPeer->setMachineState(aMachineState);
13377
13378 if ( aMachineState == MachineState_PoweredOff
13379 || aMachineState == MachineState_Teleported
13380 || aMachineState == MachineState_Aborted
13381 || aMachineState == MachineState_Saved)
13382 {
13383 /* the machine has stopped execution
13384 * (or the saved state file was adopted) */
13385 stsFlags |= SaveSTS_StateTimeStamp;
13386 }
13387
13388 if ( ( oldMachineState == MachineState_PoweredOff
13389 || oldMachineState == MachineState_Aborted
13390 || oldMachineState == MachineState_Teleported
13391 )
13392 && aMachineState == MachineState_Saved)
13393 {
13394 /* the saved state file was adopted */
13395 Assert(!mSSData->strStateFilePath.isEmpty());
13396 stsFlags |= SaveSTS_StateFilePath;
13397 }
13398
13399#ifdef VBOX_WITH_GUEST_PROPS
13400 if ( aMachineState == MachineState_PoweredOff
13401 || aMachineState == MachineState_Aborted
13402 || aMachineState == MachineState_Teleported)
13403 {
13404 /* Make sure any transient guest properties get removed from the
13405 * property store on shutdown. */
13406
13407 HWData::GuestPropertyList::iterator it;
13408 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13409 if (!fNeedsSaving)
13410 for (it = mHWData->mGuestProperties.begin();
13411 it != mHWData->mGuestProperties.end(); ++it)
13412 if ( (it->mFlags & guestProp::TRANSIENT)
13413 || (it->mFlags & guestProp::TRANSRESET))
13414 {
13415 fNeedsSaving = true;
13416 break;
13417 }
13418 if (fNeedsSaving)
13419 {
13420 mData->mCurrentStateModified = TRUE;
13421 stsFlags |= SaveSTS_CurStateModified;
13422 }
13423 }
13424#endif
13425
13426 rc = saveStateSettings(stsFlags);
13427
13428 if ( ( oldMachineState != MachineState_PoweredOff
13429 && oldMachineState != MachineState_Aborted
13430 && oldMachineState != MachineState_Teleported
13431 )
13432 && ( aMachineState == MachineState_PoweredOff
13433 || aMachineState == MachineState_Aborted
13434 || aMachineState == MachineState_Teleported
13435 )
13436 )
13437 {
13438 /* we've been shut down for any reason */
13439 /* no special action so far */
13440 }
13441
13442 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13443 LogFlowThisFuncLeave();
13444 return rc;
13445}
13446
13447/**
13448 * Sends the current machine state value to the VM process.
13449 *
13450 * @note Locks this object for reading, then calls a client process.
13451 */
13452HRESULT SessionMachine::updateMachineStateOnClient()
13453{
13454 AutoCaller autoCaller(this);
13455 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13456
13457 ComPtr<IInternalSessionControl> directControl;
13458 {
13459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13460 AssertReturn(!!mData, E_FAIL);
13461 directControl = mData->mSession.mDirectControl;
13462
13463 /* directControl may be already set to NULL here in #OnSessionEnd()
13464 * called too early by the direct session process while there is still
13465 * some operation (like deleting the snapshot) in progress. The client
13466 * process in this case is waiting inside Session::close() for the
13467 * "end session" process object to complete, while #uninit() called by
13468 * #checkForDeath() on the Watcher thread is waiting for the pending
13469 * operation to complete. For now, we accept this inconsistent behavior
13470 * and simply do nothing here. */
13471
13472 if (mData->mSession.mState == SessionState_Unlocking)
13473 return S_OK;
13474
13475 AssertReturn(!directControl.isNull(), E_FAIL);
13476 }
13477
13478 return directControl->UpdateMachineState(mData->mMachineState);
13479}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette