VirtualBox

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

Last change on this file since 42178 was 42178, checked in by vboxsync, 12 years ago

Autostart: Make the path to the autostart database configurable

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 451.9 KB
Line 
1/* $Id: MachineImpl.cpp 42178 2012-07-17 12:35:07Z 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.virtualbox.org. 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 if (vrc == VERR_PATH_NOT_FOUND)
6643 hrc = setError(E_FAIL,
6644 tr("The path to the autostart database is not set"));
6645 else
6646 hrc = setError(E_UNEXPECTED,
6647 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6648 fEnabled ? "Adding" : "Removing",
6649 mUserData->s.strName.c_str(), vrc);
6650 }
6651 }
6652 return hrc;
6653}
6654
6655STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6656{
6657 CheckComArgOutPointerValid(puDelay);
6658 AutoCaller autoCaller(this);
6659 HRESULT hrc = autoCaller.rc();
6660 if (SUCCEEDED(hrc))
6661 {
6662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6663 *puDelay = mHWData->mAutostart.uAutostartDelay;
6664 }
6665 return hrc;
6666}
6667
6668STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6669{
6670 AutoCaller autoCaller(this);
6671 HRESULT hrc = autoCaller.rc();
6672 if (SUCCEEDED(hrc))
6673 {
6674 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6675 hrc = checkStateDependency(MutableStateDep);
6676 if (SUCCEEDED(hrc))
6677 {
6678 hrc = mHWData.backupEx();
6679 if (SUCCEEDED(hrc))
6680 {
6681 setModified(IsModified_MachineData);
6682 mHWData->mAutostart.uAutostartDelay = uDelay;
6683 }
6684 }
6685 }
6686 return hrc;
6687}
6688
6689STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6690{
6691 CheckComArgOutPointerValid(penmAutostopType);
6692 AutoCaller autoCaller(this);
6693 HRESULT hrc = autoCaller.rc();
6694 if (SUCCEEDED(hrc))
6695 {
6696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6697 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6698 }
6699 return hrc;
6700}
6701
6702STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6703{
6704 AutoCaller autoCaller(this);
6705 HRESULT hrc = autoCaller.rc();
6706 if (SUCCEEDED(hrc))
6707 {
6708 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6709 hrc = checkStateDependency(MutableStateDep);
6710 if ( SUCCEEDED(hrc)
6711 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6712 {
6713 AutostartDb *autostartDb = mParent->getAutostartDb();
6714 int vrc;
6715
6716 if (enmAutostopType != AutostopType_Disabled)
6717 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6718 else
6719 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6720
6721 if (RT_SUCCESS(vrc))
6722 {
6723 hrc = mHWData.backupEx();
6724 if (SUCCEEDED(hrc))
6725 {
6726 setModified(IsModified_MachineData);
6727 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6728 }
6729 }
6730 else if (vrc == VERR_NOT_SUPPORTED)
6731 hrc = setError(VBOX_E_NOT_SUPPORTED,
6732 tr("The VM autostop feature is not supported on this platform"));
6733 else if (vrc == VERR_PATH_NOT_FOUND)
6734 hrc = setError(E_FAIL,
6735 tr("The path to the autostart database is not set"));
6736 else
6737 hrc = setError(E_UNEXPECTED,
6738 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6739 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6740 mUserData->s.strName.c_str(), vrc);
6741 }
6742 }
6743 return hrc;
6744}
6745
6746
6747STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6748{
6749 LogFlowFuncEnter();
6750
6751 CheckComArgNotNull(pTarget);
6752 CheckComArgOutPointerValid(pProgress);
6753
6754 /* Convert the options. */
6755 RTCList<CloneOptions_T> optList;
6756 if (options != NULL)
6757 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6758
6759 if (optList.contains(CloneOptions_Link))
6760 {
6761 if (!isSnapshotMachine())
6762 return setError(E_INVALIDARG,
6763 tr("Linked clone can only be created from a snapshot"));
6764 if (mode != CloneMode_MachineState)
6765 return setError(E_INVALIDARG,
6766 tr("Linked clone can only be created for a single machine state"));
6767 }
6768 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6769
6770 AutoCaller autoCaller(this);
6771 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6772
6773
6774 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6775
6776 HRESULT rc = pWorker->start(pProgress);
6777
6778 LogFlowFuncLeave();
6779
6780 return rc;
6781}
6782
6783// public methods for internal purposes
6784/////////////////////////////////////////////////////////////////////////////
6785
6786/**
6787 * Adds the given IsModified_* flag to the dirty flags of the machine.
6788 * This must be called either during loadSettings or under the machine write lock.
6789 * @param fl
6790 */
6791void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6792{
6793 mData->flModifications |= fl;
6794 if (fAllowStateModification && isStateModificationAllowed())
6795 mData->mCurrentStateModified = true;
6796}
6797
6798/**
6799 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6800 * care of the write locking.
6801 *
6802 * @param fModifications The flag to add.
6803 */
6804void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6805{
6806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6807 setModified(fModification, fAllowStateModification);
6808}
6809
6810/**
6811 * Saves the registry entry of this machine to the given configuration node.
6812 *
6813 * @param aEntryNode Node to save the registry entry to.
6814 *
6815 * @note locks this object for reading.
6816 */
6817HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
6818{
6819 AutoLimitedCaller autoCaller(this);
6820 AssertComRCReturnRC(autoCaller.rc());
6821
6822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6823
6824 data.uuid = mData->mUuid;
6825 data.strSettingsFile = mData->m_strConfigFile;
6826
6827 return S_OK;
6828}
6829
6830/**
6831 * Calculates the absolute path of the given path taking the directory of the
6832 * machine settings file as the current directory.
6833 *
6834 * @param aPath Path to calculate the absolute path for.
6835 * @param aResult Where to put the result (used only on success, can be the
6836 * same Utf8Str instance as passed in @a aPath).
6837 * @return IPRT result.
6838 *
6839 * @note Locks this object for reading.
6840 */
6841int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
6842{
6843 AutoCaller autoCaller(this);
6844 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
6845
6846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6847
6848 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
6849
6850 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
6851
6852 strSettingsDir.stripFilename();
6853 char folder[RTPATH_MAX];
6854 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
6855 if (RT_SUCCESS(vrc))
6856 aResult = folder;
6857
6858 return vrc;
6859}
6860
6861/**
6862 * Copies strSource to strTarget, making it relative to the machine folder
6863 * if it is a subdirectory thereof, or simply copying it otherwise.
6864 *
6865 * @param strSource Path to evaluate and copy.
6866 * @param strTarget Buffer to receive target path.
6867 *
6868 * @note Locks this object for reading.
6869 */
6870void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
6871 Utf8Str &strTarget)
6872{
6873 AutoCaller autoCaller(this);
6874 AssertComRCReturn(autoCaller.rc(), (void)0);
6875
6876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6877
6878 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
6879 // use strTarget as a temporary buffer to hold the machine settings dir
6880 strTarget = mData->m_strConfigFileFull;
6881 strTarget.stripFilename();
6882 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
6883 {
6884 // is relative: then append what's left
6885 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
6886 // for empty paths (only possible for subdirs) use "." to avoid
6887 // triggering default settings for not present config attributes.
6888 if (strTarget.isEmpty())
6889 strTarget = ".";
6890 }
6891 else
6892 // is not relative: then overwrite
6893 strTarget = strSource;
6894}
6895
6896/**
6897 * Returns the full path to the machine's log folder in the
6898 * \a aLogFolder argument.
6899 */
6900void Machine::getLogFolder(Utf8Str &aLogFolder)
6901{
6902 AutoCaller autoCaller(this);
6903 AssertComRCReturnVoid(autoCaller.rc());
6904
6905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6906
6907 char szTmp[RTPATH_MAX];
6908 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
6909 if (RT_SUCCESS(vrc))
6910 {
6911 if (szTmp[0] && !mUserData.isNull())
6912 {
6913 char szTmp2[RTPATH_MAX];
6914 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
6915 if (RT_SUCCESS(vrc))
6916 aLogFolder = BstrFmt("%s%c%s",
6917 szTmp2,
6918 RTPATH_DELIMITER,
6919 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
6920 }
6921 else
6922 vrc = VERR_PATH_IS_RELATIVE;
6923 }
6924
6925 if (RT_FAILURE(vrc))
6926 {
6927 // fallback if VBOX_USER_LOGHOME is not set or invalid
6928 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
6929 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
6930 aLogFolder.append(RTPATH_DELIMITER);
6931 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
6932 }
6933}
6934
6935/**
6936 * Returns the full path to the machine's log file for an given index.
6937 */
6938Utf8Str Machine::queryLogFilename(ULONG idx)
6939{
6940 Utf8Str logFolder;
6941 getLogFolder(logFolder);
6942 Assert(logFolder.length());
6943 Utf8Str log;
6944 if (idx == 0)
6945 log = Utf8StrFmt("%s%cVBox.log",
6946 logFolder.c_str(), RTPATH_DELIMITER);
6947 else
6948 log = Utf8StrFmt("%s%cVBox.log.%d",
6949 logFolder.c_str(), RTPATH_DELIMITER, idx);
6950 return log;
6951}
6952
6953/**
6954 * Composes a unique saved state filename based on the current system time. The filename is
6955 * granular to the second so this will work so long as no more than one snapshot is taken on
6956 * a machine per second.
6957 *
6958 * Before version 4.1, we used this formula for saved state files:
6959 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
6960 * which no longer works because saved state files can now be shared between the saved state of the
6961 * "saved" machine and an online snapshot, and the following would cause problems:
6962 * 1) save machine
6963 * 2) create online snapshot from that machine state --> reusing saved state file
6964 * 3) save machine again --> filename would be reused, breaking the online snapshot
6965 *
6966 * So instead we now use a timestamp.
6967 *
6968 * @param str
6969 */
6970void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
6971{
6972 AutoCaller autoCaller(this);
6973 AssertComRCReturnVoid(autoCaller.rc());
6974
6975 {
6976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6977 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
6978 }
6979
6980 RTTIMESPEC ts;
6981 RTTimeNow(&ts);
6982 RTTIME time;
6983 RTTimeExplode(&time, &ts);
6984
6985 strStateFilePath += RTPATH_DELIMITER;
6986 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
6987 time.i32Year, time.u8Month, time.u8MonthDay,
6988 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
6989}
6990
6991/**
6992 * @note Locks this object for writing, calls the client process
6993 * (inside the lock).
6994 */
6995HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
6996 const Utf8Str &strType,
6997 const Utf8Str &strEnvironment,
6998 ProgressProxy *aProgress)
6999{
7000 LogFlowThisFuncEnter();
7001
7002 AssertReturn(aControl, E_FAIL);
7003 AssertReturn(aProgress, E_FAIL);
7004
7005 AutoCaller autoCaller(this);
7006 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7007
7008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7009
7010 if (!mData->mRegistered)
7011 return setError(E_UNEXPECTED,
7012 tr("The machine '%s' is not registered"),
7013 mUserData->s.strName.c_str());
7014
7015 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7016
7017 if ( mData->mSession.mState == SessionState_Locked
7018 || mData->mSession.mState == SessionState_Spawning
7019 || mData->mSession.mState == SessionState_Unlocking)
7020 return setError(VBOX_E_INVALID_OBJECT_STATE,
7021 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7022 mUserData->s.strName.c_str());
7023
7024 /* may not be busy */
7025 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7026
7027 /* get the path to the executable */
7028 char szPath[RTPATH_MAX];
7029 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7030 size_t sz = strlen(szPath);
7031 szPath[sz++] = RTPATH_DELIMITER;
7032 szPath[sz] = 0;
7033 char *cmd = szPath + sz;
7034 sz = RTPATH_MAX - sz;
7035
7036 int vrc = VINF_SUCCESS;
7037 RTPROCESS pid = NIL_RTPROCESS;
7038
7039 RTENV env = RTENV_DEFAULT;
7040
7041 if (!strEnvironment.isEmpty())
7042 {
7043 char *newEnvStr = NULL;
7044
7045 do
7046 {
7047 /* clone the current environment */
7048 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7049 AssertRCBreakStmt(vrc2, vrc = vrc2);
7050
7051 newEnvStr = RTStrDup(strEnvironment.c_str());
7052 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7053
7054 /* put new variables to the environment
7055 * (ignore empty variable names here since RTEnv API
7056 * intentionally doesn't do that) */
7057 char *var = newEnvStr;
7058 for (char *p = newEnvStr; *p; ++p)
7059 {
7060 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7061 {
7062 *p = '\0';
7063 if (*var)
7064 {
7065 char *val = strchr(var, '=');
7066 if (val)
7067 {
7068 *val++ = '\0';
7069 vrc2 = RTEnvSetEx(env, var, val);
7070 }
7071 else
7072 vrc2 = RTEnvUnsetEx(env, var);
7073 if (RT_FAILURE(vrc2))
7074 break;
7075 }
7076 var = p + 1;
7077 }
7078 }
7079 if (RT_SUCCESS(vrc2) && *var)
7080 vrc2 = RTEnvPutEx(env, var);
7081
7082 AssertRCBreakStmt(vrc2, vrc = vrc2);
7083 }
7084 while (0);
7085
7086 if (newEnvStr != NULL)
7087 RTStrFree(newEnvStr);
7088 }
7089
7090 /* Qt is default */
7091#ifdef VBOX_WITH_QTGUI
7092 if (strType == "gui" || strType == "GUI/Qt")
7093 {
7094# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7095 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7096# else
7097 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7098# endif
7099 Assert(sz >= sizeof(VirtualBox_exe));
7100 strcpy(cmd, VirtualBox_exe);
7101
7102 Utf8Str idStr = mData->mUuid.toString();
7103 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7104 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7105 }
7106#else /* !VBOX_WITH_QTGUI */
7107 if (0)
7108 ;
7109#endif /* VBOX_WITH_QTGUI */
7110
7111 else
7112
7113#ifdef VBOX_WITH_VBOXSDL
7114 if (strType == "sdl" || strType == "GUI/SDL")
7115 {
7116 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7117 Assert(sz >= sizeof(VBoxSDL_exe));
7118 strcpy(cmd, VBoxSDL_exe);
7119
7120 Utf8Str idStr = mData->mUuid.toString();
7121 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7122 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7123 }
7124#else /* !VBOX_WITH_VBOXSDL */
7125 if (0)
7126 ;
7127#endif /* !VBOX_WITH_VBOXSDL */
7128
7129 else
7130
7131#ifdef VBOX_WITH_HEADLESS
7132 if ( strType == "headless"
7133 || strType == "capture"
7134 || strType == "vrdp" /* Deprecated. Same as headless. */
7135 )
7136 {
7137 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7138 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7139 * and a VM works even if the server has not been installed.
7140 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7141 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7142 * differently in 4.0 and 3.x.
7143 */
7144 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7145 Assert(sz >= sizeof(VBoxHeadless_exe));
7146 strcpy(cmd, VBoxHeadless_exe);
7147
7148 Utf8Str idStr = mData->mUuid.toString();
7149 /* Leave space for "--capture" arg. */
7150 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7151 "--startvm", idStr.c_str(),
7152 "--vrde", "config",
7153 0, /* For "--capture". */
7154 0 };
7155 if (strType == "capture")
7156 {
7157 unsigned pos = RT_ELEMENTS(args) - 2;
7158 args[pos] = "--capture";
7159 }
7160 vrc = RTProcCreate(szPath, args, env,
7161#ifdef RT_OS_WINDOWS
7162 RTPROC_FLAGS_NO_WINDOW
7163#else
7164 0
7165#endif
7166 , &pid);
7167 }
7168#else /* !VBOX_WITH_HEADLESS */
7169 if (0)
7170 ;
7171#endif /* !VBOX_WITH_HEADLESS */
7172 else
7173 {
7174 RTEnvDestroy(env);
7175 return setError(E_INVALIDARG,
7176 tr("Invalid session type: '%s'"),
7177 strType.c_str());
7178 }
7179
7180 RTEnvDestroy(env);
7181
7182 if (RT_FAILURE(vrc))
7183 return setError(VBOX_E_IPRT_ERROR,
7184 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7185 mUserData->s.strName.c_str(), vrc);
7186
7187 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7188
7189 /*
7190 * Note that we don't release the lock here before calling the client,
7191 * because it doesn't need to call us back if called with a NULL argument.
7192 * Releasing the lock here is dangerous because we didn't prepare the
7193 * launch data yet, but the client we've just started may happen to be
7194 * too fast and call openSession() that will fail (because of PID, etc.),
7195 * so that the Machine will never get out of the Spawning session state.
7196 */
7197
7198 /* inform the session that it will be a remote one */
7199 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7200 HRESULT rc = aControl->AssignMachine(NULL);
7201 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7202
7203 if (FAILED(rc))
7204 {
7205 /* restore the session state */
7206 mData->mSession.mState = SessionState_Unlocked;
7207 /* The failure may occur w/o any error info (from RPC), so provide one */
7208 return setError(VBOX_E_VM_ERROR,
7209 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7210 }
7211
7212 /* attach launch data to the machine */
7213 Assert(mData->mSession.mPid == NIL_RTPROCESS);
7214 mData->mSession.mRemoteControls.push_back(aControl);
7215 mData->mSession.mProgress = aProgress;
7216 mData->mSession.mPid = pid;
7217 mData->mSession.mState = SessionState_Spawning;
7218 mData->mSession.mType = strType;
7219
7220 LogFlowThisFuncLeave();
7221 return S_OK;
7222}
7223
7224/**
7225 * Returns @c true if the given machine has an open direct session and returns
7226 * the session machine instance and additional session data (on some platforms)
7227 * if so.
7228 *
7229 * Note that when the method returns @c false, the arguments remain unchanged.
7230 *
7231 * @param aMachine Session machine object.
7232 * @param aControl Direct session control object (optional).
7233 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7234 *
7235 * @note locks this object for reading.
7236 */
7237#if defined(RT_OS_WINDOWS)
7238bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7239 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7240 HANDLE *aIPCSem /*= NULL*/,
7241 bool aAllowClosing /*= false*/)
7242#elif defined(RT_OS_OS2)
7243bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7244 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7245 HMTX *aIPCSem /*= NULL*/,
7246 bool aAllowClosing /*= false*/)
7247#else
7248bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7249 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7250 bool aAllowClosing /*= false*/)
7251#endif
7252{
7253 AutoLimitedCaller autoCaller(this);
7254 AssertComRCReturn(autoCaller.rc(), false);
7255
7256 /* just return false for inaccessible machines */
7257 if (autoCaller.state() != Ready)
7258 return false;
7259
7260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7261
7262 if ( mData->mSession.mState == SessionState_Locked
7263 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7264 )
7265 {
7266 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7267
7268 aMachine = mData->mSession.mMachine;
7269
7270 if (aControl != NULL)
7271 *aControl = mData->mSession.mDirectControl;
7272
7273#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7274 /* Additional session data */
7275 if (aIPCSem != NULL)
7276 *aIPCSem = aMachine->mIPCSem;
7277#endif
7278 return true;
7279 }
7280
7281 return false;
7282}
7283
7284/**
7285 * Returns @c true if the given machine has an spawning direct session and
7286 * returns and additional session data (on some platforms) if so.
7287 *
7288 * Note that when the method returns @c false, the arguments remain unchanged.
7289 *
7290 * @param aPID PID of the spawned direct session process.
7291 *
7292 * @note locks this object for reading.
7293 */
7294#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7295bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7296#else
7297bool Machine::isSessionSpawning()
7298#endif
7299{
7300 AutoLimitedCaller autoCaller(this);
7301 AssertComRCReturn(autoCaller.rc(), false);
7302
7303 /* just return false for inaccessible machines */
7304 if (autoCaller.state() != Ready)
7305 return false;
7306
7307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7308
7309 if (mData->mSession.mState == SessionState_Spawning)
7310 {
7311#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7312 /* Additional session data */
7313 if (aPID != NULL)
7314 {
7315 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false);
7316 *aPID = mData->mSession.mPid;
7317 }
7318#endif
7319 return true;
7320 }
7321
7322 return false;
7323}
7324
7325/**
7326 * Called from the client watcher thread to check for unexpected client process
7327 * death during Session_Spawning state (e.g. before it successfully opened a
7328 * direct session).
7329 *
7330 * On Win32 and on OS/2, this method is called only when we've got the
7331 * direct client's process termination notification, so it always returns @c
7332 * true.
7333 *
7334 * On other platforms, this method returns @c true if the client process is
7335 * terminated and @c false if it's still alive.
7336 *
7337 * @note Locks this object for writing.
7338 */
7339bool Machine::checkForSpawnFailure()
7340{
7341 AutoCaller autoCaller(this);
7342 if (!autoCaller.isOk())
7343 {
7344 /* nothing to do */
7345 LogFlowThisFunc(("Already uninitialized!\n"));
7346 return true;
7347 }
7348
7349 /* VirtualBox::addProcessToReap() needs a write lock */
7350 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7351
7352 if (mData->mSession.mState != SessionState_Spawning)
7353 {
7354 /* nothing to do */
7355 LogFlowThisFunc(("Not spawning any more!\n"));
7356 return true;
7357 }
7358
7359 HRESULT rc = S_OK;
7360
7361#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7362
7363 /* the process was already unexpectedly terminated, we just need to set an
7364 * error and finalize session spawning */
7365 rc = setError(E_FAIL,
7366 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7367 getName().c_str());
7368#else
7369
7370 /* PID not yet initialized, skip check. */
7371 if (mData->mSession.mPid == NIL_RTPROCESS)
7372 return false;
7373
7374 RTPROCSTATUS status;
7375 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK,
7376 &status);
7377
7378 if (vrc != VERR_PROCESS_RUNNING)
7379 {
7380 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7381 rc = setError(E_FAIL,
7382 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7383 getName().c_str(), status.iStatus);
7384 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7385 rc = setError(E_FAIL,
7386 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7387 getName().c_str(), status.iStatus);
7388 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7389 rc = setError(E_FAIL,
7390 tr("The virtual machine '%s' has terminated abnormally"),
7391 getName().c_str(), status.iStatus);
7392 else
7393 rc = setError(E_FAIL,
7394 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7395 getName().c_str(), rc);
7396 }
7397
7398#endif
7399
7400 if (FAILED(rc))
7401 {
7402 /* Close the remote session, remove the remote control from the list
7403 * and reset session state to Closed (@note keep the code in sync with
7404 * the relevant part in checkForSpawnFailure()). */
7405
7406 Assert(mData->mSession.mRemoteControls.size() == 1);
7407 if (mData->mSession.mRemoteControls.size() == 1)
7408 {
7409 ErrorInfoKeeper eik;
7410 mData->mSession.mRemoteControls.front()->Uninitialize();
7411 }
7412
7413 mData->mSession.mRemoteControls.clear();
7414 mData->mSession.mState = SessionState_Unlocked;
7415
7416 /* finalize the progress after setting the state */
7417 if (!mData->mSession.mProgress.isNull())
7418 {
7419 mData->mSession.mProgress->notifyComplete(rc);
7420 mData->mSession.mProgress.setNull();
7421 }
7422
7423 mParent->addProcessToReap(mData->mSession.mPid);
7424 mData->mSession.mPid = NIL_RTPROCESS;
7425
7426 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7427 return true;
7428 }
7429
7430 return false;
7431}
7432
7433/**
7434 * Checks whether the machine can be registered. If so, commits and saves
7435 * all settings.
7436 *
7437 * @note Must be called from mParent's write lock. Locks this object and
7438 * children for writing.
7439 */
7440HRESULT Machine::prepareRegister()
7441{
7442 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7443
7444 AutoLimitedCaller autoCaller(this);
7445 AssertComRCReturnRC(autoCaller.rc());
7446
7447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7448
7449 /* wait for state dependents to drop to zero */
7450 ensureNoStateDependencies();
7451
7452 if (!mData->mAccessible)
7453 return setError(VBOX_E_INVALID_OBJECT_STATE,
7454 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7455 mUserData->s.strName.c_str(),
7456 mData->mUuid.toString().c_str());
7457
7458 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7459
7460 if (mData->mRegistered)
7461 return setError(VBOX_E_INVALID_OBJECT_STATE,
7462 tr("The machine '%s' with UUID {%s} is already registered"),
7463 mUserData->s.strName.c_str(),
7464 mData->mUuid.toString().c_str());
7465
7466 HRESULT rc = S_OK;
7467
7468 // Ensure the settings are saved. If we are going to be registered and
7469 // no config file exists yet, create it by calling saveSettings() too.
7470 if ( (mData->flModifications)
7471 || (!mData->pMachineConfigFile->fileExists())
7472 )
7473 {
7474 rc = saveSettings(NULL);
7475 // no need to check whether VirtualBox.xml needs saving too since
7476 // we can't have a machine XML file rename pending
7477 if (FAILED(rc)) return rc;
7478 }
7479
7480 /* more config checking goes here */
7481
7482 if (SUCCEEDED(rc))
7483 {
7484 /* we may have had implicit modifications we want to fix on success */
7485 commit();
7486
7487 mData->mRegistered = true;
7488 }
7489 else
7490 {
7491 /* we may have had implicit modifications we want to cancel on failure*/
7492 rollback(false /* aNotify */);
7493 }
7494
7495 return rc;
7496}
7497
7498/**
7499 * Increases the number of objects dependent on the machine state or on the
7500 * registered state. Guarantees that these two states will not change at least
7501 * until #releaseStateDependency() is called.
7502 *
7503 * Depending on the @a aDepType value, additional state checks may be made.
7504 * These checks will set extended error info on failure. See
7505 * #checkStateDependency() for more info.
7506 *
7507 * If this method returns a failure, the dependency is not added and the caller
7508 * is not allowed to rely on any particular machine state or registration state
7509 * value and may return the failed result code to the upper level.
7510 *
7511 * @param aDepType Dependency type to add.
7512 * @param aState Current machine state (NULL if not interested).
7513 * @param aRegistered Current registered state (NULL if not interested).
7514 *
7515 * @note Locks this object for writing.
7516 */
7517HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7518 MachineState_T *aState /* = NULL */,
7519 BOOL *aRegistered /* = NULL */)
7520{
7521 AutoCaller autoCaller(this);
7522 AssertComRCReturnRC(autoCaller.rc());
7523
7524 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7525
7526 HRESULT rc = checkStateDependency(aDepType);
7527 if (FAILED(rc)) return rc;
7528
7529 {
7530 if (mData->mMachineStateChangePending != 0)
7531 {
7532 /* ensureNoStateDependencies() is waiting for state dependencies to
7533 * drop to zero so don't add more. It may make sense to wait a bit
7534 * and retry before reporting an error (since the pending state
7535 * transition should be really quick) but let's just assert for
7536 * now to see if it ever happens on practice. */
7537
7538 AssertFailed();
7539
7540 return setError(E_ACCESSDENIED,
7541 tr("Machine state change is in progress. Please retry the operation later."));
7542 }
7543
7544 ++mData->mMachineStateDeps;
7545 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7546 }
7547
7548 if (aState)
7549 *aState = mData->mMachineState;
7550 if (aRegistered)
7551 *aRegistered = mData->mRegistered;
7552
7553 return S_OK;
7554}
7555
7556/**
7557 * Decreases the number of objects dependent on the machine state.
7558 * Must always complete the #addStateDependency() call after the state
7559 * dependency is no more necessary.
7560 */
7561void Machine::releaseStateDependency()
7562{
7563 AutoCaller autoCaller(this);
7564 AssertComRCReturnVoid(autoCaller.rc());
7565
7566 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7567
7568 /* releaseStateDependency() w/o addStateDependency()? */
7569 AssertReturnVoid(mData->mMachineStateDeps != 0);
7570 -- mData->mMachineStateDeps;
7571
7572 if (mData->mMachineStateDeps == 0)
7573 {
7574 /* inform ensureNoStateDependencies() that there are no more deps */
7575 if (mData->mMachineStateChangePending != 0)
7576 {
7577 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7578 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7579 }
7580 }
7581}
7582
7583// protected methods
7584/////////////////////////////////////////////////////////////////////////////
7585
7586/**
7587 * Performs machine state checks based on the @a aDepType value. If a check
7588 * fails, this method will set extended error info, otherwise it will return
7589 * S_OK. It is supposed, that on failure, the caller will immediately return
7590 * the return value of this method to the upper level.
7591 *
7592 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7593 *
7594 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7595 * current state of this machine object allows to change settings of the
7596 * machine (i.e. the machine is not registered, or registered but not running
7597 * and not saved). It is useful to call this method from Machine setters
7598 * before performing any change.
7599 *
7600 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7601 * as for MutableStateDep except that if the machine is saved, S_OK is also
7602 * returned. This is useful in setters which allow changing machine
7603 * properties when it is in the saved state.
7604 *
7605 * @param aDepType Dependency type to check.
7606 *
7607 * @note Non Machine based classes should use #addStateDependency() and
7608 * #releaseStateDependency() methods or the smart AutoStateDependency
7609 * template.
7610 *
7611 * @note This method must be called from under this object's read or write
7612 * lock.
7613 */
7614HRESULT Machine::checkStateDependency(StateDependency aDepType)
7615{
7616 switch (aDepType)
7617 {
7618 case AnyStateDep:
7619 {
7620 break;
7621 }
7622 case MutableStateDep:
7623 {
7624 if ( mData->mRegistered
7625 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7626 || ( mData->mMachineState != MachineState_Paused
7627 && mData->mMachineState != MachineState_Running
7628 && mData->mMachineState != MachineState_Aborted
7629 && mData->mMachineState != MachineState_Teleported
7630 && mData->mMachineState != MachineState_PoweredOff
7631 )
7632 )
7633 )
7634 return setError(VBOX_E_INVALID_VM_STATE,
7635 tr("The machine is not mutable (state is %s)"),
7636 Global::stringifyMachineState(mData->mMachineState));
7637 break;
7638 }
7639 case MutableOrSavedStateDep:
7640 {
7641 if ( mData->mRegistered
7642 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7643 || ( mData->mMachineState != MachineState_Paused
7644 && mData->mMachineState != MachineState_Running
7645 && mData->mMachineState != MachineState_Aborted
7646 && mData->mMachineState != MachineState_Teleported
7647 && mData->mMachineState != MachineState_Saved
7648 && mData->mMachineState != MachineState_PoweredOff
7649 )
7650 )
7651 )
7652 return setError(VBOX_E_INVALID_VM_STATE,
7653 tr("The machine is not mutable (state is %s)"),
7654 Global::stringifyMachineState(mData->mMachineState));
7655 break;
7656 }
7657 }
7658
7659 return S_OK;
7660}
7661
7662/**
7663 * Helper to initialize all associated child objects and allocate data
7664 * structures.
7665 *
7666 * This method must be called as a part of the object's initialization procedure
7667 * (usually done in the #init() method).
7668 *
7669 * @note Must be called only from #init() or from #registeredInit().
7670 */
7671HRESULT Machine::initDataAndChildObjects()
7672{
7673 AutoCaller autoCaller(this);
7674 AssertComRCReturnRC(autoCaller.rc());
7675 AssertComRCReturn(autoCaller.state() == InInit ||
7676 autoCaller.state() == Limited, E_FAIL);
7677
7678 AssertReturn(!mData->mAccessible, E_FAIL);
7679
7680 /* allocate data structures */
7681 mSSData.allocate();
7682 mUserData.allocate();
7683 mHWData.allocate();
7684 mMediaData.allocate();
7685 mStorageControllers.allocate();
7686
7687 /* initialize mOSTypeId */
7688 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7689
7690 /* create associated BIOS settings object */
7691 unconst(mBIOSSettings).createObject();
7692 mBIOSSettings->init(this);
7693
7694 /* create an associated VRDE object (default is disabled) */
7695 unconst(mVRDEServer).createObject();
7696 mVRDEServer->init(this);
7697
7698 /* create associated serial port objects */
7699 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7700 {
7701 unconst(mSerialPorts[slot]).createObject();
7702 mSerialPorts[slot]->init(this, slot);
7703 }
7704
7705 /* create associated parallel port objects */
7706 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7707 {
7708 unconst(mParallelPorts[slot]).createObject();
7709 mParallelPorts[slot]->init(this, slot);
7710 }
7711
7712 /* create the audio adapter object (always present, default is disabled) */
7713 unconst(mAudioAdapter).createObject();
7714 mAudioAdapter->init(this);
7715
7716 /* create the USB controller object (always present, default is disabled) */
7717 unconst(mUSBController).createObject();
7718 mUSBController->init(this);
7719
7720 /* create associated network adapter objects */
7721 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7722 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7723 {
7724 unconst(mNetworkAdapters[slot]).createObject();
7725 mNetworkAdapters[slot]->init(this, slot);
7726 }
7727
7728 /* create the bandwidth control */
7729 unconst(mBandwidthControl).createObject();
7730 mBandwidthControl->init(this);
7731
7732 return S_OK;
7733}
7734
7735/**
7736 * Helper to uninitialize all associated child objects and to free all data
7737 * structures.
7738 *
7739 * This method must be called as a part of the object's uninitialization
7740 * procedure (usually done in the #uninit() method).
7741 *
7742 * @note Must be called only from #uninit() or from #registeredInit().
7743 */
7744void Machine::uninitDataAndChildObjects()
7745{
7746 AutoCaller autoCaller(this);
7747 AssertComRCReturnVoid(autoCaller.rc());
7748 AssertComRCReturnVoid( autoCaller.state() == InUninit
7749 || autoCaller.state() == Limited);
7750
7751 /* tell all our other child objects we've been uninitialized */
7752 if (mBandwidthControl)
7753 {
7754 mBandwidthControl->uninit();
7755 unconst(mBandwidthControl).setNull();
7756 }
7757
7758 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7759 {
7760 if (mNetworkAdapters[slot])
7761 {
7762 mNetworkAdapters[slot]->uninit();
7763 unconst(mNetworkAdapters[slot]).setNull();
7764 }
7765 }
7766
7767 if (mUSBController)
7768 {
7769 mUSBController->uninit();
7770 unconst(mUSBController).setNull();
7771 }
7772
7773 if (mAudioAdapter)
7774 {
7775 mAudioAdapter->uninit();
7776 unconst(mAudioAdapter).setNull();
7777 }
7778
7779 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7780 {
7781 if (mParallelPorts[slot])
7782 {
7783 mParallelPorts[slot]->uninit();
7784 unconst(mParallelPorts[slot]).setNull();
7785 }
7786 }
7787
7788 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7789 {
7790 if (mSerialPorts[slot])
7791 {
7792 mSerialPorts[slot]->uninit();
7793 unconst(mSerialPorts[slot]).setNull();
7794 }
7795 }
7796
7797 if (mVRDEServer)
7798 {
7799 mVRDEServer->uninit();
7800 unconst(mVRDEServer).setNull();
7801 }
7802
7803 if (mBIOSSettings)
7804 {
7805 mBIOSSettings->uninit();
7806 unconst(mBIOSSettings).setNull();
7807 }
7808
7809 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
7810 * instance is uninitialized; SessionMachine instances refer to real
7811 * Machine hard disks). This is necessary for a clean re-initialization of
7812 * the VM after successfully re-checking the accessibility state. Note
7813 * that in case of normal Machine or SnapshotMachine uninitialization (as
7814 * a result of unregistering or deleting the snapshot), outdated hard
7815 * disk attachments will already be uninitialized and deleted, so this
7816 * code will not affect them. */
7817 if ( !!mMediaData
7818 && (!isSessionMachine())
7819 )
7820 {
7821 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
7822 it != mMediaData->mAttachments.end();
7823 ++it)
7824 {
7825 ComObjPtr<Medium> hd = (*it)->getMedium();
7826 if (hd.isNull())
7827 continue;
7828 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
7829 AssertComRC(rc);
7830 }
7831 }
7832
7833 if (!isSessionMachine() && !isSnapshotMachine())
7834 {
7835 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
7836 if (mData->mFirstSnapshot)
7837 {
7838 // snapshots tree is protected by media write lock; strictly
7839 // this isn't necessary here since we're deleting the entire
7840 // machine, but otherwise we assert in Snapshot::uninit()
7841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7842 mData->mFirstSnapshot->uninit();
7843 mData->mFirstSnapshot.setNull();
7844 }
7845
7846 mData->mCurrentSnapshot.setNull();
7847 }
7848
7849 /* free data structures (the essential mData structure is not freed here
7850 * since it may be still in use) */
7851 mMediaData.free();
7852 mStorageControllers.free();
7853 mHWData.free();
7854 mUserData.free();
7855 mSSData.free();
7856}
7857
7858/**
7859 * Returns a pointer to the Machine object for this machine that acts like a
7860 * parent for complex machine data objects such as shared folders, etc.
7861 *
7862 * For primary Machine objects and for SnapshotMachine objects, returns this
7863 * object's pointer itself. For SessionMachine objects, returns the peer
7864 * (primary) machine pointer.
7865 */
7866Machine* Machine::getMachine()
7867{
7868 if (isSessionMachine())
7869 return (Machine*)mPeer;
7870 return this;
7871}
7872
7873/**
7874 * Makes sure that there are no machine state dependents. If necessary, waits
7875 * for the number of dependents to drop to zero.
7876 *
7877 * Make sure this method is called from under this object's write lock to
7878 * guarantee that no new dependents may be added when this method returns
7879 * control to the caller.
7880 *
7881 * @note Locks this object for writing. The lock will be released while waiting
7882 * (if necessary).
7883 *
7884 * @warning To be used only in methods that change the machine state!
7885 */
7886void Machine::ensureNoStateDependencies()
7887{
7888 AssertReturnVoid(isWriteLockOnCurrentThread());
7889
7890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7891
7892 /* Wait for all state dependents if necessary */
7893 if (mData->mMachineStateDeps != 0)
7894 {
7895 /* lazy semaphore creation */
7896 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
7897 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
7898
7899 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
7900 mData->mMachineStateDeps));
7901
7902 ++mData->mMachineStateChangePending;
7903
7904 /* reset the semaphore before waiting, the last dependent will signal
7905 * it */
7906 RTSemEventMultiReset(mData->mMachineStateDepsSem);
7907
7908 alock.release();
7909
7910 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
7911
7912 alock.acquire();
7913
7914 -- mData->mMachineStateChangePending;
7915 }
7916}
7917
7918/**
7919 * Changes the machine state and informs callbacks.
7920 *
7921 * This method is not intended to fail so it either returns S_OK or asserts (and
7922 * returns a failure).
7923 *
7924 * @note Locks this object for writing.
7925 */
7926HRESULT Machine::setMachineState(MachineState_T aMachineState)
7927{
7928 LogFlowThisFuncEnter();
7929 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
7930
7931 AutoCaller autoCaller(this);
7932 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7933
7934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7935
7936 /* wait for state dependents to drop to zero */
7937 ensureNoStateDependencies();
7938
7939 if (mData->mMachineState != aMachineState)
7940 {
7941 mData->mMachineState = aMachineState;
7942
7943 RTTimeNow(&mData->mLastStateChange);
7944
7945 mParent->onMachineStateChange(mData->mUuid, aMachineState);
7946 }
7947
7948 LogFlowThisFuncLeave();
7949 return S_OK;
7950}
7951
7952/**
7953 * Searches for a shared folder with the given logical name
7954 * in the collection of shared folders.
7955 *
7956 * @param aName logical name of the shared folder
7957 * @param aSharedFolder where to return the found object
7958 * @param aSetError whether to set the error info if the folder is
7959 * not found
7960 * @return
7961 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
7962 *
7963 * @note
7964 * must be called from under the object's lock!
7965 */
7966HRESULT Machine::findSharedFolder(const Utf8Str &aName,
7967 ComObjPtr<SharedFolder> &aSharedFolder,
7968 bool aSetError /* = false */)
7969{
7970 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
7971 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
7972 it != mHWData->mSharedFolders.end();
7973 ++it)
7974 {
7975 SharedFolder *pSF = *it;
7976 AutoCaller autoCaller(pSF);
7977 if (pSF->getName() == aName)
7978 {
7979 aSharedFolder = pSF;
7980 rc = S_OK;
7981 break;
7982 }
7983 }
7984
7985 if (aSetError && FAILED(rc))
7986 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
7987
7988 return rc;
7989}
7990
7991/**
7992 * Initializes all machine instance data from the given settings structures
7993 * from XML. The exception is the machine UUID which needs special handling
7994 * depending on the caller's use case, so the caller needs to set that herself.
7995 *
7996 * This gets called in several contexts during machine initialization:
7997 *
7998 * -- When machine XML exists on disk already and needs to be loaded into memory,
7999 * for example, from registeredInit() to load all registered machines on
8000 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8001 * attached to the machine should be part of some media registry already.
8002 *
8003 * -- During OVF import, when a machine config has been constructed from an
8004 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8005 * ensure that the media listed as attachments in the config (which have
8006 * been imported from the OVF) receive the correct registry ID.
8007 *
8008 * -- During VM cloning.
8009 *
8010 * @param config Machine settings from XML.
8011 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8012 * @return
8013 */
8014HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8015 const Guid *puuidRegistry)
8016{
8017 // copy name, description, OS type, teleporter, UTC etc.
8018 mUserData->s = config.machineUserData;
8019
8020 // look up the object by Id to check it is valid
8021 ComPtr<IGuestOSType> guestOSType;
8022 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8023 guestOSType.asOutParam());
8024 if (FAILED(rc)) return rc;
8025
8026 // stateFile (optional)
8027 if (config.strStateFile.isEmpty())
8028 mSSData->strStateFilePath.setNull();
8029 else
8030 {
8031 Utf8Str stateFilePathFull(config.strStateFile);
8032 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8033 if (RT_FAILURE(vrc))
8034 return setError(E_FAIL,
8035 tr("Invalid saved state file path '%s' (%Rrc)"),
8036 config.strStateFile.c_str(),
8037 vrc);
8038 mSSData->strStateFilePath = stateFilePathFull;
8039 }
8040
8041 // snapshot folder needs special processing so set it again
8042 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8043 if (FAILED(rc)) return rc;
8044
8045 /* Copy the extra data items (Not in any case config is already the same as
8046 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8047 * make sure the extra data map is copied). */
8048 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8049
8050 /* currentStateModified (optional, default is true) */
8051 mData->mCurrentStateModified = config.fCurrentStateModified;
8052
8053 mData->mLastStateChange = config.timeLastStateChange;
8054
8055 /*
8056 * note: all mUserData members must be assigned prior this point because
8057 * we need to commit changes in order to let mUserData be shared by all
8058 * snapshot machine instances.
8059 */
8060 mUserData.commitCopy();
8061
8062 // machine registry, if present (must be loaded before snapshots)
8063 if (config.canHaveOwnMediaRegistry())
8064 {
8065 // determine machine folder
8066 Utf8Str strMachineFolder = getSettingsFileFull();
8067 strMachineFolder.stripFilename();
8068 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8069 config.mediaRegistry,
8070 strMachineFolder);
8071 if (FAILED(rc)) return rc;
8072 }
8073
8074 /* Snapshot node (optional) */
8075 size_t cRootSnapshots;
8076 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8077 {
8078 // there must be only one root snapshot
8079 Assert(cRootSnapshots == 1);
8080
8081 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8082
8083 rc = loadSnapshot(snap,
8084 config.uuidCurrentSnapshot,
8085 NULL); // no parent == first snapshot
8086 if (FAILED(rc)) return rc;
8087 }
8088
8089 // hardware data
8090 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8091 if (FAILED(rc)) return rc;
8092
8093 // load storage controllers
8094 rc = loadStorageControllers(config.storageMachine,
8095 puuidRegistry,
8096 NULL /* puuidSnapshot */);
8097 if (FAILED(rc)) return rc;
8098
8099 /*
8100 * NOTE: the assignment below must be the last thing to do,
8101 * otherwise it will be not possible to change the settings
8102 * somewhere in the code above because all setters will be
8103 * blocked by checkStateDependency(MutableStateDep).
8104 */
8105
8106 /* set the machine state to Aborted or Saved when appropriate */
8107 if (config.fAborted)
8108 {
8109 mSSData->strStateFilePath.setNull();
8110
8111 /* no need to use setMachineState() during init() */
8112 mData->mMachineState = MachineState_Aborted;
8113 }
8114 else if (!mSSData->strStateFilePath.isEmpty())
8115 {
8116 /* no need to use setMachineState() during init() */
8117 mData->mMachineState = MachineState_Saved;
8118 }
8119
8120 // after loading settings, we are no longer different from the XML on disk
8121 mData->flModifications = 0;
8122
8123 return S_OK;
8124}
8125
8126/**
8127 * Recursively loads all snapshots starting from the given.
8128 *
8129 * @param aNode <Snapshot> node.
8130 * @param aCurSnapshotId Current snapshot ID from the settings file.
8131 * @param aParentSnapshot Parent snapshot.
8132 */
8133HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8134 const Guid &aCurSnapshotId,
8135 Snapshot *aParentSnapshot)
8136{
8137 AssertReturn(!isSnapshotMachine(), E_FAIL);
8138 AssertReturn(!isSessionMachine(), E_FAIL);
8139
8140 HRESULT rc = S_OK;
8141
8142 Utf8Str strStateFile;
8143 if (!data.strStateFile.isEmpty())
8144 {
8145 /* optional */
8146 strStateFile = data.strStateFile;
8147 int vrc = calculateFullPath(strStateFile, strStateFile);
8148 if (RT_FAILURE(vrc))
8149 return setError(E_FAIL,
8150 tr("Invalid saved state file path '%s' (%Rrc)"),
8151 strStateFile.c_str(),
8152 vrc);
8153 }
8154
8155 /* create a snapshot machine object */
8156 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8157 pSnapshotMachine.createObject();
8158 rc = pSnapshotMachine->initFromSettings(this,
8159 data.hardware,
8160 &data.debugging,
8161 &data.autostart,
8162 data.storage,
8163 data.uuid.ref(),
8164 strStateFile);
8165 if (FAILED(rc)) return rc;
8166
8167 /* create a snapshot object */
8168 ComObjPtr<Snapshot> pSnapshot;
8169 pSnapshot.createObject();
8170 /* initialize the snapshot */
8171 rc = pSnapshot->init(mParent, // VirtualBox object
8172 data.uuid,
8173 data.strName,
8174 data.strDescription,
8175 data.timestamp,
8176 pSnapshotMachine,
8177 aParentSnapshot);
8178 if (FAILED(rc)) return rc;
8179
8180 /* memorize the first snapshot if necessary */
8181 if (!mData->mFirstSnapshot)
8182 mData->mFirstSnapshot = pSnapshot;
8183
8184 /* memorize the current snapshot when appropriate */
8185 if ( !mData->mCurrentSnapshot
8186 && pSnapshot->getId() == aCurSnapshotId
8187 )
8188 mData->mCurrentSnapshot = pSnapshot;
8189
8190 // now create the children
8191 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8192 it != data.llChildSnapshots.end();
8193 ++it)
8194 {
8195 const settings::Snapshot &childData = *it;
8196 // recurse
8197 rc = loadSnapshot(childData,
8198 aCurSnapshotId,
8199 pSnapshot); // parent = the one we created above
8200 if (FAILED(rc)) return rc;
8201 }
8202
8203 return rc;
8204}
8205
8206/**
8207 * Loads settings into mHWData.
8208 *
8209 * @param data Reference to the hardware settings.
8210 * @param pDbg Pointer to the debugging settings.
8211 * @param pAutostart Pointer to the autostart settings.
8212 */
8213HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8214 const settings::Autostart *pAutostart)
8215{
8216 AssertReturn(!isSessionMachine(), E_FAIL);
8217
8218 HRESULT rc = S_OK;
8219
8220 try
8221 {
8222 /* The hardware version attribute (optional). */
8223 mHWData->mHWVersion = data.strVersion;
8224 mHWData->mHardwareUUID = data.uuid;
8225
8226 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8227 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8228 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8229 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8230 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8231 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8232 mHWData->mPAEEnabled = data.fPAE;
8233 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8234
8235 mHWData->mCPUCount = data.cCPUs;
8236 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8237 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8238
8239 // cpu
8240 if (mHWData->mCPUHotPlugEnabled)
8241 {
8242 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8243 it != data.llCpus.end();
8244 ++it)
8245 {
8246 const settings::Cpu &cpu = *it;
8247
8248 mHWData->mCPUAttached[cpu.ulId] = true;
8249 }
8250 }
8251
8252 // cpuid leafs
8253 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8254 it != data.llCpuIdLeafs.end();
8255 ++it)
8256 {
8257 const settings::CpuIdLeaf &leaf = *it;
8258
8259 switch (leaf.ulId)
8260 {
8261 case 0x0:
8262 case 0x1:
8263 case 0x2:
8264 case 0x3:
8265 case 0x4:
8266 case 0x5:
8267 case 0x6:
8268 case 0x7:
8269 case 0x8:
8270 case 0x9:
8271 case 0xA:
8272 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8273 break;
8274
8275 case 0x80000000:
8276 case 0x80000001:
8277 case 0x80000002:
8278 case 0x80000003:
8279 case 0x80000004:
8280 case 0x80000005:
8281 case 0x80000006:
8282 case 0x80000007:
8283 case 0x80000008:
8284 case 0x80000009:
8285 case 0x8000000A:
8286 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8287 break;
8288
8289 default:
8290 /* just ignore */
8291 break;
8292 }
8293 }
8294
8295 mHWData->mMemorySize = data.ulMemorySizeMB;
8296 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8297
8298 // boot order
8299 for (size_t i = 0;
8300 i < RT_ELEMENTS(mHWData->mBootOrder);
8301 i++)
8302 {
8303 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8304 if (it == data.mapBootOrder.end())
8305 mHWData->mBootOrder[i] = DeviceType_Null;
8306 else
8307 mHWData->mBootOrder[i] = it->second;
8308 }
8309
8310 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8311 mHWData->mMonitorCount = data.cMonitors;
8312 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8313 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8314 mHWData->mFirmwareType = data.firmwareType;
8315 mHWData->mPointingHidType = data.pointingHidType;
8316 mHWData->mKeyboardHidType = data.keyboardHidType;
8317 mHWData->mChipsetType = data.chipsetType;
8318 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8319 mHWData->mHpetEnabled = data.fHpetEnabled;
8320
8321 /* VRDEServer */
8322 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8323 if (FAILED(rc)) return rc;
8324
8325 /* BIOS */
8326 rc = mBIOSSettings->loadSettings(data.biosSettings);
8327 if (FAILED(rc)) return rc;
8328
8329 // Bandwidth control (must come before network adapters)
8330 rc = mBandwidthControl->loadSettings(data.ioSettings);
8331 if (FAILED(rc)) return rc;
8332
8333 /* USB Controller */
8334 rc = mUSBController->loadSettings(data.usbController);
8335 if (FAILED(rc)) return rc;
8336
8337 // network adapters
8338 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8339 uint32_t oldCount = mNetworkAdapters.size();
8340 if (newCount > oldCount)
8341 {
8342 mNetworkAdapters.resize(newCount);
8343 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8344 {
8345 unconst(mNetworkAdapters[slot]).createObject();
8346 mNetworkAdapters[slot]->init(this, slot);
8347 }
8348 }
8349 else if (newCount < oldCount)
8350 mNetworkAdapters.resize(newCount);
8351 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8352 it != data.llNetworkAdapters.end();
8353 ++it)
8354 {
8355 const settings::NetworkAdapter &nic = *it;
8356
8357 /* slot unicity is guaranteed by XML Schema */
8358 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8359 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8360 if (FAILED(rc)) return rc;
8361 }
8362
8363 // serial ports
8364 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8365 it != data.llSerialPorts.end();
8366 ++it)
8367 {
8368 const settings::SerialPort &s = *it;
8369
8370 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8371 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8372 if (FAILED(rc)) return rc;
8373 }
8374
8375 // parallel ports (optional)
8376 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8377 it != data.llParallelPorts.end();
8378 ++it)
8379 {
8380 const settings::ParallelPort &p = *it;
8381
8382 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8383 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8384 if (FAILED(rc)) return rc;
8385 }
8386
8387 /* AudioAdapter */
8388 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8389 if (FAILED(rc)) return rc;
8390
8391 /* Shared folders */
8392 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8393 it != data.llSharedFolders.end();
8394 ++it)
8395 {
8396 const settings::SharedFolder &sf = *it;
8397
8398 ComObjPtr<SharedFolder> sharedFolder;
8399 /* Check for double entries. Not allowed! */
8400 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8401 if (SUCCEEDED(rc))
8402 return setError(VBOX_E_OBJECT_IN_USE,
8403 tr("Shared folder named '%s' already exists"),
8404 sf.strName.c_str());
8405
8406 /* Create the new shared folder. Don't break on error. This will be
8407 * reported when the machine starts. */
8408 sharedFolder.createObject();
8409 rc = sharedFolder->init(getMachine(),
8410 sf.strName,
8411 sf.strHostPath,
8412 RT_BOOL(sf.fWritable),
8413 RT_BOOL(sf.fAutoMount),
8414 false /* fFailOnError */);
8415 if (FAILED(rc)) return rc;
8416 mHWData->mSharedFolders.push_back(sharedFolder);
8417 }
8418
8419 // Clipboard
8420 mHWData->mClipboardMode = data.clipboardMode;
8421
8422 // guest settings
8423 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8424
8425 // IO settings
8426 mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled;
8427 mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize;
8428
8429 // Host PCI devices
8430 for (settings::HostPciDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8431 it != data.pciAttachments.end();
8432 ++it)
8433 {
8434 const settings::HostPciDeviceAttachment &hpda = *it;
8435 ComObjPtr<PciDeviceAttachment> pda;
8436
8437 pda.createObject();
8438 pda->loadSettings(this, hpda);
8439 mHWData->mPciDeviceAssignments.push_back(pda);
8440 }
8441
8442 /*
8443 * (The following isn't really real hardware, but it lives in HWData
8444 * for reasons of convenience.)
8445 */
8446
8447#ifdef VBOX_WITH_GUEST_PROPS
8448 /* Guest properties (optional) */
8449 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8450 it != data.llGuestProperties.end();
8451 ++it)
8452 {
8453 const settings::GuestProperty &prop = *it;
8454 uint32_t fFlags = guestProp::NILFLAG;
8455 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8456 HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags };
8457 mHWData->mGuestProperties.push_back(property);
8458 }
8459
8460 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8461#endif /* VBOX_WITH_GUEST_PROPS defined */
8462
8463 rc = loadDebugging(pDbg);
8464 if (FAILED(rc))
8465 return rc;
8466
8467 mHWData->mAutostart = *pAutostart;
8468 }
8469 catch(std::bad_alloc &)
8470 {
8471 return E_OUTOFMEMORY;
8472 }
8473
8474 AssertComRC(rc);
8475 return rc;
8476}
8477
8478/**
8479 * Called from Machine::loadHardware() to load the debugging settings of the
8480 * machine.
8481 *
8482 * @param pDbg Pointer to the settings.
8483 */
8484HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8485{
8486 mHWData->mDebugging = *pDbg;
8487 /* no more processing currently required, this will probably change. */
8488 return S_OK;
8489}
8490
8491/**
8492 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8493 *
8494 * @param data
8495 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8496 * @param puuidSnapshot
8497 * @return
8498 */
8499HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8500 const Guid *puuidRegistry,
8501 const Guid *puuidSnapshot)
8502{
8503 AssertReturn(!isSessionMachine(), E_FAIL);
8504
8505 HRESULT rc = S_OK;
8506
8507 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8508 it != data.llStorageControllers.end();
8509 ++it)
8510 {
8511 const settings::StorageController &ctlData = *it;
8512
8513 ComObjPtr<StorageController> pCtl;
8514 /* Try to find one with the name first. */
8515 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8516 if (SUCCEEDED(rc))
8517 return setError(VBOX_E_OBJECT_IN_USE,
8518 tr("Storage controller named '%s' already exists"),
8519 ctlData.strName.c_str());
8520
8521 pCtl.createObject();
8522 rc = pCtl->init(this,
8523 ctlData.strName,
8524 ctlData.storageBus,
8525 ctlData.ulInstance,
8526 ctlData.fBootable);
8527 if (FAILED(rc)) return rc;
8528
8529 mStorageControllers->push_back(pCtl);
8530
8531 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8532 if (FAILED(rc)) return rc;
8533
8534 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8535 if (FAILED(rc)) return rc;
8536
8537 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8538 if (FAILED(rc)) return rc;
8539
8540 /* Set IDE emulation settings (only for AHCI controller). */
8541 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8542 {
8543 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8544 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8545 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8546 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8547 )
8548 return rc;
8549 }
8550
8551 /* Load the attached devices now. */
8552 rc = loadStorageDevices(pCtl,
8553 ctlData,
8554 puuidRegistry,
8555 puuidSnapshot);
8556 if (FAILED(rc)) return rc;
8557 }
8558
8559 return S_OK;
8560}
8561
8562/**
8563 * Called from loadStorageControllers for a controller's devices.
8564 *
8565 * @param aStorageController
8566 * @param data
8567 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8568 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8569 * @return
8570 */
8571HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8572 const settings::StorageController &data,
8573 const Guid *puuidRegistry,
8574 const Guid *puuidSnapshot)
8575{
8576 HRESULT rc = S_OK;
8577
8578 /* paranoia: detect duplicate attachments */
8579 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8580 it != data.llAttachedDevices.end();
8581 ++it)
8582 {
8583 const settings::AttachedDevice &ad = *it;
8584
8585 for (settings::AttachedDevicesList::const_iterator it2 = it;
8586 it2 != data.llAttachedDevices.end();
8587 ++it2)
8588 {
8589 if (it == it2)
8590 continue;
8591
8592 const settings::AttachedDevice &ad2 = *it2;
8593
8594 if ( ad.lPort == ad2.lPort
8595 && ad.lDevice == ad2.lDevice)
8596 {
8597 return setError(E_FAIL,
8598 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8599 aStorageController->getName().c_str(),
8600 ad.lPort,
8601 ad.lDevice,
8602 mUserData->s.strName.c_str());
8603 }
8604 }
8605 }
8606
8607 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8608 it != data.llAttachedDevices.end();
8609 ++it)
8610 {
8611 const settings::AttachedDevice &dev = *it;
8612 ComObjPtr<Medium> medium;
8613
8614 switch (dev.deviceType)
8615 {
8616 case DeviceType_Floppy:
8617 case DeviceType_DVD:
8618 if (dev.strHostDriveSrc.isNotEmpty())
8619 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8620 else
8621 rc = mParent->findRemoveableMedium(dev.deviceType,
8622 dev.uuid,
8623 false /* fRefresh */,
8624 false /* aSetError */,
8625 medium);
8626 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8627 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8628 rc = S_OK;
8629 break;
8630
8631 case DeviceType_HardDisk:
8632 {
8633 /* find a hard disk by UUID */
8634 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8635 if (FAILED(rc))
8636 {
8637 if (isSnapshotMachine())
8638 {
8639 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8640 // so the user knows that the bad disk is in a snapshot somewhere
8641 com::ErrorInfo info;
8642 return setError(E_FAIL,
8643 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8644 puuidSnapshot->raw(),
8645 info.getText().raw());
8646 }
8647 else
8648 return rc;
8649 }
8650
8651 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8652
8653 if (medium->getType() == MediumType_Immutable)
8654 {
8655 if (isSnapshotMachine())
8656 return setError(E_FAIL,
8657 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8658 "of the virtual machine '%s' ('%s')"),
8659 medium->getLocationFull().c_str(),
8660 dev.uuid.raw(),
8661 puuidSnapshot->raw(),
8662 mUserData->s.strName.c_str(),
8663 mData->m_strConfigFileFull.c_str());
8664
8665 return setError(E_FAIL,
8666 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8667 medium->getLocationFull().c_str(),
8668 dev.uuid.raw(),
8669 mUserData->s.strName.c_str(),
8670 mData->m_strConfigFileFull.c_str());
8671 }
8672
8673 if (medium->getType() == MediumType_MultiAttach)
8674 {
8675 if (isSnapshotMachine())
8676 return setError(E_FAIL,
8677 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8678 "of the virtual machine '%s' ('%s')"),
8679 medium->getLocationFull().c_str(),
8680 dev.uuid.raw(),
8681 puuidSnapshot->raw(),
8682 mUserData->s.strName.c_str(),
8683 mData->m_strConfigFileFull.c_str());
8684
8685 return setError(E_FAIL,
8686 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8687 medium->getLocationFull().c_str(),
8688 dev.uuid.raw(),
8689 mUserData->s.strName.c_str(),
8690 mData->m_strConfigFileFull.c_str());
8691 }
8692
8693 if ( !isSnapshotMachine()
8694 && medium->getChildren().size() != 0
8695 )
8696 return setError(E_FAIL,
8697 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8698 "because it has %d differencing child hard disks"),
8699 medium->getLocationFull().c_str(),
8700 dev.uuid.raw(),
8701 mUserData->s.strName.c_str(),
8702 mData->m_strConfigFileFull.c_str(),
8703 medium->getChildren().size());
8704
8705 if (findAttachment(mMediaData->mAttachments,
8706 medium))
8707 return setError(E_FAIL,
8708 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8709 medium->getLocationFull().c_str(),
8710 dev.uuid.raw(),
8711 mUserData->s.strName.c_str(),
8712 mData->m_strConfigFileFull.c_str());
8713
8714 break;
8715 }
8716
8717 default:
8718 return setError(E_FAIL,
8719 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8720 medium->getLocationFull().c_str(),
8721 mUserData->s.strName.c_str(),
8722 mData->m_strConfigFileFull.c_str());
8723 }
8724
8725 if (FAILED(rc))
8726 break;
8727
8728 /* Bandwidth groups are loaded at this point. */
8729 ComObjPtr<BandwidthGroup> pBwGroup;
8730
8731 if (!dev.strBwGroup.isEmpty())
8732 {
8733 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8734 if (FAILED(rc))
8735 return setError(E_FAIL,
8736 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8737 medium->getLocationFull().c_str(),
8738 dev.strBwGroup.c_str(),
8739 mUserData->s.strName.c_str(),
8740 mData->m_strConfigFileFull.c_str());
8741 pBwGroup->reference();
8742 }
8743
8744 const Bstr controllerName = aStorageController->getName();
8745 ComObjPtr<MediumAttachment> pAttachment;
8746 pAttachment.createObject();
8747 rc = pAttachment->init(this,
8748 medium,
8749 controllerName,
8750 dev.lPort,
8751 dev.lDevice,
8752 dev.deviceType,
8753 false,
8754 dev.fPassThrough,
8755 dev.fTempEject,
8756 dev.fNonRotational,
8757 dev.fDiscard,
8758 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8759 if (FAILED(rc)) break;
8760
8761 /* associate the medium with this machine and snapshot */
8762 if (!medium.isNull())
8763 {
8764 AutoCaller medCaller(medium);
8765 if (FAILED(medCaller.rc())) return medCaller.rc();
8766 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8767
8768 if (isSnapshotMachine())
8769 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8770 else
8771 rc = medium->addBackReference(mData->mUuid);
8772 /* If the medium->addBackReference fails it sets an appropriate
8773 * error message, so no need to do any guesswork here. */
8774
8775 if (puuidRegistry)
8776 // caller wants registry ID to be set on all attached media (OVF import case)
8777 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
8778 }
8779
8780 if (FAILED(rc))
8781 break;
8782
8783 /* back up mMediaData to let registeredInit() properly rollback on failure
8784 * (= limited accessibility) */
8785 setModified(IsModified_Storage);
8786 mMediaData.backup();
8787 mMediaData->mAttachments.push_back(pAttachment);
8788 }
8789
8790 return rc;
8791}
8792
8793/**
8794 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
8795 *
8796 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
8797 * @param aSnapshot where to return the found snapshot
8798 * @param aSetError true to set extended error info on failure
8799 */
8800HRESULT Machine::findSnapshotById(const Guid &aId,
8801 ComObjPtr<Snapshot> &aSnapshot,
8802 bool aSetError /* = false */)
8803{
8804 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8805
8806 if (!mData->mFirstSnapshot)
8807 {
8808 if (aSetError)
8809 return setError(E_FAIL, tr("This machine does not have any snapshots"));
8810 return E_FAIL;
8811 }
8812
8813 if (aId.isEmpty())
8814 aSnapshot = mData->mFirstSnapshot;
8815 else
8816 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
8817
8818 if (!aSnapshot)
8819 {
8820 if (aSetError)
8821 return setError(E_FAIL,
8822 tr("Could not find a snapshot with UUID {%s}"),
8823 aId.toString().c_str());
8824 return E_FAIL;
8825 }
8826
8827 return S_OK;
8828}
8829
8830/**
8831 * Returns the snapshot with the given name or fails of no such snapshot.
8832 *
8833 * @param aName snapshot name to find
8834 * @param aSnapshot where to return the found snapshot
8835 * @param aSetError true to set extended error info on failure
8836 */
8837HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
8838 ComObjPtr<Snapshot> &aSnapshot,
8839 bool aSetError /* = false */)
8840{
8841 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
8842
8843 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
8844
8845 if (!mData->mFirstSnapshot)
8846 {
8847 if (aSetError)
8848 return setError(VBOX_E_OBJECT_NOT_FOUND,
8849 tr("This machine does not have any snapshots"));
8850 return VBOX_E_OBJECT_NOT_FOUND;
8851 }
8852
8853 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
8854
8855 if (!aSnapshot)
8856 {
8857 if (aSetError)
8858 return setError(VBOX_E_OBJECT_NOT_FOUND,
8859 tr("Could not find a snapshot named '%s'"), strName.c_str());
8860 return VBOX_E_OBJECT_NOT_FOUND;
8861 }
8862
8863 return S_OK;
8864}
8865
8866/**
8867 * Returns a storage controller object with the given name.
8868 *
8869 * @param aName storage controller name to find
8870 * @param aStorageController where to return the found storage controller
8871 * @param aSetError true to set extended error info on failure
8872 */
8873HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
8874 ComObjPtr<StorageController> &aStorageController,
8875 bool aSetError /* = false */)
8876{
8877 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
8878
8879 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
8880 it != mStorageControllers->end();
8881 ++it)
8882 {
8883 if ((*it)->getName() == aName)
8884 {
8885 aStorageController = (*it);
8886 return S_OK;
8887 }
8888 }
8889
8890 if (aSetError)
8891 return setError(VBOX_E_OBJECT_NOT_FOUND,
8892 tr("Could not find a storage controller named '%s'"),
8893 aName.c_str());
8894 return VBOX_E_OBJECT_NOT_FOUND;
8895}
8896
8897HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
8898 MediaData::AttachmentList &atts)
8899{
8900 AutoCaller autoCaller(this);
8901 if (FAILED(autoCaller.rc())) return autoCaller.rc();
8902
8903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8904
8905 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
8906 it != mMediaData->mAttachments.end();
8907 ++it)
8908 {
8909 const ComObjPtr<MediumAttachment> &pAtt = *it;
8910
8911 // should never happen, but deal with NULL pointers in the list.
8912 AssertStmt(!pAtt.isNull(), continue);
8913
8914 // getControllerName() needs caller+read lock
8915 AutoCaller autoAttCaller(pAtt);
8916 if (FAILED(autoAttCaller.rc()))
8917 {
8918 atts.clear();
8919 return autoAttCaller.rc();
8920 }
8921 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
8922
8923 if (pAtt->getControllerName() == aName)
8924 atts.push_back(pAtt);
8925 }
8926
8927 return S_OK;
8928}
8929
8930/**
8931 * Helper for #saveSettings. Cares about renaming the settings directory and
8932 * file if the machine name was changed and about creating a new settings file
8933 * if this is a new machine.
8934 *
8935 * @note Must be never called directly but only from #saveSettings().
8936 */
8937HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
8938{
8939 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
8940
8941 HRESULT rc = S_OK;
8942
8943 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
8944
8945 /// @todo need to handle primary group change, too
8946
8947 /* attempt to rename the settings file if machine name is changed */
8948 if ( mUserData->s.fNameSync
8949 && mUserData.isBackedUp()
8950 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
8951 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
8952 )
8953 {
8954 bool dirRenamed = false;
8955 bool fileRenamed = false;
8956
8957 Utf8Str configFile, newConfigFile;
8958 Utf8Str configFilePrev, newConfigFilePrev;
8959 Utf8Str configDir, newConfigDir;
8960
8961 do
8962 {
8963 int vrc = VINF_SUCCESS;
8964
8965 Utf8Str name = mUserData.backedUpData()->s.strName;
8966 Utf8Str newName = mUserData->s.strName;
8967 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
8968 if (group == "/")
8969 group.setNull();
8970 Utf8Str newGroup = mUserData->s.llGroups.front();
8971 if (newGroup == "/")
8972 newGroup.setNull();
8973
8974 configFile = mData->m_strConfigFileFull;
8975
8976 /* first, rename the directory if it matches the group and machine name */
8977 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
8978 group.c_str(), RTPATH_DELIMITER, name.c_str());
8979 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
8980 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
8981 configDir = configFile;
8982 configDir.stripFilename();
8983 newConfigDir = configDir;
8984 if ( configDir.length() >= groupPlusName.length()
8985 && configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).equals(groupPlusName.c_str()))
8986 {
8987 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
8988 Utf8Str newConfigBaseDir(newConfigDir);
8989 newConfigDir.append(newGroupPlusName);
8990 /* new dir and old dir cannot be equal here because of 'if'
8991 * above and because name != newName */
8992 Assert(configDir != newConfigDir);
8993 if (!fSettingsFileIsNew)
8994 {
8995 /* perform real rename only if the machine is not new */
8996 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
8997 if (vrc == VERR_FILE_NOT_FOUND)
8998 {
8999 /* create the parent directory, then retry renaming */
9000 Utf8Str parent(newConfigDir);
9001 parent.stripFilename();
9002 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9003 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9004 }
9005 if (RT_FAILURE(vrc))
9006 {
9007 rc = setError(E_FAIL,
9008 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9009 configDir.c_str(),
9010 newConfigDir.c_str(),
9011 vrc);
9012 break;
9013 }
9014 /* delete subdirectories which are no longer needed */
9015 Utf8Str dir(configDir);
9016 dir.stripFilename();
9017 while (dir != newConfigBaseDir && dir != ".")
9018 {
9019 vrc = RTDirRemove(dir.c_str());
9020 if (RT_FAILURE(vrc))
9021 break;
9022 dir.stripFilename();
9023 }
9024 dirRenamed = true;
9025 }
9026 }
9027
9028 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9029 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9030
9031 /* then try to rename the settings file itself */
9032 if (newConfigFile != configFile)
9033 {
9034 /* get the path to old settings file in renamed directory */
9035 configFile = Utf8StrFmt("%s%c%s",
9036 newConfigDir.c_str(),
9037 RTPATH_DELIMITER,
9038 RTPathFilename(configFile.c_str()));
9039 if (!fSettingsFileIsNew)
9040 {
9041 /* perform real rename only if the machine is not new */
9042 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9043 if (RT_FAILURE(vrc))
9044 {
9045 rc = setError(E_FAIL,
9046 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9047 configFile.c_str(),
9048 newConfigFile.c_str(),
9049 vrc);
9050 break;
9051 }
9052 fileRenamed = true;
9053 configFilePrev = configFile;
9054 configFilePrev += "-prev";
9055 newConfigFilePrev = newConfigFile;
9056 newConfigFilePrev += "-prev";
9057 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9058 }
9059 }
9060
9061 // update m_strConfigFileFull amd mConfigFile
9062 mData->m_strConfigFileFull = newConfigFile;
9063 // compute the relative path too
9064 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9065
9066 // store the old and new so that VirtualBox::saveSettings() can update
9067 // the media registry
9068 if ( mData->mRegistered
9069 && configDir != newConfigDir)
9070 {
9071 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9072
9073 if (pfNeedsGlobalSaveSettings)
9074 *pfNeedsGlobalSaveSettings = true;
9075 }
9076
9077 // in the saved state file path, replace the old directory with the new directory
9078 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9079 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9080
9081 // and do the same thing for the saved state file paths of all the online snapshots
9082 if (mData->mFirstSnapshot)
9083 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9084 newConfigDir.c_str());
9085 }
9086 while (0);
9087
9088 if (FAILED(rc))
9089 {
9090 /* silently try to rename everything back */
9091 if (fileRenamed)
9092 {
9093 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9094 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9095 }
9096 if (dirRenamed)
9097 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9098 }
9099
9100 if (FAILED(rc)) return rc;
9101 }
9102
9103 if (fSettingsFileIsNew)
9104 {
9105 /* create a virgin config file */
9106 int vrc = VINF_SUCCESS;
9107
9108 /* ensure the settings directory exists */
9109 Utf8Str path(mData->m_strConfigFileFull);
9110 path.stripFilename();
9111 if (!RTDirExists(path.c_str()))
9112 {
9113 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9114 if (RT_FAILURE(vrc))
9115 {
9116 return setError(E_FAIL,
9117 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9118 path.c_str(),
9119 vrc);
9120 }
9121 }
9122
9123 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9124 path = Utf8Str(mData->m_strConfigFileFull);
9125 RTFILE f = NIL_RTFILE;
9126 vrc = RTFileOpen(&f, path.c_str(),
9127 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9128 if (RT_FAILURE(vrc))
9129 return setError(E_FAIL,
9130 tr("Could not create the settings file '%s' (%Rrc)"),
9131 path.c_str(),
9132 vrc);
9133 RTFileClose(f);
9134 }
9135
9136 return rc;
9137}
9138
9139/**
9140 * Saves and commits machine data, user data and hardware data.
9141 *
9142 * Note that on failure, the data remains uncommitted.
9143 *
9144 * @a aFlags may combine the following flags:
9145 *
9146 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9147 * Used when saving settings after an operation that makes them 100%
9148 * correspond to the settings from the current snapshot.
9149 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9150 * #isReallyModified() returns false. This is necessary for cases when we
9151 * change machine data directly, not through the backup()/commit() mechanism.
9152 * - SaveS_Force: settings will be saved without doing a deep compare of the
9153 * settings structures. This is used when this is called because snapshots
9154 * have changed to avoid the overhead of the deep compare.
9155 *
9156 * @note Must be called from under this object's write lock. Locks children for
9157 * writing.
9158 *
9159 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9160 * initialized to false and that will be set to true by this function if
9161 * the caller must invoke VirtualBox::saveSettings() because the global
9162 * settings have changed. This will happen if a machine rename has been
9163 * saved and the global machine and media registries will therefore need
9164 * updating.
9165 */
9166HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9167 int aFlags /*= 0*/)
9168{
9169 LogFlowThisFuncEnter();
9170
9171 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9172
9173 /* make sure child objects are unable to modify the settings while we are
9174 * saving them */
9175 ensureNoStateDependencies();
9176
9177 AssertReturn(!isSnapshotMachine(),
9178 E_FAIL);
9179
9180 HRESULT rc = S_OK;
9181 bool fNeedsWrite = false;
9182
9183 /* First, prepare to save settings. It will care about renaming the
9184 * settings directory and file if the machine name was changed and about
9185 * creating a new settings file if this is a new machine. */
9186 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9187 if (FAILED(rc)) return rc;
9188
9189 // keep a pointer to the current settings structures
9190 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9191 settings::MachineConfigFile *pNewConfig = NULL;
9192
9193 try
9194 {
9195 // make a fresh one to have everyone write stuff into
9196 pNewConfig = new settings::MachineConfigFile(NULL);
9197 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9198
9199 // now go and copy all the settings data from COM to the settings structures
9200 // (this calles saveSettings() on all the COM objects in the machine)
9201 copyMachineDataToSettings(*pNewConfig);
9202
9203 if (aFlags & SaveS_ResetCurStateModified)
9204 {
9205 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9206 mData->mCurrentStateModified = FALSE;
9207 fNeedsWrite = true; // always, no need to compare
9208 }
9209 else if (aFlags & SaveS_Force)
9210 {
9211 fNeedsWrite = true; // always, no need to compare
9212 }
9213 else
9214 {
9215 if (!mData->mCurrentStateModified)
9216 {
9217 // do a deep compare of the settings that we just saved with the settings
9218 // previously stored in the config file; this invokes MachineConfigFile::operator==
9219 // which does a deep compare of all the settings, which is expensive but less expensive
9220 // than writing out XML in vain
9221 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9222
9223 // could still be modified if any settings changed
9224 mData->mCurrentStateModified = fAnySettingsChanged;
9225
9226 fNeedsWrite = fAnySettingsChanged;
9227 }
9228 else
9229 fNeedsWrite = true;
9230 }
9231
9232 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9233
9234 if (fNeedsWrite)
9235 // now spit it all out!
9236 pNewConfig->write(mData->m_strConfigFileFull);
9237
9238 mData->pMachineConfigFile = pNewConfig;
9239 delete pOldConfig;
9240 commit();
9241
9242 // after saving settings, we are no longer different from the XML on disk
9243 mData->flModifications = 0;
9244 }
9245 catch (HRESULT err)
9246 {
9247 // we assume that error info is set by the thrower
9248 rc = err;
9249
9250 // restore old config
9251 delete pNewConfig;
9252 mData->pMachineConfigFile = pOldConfig;
9253 }
9254 catch (...)
9255 {
9256 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9257 }
9258
9259 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9260 {
9261 /* Fire the data change event, even on failure (since we've already
9262 * committed all data). This is done only for SessionMachines because
9263 * mutable Machine instances are always not registered (i.e. private
9264 * to the client process that creates them) and thus don't need to
9265 * inform callbacks. */
9266 if (isSessionMachine())
9267 mParent->onMachineDataChange(mData->mUuid);
9268 }
9269
9270 LogFlowThisFunc(("rc=%08X\n", rc));
9271 LogFlowThisFuncLeave();
9272 return rc;
9273}
9274
9275/**
9276 * Implementation for saving the machine settings into the given
9277 * settings::MachineConfigFile instance. This copies machine extradata
9278 * from the previous machine config file in the instance data, if any.
9279 *
9280 * This gets called from two locations:
9281 *
9282 * -- Machine::saveSettings(), during the regular XML writing;
9283 *
9284 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9285 * exported to OVF and we write the VirtualBox proprietary XML
9286 * into a <vbox:Machine> tag.
9287 *
9288 * This routine fills all the fields in there, including snapshots, *except*
9289 * for the following:
9290 *
9291 * -- fCurrentStateModified. There is some special logic associated with that.
9292 *
9293 * The caller can then call MachineConfigFile::write() or do something else
9294 * with it.
9295 *
9296 * Caller must hold the machine lock!
9297 *
9298 * This throws XML errors and HRESULT, so the caller must have a catch block!
9299 */
9300void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9301{
9302 // deep copy extradata
9303 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9304
9305 config.uuid = mData->mUuid;
9306
9307 // copy name, description, OS type, teleport, UTC etc.
9308 config.machineUserData = mUserData->s;
9309
9310 if ( mData->mMachineState == MachineState_Saved
9311 || mData->mMachineState == MachineState_Restoring
9312 // when deleting a snapshot we may or may not have a saved state in the current state,
9313 // so let's not assert here please
9314 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9315 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9316 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9317 && (!mSSData->strStateFilePath.isEmpty())
9318 )
9319 )
9320 {
9321 Assert(!mSSData->strStateFilePath.isEmpty());
9322 /* try to make the file name relative to the settings file dir */
9323 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9324 }
9325 else
9326 {
9327 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9328 config.strStateFile.setNull();
9329 }
9330
9331 if (mData->mCurrentSnapshot)
9332 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9333 else
9334 config.uuidCurrentSnapshot.clear();
9335
9336 config.timeLastStateChange = mData->mLastStateChange;
9337 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9338 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9339
9340 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9341 if (FAILED(rc)) throw rc;
9342
9343 rc = saveStorageControllers(config.storageMachine);
9344 if (FAILED(rc)) throw rc;
9345
9346 // save machine's media registry if this is VirtualBox 4.0 or later
9347 if (config.canHaveOwnMediaRegistry())
9348 {
9349 // determine machine folder
9350 Utf8Str strMachineFolder = getSettingsFileFull();
9351 strMachineFolder.stripFilename();
9352 mParent->saveMediaRegistry(config.mediaRegistry,
9353 getId(), // only media with registry ID == machine UUID
9354 strMachineFolder);
9355 // this throws HRESULT
9356 }
9357
9358 // save snapshots
9359 rc = saveAllSnapshots(config);
9360 if (FAILED(rc)) throw rc;
9361}
9362
9363/**
9364 * Saves all snapshots of the machine into the given machine config file. Called
9365 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9366 * @param config
9367 * @return
9368 */
9369HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9370{
9371 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9372
9373 HRESULT rc = S_OK;
9374
9375 try
9376 {
9377 config.llFirstSnapshot.clear();
9378
9379 if (mData->mFirstSnapshot)
9380 {
9381 settings::Snapshot snapNew;
9382 config.llFirstSnapshot.push_back(snapNew);
9383
9384 // get reference to the fresh copy of the snapshot on the list and
9385 // work on that copy directly to avoid excessive copying later
9386 settings::Snapshot &snap = config.llFirstSnapshot.front();
9387
9388 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9389 if (FAILED(rc)) throw rc;
9390 }
9391
9392// if (mType == IsSessionMachine)
9393// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9394
9395 }
9396 catch (HRESULT err)
9397 {
9398 /* we assume that error info is set by the thrower */
9399 rc = err;
9400 }
9401 catch (...)
9402 {
9403 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9404 }
9405
9406 return rc;
9407}
9408
9409/**
9410 * Saves the VM hardware configuration. It is assumed that the
9411 * given node is empty.
9412 *
9413 * @param data Reference to the settings object for the hardware config.
9414 * @param pDbg Pointer to the settings object for the debugging config
9415 * which happens to live in mHWData.
9416 * @param pAutostart Pointer to the settings object for the autostart config
9417 * which happens to live in mHWData.
9418 */
9419HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9420 settings::Autostart *pAutostart)
9421{
9422 HRESULT rc = S_OK;
9423
9424 try
9425 {
9426 /* The hardware version attribute (optional).
9427 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9428 if ( mHWData->mHWVersion == "1"
9429 && mSSData->strStateFilePath.isEmpty()
9430 )
9431 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. */
9432
9433 data.strVersion = mHWData->mHWVersion;
9434 data.uuid = mHWData->mHardwareUUID;
9435
9436 // CPU
9437 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9438 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9439 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9440 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9441 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9442 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9443 data.fPAE = !!mHWData->mPAEEnabled;
9444 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9445
9446 /* Standard and Extended CPUID leafs. */
9447 data.llCpuIdLeafs.clear();
9448 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9449 {
9450 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9451 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9452 }
9453 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9454 {
9455 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9456 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9457 }
9458
9459 data.cCPUs = mHWData->mCPUCount;
9460 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9461 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9462
9463 data.llCpus.clear();
9464 if (data.fCpuHotPlug)
9465 {
9466 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9467 {
9468 if (mHWData->mCPUAttached[idx])
9469 {
9470 settings::Cpu cpu;
9471 cpu.ulId = idx;
9472 data.llCpus.push_back(cpu);
9473 }
9474 }
9475 }
9476
9477 // memory
9478 data.ulMemorySizeMB = mHWData->mMemorySize;
9479 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9480
9481 // firmware
9482 data.firmwareType = mHWData->mFirmwareType;
9483
9484 // HID
9485 data.pointingHidType = mHWData->mPointingHidType;
9486 data.keyboardHidType = mHWData->mKeyboardHidType;
9487
9488 // chipset
9489 data.chipsetType = mHWData->mChipsetType;
9490
9491 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9492
9493 // HPET
9494 data.fHpetEnabled = !!mHWData->mHpetEnabled;
9495
9496 // boot order
9497 data.mapBootOrder.clear();
9498 for (size_t i = 0;
9499 i < RT_ELEMENTS(mHWData->mBootOrder);
9500 ++i)
9501 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9502
9503 // display
9504 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9505 data.cMonitors = mHWData->mMonitorCount;
9506 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9507 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9508
9509 /* VRDEServer settings (optional) */
9510 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9511 if (FAILED(rc)) throw rc;
9512
9513 /* BIOS (required) */
9514 rc = mBIOSSettings->saveSettings(data.biosSettings);
9515 if (FAILED(rc)) throw rc;
9516
9517 /* USB Controller (required) */
9518 rc = mUSBController->saveSettings(data.usbController);
9519 if (FAILED(rc)) throw rc;
9520
9521 /* Network adapters (required) */
9522 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9523 data.llNetworkAdapters.clear();
9524 /* Write out only the nominal number of network adapters for this
9525 * chipset type. Since Machine::commit() hasn't been called there
9526 * may be extra NIC settings in the vector. */
9527 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9528 {
9529 settings::NetworkAdapter nic;
9530 nic.ulSlot = slot;
9531 /* paranoia check... must not be NULL, but must not crash either. */
9532 if (mNetworkAdapters[slot])
9533 {
9534 rc = mNetworkAdapters[slot]->saveSettings(nic);
9535 if (FAILED(rc)) throw rc;
9536
9537 data.llNetworkAdapters.push_back(nic);
9538 }
9539 }
9540
9541 /* Serial ports */
9542 data.llSerialPorts.clear();
9543 for (ULONG slot = 0;
9544 slot < RT_ELEMENTS(mSerialPorts);
9545 ++slot)
9546 {
9547 settings::SerialPort s;
9548 s.ulSlot = slot;
9549 rc = mSerialPorts[slot]->saveSettings(s);
9550 if (FAILED(rc)) return rc;
9551
9552 data.llSerialPorts.push_back(s);
9553 }
9554
9555 /* Parallel ports */
9556 data.llParallelPorts.clear();
9557 for (ULONG slot = 0;
9558 slot < RT_ELEMENTS(mParallelPorts);
9559 ++slot)
9560 {
9561 settings::ParallelPort p;
9562 p.ulSlot = slot;
9563 rc = mParallelPorts[slot]->saveSettings(p);
9564 if (FAILED(rc)) return rc;
9565
9566 data.llParallelPorts.push_back(p);
9567 }
9568
9569 /* Audio adapter */
9570 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9571 if (FAILED(rc)) return rc;
9572
9573 /* Shared folders */
9574 data.llSharedFolders.clear();
9575 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9576 it != mHWData->mSharedFolders.end();
9577 ++it)
9578 {
9579 SharedFolder *pSF = *it;
9580 AutoCaller sfCaller(pSF);
9581 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9582 settings::SharedFolder sf;
9583 sf.strName = pSF->getName();
9584 sf.strHostPath = pSF->getHostPath();
9585 sf.fWritable = !!pSF->isWritable();
9586 sf.fAutoMount = !!pSF->isAutoMounted();
9587
9588 data.llSharedFolders.push_back(sf);
9589 }
9590
9591 // clipboard
9592 data.clipboardMode = mHWData->mClipboardMode;
9593
9594 /* Guest */
9595 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9596
9597 // IO settings
9598 data.ioSettings.fIoCacheEnabled = !!mHWData->mIoCacheEnabled;
9599 data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize;
9600
9601 /* BandwidthControl (required) */
9602 rc = mBandwidthControl->saveSettings(data.ioSettings);
9603 if (FAILED(rc)) throw rc;
9604
9605 /* Host PCI devices */
9606 for (HWData::PciDeviceAssignmentList::const_iterator it = mHWData->mPciDeviceAssignments.begin();
9607 it != mHWData->mPciDeviceAssignments.end();
9608 ++it)
9609 {
9610 ComObjPtr<PciDeviceAttachment> pda = *it;
9611 settings::HostPciDeviceAttachment hpda;
9612
9613 rc = pda->saveSettings(hpda);
9614 if (FAILED(rc)) throw rc;
9615
9616 data.pciAttachments.push_back(hpda);
9617 }
9618
9619
9620 // guest properties
9621 data.llGuestProperties.clear();
9622#ifdef VBOX_WITH_GUEST_PROPS
9623 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
9624 it != mHWData->mGuestProperties.end();
9625 ++it)
9626 {
9627 HWData::GuestProperty property = *it;
9628
9629 /* Remove transient guest properties at shutdown unless we
9630 * are saving state */
9631 if ( ( mData->mMachineState == MachineState_PoweredOff
9632 || mData->mMachineState == MachineState_Aborted
9633 || mData->mMachineState == MachineState_Teleported)
9634 && ( property.mFlags & guestProp::TRANSIENT
9635 || property.mFlags & guestProp::TRANSRESET))
9636 continue;
9637 settings::GuestProperty prop;
9638 prop.strName = property.strName;
9639 prop.strValue = property.strValue;
9640 prop.timestamp = property.mTimestamp;
9641 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9642 guestProp::writeFlags(property.mFlags, szFlags);
9643 prop.strFlags = szFlags;
9644
9645 data.llGuestProperties.push_back(prop);
9646 }
9647
9648 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9649 /* I presume this doesn't require a backup(). */
9650 mData->mGuestPropertiesModified = FALSE;
9651#endif /* VBOX_WITH_GUEST_PROPS defined */
9652
9653 *pDbg = mHWData->mDebugging;
9654 *pAutostart = mHWData->mAutostart;
9655 }
9656 catch(std::bad_alloc &)
9657 {
9658 return E_OUTOFMEMORY;
9659 }
9660
9661 AssertComRC(rc);
9662 return rc;
9663}
9664
9665/**
9666 * Saves the storage controller configuration.
9667 *
9668 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9669 */
9670HRESULT Machine::saveStorageControllers(settings::Storage &data)
9671{
9672 data.llStorageControllers.clear();
9673
9674 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9675 it != mStorageControllers->end();
9676 ++it)
9677 {
9678 HRESULT rc;
9679 ComObjPtr<StorageController> pCtl = *it;
9680
9681 settings::StorageController ctl;
9682 ctl.strName = pCtl->getName();
9683 ctl.controllerType = pCtl->getControllerType();
9684 ctl.storageBus = pCtl->getStorageBus();
9685 ctl.ulInstance = pCtl->getInstance();
9686 ctl.fBootable = pCtl->getBootable();
9687
9688 /* Save the port count. */
9689 ULONG portCount;
9690 rc = pCtl->COMGETTER(PortCount)(&portCount);
9691 ComAssertComRCRet(rc, rc);
9692 ctl.ulPortCount = portCount;
9693
9694 /* Save fUseHostIOCache */
9695 BOOL fUseHostIOCache;
9696 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9697 ComAssertComRCRet(rc, rc);
9698 ctl.fUseHostIOCache = !!fUseHostIOCache;
9699
9700 /* Save IDE emulation settings. */
9701 if (ctl.controllerType == StorageControllerType_IntelAhci)
9702 {
9703 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9704 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9705 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9706 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9707 )
9708 ComAssertComRCRet(rc, rc);
9709 }
9710
9711 /* save the devices now. */
9712 rc = saveStorageDevices(pCtl, ctl);
9713 ComAssertComRCRet(rc, rc);
9714
9715 data.llStorageControllers.push_back(ctl);
9716 }
9717
9718 return S_OK;
9719}
9720
9721/**
9722 * Saves the hard disk configuration.
9723 */
9724HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9725 settings::StorageController &data)
9726{
9727 MediaData::AttachmentList atts;
9728
9729 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9730 if (FAILED(rc)) return rc;
9731
9732 data.llAttachedDevices.clear();
9733 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9734 it != atts.end();
9735 ++it)
9736 {
9737 settings::AttachedDevice dev;
9738
9739 MediumAttachment *pAttach = *it;
9740 Medium *pMedium = pAttach->getMedium();
9741
9742 dev.deviceType = pAttach->getType();
9743 dev.lPort = pAttach->getPort();
9744 dev.lDevice = pAttach->getDevice();
9745 if (pMedium)
9746 {
9747 if (pMedium->isHostDrive())
9748 dev.strHostDriveSrc = pMedium->getLocationFull();
9749 else
9750 dev.uuid = pMedium->getId();
9751 dev.fPassThrough = pAttach->getPassthrough();
9752 dev.fTempEject = pAttach->getTempEject();
9753 dev.fDiscard = pAttach->getDiscard();
9754 }
9755
9756 dev.strBwGroup = pAttach->getBandwidthGroup();
9757
9758 data.llAttachedDevices.push_back(dev);
9759 }
9760
9761 return S_OK;
9762}
9763
9764/**
9765 * Saves machine state settings as defined by aFlags
9766 * (SaveSTS_* values).
9767 *
9768 * @param aFlags Combination of SaveSTS_* flags.
9769 *
9770 * @note Locks objects for writing.
9771 */
9772HRESULT Machine::saveStateSettings(int aFlags)
9773{
9774 if (aFlags == 0)
9775 return S_OK;
9776
9777 AutoCaller autoCaller(this);
9778 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9779
9780 /* This object's write lock is also necessary to serialize file access
9781 * (prevent concurrent reads and writes) */
9782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
9783
9784 HRESULT rc = S_OK;
9785
9786 Assert(mData->pMachineConfigFile);
9787
9788 try
9789 {
9790 if (aFlags & SaveSTS_CurStateModified)
9791 mData->pMachineConfigFile->fCurrentStateModified = true;
9792
9793 if (aFlags & SaveSTS_StateFilePath)
9794 {
9795 if (!mSSData->strStateFilePath.isEmpty())
9796 /* try to make the file name relative to the settings file dir */
9797 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
9798 else
9799 mData->pMachineConfigFile->strStateFile.setNull();
9800 }
9801
9802 if (aFlags & SaveSTS_StateTimeStamp)
9803 {
9804 Assert( mData->mMachineState != MachineState_Aborted
9805 || mSSData->strStateFilePath.isEmpty());
9806
9807 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
9808
9809 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
9810//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
9811 }
9812
9813 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
9814 }
9815 catch (...)
9816 {
9817 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9818 }
9819
9820 return rc;
9821}
9822
9823/**
9824 * Ensures that the given medium is added to a media registry. If this machine
9825 * was created with 4.0 or later, then the machine registry is used. Otherwise
9826 * the global VirtualBox media registry is used.
9827 *
9828 * Caller must NOT hold machine lock, media tree or any medium locks!
9829 *
9830 * @param pMedium
9831 */
9832void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
9833{
9834 /* Paranoia checks: do not hold machine or media tree locks. */
9835 AssertReturnVoid(!isWriteLockOnCurrentThread());
9836 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
9837
9838 ComObjPtr<Medium> pBase;
9839 {
9840 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9841 pBase = pMedium->getBase();
9842 }
9843
9844 /* Paranoia checks: do not hold medium locks. */
9845 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
9846 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
9847
9848 // decide which medium registry to use now that the medium is attached:
9849 Guid uuid;
9850 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
9851 // machine XML is VirtualBox 4.0 or higher:
9852 uuid = getId(); // machine UUID
9853 else
9854 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
9855
9856 if (pMedium->addRegistry(uuid, false /* fRecurse */))
9857 mParent->markRegistryModified(uuid);
9858
9859 /* For more complex hard disk structures it can happen that the base
9860 * medium isn't yet associated with any medium registry. Do that now. */
9861 if (pMedium != pBase)
9862 {
9863 if (pBase->addRegistry(uuid, true /* fRecurse */))
9864 mParent->markRegistryModified(uuid);
9865 }
9866}
9867
9868/**
9869 * Creates differencing hard disks for all normal hard disks attached to this
9870 * machine and a new set of attachments to refer to created disks.
9871 *
9872 * Used when taking a snapshot or when deleting the current state. Gets called
9873 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
9874 *
9875 * This method assumes that mMediaData contains the original hard disk attachments
9876 * it needs to create diffs for. On success, these attachments will be replaced
9877 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
9878 * called to delete created diffs which will also rollback mMediaData and restore
9879 * whatever was backed up before calling this method.
9880 *
9881 * Attachments with non-normal hard disks are left as is.
9882 *
9883 * If @a aOnline is @c false then the original hard disks that require implicit
9884 * diffs will be locked for reading. Otherwise it is assumed that they are
9885 * already locked for writing (when the VM was started). Note that in the latter
9886 * case it is responsibility of the caller to lock the newly created diffs for
9887 * writing if this method succeeds.
9888 *
9889 * @param aProgress Progress object to run (must contain at least as
9890 * many operations left as the number of hard disks
9891 * attached).
9892 * @param aOnline Whether the VM was online prior to this operation.
9893 *
9894 * @note The progress object is not marked as completed, neither on success nor
9895 * on failure. This is a responsibility of the caller.
9896 *
9897 * @note Locks this object for writing.
9898 */
9899HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
9900 ULONG aWeight,
9901 bool aOnline)
9902{
9903 LogFlowThisFunc(("aOnline=%d\n", aOnline));
9904
9905 AutoCaller autoCaller(this);
9906 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
9907
9908 AutoMultiWriteLock2 alock(this->lockHandle(),
9909 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
9910
9911 /* must be in a protective state because we release the lock below */
9912 AssertReturn( mData->mMachineState == MachineState_Saving
9913 || mData->mMachineState == MachineState_LiveSnapshotting
9914 || mData->mMachineState == MachineState_RestoringSnapshot
9915 || mData->mMachineState == MachineState_DeletingSnapshot
9916 , E_FAIL);
9917
9918 HRESULT rc = S_OK;
9919
9920 MediumLockListMap lockedMediaOffline;
9921 MediumLockListMap *lockedMediaMap;
9922 if (aOnline)
9923 lockedMediaMap = &mData->mSession.mLockedMedia;
9924 else
9925 lockedMediaMap = &lockedMediaOffline;
9926
9927 try
9928 {
9929 if (!aOnline)
9930 {
9931 /* lock all attached hard disks early to detect "in use"
9932 * situations before creating actual diffs */
9933 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
9934 it != mMediaData->mAttachments.end();
9935 ++it)
9936 {
9937 MediumAttachment* pAtt = *it;
9938 if (pAtt->getType() == DeviceType_HardDisk)
9939 {
9940 Medium* pMedium = pAtt->getMedium();
9941 Assert(pMedium);
9942
9943 MediumLockList *pMediumLockList(new MediumLockList());
9944 alock.release();
9945 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
9946 false /* fMediumLockWrite */,
9947 NULL,
9948 *pMediumLockList);
9949 alock.acquire();
9950 if (FAILED(rc))
9951 {
9952 delete pMediumLockList;
9953 throw rc;
9954 }
9955 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
9956 if (FAILED(rc))
9957 {
9958 throw setError(rc,
9959 tr("Collecting locking information for all attached media failed"));
9960 }
9961 }
9962 }
9963
9964 /* Now lock all media. If this fails, nothing is locked. */
9965 alock.release();
9966 rc = lockedMediaMap->Lock();
9967 alock.acquire();
9968 if (FAILED(rc))
9969 {
9970 throw setError(rc,
9971 tr("Locking of attached media failed"));
9972 }
9973 }
9974
9975 /* remember the current list (note that we don't use backup() since
9976 * mMediaData may be already backed up) */
9977 MediaData::AttachmentList atts = mMediaData->mAttachments;
9978
9979 /* start from scratch */
9980 mMediaData->mAttachments.clear();
9981
9982 /* go through remembered attachments and create diffs for normal hard
9983 * disks and attach them */
9984 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9985 it != atts.end();
9986 ++it)
9987 {
9988 MediumAttachment* pAtt = *it;
9989
9990 DeviceType_T devType = pAtt->getType();
9991 Medium* pMedium = pAtt->getMedium();
9992
9993 if ( devType != DeviceType_HardDisk
9994 || pMedium == NULL
9995 || pMedium->getType() != MediumType_Normal)
9996 {
9997 /* copy the attachment as is */
9998
9999 /** @todo the progress object created in Console::TakeSnaphot
10000 * only expects operations for hard disks. Later other
10001 * device types need to show up in the progress as well. */
10002 if (devType == DeviceType_HardDisk)
10003 {
10004 if (pMedium == NULL)
10005 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10006 aWeight); // weight
10007 else
10008 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10009 pMedium->getBase()->getName().c_str()).raw(),
10010 aWeight); // weight
10011 }
10012
10013 mMediaData->mAttachments.push_back(pAtt);
10014 continue;
10015 }
10016
10017 /* need a diff */
10018 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10019 pMedium->getBase()->getName().c_str()).raw(),
10020 aWeight); // weight
10021
10022 Utf8Str strFullSnapshotFolder;
10023 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10024
10025 ComObjPtr<Medium> diff;
10026 diff.createObject();
10027 // store the diff in the same registry as the parent
10028 // (this cannot fail here because we can't create implicit diffs for
10029 // unregistered images)
10030 Guid uuidRegistryParent;
10031 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10032 Assert(fInRegistry); NOREF(fInRegistry);
10033 rc = diff->init(mParent,
10034 pMedium->getPreferredDiffFormat(),
10035 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10036 uuidRegistryParent);
10037 if (FAILED(rc)) throw rc;
10038
10039 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10040 * the push_back? Looks like we're going to release medium with the
10041 * wrong kind of lock (general issue with if we fail anywhere at all)
10042 * and an orphaned VDI in the snapshots folder. */
10043
10044 /* update the appropriate lock list */
10045 MediumLockList *pMediumLockList;
10046 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10047 AssertComRCThrowRC(rc);
10048 if (aOnline)
10049 {
10050 alock.release();
10051 rc = pMediumLockList->Update(pMedium, false);
10052 alock.acquire();
10053 AssertComRCThrowRC(rc);
10054 }
10055
10056 /* release the locks before the potentially lengthy operation */
10057 alock.release();
10058 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10059 pMediumLockList,
10060 NULL /* aProgress */,
10061 true /* aWait */);
10062 alock.acquire();
10063 if (FAILED(rc)) throw rc;
10064
10065 rc = lockedMediaMap->Unlock();
10066 AssertComRCThrowRC(rc);
10067 alock.release();
10068 rc = pMediumLockList->Append(diff, true);
10069 alock.acquire();
10070 AssertComRCThrowRC(rc);
10071 alock.release();
10072 rc = lockedMediaMap->Lock();
10073 alock.acquire();
10074 AssertComRCThrowRC(rc);
10075
10076 rc = diff->addBackReference(mData->mUuid);
10077 AssertComRCThrowRC(rc);
10078
10079 /* add a new attachment */
10080 ComObjPtr<MediumAttachment> attachment;
10081 attachment.createObject();
10082 rc = attachment->init(this,
10083 diff,
10084 pAtt->getControllerName(),
10085 pAtt->getPort(),
10086 pAtt->getDevice(),
10087 DeviceType_HardDisk,
10088 true /* aImplicit */,
10089 false /* aPassthrough */,
10090 false /* aTempEject */,
10091 pAtt->getNonRotational(),
10092 pAtt->getDiscard(),
10093 pAtt->getBandwidthGroup());
10094 if (FAILED(rc)) throw rc;
10095
10096 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10097 AssertComRCThrowRC(rc);
10098 mMediaData->mAttachments.push_back(attachment);
10099 }
10100 }
10101 catch (HRESULT aRC) { rc = aRC; }
10102
10103 /* unlock all hard disks we locked */
10104 if (!aOnline)
10105 {
10106 ErrorInfoKeeper eik;
10107
10108 HRESULT rc1 = lockedMediaMap->Clear();
10109 AssertComRC(rc1);
10110 }
10111
10112 if (FAILED(rc))
10113 {
10114 MultiResult mrc = rc;
10115
10116 alock.release();
10117 mrc = deleteImplicitDiffs();
10118 }
10119
10120 return rc;
10121}
10122
10123/**
10124 * Deletes implicit differencing hard disks created either by
10125 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10126 *
10127 * Note that to delete hard disks created by #AttachDevice() this method is
10128 * called from #fixupMedia() when the changes are rolled back.
10129 *
10130 * @note Locks this object for writing.
10131 */
10132HRESULT Machine::deleteImplicitDiffs()
10133{
10134 AutoCaller autoCaller(this);
10135 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10136
10137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10138 LogFlowThisFuncEnter();
10139
10140 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10141
10142 HRESULT rc = S_OK;
10143
10144 MediaData::AttachmentList implicitAtts;
10145
10146 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10147
10148 /* enumerate new attachments */
10149 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10150 it != mMediaData->mAttachments.end();
10151 ++it)
10152 {
10153 ComObjPtr<Medium> hd = (*it)->getMedium();
10154 if (hd.isNull())
10155 continue;
10156
10157 if ((*it)->isImplicit())
10158 {
10159 /* deassociate and mark for deletion */
10160 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
10161 rc = hd->removeBackReference(mData->mUuid);
10162 AssertComRC(rc);
10163 implicitAtts.push_back(*it);
10164 continue;
10165 }
10166
10167 /* was this hard disk attached before? */
10168 if (!findAttachment(oldAtts, hd))
10169 {
10170 /* no: de-associate */
10171 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
10172 rc = hd->removeBackReference(mData->mUuid);
10173 AssertComRC(rc);
10174 continue;
10175 }
10176 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
10177 }
10178
10179 /* rollback hard disk changes */
10180 mMediaData.rollback();
10181
10182 MultiResult mrc(S_OK);
10183
10184 /* delete unused implicit diffs */
10185 if (implicitAtts.size() != 0)
10186 {
10187 /* will release the lock before the potentially lengthy
10188 * operation, so protect with the special state (unless already
10189 * protected) */
10190 MachineState_T oldState = mData->mMachineState;
10191 if ( oldState != MachineState_Saving
10192 && oldState != MachineState_LiveSnapshotting
10193 && oldState != MachineState_RestoringSnapshot
10194 && oldState != MachineState_DeletingSnapshot
10195 && oldState != MachineState_DeletingSnapshotOnline
10196 && oldState != MachineState_DeletingSnapshotPaused
10197 )
10198 setMachineState(MachineState_SettingUp);
10199
10200 alock.release();
10201
10202 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10203 it != implicitAtts.end();
10204 ++it)
10205 {
10206 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
10207 ComObjPtr<Medium> hd = (*it)->getMedium();
10208
10209 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10210 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
10211 mrc = rc;
10212 }
10213
10214 alock.acquire();
10215
10216 if (mData->mMachineState == MachineState_SettingUp)
10217 setMachineState(oldState);
10218 }
10219
10220 return mrc;
10221}
10222
10223/**
10224 * Looks through the given list of media attachments for one with the given parameters
10225 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10226 * can be searched as well if needed.
10227 *
10228 * @param list
10229 * @param aControllerName
10230 * @param aControllerPort
10231 * @param aDevice
10232 * @return
10233 */
10234MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10235 IN_BSTR aControllerName,
10236 LONG aControllerPort,
10237 LONG aDevice)
10238{
10239 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10240 it != ll.end();
10241 ++it)
10242 {
10243 MediumAttachment *pAttach = *it;
10244 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10245 return pAttach;
10246 }
10247
10248 return NULL;
10249}
10250
10251/**
10252 * Looks through the given list of media attachments for one with the given parameters
10253 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10254 * can be searched as well if needed.
10255 *
10256 * @param list
10257 * @param aControllerName
10258 * @param aControllerPort
10259 * @param aDevice
10260 * @return
10261 */
10262MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10263 ComObjPtr<Medium> pMedium)
10264{
10265 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10266 it != ll.end();
10267 ++it)
10268 {
10269 MediumAttachment *pAttach = *it;
10270 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10271 if (pMediumThis == pMedium)
10272 return pAttach;
10273 }
10274
10275 return NULL;
10276}
10277
10278/**
10279 * Looks through the given list of media attachments for one with the given parameters
10280 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10281 * can be searched as well if needed.
10282 *
10283 * @param list
10284 * @param aControllerName
10285 * @param aControllerPort
10286 * @param aDevice
10287 * @return
10288 */
10289MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10290 Guid &id)
10291{
10292 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10293 it != ll.end();
10294 ++it)
10295 {
10296 MediumAttachment *pAttach = *it;
10297 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10298 if (pMediumThis->getId() == id)
10299 return pAttach;
10300 }
10301
10302 return NULL;
10303}
10304
10305/**
10306 * Main implementation for Machine::DetachDevice. This also gets called
10307 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10308 *
10309 * @param pAttach Medium attachment to detach.
10310 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10311 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10312 * @return
10313 */
10314HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10315 AutoWriteLock &writeLock,
10316 Snapshot *pSnapshot)
10317{
10318 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10319 DeviceType_T mediumType = pAttach->getType();
10320
10321 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10322
10323 if (pAttach->isImplicit())
10324 {
10325 /* attempt to implicitly delete the implicitly created diff */
10326
10327 /// @todo move the implicit flag from MediumAttachment to Medium
10328 /// and forbid any hard disk operation when it is implicit. Or maybe
10329 /// a special media state for it to make it even more simple.
10330
10331 Assert(mMediaData.isBackedUp());
10332
10333 /* will release the lock before the potentially lengthy operation, so
10334 * protect with the special state */
10335 MachineState_T oldState = mData->mMachineState;
10336 setMachineState(MachineState_SettingUp);
10337
10338 writeLock.release();
10339
10340 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10341 true /*aWait*/);
10342
10343 writeLock.acquire();
10344
10345 setMachineState(oldState);
10346
10347 if (FAILED(rc)) return rc;
10348 }
10349
10350 setModified(IsModified_Storage);
10351 mMediaData.backup();
10352 mMediaData->mAttachments.remove(pAttach);
10353
10354 if (!oldmedium.isNull())
10355 {
10356 // if this is from a snapshot, do not defer detachment to commitMedia()
10357 if (pSnapshot)
10358 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10359 // else if non-hard disk media, do not defer detachment to commitMedia() either
10360 else if (mediumType != DeviceType_HardDisk)
10361 oldmedium->removeBackReference(mData->mUuid);
10362 }
10363
10364 return S_OK;
10365}
10366
10367/**
10368 * Goes thru all media of the given list and
10369 *
10370 * 1) calls detachDevice() on each of them for this machine and
10371 * 2) adds all Medium objects found in the process to the given list,
10372 * depending on cleanupMode.
10373 *
10374 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10375 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10376 * media to the list.
10377 *
10378 * This gets called from Machine::Unregister, both for the actual Machine and
10379 * the SnapshotMachine objects that might be found in the snapshots.
10380 *
10381 * Requires caller and locking. The machine lock must be passed in because it
10382 * will be passed on to detachDevice which needs it for temporary unlocking.
10383 *
10384 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10385 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10386 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10387 * otherwise no media get added.
10388 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10389 * @return
10390 */
10391HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10392 Snapshot *pSnapshot,
10393 CleanupMode_T cleanupMode,
10394 MediaList &llMedia)
10395{
10396 Assert(isWriteLockOnCurrentThread());
10397
10398 HRESULT rc;
10399
10400 // make a temporary list because detachDevice invalidates iterators into
10401 // mMediaData->mAttachments
10402 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10403
10404 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10405 it != llAttachments2.end();
10406 ++it)
10407 {
10408 ComObjPtr<MediumAttachment> &pAttach = *it;
10409 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10410
10411 if (!pMedium.isNull())
10412 {
10413 AutoCaller mac(pMedium);
10414 if (FAILED(mac.rc())) return mac.rc();
10415 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10416 DeviceType_T devType = pMedium->getDeviceType();
10417 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10418 && devType == DeviceType_HardDisk)
10419 || (cleanupMode == CleanupMode_Full)
10420 )
10421 {
10422 llMedia.push_back(pMedium);
10423 ComObjPtr<Medium> pParent = pMedium->getParent();
10424 /*
10425 * Search for medias which are not attached to any machine, but
10426 * in the chain to an attached disk. Mediums are only consided
10427 * if they are:
10428 * - have only one child
10429 * - no references to any machines
10430 * - are of normal medium type
10431 */
10432 while (!pParent.isNull())
10433 {
10434 AutoCaller mac1(pParent);
10435 if (FAILED(mac1.rc())) return mac1.rc();
10436 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10437 if (pParent->getChildren().size() == 1)
10438 {
10439 if ( pParent->getMachineBackRefCount() == 0
10440 && pParent->getType() == MediumType_Normal
10441 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10442 llMedia.push_back(pParent);
10443 }else
10444 break;
10445 pParent = pParent->getParent();
10446 }
10447 }
10448 }
10449
10450 // real machine: then we need to use the proper method
10451 rc = detachDevice(pAttach, writeLock, pSnapshot);
10452
10453 if (FAILED(rc))
10454 return rc;
10455 }
10456
10457 return S_OK;
10458}
10459
10460/**
10461 * Perform deferred hard disk detachments.
10462 *
10463 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10464 * backed up).
10465 *
10466 * If @a aOnline is @c true then this method will also unlock the old hard disks
10467 * for which the new implicit diffs were created and will lock these new diffs for
10468 * writing.
10469 *
10470 * @param aOnline Whether the VM was online prior to this operation.
10471 *
10472 * @note Locks this object for writing!
10473 */
10474void Machine::commitMedia(bool aOnline /*= false*/)
10475{
10476 AutoCaller autoCaller(this);
10477 AssertComRCReturnVoid(autoCaller.rc());
10478
10479 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10480
10481 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10482
10483 HRESULT rc = S_OK;
10484
10485 /* no attach/detach operations -- nothing to do */
10486 if (!mMediaData.isBackedUp())
10487 return;
10488
10489 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10490 bool fMediaNeedsLocking = false;
10491
10492 /* enumerate new attachments */
10493 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10494 it != mMediaData->mAttachments.end();
10495 ++it)
10496 {
10497 MediumAttachment *pAttach = *it;
10498
10499 pAttach->commit();
10500
10501 Medium* pMedium = pAttach->getMedium();
10502 bool fImplicit = pAttach->isImplicit();
10503
10504 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10505 (pMedium) ? pMedium->getName().c_str() : "NULL",
10506 fImplicit));
10507
10508 /** @todo convert all this Machine-based voodoo to MediumAttachment
10509 * based commit logic. */
10510 if (fImplicit)
10511 {
10512 /* convert implicit attachment to normal */
10513 pAttach->setImplicit(false);
10514
10515 if ( aOnline
10516 && pMedium
10517 && pAttach->getType() == DeviceType_HardDisk
10518 )
10519 {
10520 ComObjPtr<Medium> parent = pMedium->getParent();
10521 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10522
10523 /* update the appropriate lock list */
10524 MediumLockList *pMediumLockList;
10525 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10526 AssertComRC(rc);
10527 if (pMediumLockList)
10528 {
10529 /* unlock if there's a need to change the locking */
10530 if (!fMediaNeedsLocking)
10531 {
10532 rc = mData->mSession.mLockedMedia.Unlock();
10533 AssertComRC(rc);
10534 fMediaNeedsLocking = true;
10535 }
10536 rc = pMediumLockList->Update(parent, false);
10537 AssertComRC(rc);
10538 rc = pMediumLockList->Append(pMedium, true);
10539 AssertComRC(rc);
10540 }
10541 }
10542
10543 continue;
10544 }
10545
10546 if (pMedium)
10547 {
10548 /* was this medium attached before? */
10549 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10550 oldIt != oldAtts.end();
10551 ++oldIt)
10552 {
10553 MediumAttachment *pOldAttach = *oldIt;
10554 if (pOldAttach->getMedium() == pMedium)
10555 {
10556 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10557
10558 /* yes: remove from old to avoid de-association */
10559 oldAtts.erase(oldIt);
10560 break;
10561 }
10562 }
10563 }
10564 }
10565
10566 /* enumerate remaining old attachments and de-associate from the
10567 * current machine state */
10568 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10569 it != oldAtts.end();
10570 ++it)
10571 {
10572 MediumAttachment *pAttach = *it;
10573 Medium* pMedium = pAttach->getMedium();
10574
10575 /* Detach only hard disks, since DVD/floppy media is detached
10576 * instantly in MountMedium. */
10577 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10578 {
10579 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10580
10581 /* now de-associate from the current machine state */
10582 rc = pMedium->removeBackReference(mData->mUuid);
10583 AssertComRC(rc);
10584
10585 if (aOnline)
10586 {
10587 /* unlock since medium is not used anymore */
10588 MediumLockList *pMediumLockList;
10589 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10590 AssertComRC(rc);
10591 if (pMediumLockList)
10592 {
10593 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10594 AssertComRC(rc);
10595 }
10596 }
10597 }
10598 }
10599
10600 /* take media locks again so that the locking state is consistent */
10601 if (fMediaNeedsLocking)
10602 {
10603 Assert(aOnline);
10604 rc = mData->mSession.mLockedMedia.Lock();
10605 AssertComRC(rc);
10606 }
10607
10608 /* commit the hard disk changes */
10609 mMediaData.commit();
10610
10611 if (isSessionMachine())
10612 {
10613 /*
10614 * Update the parent machine to point to the new owner.
10615 * This is necessary because the stored parent will point to the
10616 * session machine otherwise and cause crashes or errors later
10617 * when the session machine gets invalid.
10618 */
10619 /** @todo Change the MediumAttachment class to behave like any other
10620 * class in this regard by creating peer MediumAttachment
10621 * objects for session machines and share the data with the peer
10622 * machine.
10623 */
10624 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10625 it != mMediaData->mAttachments.end();
10626 ++it)
10627 {
10628 (*it)->updateParentMachine(mPeer);
10629 }
10630
10631 /* attach new data to the primary machine and reshare it */
10632 mPeer->mMediaData.attach(mMediaData);
10633 }
10634
10635 return;
10636}
10637
10638/**
10639 * Perform deferred deletion of implicitly created diffs.
10640 *
10641 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10642 * backed up).
10643 *
10644 * @note Locks this object for writing!
10645 */
10646void Machine::rollbackMedia()
10647{
10648 AutoCaller autoCaller(this);
10649 AssertComRCReturnVoid (autoCaller.rc());
10650
10651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10652
10653 LogFlowThisFunc(("Entering\n"));
10654
10655 HRESULT rc = S_OK;
10656
10657 /* no attach/detach operations -- nothing to do */
10658 if (!mMediaData.isBackedUp())
10659 return;
10660
10661 /* enumerate new attachments */
10662 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10663 it != mMediaData->mAttachments.end();
10664 ++it)
10665 {
10666 MediumAttachment *pAttach = *it;
10667 /* Fix up the backrefs for DVD/floppy media. */
10668 if (pAttach->getType() != DeviceType_HardDisk)
10669 {
10670 Medium* pMedium = pAttach->getMedium();
10671 if (pMedium)
10672 {
10673 rc = pMedium->removeBackReference(mData->mUuid);
10674 AssertComRC(rc);
10675 }
10676 }
10677
10678 (*it)->rollback();
10679
10680 pAttach = *it;
10681 /* Fix up the backrefs for DVD/floppy media. */
10682 if (pAttach->getType() != DeviceType_HardDisk)
10683 {
10684 Medium* pMedium = pAttach->getMedium();
10685 if (pMedium)
10686 {
10687 rc = pMedium->addBackReference(mData->mUuid);
10688 AssertComRC(rc);
10689 }
10690 }
10691 }
10692
10693 /** @todo convert all this Machine-based voodoo to MediumAttachment
10694 * based rollback logic. */
10695 deleteImplicitDiffs();
10696
10697 return;
10698}
10699
10700/**
10701 * Returns true if the settings file is located in the directory named exactly
10702 * as the machine; this means, among other things, that the machine directory
10703 * should be auto-renamed.
10704 *
10705 * @param aSettingsDir if not NULL, the full machine settings file directory
10706 * name will be assigned there.
10707 *
10708 * @note Doesn't lock anything.
10709 * @note Not thread safe (must be called from this object's lock).
10710 */
10711bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
10712{
10713 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10714 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
10715 if (aSettingsDir)
10716 *aSettingsDir = strMachineDirName;
10717 strMachineDirName.stripPath(); // vmname
10718 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10719 strConfigFileOnly.stripPath() // vmname.vbox
10720 .stripExt(); // vmname
10721
10722 AssertReturn(!strMachineDirName.isEmpty(), false);
10723 AssertReturn(!strConfigFileOnly.isEmpty(), false);
10724
10725 return strMachineDirName == strConfigFileOnly;
10726}
10727
10728/**
10729 * Discards all changes to machine settings.
10730 *
10731 * @param aNotify Whether to notify the direct session about changes or not.
10732 *
10733 * @note Locks objects for writing!
10734 */
10735void Machine::rollback(bool aNotify)
10736{
10737 AutoCaller autoCaller(this);
10738 AssertComRCReturn(autoCaller.rc(), (void)0);
10739
10740 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10741
10742 if (!mStorageControllers.isNull())
10743 {
10744 if (mStorageControllers.isBackedUp())
10745 {
10746 /* unitialize all new devices (absent in the backed up list). */
10747 StorageControllerList::const_iterator it = mStorageControllers->begin();
10748 StorageControllerList *backedList = mStorageControllers.backedUpData();
10749 while (it != mStorageControllers->end())
10750 {
10751 if ( std::find(backedList->begin(), backedList->end(), *it)
10752 == backedList->end()
10753 )
10754 {
10755 (*it)->uninit();
10756 }
10757 ++it;
10758 }
10759
10760 /* restore the list */
10761 mStorageControllers.rollback();
10762 }
10763
10764 /* rollback any changes to devices after restoring the list */
10765 if (mData->flModifications & IsModified_Storage)
10766 {
10767 StorageControllerList::const_iterator it = mStorageControllers->begin();
10768 while (it != mStorageControllers->end())
10769 {
10770 (*it)->rollback();
10771 ++it;
10772 }
10773 }
10774 }
10775
10776 mUserData.rollback();
10777
10778 mHWData.rollback();
10779
10780 if (mData->flModifications & IsModified_Storage)
10781 rollbackMedia();
10782
10783 if (mBIOSSettings)
10784 mBIOSSettings->rollback();
10785
10786 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
10787 mVRDEServer->rollback();
10788
10789 if (mAudioAdapter)
10790 mAudioAdapter->rollback();
10791
10792 if (mUSBController && (mData->flModifications & IsModified_USB))
10793 mUSBController->rollback();
10794
10795 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
10796 mBandwidthControl->rollback();
10797
10798 if (!mHWData.isNull())
10799 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10800 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
10801 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
10802 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
10803
10804 if (mData->flModifications & IsModified_NetworkAdapters)
10805 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10806 if ( mNetworkAdapters[slot]
10807 && mNetworkAdapters[slot]->isModified())
10808 {
10809 mNetworkAdapters[slot]->rollback();
10810 networkAdapters[slot] = mNetworkAdapters[slot];
10811 }
10812
10813 if (mData->flModifications & IsModified_SerialPorts)
10814 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10815 if ( mSerialPorts[slot]
10816 && mSerialPorts[slot]->isModified())
10817 {
10818 mSerialPorts[slot]->rollback();
10819 serialPorts[slot] = mSerialPorts[slot];
10820 }
10821
10822 if (mData->flModifications & IsModified_ParallelPorts)
10823 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10824 if ( mParallelPorts[slot]
10825 && mParallelPorts[slot]->isModified())
10826 {
10827 mParallelPorts[slot]->rollback();
10828 parallelPorts[slot] = mParallelPorts[slot];
10829 }
10830
10831 if (aNotify)
10832 {
10833 /* inform the direct session about changes */
10834
10835 ComObjPtr<Machine> that = this;
10836 uint32_t flModifications = mData->flModifications;
10837 alock.release();
10838
10839 if (flModifications & IsModified_SharedFolders)
10840 that->onSharedFolderChange();
10841
10842 if (flModifications & IsModified_VRDEServer)
10843 that->onVRDEServerChange(/* aRestart */ TRUE);
10844 if (flModifications & IsModified_USB)
10845 that->onUSBControllerChange();
10846
10847 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
10848 if (networkAdapters[slot])
10849 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
10850 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
10851 if (serialPorts[slot])
10852 that->onSerialPortChange(serialPorts[slot]);
10853 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
10854 if (parallelPorts[slot])
10855 that->onParallelPortChange(parallelPorts[slot]);
10856
10857 if (flModifications & IsModified_Storage)
10858 that->onStorageControllerChange();
10859
10860#if 0
10861 if (flModifications & IsModified_BandwidthControl)
10862 that->onBandwidthControlChange();
10863#endif
10864 }
10865}
10866
10867/**
10868 * Commits all the changes to machine settings.
10869 *
10870 * Note that this operation is supposed to never fail.
10871 *
10872 * @note Locks this object and children for writing.
10873 */
10874void Machine::commit()
10875{
10876 AutoCaller autoCaller(this);
10877 AssertComRCReturnVoid(autoCaller.rc());
10878
10879 AutoCaller peerCaller(mPeer);
10880 AssertComRCReturnVoid(peerCaller.rc());
10881
10882 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
10883
10884 /*
10885 * use safe commit to ensure Snapshot machines (that share mUserData)
10886 * will still refer to a valid memory location
10887 */
10888 mUserData.commitCopy();
10889
10890 mHWData.commit();
10891
10892 if (mMediaData.isBackedUp())
10893 commitMedia();
10894
10895 mBIOSSettings->commit();
10896 mVRDEServer->commit();
10897 mAudioAdapter->commit();
10898 mUSBController->commit();
10899 mBandwidthControl->commit();
10900
10901 /* Keep the original network adapter count until this point, so that
10902 * discarding a chipset type change will not lose settings. */
10903 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
10904 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
10905 mNetworkAdapters[slot]->commit();
10906 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
10907 mSerialPorts[slot]->commit();
10908 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
10909 mParallelPorts[slot]->commit();
10910
10911 bool commitStorageControllers = false;
10912
10913 if (mStorageControllers.isBackedUp())
10914 {
10915 mStorageControllers.commit();
10916
10917 if (mPeer)
10918 {
10919 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);
10920
10921 /* Commit all changes to new controllers (this will reshare data with
10922 * peers for those who have peers) */
10923 StorageControllerList *newList = new StorageControllerList();
10924 StorageControllerList::const_iterator it = mStorageControllers->begin();
10925 while (it != mStorageControllers->end())
10926 {
10927 (*it)->commit();
10928
10929 /* look if this controller has a peer device */
10930 ComObjPtr<StorageController> peer = (*it)->getPeer();
10931 if (!peer)
10932 {
10933 /* no peer means the device is a newly created one;
10934 * create a peer owning data this device share it with */
10935 peer.createObject();
10936 peer->init(mPeer, *it, true /* aReshare */);
10937 }
10938 else
10939 {
10940 /* remove peer from the old list */
10941 mPeer->mStorageControllers->remove(peer);
10942 }
10943 /* and add it to the new list */
10944 newList->push_back(peer);
10945
10946 ++it;
10947 }
10948
10949 /* uninit old peer's controllers that are left */
10950 it = mPeer->mStorageControllers->begin();
10951 while (it != mPeer->mStorageControllers->end())
10952 {
10953 (*it)->uninit();
10954 ++it;
10955 }
10956
10957 /* attach new list of controllers to our peer */
10958 mPeer->mStorageControllers.attach(newList);
10959 }
10960 else
10961 {
10962 /* we have no peer (our parent is the newly created machine);
10963 * just commit changes to devices */
10964 commitStorageControllers = true;
10965 }
10966 }
10967 else
10968 {
10969 /* the list of controllers itself is not changed,
10970 * just commit changes to controllers themselves */
10971 commitStorageControllers = true;
10972 }
10973
10974 if (commitStorageControllers)
10975 {
10976 StorageControllerList::const_iterator it = mStorageControllers->begin();
10977 while (it != mStorageControllers->end())
10978 {
10979 (*it)->commit();
10980 ++it;
10981 }
10982 }
10983
10984 if (isSessionMachine())
10985 {
10986 /* attach new data to the primary machine and reshare it */
10987 mPeer->mUserData.attach(mUserData);
10988 mPeer->mHWData.attach(mHWData);
10989 /* mMediaData is reshared by fixupMedia */
10990 // mPeer->mMediaData.attach(mMediaData);
10991 Assert(mPeer->mMediaData.data() == mMediaData.data());
10992 }
10993}
10994
10995/**
10996 * Copies all the hardware data from the given machine.
10997 *
10998 * Currently, only called when the VM is being restored from a snapshot. In
10999 * particular, this implies that the VM is not running during this method's
11000 * call.
11001 *
11002 * @note This method must be called from under this object's lock.
11003 *
11004 * @note This method doesn't call #commit(), so all data remains backed up and
11005 * unsaved.
11006 */
11007void Machine::copyFrom(Machine *aThat)
11008{
11009 AssertReturnVoid(!isSnapshotMachine());
11010 AssertReturnVoid(aThat->isSnapshotMachine());
11011
11012 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11013
11014 mHWData.assignCopy(aThat->mHWData);
11015
11016 // create copies of all shared folders (mHWData after attaching a copy
11017 // contains just references to original objects)
11018 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11019 it != mHWData->mSharedFolders.end();
11020 ++it)
11021 {
11022 ComObjPtr<SharedFolder> folder;
11023 folder.createObject();
11024 HRESULT rc = folder->initCopy(getMachine(), *it);
11025 AssertComRC(rc);
11026 *it = folder;
11027 }
11028
11029 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11030 mVRDEServer->copyFrom(aThat->mVRDEServer);
11031 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11032 mUSBController->copyFrom(aThat->mUSBController);
11033 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11034
11035 /* create private copies of all controllers */
11036 mStorageControllers.backup();
11037 mStorageControllers->clear();
11038 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11039 it != aThat->mStorageControllers->end();
11040 ++it)
11041 {
11042 ComObjPtr<StorageController> ctrl;
11043 ctrl.createObject();
11044 ctrl->initCopy(this, *it);
11045 mStorageControllers->push_back(ctrl);
11046 }
11047
11048 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11049 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11050 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11051 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11052 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11053 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11054}
11055
11056/**
11057 * Returns whether the given storage controller is hotplug capable.
11058 *
11059 * @returns true if the controller supports hotplugging
11060 * false otherwise.
11061 * @param enmCtrlType The controller type to check for.
11062 */
11063bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11064{
11065 switch (enmCtrlType)
11066 {
11067 case StorageControllerType_IntelAhci:
11068 return true;
11069 case StorageControllerType_LsiLogic:
11070 case StorageControllerType_LsiLogicSas:
11071 case StorageControllerType_BusLogic:
11072 case StorageControllerType_PIIX3:
11073 case StorageControllerType_PIIX4:
11074 case StorageControllerType_ICH6:
11075 case StorageControllerType_I82078:
11076 default:
11077 return false;
11078 }
11079}
11080
11081#ifdef VBOX_WITH_RESOURCE_USAGE_API
11082
11083void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11084{
11085 AssertReturnVoid(isWriteLockOnCurrentThread());
11086 AssertPtrReturnVoid(aCollector);
11087
11088 pm::CollectorHAL *hal = aCollector->getHAL();
11089 /* Create sub metrics */
11090 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11091 "Percentage of processor time spent in user mode by the VM process.");
11092 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11093 "Percentage of processor time spent in kernel mode by the VM process.");
11094 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11095 "Size of resident portion of VM process in memory.");
11096 /* Create and register base metrics */
11097 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11098 cpuLoadUser, cpuLoadKernel);
11099 aCollector->registerBaseMetric(cpuLoad);
11100 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11101 ramUsageUsed);
11102 aCollector->registerBaseMetric(ramUsage);
11103
11104 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11105 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11106 new pm::AggregateAvg()));
11107 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11108 new pm::AggregateMin()));
11109 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11110 new pm::AggregateMax()));
11111 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11112 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11113 new pm::AggregateAvg()));
11114 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11115 new pm::AggregateMin()));
11116 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11117 new pm::AggregateMax()));
11118
11119 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11120 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11121 new pm::AggregateAvg()));
11122 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11123 new pm::AggregateMin()));
11124 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11125 new pm::AggregateMax()));
11126
11127
11128 /* Guest metrics collector */
11129 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11130 aCollector->registerGuest(mCollectorGuest);
11131 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11132 this, __PRETTY_FUNCTION__, mCollectorGuest));
11133
11134 /* Create sub metrics */
11135 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11136 "Percentage of processor time spent in user mode as seen by the guest.");
11137 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11138 "Percentage of processor time spent in kernel mode as seen by the guest.");
11139 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11140 "Percentage of processor time spent idling as seen by the guest.");
11141
11142 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11143 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11144 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11145 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11146 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11147 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11148
11149 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11150
11151 /* Create and register base metrics */
11152 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11153 guestLoadUser, guestLoadKernel, guestLoadIdle);
11154 aCollector->registerBaseMetric(guestCpuLoad);
11155
11156 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11157 guestMemTotal, guestMemFree,
11158 guestMemBalloon, guestMemShared,
11159 guestMemCache, guestPagedTotal);
11160 aCollector->registerBaseMetric(guestCpuMem);
11161
11162 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11163 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11164 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11165 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11166
11167 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11168 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11169 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11170 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11171
11172 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11173 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11174 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11175 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11176
11177 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11178 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11179 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11180 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11181
11182 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11183 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11184 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11185 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11186
11187 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11188 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11189 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11190 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11191
11192 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11193 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11194 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11195 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11196
11197 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11198 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11199 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11200 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11201
11202 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11203 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11204 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11205 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11206}
11207
11208void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11209{
11210 AssertReturnVoid(isWriteLockOnCurrentThread());
11211
11212 if (aCollector)
11213 {
11214 aCollector->unregisterMetricsFor(aMachine);
11215 aCollector->unregisterBaseMetricsFor(aMachine);
11216 }
11217}
11218
11219#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11220
11221
11222////////////////////////////////////////////////////////////////////////////////
11223
11224DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11225
11226HRESULT SessionMachine::FinalConstruct()
11227{
11228 LogFlowThisFunc(("\n"));
11229
11230#if defined(RT_OS_WINDOWS)
11231 mIPCSem = NULL;
11232#elif defined(RT_OS_OS2)
11233 mIPCSem = NULLHANDLE;
11234#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11235 mIPCSem = -1;
11236#else
11237# error "Port me!"
11238#endif
11239
11240 return BaseFinalConstruct();
11241}
11242
11243void SessionMachine::FinalRelease()
11244{
11245 LogFlowThisFunc(("\n"));
11246
11247 uninit(Uninit::Unexpected);
11248
11249 BaseFinalRelease();
11250}
11251
11252/**
11253 * @note Must be called only by Machine::openSession() from its own write lock.
11254 */
11255HRESULT SessionMachine::init(Machine *aMachine)
11256{
11257 LogFlowThisFuncEnter();
11258 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11259
11260 AssertReturn(aMachine, E_INVALIDARG);
11261
11262 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11263
11264 /* Enclose the state transition NotReady->InInit->Ready */
11265 AutoInitSpan autoInitSpan(this);
11266 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11267
11268 /* create the interprocess semaphore */
11269#if defined(RT_OS_WINDOWS)
11270 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11271 for (size_t i = 0; i < mIPCSemName.length(); i++)
11272 if (mIPCSemName.raw()[i] == '\\')
11273 mIPCSemName.raw()[i] = '/';
11274 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11275 ComAssertMsgRet(mIPCSem,
11276 ("Cannot create IPC mutex '%ls', err=%d",
11277 mIPCSemName.raw(), ::GetLastError()),
11278 E_FAIL);
11279#elif defined(RT_OS_OS2)
11280 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11281 aMachine->mData->mUuid.raw());
11282 mIPCSemName = ipcSem;
11283 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11284 ComAssertMsgRet(arc == NO_ERROR,
11285 ("Cannot create IPC mutex '%s', arc=%ld",
11286 ipcSem.c_str(), arc),
11287 E_FAIL);
11288#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11289# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11290# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11291 /** @todo Check that this still works correctly. */
11292 AssertCompileSize(key_t, 8);
11293# else
11294 AssertCompileSize(key_t, 4);
11295# endif
11296 key_t key;
11297 mIPCSem = -1;
11298 mIPCKey = "0";
11299 for (uint32_t i = 0; i < 1 << 24; i++)
11300 {
11301 key = ((uint32_t)'V' << 24) | i;
11302 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11303 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11304 {
11305 mIPCSem = sem;
11306 if (sem >= 0)
11307 mIPCKey = BstrFmt("%u", key);
11308 break;
11309 }
11310 }
11311# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11312 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11313 char *pszSemName = NULL;
11314 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11315 key_t key = ::ftok(pszSemName, 'V');
11316 RTStrFree(pszSemName);
11317
11318 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11319# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11320
11321 int errnoSave = errno;
11322 if (mIPCSem < 0 && errnoSave == ENOSYS)
11323 {
11324 setError(E_FAIL,
11325 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11326 "support for SysV IPC. Check the host kernel configuration for "
11327 "CONFIG_SYSVIPC=y"));
11328 return E_FAIL;
11329 }
11330 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11331 * the IPC semaphores */
11332 if (mIPCSem < 0 && errnoSave == ENOSPC)
11333 {
11334#ifdef RT_OS_LINUX
11335 setError(E_FAIL,
11336 tr("Cannot create IPC semaphore because the system limit for the "
11337 "maximum number of semaphore sets (SEMMNI), or the system wide "
11338 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11339 "current set of SysV IPC semaphores can be determined from "
11340 "the file /proc/sysvipc/sem"));
11341#else
11342 setError(E_FAIL,
11343 tr("Cannot create IPC semaphore because the system-imposed limit "
11344 "on the maximum number of allowed semaphores or semaphore "
11345 "identifiers system-wide would be exceeded"));
11346#endif
11347 return E_FAIL;
11348 }
11349 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11350 E_FAIL);
11351 /* set the initial value to 1 */
11352 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11353 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11354 E_FAIL);
11355#else
11356# error "Port me!"
11357#endif
11358
11359 /* memorize the peer Machine */
11360 unconst(mPeer) = aMachine;
11361 /* share the parent pointer */
11362 unconst(mParent) = aMachine->mParent;
11363
11364 /* take the pointers to data to share */
11365 mData.share(aMachine->mData);
11366 mSSData.share(aMachine->mSSData);
11367
11368 mUserData.share(aMachine->mUserData);
11369 mHWData.share(aMachine->mHWData);
11370 mMediaData.share(aMachine->mMediaData);
11371
11372 mStorageControllers.allocate();
11373 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11374 it != aMachine->mStorageControllers->end();
11375 ++it)
11376 {
11377 ComObjPtr<StorageController> ctl;
11378 ctl.createObject();
11379 ctl->init(this, *it);
11380 mStorageControllers->push_back(ctl);
11381 }
11382
11383 unconst(mBIOSSettings).createObject();
11384 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11385 /* create another VRDEServer object that will be mutable */
11386 unconst(mVRDEServer).createObject();
11387 mVRDEServer->init(this, aMachine->mVRDEServer);
11388 /* create another audio adapter object that will be mutable */
11389 unconst(mAudioAdapter).createObject();
11390 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11391 /* create a list of serial ports that will be mutable */
11392 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11393 {
11394 unconst(mSerialPorts[slot]).createObject();
11395 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11396 }
11397 /* create a list of parallel ports that will be mutable */
11398 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11399 {
11400 unconst(mParallelPorts[slot]).createObject();
11401 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11402 }
11403 /* create another USB controller object that will be mutable */
11404 unconst(mUSBController).createObject();
11405 mUSBController->init(this, aMachine->mUSBController);
11406
11407 /* create a list of network adapters that will be mutable */
11408 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11409 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11410 {
11411 unconst(mNetworkAdapters[slot]).createObject();
11412 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11413 }
11414
11415 /* create another bandwidth control object that will be mutable */
11416 unconst(mBandwidthControl).createObject();
11417 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11418
11419 /* default is to delete saved state on Saved -> PoweredOff transition */
11420 mRemoveSavedState = true;
11421
11422 /* Confirm a successful initialization when it's the case */
11423 autoInitSpan.setSucceeded();
11424
11425 LogFlowThisFuncLeave();
11426 return S_OK;
11427}
11428
11429/**
11430 * Uninitializes this session object. If the reason is other than
11431 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11432 *
11433 * @param aReason uninitialization reason
11434 *
11435 * @note Locks mParent + this object for writing.
11436 */
11437void SessionMachine::uninit(Uninit::Reason aReason)
11438{
11439 LogFlowThisFuncEnter();
11440 LogFlowThisFunc(("reason=%d\n", aReason));
11441
11442 /*
11443 * Strongly reference ourselves to prevent this object deletion after
11444 * mData->mSession.mMachine.setNull() below (which can release the last
11445 * reference and call the destructor). Important: this must be done before
11446 * accessing any members (and before AutoUninitSpan that does it as well).
11447 * This self reference will be released as the very last step on return.
11448 */
11449 ComObjPtr<SessionMachine> selfRef = this;
11450
11451 /* Enclose the state transition Ready->InUninit->NotReady */
11452 AutoUninitSpan autoUninitSpan(this);
11453 if (autoUninitSpan.uninitDone())
11454 {
11455 LogFlowThisFunc(("Already uninitialized\n"));
11456 LogFlowThisFuncLeave();
11457 return;
11458 }
11459
11460 if (autoUninitSpan.initFailed())
11461 {
11462 /* We've been called by init() because it's failed. It's not really
11463 * necessary (nor it's safe) to perform the regular uninit sequence
11464 * below, the following is enough.
11465 */
11466 LogFlowThisFunc(("Initialization failed.\n"));
11467#if defined(RT_OS_WINDOWS)
11468 if (mIPCSem)
11469 ::CloseHandle(mIPCSem);
11470 mIPCSem = NULL;
11471#elif defined(RT_OS_OS2)
11472 if (mIPCSem != NULLHANDLE)
11473 ::DosCloseMutexSem(mIPCSem);
11474 mIPCSem = NULLHANDLE;
11475#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11476 if (mIPCSem >= 0)
11477 ::semctl(mIPCSem, 0, IPC_RMID);
11478 mIPCSem = -1;
11479# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11480 mIPCKey = "0";
11481# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11482#else
11483# error "Port me!"
11484#endif
11485 uninitDataAndChildObjects();
11486 mData.free();
11487 unconst(mParent) = NULL;
11488 unconst(mPeer) = NULL;
11489 LogFlowThisFuncLeave();
11490 return;
11491 }
11492
11493 MachineState_T lastState;
11494 {
11495 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11496 lastState = mData->mMachineState;
11497 }
11498 NOREF(lastState);
11499
11500#ifdef VBOX_WITH_USB
11501 // release all captured USB devices, but do this before requesting the locks below
11502 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11503 {
11504 /* Console::captureUSBDevices() is called in the VM process only after
11505 * setting the machine state to Starting or Restoring.
11506 * Console::detachAllUSBDevices() will be called upon successful
11507 * termination. So, we need to release USB devices only if there was
11508 * an abnormal termination of a running VM.
11509 *
11510 * This is identical to SessionMachine::DetachAllUSBDevices except
11511 * for the aAbnormal argument. */
11512 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11513 AssertComRC(rc);
11514 NOREF(rc);
11515
11516 USBProxyService *service = mParent->host()->usbProxyService();
11517 if (service)
11518 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11519 }
11520#endif /* VBOX_WITH_USB */
11521
11522 // we need to lock this object in uninit() because the lock is shared
11523 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11524 // and others need mParent lock, and USB needs host lock.
11525 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11526
11527#if 0
11528 // Trigger async cleanup tasks, avoid doing things here which are not
11529 // vital to be done immediately and maybe need more locks. This calls
11530 // Machine::unregisterMetrics().
11531 mParent->onMachineUninit(mPeer);
11532#else
11533 /*
11534 * It is safe to call Machine::unregisterMetrics() here because
11535 * PerformanceCollector::samplerCallback no longer accesses guest methods
11536 * holding the lock.
11537 */
11538 unregisterMetrics(mParent->performanceCollector(), mPeer);
11539#endif
11540 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11541 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11542 this, __PRETTY_FUNCTION__, mCollectorGuest));
11543 if (mCollectorGuest)
11544 {
11545 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11546 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11547 mCollectorGuest = NULL;
11548 }
11549
11550 if (aReason == Uninit::Abnormal)
11551 {
11552 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11553 Global::IsOnlineOrTransient(lastState)));
11554
11555 /* reset the state to Aborted */
11556 if (mData->mMachineState != MachineState_Aborted)
11557 setMachineState(MachineState_Aborted);
11558 }
11559
11560 // any machine settings modified?
11561 if (mData->flModifications)
11562 {
11563 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
11564 rollback(false /* aNotify */);
11565 }
11566
11567 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
11568 || !mConsoleTaskData.mSnapshot);
11569 if (!mConsoleTaskData.strStateFilePath.isEmpty())
11570 {
11571 LogWarningThisFunc(("canceling failed save state request!\n"));
11572 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
11573 }
11574 else if (!mConsoleTaskData.mSnapshot.isNull())
11575 {
11576 LogWarningThisFunc(("canceling untaken snapshot!\n"));
11577
11578 /* delete all differencing hard disks created (this will also attach
11579 * their parents back by rolling back mMediaData) */
11580 rollbackMedia();
11581
11582 // delete the saved state file (it might have been already created)
11583 // AFTER killing the snapshot so that releaseSavedStateFile() won't
11584 // think it's still in use
11585 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
11586 mConsoleTaskData.mSnapshot->uninit();
11587 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
11588 }
11589
11590 if (!mData->mSession.mType.isEmpty())
11591 {
11592 /* mType is not null when this machine's process has been started by
11593 * Machine::LaunchVMProcess(), therefore it is our child. We
11594 * need to queue the PID to reap the process (and avoid zombies on
11595 * Linux). */
11596 Assert(mData->mSession.mPid != NIL_RTPROCESS);
11597 mParent->addProcessToReap(mData->mSession.mPid);
11598 }
11599
11600 mData->mSession.mPid = NIL_RTPROCESS;
11601
11602 if (aReason == Uninit::Unexpected)
11603 {
11604 /* Uninitialization didn't come from #checkForDeath(), so tell the
11605 * client watcher thread to update the set of machines that have open
11606 * sessions. */
11607 mParent->updateClientWatcher();
11608 }
11609
11610 /* uninitialize all remote controls */
11611 if (mData->mSession.mRemoteControls.size())
11612 {
11613 LogFlowThisFunc(("Closing remote sessions (%d):\n",
11614 mData->mSession.mRemoteControls.size()));
11615
11616 Data::Session::RemoteControlList::iterator it =
11617 mData->mSession.mRemoteControls.begin();
11618 while (it != mData->mSession.mRemoteControls.end())
11619 {
11620 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
11621 HRESULT rc = (*it)->Uninitialize();
11622 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
11623 if (FAILED(rc))
11624 LogWarningThisFunc(("Forgot to close the remote session?\n"));
11625 ++it;
11626 }
11627 mData->mSession.mRemoteControls.clear();
11628 }
11629
11630 /*
11631 * An expected uninitialization can come only from #checkForDeath().
11632 * Otherwise it means that something's gone really wrong (for example,
11633 * the Session implementation has released the VirtualBox reference
11634 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
11635 * etc). However, it's also possible, that the client releases the IPC
11636 * semaphore correctly (i.e. before it releases the VirtualBox reference),
11637 * but the VirtualBox release event comes first to the server process.
11638 * This case is practically possible, so we should not assert on an
11639 * unexpected uninit, just log a warning.
11640 */
11641
11642 if ((aReason == Uninit::Unexpected))
11643 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
11644
11645 if (aReason != Uninit::Normal)
11646 {
11647 mData->mSession.mDirectControl.setNull();
11648 }
11649 else
11650 {
11651 /* this must be null here (see #OnSessionEnd()) */
11652 Assert(mData->mSession.mDirectControl.isNull());
11653 Assert(mData->mSession.mState == SessionState_Unlocking);
11654 Assert(!mData->mSession.mProgress.isNull());
11655 }
11656 if (mData->mSession.mProgress)
11657 {
11658 if (aReason == Uninit::Normal)
11659 mData->mSession.mProgress->notifyComplete(S_OK);
11660 else
11661 mData->mSession.mProgress->notifyComplete(E_FAIL,
11662 COM_IIDOF(ISession),
11663 getComponentName(),
11664 tr("The VM session was aborted"));
11665 mData->mSession.mProgress.setNull();
11666 }
11667
11668 /* remove the association between the peer machine and this session machine */
11669 Assert( (SessionMachine*)mData->mSession.mMachine == this
11670 || aReason == Uninit::Unexpected);
11671
11672 /* reset the rest of session data */
11673 mData->mSession.mMachine.setNull();
11674 mData->mSession.mState = SessionState_Unlocked;
11675 mData->mSession.mType.setNull();
11676
11677 /* close the interprocess semaphore before leaving the exclusive lock */
11678#if defined(RT_OS_WINDOWS)
11679 if (mIPCSem)
11680 ::CloseHandle(mIPCSem);
11681 mIPCSem = NULL;
11682#elif defined(RT_OS_OS2)
11683 if (mIPCSem != NULLHANDLE)
11684 ::DosCloseMutexSem(mIPCSem);
11685 mIPCSem = NULLHANDLE;
11686#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11687 if (mIPCSem >= 0)
11688 ::semctl(mIPCSem, 0, IPC_RMID);
11689 mIPCSem = -1;
11690# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11691 mIPCKey = "0";
11692# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11693#else
11694# error "Port me!"
11695#endif
11696
11697 /* fire an event */
11698 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
11699
11700 uninitDataAndChildObjects();
11701
11702 /* free the essential data structure last */
11703 mData.free();
11704
11705 /* release the exclusive lock before setting the below two to NULL */
11706 multilock.release();
11707
11708 unconst(mParent) = NULL;
11709 unconst(mPeer) = NULL;
11710
11711 LogFlowThisFuncLeave();
11712}
11713
11714// util::Lockable interface
11715////////////////////////////////////////////////////////////////////////////////
11716
11717/**
11718 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
11719 * with the primary Machine instance (mPeer).
11720 */
11721RWLockHandle *SessionMachine::lockHandle() const
11722{
11723 AssertReturn(mPeer != NULL, NULL);
11724 return mPeer->lockHandle();
11725}
11726
11727// IInternalMachineControl methods
11728////////////////////////////////////////////////////////////////////////////////
11729
11730/**
11731 * Passes collected guest statistics to performance collector object
11732 */
11733STDMETHODIMP SessionMachine::ReportGuestStatistics(ULONG aValidStats, ULONG aCpuUser,
11734 ULONG aCpuKernel, ULONG aCpuIdle,
11735 ULONG aMemTotal, ULONG aMemFree,
11736 ULONG aMemBalloon, ULONG aMemShared,
11737 ULONG aMemCache, ULONG aPageTotal,
11738 ULONG aAllocVMM, ULONG aFreeVMM,
11739 ULONG aBalloonedVMM, ULONG aSharedVMM)
11740{
11741 if (mCollectorGuest)
11742 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
11743 aMemTotal, aMemFree, aMemBalloon, aMemShared,
11744 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
11745 aBalloonedVMM, aSharedVMM);
11746
11747 return S_OK;
11748}
11749
11750/**
11751 * @note Locks this object for writing.
11752 */
11753STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
11754{
11755 AutoCaller autoCaller(this);
11756 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11757
11758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11759
11760 mRemoveSavedState = aRemove;
11761
11762 return S_OK;
11763}
11764
11765/**
11766 * @note Locks the same as #setMachineState() does.
11767 */
11768STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
11769{
11770 return setMachineState(aMachineState);
11771}
11772
11773/**
11774 * @note Locks this object for reading.
11775 */
11776STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
11777{
11778 AutoCaller autoCaller(this);
11779 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11780
11781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
11782
11783#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
11784 mIPCSemName.cloneTo(aId);
11785 return S_OK;
11786#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11787# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11788 mIPCKey.cloneTo(aId);
11789# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11790 mData->m_strConfigFileFull.cloneTo(aId);
11791# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11792 return S_OK;
11793#else
11794# error "Port me!"
11795#endif
11796}
11797
11798/**
11799 * @note Locks this object for writing.
11800 */
11801STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
11802{
11803 LogFlowThisFunc(("aProgress=%p\n", aProgress));
11804 AutoCaller autoCaller(this);
11805 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11806
11807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11808
11809 if (mData->mSession.mState != SessionState_Locked)
11810 return VBOX_E_INVALID_OBJECT_STATE;
11811
11812 if (!mData->mSession.mProgress.isNull())
11813 mData->mSession.mProgress->setOtherProgressObject(aProgress);
11814
11815 LogFlowThisFunc(("returns S_OK.\n"));
11816 return S_OK;
11817}
11818
11819/**
11820 * @note Locks this object for writing.
11821 */
11822STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
11823{
11824 AutoCaller autoCaller(this);
11825 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11826
11827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11828
11829 if (mData->mSession.mState != SessionState_Locked)
11830 return VBOX_E_INVALID_OBJECT_STATE;
11831
11832 /* Finalize the LaunchVMProcess progress object. */
11833 if (mData->mSession.mProgress)
11834 {
11835 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
11836 mData->mSession.mProgress.setNull();
11837 }
11838
11839 if (SUCCEEDED((HRESULT)iResult))
11840 {
11841#ifdef VBOX_WITH_RESOURCE_USAGE_API
11842 /* The VM has been powered up successfully, so it makes sense
11843 * now to offer the performance metrics for a running machine
11844 * object. Doing it earlier wouldn't be safe. */
11845 registerMetrics(mParent->performanceCollector(), mPeer,
11846 mData->mSession.mPid);
11847#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11848 }
11849
11850 return S_OK;
11851}
11852
11853/**
11854 * @note Locks this object for writing.
11855 */
11856STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
11857{
11858 LogFlowThisFuncEnter();
11859
11860 CheckComArgOutPointerValid(aProgress);
11861
11862 AutoCaller autoCaller(this);
11863 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11864
11865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11866
11867 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
11868 E_FAIL);
11869
11870 /* create a progress object to track operation completion */
11871 ComObjPtr<Progress> pProgress;
11872 pProgress.createObject();
11873 pProgress->init(getVirtualBox(),
11874 static_cast<IMachine *>(this) /* aInitiator */,
11875 Bstr(tr("Stopping the virtual machine")).raw(),
11876 FALSE /* aCancelable */);
11877
11878 /* fill in the console task data */
11879 mConsoleTaskData.mLastState = mData->mMachineState;
11880 mConsoleTaskData.mProgress = pProgress;
11881
11882 /* set the state to Stopping (this is expected by Console::PowerDown()) */
11883 setMachineState(MachineState_Stopping);
11884
11885 pProgress.queryInterfaceTo(aProgress);
11886
11887 return S_OK;
11888}
11889
11890/**
11891 * @note Locks this object for writing.
11892 */
11893STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
11894{
11895 LogFlowThisFuncEnter();
11896
11897 AutoCaller autoCaller(this);
11898 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11899
11900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11901
11902 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
11903 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
11904 && mConsoleTaskData.mLastState != MachineState_Null,
11905 E_FAIL);
11906
11907 /*
11908 * On failure, set the state to the state we had when BeginPoweringDown()
11909 * was called (this is expected by Console::PowerDown() and the associated
11910 * task). On success the VM process already changed the state to
11911 * MachineState_PoweredOff, so no need to do anything.
11912 */
11913 if (FAILED(iResult))
11914 setMachineState(mConsoleTaskData.mLastState);
11915
11916 /* notify the progress object about operation completion */
11917 Assert(mConsoleTaskData.mProgress);
11918 if (SUCCEEDED(iResult))
11919 mConsoleTaskData.mProgress->notifyComplete(S_OK);
11920 else
11921 {
11922 Utf8Str strErrMsg(aErrMsg);
11923 if (strErrMsg.length())
11924 mConsoleTaskData.mProgress->notifyComplete(iResult,
11925 COM_IIDOF(ISession),
11926 getComponentName(),
11927 strErrMsg.c_str());
11928 else
11929 mConsoleTaskData.mProgress->notifyComplete(iResult);
11930 }
11931
11932 /* clear out the temporary saved state data */
11933 mConsoleTaskData.mLastState = MachineState_Null;
11934 mConsoleTaskData.mProgress.setNull();
11935
11936 LogFlowThisFuncLeave();
11937 return S_OK;
11938}
11939
11940
11941/**
11942 * Goes through the USB filters of the given machine to see if the given
11943 * device matches any filter or not.
11944 *
11945 * @note Locks the same as USBController::hasMatchingFilter() does.
11946 */
11947STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
11948 BOOL *aMatched,
11949 ULONG *aMaskedIfs)
11950{
11951 LogFlowThisFunc(("\n"));
11952
11953 CheckComArgNotNull(aUSBDevice);
11954 CheckComArgOutPointerValid(aMatched);
11955
11956 AutoCaller autoCaller(this);
11957 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11958
11959#ifdef VBOX_WITH_USB
11960 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
11961#else
11962 NOREF(aUSBDevice);
11963 NOREF(aMaskedIfs);
11964 *aMatched = FALSE;
11965#endif
11966
11967 return S_OK;
11968}
11969
11970/**
11971 * @note Locks the same as Host::captureUSBDevice() does.
11972 */
11973STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
11974{
11975 LogFlowThisFunc(("\n"));
11976
11977 AutoCaller autoCaller(this);
11978 AssertComRCReturnRC(autoCaller.rc());
11979
11980#ifdef VBOX_WITH_USB
11981 /* if captureDeviceForVM() fails, it must have set extended error info */
11982 clearError();
11983 MultiResult rc = mParent->host()->checkUSBProxyService();
11984 if (FAILED(rc)) return rc;
11985
11986 USBProxyService *service = mParent->host()->usbProxyService();
11987 AssertReturn(service, E_FAIL);
11988 return service->captureDeviceForVM(this, Guid(aId).ref());
11989#else
11990 NOREF(aId);
11991 return E_NOTIMPL;
11992#endif
11993}
11994
11995/**
11996 * @note Locks the same as Host::detachUSBDevice() does.
11997 */
11998STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
11999{
12000 LogFlowThisFunc(("\n"));
12001
12002 AutoCaller autoCaller(this);
12003 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12004
12005#ifdef VBOX_WITH_USB
12006 USBProxyService *service = mParent->host()->usbProxyService();
12007 AssertReturn(service, E_FAIL);
12008 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12009#else
12010 NOREF(aId);
12011 NOREF(aDone);
12012 return E_NOTIMPL;
12013#endif
12014}
12015
12016/**
12017 * Inserts all machine filters to the USB proxy service and then calls
12018 * Host::autoCaptureUSBDevices().
12019 *
12020 * Called by Console from the VM process upon VM startup.
12021 *
12022 * @note Locks what called methods lock.
12023 */
12024STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12025{
12026 LogFlowThisFunc(("\n"));
12027
12028 AutoCaller autoCaller(this);
12029 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12030
12031#ifdef VBOX_WITH_USB
12032 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12033 AssertComRC(rc);
12034 NOREF(rc);
12035
12036 USBProxyService *service = mParent->host()->usbProxyService();
12037 AssertReturn(service, E_FAIL);
12038 return service->autoCaptureDevicesForVM(this);
12039#else
12040 return S_OK;
12041#endif
12042}
12043
12044/**
12045 * Removes all machine filters from the USB proxy service and then calls
12046 * Host::detachAllUSBDevices().
12047 *
12048 * Called by Console from the VM process upon normal VM termination or by
12049 * SessionMachine::uninit() upon abnormal VM termination (from under the
12050 * Machine/SessionMachine lock).
12051 *
12052 * @note Locks what called methods lock.
12053 */
12054STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12055{
12056 LogFlowThisFunc(("\n"));
12057
12058 AutoCaller autoCaller(this);
12059 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12060
12061#ifdef VBOX_WITH_USB
12062 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12063 AssertComRC(rc);
12064 NOREF(rc);
12065
12066 USBProxyService *service = mParent->host()->usbProxyService();
12067 AssertReturn(service, E_FAIL);
12068 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12069#else
12070 NOREF(aDone);
12071 return S_OK;
12072#endif
12073}
12074
12075/**
12076 * @note Locks this object for writing.
12077 */
12078STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12079 IProgress **aProgress)
12080{
12081 LogFlowThisFuncEnter();
12082
12083 AssertReturn(aSession, E_INVALIDARG);
12084 AssertReturn(aProgress, E_INVALIDARG);
12085
12086 AutoCaller autoCaller(this);
12087
12088 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12089 /*
12090 * We don't assert below because it might happen that a non-direct session
12091 * informs us it is closed right after we've been uninitialized -- it's ok.
12092 */
12093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12094
12095 /* get IInternalSessionControl interface */
12096 ComPtr<IInternalSessionControl> control(aSession);
12097
12098 ComAssertRet(!control.isNull(), E_INVALIDARG);
12099
12100 /* Creating a Progress object requires the VirtualBox lock, and
12101 * thus locking it here is required by the lock order rules. */
12102 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12103
12104 if (control == mData->mSession.mDirectControl)
12105 {
12106 ComAssertRet(aProgress, E_POINTER);
12107
12108 /* The direct session is being normally closed by the client process
12109 * ----------------------------------------------------------------- */
12110
12111 /* go to the closing state (essential for all open*Session() calls and
12112 * for #checkForDeath()) */
12113 Assert(mData->mSession.mState == SessionState_Locked);
12114 mData->mSession.mState = SessionState_Unlocking;
12115
12116 /* set direct control to NULL to release the remote instance */
12117 mData->mSession.mDirectControl.setNull();
12118 LogFlowThisFunc(("Direct control is set to NULL\n"));
12119
12120 if (mData->mSession.mProgress)
12121 {
12122 /* finalize the progress, someone might wait if a frontend
12123 * closes the session before powering on the VM. */
12124 mData->mSession.mProgress->notifyComplete(E_FAIL,
12125 COM_IIDOF(ISession),
12126 getComponentName(),
12127 tr("The VM session was closed before any attempt to power it on"));
12128 mData->mSession.mProgress.setNull();
12129 }
12130
12131 /* Create the progress object the client will use to wait until
12132 * #checkForDeath() is called to uninitialize this session object after
12133 * it releases the IPC semaphore.
12134 * Note! Because we're "reusing" mProgress here, this must be a proxy
12135 * object just like for LaunchVMProcess. */
12136 Assert(mData->mSession.mProgress.isNull());
12137 ComObjPtr<ProgressProxy> progress;
12138 progress.createObject();
12139 ComPtr<IUnknown> pPeer(mPeer);
12140 progress->init(mParent, pPeer,
12141 Bstr(tr("Closing session")).raw(),
12142 FALSE /* aCancelable */);
12143 progress.queryInterfaceTo(aProgress);
12144 mData->mSession.mProgress = progress;
12145 }
12146 else
12147 {
12148 /* the remote session is being normally closed */
12149 Data::Session::RemoteControlList::iterator it =
12150 mData->mSession.mRemoteControls.begin();
12151 while (it != mData->mSession.mRemoteControls.end())
12152 {
12153 if (control == *it)
12154 break;
12155 ++it;
12156 }
12157 BOOL found = it != mData->mSession.mRemoteControls.end();
12158 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12159 E_INVALIDARG);
12160 // This MUST be erase(it), not remove(*it) as the latter triggers a
12161 // very nasty use after free due to the place where the value "lives".
12162 mData->mSession.mRemoteControls.erase(it);
12163 }
12164
12165 LogFlowThisFuncLeave();
12166 return S_OK;
12167}
12168
12169/**
12170 * @note Locks this object for writing.
12171 */
12172STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12173{
12174 LogFlowThisFuncEnter();
12175
12176 CheckComArgOutPointerValid(aProgress);
12177 CheckComArgOutPointerValid(aStateFilePath);
12178
12179 AutoCaller autoCaller(this);
12180 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12181
12182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12183
12184 AssertReturn( mData->mMachineState == MachineState_Paused
12185 && mConsoleTaskData.mLastState == MachineState_Null
12186 && mConsoleTaskData.strStateFilePath.isEmpty(),
12187 E_FAIL);
12188
12189 /* create a progress object to track operation completion */
12190 ComObjPtr<Progress> pProgress;
12191 pProgress.createObject();
12192 pProgress->init(getVirtualBox(),
12193 static_cast<IMachine *>(this) /* aInitiator */,
12194 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12195 FALSE /* aCancelable */);
12196
12197 Utf8Str strStateFilePath;
12198 /* stateFilePath is null when the machine is not running */
12199 if (mData->mMachineState == MachineState_Paused)
12200 composeSavedStateFilename(strStateFilePath);
12201
12202 /* fill in the console task data */
12203 mConsoleTaskData.mLastState = mData->mMachineState;
12204 mConsoleTaskData.strStateFilePath = strStateFilePath;
12205 mConsoleTaskData.mProgress = pProgress;
12206
12207 /* set the state to Saving (this is expected by Console::SaveState()) */
12208 setMachineState(MachineState_Saving);
12209
12210 strStateFilePath.cloneTo(aStateFilePath);
12211 pProgress.queryInterfaceTo(aProgress);
12212
12213 return S_OK;
12214}
12215
12216/**
12217 * @note Locks mParent + this object for writing.
12218 */
12219STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12220{
12221 LogFlowThisFunc(("\n"));
12222
12223 AutoCaller autoCaller(this);
12224 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12225
12226 /* endSavingState() need mParent lock */
12227 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12228
12229 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12230 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12231 && mConsoleTaskData.mLastState != MachineState_Null
12232 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12233 E_FAIL);
12234
12235 /*
12236 * On failure, set the state to the state we had when BeginSavingState()
12237 * was called (this is expected by Console::SaveState() and the associated
12238 * task). On success the VM process already changed the state to
12239 * MachineState_Saved, so no need to do anything.
12240 */
12241 if (FAILED(iResult))
12242 setMachineState(mConsoleTaskData.mLastState);
12243
12244 return endSavingState(iResult, aErrMsg);
12245}
12246
12247/**
12248 * @note Locks this object for writing.
12249 */
12250STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12251{
12252 LogFlowThisFunc(("\n"));
12253
12254 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12255
12256 AutoCaller autoCaller(this);
12257 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12258
12259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12260
12261 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12262 || mData->mMachineState == MachineState_Teleported
12263 || mData->mMachineState == MachineState_Aborted
12264 , E_FAIL); /** @todo setError. */
12265
12266 Utf8Str stateFilePathFull = aSavedStateFile;
12267 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12268 if (RT_FAILURE(vrc))
12269 return setError(VBOX_E_FILE_ERROR,
12270 tr("Invalid saved state file path '%ls' (%Rrc)"),
12271 aSavedStateFile,
12272 vrc);
12273
12274 mSSData->strStateFilePath = stateFilePathFull;
12275
12276 /* The below setMachineState() will detect the state transition and will
12277 * update the settings file */
12278
12279 return setMachineState(MachineState_Saved);
12280}
12281
12282STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12283 ComSafeArrayOut(BSTR, aValues),
12284 ComSafeArrayOut(LONG64, aTimestamps),
12285 ComSafeArrayOut(BSTR, aFlags))
12286{
12287 LogFlowThisFunc(("\n"));
12288
12289#ifdef VBOX_WITH_GUEST_PROPS
12290 using namespace guestProp;
12291
12292 AutoCaller autoCaller(this);
12293 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12294
12295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12296
12297 CheckComArgOutSafeArrayPointerValid(aNames);
12298 CheckComArgOutSafeArrayPointerValid(aValues);
12299 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12300 CheckComArgOutSafeArrayPointerValid(aFlags);
12301
12302 size_t cEntries = mHWData->mGuestProperties.size();
12303 com::SafeArray<BSTR> names(cEntries);
12304 com::SafeArray<BSTR> values(cEntries);
12305 com::SafeArray<LONG64> timestamps(cEntries);
12306 com::SafeArray<BSTR> flags(cEntries);
12307 unsigned i = 0;
12308 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
12309 it != mHWData->mGuestProperties.end();
12310 ++it)
12311 {
12312 char szFlags[MAX_FLAGS_LEN + 1];
12313 it->strName.cloneTo(&names[i]);
12314 it->strValue.cloneTo(&values[i]);
12315 timestamps[i] = it->mTimestamp;
12316 /* If it is NULL, keep it NULL. */
12317 if (it->mFlags)
12318 {
12319 writeFlags(it->mFlags, szFlags);
12320 Bstr(szFlags).cloneTo(&flags[i]);
12321 }
12322 else
12323 flags[i] = NULL;
12324 ++i;
12325 }
12326 names.detachTo(ComSafeArrayOutArg(aNames));
12327 values.detachTo(ComSafeArrayOutArg(aValues));
12328 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12329 flags.detachTo(ComSafeArrayOutArg(aFlags));
12330 return S_OK;
12331#else
12332 ReturnComNotImplemented();
12333#endif
12334}
12335
12336STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12337 IN_BSTR aValue,
12338 LONG64 aTimestamp,
12339 IN_BSTR aFlags)
12340{
12341 LogFlowThisFunc(("\n"));
12342
12343#ifdef VBOX_WITH_GUEST_PROPS
12344 using namespace guestProp;
12345
12346 CheckComArgStrNotEmptyOrNull(aName);
12347 CheckComArgNotNull(aValue);
12348 CheckComArgNotNull(aFlags);
12349
12350 try
12351 {
12352 /*
12353 * Convert input up front.
12354 */
12355 Utf8Str utf8Name(aName);
12356 uint32_t fFlags = NILFLAG;
12357 if (aFlags)
12358 {
12359 Utf8Str utf8Flags(aFlags);
12360 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12361 AssertRCReturn(vrc, E_INVALIDARG);
12362 }
12363
12364 /*
12365 * Now grab the object lock, validate the state and do the update.
12366 */
12367 AutoCaller autoCaller(this);
12368 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12369
12370 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12371
12372 switch (mData->mMachineState)
12373 {
12374 case MachineState_Paused:
12375 case MachineState_Running:
12376 case MachineState_Teleporting:
12377 case MachineState_TeleportingPausedVM:
12378 case MachineState_LiveSnapshotting:
12379 case MachineState_DeletingSnapshotOnline:
12380 case MachineState_DeletingSnapshotPaused:
12381 case MachineState_Saving:
12382 break;
12383
12384 default:
12385#ifndef DEBUG_sunlover
12386 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12387 VBOX_E_INVALID_VM_STATE);
12388#else
12389 return VBOX_E_INVALID_VM_STATE;
12390#endif
12391 }
12392
12393 setModified(IsModified_MachineData);
12394 mHWData.backup();
12395
12396 /** @todo r=bird: The careful memory handling doesn't work out here because
12397 * the catch block won't undo any damage we've done. So, if push_back throws
12398 * bad_alloc then you've lost the value.
12399 *
12400 * Another thing. Doing a linear search here isn't extremely efficient, esp.
12401 * since values that changes actually bubbles to the end of the list. Using
12402 * something that has an efficient lookup and can tolerate a bit of updates
12403 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
12404 * combination of RTStrCache (for sharing names and getting uniqueness into
12405 * the bargain) and hash/tree is another. */
12406 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
12407 iter != mHWData->mGuestProperties.end();
12408 ++iter)
12409 if (utf8Name == iter->strName)
12410 {
12411 mHWData->mGuestProperties.erase(iter);
12412 mData->mGuestPropertiesModified = TRUE;
12413 break;
12414 }
12415 if (aValue != NULL)
12416 {
12417 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
12418 mHWData->mGuestProperties.push_back(property);
12419 mData->mGuestPropertiesModified = TRUE;
12420 }
12421
12422 /*
12423 * Send a callback notification if appropriate
12424 */
12425 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12426 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12427 RTSTR_MAX,
12428 utf8Name.c_str(),
12429 RTSTR_MAX, NULL)
12430 )
12431 {
12432 alock.release();
12433
12434 mParent->onGuestPropertyChange(mData->mUuid,
12435 aName,
12436 aValue,
12437 aFlags);
12438 }
12439 }
12440 catch (...)
12441 {
12442 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12443 }
12444 return S_OK;
12445#else
12446 ReturnComNotImplemented();
12447#endif
12448}
12449
12450STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12451 IMediumAttachment **aNewAttachment)
12452{
12453 CheckComArgNotNull(aAttachment);
12454 CheckComArgOutPointerValid(aNewAttachment);
12455
12456 AutoCaller autoCaller(this);
12457 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12458
12459 // request the host lock first, since might be calling Host methods for getting host drives;
12460 // next, protect the media tree all the while we're in here, as well as our member variables
12461 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12462 this->lockHandle(),
12463 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12464
12465 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12466
12467 Bstr ctrlName;
12468 LONG lPort;
12469 LONG lDevice;
12470 bool fTempEject;
12471 {
12472 AutoCaller autoAttachCaller(this);
12473 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12474
12475 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12476
12477 /* Need to query the details first, as the IMediumAttachment reference
12478 * might be to the original settings, which we are going to change. */
12479 ctrlName = pAttach->getControllerName();
12480 lPort = pAttach->getPort();
12481 lDevice = pAttach->getDevice();
12482 fTempEject = pAttach->getTempEject();
12483 }
12484
12485 if (!fTempEject)
12486 {
12487 /* Remember previously mounted medium. The medium before taking the
12488 * backup is not necessarily the same thing. */
12489 ComObjPtr<Medium> oldmedium;
12490 oldmedium = pAttach->getMedium();
12491
12492 setModified(IsModified_Storage);
12493 mMediaData.backup();
12494
12495 // The backup operation makes the pAttach reference point to the
12496 // old settings. Re-get the correct reference.
12497 pAttach = findAttachment(mMediaData->mAttachments,
12498 ctrlName.raw(),
12499 lPort,
12500 lDevice);
12501
12502 {
12503 AutoCaller autoAttachCaller(this);
12504 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12505
12506 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12507 if (!oldmedium.isNull())
12508 oldmedium->removeBackReference(mData->mUuid);
12509
12510 pAttach->updateMedium(NULL);
12511 pAttach->updateEjected();
12512 }
12513
12514 setModified(IsModified_Storage);
12515 }
12516 else
12517 {
12518 {
12519 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12520 pAttach->updateEjected();
12521 }
12522 }
12523
12524 pAttach.queryInterfaceTo(aNewAttachment);
12525
12526 return S_OK;
12527}
12528
12529// public methods only for internal purposes
12530/////////////////////////////////////////////////////////////////////////////
12531
12532/**
12533 * Called from the client watcher thread to check for expected or unexpected
12534 * death of the client process that has a direct session to this machine.
12535 *
12536 * On Win32 and on OS/2, this method is called only when we've got the
12537 * mutex (i.e. the client has either died or terminated normally) so it always
12538 * returns @c true (the client is terminated, the session machine is
12539 * uninitialized).
12540 *
12541 * On other platforms, the method returns @c true if the client process has
12542 * terminated normally or abnormally and the session machine was uninitialized,
12543 * and @c false if the client process is still alive.
12544 *
12545 * @note Locks this object for writing.
12546 */
12547bool SessionMachine::checkForDeath()
12548{
12549 Uninit::Reason reason;
12550 bool terminated = false;
12551
12552 /* Enclose autoCaller with a block because calling uninit() from under it
12553 * will deadlock. */
12554 {
12555 AutoCaller autoCaller(this);
12556 if (!autoCaller.isOk())
12557 {
12558 /* return true if not ready, to cause the client watcher to exclude
12559 * the corresponding session from watching */
12560 LogFlowThisFunc(("Already uninitialized!\n"));
12561 return true;
12562 }
12563
12564 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12565
12566 /* Determine the reason of death: if the session state is Closing here,
12567 * everything is fine. Otherwise it means that the client did not call
12568 * OnSessionEnd() before it released the IPC semaphore. This may happen
12569 * either because the client process has abnormally terminated, or
12570 * because it simply forgot to call ISession::Close() before exiting. We
12571 * threat the latter also as an abnormal termination (see
12572 * Session::uninit() for details). */
12573 reason = mData->mSession.mState == SessionState_Unlocking ?
12574 Uninit::Normal :
12575 Uninit::Abnormal;
12576
12577#if defined(RT_OS_WINDOWS)
12578
12579 AssertMsg(mIPCSem, ("semaphore must be created"));
12580
12581 /* release the IPC mutex */
12582 ::ReleaseMutex(mIPCSem);
12583
12584 terminated = true;
12585
12586#elif defined(RT_OS_OS2)
12587
12588 AssertMsg(mIPCSem, ("semaphore must be created"));
12589
12590 /* release the IPC mutex */
12591 ::DosReleaseMutexSem(mIPCSem);
12592
12593 terminated = true;
12594
12595#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12596
12597 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
12598
12599 int val = ::semctl(mIPCSem, 0, GETVAL);
12600 if (val > 0)
12601 {
12602 /* the semaphore is signaled, meaning the session is terminated */
12603 terminated = true;
12604 }
12605
12606#else
12607# error "Port me!"
12608#endif
12609
12610 } /* AutoCaller block */
12611
12612 if (terminated)
12613 uninit(reason);
12614
12615 return terminated;
12616}
12617
12618/**
12619 * @note Locks this object for reading.
12620 */
12621HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
12622{
12623 LogFlowThisFunc(("\n"));
12624
12625 AutoCaller autoCaller(this);
12626 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12627
12628 ComPtr<IInternalSessionControl> directControl;
12629 {
12630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12631 directControl = mData->mSession.mDirectControl;
12632 }
12633
12634 /* ignore notifications sent after #OnSessionEnd() is called */
12635 if (!directControl)
12636 return S_OK;
12637
12638 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
12639}
12640
12641/**
12642 * @note Locks this object for reading.
12643 */
12644HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
12645 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
12646{
12647 LogFlowThisFunc(("\n"));
12648
12649 AutoCaller autoCaller(this);
12650 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12651
12652 ComPtr<IInternalSessionControl> directControl;
12653 {
12654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12655 directControl = mData->mSession.mDirectControl;
12656 }
12657
12658 /* ignore notifications sent after #OnSessionEnd() is called */
12659 if (!directControl)
12660 return S_OK;
12661 /*
12662 * instead acting like callback we ask IVirtualBox deliver corresponding event
12663 */
12664
12665 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
12666 return S_OK;
12667}
12668
12669/**
12670 * @note Locks this object for reading.
12671 */
12672HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
12673{
12674 LogFlowThisFunc(("\n"));
12675
12676 AutoCaller autoCaller(this);
12677 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12678
12679 ComPtr<IInternalSessionControl> directControl;
12680 {
12681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12682 directControl = mData->mSession.mDirectControl;
12683 }
12684
12685 /* ignore notifications sent after #OnSessionEnd() is called */
12686 if (!directControl)
12687 return S_OK;
12688
12689 return directControl->OnSerialPortChange(serialPort);
12690}
12691
12692/**
12693 * @note Locks this object for reading.
12694 */
12695HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
12696{
12697 LogFlowThisFunc(("\n"));
12698
12699 AutoCaller autoCaller(this);
12700 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12701
12702 ComPtr<IInternalSessionControl> directControl;
12703 {
12704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12705 directControl = mData->mSession.mDirectControl;
12706 }
12707
12708 /* ignore notifications sent after #OnSessionEnd() is called */
12709 if (!directControl)
12710 return S_OK;
12711
12712 return directControl->OnParallelPortChange(parallelPort);
12713}
12714
12715/**
12716 * @note Locks this object for reading.
12717 */
12718HRESULT SessionMachine::onStorageControllerChange()
12719{
12720 LogFlowThisFunc(("\n"));
12721
12722 AutoCaller autoCaller(this);
12723 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12724
12725 ComPtr<IInternalSessionControl> directControl;
12726 {
12727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12728 directControl = mData->mSession.mDirectControl;
12729 }
12730
12731 /* ignore notifications sent after #OnSessionEnd() is called */
12732 if (!directControl)
12733 return S_OK;
12734
12735 return directControl->OnStorageControllerChange();
12736}
12737
12738/**
12739 * @note Locks this object for reading.
12740 */
12741HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
12742{
12743 LogFlowThisFunc(("\n"));
12744
12745 AutoCaller autoCaller(this);
12746 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12747
12748 ComPtr<IInternalSessionControl> directControl;
12749 {
12750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12751 directControl = mData->mSession.mDirectControl;
12752 }
12753
12754 /* ignore notifications sent after #OnSessionEnd() is called */
12755 if (!directControl)
12756 return S_OK;
12757
12758 return directControl->OnMediumChange(aAttachment, aForce);
12759}
12760
12761/**
12762 * @note Locks this object for reading.
12763 */
12764HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
12765{
12766 LogFlowThisFunc(("\n"));
12767
12768 AutoCaller autoCaller(this);
12769 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12770
12771 ComPtr<IInternalSessionControl> directControl;
12772 {
12773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12774 directControl = mData->mSession.mDirectControl;
12775 }
12776
12777 /* ignore notifications sent after #OnSessionEnd() is called */
12778 if (!directControl)
12779 return S_OK;
12780
12781 return directControl->OnCPUChange(aCPU, aRemove);
12782}
12783
12784HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
12785{
12786 LogFlowThisFunc(("\n"));
12787
12788 AutoCaller autoCaller(this);
12789 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12790
12791 ComPtr<IInternalSessionControl> directControl;
12792 {
12793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12794 directControl = mData->mSession.mDirectControl;
12795 }
12796
12797 /* ignore notifications sent after #OnSessionEnd() is called */
12798 if (!directControl)
12799 return S_OK;
12800
12801 return directControl->OnCPUExecutionCapChange(aExecutionCap);
12802}
12803
12804/**
12805 * @note Locks this object for reading.
12806 */
12807HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
12808{
12809 LogFlowThisFunc(("\n"));
12810
12811 AutoCaller autoCaller(this);
12812 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12813
12814 ComPtr<IInternalSessionControl> directControl;
12815 {
12816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12817 directControl = mData->mSession.mDirectControl;
12818 }
12819
12820 /* ignore notifications sent after #OnSessionEnd() is called */
12821 if (!directControl)
12822 return S_OK;
12823
12824 return directControl->OnVRDEServerChange(aRestart);
12825}
12826
12827/**
12828 * @note Locks this object for reading.
12829 */
12830HRESULT SessionMachine::onUSBControllerChange()
12831{
12832 LogFlowThisFunc(("\n"));
12833
12834 AutoCaller autoCaller(this);
12835 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12836
12837 ComPtr<IInternalSessionControl> directControl;
12838 {
12839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12840 directControl = mData->mSession.mDirectControl;
12841 }
12842
12843 /* ignore notifications sent after #OnSessionEnd() is called */
12844 if (!directControl)
12845 return S_OK;
12846
12847 return directControl->OnUSBControllerChange();
12848}
12849
12850/**
12851 * @note Locks this object for reading.
12852 */
12853HRESULT SessionMachine::onSharedFolderChange()
12854{
12855 LogFlowThisFunc(("\n"));
12856
12857 AutoCaller autoCaller(this);
12858 AssertComRCReturnRC(autoCaller.rc());
12859
12860 ComPtr<IInternalSessionControl> directControl;
12861 {
12862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12863 directControl = mData->mSession.mDirectControl;
12864 }
12865
12866 /* ignore notifications sent after #OnSessionEnd() is called */
12867 if (!directControl)
12868 return S_OK;
12869
12870 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
12871}
12872
12873/**
12874 * @note Locks this object for reading.
12875 */
12876HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
12877{
12878 LogFlowThisFunc(("\n"));
12879
12880 AutoCaller autoCaller(this);
12881 AssertComRCReturnRC(autoCaller.rc());
12882
12883 ComPtr<IInternalSessionControl> directControl;
12884 {
12885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12886 directControl = mData->mSession.mDirectControl;
12887 }
12888
12889 /* ignore notifications sent after #OnSessionEnd() is called */
12890 if (!directControl)
12891 return S_OK;
12892
12893 return directControl->OnClipboardModeChange(aClipboardMode);
12894}
12895
12896/**
12897 * @note Locks this object for reading.
12898 */
12899HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
12900{
12901 LogFlowThisFunc(("\n"));
12902
12903 AutoCaller autoCaller(this);
12904 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
12905
12906 ComPtr<IInternalSessionControl> directControl;
12907 {
12908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12909 directControl = mData->mSession.mDirectControl;
12910 }
12911
12912 /* ignore notifications sent after #OnSessionEnd() is called */
12913 if (!directControl)
12914 return S_OK;
12915
12916 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
12917}
12918
12919/**
12920 * @note Locks this object for reading.
12921 */
12922HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
12923{
12924 LogFlowThisFunc(("\n"));
12925
12926 AutoCaller autoCaller(this);
12927 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12928
12929 ComPtr<IInternalSessionControl> directControl;
12930 {
12931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12932 directControl = mData->mSession.mDirectControl;
12933 }
12934
12935 /* ignore notifications sent after #OnSessionEnd() is called */
12936 if (!directControl)
12937 return S_OK;
12938
12939 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
12940}
12941
12942/**
12943 * Returns @c true if this machine's USB controller reports it has a matching
12944 * filter for the given USB device and @c false otherwise.
12945 *
12946 * @note locks this object for reading.
12947 */
12948bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
12949{
12950 AutoCaller autoCaller(this);
12951 /* silently return if not ready -- this method may be called after the
12952 * direct machine session has been called */
12953 if (!autoCaller.isOk())
12954 return false;
12955
12956#ifdef VBOX_WITH_USB
12957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12958
12959 switch (mData->mMachineState)
12960 {
12961 case MachineState_Starting:
12962 case MachineState_Restoring:
12963 case MachineState_TeleportingIn:
12964 case MachineState_Paused:
12965 case MachineState_Running:
12966 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
12967 * elsewhere... */
12968 alock.release();
12969 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
12970 default: break;
12971 }
12972#else
12973 NOREF(aDevice);
12974 NOREF(aMaskedIfs);
12975#endif
12976 return false;
12977}
12978
12979/**
12980 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
12981 */
12982HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
12983 IVirtualBoxErrorInfo *aError,
12984 ULONG aMaskedIfs)
12985{
12986 LogFlowThisFunc(("\n"));
12987
12988 AutoCaller autoCaller(this);
12989
12990 /* This notification may happen after the machine object has been
12991 * uninitialized (the session was closed), so don't assert. */
12992 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12993
12994 ComPtr<IInternalSessionControl> directControl;
12995 {
12996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12997 directControl = mData->mSession.mDirectControl;
12998 }
12999
13000 /* fail on notifications sent after #OnSessionEnd() is called, it is
13001 * expected by the caller */
13002 if (!directControl)
13003 return E_FAIL;
13004
13005 /* No locks should be held at this point. */
13006 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13007 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13008
13009 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13010}
13011
13012/**
13013 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13014 */
13015HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13016 IVirtualBoxErrorInfo *aError)
13017{
13018 LogFlowThisFunc(("\n"));
13019
13020 AutoCaller autoCaller(this);
13021
13022 /* This notification may happen after the machine object has been
13023 * uninitialized (the session was closed), so don't assert. */
13024 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13025
13026 ComPtr<IInternalSessionControl> directControl;
13027 {
13028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13029 directControl = mData->mSession.mDirectControl;
13030 }
13031
13032 /* fail on notifications sent after #OnSessionEnd() is called, it is
13033 * expected by the caller */
13034 if (!directControl)
13035 return E_FAIL;
13036
13037 /* No locks should be held at this point. */
13038 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13039 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13040
13041 return directControl->OnUSBDeviceDetach(aId, aError);
13042}
13043
13044// protected methods
13045/////////////////////////////////////////////////////////////////////////////
13046
13047/**
13048 * Helper method to finalize saving the state.
13049 *
13050 * @note Must be called from under this object's lock.
13051 *
13052 * @param aRc S_OK if the snapshot has been taken successfully
13053 * @param aErrMsg human readable error message for failure
13054 *
13055 * @note Locks mParent + this objects for writing.
13056 */
13057HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13058{
13059 LogFlowThisFuncEnter();
13060
13061 AutoCaller autoCaller(this);
13062 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13063
13064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13065
13066 HRESULT rc = S_OK;
13067
13068 if (SUCCEEDED(aRc))
13069 {
13070 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13071
13072 /* save all VM settings */
13073 rc = saveSettings(NULL);
13074 // no need to check whether VirtualBox.xml needs saving also since
13075 // we can't have a name change pending at this point
13076 }
13077 else
13078 {
13079 // delete the saved state file (it might have been already created);
13080 // we need not check whether this is shared with a snapshot here because
13081 // we certainly created this saved state file here anew
13082 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13083 }
13084
13085 /* notify the progress object about operation completion */
13086 Assert(mConsoleTaskData.mProgress);
13087 if (SUCCEEDED(aRc))
13088 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13089 else
13090 {
13091 if (aErrMsg.length())
13092 mConsoleTaskData.mProgress->notifyComplete(aRc,
13093 COM_IIDOF(ISession),
13094 getComponentName(),
13095 aErrMsg.c_str());
13096 else
13097 mConsoleTaskData.mProgress->notifyComplete(aRc);
13098 }
13099
13100 /* clear out the temporary saved state data */
13101 mConsoleTaskData.mLastState = MachineState_Null;
13102 mConsoleTaskData.strStateFilePath.setNull();
13103 mConsoleTaskData.mProgress.setNull();
13104
13105 LogFlowThisFuncLeave();
13106 return rc;
13107}
13108
13109/**
13110 * Deletes the given file if it is no longer in use by either the current machine state
13111 * (if the machine is "saved") or any of the machine's snapshots.
13112 *
13113 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13114 * but is different for each SnapshotMachine. When calling this, the order of calling this
13115 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13116 * is therefore critical. I know, it's all rather messy.
13117 *
13118 * @param strStateFile
13119 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13120 */
13121void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13122 Snapshot *pSnapshotToIgnore)
13123{
13124 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13125 if ( (strStateFile.isNotEmpty())
13126 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13127 )
13128 // ... and it must also not be shared with other snapshots
13129 if ( !mData->mFirstSnapshot
13130 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13131 // this checks the SnapshotMachine's state file paths
13132 )
13133 RTFileDelete(strStateFile.c_str());
13134}
13135
13136/**
13137 * Locks the attached media.
13138 *
13139 * All attached hard disks are locked for writing and DVD/floppy are locked for
13140 * reading. Parents of attached hard disks (if any) are locked for reading.
13141 *
13142 * This method also performs accessibility check of all media it locks: if some
13143 * media is inaccessible, the method will return a failure and a bunch of
13144 * extended error info objects per each inaccessible medium.
13145 *
13146 * Note that this method is atomic: if it returns a success, all media are
13147 * locked as described above; on failure no media is locked at all (all
13148 * succeeded individual locks will be undone).
13149 *
13150 * This method is intended to be called when the machine is in Starting or
13151 * Restoring state and asserts otherwise.
13152 *
13153 * The locks made by this method must be undone by calling #unlockMedia() when
13154 * no more needed.
13155 */
13156HRESULT SessionMachine::lockMedia()
13157{
13158 AutoCaller autoCaller(this);
13159 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13160
13161 AutoMultiWriteLock2 alock(this->lockHandle(),
13162 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13163
13164 AssertReturn( mData->mMachineState == MachineState_Starting
13165 || mData->mMachineState == MachineState_Restoring
13166 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13167 /* bail out if trying to lock things with already set up locking */
13168 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13169
13170 clearError();
13171 MultiResult mrc(S_OK);
13172
13173 /* Collect locking information for all medium objects attached to the VM. */
13174 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13175 it != mMediaData->mAttachments.end();
13176 ++it)
13177 {
13178 MediumAttachment* pAtt = *it;
13179 DeviceType_T devType = pAtt->getType();
13180 Medium *pMedium = pAtt->getMedium();
13181
13182 MediumLockList *pMediumLockList(new MediumLockList());
13183 // There can be attachments without a medium (floppy/dvd), and thus
13184 // it's impossible to create a medium lock list. It still makes sense
13185 // to have the empty medium lock list in the map in case a medium is
13186 // attached later.
13187 if (pMedium != NULL)
13188 {
13189 MediumType_T mediumType = pMedium->getType();
13190 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13191 || mediumType == MediumType_Shareable;
13192 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13193
13194 alock.release();
13195 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13196 !fIsReadOnlyLock /* fMediumLockWrite */,
13197 NULL,
13198 *pMediumLockList);
13199 alock.acquire();
13200 if (FAILED(mrc))
13201 {
13202 delete pMediumLockList;
13203 mData->mSession.mLockedMedia.Clear();
13204 break;
13205 }
13206 }
13207
13208 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13209 if (FAILED(rc))
13210 {
13211 mData->mSession.mLockedMedia.Clear();
13212 mrc = setError(rc,
13213 tr("Collecting locking information for all attached media failed"));
13214 break;
13215 }
13216 }
13217
13218 if (SUCCEEDED(mrc))
13219 {
13220 /* Now lock all media. If this fails, nothing is locked. */
13221 alock.release();
13222 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13223 alock.acquire();
13224 if (FAILED(rc))
13225 {
13226 mrc = setError(rc,
13227 tr("Locking of attached media failed"));
13228 }
13229 }
13230
13231 return mrc;
13232}
13233
13234/**
13235 * Undoes the locks made by by #lockMedia().
13236 */
13237void SessionMachine::unlockMedia()
13238{
13239 AutoCaller autoCaller(this);
13240 AssertComRCReturnVoid(autoCaller.rc());
13241
13242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13243
13244 /* we may be holding important error info on the current thread;
13245 * preserve it */
13246 ErrorInfoKeeper eik;
13247
13248 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13249 AssertComRC(rc);
13250}
13251
13252/**
13253 * Helper to change the machine state (reimplementation).
13254 *
13255 * @note Locks this object for writing.
13256 * @note This method must not call saveSettings or SaveSettings, otherwise
13257 * it can cause crashes in random places due to unexpectedly committing
13258 * the current settings. The caller is responsible for that. The call
13259 * to saveStateSettings is fine, because this method does not commit.
13260 */
13261HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13262{
13263 LogFlowThisFuncEnter();
13264 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13265
13266 AutoCaller autoCaller(this);
13267 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13268
13269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13270
13271 MachineState_T oldMachineState = mData->mMachineState;
13272
13273 AssertMsgReturn(oldMachineState != aMachineState,
13274 ("oldMachineState=%s, aMachineState=%s\n",
13275 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13276 E_FAIL);
13277
13278 HRESULT rc = S_OK;
13279
13280 int stsFlags = 0;
13281 bool deleteSavedState = false;
13282
13283 /* detect some state transitions */
13284
13285 if ( ( oldMachineState == MachineState_Saved
13286 && aMachineState == MachineState_Restoring)
13287 || ( ( oldMachineState == MachineState_PoweredOff
13288 || oldMachineState == MachineState_Teleported
13289 || oldMachineState == MachineState_Aborted
13290 )
13291 && ( aMachineState == MachineState_TeleportingIn
13292 || aMachineState == MachineState_Starting
13293 )
13294 )
13295 )
13296 {
13297 /* The EMT thread is about to start */
13298
13299 /* Nothing to do here for now... */
13300
13301 /// @todo NEWMEDIA don't let mDVDDrive and other children
13302 /// change anything when in the Starting/Restoring state
13303 }
13304 else if ( ( oldMachineState == MachineState_Running
13305 || oldMachineState == MachineState_Paused
13306 || oldMachineState == MachineState_Teleporting
13307 || oldMachineState == MachineState_LiveSnapshotting
13308 || oldMachineState == MachineState_Stuck
13309 || oldMachineState == MachineState_Starting
13310 || oldMachineState == MachineState_Stopping
13311 || oldMachineState == MachineState_Saving
13312 || oldMachineState == MachineState_Restoring
13313 || oldMachineState == MachineState_TeleportingPausedVM
13314 || oldMachineState == MachineState_TeleportingIn
13315 )
13316 && ( aMachineState == MachineState_PoweredOff
13317 || aMachineState == MachineState_Saved
13318 || aMachineState == MachineState_Teleported
13319 || aMachineState == MachineState_Aborted
13320 )
13321 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13322 * snapshot */
13323 && ( mConsoleTaskData.mSnapshot.isNull()
13324 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13325 )
13326 )
13327 {
13328 /* The EMT thread has just stopped, unlock attached media. Note that as
13329 * opposed to locking that is done from Console, we do unlocking here
13330 * because the VM process may have aborted before having a chance to
13331 * properly unlock all media it locked. */
13332
13333 unlockMedia();
13334 }
13335
13336 if (oldMachineState == MachineState_Restoring)
13337 {
13338 if (aMachineState != MachineState_Saved)
13339 {
13340 /*
13341 * delete the saved state file once the machine has finished
13342 * restoring from it (note that Console sets the state from
13343 * Restoring to Saved if the VM couldn't restore successfully,
13344 * to give the user an ability to fix an error and retry --
13345 * we keep the saved state file in this case)
13346 */
13347 deleteSavedState = true;
13348 }
13349 }
13350 else if ( oldMachineState == MachineState_Saved
13351 && ( aMachineState == MachineState_PoweredOff
13352 || aMachineState == MachineState_Aborted
13353 || aMachineState == MachineState_Teleported
13354 )
13355 )
13356 {
13357 /*
13358 * delete the saved state after Console::ForgetSavedState() is called
13359 * or if the VM process (owning a direct VM session) crashed while the
13360 * VM was Saved
13361 */
13362
13363 /// @todo (dmik)
13364 // Not sure that deleting the saved state file just because of the
13365 // client death before it attempted to restore the VM is a good
13366 // thing. But when it crashes we need to go to the Aborted state
13367 // which cannot have the saved state file associated... The only
13368 // way to fix this is to make the Aborted condition not a VM state
13369 // but a bool flag: i.e., when a crash occurs, set it to true and
13370 // change the state to PoweredOff or Saved depending on the
13371 // saved state presence.
13372
13373 deleteSavedState = true;
13374 mData->mCurrentStateModified = TRUE;
13375 stsFlags |= SaveSTS_CurStateModified;
13376 }
13377
13378 if ( aMachineState == MachineState_Starting
13379 || aMachineState == MachineState_Restoring
13380 || aMachineState == MachineState_TeleportingIn
13381 )
13382 {
13383 /* set the current state modified flag to indicate that the current
13384 * state is no more identical to the state in the
13385 * current snapshot */
13386 if (!mData->mCurrentSnapshot.isNull())
13387 {
13388 mData->mCurrentStateModified = TRUE;
13389 stsFlags |= SaveSTS_CurStateModified;
13390 }
13391 }
13392
13393 if (deleteSavedState)
13394 {
13395 if (mRemoveSavedState)
13396 {
13397 Assert(!mSSData->strStateFilePath.isEmpty());
13398
13399 // it is safe to delete the saved state file if ...
13400 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13401 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13402 // ... none of the snapshots share the saved state file
13403 )
13404 RTFileDelete(mSSData->strStateFilePath.c_str());
13405 }
13406
13407 mSSData->strStateFilePath.setNull();
13408 stsFlags |= SaveSTS_StateFilePath;
13409 }
13410
13411 /* redirect to the underlying peer machine */
13412 mPeer->setMachineState(aMachineState);
13413
13414 if ( aMachineState == MachineState_PoweredOff
13415 || aMachineState == MachineState_Teleported
13416 || aMachineState == MachineState_Aborted
13417 || aMachineState == MachineState_Saved)
13418 {
13419 /* the machine has stopped execution
13420 * (or the saved state file was adopted) */
13421 stsFlags |= SaveSTS_StateTimeStamp;
13422 }
13423
13424 if ( ( oldMachineState == MachineState_PoweredOff
13425 || oldMachineState == MachineState_Aborted
13426 || oldMachineState == MachineState_Teleported
13427 )
13428 && aMachineState == MachineState_Saved)
13429 {
13430 /* the saved state file was adopted */
13431 Assert(!mSSData->strStateFilePath.isEmpty());
13432 stsFlags |= SaveSTS_StateFilePath;
13433 }
13434
13435#ifdef VBOX_WITH_GUEST_PROPS
13436 if ( aMachineState == MachineState_PoweredOff
13437 || aMachineState == MachineState_Aborted
13438 || aMachineState == MachineState_Teleported)
13439 {
13440 /* Make sure any transient guest properties get removed from the
13441 * property store on shutdown. */
13442
13443 HWData::GuestPropertyList::iterator it;
13444 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13445 if (!fNeedsSaving)
13446 for (it = mHWData->mGuestProperties.begin();
13447 it != mHWData->mGuestProperties.end(); ++it)
13448 if ( (it->mFlags & guestProp::TRANSIENT)
13449 || (it->mFlags & guestProp::TRANSRESET))
13450 {
13451 fNeedsSaving = true;
13452 break;
13453 }
13454 if (fNeedsSaving)
13455 {
13456 mData->mCurrentStateModified = TRUE;
13457 stsFlags |= SaveSTS_CurStateModified;
13458 }
13459 }
13460#endif
13461
13462 rc = saveStateSettings(stsFlags);
13463
13464 if ( ( oldMachineState != MachineState_PoweredOff
13465 && oldMachineState != MachineState_Aborted
13466 && oldMachineState != MachineState_Teleported
13467 )
13468 && ( aMachineState == MachineState_PoweredOff
13469 || aMachineState == MachineState_Aborted
13470 || aMachineState == MachineState_Teleported
13471 )
13472 )
13473 {
13474 /* we've been shut down for any reason */
13475 /* no special action so far */
13476 }
13477
13478 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13479 LogFlowThisFuncLeave();
13480 return rc;
13481}
13482
13483/**
13484 * Sends the current machine state value to the VM process.
13485 *
13486 * @note Locks this object for reading, then calls a client process.
13487 */
13488HRESULT SessionMachine::updateMachineStateOnClient()
13489{
13490 AutoCaller autoCaller(this);
13491 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13492
13493 ComPtr<IInternalSessionControl> directControl;
13494 {
13495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13496 AssertReturn(!!mData, E_FAIL);
13497 directControl = mData->mSession.mDirectControl;
13498
13499 /* directControl may be already set to NULL here in #OnSessionEnd()
13500 * called too early by the direct session process while there is still
13501 * some operation (like deleting the snapshot) in progress. The client
13502 * process in this case is waiting inside Session::close() for the
13503 * "end session" process object to complete, while #uninit() called by
13504 * #checkForDeath() on the Watcher thread is waiting for the pending
13505 * operation to complete. For now, we accept this inconsistent behavior
13506 * and simply do nothing here. */
13507
13508 if (mData->mSession.mState == SessionState_Unlocking)
13509 return S_OK;
13510
13511 AssertReturn(!directControl.isNull(), E_FAIL);
13512 }
13513
13514 return directControl->UpdateMachineState(mData->mMachineState);
13515}
Note: See TracBrowser for help on using the repository browser.

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